1 #![allow(dead_code)]
2
3 //! Tessellation routines for simple shapes.
4 //!
5 //! #Overview
6 //!
7 //! This module contains tessellators for specific shapes that can
8 //! benefit from having a specialized algorithm rather than using
9 //! the generic algorithm (for performance purposes or in some cases
10 //! just for convenience).
11 //!
12 //! See also the generic [fill](../struct.FillTessellator.html) and
13 //! [stroke](../struct.StrokeTessellator.html) tessellators.
14 //!
15 //! Some of these algorithms approximate the geometry based on a
16 //! tolerance threshold which sets the maximum allowed distance
17 //! between the theoretical curve and its approximation.
18 //!
19 //! This tolerance threshold is configured in the
20 //! [FillOptions](../struct.FillOptions.html) and
21 //! [StrokeOptions](../struct.StrokeOptions.html) parameters.
22 //!
23 //! More explanation about flattening and tolerance in the
24 //! [lyon_geom crate](https://docs.rs/lyon_geom/#flattening).
25
26 use crate::geom::math::*;
27 use crate::geom::Arc;
28 use crate::geometry_builder::*;
29 use crate::path::builder::FlatPathBuilder;
30 use crate::path::iterator::FromPolyline;
31 use crate::path::EndpointId;
32 use crate::stroke::{StrokeBuilder, StrokeTessellator};
33 use crate::{
34 FillOptions, FillTessellator, Side, StrokeAttributes, StrokeAttributesData, StrokeOptions,
35 TessellationResult, VertexId, VertexSource,
36 };
37
38 use std::f32::consts::PI;
39
bottom_left(rect: &Rect) -> Point40 fn bottom_left(rect: &Rect) -> Point {
41 point(rect.min_x(), rect.max_y())
42 }
43
top_right(rect: &Rect) -> Point44 fn top_right(rect: &Rect) -> Point {
45 point(rect.max_x(), rect.min_y())
46 }
47
bottom_right(rect: &Rect) -> Point48 fn bottom_right(rect: &Rect) -> Point {
49 rect.max()
50 }
51
52 /// Tessellate the stroke for a triangle.
stroke_triangle( v1: Point, v2: Point, v3: Point, options: &StrokeOptions, output: &mut dyn StrokeGeometryBuilder, ) -> TessellationResult53 pub fn stroke_triangle(
54 v1: Point,
55 v2: Point,
56 v3: Point,
57 options: &StrokeOptions,
58 output: &mut dyn StrokeGeometryBuilder,
59 ) -> TessellationResult {
60 stroke_polyline([v1, v2, v3].iter().cloned(), true, options, output)
61 }
62
63 /// Tessellate a quad.
fill_quad( v1: Point, mut v2: Point, v3: Point, mut v4: Point, _options: &FillOptions, output: &mut dyn BasicGeometryBuilder, ) -> TessellationResult64 pub fn fill_quad(
65 v1: Point,
66 mut v2: Point,
67 v3: Point,
68 mut v4: Point,
69 _options: &FillOptions,
70 output: &mut dyn BasicGeometryBuilder,
71 ) -> TessellationResult {
72 output.begin_geometry();
73
74 // Make sure the winding order is correct.
75 if (v1 - v2).cross(v3 - v2) < 0.0 {
76 std::mem::swap(&mut v2, &mut v4);
77 }
78
79 let a = output.add_vertex(v1)?;
80 let b = output.add_vertex(v2)?;
81 let c = output.add_vertex(v3)?;
82 let d = output.add_vertex(v4)?;
83 output.add_triangle(a, b, c);
84 output.add_triangle(a, c, d);
85
86 Ok(output.end_geometry())
87 }
88
89 /// Tessellate the stroke for a quad.
stroke_quad( v1: Point, v2: Point, v3: Point, v4: Point, options: &StrokeOptions, output: &mut dyn StrokeGeometryBuilder, ) -> TessellationResult90 pub fn stroke_quad(
91 v1: Point,
92 v2: Point,
93 v3: Point,
94 v4: Point,
95 options: &StrokeOptions,
96 output: &mut dyn StrokeGeometryBuilder,
97 ) -> TessellationResult {
98 stroke_polyline([v1, v2, v3, v4].iter().cloned(), true, options, output)
99 }
100
101 /// Tessellate an axis-aligned rectangle.
fill_rectangle( rect: &Rect, _options: &FillOptions, output: &mut dyn BasicGeometryBuilder, ) -> TessellationResult102 pub fn fill_rectangle(
103 rect: &Rect,
104 _options: &FillOptions,
105 output: &mut dyn BasicGeometryBuilder,
106 ) -> TessellationResult {
107 output.begin_geometry();
108
109 let a = output.add_vertex(rect.origin)?;
110 let b = output.add_vertex(bottom_left(&rect))?;
111 let c = output.add_vertex(bottom_right(&rect))?;
112 let d = output.add_vertex(top_right(&rect))?;
113 output.add_triangle(a, b, c);
114 output.add_triangle(a, c, d);
115
116 Ok(output.end_geometry())
117 }
118
119 /// Tessellate the stroke for an axis-aligned rectangle.
stroke_rectangle( rect: &Rect, options: &StrokeOptions, output: &mut dyn StrokeGeometryBuilder, ) -> TessellationResult120 pub fn stroke_rectangle(
121 rect: &Rect,
122 options: &StrokeOptions,
123 output: &mut dyn StrokeGeometryBuilder,
124 ) -> TessellationResult {
125 let line_width = options.line_width;
126 if rect.size.width.abs() < line_width || rect.size.height < line_width {
127 return stroke_thin_rectangle(rect, options, output);
128 }
129
130 stroke_quad(
131 rect.origin,
132 top_right(&rect),
133 bottom_right(&rect),
134 bottom_left(&rect),
135 options,
136 output,
137 )
138 }
139
140 // A fall-back that avoids off artifacts with zero-area rectangles as
141 // well as overlapping triangles if the rectangle is smaller than the
142 // line width in any dimension.
143 #[inline(never)]
stroke_thin_rectangle( rect: &Rect, options: &StrokeOptions, output: &mut dyn StrokeGeometryBuilder, ) -> TessellationResult144 fn stroke_thin_rectangle(
145 rect: &Rect,
146 options: &StrokeOptions,
147 output: &mut dyn StrokeGeometryBuilder,
148 ) -> TessellationResult {
149 let rect = if options.apply_line_width {
150 let w = options.line_width * 0.5;
151 rect.inflate(w, w)
152 } else {
153 *rect
154 };
155
156 output.begin_geometry();
157
158 let mut attributes = StrokeAttributesData {
159 normal: vector(-1.0, -1.0),
160 advancement: 0.0,
161 side: Side::Left,
162 src: VertexSource::Endpoint {
163 id: EndpointId::INVALID,
164 },
165 store: &(),
166 buffer: &mut [],
167 buffer_is_valid: true,
168 };
169
170 let a = output.add_stroke_vertex(rect.origin, StrokeAttributes(&mut attributes))?;
171
172 attributes.normal = vector(-1.0, 1.0);
173 attributes.advancement += rect.size.height;
174
175 let b = output.add_stroke_vertex(bottom_left(&rect), StrokeAttributes(&mut attributes))?;
176
177 attributes.side = Side::Right;
178
179 attributes.normal = vector(1.0, 1.0);
180 attributes.advancement += rect.size.width;
181
182 let c = output.add_stroke_vertex(bottom_right(&rect), StrokeAttributes(&mut attributes))?;
183
184 attributes.normal = vector(1.0, -1.0);
185 attributes.advancement += rect.size.height;
186
187 let d = output.add_stroke_vertex(top_right(&rect), StrokeAttributes(&mut attributes))?;
188
189 output.add_triangle(a, b, c);
190 output.add_triangle(a, c, d);
191
192 Ok(output.end_geometry())
193 }
194
195 /// The radius of each corner of a rounded rectangle.
196 #[derive(Copy, Clone)]
197 pub struct BorderRadii {
198 pub top_left: f32,
199 pub top_right: f32,
200 pub bottom_left: f32,
201 pub bottom_right: f32,
202 }
203
204 impl BorderRadii {
new(top_left: f32, top_right: f32, bottom_left: f32, bottom_right: f32) -> Self205 pub fn new(top_left: f32, top_right: f32, bottom_left: f32, bottom_right: f32) -> Self {
206 BorderRadii {
207 top_left: top_left.abs(),
208 top_right: top_right.abs(),
209 bottom_left: bottom_left.abs(),
210 bottom_right: bottom_right.abs(),
211 }
212 }
213
new_all_same(radius: f32) -> Self214 pub fn new_all_same(radius: f32) -> Self {
215 let r = radius.abs();
216 BorderRadii {
217 top_left: r,
218 top_right: r,
219 bottom_left: r,
220 bottom_right: r,
221 }
222 }
223 }
224
225 /// Tessellate an axis-aligned rounded rectangle.
fill_rounded_rectangle( rect: &Rect, radii: &BorderRadii, options: &FillOptions, output: &mut dyn BasicGeometryBuilder, ) -> TessellationResult226 pub fn fill_rounded_rectangle(
227 rect: &Rect,
228 radii: &BorderRadii,
229 options: &FillOptions,
230 output: &mut dyn BasicGeometryBuilder,
231 ) -> TessellationResult {
232 output.begin_geometry();
233
234 let w = rect.size.width;
235 let h = rect.size.height;
236 let x_min = rect.min_x();
237 let y_min = rect.min_y();
238 let x_max = rect.max_x();
239 let y_max = rect.max_y();
240 let min_wh = w.min(h);
241 let mut tl = radii.top_left.abs().min(min_wh);
242 let mut tr = radii.top_right.abs().min(min_wh);
243 let mut bl = radii.bottom_left.abs().min(min_wh);
244 let mut br = radii.bottom_right.abs().min(min_wh);
245
246 // clamp border radii if they don't fit in the rectangle.
247 if tl + tr > w {
248 let x = (tl + tr - w) * 0.5;
249 tl -= x;
250 tr -= x;
251 }
252 if bl + br > w {
253 let x = (bl + br - w) * 0.5;
254 bl -= x;
255 br -= x;
256 }
257 if tr + br > h {
258 let x = (tr + br - h) * 0.5;
259 tr -= x;
260 br -= x;
261 }
262 if tl + bl > h {
263 let x = (tl + bl - h) * 0.5;
264 tl -= x;
265 bl -= x;
266 }
267
268 // top
269 let p1 = point(x_min + tl, y_min);
270 let p2 = point(x_max - tr, y_min);
271
272 // right
273 let p3 = point(x_max, y_min + tr);
274 let p4 = point(x_max, y_max - br);
275
276 // bottom
277 let p6 = point(x_min + bl, y_max);
278 let p5 = point(x_max - br, y_max);
279
280 // left
281 let p0 = point(x_min, y_min + tl);
282 let p7 = point(x_min, y_max - bl);
283
284 let v = [
285 output.add_vertex(p7)?,
286 output.add_vertex(p6)?,
287 output.add_vertex(p5)?,
288 output.add_vertex(p4)?,
289 output.add_vertex(p3)?,
290 output.add_vertex(p2)?,
291 output.add_vertex(p1)?,
292 output.add_vertex(p0)?,
293 ];
294
295 output.add_triangle(v[6], v[7], v[0]);
296 output.add_triangle(v[6], v[0], v[1]);
297 output.add_triangle(v[6], v[1], v[5]);
298 output.add_triangle(v[5], v[1], v[2]);
299 output.add_triangle(v[5], v[2], v[4]);
300 output.add_triangle(v[4], v[2], v[3]);
301
302 let radii = [bl, br, tr, tl];
303 let angles = [
304 (PI * 0.5, PI),
305 (0.0, PI * 0.5),
306 (1.5 * PI, 2.0 * PI),
307 (PI, 1.5 * PI),
308 ];
309
310 let centers = [
311 point(p6.x, p7.y),
312 point(p5.x, p4.y),
313 point(p2.x, p3.y),
314 point(p1.x, p0.y),
315 ];
316
317 for i in 0..4 {
318 let radius = radii[i];
319 if radius > 0.0 {
320 let arc_len = 0.5 * PI * radius;
321
322 let step = circle_flattening_step(radius, options.tolerance);
323 let num_segments = (arc_len / step).ceil();
324
325 let num_recursions = num_segments.log2() as u32;
326
327 fill_border_radius(
328 centers[i],
329 angles[i],
330 radius,
331 v[i * 2 + 1],
332 v[i * 2],
333 num_recursions,
334 output,
335 )?;
336 }
337 }
338
339 Ok(output.end_geometry())
340 }
341
342 // recursively tessellate the rounded corners.
fill_border_radius( center: Point, angle: (f32, f32), radius: f32, va: VertexId, vb: VertexId, num_recursions: u32, output: &mut dyn BasicGeometryBuilder, ) -> Result<(), GeometryBuilderError>343 fn fill_border_radius(
344 center: Point,
345 angle: (f32, f32),
346 radius: f32,
347 va: VertexId,
348 vb: VertexId,
349 num_recursions: u32,
350 output: &mut dyn BasicGeometryBuilder,
351 ) -> Result<(), GeometryBuilderError> {
352 if num_recursions == 0 {
353 return Ok(());
354 }
355
356 let mid_angle = (angle.0 + angle.1) * 0.5;
357
358 let normal = vector(mid_angle.cos(), mid_angle.sin());
359 let position = center + normal * radius;
360
361 let vertex = output.add_vertex(position)?;
362
363 output.add_triangle(vb, vertex, va);
364
365 fill_border_radius(
366 center,
367 (angle.0, mid_angle),
368 radius,
369 va,
370 vertex,
371 num_recursions - 1,
372 output,
373 )?;
374 fill_border_radius(
375 center,
376 (mid_angle, angle.1),
377 radius,
378 vertex,
379 vb,
380 num_recursions - 1,
381 output,
382 )
383 }
384
385 /// Tessellate the stroke for an axis-aligned rounded rectangle.
stroke_rounded_rectangle( rect: &Rect, radii: &BorderRadii, options: &StrokeOptions, output: &mut dyn StrokeGeometryBuilder, ) -> TessellationResult386 pub fn stroke_rounded_rectangle(
387 rect: &Rect,
388 radii: &BorderRadii,
389 options: &StrokeOptions,
390 output: &mut dyn StrokeGeometryBuilder,
391 ) -> TessellationResult {
392 output.begin_geometry();
393
394 let w = rect.size.width;
395 let h = rect.size.height;
396 let x_min = rect.min_x();
397 let y_min = rect.min_y();
398 let x_max = rect.max_x();
399 let y_max = rect.max_y();
400 let min_wh = w.min(h);
401 let mut tl = radii.top_left.abs().min(min_wh);
402 let mut tr = radii.top_right.abs().min(min_wh);
403 let mut bl = radii.bottom_left.abs().min(min_wh);
404 let mut br = radii.bottom_right.abs().min(min_wh);
405
406 // clamp border radii if they don't fit in the rectangle.
407 if tl + tr > w {
408 let x = (tl + tr - w) * 0.5;
409 tl -= x;
410 tr -= x;
411 }
412 if bl + br > w {
413 let x = (bl + br - w) * 0.5;
414 bl -= x;
415 br -= x;
416 }
417 if tr + br > h {
418 let x = (tr + br - h) * 0.5;
419 tr -= x;
420 br -= x;
421 }
422 if tl + bl > h {
423 let x = (tl + bl - h) * 0.5;
424 tl -= x;
425 bl -= x;
426 }
427
428 // top
429 let p1 = point(x_min + tl, y_min);
430 let p2 = point(x_max - tr, y_min);
431
432 // right
433 let p3 = point(x_max, y_min + tr);
434 let p4 = point(x_max, y_max - br);
435
436 // bottom
437 let p6 = point(x_min + bl, y_max);
438 let p5 = point(x_max - br, y_max);
439
440 // left
441 let p0 = point(x_min, y_min + tl);
442 let p7 = point(x_min, y_max - bl);
443
444 let sides = &[[p1, p2], [p3, p4], [p5, p6], [p7, p0]];
445
446 let radii = [tl, tr, br, bl];
447 let angles = [
448 (PI, 1.5 * PI),
449 (1.5 * PI, 2.0 * PI),
450 (0.0, PI * 0.5),
451 (PI * 0.5, PI),
452 ];
453
454 let centers = [
455 point(p1.x, p0.y),
456 point(p2.x, p3.y),
457 point(p5.x, p4.y),
458 point(p6.x, p7.y),
459 ];
460
461 let mut nums = radii.iter().map(|&radius| {
462 if radius > 0.0 {
463 let arc_len = 0.5 * PI * radius;
464 let step = circle_flattening_step(radius, options.tolerance);
465 (arc_len / step).ceil() as u32 - 1
466 } else {
467 0
468 }
469 });
470
471 {
472 let mut builder = StrokeBuilder::new(options, &(), &mut [], output);
473 builder.move_to(p0);
474 for i in 0..4 {
475 stroke_border_radius(
476 centers[i],
477 angles[i],
478 radii[i],
479 nums.next().unwrap(),
480 &mut builder,
481 );
482
483 builder.line_to(sides[i][0]);
484 builder.line_to(sides[i][1]);
485 }
486 builder.close();
487 }
488
489 Ok(output.end_geometry())
490 }
491
492 /// Tessellate a circle.
fill_circle( center: Point, radius: f32, options: &FillOptions, output: &mut dyn BasicGeometryBuilder, ) -> TessellationResult493 pub fn fill_circle(
494 center: Point,
495 radius: f32,
496 options: &FillOptions,
497 output: &mut dyn BasicGeometryBuilder,
498 ) -> TessellationResult {
499 output.begin_geometry();
500
501 let radius = radius.abs();
502 if radius == 0.0 {
503 return Ok(output.end_geometry());
504 }
505
506 let up = vector(0.0, -1.0);
507 let down = vector(0.0, 1.0);
508 let left = vector(-1.0, 0.0);
509 let right = vector(1.0, 0.0);
510
511 let v = [
512 output.add_vertex(center + (left * radius))?,
513 output.add_vertex(center + (up * radius))?,
514 output.add_vertex(center + (right * radius))?,
515 output.add_vertex(center + (down * radius))?,
516 ];
517
518 output.add_triangle(v[0], v[3], v[1]);
519 output.add_triangle(v[1], v[3], v[2]);
520
521 let angles = [
522 (PI, 1.5 * PI),
523 (1.5 * PI, 2.0 * PI),
524 (0.0, PI * 0.5),
525 (PI * 0.5, PI),
526 ];
527
528 let arc_len = 0.5 * PI * radius;
529 let step = circle_flattening_step(radius, options.tolerance);
530 let num_segments = (arc_len / step).ceil();
531 let num_recursions = num_segments.log2() as u32;
532
533 for i in 0..4 {
534 fill_border_radius(
535 center,
536 angles[i],
537 radius,
538 v[i],
539 v[(i + 1) % 4],
540 num_recursions,
541 output,
542 )?;
543 }
544
545 Ok(output.end_geometry())
546 }
547
548 /// Tessellate the stroke for a circle.
stroke_circle( center: Point, radius: f32, options: &StrokeOptions, output: &mut dyn StrokeGeometryBuilder, ) -> TessellationResult549 pub fn stroke_circle(
550 center: Point,
551 radius: f32,
552 options: &StrokeOptions,
553 output: &mut dyn StrokeGeometryBuilder,
554 ) -> TessellationResult {
555 output.begin_geometry();
556
557 let radius = radius.abs();
558 if radius == 0.0 {
559 return Ok(output.end_geometry());
560 }
561
562 let angle = (0.0, 2.0 * PI);
563 let starting_point = center + vector(1.0, 0.0) * radius;
564
565 let arc_len = 2.0 * PI * radius;
566 let step = circle_flattening_step(radius, options.tolerance);
567 let num_points = (arc_len / step).ceil() as u32 - 1;
568
569 {
570 // output borrow scope start
571 let mut builder = StrokeBuilder::new(options, &(), &mut [], output);
572 builder.move_to(starting_point);
573 stroke_border_radius(center, angle, radius, num_points, &mut builder);
574 builder.close();
575 } // output borrow scope end
576
577 Ok(output.end_geometry())
578 }
579
580 // tessellate the stroke for rounded corners using the inner points.
581 // assumming the builder started with move_to().
stroke_border_radius( center: Point, angle: (f32, f32), radius: f32, num_points: u32, builder: &mut StrokeBuilder, )582 fn stroke_border_radius(
583 center: Point,
584 angle: (f32, f32),
585 radius: f32,
586 num_points: u32,
587 builder: &mut StrokeBuilder,
588 ) {
589 let angle_size = (angle.0 - angle.1).abs();
590 let starting_angle = angle.0.min(angle.1);
591
592 for i in 1..num_points + 1 {
593 let new_angle = i as f32 * (angle_size) / (num_points + 1) as f32 + starting_angle;
594 let normal = vector(new_angle.cos(), new_angle.sin());
595
596 builder.line_to(center + normal * radius)
597 }
598 }
599
600 /* TODO
601 /// Tessellate an ellipse.
602 pub fn fill_ellipse(
603 center: Point,
604 radii: Vector,
605 x_rotation: Angle,
606 options: &FillOptions,
607 output: &mut dyn GeometryBuilder<FillVertex>,
608 ) -> TessellationResult {
609 if radii.x == radii.y {
610 return Ok(fill_circle(center, radii.x, options, output)?);
611 }
612
613 // TODO: This is far from optimal compared to the circle tessellation, but it
614 // correctly takes the tolerance threshold into account which is harder to do
615 // than with circles.
616
617 let arc = Arc {
618 center,
619 radii,
620 x_rotation,
621 start_angle: Angle::radians(0.0),
622 sweep_angle: Angle::radians(2.0 * PI-0.01),
623 };
624
625 use crate::path::builder::{Build, PathBuilder, FlatteningBuilder};
626 use crate::path_fill::EventsBuilder;
627
628 let mut path = FlatteningBuilder::new(
629 EventsBuilder::new(),
630 options.tolerance
631 ).with_svg();
632
633 // TODO don't need to go through quadratic bézier approximation here.
634 path.move_to(arc.sample(0.0));
635 arc.for_each_quadratic_bezier(&mut|curve| {
636 path.quadratic_bezier_to(curve.ctrl, curve.to);
637 });
638 path.close();
639
640 let events = path.build();
641
642 // TODO: We could avoid checking for intersections, however the way we
643 // generate the path is a little silly and because of finite float precision,
644 // it will sometimes produce an intersection where the end of ellipse meets
645 // the beginning, which confuses the fill tessellator.
646 FillTessellator::new().tessellate_events(
647 &events,
648 &options,
649 output,
650 )
651 }
652 */
653
654 /// Tessellate the stroke for an ellipse.
stroke_ellipse( center: Point, radii: Vector, x_rotation: Angle, options: &StrokeOptions, output: &mut dyn StrokeGeometryBuilder, ) -> TessellationResult655 pub fn stroke_ellipse(
656 center: Point,
657 radii: Vector,
658 x_rotation: Angle,
659 options: &StrokeOptions,
660 output: &mut dyn StrokeGeometryBuilder,
661 ) -> TessellationResult {
662 // TODO: This is far from optimal compared to the circle tessellation, but it
663 // correctly takes the tolerance threshold into account which is harder to do
664 // than with circles.
665
666 let arc = Arc {
667 center,
668 radii,
669 x_rotation,
670 start_angle: Angle::radians(0.0),
671 sweep_angle: Angle::radians(2.0 * PI - 0.01),
672 };
673
674 use crate::path::builder::{Build, FlatteningBuilder, PathBuilder};
675
676 output.begin_geometry();
677 {
678 let mut path = FlatteningBuilder::new(
679 StrokeBuilder::new(options, &(), &mut [], output),
680 options.tolerance,
681 )
682 .with_svg();
683
684 path.move_to(arc.sample(0.0));
685 arc.for_each_quadratic_bezier(&mut |curve| {
686 path.quadratic_bezier_to(curve.ctrl, curve.to);
687 });
688 path.close();
689
690 path.build()?;
691 }
692
693 Ok(output.end_geometry())
694 }
695
696 /// Tessellate a convex shape that is described by an iterator of points.
697 ///
698 /// The shape is assumed to be convex, calling this function with a concave
699 /// shape may produce incorrect results.
fill_convex_polyline<Iter>( it: Iter, options: &FillOptions, output: &mut dyn FillGeometryBuilder, ) -> TessellationResult where Iter: Iterator<Item = Point> + Clone,700 pub fn fill_convex_polyline<Iter>(
701 it: Iter,
702 options: &FillOptions,
703 output: &mut dyn FillGeometryBuilder,
704 ) -> TessellationResult
705 where
706 Iter: Iterator<Item = Point> + Clone,
707 {
708 fill_polyline(it, &mut FillTessellator::new(), options, output)
709 }
710
711 /// Tessellate the stroke for a shape that is described by an iterator of points.
712 ///
713 /// Convenient when tessellating a shape that is represented as a slice `&[Point]`.
stroke_polyline<Iter>( it: Iter, is_closed: bool, options: &StrokeOptions, output: &mut dyn StrokeGeometryBuilder, ) -> TessellationResult where Iter: IntoIterator<Item = Point>,714 pub fn stroke_polyline<Iter>(
715 it: Iter,
716 is_closed: bool,
717 options: &StrokeOptions,
718 output: &mut dyn StrokeGeometryBuilder,
719 ) -> TessellationResult
720 where
721 Iter: IntoIterator<Item = Point>,
722 {
723 let mut tess = StrokeTessellator::new();
724
725 tess.tessellate(
726 FromPolyline::new(is_closed, it.into_iter()),
727 options,
728 output,
729 )
730 }
731
732 /// Tessellate an arbitrary shape that is described by an iterator of points.
fill_polyline<Iter>( polyline: Iter, tessellator: &mut FillTessellator, options: &FillOptions, output: &mut dyn FillGeometryBuilder, ) -> TessellationResult where Iter: IntoIterator<Item = Point>,733 pub fn fill_polyline<Iter>(
734 polyline: Iter,
735 tessellator: &mut FillTessellator,
736 options: &FillOptions,
737 output: &mut dyn FillGeometryBuilder,
738 ) -> TessellationResult
739 where
740 Iter: IntoIterator<Item = Point>,
741 {
742 tessellator.tessellate(FromPolyline::closed(polyline.into_iter()), options, output)
743 }
744
745 // Returns the maximum length of individual line segments when approximating a
746 // circle.
747 //
748 // From pythagora's theorem:
749 // r² = (d/2)² + (r - t)²
750 // r² = d²/4 + r² + t² - 2 * e * r
751 // d² = 4 * (2 * t * r - t²)
752 // d = 2 * sqrt(2 * t * r - t²)
753 //
754 // With:
755 // r: the radius
756 // t: the tolerance threshold
757 // d: the line segment length
circle_flattening_step(radius: f32, mut tolerance: f32) -> f32758 pub(crate) fn circle_flattening_step(radius: f32, mut tolerance: f32) -> f32 {
759 // Don't allow high tolerance values (compared to the radius) to avoid edge cases.
760 tolerance = f32::min(tolerance, radius);
761 2.0 * f32::sqrt(2.0 * tolerance * radius - tolerance * tolerance)
762 }
763
764 //#[test]
765 //fn issue_358() {
766 // use crate::geometry_builder::NoOutput;
767 //
768 // fill_ellipse(
769 // point(25218.9902, 25669.6738),
770 // vector(2.0, 2.0),
771 // Angle { radians: 0.0 },
772 // &FillOptions::tolerance(1.0),
773 // &mut NoOutput::new(),
774 // ).unwrap();
775 //}
776
777 #[test]
issue_366()778 fn issue_366() {
779 use crate::geometry_builder::NoOutput;
780
781 fill_circle(
782 point(0.0, 0.0),
783 1.0,
784 &FillOptions::tolerance(100.0),
785 &mut NoOutput::new(),
786 )
787 .unwrap();
788
789 stroke_circle(
790 point(0.0, 0.0),
791 1.0,
792 &StrokeOptions::tolerance(100.0),
793 &mut NoOutput::new(),
794 )
795 .unwrap();
796 }
797