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