lyon_tessellation/
stroke.rs

1// There's a number of cases in this file where this lint just complicates the code.
2#![allow(clippy::needless_range_loop)]
3
4use crate::geom::arrayvec::ArrayVec;
5use crate::geom::utils::tangent;
6use crate::geom::{CubicBezierSegment, Line, LineSegment, QuadraticBezierSegment};
7use crate::math::*;
8use crate::math_utils::compute_normal;
9use crate::path::builder::{Build, NoAttributes, PathBuilder};
10use crate::path::polygon::Polygon;
11use crate::path::private::DebugValidator;
12use crate::path::{
13    AttributeStore, Attributes, EndpointId, IdEvent, PathEvent, PathSlice, PositionStore, Winding,
14};
15use crate::{
16    LineCap, LineJoin, Side, SimpleAttributeStore, StrokeGeometryBuilder, StrokeOptions,
17    TessellationError, TessellationResult, VertexId, VertexSource,
18};
19
20use core::f32::consts::PI;
21use alloc::vec::Vec;
22
23#[cfg(not(feature = "std"))]
24use num_traits::Float;
25
26const SIDE_POSITIVE: usize = 0;
27const SIDE_NEGATIVE: usize = 1;
28
29macro_rules! nan_check {
30    ($($v:expr),+) => { $(debug_assert!(!$v.is_nan());)+ };
31}
32
33// TODO: the stroke tessellator's code is has a lot of duplication and a bunch of error prone parts
34// such as having to know at which stage each member of SidePoints is set.
35// It would be good to spend some time simplifying it.
36
37/// A Context object that can tessellate stroke operations for complex paths.
38///
39/// ## Overview
40///
41/// The stroke tessellation algorithm simply generates a strip of triangles along
42/// the path. This method is fast and simple to implement, however it means that
43/// if the path overlap with itself (for example in the case of a self-intersecting
44/// path), some triangles will overlap in the intersecting region, which may not
45/// be the desired behavior. This needs to be kept in mind when rendering transparent
46/// SVG strokes since the spec mandates that each point along a semi-transparent path
47/// is shaded once no matter how many times the path overlaps with itself at this
48/// location.
49///
50/// `StrokeTessellator` exposes a similar interface to its
51/// [fill equivalent](struct.FillTessellator.html).
52///
53/// This stroke tessellator takes an iterator of path events as inputs as well as
54/// a [`StrokeOption`](struct.StrokeOptions.html), and produces its outputs using
55/// a [`StrokeGeometryBuilder`](geometry_builder/trait.StrokeGeometryBuilder.html).
56///
57///
58/// See the [`geometry_builder` module documentation](geometry_builder/index.html)
59/// for more details about how to output custom vertex layouts.
60///
61/// See <https://github.com/nical/lyon/wiki/Stroke-tessellation> for some notes
62/// about how the path stroke tessellator is implemented.
63///
64/// # Examples
65///
66/// ```
67/// # extern crate lyon_tessellation as tess;
68/// # use tess::path::Path;
69/// # use tess::path::builder::*;
70/// # use tess::path::iterator::*;
71/// # use tess::math::*;
72/// # use tess::geometry_builder::{VertexBuffers, simple_builder};
73/// # use tess::*;
74/// # fn main() {
75/// // Create a simple path.
76/// let mut path_builder = Path::builder();
77/// path_builder.begin(point(0.0, 0.0));
78/// path_builder.line_to(point(1.0, 2.0));
79/// path_builder.line_to(point(2.0, 0.0));
80/// path_builder.line_to(point(1.0, 1.0));
81/// path_builder.end(true);
82/// let path = path_builder.build();
83///
84/// // Create the destination vertex and index buffers.
85/// let mut buffers: VertexBuffers<Point, u16> = VertexBuffers::new();
86///
87/// {
88///     // Create the destination vertex and index buffers.
89///     let mut vertex_builder = simple_builder(&mut buffers);
90///
91///     // Create the tessellator.
92///     let mut tessellator = StrokeTessellator::new();
93///
94///     // Compute the tessellation.
95///     tessellator.tessellate(
96///         &path,
97///         &StrokeOptions::default(),
98///         &mut vertex_builder
99///     );
100/// }
101///
102/// println!("The generated vertices are: {:?}.", &buffers.vertices[..]);
103/// println!("The generated indices are: {:?}.", &buffers.indices[..]);
104///
105/// # }
106/// ```
107#[derive(Default)]
108pub struct StrokeTessellator {
109    attrib_buffer: Vec<f32>,
110    builder_attrib_store: SimpleAttributeStore,
111}
112
113impl StrokeTessellator {
114    pub fn new() -> Self {
115        StrokeTessellator {
116            attrib_buffer: Vec::new(),
117            builder_attrib_store: SimpleAttributeStore::new(0),
118        }
119    }
120
121    /// Compute the tessellation from a path iterator.
122    pub fn tessellate(
123        &mut self,
124        input: impl IntoIterator<Item = PathEvent>,
125        options: &StrokeOptions,
126        builder: &mut dyn StrokeGeometryBuilder,
127    ) -> TessellationResult {
128        debug_assert!(
129            options.variable_line_width.is_none(),
130            "Variable line width requires custom attributes. Try tessellate_with_ids or tessellate_path",
131        );
132
133        let mut buffer = Vec::new();
134        let builder = StrokeBuilderImpl::new(options, &mut buffer, builder);
135
136        builder.tessellate_fw(input)
137    }
138
139    /// Compute the tessellation from a path iterator.
140    pub fn tessellate_with_ids(
141        &mut self,
142        path: impl IntoIterator<Item = IdEvent>,
143        positions: &impl PositionStore,
144        custom_attributes: Option<&dyn AttributeStore>,
145        options: &StrokeOptions,
146        output: &mut dyn StrokeGeometryBuilder,
147    ) -> TessellationResult {
148        let custom_attributes = custom_attributes.unwrap_or(&());
149
150        self.attrib_buffer.clear();
151        for _ in 0..custom_attributes.num_attributes() {
152            self.attrib_buffer.push(0.0);
153        }
154
155        let builder = StrokeBuilderImpl::new(options, &mut self.attrib_buffer, output);
156
157        builder.tessellate_with_ids(path, positions, custom_attributes)
158    }
159
160    /// Compute the tessellation from a path slice.
161    ///
162    /// The tessellator will internally only track vertex sources and interpolated
163    /// attributes if the path has interpolated attributes.
164    pub fn tessellate_path<'l>(
165        &'l mut self,
166        path: impl Into<PathSlice<'l>>,
167        options: &'l StrokeOptions,
168        builder: &'l mut dyn StrokeGeometryBuilder,
169    ) -> TessellationResult {
170        let path = path.into();
171
172        if path.num_attributes() > 0 {
173            self.tessellate_with_ids(path.id_iter(), &path, Some(&path), options, builder)
174        } else {
175            self.tessellate(path.iter(), options, builder)
176        }
177    }
178
179    /// Tessellate directly from a sequence of `PathBuilder` commands, without
180    /// creating an intermediate path data structure.
181    ///
182    /// The returned builder implements the [`lyon_path::traits::PathBuilder`] trait,
183    /// is compatible with the all `PathBuilder` adapters.
184    /// It also has all requirements documented in `PathBuilder` (All sub-paths must be
185    /// wrapped in a `begin`/`end` pair).
186    ///
187    /// # Example
188    ///
189    /// ```rust
190    /// use lyon_tessellation::{StrokeTessellator, StrokeOptions};
191    /// use lyon_tessellation::geometry_builder::{simple_builder, VertexBuffers};
192    /// use lyon_tessellation::math::{Point, point};
193    ///
194    /// let mut buffers: VertexBuffers<Point, u16> = VertexBuffers::new();
195    /// let mut vertex_builder = simple_builder(&mut buffers);
196    /// let mut tessellator = StrokeTessellator::new();
197    /// let options = StrokeOptions::default();
198    ///
199    /// // Create a temporary builder (borrows the tessellator).
200    /// let mut builder = tessellator.builder(&options, &mut vertex_builder);
201    ///
202    /// // Build the path directly in the tessellator, skipping an intermediate data
203    /// // structure.
204    /// builder.begin(point(0.0, 0.0));
205    /// builder.line_to(point(10.0, 0.0));
206    /// builder.line_to(point(10.0, 10.0));
207    /// builder.line_to(point(0.0, 10.0));
208    /// builder.end(true);
209    ///
210    /// // Finish the tessellation and get the result.
211    /// let result = builder.build();
212    /// ```
213    ///
214    /// [`lyon_path::traits::PathBuilder`]: https://docs.rs/lyon_path/*/lyon_path/traits/trait.PathBuilder.html
215    pub fn builder<'l>(
216        &'l mut self,
217        options: &'l StrokeOptions,
218        output: &'l mut dyn StrokeGeometryBuilder,
219    ) -> NoAttributes<StrokeBuilder<'l>> {
220        self.builder_attrib_store.reset(0);
221        self.attrib_buffer.clear();
222        NoAttributes::wrap(StrokeBuilder::new(
223            options,
224            &mut self.attrib_buffer,
225            &mut self.builder_attrib_store,
226            output,
227        ))
228    }
229
230    /// Tessellate directly from a sequence of `PathBuilder` commands, without
231    /// creating an intermediate path data structure.
232    ///
233    /// Similar to `StrokeTessellator::builder` with custom attributes.
234    pub fn builder_with_attributes<'l>(
235        &'l mut self,
236        num_attributes: usize,
237        options: &'l StrokeOptions,
238        output: &'l mut dyn StrokeGeometryBuilder,
239    ) -> StrokeBuilder<'l> {
240        self.builder_attrib_store.reset(num_attributes);
241        self.attrib_buffer.clear();
242        for _ in 0..num_attributes {
243            self.attrib_buffer.push(0.0);
244        }
245
246        StrokeBuilder::new(
247            options,
248            &mut self.attrib_buffer,
249            &mut self.builder_attrib_store,
250            output,
251        )
252    }
253
254    /// Tessellate the stroke for a `Polygon`.
255    pub fn tessellate_polygon(
256        &mut self,
257        polygon: Polygon<Point>,
258        options: &StrokeOptions,
259        output: &mut dyn StrokeGeometryBuilder,
260    ) -> TessellationResult {
261        self.tessellate(polygon.path_events(), options, output)
262    }
263
264    /// Tessellate the stroke for an axis-aligned rectangle.
265    pub fn tessellate_rectangle(
266        &mut self,
267        rect: &Box2D,
268        options: &StrokeOptions,
269        output: &mut dyn StrokeGeometryBuilder,
270    ) -> TessellationResult {
271        assert!(options.variable_line_width.is_none());
272
273        let mut builder = self.builder(options, output);
274        builder.add_rectangle(rect, Winding::Positive);
275
276        builder.build()
277    }
278
279    /// Tessellate the stroke for a circle.
280    pub fn tessellate_circle(
281        &mut self,
282        center: Point,
283        radius: f32,
284        options: &StrokeOptions,
285        output: &mut dyn StrokeGeometryBuilder,
286    ) -> TessellationResult {
287        let mut builder = self.builder(options, output);
288        builder.add_circle(center, radius, Winding::Positive);
289
290        builder.build()
291    }
292
293    /// Tessellate the stroke for an ellipse.
294    pub fn tessellate_ellipse(
295        &mut self,
296        center: Point,
297        radii: Vector,
298        x_rotation: Angle,
299        winding: Winding,
300        options: &StrokeOptions,
301        output: &mut dyn StrokeGeometryBuilder,
302    ) -> TessellationResult {
303        let mut builder = self.builder(options, output);
304        builder.add_ellipse(center, radii, x_rotation, winding);
305
306        builder.build()
307    }
308}
309
310#[derive(Copy, Clone, Debug)]
311pub(crate) struct SidePoints {
312    prev: Point,
313    next: Point,
314    single_vertex: Option<Point>,
315    prev_vertex: VertexId,
316    next_vertex: VertexId,
317}
318
319#[derive(Copy, Clone, Debug)]
320pub(crate) struct EndpointData {
321    pub position: Point,
322    pub half_width: f32,
323    pub advancement: f32,
324    pub line_join: LineJoin,
325    pub src: VertexSource,
326    pub side_points: [SidePoints; 2],
327    pub fold: [bool; 2],
328    pub is_flattening_step: bool,
329}
330
331impl Default for EndpointData {
332    fn default() -> Self {
333        EndpointData {
334            position: Point::zero(),
335            half_width: f32::NAN,
336            advancement: f32::NAN,
337            line_join: LineJoin::Miter,
338            src: VertexSource::Endpoint {
339                id: EndpointId::INVALID,
340            },
341            side_points: [SidePoints {
342                prev: point(f32::NAN, f32::NAN),
343                prev_vertex: VertexId(u32::MAX),
344                next: point(f32::NAN, f32::NAN),
345                next_vertex: VertexId(u32::MAX),
346                single_vertex: None,
347            }; 2],
348            fold: [false, false],
349            is_flattening_step: false,
350        }
351    }
352}
353
354/// A builder object that tessellates a stroked path via the `PathBuilder`
355/// interface.
356///
357/// Can be created using `StrokeTessellator::builder_with_attributes`.
358pub struct StrokeBuilder<'l> {
359    builder: StrokeBuilderImpl<'l>,
360    attrib_store: &'l mut SimpleAttributeStore,
361    validator: DebugValidator,
362    prev: (Point, EndpointId, f32),
363}
364
365impl<'l> StrokeBuilder<'l> {
366    pub(crate) fn new(
367        options: &StrokeOptions,
368        attrib_buffer: &'l mut Vec<f32>,
369        attrib_store: &'l mut SimpleAttributeStore,
370        output: &'l mut dyn StrokeGeometryBuilder,
371    ) -> Self {
372        StrokeBuilder {
373            builder: StrokeBuilderImpl::new(options, attrib_buffer, output),
374            attrib_store,
375            validator: DebugValidator::new(),
376            prev: (Point::zero(), EndpointId::INVALID, 0.0),
377        }
378    }
379
380    #[inline]
381    pub fn set_line_join(&mut self, join: LineJoin) {
382        self.builder.options.line_join = join;
383    }
384
385    #[inline]
386    pub fn set_start_cap(&mut self, cap: LineCap) {
387        self.builder.options.start_cap = cap;
388    }
389
390    #[inline]
391    pub fn set_end_cap(&mut self, cap: LineCap) {
392        self.builder.options.end_cap = cap;
393    }
394
395    #[inline]
396    pub fn set_miter_limit(&mut self, limit: f32) {
397        self.builder.options.miter_limit = limit;
398    }
399
400    fn get_width(&self, attributes: Attributes) -> f32 {
401        if let Some(idx) = self.builder.options.variable_line_width {
402            self.builder.options.line_width * attributes[idx]
403        } else {
404            self.builder.options.line_width
405        }
406    }
407}
408
409impl<'l> PathBuilder for StrokeBuilder<'l> {
410    fn num_attributes(&self) -> usize {
411        self.attrib_store.num_attributes()
412    }
413
414    fn begin(&mut self, to: Point, attributes: Attributes) -> EndpointId {
415        self.validator.begin();
416        let id = self.attrib_store.add(attributes);
417        if let Some(attrib_index) = self.builder.options.variable_line_width {
418            let width = self.builder.options.line_width * attributes[attrib_index];
419            self.builder.begin(to, id, width, self.attrib_store);
420            self.prev = (to, id, width);
421        } else {
422            self.builder.begin_fw(to, id, self.attrib_store);
423            self.prev = (to, id, self.builder.options.line_width);
424        }
425
426        id
427    }
428
429    fn end(&mut self, close: bool) {
430        self.validator.end();
431        self.builder.end(close, self.attrib_store);
432    }
433
434    fn line_to(&mut self, to: Point, attributes: Attributes) -> EndpointId {
435        let id = self.attrib_store.add(attributes);
436        self.validator.edge();
437        if let Some(attrib_index) = self.builder.options.variable_line_width {
438            let width = self.builder.options.line_width * attributes[attrib_index];
439            self.builder.line_to(to, id, width, self.attrib_store);
440            self.prev = (to, id, width);
441        } else {
442            self.builder.line_to_fw(to, id, self.attrib_store);
443            self.prev = (to, id, self.builder.options.line_width);
444        }
445
446        id
447    }
448
449    fn quadratic_bezier_to(
450        &mut self,
451        ctrl: Point,
452        to: Point,
453        attributes: Attributes,
454    ) -> EndpointId {
455        self.validator.edge();
456        let (from, from_id, start_width) = self.prev;
457        let to_id = self.attrib_store.add(attributes);
458
459        let curve = QuadraticBezierSegment { from, ctrl, to };
460
461        if let Some(attrib_index) = self.builder.options.variable_line_width {
462            let end_width = self.builder.options.line_width * attributes[attrib_index];
463            self.builder.quadratic_bezier_to(
464                &curve,
465                from_id,
466                to_id,
467                start_width,
468                end_width,
469                self.attrib_store,
470            );
471
472            self.prev = (to, to_id, end_width);
473        } else {
474            self.builder
475                .quadratic_bezier_to_fw(&curve, from_id, to_id, self.attrib_store);
476
477            self.prev = (to, to_id, self.builder.options.line_width);
478        }
479
480        to_id
481    }
482
483    fn cubic_bezier_to(
484        &mut self,
485        ctrl1: Point,
486        ctrl2: Point,
487        to: Point,
488        attributes: Attributes,
489    ) -> EndpointId {
490        self.validator.edge();
491        let (from, from_id, start_width) = self.prev;
492        let to_id = self.attrib_store.add(attributes);
493
494        let curve = CubicBezierSegment {
495            from,
496            ctrl1,
497            ctrl2,
498            to,
499        };
500
501        if let Some(attrib_index) = self.builder.options.variable_line_width {
502            let end_width = self.builder.options.line_width * attributes[attrib_index];
503            self.builder.cubic_bezier_to(
504                &curve,
505                from_id,
506                to_id,
507                start_width,
508                end_width,
509                self.attrib_store,
510            );
511
512            self.prev = (to, to_id, end_width);
513        } else {
514            self.builder
515                .cubic_bezier_to_fw(&curve, from_id, to_id, self.attrib_store);
516
517            self.prev = (to, to_id, self.builder.options.line_width);
518        }
519
520        to_id
521    }
522
523    fn add_rectangle(&mut self, rect: &Box2D, winding: Winding, attributes: Attributes) {
524        // The thin rectangle approximation for works best with miter joins. We
525        // only use it with other joins if the rectangle is much smaller than the
526        // line width.
527        let threshold = match self.builder.options.line_join {
528            LineJoin::Miter => 1.0,
529            _ => 0.05,
530        } * self.builder.options.line_width;
531
532        if self.builder.options.variable_line_width.is_none()
533            && (rect.width().abs() < threshold || rect.height().abs() < threshold)
534        {
535            approximate_thin_rectangle(self, rect, attributes);
536            return;
537        }
538
539        match winding {
540            Winding::Positive => self.add_polygon(
541                Polygon {
542                    points: &[
543                        rect.min,
544                        point(rect.max.x, rect.min.y),
545                        rect.max,
546                        point(rect.min.x, rect.max.y),
547                    ],
548                    closed: true,
549                },
550                attributes,
551            ),
552            Winding::Negative => self.add_polygon(
553                Polygon {
554                    points: &[
555                        rect.min,
556                        point(rect.min.x, rect.max.y),
557                        rect.max,
558                        point(rect.max.x, rect.min.y),
559                    ],
560                    closed: true,
561                },
562                attributes,
563            ),
564        };
565    }
566}
567
568impl<'l> Build for StrokeBuilder<'l> {
569    type PathType = TessellationResult;
570
571    fn build(self) -> TessellationResult {
572        self.builder.build()
573    }
574}
575
576/// A builder that tessellates a stroke directly without allocating any intermediate data structure.
577pub(crate) struct StrokeBuilderImpl<'l> {
578    options: StrokeOptions,
579    pub(crate) error: Option<TessellationError>,
580    pub(crate) output: &'l mut dyn StrokeGeometryBuilder,
581    vertex: StrokeVertexData<'l>,
582    point_buffer: PointBuffer,
583    firsts: ArrayVec<EndpointData, 2>,
584    previous: Option<EndpointData>,
585    sub_path_start_advancement: f32,
586    square_merge_threshold: f32,
587    may_need_empty_cap: bool,
588}
589
590impl<'l> StrokeBuilderImpl<'l> {
591    pub(crate) fn new(
592        options: &StrokeOptions,
593        attrib_buffer: &'l mut Vec<f32>,
594        output: &'l mut dyn StrokeGeometryBuilder,
595    ) -> Self {
596        output.begin_geometry();
597
598        // Ideally we'd use the bounding rect of the path as an indication
599        // of what is considered a very small distance between two points,
600        // but we don't have this information so we use a combination of the
601        // tolerance threshold and, in case the latter is high to get "low-poly"
602        // curves, the line width.
603        let square_merge_threshold = (options.tolerance * options.tolerance * 0.5)
604            .min(options.line_width * options.line_width * 0.05)
605            .max(1e-8);
606
607        let zero = Point::new(0.0, 0.0);
608        StrokeBuilderImpl {
609            options: *options,
610            error: None,
611            output,
612            vertex: StrokeVertexData {
613                position_on_path: zero,
614                normal: vector(0.0, 0.0),
615                half_width: options.line_width * 0.5,
616                advancement: 0.0,
617                buffer: attrib_buffer,
618                side: Side::Negative,
619                src: VertexSource::Endpoint {
620                    id: EndpointId::INVALID,
621                },
622                buffer_is_valid: false,
623            },
624            point_buffer: PointBuffer::new(),
625            firsts: ArrayVec::new(),
626            previous: None,
627            sub_path_start_advancement: 0.0,
628            square_merge_threshold,
629            may_need_empty_cap: false,
630        }
631    }
632
633    #[cold]
634    pub(crate) fn error<E: Into<TessellationError>>(&mut self, e: E) {
635        if self.error.is_none() {
636            self.error = Some(e.into());
637        }
638    }
639
640    pub(crate) fn tessellate_with_ids(
641        self,
642        path: impl IntoIterator<Item = IdEvent>,
643        positions: &impl PositionStore,
644        attributes: &dyn AttributeStore,
645    ) -> TessellationResult {
646        if self.options.variable_line_width.is_some() {
647            self.tessellate_with_ids_vw(path, positions, attributes)
648        } else {
649            self.tessellate_with_ids_fw(path, positions, attributes)
650        }
651    }
652
653    fn tessellate_with_ids_vw(
654        mut self,
655        path: impl IntoIterator<Item = IdEvent>,
656        positions: &impl PositionStore,
657        attributes: &dyn AttributeStore,
658    ) -> TessellationResult {
659        let base_width = self.options.line_width;
660        let attrib_index = self.options.variable_line_width.unwrap();
661
662        let mut validator = DebugValidator::new();
663
664        let mut current_endpoint = EndpointId(u32::MAX);
665        let mut current_position = point(f32::NAN, f32::NAN);
666
667        for evt in path.into_iter() {
668            match evt {
669                IdEvent::Begin { at } => {
670                    validator.begin();
671                    let half_width = base_width * attributes.get(at)[attrib_index] * 0.5;
672                    current_endpoint = at;
673                    current_position = positions.get_endpoint(at);
674                    self.may_need_empty_cap = false;
675                    self.step(
676                        EndpointData {
677                            position: current_position,
678                            half_width,
679                            advancement: self.sub_path_start_advancement,
680                            line_join: self.options.line_join,
681                            src: VertexSource::Endpoint { id: at },
682                            ..Default::default()
683                        },
684                        attributes,
685                    );
686                }
687                IdEvent::Line { to, .. } => {
688                    validator.edge();
689                    let half_width = base_width * attributes.get(to)[attrib_index] * 0.5;
690                    current_endpoint = to;
691                    current_position = positions.get_endpoint(to);
692                    self.step(
693                        EndpointData {
694                            position: current_position,
695                            half_width,
696                            line_join: self.options.line_join,
697                            src: VertexSource::Endpoint { id: to },
698                            ..Default::default()
699                        },
700                        attributes,
701                    );
702                }
703                IdEvent::Quadratic { ctrl, to, .. } => {
704                    validator.edge();
705                    let start_width = base_width * attributes.get(current_endpoint)[attrib_index];
706                    let end_width = base_width * attributes.get(to)[attrib_index];
707
708                    let from = current_endpoint;
709                    let from_pos = current_position;
710                    current_endpoint = to;
711                    current_position = positions.get_endpoint(to);
712
713                    self.quadratic_bezier_to(
714                        &QuadraticBezierSegment {
715                            from: from_pos,
716                            ctrl: positions.get_control_point(ctrl),
717                            to: current_position,
718                        },
719                        from,
720                        to,
721                        start_width,
722                        end_width,
723                        attributes,
724                    );
725                }
726                IdEvent::Cubic {
727                    ctrl1, ctrl2, to, ..
728                } => {
729                    validator.edge();
730
731                    let start_width = base_width * attributes.get(current_endpoint)[attrib_index];
732                    let end_width = base_width * attributes.get(to)[attrib_index];
733
734                    let from = current_endpoint;
735                    let from_pos = current_position;
736                    current_endpoint = to;
737                    current_position = positions.get_endpoint(to);
738
739                    self.cubic_bezier_to(
740                        &CubicBezierSegment {
741                            from: from_pos,
742                            ctrl1: positions.get_control_point(ctrl1),
743                            ctrl2: positions.get_control_point(ctrl2),
744                            to: current_position,
745                        },
746                        from,
747                        to,
748                        start_width,
749                        end_width,
750                        attributes,
751                    );
752                }
753                IdEvent::End { close, .. } => {
754                    validator.end();
755                    self.end(close, attributes);
756                }
757            }
758
759            if let Some(err) = self.error {
760                self.output.abort_geometry();
761                return Err(err);
762            }
763        }
764
765        validator.build();
766        self.build()
767    }
768
769    fn tessellate_with_ids_fw(
770        mut self,
771        path: impl IntoIterator<Item = IdEvent>,
772        positions: &impl PositionStore,
773        attributes: &dyn AttributeStore,
774    ) -> TessellationResult {
775        let mut validator = DebugValidator::new();
776
777        let mut current_endpoint = EndpointId(u32::MAX);
778        let mut current_position = point(f32::NAN, f32::NAN);
779
780        let half_width = self.options.line_width * 0.5;
781
782        for evt in path.into_iter() {
783            match evt {
784                IdEvent::Begin { at } => {
785                    validator.begin();
786                    current_endpoint = at;
787                    current_position = positions.get_endpoint(at);
788                    self.may_need_empty_cap = false;
789                    self.fixed_width_step(
790                        EndpointData {
791                            position: current_position,
792                            half_width,
793                            advancement: self.sub_path_start_advancement,
794                            line_join: self.options.line_join,
795                            src: VertexSource::Endpoint { id: at },
796                            ..Default::default()
797                        },
798                        attributes,
799                    );
800                }
801                IdEvent::Line { to, .. } => {
802                    validator.edge();
803                    current_endpoint = to;
804                    current_position = positions.get_endpoint(to);
805                    self.fixed_width_step(
806                        EndpointData {
807                            position: current_position,
808                            half_width,
809                            line_join: self.options.line_join,
810                            src: VertexSource::Endpoint { id: to },
811                            ..Default::default()
812                        },
813                        attributes,
814                    );
815                }
816                IdEvent::Quadratic { ctrl, to, .. } => {
817                    validator.edge();
818                    let from = current_endpoint;
819                    let from_pos = current_position;
820                    current_endpoint = to;
821                    current_position = positions.get_endpoint(to);
822
823                    self.quadratic_bezier_to_fw(
824                        &QuadraticBezierSegment {
825                            from: from_pos,
826                            ctrl: positions.get_control_point(ctrl),
827                            to: current_position,
828                        },
829                        from,
830                        to,
831                        attributes,
832                    );
833                }
834                IdEvent::Cubic {
835                    ctrl1, ctrl2, to, ..
836                } => {
837                    validator.edge();
838                    let from = current_endpoint;
839                    let from_pos = current_position;
840                    current_endpoint = to;
841                    current_position = positions.get_endpoint(to);
842
843                    self.cubic_bezier_to_fw(
844                        &CubicBezierSegment {
845                            from: from_pos,
846                            ctrl1: positions.get_control_point(ctrl1),
847                            ctrl2: positions.get_control_point(ctrl2),
848                            to: current_position,
849                        },
850                        from,
851                        to,
852                        attributes,
853                    );
854                }
855                IdEvent::End { close, .. } => {
856                    validator.end();
857                    self.end(close, attributes);
858                }
859            }
860
861            if let Some(err) = self.error {
862                self.output.abort_geometry();
863                return Err(err);
864            }
865        }
866
867        validator.build();
868        self.build()
869    }
870
871    /// Compute the tessellation from a path iterator.
872    pub(crate) fn tessellate_fw(
873        mut self,
874        input: impl IntoIterator<Item = PathEvent>,
875    ) -> TessellationResult {
876        // Ensure we use the fixed line width code paths since we don't have
877        // custom attributes to get the line width from;
878        self.options.variable_line_width = None;
879
880        let mut validator = DebugValidator::new();
881
882        let mut id = EndpointId(0);
883        let mut current_position = point(f32::NAN, f32::NAN);
884
885        for evt in input {
886            match evt {
887                PathEvent::Begin { at } => {
888                    validator.begin();
889                    current_position = at;
890                    self.begin_fw(at, id, &());
891                    id.0 += 1;
892                }
893                PathEvent::Line { to, .. } => {
894                    validator.edge();
895                    current_position = to;
896                    self.line_to_fw(to, id, &());
897                    id.0 += 1;
898                }
899                PathEvent::Quadratic { ctrl, to, .. } => {
900                    validator.edge();
901
902                    let from = current_position;
903                    current_position = to;
904                    let prev_id = EndpointId(id.0 - 1);
905
906                    self.quadratic_bezier_to_fw(
907                        &QuadraticBezierSegment { from, ctrl, to },
908                        prev_id,
909                        id,
910                        &(),
911                    );
912
913                    id.0 += 1;
914                }
915                PathEvent::Cubic {
916                    ctrl1, ctrl2, to, ..
917                } => {
918                    validator.edge();
919                    let prev_id = EndpointId(id.0 - 1);
920
921                    let from = current_position;
922                    current_position = to;
923
924                    self.cubic_bezier_to_fw(
925                        &CubicBezierSegment {
926                            from,
927                            ctrl1,
928                            ctrl2,
929                            to,
930                        },
931                        prev_id,
932                        id,
933                        &(),
934                    );
935
936                    id.0 += 1;
937                }
938                PathEvent::End { close, .. } => {
939                    validator.end();
940                    self.end(close, &());
941                }
942            }
943
944            if let Some(err) = self.error {
945                self.output.abort_geometry();
946                return Err(err);
947            }
948        }
949
950        validator.build();
951        self.build()
952    }
953
954    pub(crate) fn begin(
955        &mut self,
956        position: Point,
957        endpoint: EndpointId,
958        width: f32,
959        attributes: &dyn AttributeStore,
960    ) {
961        self.may_need_empty_cap = false;
962        let half_width = width * 0.5;
963        self.step(
964            EndpointData {
965                position,
966                half_width,
967                advancement: self.sub_path_start_advancement,
968                line_join: self.options.line_join,
969                src: VertexSource::Endpoint { id: endpoint },
970                ..Default::default()
971            },
972            attributes,
973        );
974    }
975
976    pub(crate) fn line_to(
977        &mut self,
978        to: Point,
979        endpoint: EndpointId,
980        width: f32,
981        attributes: &dyn AttributeStore,
982    ) {
983        let half_width = width * 0.5;
984        self.step(
985            EndpointData {
986                position: to,
987                half_width,
988                line_join: self.options.line_join,
989                src: VertexSource::Endpoint { id: endpoint },
990                ..Default::default()
991            },
992            attributes,
993        );
994    }
995
996    pub(crate) fn quadratic_bezier_to(
997        &mut self,
998        curve: &QuadraticBezierSegment<f32>,
999        from_id: EndpointId,
1000        to_id: EndpointId,
1001        start_width: f32,
1002        end_width: f32,
1003        attributes: &dyn AttributeStore,
1004    ) {
1005        flatten_quad(
1006            curve,
1007            self.options.tolerance,
1008            &mut |position, t, is_flattening_step| {
1009                let src = if t == 1.0 {
1010                    VertexSource::Endpoint { id: to_id }
1011                } else {
1012                    VertexSource::Edge {
1013                        from: from_id,
1014                        to: to_id,
1015                        t,
1016                    }
1017                };
1018
1019                self.step(
1020                    EndpointData {
1021                        position,
1022                        half_width: (start_width * (1.0 - t) + end_width * t) * 0.5,
1023                        line_join: self.options.line_join,
1024                        src,
1025                        is_flattening_step,
1026                        ..Default::default()
1027                    },
1028                    attributes,
1029                );
1030            },
1031        );
1032    }
1033
1034    pub(crate) fn cubic_bezier_to(
1035        &mut self,
1036        curve: &CubicBezierSegment<f32>,
1037        from_id: EndpointId,
1038        to_id: EndpointId,
1039        start_width: f32,
1040        end_width: f32,
1041        attributes: &dyn AttributeStore,
1042    ) {
1043        curve.for_each_flattened_with_t(self.options.tolerance, &mut |line, t| {
1044            let is_flattening_step = t.end != 1.0;
1045            let src = if is_flattening_step {
1046                VertexSource::Edge {
1047                    from: from_id,
1048                    to: to_id,
1049                    t: t.end,
1050                }
1051            } else {
1052                VertexSource::Endpoint { id: to_id }
1053            };
1054
1055            self.step(
1056                EndpointData {
1057                    position: line.to,
1058                    half_width: (start_width * (1.0 - t.end) + end_width * t.end) * 0.5,
1059                    line_join: self.options.line_join,
1060                    src,
1061                    is_flattening_step,
1062                    ..Default::default()
1063                },
1064                attributes,
1065            );
1066        });
1067    }
1068
1069    pub(crate) fn begin_fw(
1070        &mut self,
1071        position: Point,
1072        endpoint: EndpointId,
1073        attributes: &dyn AttributeStore,
1074    ) {
1075        self.may_need_empty_cap = false;
1076        self.fixed_width_step(
1077            EndpointData {
1078                position,
1079                half_width: self.options.line_width * 0.5,
1080                advancement: self.sub_path_start_advancement,
1081                line_join: self.options.line_join,
1082                src: VertexSource::Endpoint { id: endpoint },
1083                ..Default::default()
1084            },
1085            attributes,
1086        );
1087    }
1088
1089    pub(crate) fn line_to_fw(
1090        &mut self,
1091        to: Point,
1092        endpoint: EndpointId,
1093        attributes: &dyn AttributeStore,
1094    ) {
1095        let half_width = self.options.line_width * 0.5;
1096        self.fixed_width_step(
1097            EndpointData {
1098                position: to,
1099                half_width,
1100                line_join: self.options.line_join,
1101                src: VertexSource::Endpoint { id: endpoint },
1102                ..Default::default()
1103            },
1104            attributes,
1105        );
1106    }
1107
1108    pub(crate) fn quadratic_bezier_to_fw(
1109        &mut self,
1110        curve: &QuadraticBezierSegment<f32>,
1111        from_id: EndpointId,
1112        to_id: EndpointId,
1113        attributes: &dyn AttributeStore,
1114    ) {
1115        let half_width = self.options.line_width * 0.5;
1116        flatten_quad(
1117            curve,
1118            self.options.tolerance,
1119            &mut |position, t, is_flattening_step| {
1120                let src = if t == 1.0 {
1121                    VertexSource::Endpoint { id: to_id }
1122                } else {
1123                    VertexSource::Edge {
1124                        from: from_id,
1125                        to: to_id,
1126                        t,
1127                    }
1128                };
1129
1130                self.fixed_width_step(
1131                    EndpointData {
1132                        position,
1133                        half_width,
1134                        line_join: self.options.line_join,
1135                        src,
1136                        is_flattening_step,
1137                        ..Default::default()
1138                    },
1139                    attributes,
1140                );
1141            },
1142        );
1143    }
1144
1145    pub(crate) fn cubic_bezier_to_fw(
1146        &mut self,
1147        curve: &CubicBezierSegment<f32>,
1148        from_id: EndpointId,
1149        to_id: EndpointId,
1150        attributes: &dyn AttributeStore,
1151    ) {
1152        let half_width = self.options.line_width * 0.5;
1153        curve.for_each_flattened_with_t(self.options.tolerance, &mut |line, t| {
1154            let is_flattening_step = t.end != 1.0;
1155            let src = if is_flattening_step {
1156                VertexSource::Edge {
1157                    from: from_id,
1158                    to: to_id,
1159                    t: t.end,
1160                }
1161            } else {
1162                VertexSource::Endpoint { id: to_id }
1163            };
1164
1165            self.fixed_width_step(
1166                EndpointData {
1167                    position: line.to,
1168                    half_width,
1169                    line_join: self.options.line_join,
1170                    src,
1171                    is_flattening_step,
1172                    ..Default::default()
1173                },
1174                attributes,
1175            );
1176        });
1177    }
1178
1179    pub(crate) fn end(&mut self, close: bool, attributes: &dyn AttributeStore) {
1180        self.may_need_empty_cap |= close && self.point_buffer.count() == 1;
1181        let e = if close && self.point_buffer.count() > 2 {
1182            self.close(attributes)
1183        } else {
1184            self.end_with_caps(attributes)
1185        };
1186
1187        if let Err(e) = e {
1188            self.error(e);
1189        }
1190
1191        self.point_buffer.clear();
1192        self.firsts.clear();
1193    }
1194
1195    pub(crate) fn build(self) -> TessellationResult {
1196        if let Some(err) = self.error {
1197            self.output.abort_geometry();
1198            return Err(err);
1199        }
1200
1201        self.output.end_geometry();
1202
1203        Ok(())
1204    }
1205
1206    fn close(&mut self, attributes: &dyn AttributeStore) -> Result<(), TessellationError> {
1207        if self.point_buffer.count() == 1 {
1208            self.tessellate_empty_cap(attributes)?;
1209        }
1210
1211        if self.point_buffer.count() <= 2 {
1212            return Ok(());
1213        }
1214
1215        assert!(!self.firsts.is_empty());
1216
1217        let mut p = self.firsts[0];
1218        // Make sure we re-compute the advancement instead of using the one found at the
1219        // beginning of the sub-path.
1220        let advancement = p.advancement;
1221        p.advancement = f32::NAN;
1222        let segment_added = if self.options.variable_line_width.is_some() {
1223            self.step_impl(p, attributes)?
1224        } else {
1225            self.fixed_width_step_impl(p, attributes)?
1226        };
1227
1228        if !segment_added {
1229            // The closing code relies on not skipping the edge from the first to
1230            // the second point. In most case this is ensured by points not being
1231            // added to self.firsts if they are not far enough apart. However there
1232            // could still be a situation where the last point is placed in such
1233            // a way that it is within merge range of both the first and second
1234            // points.
1235            // Fixing the position up ensures that even though we skip the edge to
1236            // the first point, we don't skip the edge to the second one.
1237            self.point_buffer.last_mut().position = p.position;
1238        }
1239
1240        if self.firsts.len() >= 2 {
1241            let p2 = self.firsts[1];
1242            if self.options.variable_line_width.is_some() {
1243                self.step_impl(p2, attributes)?;
1244            } else {
1245                self.fixed_width_step_impl(p2, attributes)?;
1246            }
1247
1248            let (p0, p1) = self.point_buffer.last_two_mut();
1249            // TODO: This is hacky: We re-create the first two vertices on the edge towards the second endpoint
1250            // so that they use the advancement value of the start of the sub path instead of the end of the
1251            // sub path as computed in the step_impl above.
1252            self.vertex.src = p0.src;
1253            self.vertex.position_on_path = p0.position;
1254            self.vertex.half_width = p0.half_width;
1255            self.vertex.advancement = advancement;
1256            self.vertex.buffer_is_valid = false;
1257            for side in 0..2 {
1258                self.vertex.side = if side == SIDE_POSITIVE {
1259                    Side::Positive
1260                } else {
1261                    Side::Negative
1262                };
1263                self.vertex.normal = if let Some(pos) = p0.side_points[side].single_vertex {
1264                    (pos - p0.position) / p0.half_width
1265                } else {
1266                    (p0.side_points[side].next - p0.position) / p0.half_width
1267                };
1268
1269                let vertex = self
1270                    .output
1271                    .add_stroke_vertex(StrokeVertex(&mut self.vertex, attributes))?;
1272                p0.side_points[side].next_vertex = vertex;
1273            }
1274
1275            add_edge_triangles(p0, p1, self.output);
1276        }
1277
1278        Ok(())
1279    }
1280
1281    #[cold]
1282    fn tessellate_empty_cap(
1283        &mut self,
1284        attributes: &dyn AttributeStore,
1285    ) -> Result<(), TessellationError> {
1286        let point = self.point_buffer.get(0);
1287        self.vertex.advancement = point.advancement;
1288        self.vertex.src = point.src;
1289        self.vertex.half_width = point.half_width;
1290
1291        match self.options.start_cap {
1292            LineCap::Square => {
1293                // Even if there is no edge, if we are using square caps we have to place a square
1294                // at the current position.
1295                crate::stroke::tessellate_empty_square_cap(
1296                    point.position,
1297                    &mut self.vertex,
1298                    attributes,
1299                    self.output,
1300                )?;
1301            }
1302            LineCap::Round => {
1303                // Same thing for round caps.
1304                crate::stroke::tessellate_empty_round_cap(
1305                    point.position,
1306                    &self.options,
1307                    &mut self.vertex,
1308                    attributes,
1309                    self.output,
1310                )?;
1311            }
1312            _ => {}
1313        }
1314
1315        Ok(())
1316    }
1317
1318    fn points_are_too_close(&self, p0: Point, p1: Point) -> bool {
1319        (p0 - p1).square_length() < self.square_merge_threshold
1320    }
1321
1322    fn end_with_caps(&mut self, attributes: &dyn AttributeStore) -> Result<(), TessellationError> {
1323        let count = self.point_buffer.count();
1324
1325        if self.may_need_empty_cap && count == 1 {
1326            return self.tessellate_empty_cap(attributes);
1327        }
1328
1329        if count >= 2 {
1330            // Last edge.
1331
1332            // Add a fake fake point p2 aligned with p0 and p1 so that we can tessellate
1333            // the join for p1.
1334            let (p0, p1) = self.point_buffer.last_two_mut();
1335            let mut p0 = *p0;
1336            let mut p1 = *p1;
1337
1338            if self.options.variable_line_width.is_none() {
1339                // TODO: this is a bit hacky: with the fixed width fast path we only compute the
1340                // side point positions for joins, so we haven't gotten to that in the case of
1341                // the last edge.
1342                let tangent = (p1.position - p0.position).normalize();
1343                let n = vector(-tangent.y, tangent.x) * p1.half_width;
1344                p1.side_points[SIDE_POSITIVE].prev = p1.position + n;
1345                p1.side_points[SIDE_NEGATIVE].prev = p1.position - n;
1346            }
1347
1348            let is_first = count == 2;
1349            tessellate_last_edge(
1350                &p0,
1351                &mut p1,
1352                is_first,
1353                &self.options,
1354                &mut self.vertex,
1355                attributes,
1356                self.output,
1357            )?;
1358
1359            self.sub_path_start_advancement = p1.advancement;
1360
1361            if count > 2 {
1362                p0 = self.firsts[0];
1363                p1 = self.firsts[1];
1364            }
1365
1366            // First edge.
1367            tessellate_first_edge(
1368                &mut p0,
1369                &p1,
1370                &self.options,
1371                &mut self.vertex,
1372                attributes,
1373                self.output,
1374            )?;
1375        }
1376
1377        Ok(())
1378    }
1379
1380    pub(crate) fn step_impl(
1381        &mut self,
1382        mut next: EndpointData,
1383        attributes: &dyn AttributeStore,
1384    ) -> Result<bool, TessellationError> {
1385        let count = self.point_buffer.count();
1386
1387        debug_assert!(self.options.variable_line_width.is_some());
1388
1389        if count > 0 && self.points_are_too_close(self.point_buffer.last().position, next.position)
1390        {
1391            if count == 1 {
1392                // move-to followed by empty segment and end of paths means we have to generate
1393                // an empty cap.
1394                self.may_need_empty_cap = true;
1395            }
1396            // TODO: should do something like:
1397            // - add the endpoint
1398            // - only allow two consecutive endpoints at the same position
1399            // - if the join type is round, maybe tessellate a round cap for the largest one
1400            // TODO: we should make sure that if the next point is an endpoint and the previous
1401            // is on an edge, we discard the previous instead of the next (to get the correct join)
1402            return Ok(false);
1403        }
1404
1405        if count > 0 {
1406            let join = self.point_buffer.last_mut();
1407            // Compute the position of the vertices that act as reference the edge between
1408            // p0 and next
1409            if !join.is_flattening_step || !next.is_flattening_step {
1410                compute_edge_attachment_positions(join, &mut next);
1411            }
1412        }
1413
1414        let mut skip = false;
1415        if count > 1 {
1416            let (prev, join) = self.point_buffer.last_two_mut();
1417            nan_check!(join.advancement);
1418            nan_check!(prev.advancement);
1419
1420            self.vertex.src = join.src;
1421            self.vertex.position_on_path = join.position;
1422            self.vertex.half_width = join.half_width;
1423            self.vertex.advancement = join.advancement;
1424            self.vertex.buffer_is_valid = false;
1425            // We can take the fast path if the join is a flattening step and
1426            // not at a sharp turn.
1427            let fast_path = if join.is_flattening_step {
1428                let v0 = join.position - prev.position;
1429                let v1 = next.position - join.position;
1430                v0.dot(v1) > 0.0
1431            } else {
1432                false
1433            };
1434            if fast_path {
1435                join.line_join = LineJoin::Miter;
1436                // can fast-path.
1437                skip = flattened_step(
1438                    prev,
1439                    join,
1440                    &mut next,
1441                    &mut self.vertex,
1442                    attributes,
1443                    self.output,
1444                )?;
1445            } else {
1446                compute_join_side_positions(
1447                    prev,
1448                    join,
1449                    &next,
1450                    self.options.miter_limit,
1451                    SIDE_POSITIVE,
1452                );
1453                compute_join_side_positions(
1454                    prev,
1455                    join,
1456                    &next,
1457                    self.options.miter_limit,
1458                    SIDE_NEGATIVE,
1459                );
1460
1461                // Prevent folding when the other side is concave.
1462                if join.side_points[SIDE_POSITIVE].single_vertex.is_some() {
1463                    join.fold[SIDE_NEGATIVE] = false;
1464                }
1465                if join.side_points[SIDE_NEGATIVE].single_vertex.is_some() {
1466                    join.fold[SIDE_POSITIVE] = false;
1467                }
1468
1469                add_join_base_vertices(
1470                    join,
1471                    &mut self.vertex,
1472                    attributes,
1473                    self.output,
1474                    Side::Negative,
1475                )?;
1476                add_join_base_vertices(
1477                    join,
1478                    &mut self.vertex,
1479                    attributes,
1480                    self.output,
1481                    Side::Positive,
1482                )?;
1483            }
1484
1485            if !skip {
1486                if count > 2 {
1487                    add_edge_triangles(prev, join, self.output);
1488                }
1489
1490                tessellate_join(
1491                    join,
1492                    &self.options,
1493                    &mut self.vertex,
1494                    attributes,
1495                    self.output,
1496                )?;
1497
1498                if count == 2 {
1499                    self.firsts.push(*prev);
1500                    self.firsts.push(*join);
1501                }
1502            }
1503        }
1504
1505        if skip {
1506            self.point_buffer.replace_last(next);
1507        } else {
1508            self.point_buffer.push(next);
1509        }
1510
1511        Ok(true)
1512    }
1513
1514    #[cfg_attr(feature = "profiling", inline(never))]
1515    pub(crate) fn step(&mut self, next: EndpointData, attributes: &dyn AttributeStore) {
1516        if let Err(e) = self.step_impl(next, attributes) {
1517            self.error(e);
1518        }
1519    }
1520
1521    pub(crate) fn fixed_width_step_impl(
1522        &mut self,
1523        mut next: EndpointData,
1524        attributes: &dyn AttributeStore,
1525    ) -> Result<bool, TessellationError> {
1526        let count = self.point_buffer.count();
1527
1528        debug_assert!(self.options.variable_line_width.is_none());
1529
1530        if count > 0 {
1531            if self.points_are_too_close(self.point_buffer.last().position, next.position) {
1532                if count == 1 {
1533                    self.may_need_empty_cap = true;
1534                }
1535                return Ok(false);
1536            }
1537
1538            if count == 1 {
1539                // TODO: this is a bit hacky: with the fixed width fast path we only compute the
1540                // side point positions for joins but we'll need it for the first point when we get
1541                // back to tessellating the first edge.
1542                let first = self.point_buffer.last_mut();
1543                let edge = next.position - first.position;
1544                let length = edge.length();
1545
1546                if next.advancement.is_nan() {
1547                    nan_check!(first.advancement);
1548                    nan_check!(length);
1549                    next.advancement = first.advancement + length;
1550                }
1551
1552                let tangent = edge / length;
1553                let n = vector(-tangent.y, tangent.x) * next.half_width;
1554                first.side_points[SIDE_POSITIVE].next = first.position + n;
1555                first.side_points[SIDE_NEGATIVE].next = first.position - n;
1556            }
1557        }
1558
1559        if count > 1 {
1560            let (prev, join) = self.point_buffer.last_two_mut();
1561
1562            self.vertex.src = join.src;
1563            self.vertex.position_on_path = join.position;
1564            self.vertex.half_width = join.half_width;
1565            self.vertex.buffer_is_valid = false;
1566            // We can take the fast path if the join is a flattening step and
1567            // not at a sharp turn.
1568            let fast_path = if join.is_flattening_step {
1569                let v0 = join.position - prev.position;
1570                let v1 = next.position - join.position;
1571                v0.dot(v1) > 0.0
1572            } else {
1573                false
1574            };
1575            if fast_path {
1576                join.line_join = LineJoin::Miter;
1577                // can fast-path.
1578                flattened_step(
1579                    prev,
1580                    join,
1581                    &mut next,
1582                    &mut self.vertex,
1583                    attributes,
1584                    self.output,
1585                )?;
1586            } else {
1587                compute_join_side_positions_fixed_width(
1588                    prev,
1589                    join,
1590                    &next,
1591                    self.options.miter_limit,
1592                    &mut self.vertex,
1593                )?;
1594
1595                add_join_base_vertices(
1596                    join,
1597                    &mut self.vertex,
1598                    attributes,
1599                    self.output,
1600                    Side::Negative,
1601                )?;
1602                add_join_base_vertices(
1603                    join,
1604                    &mut self.vertex,
1605                    attributes,
1606                    self.output,
1607                    Side::Positive,
1608                )?;
1609            }
1610
1611            if count > 2 {
1612                add_edge_triangles(prev, join, self.output);
1613            }
1614
1615            tessellate_join(
1616                join,
1617                &self.options,
1618                &mut self.vertex,
1619                attributes,
1620                self.output,
1621            )?;
1622
1623            if count == 2 {
1624                self.firsts.push(*prev);
1625                self.firsts.push(*join);
1626            }
1627        }
1628
1629        self.point_buffer.push(next);
1630
1631        Ok(true)
1632    }
1633
1634    #[cfg_attr(feature = "profiling", inline(never))]
1635    pub(crate) fn fixed_width_step(&mut self, next: EndpointData, attributes: &dyn AttributeStore) {
1636        if let Err(e) = self.fixed_width_step_impl(next, attributes) {
1637            self.error(e);
1638        }
1639    }
1640}
1641
1642#[cfg_attr(feature = "profiling", inline(never))]
1643fn compute_join_side_positions_fixed_width(
1644    prev: &EndpointData,
1645    join: &mut EndpointData,
1646    next: &EndpointData,
1647    miter_limit: f32,
1648    vertex: &mut StrokeVertexData,
1649) -> Result<(), TessellationError> {
1650    let prev_tangent = join.position - prev.position;
1651    let next_tangent = next.position - join.position;
1652    let prev_length = prev_tangent.length();
1653    let next_length = next_tangent.length();
1654    let prev_tangent = prev_tangent / prev_length;
1655    let next_tangent = next_tangent / next_length;
1656
1657    if join.advancement.is_nan() {
1658        nan_check!(prev.advancement);
1659        nan_check!(prev_length);
1660        join.advancement = prev.advancement + prev_length;
1661    }
1662    vertex.advancement = join.advancement;
1663
1664    let normal = compute_normal(prev_tangent, next_tangent);
1665    let (front_side, front_normal) = if prev_tangent.cross(next_tangent) >= 0.0 {
1666        (SIDE_NEGATIVE, -normal)
1667    } else {
1668        (SIDE_POSITIVE, normal)
1669    };
1670
1671    let back_side = 1 - front_side;
1672
1673    // The folding code path's threshold is a tad too eager when dealing with flattened curves due to
1674    // how flattening can create segments that are much smaller than the line width. In practice the
1675    // curve tends to cover the spike of the back vertex in a lot of cases. Unfortunately at the moment
1676    // the stroke tessellators force miter joins to be clipped in the folding case. Work around it by
1677    // disabling folding when a miter join is not clipped. That's often an indication that the join is not
1678    // sharp enough to generate a huge spike, although the spiking artifact will show up in some cases.
1679    // Better solutions could involve:
1680    //  - Using the distance between the curve control point and endpoint instead of the previous flattened
1681    //    segment in the spike detection heuristic.
1682    //  - Implement proper miter joins when the folding code is active.
1683    //  - Better integrating curves with the tessellator instead of only considering the previous and next
1684    //    flattened segment.
1685    let extruded_normal = front_normal * vertex.half_width;
1686    let unclipped_miter = (join.line_join == LineJoin::Miter
1687        || join.line_join == LineJoin::MiterClip)
1688        && !miter_limit_is_exceeded(front_normal, miter_limit);
1689
1690    let mut fold = false;
1691    let angle_is_sharp = next_tangent.dot(prev_tangent) < 0.0;
1692    if !unclipped_miter && angle_is_sharp {
1693        // Project the back vertex on the previous and next edges and subtract the edge length
1694        // to see if the back vertex ends up further than the opposite endpoint of the edge.
1695        let d_next = extruded_normal.dot(-next_tangent) - next_length;
1696        let d_prev = extruded_normal.dot(prev_tangent) - prev_length;
1697        if d_next.min(d_prev) > 0.0 || normal.square_length() < 1e-5 {
1698            // Case of an overlapping stroke. In order to prevent the back vertex from creating a
1699            // spike outside of the stroke, we simply don't create it and we'll "fold" the join
1700            // instead.
1701            join.fold[front_side] = true;
1702            fold = true;
1703        }
1704    }
1705
1706    let n0 = vector(-prev_tangent.y, prev_tangent.x) * vertex.half_width;
1707    let n1 = vector(-next_tangent.y, next_tangent.x) * vertex.half_width;
1708    join.side_points[SIDE_POSITIVE].prev = join.position + n0;
1709    join.side_points[SIDE_POSITIVE].next = join.position + n1;
1710    join.side_points[SIDE_NEGATIVE].prev = join.position - n0;
1711    join.side_points[SIDE_NEGATIVE].next = join.position - n1;
1712
1713    if !fold {
1714        let miter_pos = [
1715            join.position + normal * vertex.half_width,
1716            join.position - normal * vertex.half_width,
1717        ];
1718
1719        join.side_points[back_side].single_vertex = Some(miter_pos[back_side]);
1720        if unclipped_miter {
1721            join.side_points[front_side].single_vertex = Some(miter_pos[front_side]);
1722        } else if join.line_join == LineJoin::MiterClip {
1723            let n0 = join.side_points[front_side].prev - join.position;
1724            let n1 = join.side_points[front_side].next - join.position;
1725            let (prev_normal, next_normal) =
1726                get_clip_intersections(n0, n1, front_normal, miter_limit * 0.5 * vertex.half_width);
1727            join.side_points[front_side].prev = join.position + prev_normal;
1728            join.side_points[front_side].next = join.position + next_normal;
1729        }
1730    }
1731
1732    Ok(())
1733}
1734
1735// A fast path for when we know we are in a flattened curve, taking
1736// advantage of knowing that we don't have to handle special joins.
1737//
1738// Returning Ok(true) means we are in a weird looking case with small edges
1739// and varying line width causing the join to fold back. When this is the
1740// case we are better off skipping this join.
1741// "M 170 150 60 Q 215 120 240 140 2" is an example of this.
1742#[cfg_attr(feature = "profiling", inline(never))]
1743fn flattened_step(
1744    prev: &mut EndpointData,
1745    join: &mut EndpointData,
1746    next: &mut EndpointData,
1747    vertex: &mut StrokeVertexData,
1748    attributes: &dyn AttributeStore,
1749    output: &mut dyn StrokeGeometryBuilder,
1750) -> Result<bool, TessellationError> {
1751    let prev_edge = join.position - prev.position;
1752    let prev_length = prev_edge.length();
1753    let prev_tangent = prev_edge / prev_length;
1754    let next_edge = next.position - join.position;
1755    let next_length = next_edge.length();
1756    let next_tangent = next_edge / next_length;
1757    let normal = compute_normal(prev_tangent, next_tangent);
1758
1759    if join.advancement.is_nan() {
1760        nan_check!(prev.advancement);
1761        nan_check!(prev_length);
1762        join.advancement = prev.advancement + prev_length;
1763    }
1764
1765    if next.advancement.is_nan() {
1766        nan_check!(join.advancement);
1767        nan_check!(next_length);
1768        next.advancement = join.advancement + next_length;
1769    }
1770
1771    vertex.advancement = join.advancement;
1772
1773    let p0 = join.position + normal * vertex.half_width;
1774    nan_check!(p0);
1775    join.side_points[SIDE_POSITIVE].prev = p0;
1776    join.side_points[SIDE_POSITIVE].next = p0;
1777    join.side_points[SIDE_POSITIVE].single_vertex = Some(p0);
1778
1779    let p1 = join.position - normal * vertex.half_width;
1780    nan_check!(p1);
1781    join.side_points[SIDE_NEGATIVE].prev = p1;
1782    join.side_points[SIDE_NEGATIVE].next = p1;
1783    join.side_points[SIDE_NEGATIVE].single_vertex = Some(p1);
1784
1785    let v0 = p0 - prev.side_points[SIDE_POSITIVE].next;
1786    let v1 = p1 - prev.side_points[SIDE_NEGATIVE].next;
1787    if prev_edge.dot(v0) < 0.0 && prev_edge.dot(v1) < 0.0 {
1788        return Ok(true);
1789    }
1790
1791    vertex.normal = normal;
1792    vertex.side = Side::Positive;
1793    let pos_vertex = output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
1794
1795    vertex.normal = -normal;
1796    vertex.side = Side::Negative;
1797    let neg_vertex = output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
1798
1799    join.side_points[SIDE_POSITIVE].prev_vertex = pos_vertex;
1800    join.side_points[SIDE_POSITIVE].next_vertex = pos_vertex;
1801
1802    join.side_points[SIDE_NEGATIVE].prev_vertex = neg_vertex;
1803    join.side_points[SIDE_NEGATIVE].next_vertex = neg_vertex;
1804
1805    Ok(false)
1806}
1807
1808#[cfg_attr(feature = "profiling", inline(never))]
1809fn compute_edge_attachment_positions(p0: &mut EndpointData, p1: &mut EndpointData) {
1810    let edge = p1.position - p0.position;
1811    let d = edge.length();
1812    let edge_angle = edge.angle_from_x_axis().radians;
1813
1814    // Extra angle produced by the varying stroke width.
1815    // sin(vwidth_angle) = (hw1 - hw0) / d
1816    let sin_vwidth_angle = (p1.half_width - p0.half_width) / d;
1817    let mut vwidth_angle = sin_vwidth_angle.asin();
1818    // If the distance between the joins (d) is smaller than either of the half
1819    // widths, we end up in a situation where sin_vwidth_angle is not in [-1, 1],
1820    // which causes vwidth_angle to be NaN. Prevent that here for safety's sake
1821    // but it would be better to handle that earlier to do something that looks
1822    // more plausible with round joins.
1823    if vwidth_angle.is_nan() {
1824        vwidth_angle = 0.0;
1825    }
1826
1827    nan_check!(d, p0.half_width, p1.half_width, vwidth_angle);
1828
1829    compute_side_attachment_positions(p0, p1, edge_angle, vwidth_angle, SIDE_POSITIVE);
1830    compute_side_attachment_positions(p0, p1, edge_angle, vwidth_angle, SIDE_NEGATIVE);
1831
1832    if p1.advancement.is_nan() {
1833        nan_check!(p0.advancement, d);
1834        p1.advancement = p0.advancement + d;
1835    }
1836}
1837
1838fn compute_side_attachment_positions(
1839    p0: &mut EndpointData,
1840    p1: &mut EndpointData,
1841    edge_angle: f32,
1842    vwidth_angle: f32,
1843    side: usize,
1844) {
1845    nan_check!(
1846        edge_angle,
1847        vwidth_angle,
1848        p0.position,
1849        p1.position,
1850        p0.half_width,
1851        p1.half_width
1852    );
1853
1854    let nl = side_sign(side);
1855
1856    let normal_angle = edge_angle + nl * (PI * 0.5 + vwidth_angle);
1857    let normal = vector(normal_angle.cos(), normal_angle.sin());
1858
1859    nan_check!(normal);
1860
1861    p0.side_points[side].next = p0.position + normal * p0.half_width;
1862    p1.side_points[side].prev = p1.position + normal * p1.half_width;
1863
1864    nan_check!(p0.side_points[side].next);
1865    nan_check!(p1.side_points[side].prev);
1866}
1867
1868#[cfg_attr(feature = "profiling", inline(never))]
1869fn add_edge_triangles(
1870    p0: &EndpointData,
1871    p1: &EndpointData,
1872    output: &mut dyn StrokeGeometryBuilder,
1873) {
1874    let mut p0_neg = p0.side_points[SIDE_NEGATIVE].next_vertex;
1875    let mut p0_pos = p0.side_points[SIDE_POSITIVE].next_vertex;
1876    let mut p1_neg = p1.side_points[SIDE_NEGATIVE].prev_vertex;
1877    let mut p1_pos = p1.side_points[SIDE_POSITIVE].prev_vertex;
1878
1879    if p0.fold[SIDE_POSITIVE] {
1880        p0_neg = p0.side_points[SIDE_POSITIVE].prev_vertex;
1881    }
1882    if p0.fold[SIDE_NEGATIVE] {
1883        p0_pos = p0.side_points[SIDE_NEGATIVE].prev_vertex;
1884    }
1885    if p1.fold[SIDE_POSITIVE] {
1886        p1_neg = p1.side_points[SIDE_POSITIVE].next_vertex;
1887    }
1888    if p1.fold[SIDE_NEGATIVE] {
1889        p1_pos = p1.side_points[SIDE_NEGATIVE].next_vertex;
1890    }
1891
1892    // TODO: These checks are a temporary workaround, see the issue_894 test below.
1893    if p0_neg == p1_pos {
1894        return;
1895    }
1896
1897    if p0_neg != p0_pos && p0_pos != p1_pos {
1898        output.add_triangle(p0_neg, p0_pos, p1_pos);
1899    }
1900
1901    if p0_neg != p1_neg && p1_pos != p1_neg {
1902        output.add_triangle(p0_neg, p1_pos, p1_neg);
1903    }
1904}
1905
1906#[cfg_attr(feature = "profiling", inline(never))]
1907fn tessellate_join(
1908    join: &mut EndpointData,
1909    options: &StrokeOptions,
1910    vertex: &mut StrokeVertexData,
1911    attributes: &dyn AttributeStore,
1912    output: &mut dyn StrokeGeometryBuilder,
1913) -> Result<(), TessellationError> {
1914    let side_needs_join = [
1915        join.side_points[SIDE_POSITIVE].single_vertex.is_none() && !join.fold[SIDE_NEGATIVE],
1916        join.side_points[SIDE_NEGATIVE].single_vertex.is_none() && !join.fold[SIDE_POSITIVE],
1917    ];
1918
1919    if !join.fold[SIDE_POSITIVE] && !join.fold[SIDE_NEGATIVE] {
1920        // Tessellate the interior of the join.
1921        match side_needs_join {
1922            [true, true] => {
1923                output.add_triangle(
1924                    join.side_points[SIDE_POSITIVE].prev_vertex,
1925                    join.side_points[SIDE_POSITIVE].next_vertex,
1926                    join.side_points[SIDE_NEGATIVE].next_vertex,
1927                );
1928
1929                output.add_triangle(
1930                    join.side_points[SIDE_POSITIVE].prev_vertex,
1931                    join.side_points[SIDE_NEGATIVE].next_vertex,
1932                    join.side_points[SIDE_NEGATIVE].prev_vertex,
1933                );
1934            }
1935            [false, true] => {
1936                output.add_triangle(
1937                    join.side_points[SIDE_NEGATIVE].prev_vertex,
1938                    join.side_points[SIDE_POSITIVE].prev_vertex,
1939                    join.side_points[SIDE_NEGATIVE].next_vertex,
1940                );
1941            }
1942            [true, false] => {
1943                output.add_triangle(
1944                    join.side_points[SIDE_NEGATIVE].prev_vertex,
1945                    join.side_points[SIDE_POSITIVE].prev_vertex,
1946                    join.side_points[SIDE_POSITIVE].next_vertex,
1947                );
1948            }
1949            [false, false] => {}
1950        }
1951    }
1952
1953    // Tessellate the remaining specific shape for convex joins
1954    for side in 0..2 {
1955        if !side_needs_join[side] {
1956            continue;
1957        }
1958
1959        if join.line_join == LineJoin::Round {
1960            tessellate_round_join(join, side, options, vertex, attributes, output)?;
1961        }
1962    }
1963
1964    Ok(())
1965}
1966
1967#[cfg_attr(feature = "profiling", inline(never))]
1968fn tessellate_round_join(
1969    join: &mut EndpointData,
1970    side: usize,
1971    options: &StrokeOptions,
1972    vertex: &mut StrokeVertexData,
1973    attributes: &dyn AttributeStore,
1974    output: &mut dyn StrokeGeometryBuilder,
1975) -> Result<(), TessellationError> {
1976    let center = join.position;
1977    let radius = join.half_width;
1978    let start_normal = join.side_points[side].prev - center;
1979    let end_normal = join.side_points[side].next - center;
1980
1981    let mut start_vertex = join.side_points[side].prev_vertex;
1982    let mut end_vertex = join.side_points[side].next_vertex;
1983
1984    let angle_sign = if side == SIDE_NEGATIVE { 1.0 } else { -1.0 };
1985
1986    let mut start_angle = start_normal.angle_from_x_axis();
1987    let mut diff = start_angle.angle_to(end_normal.angle_from_x_axis());
1988
1989    // if the angle is doesn't have the desired sign, adjust it.
1990    if diff.radians * angle_sign < 0.0 {
1991        diff.radians = angle_sign * (2.0 * PI - diff.radians.abs());
1992    }
1993    let mut end_angle = start_angle + diff;
1994
1995    if side == SIDE_NEGATIVE {
1996        // Flip to keep consistent winding order.
1997        core::mem::swap(&mut start_angle, &mut end_angle);
1998        core::mem::swap(&mut start_vertex, &mut end_vertex);
1999    }
2000
2001    // Compute the required number of subdivisions,
2002    let step = circle_flattening_step(radius, options.tolerance);
2003    let num_segments = (diff.radians.abs() / step).ceil();
2004    let num_subdivisions = num_segments.log2().round() as u32;
2005
2006    vertex.side = if side == SIDE_POSITIVE {
2007        Side::Positive
2008    } else {
2009        Side::Negative
2010    };
2011
2012    crate::stroke::tessellate_arc(
2013        (start_angle.radians, end_angle.radians),
2014        start_vertex,
2015        end_vertex,
2016        num_subdivisions,
2017        vertex,
2018        attributes,
2019        output,
2020    )
2021}
2022
2023#[cfg_attr(feature = "profiling", inline(never))]
2024fn add_join_base_vertices(
2025    join: &mut EndpointData,
2026    vertex: &mut StrokeVertexData,
2027    attributes: &dyn AttributeStore,
2028    output: &mut dyn StrokeGeometryBuilder,
2029    side: Side,
2030) -> Result<(), TessellationError> {
2031    vertex.side = side;
2032
2033    let side = match side {
2034        Side::Positive => SIDE_POSITIVE,
2035        Side::Negative => SIDE_NEGATIVE,
2036    };
2037
2038    if let Some(pos) = join.side_points[side].single_vertex {
2039        vertex.normal = (pos - join.position) / join.half_width;
2040        let vertex = output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
2041        join.side_points[side].prev_vertex = vertex;
2042        join.side_points[side].next_vertex = vertex;
2043    } else {
2044        vertex.normal = (join.side_points[side].prev - join.position) / join.half_width;
2045        let prev_vertex = output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
2046
2047        vertex.normal = (join.side_points[side].next - join.position) / join.half_width;
2048        let next_vertex = output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
2049
2050        join.side_points[side].prev_vertex = prev_vertex;
2051        join.side_points[side].next_vertex = next_vertex;
2052    }
2053
2054    Ok(())
2055}
2056
2057// TODO: the naming is a bit confusing. We do half of the work to compute the join's side positions
2058// in compute_side_attachment_positions.
2059#[cfg_attr(feature = "profiling", inline(never))]
2060fn compute_join_side_positions(
2061    prev: &EndpointData,
2062    join: &mut EndpointData,
2063    next: &EndpointData,
2064    miter_limit: f32,
2065    side: usize,
2066) {
2067    nan_check!(join.position);
2068    nan_check!(prev.side_points[side].next);
2069    nan_check!(join.side_points[side].next);
2070
2071    let sign = side_sign(side);
2072    let v0 = (join.side_points[side].prev - prev.side_points[side].next).normalize();
2073    let v1 = (next.side_points[side].prev - join.side_points[side].next).normalize();
2074    let inward = v0.cross(v1) * sign > 0.0;
2075    let forward = v0.dot(v1) > 0.0;
2076
2077    let normal = compute_normal(v0, v1) * sign;
2078    let path_v0 = (join.position - prev.position).normalize();
2079    let path_v1 = (next.position - join.position).normalize();
2080
2081    nan_check!(v0, v1);
2082
2083    let normal_same_side = (v0 + v1).dot(path_v0 + path_v1) >= 0.0;
2084
2085    // We must watch out for special cases where the previous or next edge is small relative
2086    // to the line width. Our workaround only applies to "sharp" angles (more than 90 degrees).
2087    let angle_is_sharp = inward && !forward && normal_same_side;
2088    if angle_is_sharp {
2089        // Project the back vertex on the previous and next edges and subtract the edge length
2090        // to see if the back vertex ends up further than the opposite endpoint of the edge.
2091        let extruded_normal = normal * join.half_width;
2092        let prev_length = join.advancement - prev.advancement;
2093        let next_length = next.advancement - join.advancement;
2094        let d_next = extruded_normal.dot(v1) - next_length;
2095        let d_prev = extruded_normal.dot(-v0) - prev_length;
2096
2097        if d_next.min(d_prev) >= 0.0 || normal.square_length() < 1e-5 {
2098            // Case of an overlapping stroke. In order to prevent the back vertex to create a
2099            // spike outside of the stroke, we simply don't create it and we'll "fold" the join
2100            // instead.
2101            join.fold[side] = true;
2102        }
2103    }
2104
2105    // For concave sides we'll simply connect at the intersection of the two side edges.
2106    let concave = inward && normal_same_side && !join.fold[side];
2107
2108    if concave
2109        || ((join.line_join == LineJoin::Miter || join.line_join == LineJoin::MiterClip)
2110            && !miter_limit_is_exceeded(normal, miter_limit))
2111    {
2112        let p = join.position + normal * join.half_width;
2113        join.side_points[side].single_vertex = Some(p);
2114    } else if join.line_join == LineJoin::MiterClip {
2115        // It is convenient to handle the miter-clip case here by simply moving
2116        // tow points on this side to the clip line.
2117        // This way the rest of the code doesn't differentiate between miter and miter-clip.
2118        let n0 = join.side_points[side].prev - join.position;
2119        let n1 = join.side_points[side].next - join.position;
2120        let (prev_normal, next_normal) =
2121            get_clip_intersections(n0, n1, normal, miter_limit * 0.5 * join.half_width);
2122        join.side_points[side].prev = join.position + prev_normal;
2123        join.side_points[side].next = join.position + next_normal;
2124        nan_check!(n0, n1, prev_normal, next_normal);
2125        nan_check!(join.side_points[side].prev);
2126        nan_check!(join.side_points[side].next);
2127    }
2128}
2129
2130fn tessellate_last_edge(
2131    p0: &EndpointData,
2132    p1: &mut EndpointData,
2133    is_first_edge: bool,
2134    options: &StrokeOptions,
2135    vertex: &mut StrokeVertexData,
2136    attributes: &dyn AttributeStore,
2137    output: &mut dyn StrokeGeometryBuilder,
2138) -> Result<(), TessellationError> {
2139    let v = p1.position - p0.position;
2140    p1.advancement = p0.advancement + v.length();
2141
2142    vertex.src = p1.src;
2143    vertex.position_on_path = p1.position;
2144    vertex.advancement = p1.advancement;
2145    vertex.half_width = p1.half_width;
2146    vertex.buffer_is_valid = false;
2147
2148    let sides = [Side::Positive, Side::Negative];
2149
2150    for side in 0..2 {
2151        let side_position = p1.side_points[side].prev;
2152        let clip = match options.end_cap {
2153            LineCap::Square => Some(p1.half_width),
2154            LineCap::Butt => Some(0.0),
2155            _ => None,
2156        };
2157
2158        if let Some(clip) = clip {
2159            let normal = (p1.position - p0.position).normalize();
2160            let clip_line = Line {
2161                point: p1.position + normal * clip,
2162                vector: tangent(normal),
2163            };
2164            let side_line = Line {
2165                point: side_position,
2166                vector: side_position - p0.side_points[side].next,
2167            };
2168
2169            let intersection = clip_line
2170                .to_f64()
2171                .intersection(&side_line.to_f64())
2172                .map(|p| p.to_f32())
2173                .unwrap_or(p1.side_points[side].prev);
2174
2175            p1.side_points[side].prev = intersection;
2176        }
2177
2178        vertex.side = sides[side];
2179        vertex.normal = (p1.side_points[side].prev - p1.position) / p1.half_width;
2180        let prev_vertex = output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
2181        p1.side_points[side].prev_vertex = prev_vertex;
2182    }
2183
2184    // Skip the edge triangles if it is also the first edge (tessellate_first_edge will do it).
2185    if !is_first_edge {
2186        add_edge_triangles(p0, p1, output);
2187    }
2188
2189    if options.end_cap == LineCap::Round {
2190        crate::stroke::tessellate_round_cap(
2191            p1.position,
2192            p1.half_width,
2193            p1.side_points[SIDE_POSITIVE].prev - p1.position,
2194            p1.side_points[SIDE_POSITIVE].prev_vertex,
2195            p1.side_points[SIDE_NEGATIVE].prev_vertex,
2196            v,
2197            options,
2198            false,
2199            vertex,
2200            attributes,
2201            output,
2202        )?;
2203    }
2204
2205    Ok(())
2206}
2207
2208fn tessellate_first_edge(
2209    first: &mut EndpointData,
2210    second: &EndpointData,
2211    options: &StrokeOptions,
2212    vertex: &mut StrokeVertexData,
2213    attributes: &dyn AttributeStore,
2214    output: &mut dyn StrokeGeometryBuilder,
2215) -> Result<(), TessellationError> {
2216    vertex.src = first.src;
2217    vertex.position_on_path = first.position;
2218    vertex.advancement = first.advancement;
2219    vertex.half_width = first.half_width;
2220    vertex.buffer_is_valid = false;
2221
2222    let sides = [Side::Positive, Side::Negative];
2223
2224    for side in 0..2 {
2225        let mut side_position = first.side_points[side].next;
2226        let clip = match options.start_cap {
2227            LineCap::Square => Some(first.half_width),
2228            LineCap::Butt => Some(0.0),
2229            _ => None,
2230        };
2231
2232        if let Some(clip) = clip {
2233            let normal = (first.position - second.position).normalize();
2234            let clip_line = Line {
2235                point: first.position + normal * clip,
2236                vector: tangent(normal),
2237            };
2238            let side_line = Line {
2239                point: side_position,
2240                vector: side_position - second.side_points[side].prev,
2241            };
2242
2243            let intersection = clip_line
2244                .to_f64()
2245                .intersection(&side_line.to_f64())
2246                .map(|p| p.to_f32())
2247                .unwrap_or(first.side_points[side].next);
2248            side_position = intersection;
2249        }
2250
2251        vertex.side = sides[side];
2252        vertex.normal = (side_position - first.position) / first.half_width;
2253        first.side_points[side].next_vertex =
2254            output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
2255    }
2256
2257    // Tessellate the edge between prev and join.
2258    add_edge_triangles(first, second, output);
2259
2260    match options.start_cap {
2261        LineCap::Round => crate::stroke::tessellate_round_cap(
2262            first.position,
2263            first.half_width,
2264            first.side_points[SIDE_NEGATIVE].next - first.position,
2265            first.side_points[SIDE_NEGATIVE].next_vertex,
2266            first.side_points[SIDE_POSITIVE].next_vertex,
2267            first.position - second.position,
2268            options,
2269            true,
2270            vertex,
2271            attributes,
2272            output,
2273        ),
2274        _ => Ok(()),
2275    }
2276}
2277
2278#[cfg_attr(feature = "profiling", inline(never))]
2279fn get_clip_intersections(
2280    previous_normal: Vector,
2281    next_normal: Vector,
2282    normal: Vector,
2283    clip_distance: f32,
2284) -> (Vector, Vector) {
2285    let clip_line = Line {
2286        point: normal.normalize().to_point() * clip_distance,
2287        vector: tangent(normal),
2288    }
2289    .to_f64();
2290
2291    let prev_line = Line {
2292        point: previous_normal.to_point(),
2293        vector: tangent(previous_normal),
2294    }
2295    .to_f64();
2296
2297    let next_line = Line {
2298        point: next_normal.to_point(),
2299        vector: tangent(next_normal),
2300    }
2301    .to_f64();
2302
2303    let i1 = clip_line
2304        .intersection(&prev_line)
2305        .map(|p| p.to_f32())
2306        .unwrap_or_else(|| normal.to_point())
2307        .to_vector();
2308    let i2 = clip_line
2309        .intersection(&next_line)
2310        .map(|p| p.to_f32())
2311        .unwrap_or_else(|| normal.to_point())
2312        .to_vector();
2313
2314    (i1, i2)
2315}
2316
2317// Derived from:
2318// miter_limit = miter_length / stroke_width
2319// miter_limit = (normal.length() * half_width) / (2.0 * half_width)
2320fn miter_limit_is_exceeded(normal: Vector, miter_limit: f32) -> bool {
2321    normal.square_length() > miter_limit * miter_limit * 4.0
2322}
2323
2324fn side_sign(side: usize) -> f32 {
2325    if side == SIDE_NEGATIVE {
2326        -1.0
2327    } else {
2328        1.0
2329    }
2330}
2331
2332// A fall-back that avoids off artifacts with zero-area rectangles as
2333// well as overlapping triangles if the rectangle is much smaller than the
2334// line width in any dimension.
2335#[inline(never)]
2336fn approximate_thin_rectangle(builder: &mut StrokeBuilder, rect: &Box2D, attributes: Attributes) {
2337    let (from, to, d) = if rect.width() > rect.height() {
2338        let d = rect.height() * 0.5;
2339        let min_x = rect.min.x + d;
2340        let max_x = rect.max.x - d;
2341        let y = (rect.min.y + rect.max.y) * 0.5;
2342
2343        (point(min_x, y), point(max_x, y), d)
2344    } else {
2345        let d = rect.width() * 0.5;
2346        let min_y = rect.min.y + d;
2347        let max_y = rect.max.y - d;
2348        let x = (rect.min.x + rect.max.x) * 0.5;
2349
2350        (point(x, min_y), point(x, max_y), d)
2351    };
2352
2353    // Save the builder options.
2354    let options = builder.builder.options;
2355
2356    let cap = match options.line_join {
2357        LineJoin::Round => LineCap::Round,
2358        _ => LineCap::Square,
2359    };
2360
2361    builder.builder.options.line_width += d;
2362    builder.builder.options.start_cap = cap;
2363    builder.builder.options.end_cap = cap;
2364
2365    builder.add_line_segment(&LineSegment { from, to }, attributes);
2366
2367    // Restore the builder options.
2368    builder.builder.options = options;
2369}
2370
2371struct PointBuffer {
2372    points: [EndpointData; 3],
2373    start: usize,
2374    count: usize,
2375}
2376
2377impl PointBuffer {
2378    fn new() -> Self {
2379        PointBuffer {
2380            points: [EndpointData::default(); 3],
2381            start: 0,
2382            count: 0,
2383        }
2384    }
2385
2386    fn push(&mut self, point: EndpointData) {
2387        if self.count < 3 {
2388            self.points[self.count] = point;
2389            self.count += 1;
2390            return;
2391        }
2392
2393        self.points[self.start] = point;
2394        self.start += 1;
2395        if self.start == 3 {
2396            self.start = 0;
2397        }
2398    }
2399
2400    fn replace_last(&mut self, point: EndpointData) {
2401        let mut idx = self.start;
2402        if idx == 0 {
2403            idx = self.count;
2404        }
2405        self.points[idx - 1] = point;
2406    }
2407
2408    fn clear(&mut self) {
2409        self.count = 0;
2410        self.start = 0;
2411    }
2412
2413    fn count(&self) -> usize {
2414        self.count
2415    }
2416
2417    fn get(&self, idx: usize) -> &EndpointData {
2418        assert!(idx < self.count);
2419        let idx = (idx + self.start) % 3;
2420
2421        &self.points[idx]
2422    }
2423
2424    fn get_reverse(&self, idx: usize) -> &EndpointData {
2425        assert!(idx < self.count);
2426        self.get(self.count - 1 - idx)
2427    }
2428
2429    fn get_mut(&mut self, idx: usize) -> &mut EndpointData {
2430        assert!(idx < self.count);
2431        let idx = (idx + self.start) % 3;
2432
2433        &mut self.points[idx]
2434    }
2435
2436    fn last(&self) -> &EndpointData {
2437        assert!(self.count > 0);
2438        self.get(self.count - 1)
2439    }
2440
2441    fn last_mut(&mut self) -> &mut EndpointData {
2442        assert!(self.count > 0);
2443        self.get_mut(self.count - 1)
2444    }
2445
2446    fn last_two_mut(&mut self) -> (&mut EndpointData, &mut EndpointData) {
2447        assert!(self.count >= 2);
2448        let i0 = (self.start + self.count - 1) % 3;
2449        let i1 = (self.start + self.count - 2) % 3;
2450        unsafe {
2451            (
2452                &mut *(self.points.get_unchecked_mut(i1) as *mut _),
2453                &mut *(self.points.get_unchecked_mut(i0) as *mut _),
2454            )
2455        }
2456    }
2457}
2458
2459pub(crate) fn tessellate_round_cap(
2460    center: Point,
2461    radius: f32,
2462    start_normal: Vector,
2463    start_vertex: VertexId,
2464    end_vertex: VertexId,
2465    edge_normal: Vector,
2466    options: &StrokeOptions,
2467    is_start: bool,
2468    vertex: &mut StrokeVertexData,
2469    attributes: &dyn AttributeStore,
2470    output: &mut dyn StrokeGeometryBuilder,
2471) -> Result<(), TessellationError> {
2472    if radius < options.tolerance {
2473        return Ok(());
2474    }
2475
2476    let first_side = if is_start ^ (edge_normal.cross(start_normal) >= 0.0) {
2477        Side::Positive
2478    } else {
2479        Side::Negative
2480    };
2481
2482    let start_angle = start_normal.angle_from_x_axis();
2483    let diff = start_angle.angle_to(edge_normal.angle_from_x_axis());
2484    let mid_angle = start_angle + diff;
2485    let end_angle = mid_angle + diff;
2486
2487    // Compute the required number of subdivisions on each side,
2488    let step = circle_flattening_step(radius, options.tolerance);
2489    let num_segments = (diff.radians.abs() / step).ceil();
2490    let num_subdivisions = num_segments.log2().round() as u32;
2491
2492    vertex.position_on_path = center;
2493    vertex.half_width = radius;
2494    vertex.side = first_side;
2495
2496    vertex.normal = edge_normal.normalize();
2497    let mid_vertex = output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
2498
2499    output.add_triangle(start_vertex, mid_vertex, end_vertex);
2500
2501    tessellate_arc(
2502        (start_angle.radians, mid_angle.radians),
2503        start_vertex,
2504        mid_vertex,
2505        num_subdivisions,
2506        vertex,
2507        attributes,
2508        output,
2509    )?;
2510
2511    vertex.side = first_side.opposite();
2512
2513    tessellate_arc(
2514        (mid_angle.radians, end_angle.radians),
2515        mid_vertex,
2516        end_vertex,
2517        num_subdivisions,
2518        vertex,
2519        attributes,
2520        output,
2521    )?;
2522
2523    Ok(())
2524}
2525
2526pub(crate) fn tessellate_empty_square_cap(
2527    position: Point,
2528    vertex: &mut StrokeVertexData,
2529    attributes: &dyn AttributeStore,
2530    output: &mut dyn StrokeGeometryBuilder,
2531) -> Result<(), TessellationError> {
2532    vertex.position_on_path = position;
2533
2534    vertex.normal = vector(1.0, 1.0);
2535    vertex.side = Side::Negative;
2536
2537    let a = output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
2538
2539    vertex.normal = vector(1.0, -1.0);
2540    vertex.side = Side::Positive;
2541
2542    let b = output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
2543
2544    vertex.normal = vector(-1.0, -1.0);
2545    vertex.side = Side::Positive;
2546
2547    let c = output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
2548
2549    vertex.normal = vector(-1.0, 1.0);
2550    vertex.side = Side::Negative;
2551
2552    let d = output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
2553
2554    output.add_triangle(a, b, c);
2555    output.add_triangle(a, c, d);
2556
2557    Ok(())
2558}
2559
2560pub(crate) fn tessellate_empty_round_cap(
2561    center: Point,
2562    options: &StrokeOptions,
2563    vertex: &mut StrokeVertexData,
2564    attribute_store: &dyn AttributeStore,
2565    output: &mut dyn StrokeGeometryBuilder,
2566) -> Result<(), TessellationError> {
2567    let radius = vertex.half_width;
2568
2569    vertex.position_on_path = center;
2570    vertex.normal = vector(-1.0, 0.0);
2571    vertex.side = Side::Positive;
2572
2573    let left_id = output.add_stroke_vertex(StrokeVertex(vertex, attribute_store))?;
2574
2575    vertex.normal = vector(1.0, 0.0);
2576    vertex.side = Side::Negative;
2577
2578    let right_id = output.add_stroke_vertex(StrokeVertex(vertex, attribute_store))?;
2579
2580    tessellate_round_cap(
2581        center,
2582        radius,
2583        vector(-1.0, 0.0),
2584        left_id,
2585        right_id,
2586        vector(0.0, 1.0),
2587        options,
2588        true,
2589        vertex,
2590        attribute_store,
2591        output,
2592    )?;
2593
2594    tessellate_round_cap(
2595        center,
2596        radius,
2597        vector(1.0, 0.0),
2598        right_id,
2599        left_id,
2600        vector(0.0, -1.0),
2601        options,
2602        false,
2603        vertex,
2604        attribute_store,
2605        output,
2606    )?;
2607
2608    Ok(())
2609}
2610
2611#[allow(clippy::too_many_arguments)]
2612pub(crate) fn tessellate_arc(
2613    angle: (f32, f32),
2614    va: VertexId,
2615    vb: VertexId,
2616    num_recursions: u32,
2617    vertex: &mut StrokeVertexData,
2618    attributes: &dyn AttributeStore,
2619    output: &mut dyn StrokeGeometryBuilder,
2620) -> Result<(), TessellationError> {
2621    if num_recursions == 0 {
2622        return Ok(());
2623    }
2624
2625    let mid_angle = (angle.0 + angle.1) * 0.5;
2626
2627    let normal = vector(mid_angle.cos(), mid_angle.sin());
2628
2629    vertex.normal = normal;
2630
2631    let vertex_id = output.add_stroke_vertex(StrokeVertex(vertex, attributes))?;
2632
2633    output.add_triangle(va, vertex_id, vb);
2634
2635    tessellate_arc(
2636        (angle.0, mid_angle),
2637        va,
2638        vertex_id,
2639        num_recursions - 1,
2640        vertex,
2641        attributes,
2642        output,
2643    )?;
2644    tessellate_arc(
2645        (mid_angle, angle.1),
2646        vertex_id,
2647        vb,
2648        num_recursions - 1,
2649        vertex,
2650        attributes,
2651        output,
2652    )
2653}
2654
2655/// Extra vertex information from the `StrokeTessellator`.
2656pub(crate) struct StrokeVertexData<'l> {
2657    pub(crate) position_on_path: Point,
2658    pub(crate) half_width: f32,
2659    pub(crate) normal: Vector,
2660    pub(crate) advancement: f32,
2661    pub(crate) side: Side,
2662    pub(crate) src: VertexSource,
2663    pub(crate) buffer: &'l mut [f32],
2664    pub(crate) buffer_is_valid: bool,
2665}
2666
2667/// Extra vertex information from the `StrokeTessellator` accessible when building vertices.
2668pub struct StrokeVertex<'a, 'b>(
2669    pub(crate) &'b mut StrokeVertexData<'a>,
2670    pub(crate) &'b dyn AttributeStore,
2671);
2672
2673impl<'a, 'b> StrokeVertex<'a, 'b> {
2674    /// The vertex position.
2675    #[inline]
2676    pub fn position(&self) -> Point {
2677        self.0.position_on_path + self.0.normal * self.0.half_width
2678    }
2679
2680    /// Normal at this vertex.
2681    ///
2682    /// The length of the provided normal is such that displacing the vertex along it
2683    /// inflates the stroke by 2.0 (1.0 on each side).
2684    #[inline]
2685    pub fn normal(&self) -> Vector {
2686        self.0.normal
2687    }
2688
2689    /// Position of this vertex on the path, unaffected by the line width.
2690    #[inline]
2691    pub fn position_on_path(&self) -> Point {
2692        self.0.position_on_path
2693    }
2694
2695    /// The line width at this vertex.
2696    ///
2697    /// If a line width modifier is set via `StrokeOptions::variable_line_width`, the
2698    /// returned line width is equal to `StrokeOptions::line_width` multiplied by the
2699    /// line width modifier at this vertex.
2700    #[inline]
2701    pub fn line_width(&self) -> f32 {
2702        self.0.half_width * 2.0
2703    }
2704
2705    /// How far along the path this vertex is.
2706    #[inline]
2707    pub fn advancement(&self) -> f32 {
2708        self.0.advancement
2709    }
2710
2711    /// Whether the vertex is on the positive or negative side of the path.
2712    #[inline]
2713    pub fn side(&self) -> Side {
2714        self.0.side
2715    }
2716
2717    /// Returns the source of this vertex.
2718    #[inline]
2719    pub fn source(&self) -> VertexSource {
2720        self.0.src
2721    }
2722
2723    /// Computes and returns the custom attributes for this vertex.
2724    ///
2725    /// The attributes are interpolated along the edges on which this vertex is.
2726    /// This can include multiple edges if the vertex is at an intersection.
2727    #[inline]
2728    pub fn interpolated_attributes(&mut self) -> Attributes {
2729        if self.0.buffer_is_valid {
2730            return self.0.buffer;
2731        }
2732
2733        match self.0.src {
2734            VertexSource::Endpoint { id } => self.1.get(id),
2735            VertexSource::Edge { from, to, t } => {
2736                let a = self.1.get(from);
2737                let b = self.1.get(to);
2738                for i in 0..self.0.buffer.len() {
2739                    self.0.buffer[i] = a[i] * (1.0 - t) + b[i] * t;
2740                }
2741                self.0.buffer_is_valid = true;
2742
2743                self.0.buffer
2744            }
2745        }
2746    }
2747}
2748
2749pub(crate) fn circle_flattening_step(radius: f32, mut tolerance: f32) -> f32 {
2750    // Don't allow high tolerance values (compared to the radius) to avoid edge cases.
2751    tolerance = f32::min(tolerance, radius);
2752    2.0 * ((radius - tolerance) / radius).acos()
2753}
2754
2755fn flatten_quad<F>(curve: &QuadraticBezierSegment<f32>, tolerance: f32, cb: &mut F)
2756where
2757    F: FnMut(Point, f32, bool),
2758{
2759    if let Some(t_split) = find_sharp_turn(curve) {
2760        let (before, after) = curve.split(t_split);
2761
2762        before.for_each_flattened_with_t(tolerance, &mut |line, t| {
2763            let is_flattening_step = t.end != 1.0;
2764            let t = t.end * t_split;
2765            cb(line.to, t, is_flattening_step);
2766        });
2767
2768        after.for_each_flattened_with_t(tolerance, &mut |line, t| {
2769            let is_flattening_step = t.end != 1.0;
2770            let t = t_split + t.end * (1.0 - t_split);
2771            cb(line.to, t, is_flattening_step);
2772        });
2773    } else {
2774        curve.for_each_flattened_with_t(tolerance, &mut |line, t| {
2775            let is_flattening_step = t.end != 1.0;
2776            cb(line.to, t.end, is_flattening_step);
2777        });
2778    }
2779}
2780
2781#[cfg(test)]
2782use crate::geometry_builder::*;
2783#[cfg(test)]
2784use crate::path::Path;
2785
2786#[cfg(test)]
2787fn test_path(path: PathSlice, options: &StrokeOptions, expected_triangle_count: Option<u32>) {
2788    struct TestBuilder<'l> {
2789        builder: SimpleBuffersBuilder<'l>,
2790    }
2791
2792    impl<'l> GeometryBuilder for TestBuilder<'l> {
2793        fn begin_geometry(&mut self) {
2794            self.builder.begin_geometry();
2795        }
2796        fn end_geometry(&mut self) {
2797            self.builder.end_geometry();
2798        }
2799        fn add_triangle(&mut self, a: VertexId, b: VertexId, c: VertexId) {
2800            assert_ne!(a, b);
2801            assert_ne!(a, c);
2802            assert_ne!(b, c);
2803            let pa = self.builder.buffers().vertices[a.0 as usize];
2804            let pb = self.builder.buffers().vertices[b.0 as usize];
2805            let pc = self.builder.buffers().vertices[c.0 as usize];
2806            let threshold = -0.035; // Floating point errors :(
2807            assert!((pa - pb).cross(pc - pb) >= threshold);
2808            self.builder.add_triangle(a, b, c);
2809        }
2810        fn abort_geometry(&mut self) {
2811            panic!();
2812        }
2813    }
2814
2815    impl<'l> StrokeGeometryBuilder for TestBuilder<'l> {
2816        fn add_stroke_vertex(
2817            &mut self,
2818            attributes: StrokeVertex,
2819        ) -> Result<VertexId, GeometryBuilderError> {
2820            assert!(!attributes.position().x.is_nan());
2821            assert!(!attributes.position().y.is_nan());
2822            assert!(!attributes.normal().x.is_nan());
2823            assert!(!attributes.normal().y.is_nan());
2824            assert_ne!(attributes.normal().square_length(), 0.0);
2825            assert!(!attributes.advancement().is_nan());
2826            self.builder.add_stroke_vertex(attributes)
2827        }
2828    }
2829
2830    let mut buffers: VertexBuffers<Point, u16> = VertexBuffers::new();
2831
2832    let mut tess = StrokeTessellator::new();
2833    tess.tessellate_path(
2834        path,
2835        options,
2836        &mut TestBuilder {
2837            builder: simple_builder(&mut buffers),
2838        },
2839    )
2840    .unwrap();
2841
2842    if let Some(triangles) = expected_triangle_count {
2843        assert_eq!(
2844            triangles,
2845            buffers.indices.len() as u32 / 3,
2846            "Unexpected number of triangles"
2847        );
2848    }
2849}
2850
2851#[test]
2852fn test_square() {
2853    let mut builder = Path::builder_with_attributes(1);
2854
2855    builder.begin(point(-1.0, 1.0), &[0.3]);
2856    builder.line_to(point(1.0, 1.0), &[0.3]);
2857    builder.line_to(point(1.0, -1.0), &[0.3]);
2858    builder.line_to(point(-1.0, -1.0), &[0.3]);
2859    builder.end(false);
2860
2861    builder.begin(point(-1.0, -1.0), &[0.3]);
2862    builder.line_to(point(1.0, -1.0), &[0.3]);
2863    builder.line_to(point(1.0, 1.0), &[0.3]);
2864    builder.line_to(point(-1.0, 1.0), &[0.3]);
2865    builder.end(false);
2866
2867    let path = builder.build();
2868
2869    // Test both with and without the fixed width fast path.
2870    let options = [
2871        StrokeOptions::default().with_variable_line_width(0),
2872        StrokeOptions::default(),
2873    ];
2874
2875    for options in options {
2876        test_path(
2877            path.as_slice(),
2878            &options
2879                .with_line_join(LineJoin::Miter)
2880                .with_line_cap(LineCap::Butt),
2881            Some(12),
2882        );
2883
2884        test_path(
2885            path.as_slice(),
2886            &options
2887                .with_line_join(LineJoin::Bevel)
2888                .with_line_cap(LineCap::Square),
2889            Some(16),
2890        );
2891
2892        test_path(
2893            path.as_slice(),
2894            &options
2895                .with_line_join(LineJoin::MiterClip)
2896                .with_miter_limit(1.0)
2897                .with_line_cap(LineCap::Round),
2898            None,
2899        );
2900
2901        test_path(
2902            path.as_slice(),
2903            &options
2904                .with_tolerance(0.001)
2905                .with_line_join(LineJoin::Round)
2906                .with_line_cap(LineCap::Round),
2907            None,
2908        );
2909    }
2910}
2911
2912#[test]
2913fn test_empty_path() {
2914    let path = Path::builder().build();
2915    test_path(path.as_slice(), &StrokeOptions::default(), Some(0));
2916
2917    let path = Path::builder_with_attributes(1).build();
2918    test_path(path.as_slice(), &StrokeOptions::default(), Some(0));
2919}
2920
2921#[test]
2922fn test_empty_caps() {
2923    let mut builder = Path::builder_with_attributes(1);
2924
2925    // moveto + close: empty cap.
2926    builder.begin(point(1.0, 0.0), &[1.0]);
2927    builder.end(true);
2928
2929    // Only moveto + lineto at same position: empty cap.
2930    builder.begin(point(2.0, 0.0), &[1.0]);
2931    builder.line_to(point(2.0, 0.0), &[1.0]);
2932    builder.end(false);
2933
2934    // Only moveto + lineto at same position: empty cap.
2935    builder.begin(point(3.0, 0.0), &[1.0]);
2936    builder.line_to(point(3.0, 0.0), &[1.0]);
2937    builder.end(true);
2938
2939    // Only moveto + multiple lineto at same position: empty cap.
2940    builder.begin(point(3.0, 0.0), &[1.0]);
2941    builder.line_to(point(3.0, 0.0), &[1.0]);
2942    builder.line_to(point(3.0, 0.0), &[1.0]);
2943    builder.line_to(point(3.0, 0.0), &[1.0]);
2944    builder.end(true);
2945
2946    // moveto then end (not closed): no empty cap.
2947    builder.begin(point(4.0, 0.0), &[1.0]);
2948    builder.end(false);
2949
2950    let path = builder.build();
2951
2952    let options = [
2953        StrokeOptions::default().with_variable_line_width(0),
2954        StrokeOptions::default(),
2955    ];
2956
2957    for options in options {
2958        test_path(
2959            path.as_slice(),
2960            &options.with_line_cap(LineCap::Butt),
2961            Some(0),
2962        );
2963        test_path(
2964            path.as_slice(),
2965            &options.with_line_cap(LineCap::Square),
2966            Some(8),
2967        );
2968        test_path(
2969            path.as_slice(),
2970            &options.with_line_cap(LineCap::Round),
2971            None,
2972        );
2973    }
2974}
2975
2976#[test]
2977fn test_too_many_vertices() {
2978    /// This test checks that the tessellator returns the proper error when
2979    /// the geometry builder run out of vertex ids.
2980    use crate::extra::rust_logo::build_logo_path;
2981    use crate::GeometryBuilder;
2982
2983    struct Builder {
2984        max_vertices: u32,
2985    }
2986
2987    impl GeometryBuilder for Builder {
2988        fn begin_geometry(&mut self) {}
2989        fn end_geometry(&mut self) {
2990            // Expected to abort the geometry.
2991            panic!();
2992        }
2993        fn add_triangle(&mut self, a: VertexId, b: VertexId, c: VertexId) {
2994            assert_ne!(a, b);
2995            assert_ne!(a, c);
2996            assert_ne!(b, c);
2997        }
2998        fn abort_geometry(&mut self) {}
2999    }
3000
3001    impl StrokeGeometryBuilder for Builder {
3002        fn add_stroke_vertex(&mut self, _: StrokeVertex) -> Result<VertexId, GeometryBuilderError> {
3003            if self.max_vertices == 0 {
3004                return Err(GeometryBuilderError::TooManyVertices);
3005            }
3006            self.max_vertices -= 1;
3007            Ok(VertexId(self.max_vertices))
3008        }
3009    }
3010
3011    let mut path = Path::builder().with_svg();
3012    build_logo_path(&mut path);
3013    let path = path.build();
3014
3015    let mut tess = StrokeTessellator::new();
3016    let options = StrokeOptions::tolerance(0.05);
3017
3018    assert_eq!(
3019        tess.tessellate(&path, &options, &mut Builder { max_vertices: 0 }),
3020        Err(TessellationError::GeometryBuilder(
3021            GeometryBuilderError::TooManyVertices
3022        )),
3023    );
3024    assert_eq!(
3025        tess.tessellate(&path, &options, &mut Builder { max_vertices: 10 }),
3026        Err(TessellationError::GeometryBuilder(
3027            GeometryBuilderError::TooManyVertices
3028        )),
3029    );
3030
3031    assert_eq!(
3032        tess.tessellate(&path, &options, &mut Builder { max_vertices: 100 }),
3033        Err(TessellationError::GeometryBuilder(
3034            GeometryBuilderError::TooManyVertices
3035        )),
3036    );
3037}
3038
3039#[test]
3040fn stroke_vertex_source_01() {
3041    let mut path = crate::path::Path::builder_with_attributes(1);
3042    let a = path.begin(point(0.0, 0.0), &[1.0]);
3043    let b = path.line_to(point(10.0, 10.0), &[2.0]);
3044    let c = path.quadratic_bezier_to(point(10.0, 20.0), point(0.0, 20.0), &[3.0]);
3045    path.end(true);
3046
3047    let path = path.build();
3048
3049    let mut tess = StrokeTessellator::new();
3050    tess.tessellate_with_ids(
3051        &mut path.id_iter(),
3052        &path,
3053        Some(&path),
3054        &StrokeOptions::default().with_variable_line_width(0),
3055        &mut CheckVertexSources {
3056            next_vertex: 0,
3057            a,
3058            b,
3059            c,
3060        },
3061    )
3062    .unwrap();
3063
3064    struct CheckVertexSources {
3065        next_vertex: u32,
3066        a: EndpointId,
3067        b: EndpointId,
3068        c: EndpointId,
3069    }
3070
3071    impl GeometryBuilder for CheckVertexSources {
3072        fn add_triangle(&mut self, _: VertexId, _: VertexId, _: VertexId) {}
3073        fn abort_geometry(&mut self) {}
3074    }
3075
3076    fn eq(a: Point, b: Point) -> bool {
3077        (a.x - b.x).abs() < 0.00001 && (a.y - b.y).abs() < 0.00001
3078    }
3079
3080    impl StrokeGeometryBuilder for CheckVertexSources {
3081        fn add_stroke_vertex(
3082            &mut self,
3083            mut attr: StrokeVertex,
3084        ) -> Result<VertexId, GeometryBuilderError> {
3085            let pos = attr.position_on_path();
3086            let src = attr.source();
3087            if eq(pos, point(0.0, 0.0)) {
3088                assert_eq!(src, VertexSource::Endpoint { id: self.a })
3089            } else if eq(pos, point(10.0, 10.0)) {
3090                assert_eq!(src, VertexSource::Endpoint { id: self.b })
3091            } else if eq(pos, point(0.0, 20.0)) {
3092                assert_eq!(src, VertexSource::Endpoint { id: self.c })
3093            } else {
3094                match src {
3095                    VertexSource::Edge { from, to, t } => {
3096                        assert_eq!(from, self.b);
3097                        assert_eq!(to, self.c);
3098                        assert!(t < 1.0);
3099                        assert!(t > 0.0);
3100                    }
3101                    _ => panic!("{:?} at {:?}", src, pos),
3102                }
3103            }
3104
3105            let vertex = attr.interpolated_attributes();
3106            if eq(pos, point(0.0, 0.0)) {
3107                assert_eq!(vertex, &[1.0])
3108            } else if eq(pos, point(10.0, 10.0)) {
3109                assert_eq!(vertex, &[2.0])
3110            } else if eq(pos, point(0.0, 20.0)) {
3111                assert_eq!(vertex, &[3.0])
3112            } else {
3113                assert_eq!(vertex.len(), 1);
3114                assert!(vertex[0] > 2.0);
3115                assert!(vertex[0] < 3.0);
3116            }
3117
3118            let id = self.next_vertex;
3119            self.next_vertex += 1;
3120
3121            Ok(VertexId(id))
3122        }
3123    }
3124}
3125
3126#[test]
3127fn test_line_width() {
3128    use crate::geom::euclid::approxeq::ApproxEq;
3129    use crate::math::{point, Point};
3130    let mut builder = crate::path::Path::builder();
3131    builder.begin(point(0.0, 1.0));
3132    builder.line_to(point(2.0, 1.0));
3133    builder.end(false);
3134    let path = builder.build();
3135
3136    let options = StrokeOptions::DEFAULT.with_line_width(2.0);
3137    let mut geometry: VertexBuffers<Point, u16> = VertexBuffers::new();
3138    StrokeTessellator::new()
3139        .tessellate(
3140            path.iter(),
3141            &options,
3142            &mut crate::geometry_builder::simple_builder(&mut geometry),
3143        )
3144        .unwrap();
3145
3146    for p in &geometry.vertices {
3147        assert!(
3148            p.approx_eq(&point(0.0, 0.0))
3149                || p.approx_eq(&point(0.0, 2.0))
3150                || p.approx_eq(&point(2.0, 0.0))
3151                || p.approx_eq(&point(2.0, 2.0))
3152        );
3153    }
3154}
3155
3156trait IsNan {
3157    fn is_nan(&self) -> bool;
3158}
3159
3160impl IsNan for f32 {
3161    fn is_nan(&self) -> bool {
3162        f32::is_nan(*self)
3163    }
3164}
3165
3166impl IsNan for Point {
3167    fn is_nan(&self) -> bool {
3168        self.x.is_nan() || self.y.is_nan()
3169    }
3170}
3171
3172impl IsNan for Vector {
3173    fn is_nan(&self) -> bool {
3174        self.x.is_nan() || self.y.is_nan()
3175    }
3176}
3177
3178fn find_sharp_turn(curve: &QuadraticBezierSegment<f32>) -> Option<f32> {
3179    // TODO: The various thresholds here should take the line width into account.
3180
3181    let baseline = curve.to - curve.from;
3182    let v = curve.ctrl - curve.from;
3183    let n = vector(-baseline.y, baseline.x);
3184    let v_dot_b = v.dot(baseline);
3185    let v_dot_n = v.dot(n);
3186
3187    // If the projection of the control point on the baseline is between the endpoint, we
3188    // can only get a sharp turn with a control point that is very far away.
3189    let long_axis = if (v_dot_b >= 0.0 && v_dot_b <= baseline.dot(baseline))
3190        || v_dot_n.abs() * 2.0 >= v_dot_b.abs()
3191    {
3192        // The control point is far enough from the endpoints It can cause a sharp turn.
3193        if baseline.square_length() * 30.0 > v.square_length() {
3194            return None;
3195        }
3196
3197        v
3198    } else {
3199        baseline
3200    };
3201
3202    // Rotate the curve to find its extremum along the long axis, where we should split to
3203    // avoid the sharp turn.
3204    let rot = crate::geom::euclid::Rotation2D::new(-long_axis.angle_from_x_axis());
3205    let rotated = QuadraticBezierSegment {
3206        from: point(0.0, 0.0),
3207        ctrl: rot.transform_vector(v).to_point(),
3208        to: rot.transform_vector(baseline).to_point(),
3209    };
3210
3211    rotated.local_x_extremum_t()
3212}
3213
3214#[test]
3215fn test_triangle_winding() {
3216    use crate::math::{point, Point};
3217    use crate::GeometryBuilder;
3218
3219    struct Builder {
3220        vertices: Vec<Point>,
3221    }
3222
3223    impl GeometryBuilder for Builder {
3224        fn add_triangle(&mut self, a: VertexId, b: VertexId, c: VertexId) {
3225            let a = self.vertices[a.to_usize()];
3226            let b = self.vertices[b.to_usize()];
3227            let c = self.vertices[c.to_usize()];
3228            assert!((b - a).cross(c - b) <= 0.0);
3229        }
3230    }
3231
3232    impl StrokeGeometryBuilder for Builder {
3233        fn add_stroke_vertex(&mut self, v: StrokeVertex) -> Result<VertexId, GeometryBuilderError> {
3234            let id = VertexId(self.vertices.len() as u32);
3235            self.vertices.push(v.position());
3236
3237            Ok(id)
3238        }
3239    }
3240
3241    let mut path = Path::builder().with_svg();
3242    path.move_to(point(0.0, 0.0));
3243    path.quadratic_bezier_to(point(100.0, 0.0), point(100.0, 100.0));
3244    let path = path.build();
3245
3246    let mut tess = StrokeTessellator::new();
3247    let options = StrokeOptions::tolerance(0.05);
3248
3249    tess.tessellate(
3250        &path,
3251        &options,
3252        &mut Builder {
3253            vertices: Vec::new(),
3254        },
3255    )
3256    .unwrap();
3257}
3258
3259#[test]
3260fn single_segment_closed() {
3261    let mut path = Path::builder().with_svg();
3262    path.move_to(point(0.0, 0.0));
3263    path.line_to(point(100.0, 0.0));
3264    let path = path.build();
3265
3266    let mut tess = StrokeTessellator::new();
3267    let options = StrokeOptions::tolerance(0.05);
3268    let mut output: VertexBuffers<Point, u16> = VertexBuffers::new();
3269    tess.tessellate(&path, &options, &mut simple_builder(&mut output))
3270        .unwrap();
3271
3272    assert!(!output.indices.is_empty());
3273
3274    let mut path = Path::builder_with_attributes(1);
3275    path.begin(point(0.0, 0.0), &[1.0]);
3276    path.line_to(point(100.0, 0.0), &[1.0]);
3277    path.end(false);
3278    let path = path.build();
3279
3280    let mut tess = StrokeTessellator::new();
3281    let options = StrokeOptions::tolerance(0.05);
3282    let mut output: VertexBuffers<Point, u16> = VertexBuffers::new();
3283    tess.tessellate(&path, &options, &mut simple_builder(&mut output))
3284        .unwrap();
3285
3286    assert!(!output.indices.is_empty());
3287}
3288
3289#[test]
3290fn issue_819() {
3291    // In this test case, the last point of the path is within merge range
3292    // of both the first and second points while the latter ones aren't within
3293    // merge range of one-another. As a result they are both skipped when
3294    // closing the path.
3295
3296    let mut path = Path::builder();
3297    path.begin(point(650.539978027344, 173.559997558594));
3298    path.line_to(point(650.609985351562, 173.529998779297));
3299    path.line_to(point(650.729980468750, 173.630004882812));
3300    path.line_to(point(650.559997558594, 173.570007324219));
3301    path.end(true);
3302
3303    let mut tess = StrokeTessellator::new();
3304    let options = StrokeOptions::tolerance(0.1);
3305    let mut output: VertexBuffers<Point, u16> = VertexBuffers::new();
3306    tess.tessellate(&path.build(), &options, &mut simple_builder(&mut output))
3307        .unwrap();
3308
3309    let mut path = Path::builder_with_attributes(1);
3310    path.begin(point(650.539978027344, 173.559997558594), &[0.0]);
3311    path.line_to(point(650.609985351562, 173.529998779297), &[0.0]);
3312    path.line_to(point(650.729980468750, 173.630004882812), &[0.0]);
3313    path.line_to(point(650.559997558594, 173.570007324219), &[0.0]);
3314    path.end(true);
3315
3316    let mut tess = StrokeTessellator::new();
3317    let options = StrokeOptions::tolerance(0.1);
3318    let mut output: VertexBuffers<Point, u16> = VertexBuffers::new();
3319    tess.tessellate(&path.build(), &options, &mut simple_builder(&mut output))
3320        .unwrap();
3321}
3322
3323#[test]
3324fn issue_821() {
3325    let mut tessellator = StrokeTessellator::new();
3326
3327    let options = StrokeOptions::default()
3328        .with_tolerance(0.001)
3329        .with_line_cap(LineCap::Round)
3330        .with_variable_line_width(0);
3331
3332    let mut path = Path::builder_with_attributes(1);
3333    path.begin(point(-45.192276, -69.800575), &[1.0]);
3334    path.line_to(point(-45.164116, -69.84931), &[1.0]);
3335    path.line_to(point(-45.135952, -69.90564), &[1.0]);
3336    path.line_to(point(-45.10779, -69.93248), &[1.0]);
3337    path.line_to(point(-45.052788, -69.96064), &[1.0]);
3338    path.line_to(point(-45.026604, -69.898155), &[1.0]);
3339
3340    path.end(false);
3341
3342    let path = path.build();
3343    let mut mesh = VertexBuffers::new();
3344    let mut builder = simple_builder(&mut mesh);
3345    tessellator
3346        .tessellate_path(&path, &options, &mut builder)
3347        .unwrap();
3348}
3349
3350#[test]
3351fn issue_894() {
3352    struct VariableWidthStrokeCtor;
3353    impl StrokeVertexConstructor<[f32; 2]> for VariableWidthStrokeCtor {
3354        fn new_vertex(&mut self, vertex: StrokeVertex) -> [f32; 2] {
3355            vertex.position().to_array()
3356        }
3357    }
3358
3359    const STROKE_WIDTH: lyon_path::AttributeIndex = 0;
3360
3361    let mut builder = Path::builder_with_attributes(1);
3362    builder.begin(point(435.72, 368.42), &[38.82]);
3363    builder.line_to(point(433.53, 366.06), &[38.82]);
3364    builder.quadratic_bezier_to(point(431.35, 363.70), point(430.22, 362.52), &[39.59]);
3365    builder.quadratic_bezier_to(point(429.09, 361.34), point(429.05, 362.14), &[41.62]);
3366    builder.line_to(point(429.00, 362.95), &[41.63]);
3367    builder.end(false);
3368    let path = builder.build();
3369    let mut stroke_tessellator = StrokeTessellator::new();
3370    let mut geometry: crate::VertexBuffers<[f32; 2], u16> = crate::VertexBuffers::new();
3371    _ = stroke_tessellator.tessellate_path(
3372        &path,
3373        &StrokeOptions::tolerance(0.01)
3374            .with_line_cap(LineCap::Round)
3375            .with_variable_line_width(STROKE_WIDTH),
3376        &mut BuffersBuilder::new(&mut geometry, VariableWidthStrokeCtor),
3377    );
3378}