forma_render/
path.rs

1// Copyright 2022 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// The path division algorithm is mostly based on Raph Levien's curvature approximation[1] for
16// quadratic Bézier division and Adrian Colomitchi's cubic-to-quadratic approximation[2] with a few
17// additions to improve robustness.
18//
19// The algorithm converts all possible types of curves into primitives (lines and quadratic
20// Béziers) sequentially, while these are pushed onto the `PathBuilder`. Afterwards, each `Path`
21// is converted into segments in parallel. This part of the algorithms tries its best not to create
22// new segments if two neighboring segments are close enough to forming a line together according
23// to some threshold.
24//
25// [1]: https://raphlinus.github.io/graphics/curves/2019/12/23/flatten-quadbez.html
26// [2]: http://www.caffeineowl.com/graphics/2d/vectorial/bezierintro.html
27
28use std::{cell::RefCell, f32, rc::Rc};
29
30use rayon::prelude::*;
31
32use crate::{
33    consts,
34    math::{GeomPresTransform, Point},
35    utils::{ExtendTuple3, ExtendVec},
36    GeomId,
37};
38
39// Pixel accuracy should be within 0.5 of a sub-pixel.
40pub const MAX_ERROR: f32 = 1.0 / consts::PIXEL_WIDTH as f32;
41const MAX_ANGLE_ERROR: f32 = 0.001;
42const MIN_LEN: usize = 256;
43
44fn lerp(t: f32, a: f32, b: f32) -> f32 {
45    t.mul_add(b, (-t).mul_add(a, a))
46}
47
48fn curvature(x: f32) -> f32 {
49    const C: f32 = 0.67;
50    x / (1.0 - C + ((x * x).mul_add(0.25, C * C * C * C)).sqrt().sqrt())
51}
52
53fn inv_curvature(k: f32) -> f32 {
54    const C: f32 = 0.39;
55    k * (1.0 - C + ((k * k).mul_add(0.25, C * C)).sqrt())
56}
57
58#[derive(Clone, Copy, Debug)]
59pub struct WeightedPoint {
60    pub point: Point,
61    pub weight: f32,
62}
63
64impl WeightedPoint {
65    pub fn applied(self) -> Point {
66        let w_recip = self.weight.recip();
67
68        Point {
69            x: self.point.x * w_recip,
70            y: self.point.y * w_recip,
71        }
72    }
73}
74
75fn eval_cubic(t: f32, points: &[WeightedPoint; 4]) -> WeightedPoint {
76    let x = lerp(
77        t,
78        lerp(
79            t,
80            lerp(t, points[0].point.x, points[1].point.x),
81            lerp(t, points[1].point.x, points[2].point.x),
82        ),
83        lerp(
84            t,
85            lerp(t, points[1].point.x, points[2].point.x),
86            lerp(t, points[2].point.x, points[3].point.x),
87        ),
88    );
89    let y = lerp(
90        t,
91        lerp(
92            t,
93            lerp(t, points[0].point.y, points[1].point.y),
94            lerp(t, points[1].point.y, points[2].point.y),
95        ),
96        lerp(
97            t,
98            lerp(t, points[1].point.y, points[2].point.y),
99            lerp(t, points[2].point.y, points[3].point.y),
100        ),
101    );
102    let weight = lerp(
103        t,
104        lerp(
105            t,
106            lerp(t, points[0].weight, points[1].weight),
107            lerp(t, points[1].weight, points[2].weight),
108        ),
109        lerp(
110            t,
111            lerp(t, points[1].weight, points[2].weight),
112            lerp(t, points[2].weight, points[3].weight),
113        ),
114    );
115
116    WeightedPoint {
117        point: Point { x, y },
118        weight,
119    }
120}
121
122#[derive(Debug, Default)]
123pub struct ScratchBuffers {
124    point_indices: Vec<usize>,
125    quad_indices: Vec<usize>,
126    point_commands: Vec<u32>,
127}
128
129impl ScratchBuffers {
130    pub fn clear(&mut self) {
131        self.point_indices.clear();
132        self.quad_indices.clear();
133        self.point_commands.clear();
134    }
135}
136
137#[derive(Clone, Copy, Debug)]
138enum PointCommand {
139    Start(usize),
140    Incr(f32),
141    End(usize, bool),
142}
143
144impl From<u32> for PointCommand {
145    fn from(val: u32) -> Self {
146        if val & 0x7F80_0000 == 0x7F80_0000 {
147            if val & 0x8000_0000 == 0 {
148                Self::Start((val & 0x3F_FFFF) as usize)
149            } else {
150                Self::End((val & 0x3F_FFFF) as usize, val & 0x40_0000 != 0)
151            }
152        } else {
153            Self::Incr(f32::from_bits(val))
154        }
155    }
156}
157
158impl From<PointCommand> for u32 {
159    fn from(command: PointCommand) -> Self {
160        match command {
161            PointCommand::Start(i) => 0x7F80_0000 | (i as u32 & 0x3F_FFFF),
162            PointCommand::Incr(point_command) => point_command.to_bits(),
163            PointCommand::End(i, new_contour) => {
164                0xFF80_0000 | (i as u32 & 0x3F_FFFF) | (u32::from(new_contour) << 22)
165            }
166        }
167    }
168}
169
170#[derive(Clone, Debug)]
171struct Contour;
172
173#[derive(Clone, Debug)]
174struct Spline {
175    curvature: f32,
176    p0: Point,
177    p2: Point,
178    contour: Option<Contour>,
179}
180
181impl Spline {
182    #[inline]
183    pub fn new_spline_needed(&mut self, angle_changed: bool, point: Point) -> Option<Contour> {
184        let needed = angle_changed || (point - self.p2).len() >= MAX_ERROR;
185
186        needed.then(|| self.contour.take()).flatten()
187    }
188}
189
190#[derive(Clone, Debug)]
191struct Primitives {
192    last_angle: Option<f32>,
193    contour: Option<Contour>,
194    splines: Vec<Spline>,
195    x: Vec<f32>,
196    y: Vec<f32>,
197    weight: Vec<f32>,
198    x0: Vec<f32>,
199    dx_recip: Vec<f32>,
200    k0: Vec<f32>,
201    dk: Vec<f32>,
202    curvatures_recip: Vec<f32>,
203    partial_curvatures: Vec<(u32, f32)>,
204}
205
206impl Primitives {
207    #[inline]
208    fn last_spline_or_insert_with<F>(
209        &mut self,
210        angle: Option<f32>,
211        point: Point,
212        f: F,
213    ) -> &mut Spline
214    where
215        F: FnOnce(Contour) -> Spline,
216    {
217        if let Some(contour) = self.contour.take().or_else(|| {
218            fn diff(a0: f32, a1: f32) -> f32 {
219                let mut diff = (a1 - a0).abs();
220
221                if diff > f32::consts::PI {
222                    diff -= f32::consts::PI;
223                }
224
225                if diff > f32::consts::FRAC_PI_2 {
226                    diff = f32::consts::PI - diff;
227                }
228
229                diff
230            }
231
232            let angle_changed = if let (Some(a0), Some(a1)) = (self.last_angle, angle) {
233                diff(a0, a1) > MAX_ANGLE_ERROR
234            } else {
235                false
236            };
237
238            self.splines
239                .last_mut()
240                .and_then(|spline| spline.new_spline_needed(angle_changed, point))
241        }) {
242            self.splines.push(f(contour));
243        }
244
245        self.splines.last_mut().unwrap()
246    }
247
248    pub fn push_contour(&mut self, contour: Contour) {
249        self.contour = Some(contour);
250    }
251
252    pub fn push_line(&mut self, points: [WeightedPoint; 2]) {
253        let p0 = points[0].applied();
254        let p1 = points[1].applied();
255
256        let d = p1 - p0;
257        let angle = d.angle();
258
259        let spline = self.last_spline_or_insert_with(angle, p0, |contour| Spline {
260            curvature: 0.0,
261            p0,
262            p2: p1,
263            contour: Some(contour),
264        });
265
266        spline.p2 = p1;
267
268        self.last_angle = angle;
269    }
270
271    pub fn push_quad(&mut self, points: [WeightedPoint; 3]) {
272        const PIXEL_ACCURACY_RECIP: f32 = 1.0 / MAX_ERROR;
273
274        let p0 = points[0].applied();
275        let p1 = points[1].applied();
276        let p2 = points[2].applied();
277
278        let a = p1 - p0;
279        let b = p2 - p1;
280
281        let in_angle = a.angle();
282        let out_angle = b.angle();
283
284        if in_angle.is_none() && out_angle.is_none() {
285            return;
286        }
287
288        if in_angle.is_none() || out_angle.is_none() {
289            return self.push_line([points[0], points[2]]);
290        }
291
292        self.x.extend(points.iter().map(|p| p.point.x));
293        self.y.extend(points.iter().map(|p| p.point.y));
294        self.weight.extend(points.iter().map(|p| p.weight));
295
296        let spline = self.last_spline_or_insert_with(in_angle, p0, |contour| Spline {
297            curvature: 0.0,
298            p0,
299            p2,
300            contour: Some(contour),
301        });
302
303        spline.p2 = p2;
304
305        let h = a - b;
306
307        let cross = (p2.x - p0.x).mul_add(h.y, -(p2.y - p0.y) * h.x);
308        let cross_recip = cross.recip();
309
310        let mut x0 = a.x.mul_add(h.x, a.y * h.y) * cross_recip;
311        let x2 = b.x.mul_add(h.x, b.y * h.y) * cross_recip;
312        let mut dx_recip = (x2 - x0).recip();
313
314        let scale = (cross / (h.len() * (x2 - x0))).abs();
315
316        let mut k0 = curvature(x0);
317        let k2 = curvature(x2);
318
319        let mut dk = k2 - k0;
320        let mut current_curvature = 0.5 * dk.abs() * (scale * PIXEL_ACCURACY_RECIP).sqrt();
321
322        // Points are collinear.
323        if !current_curvature.is_finite() || current_curvature <= 1.0 {
324            // These values are chosen such that the resulting points will be found at t = 0.5 and
325            // t = 1.0.
326            x0 = 0.036_624_67;
327            dx_recip = 1.0;
328            k0 = 0.0;
329            dk = 1.0;
330
331            current_curvature = 2.0;
332        }
333
334        let total_curvature = spline.curvature + current_curvature;
335
336        spline.curvature = total_curvature;
337
338        self.last_angle = out_angle;
339
340        self.x0.push(x0);
341        self.dx_recip.push(dx_recip);
342        self.k0.push(k0);
343        self.dk.push(dk);
344        self.curvatures_recip.push(current_curvature.recip());
345        self.partial_curvatures
346            .push((self.splines.len() as u32 - 1, total_curvature));
347    }
348
349    pub fn push_cubic(&mut self, points: [WeightedPoint; 4]) {
350        const MAX_CUBIC_ERROR_SQUARED: f32 = (36.0 * 36.0 / 3.0) * MAX_ERROR * MAX_ERROR;
351
352        let p0 = points[0].applied();
353        let p1 = points[1].applied();
354        let p2 = points[2].applied();
355
356        let dx = p2.x.mul_add(3.0, -p0.x) - p1.x.mul_add(3.0, -p1.x);
357        let dy = p2.y.mul_add(3.0, -p0.y) - p1.y.mul_add(3.0, -p1.y);
358
359        let err = dx.mul_add(dx, dy * dy);
360
361        let mult = points[1].weight.max(points[2].weight).max(1.0);
362
363        let subdivisions = (((err * MAX_CUBIC_ERROR_SQUARED.recip()).powf(1.0 / 6.0) * mult).ceil()
364            as usize)
365            .max(1);
366        let incr = (subdivisions as f32).recip();
367
368        let mut quad_p0 = p0;
369        for i in 1..=subdivisions {
370            let t = i as f32 * incr;
371
372            let quad_p2 = eval_cubic(t, &points).applied();
373
374            let mid_point = eval_cubic(t - 0.5 * incr, &points).applied();
375
376            let quad_p1 = Point {
377                x: mid_point.x.mul_add(2.0, -0.5 * (quad_p0.x + quad_p2.x)),
378                y: mid_point.y.mul_add(2.0, -0.5 * (quad_p0.y + quad_p2.y)),
379            };
380
381            self.push_quad([
382                WeightedPoint {
383                    point: quad_p0,
384                    weight: 1.0,
385                },
386                WeightedPoint {
387                    point: quad_p1,
388                    weight: 1.0,
389                },
390                WeightedPoint {
391                    point: quad_p2,
392                    weight: 1.0,
393                },
394            ]);
395
396            quad_p0 = quad_p2;
397        }
398    }
399
400    pub fn populate_buffers(&self, buffers: &mut ScratchBuffers) {
401        buffers.clear();
402
403        let mut i = 0;
404        let mut last_spline = None;
405        for (spline_i, spline) in self.splines.iter().enumerate() {
406            let subdivisions = spline.curvature.ceil() as usize;
407            let point_command = spline.curvature / subdivisions as f32;
408
409            let needs_start_point = last_spline.map_or(true, |last_spline: &Spline| {
410                last_spline.contour.is_some() || (last_spline.p2 - spline.p0).len() > MAX_ERROR
411            });
412
413            if needs_start_point {
414                buffers.point_indices.push(Default::default());
415                buffers.quad_indices.push(Default::default());
416                buffers
417                    .point_commands
418                    .push(PointCommand::Start(spline_i).into());
419            }
420
421            for pi in 1..subdivisions {
422                if pi as f32 > self.partial_curvatures[i].1 {
423                    i += 1;
424                }
425
426                buffers.point_indices.push(pi);
427                buffers.quad_indices.push(i);
428                buffers
429                    .point_commands
430                    .push(PointCommand::Incr(point_command).into());
431            }
432
433            buffers.point_indices.push(Default::default());
434            buffers.quad_indices.push(Default::default());
435            buffers
436                .point_commands
437                .push(PointCommand::End(spline_i, spline.contour.is_some()).into());
438
439            last_spline = Some(spline);
440
441            if subdivisions > 0 {
442                i += 1;
443            }
444        }
445    }
446
447    pub fn eval_quad(&self, quad_index: usize, t: f32) -> Point {
448        let i0 = 3 * quad_index;
449        let i1 = i0 + 1;
450        let i2 = i0 + 2;
451
452        let weight = lerp(
453            t,
454            lerp(t, self.weight[i0], self.weight[i1]),
455            lerp(t, self.weight[i1], self.weight[i2]),
456        );
457        let w_recip = weight.recip();
458
459        let x = lerp(
460            t,
461            lerp(t, self.x[i0], self.x[i1]),
462            lerp(t, self.x[i1], self.x[i2]),
463        ) * w_recip;
464        let y = lerp(
465            t,
466            lerp(t, self.y[i0], self.y[i1]),
467            lerp(t, self.y[i1], self.y[i2]),
468        ) * w_recip;
469
470        Point::new(x, y)
471    }
472
473    pub fn into_segments(self) -> Segments {
474        let mut segments = Segments::default();
475
476        thread_local!(static BUFFERS: RefCell<ScratchBuffers> = RefCell::new(ScratchBuffers {
477            point_indices: Vec::new(),
478            quad_indices: Vec::new(),
479            point_commands: Vec::new(),
480        }));
481
482        BUFFERS.with(|buffers| {
483            let mut buffers = buffers.borrow_mut();
484
485            self.populate_buffers(&mut buffers);
486
487            let points = buffers
488                .point_indices
489                .par_iter()
490                .with_min_len(MIN_LEN)
491                .zip(buffers.quad_indices.par_iter().with_min_len(MIN_LEN))
492                .zip(buffers.point_commands.par_iter().with_min_len(MIN_LEN))
493                .map(|((&pi, &qi), &point_command)| {
494                    let incr = match PointCommand::from(point_command) {
495                        PointCommand::Start(spline_i) => {
496                            let point = self.splines[spline_i].p0;
497                            return ((point.x, point.y), false);
498                        }
499                        PointCommand::End(spline_i, new_contour) => {
500                            let point = self.splines[spline_i].p2;
501                            return ((point.x, point.y), new_contour);
502                        }
503                        PointCommand::Incr(incr) => incr,
504                    };
505
506                    let spline_i = self.partial_curvatures[qi].0;
507
508                    let previous_curvature = qi
509                        .checked_sub(1)
510                        .and_then(|i| {
511                            let partial_curvature = self.partial_curvatures[i];
512                            (partial_curvature.0 == spline_i).then_some(partial_curvature.1)
513                        })
514                        .unwrap_or_default();
515                    let ratio =
516                        incr.mul_add(pi as f32, -previous_curvature) * self.curvatures_recip[qi];
517
518                    let x = inv_curvature(ratio.mul_add(self.dk[qi], self.k0[qi]));
519
520                    let t = ((x - self.x0[qi]) * self.dx_recip[qi]).clamp(0.0, 1.0);
521
522                    let point = self.eval_quad(qi, t);
523
524                    ((point.x, point.y), false)
525                });
526
527            (
528                (
529                    ExtendVec::new(&mut segments.x),
530                    ExtendVec::new(&mut segments.y),
531                ),
532                ExtendVec::new(&mut segments.start_new_contour),
533            )
534                .par_extend(points);
535        });
536
537        segments
538    }
539}
540
541impl Default for Primitives {
542    fn default() -> Self {
543        Self {
544            last_angle: None,
545            contour: Some(Contour),
546            splines: Default::default(),
547            x: Default::default(),
548            y: Default::default(),
549            weight: Default::default(),
550            x0: Default::default(),
551            dx_recip: Default::default(),
552            k0: Default::default(),
553            dk: Default::default(),
554            curvatures_recip: Default::default(),
555            partial_curvatures: Default::default(),
556        }
557    }
558}
559
560#[derive(Clone, Copy, Debug, PartialEq)]
561enum PathCommand {
562    Move,
563    Line,
564    Quad,
565    Cubic,
566}
567#[derive(Debug, Default)]
568struct Segments {
569    x: Vec<f32>,
570    y: Vec<f32>,
571    start_new_contour: Vec<bool>,
572}
573
574#[derive(Debug)]
575struct PathData {
576    x: Vec<f32>,
577    y: Vec<f32>,
578    weight: Vec<f32>,
579    commands: Vec<PathCommand>,
580    open_point_index: usize,
581    segments: Option<Segments>,
582}
583
584macro_rules! points {
585    ( $inner:expr , $i:expr , $( $d:expr ),+ $( , )? ) => {[
586        $(
587            WeightedPoint {
588                point: Point::new($inner.x[$i - $d], $inner.y[$i - $d]),
589                weight: $inner.weight[$i - $d],
590            },
591        )*
592    ]};
593}
594
595impl PathData {
596    pub fn close(&mut self) {
597        let len = self.x.len();
598
599        let last_point = WeightedPoint {
600            point: Point::new(self.x[len - 1], self.y[len - 1]),
601            weight: self.weight[len - 1],
602        };
603        let open_point = WeightedPoint {
604            point: Point::new(self.x[self.open_point_index], self.y[self.open_point_index]),
605            weight: self.weight[self.open_point_index],
606        };
607
608        if last_point.applied() != open_point.applied() {
609            self.x.push(open_point.point.x);
610            self.y.push(open_point.point.y);
611            self.weight.push(open_point.weight);
612
613            self.commands.push(PathCommand::Line);
614        }
615    }
616
617    pub fn segments(&mut self) -> &Segments {
618        if self.segments.is_none() {
619            let mut primitives = Primitives::default();
620
621            let mut i = 0;
622            for command in &self.commands {
623                match command {
624                    PathCommand::Move => {
625                        i += 1;
626
627                        primitives.push_contour(Contour);
628                    }
629                    PathCommand::Line => {
630                        i += 1;
631
632                        let points = points!(self, i, 2, 1);
633                        primitives.push_line(points);
634                    }
635                    PathCommand::Quad => {
636                        i += 2;
637
638                        let points = points!(self, i, 3, 2, 1);
639                        primitives.push_quad(points);
640                    }
641                    PathCommand::Cubic => {
642                        i += 3;
643
644                        let points = points!(self, i, 4, 3, 2, 1);
645                        primitives.push_cubic(points);
646                    }
647                }
648            }
649
650            self.segments = Some(primitives.into_segments());
651        }
652
653        self.segments.as_ref().unwrap()
654    }
655}
656
657impl Default for PathData {
658    fn default() -> Self {
659        Self {
660            x: vec![0.0],
661            y: vec![0.0],
662            weight: vec![1.0],
663            commands: vec![PathCommand::Move],
664            open_point_index: 0,
665            segments: None,
666        }
667    }
668}
669
670#[derive(Clone, Debug, Default)]
671pub struct Path {
672    inner: Rc<RefCell<PathData>>,
673    transform: Option<GeomPresTransform>,
674}
675
676impl Path {
677    pub(crate) fn push_segments_to(
678        &self,
679        x: &mut Vec<f32>,
680        y: &mut Vec<f32>,
681        id: GeomId,
682        ids: &mut Vec<Option<GeomId>>,
683    ) {
684        let mut inner = self.inner.borrow_mut();
685
686        let segments = inner.segments();
687        let transform = &self.transform;
688
689        if let Some(transform) = &transform {
690            let iter = segments
691                .x
692                .par_iter()
693                .with_min_len(MIN_LEN)
694                .zip(
695                    segments
696                        .y
697                        .par_iter()
698                        .with_min_len(MIN_LEN)
699                        .zip(segments.start_new_contour.par_iter().with_min_len(MIN_LEN)),
700                )
701                .map(|(&x, (&y, start_new_contour))| {
702                    let point = transform.transform(Point::new(x, y));
703                    (point.x, point.y, (!start_new_contour).then_some(id))
704                });
705
706            ExtendTuple3::new((x, y, ids)).par_extend(iter);
707        } else {
708            let iter = segments
709                .x
710                .par_iter()
711                .with_min_len(MIN_LEN)
712                .zip(
713                    segments
714                        .y
715                        .par_iter()
716                        .with_min_len(MIN_LEN)
717                        .zip(segments.start_new_contour.par_iter().with_min_len(MIN_LEN)),
718                )
719                .map(|(&x, (&y, start_new_contour))| (x, y, (!start_new_contour).then_some(id)));
720
721            ExtendTuple3::new((x, y, ids)).par_extend(iter);
722        }
723    }
724
725    #[inline]
726    pub fn transform(&self, transform: &[f32; 9]) -> Self {
727        if let Some(transform) = GeomPresTransform::new(*transform) {
728            return Self {
729                inner: Rc::clone(&self.inner),
730                transform: Some(transform),
731            };
732        }
733
734        let inner = self.inner.borrow();
735        let mut data = PathData {
736            x: inner.x.clone(),
737            y: inner.y.clone(),
738            weight: inner.weight.clone(),
739            commands: inner.commands.clone(),
740            open_point_index: inner.open_point_index,
741            segments: None,
742        };
743
744        data.x
745            .par_iter_mut()
746            .with_min_len(MIN_LEN)
747            .zip(
748                data.y
749                    .par_iter_mut()
750                    .with_min_len(MIN_LEN)
751                    .zip(data.weight.par_iter_mut().with_min_len(MIN_LEN)),
752            )
753            .for_each(|(x, (y, weight))| {
754                (*x, *y, *weight) = (
755                    transform[0].mul_add(*x, transform[1].mul_add(*y, transform[2] * *weight)),
756                    transform[3].mul_add(*x, transform[4].mul_add(*y, transform[5] * *weight)),
757                    transform[6].mul_add(*x, transform[7].mul_add(*y, transform[8] * *weight)),
758                );
759            });
760
761        Self {
762            inner: Rc::new(RefCell::new(data)),
763            transform: None,
764        }
765    }
766}
767
768impl Eq for Path {}
769
770impl PartialEq for Path {
771    fn eq(&self, other: &Self) -> bool {
772        Rc::ptr_eq(&self.inner, &other.inner)
773    }
774}
775
776#[derive(Clone, Debug, Default)]
777pub struct PathBuilder {
778    inner: Rc<RefCell<PathData>>,
779}
780
781impl PathBuilder {
782    #[inline]
783    pub fn new() -> Self {
784        Self::default()
785    }
786
787    #[inline]
788    pub fn move_to(&mut self, p: Point) -> &mut Self {
789        {
790            let mut inner = self.inner.borrow_mut();
791            let len = inner.x.len();
792
793            if matches!(inner.commands[inner.commands.len() - 1], PathCommand::Move) {
794                inner.x[len - 1] = p.x;
795                inner.y[len - 1] = p.y;
796                inner.weight[len - 1] = 1.0;
797            } else {
798                inner.close();
799
800                let open_point_index = inner.x.len();
801
802                inner.x.push(p.x);
803                inner.y.push(p.y);
804                inner.weight.push(1.0);
805
806                inner.commands.push(PathCommand::Move);
807
808                inner.open_point_index = open_point_index;
809            }
810        }
811
812        self
813    }
814
815    #[inline]
816    pub fn line_to(&mut self, p: Point) -> &mut Self {
817        {
818            let mut inner = self.inner.borrow_mut();
819
820            inner.x.push(p.x);
821            inner.y.push(p.y);
822            inner.weight.push(1.0);
823
824            inner.commands.push(PathCommand::Line);
825        }
826
827        self
828    }
829
830    #[inline]
831    pub fn quad_to(&mut self, p1: Point, p2: Point) -> &mut Self {
832        {
833            let mut inner = self.inner.borrow_mut();
834
835            inner.x.push(p1.x);
836            inner.y.push(p1.y);
837            inner.weight.push(1.0);
838
839            inner.x.push(p2.x);
840            inner.y.push(p2.y);
841            inner.weight.push(1.0);
842
843            inner.commands.push(PathCommand::Quad);
844        }
845
846        self
847    }
848
849    #[inline]
850    pub fn cubic_to(&mut self, p1: Point, p2: Point, p3: Point) -> &mut Self {
851        {
852            let mut inner = self.inner.borrow_mut();
853
854            inner.x.push(p1.x);
855            inner.y.push(p1.y);
856            inner.weight.push(1.0);
857
858            inner.x.push(p2.x);
859            inner.y.push(p2.y);
860            inner.weight.push(1.0);
861
862            inner.x.push(p3.x);
863            inner.y.push(p3.y);
864            inner.weight.push(1.0);
865
866            inner.commands.push(PathCommand::Cubic);
867        }
868
869        self
870    }
871
872    #[inline]
873    pub fn rat_quad_to(&mut self, p1: Point, p2: Point, weight: f32) -> &mut Self {
874        {
875            let mut inner = self.inner.borrow_mut();
876
877            inner.x.push(p1.x * weight);
878            inner.y.push(p1.y * weight);
879            inner.weight.push(weight);
880
881            inner.x.push(p2.x);
882            inner.y.push(p2.y);
883            inner.weight.push(1.0);
884
885            inner.commands.push(PathCommand::Quad);
886        }
887
888        self
889    }
890
891    #[inline]
892    pub fn rat_cubic_to(&mut self, p1: Point, p2: Point, p3: Point, w1: f32, w2: f32) -> &mut Self {
893        {
894            let mut inner = self.inner.borrow_mut();
895
896            inner.x.push(p1.x * w1);
897            inner.y.push(p1.y * w1);
898            inner.weight.push(w1);
899
900            inner.x.push(p2.x * w2);
901            inner.y.push(p2.y * w2);
902            inner.weight.push(w2);
903
904            inner.x.push(p3.x);
905            inner.y.push(p3.y);
906            inner.weight.push(1.0);
907
908            inner.commands.push(PathCommand::Cubic);
909        }
910
911        self
912    }
913
914    #[inline]
915    pub fn build(&mut self) -> Path {
916        let mut inner = self.inner.borrow_mut();
917
918        inner.close();
919
920        Path {
921            inner: Rc::clone(&self.inner),
922            transform: None,
923        }
924    }
925}
926
927#[cfg(test)]
928mod tests {
929    use super::*;
930
931    use crate::math::Point;
932
933    fn dist(p0: Point, p1: Point, p2: Point) -> f32 {
934        let d10 = p1 - p0;
935        let d21 = p2 - p1;
936
937        (d21.x * d10.y - d10.x * d21.y).abs() / d21.len()
938    }
939
940    fn min_dist(p: Point, points: &[Point]) -> f32 {
941        points
942            .windows(2)
943            .map(|window| dist(p, window[0], window[1]))
944            .min_by(|a, b| a.partial_cmp(b).unwrap())
945            .unwrap()
946    }
947
948    fn eval_quad(t: f32, points: &[WeightedPoint; 3]) -> Point {
949        let x = lerp(
950            t,
951            lerp(t, points[0].point.x, points[1].point.x),
952            lerp(t, points[1].point.x, points[2].point.x),
953        );
954        let y = lerp(
955            t,
956            lerp(t, points[0].point.y, points[1].point.y),
957            lerp(t, points[1].point.y, points[2].point.y),
958        );
959
960        Point { x, y }
961    }
962
963    macro_rules! curve {
964        (
965            ( $p0x:expr , $p0y:expr ) ,
966            ( $p1x:expr , $p1y:expr ) ,
967            ( $p2x:expr , $p2y:expr ) ,
968            ( $p3x:expr , $p3y:expr ) $( , )?
969        ) => {
970            [
971                WeightedPoint {
972                    point: Point::new($p0x, $p0y),
973                    weight: 1.0,
974                },
975                WeightedPoint {
976                    point: Point::new($p1x, $p1y),
977                    weight: 1.0,
978                },
979                WeightedPoint {
980                    point: Point::new($p2x, $p2y),
981                    weight: 1.0,
982                },
983                WeightedPoint {
984                    point: Point::new($p3x, $p3y),
985                    weight: 1.0,
986                },
987            ]
988        };
989        (
990            ( $p0x:expr , $p0y:expr ) ,
991            ( $p1x:expr , $p1y:expr ) ,
992            ( $p2x:expr , $p2y:expr ) $( , )?
993        ) => {
994            [
995                WeightedPoint {
996                    point: Point::new($p0x, $p0y),
997                    weight: 1.0,
998                },
999                WeightedPoint {
1000                    point: Point::new($p1x, $p1y),
1001                    weight: 1.0,
1002                },
1003                WeightedPoint {
1004                    point: Point::new($p2x, $p2y),
1005                    weight: 1.0,
1006                },
1007            ]
1008        };
1009        ( ( $p0x:expr , $p0y:expr ) , ( $p1x:expr , $p1y:expr ) $( , )? ) => {
1010            [
1011                WeightedPoint {
1012                    point: Point::new($p0x, $p0y),
1013                    weight: 1.0,
1014                },
1015                WeightedPoint {
1016                    point: Point::new($p1x, $p1y),
1017                    weight: 1.0,
1018                },
1019            ]
1020        };
1021    }
1022
1023    #[test]
1024    fn quads() {
1025        let mut primitives = Primitives::default();
1026
1027        let c0 = curve![(2.0, 0.0), (0.0, 1.0), (10.0, 1.0)];
1028        let c1 = curve![(10.0, 1.0), (20.0, 1.0), (18.0, 0.0)];
1029
1030        primitives.push_quad(c0);
1031        primitives.push_quad(c1);
1032
1033        let segments = primitives.into_segments();
1034
1035        assert_eq!(segments.x.len(), 9);
1036
1037        assert_eq!(segments.x[0], 2.0);
1038        assert_eq!(segments.y[0], 0.0);
1039        assert_eq!(segments.x[8], 18.0);
1040        assert_eq!(segments.y[8], 0.0);
1041
1042        let a = Point::new(segments.x[3], segments.y[3]);
1043        let b = Point::new(segments.x[5], segments.y[5]);
1044
1045        assert!((a - b).len() > 10.0);
1046
1047        let points: Vec<_> = segments
1048            .x
1049            .iter()
1050            .zip(segments.y.iter())
1051            .map(|(&x, &y)| Point::new(x, y))
1052            .collect();
1053
1054        let min_c0 = (0..=50)
1055            .into_iter()
1056            .map(|i| {
1057                let t = i as f32 / 50.0;
1058
1059                min_dist(eval_quad(t, &c0), &points)
1060            })
1061            .max_by(|a, b| a.partial_cmp(b).unwrap())
1062            .unwrap();
1063        let min_c1 = (0..=50)
1064            .into_iter()
1065            .map(|i| {
1066                let t = i as f32 / 50.0;
1067
1068                min_dist(eval_quad(t, &c1), &points)
1069            })
1070            .max_by(|a, b| a.partial_cmp(b).unwrap())
1071            .unwrap();
1072
1073        assert!(min_c0 < MAX_ERROR);
1074        assert!(min_c1 < MAX_ERROR);
1075    }
1076
1077    #[test]
1078    fn two_splines() {
1079        let mut primitives = Primitives::default();
1080
1081        primitives.push_quad(curve![(0.0, 0.0), (1.0, 2.0), (2.0, 0.0)]);
1082        primitives.push_quad(curve![(3.0, 0.0), (4.0, 4.0), (5.0, 0.0)]);
1083
1084        let segments = primitives.into_segments();
1085
1086        assert_eq!(segments.x.len(), 11);
1087
1088        assert_eq!(segments.x[0], 0.0);
1089        assert_eq!(segments.y[0], 0.0);
1090        assert_eq!(segments.x[4], 2.0);
1091        assert_eq!(segments.y[4], 0.0);
1092        assert_eq!(segments.x[5], 3.0);
1093        assert_eq!(segments.y[5], 0.0);
1094        assert_eq!(segments.x[10], 5.0);
1095        assert_eq!(segments.y[10], 0.0);
1096    }
1097
1098    #[test]
1099    fn collinear_quad() {
1100        let mut primitives = Primitives::default();
1101
1102        primitives.push_quad(curve![(0.0, 0.0), (2.0, 0.0001), (1.0, 0.0)]);
1103
1104        let segments = primitives.into_segments();
1105
1106        assert_eq!(segments.x.len(), 3);
1107
1108        assert!((segments.x[1] - 1.25).abs() < 0.01);
1109        assert!((segments.y[1] - 0.0).abs() < 0.01);
1110    }
1111
1112    #[test]
1113    fn overlapping_control_point_quad() {
1114        let mut primitives = Primitives::default();
1115
1116        primitives.push_quad(curve![(0.0, 0.0), (0.0, 0.0), (1.0, 1.0)]);
1117        primitives.push_quad(curve![(1.0, 1.0), (1.0, 1.0), (1.0, 1.0)]);
1118        primitives.push_quad(curve![(1.0, 1.0), (2.0, 2.0), (2.0, 2.0)]);
1119
1120        let segments = primitives.into_segments();
1121
1122        assert_eq!(segments.x.len(), 2);
1123
1124        assert!((segments.x[0] - 0.0).abs() < 0.01);
1125        assert!((segments.y[0] - 0.0).abs() < 0.01);
1126        assert!((segments.x[1] - 2.0).abs() < 0.01);
1127        assert!((segments.y[1] - 2.0).abs() < 0.01);
1128    }
1129
1130    #[test]
1131    fn rat_quad() {
1132        let mut primitives = Primitives::default();
1133        let weight = 10.0;
1134
1135        primitives.push_quad([
1136            WeightedPoint {
1137                point: Point::new(0.0, 0.0),
1138                weight: 1.0,
1139            },
1140            WeightedPoint {
1141                point: Point::new(1.0 * weight, 2.0 * weight),
1142                weight,
1143            },
1144            WeightedPoint {
1145                point: Point::new(2.0, 0.0),
1146                weight: 1.0,
1147            },
1148        ]);
1149
1150        let segments = primitives.into_segments();
1151
1152        assert_eq!(segments.x.len(), 5);
1153
1154        let points: Vec<_> = segments
1155            .x
1156            .iter()
1157            .zip(segments.y.iter())
1158            .map(|(&x, &y)| Point::new(x, y))
1159            .collect();
1160
1161        assert!((points[2].x - 1.0).abs() <= 0.001);
1162
1163        let distances: Vec<_> = points
1164            .windows(2)
1165            .map(|window| (window[1] - window[0]).len())
1166            .collect();
1167
1168        assert!(distances[0] > 1.5);
1169        assert!(distances[1] < 0.2);
1170        assert!(distances[2] < 0.2);
1171        assert!(distances[3] > 1.5);
1172    }
1173
1174    #[test]
1175    fn lines_and_quads() {
1176        let mut primitives = Primitives::default();
1177
1178        primitives.push_line(curve![(-1.0, -2.0), (0.0, 0.0)]);
1179        primitives.push_quad(curve![(0.0, 0.0), (1.0, 2.0), (2.0, 0.0)]);
1180        primitives.push_line(curve![(2.0, 0.0), (3.0, -2.0)]);
1181        primitives.push_line(curve![(3.0, -2.0), (4.0, 2.0)]);
1182        primitives.push_line(curve![(4.0, 2.0), (5.0, -4.0)]);
1183        primitives.push_line(curve![(5.0, -4.0), (6.0, 0.0)]);
1184        primitives.push_quad(curve![(6.0, 0.0), (7.0, 4.0), (8.0, 0.0)]);
1185        primitives.push_line(curve![(8.0, 0.0), (9.0, -4.0)]);
1186
1187        let segments = primitives.into_segments();
1188
1189        assert_eq!(segments.x.len(), 12);
1190
1191        assert_eq!(segments.x[0], -1.0);
1192        assert_eq!(segments.y[0], -2.0);
1193        assert_eq!(segments.x[4], 3.0);
1194        assert_eq!(segments.y[4], -2.0);
1195        assert_eq!(segments.x[5], 4.0);
1196        assert_eq!(segments.y[5], 2.0);
1197        assert_eq!(segments.x[6], 5.0);
1198        assert_eq!(segments.y[6], -4.0);
1199        assert_eq!(segments.x[11], 9.0);
1200        assert_eq!(segments.y[11], -4.0);
1201    }
1202
1203    #[test]
1204    fn cubic() {
1205        let mut primitives = Primitives::default();
1206
1207        primitives.push_cubic(curve![(0.0, 0.0), (10.0, 6.0), (-2.0, 6.0), (8.0, 0.0)]);
1208
1209        let segments = primitives.into_segments();
1210
1211        assert_eq!(segments.x.len(), 10);
1212
1213        assert!(segments.x[2] > segments.x[7]);
1214        assert!(segments.x[3] > segments.x[6]);
1215        assert!(segments.x[4] > segments.x[5]);
1216
1217        assert!(segments.y[0] < segments.y[1]);
1218        assert!(segments.y[1] < segments.y[2]);
1219        assert!(segments.y[2] < segments.y[3]);
1220        assert!(segments.y[3] < segments.y[4]);
1221
1222        assert!(segments.y[5] > segments.y[6]);
1223        assert!(segments.y[6] > segments.y[7]);
1224        assert!(segments.y[7] > segments.y[8]);
1225        assert!(segments.y[8] > segments.y[9]);
1226    }
1227
1228    #[test]
1229    fn rat_cubic_high() {
1230        let mut primitives = Primitives::default();
1231        let weight = 10.0;
1232
1233        primitives.push_cubic([
1234            WeightedPoint {
1235                point: Point::new(0.0, 0.0),
1236                weight: 1.0,
1237            },
1238            WeightedPoint {
1239                point: Point::new(5.0 * weight, 3.0 * weight),
1240                weight,
1241            },
1242            WeightedPoint {
1243                point: Point::new(-1.0 * weight, 3.0 * weight),
1244                weight,
1245            },
1246            WeightedPoint {
1247                point: Point::new(4.0, 0.0),
1248                weight: 1.0,
1249            },
1250        ]);
1251
1252        let segments = primitives.into_segments();
1253
1254        assert_eq!(segments.x.len(), 45);
1255    }
1256
1257    #[test]
1258    fn rat_cubic_low() {
1259        let mut primitives = Primitives::default();
1260        let weight = 0.5;
1261
1262        primitives.push_cubic([
1263            WeightedPoint {
1264                point: Point::new(0.0, 0.0),
1265                weight: 1.0,
1266            },
1267            WeightedPoint {
1268                point: Point::new(5.0 * weight, 3.0 * weight),
1269                weight,
1270            },
1271            WeightedPoint {
1272                point: Point::new(-1.0 * weight, 3.0 * weight),
1273                weight,
1274            },
1275            WeightedPoint {
1276                point: Point::new(4.0, 0.0),
1277                weight: 1.0,
1278            },
1279        ]);
1280
1281        let segments = primitives.into_segments();
1282
1283        assert_eq!(segments.x.len(), 7);
1284    }
1285
1286    #[test]
1287    fn collinear_cubic() {
1288        let mut primitives = Primitives::default();
1289
1290        primitives.push_cubic(curve![(1.0, 0.0), (0.0, 0.0), (3.0, 0.0), (2.0, 0.0)]);
1291
1292        let segments = primitives.into_segments();
1293
1294        assert_eq!(segments.x.len(), 5);
1295
1296        assert_eq!(segments.x[0], 1.0);
1297        assert_eq!(segments.y[0], 0.0);
1298
1299        assert!(segments.x[1] > 0.5);
1300        assert!(segments.x[1] < 1.0);
1301        assert_eq!(segments.y[1], 0.0);
1302
1303        assert!(segments.x[2] > 1.0);
1304        assert!(segments.x[2] < 2.0);
1305        assert_eq!(segments.y[2], 0.0);
1306
1307        assert!(segments.x[3] > 2.0);
1308        assert!(segments.x[3] < 2.5);
1309        assert_eq!(segments.y[3], 0.0);
1310
1311        assert_eq!(segments.x[4], 2.0);
1312        assert_eq!(segments.y[4], 0.0);
1313    }
1314
1315    #[test]
1316    fn overlapping_control_point_cubic_line() {
1317        let mut primitives = Primitives::default();
1318
1319        primitives.push_cubic(curve![(0.0, 0.0), (0.0, 0.0), (1.0, 1.0), (1.0, 1.0)]);
1320        primitives.push_cubic(curve![(1.0, 1.0), (1.0, 1.0), (1.0, 1.0), (1.0, 1.0)]);
1321        primitives.push_cubic(curve![(1.0, 1.0), (1.0, 1.0), (2.0, 2.0), (2.0, 2.0)]);
1322
1323        let segments = primitives.into_segments();
1324
1325        assert_eq!(segments.x.len(), 9);
1326
1327        for x in segments.x.windows(2) {
1328            assert!(x[0] < x[1]);
1329        }
1330
1331        for y in segments.y.windows(2) {
1332            assert!(y[0] < y[1]);
1333        }
1334
1335        for (&x, &y) in segments.x.iter().zip(segments.y.iter()) {
1336            assert_eq!(x, y);
1337        }
1338
1339        assert!((segments.x[0] - 0.0).abs() < 0.01);
1340        assert!((segments.y[0] - 0.0).abs() < 0.01);
1341        assert!((segments.x[8] - 2.0).abs() < 0.01);
1342        assert!((segments.y[8] - 2.0).abs() < 0.01);
1343    }
1344
1345    #[test]
1346    fn ring() {
1347        let mut primitives = Primitives::default();
1348
1349        primitives.push_cubic(curve![(0.0, 2.0), (2.0, 2.0), (2.0, 2.0), (2.0, 0.0)]);
1350        primitives.push_cubic(curve![(2.0, 0.0), (2.0, -2.0), (2.0, -2.0), (0.0, -2.0)]);
1351        primitives.push_cubic(curve![(0.0, -2.0), (-2.0, -2.0), (-2.0, -2.0), (-2.0, 0.0)]);
1352        primitives.push_cubic(curve![(-2.0, 0.0), (-2.0, 2.0), (-2.0, 2.0), (0.0, 2.0)]);
1353
1354        primitives.push_contour(Contour);
1355
1356        primitives.push_cubic(curve![(0.0, 1.0), (-1.0, 1.0), (-1.0, 1.0), (-1.0, 0.0)]);
1357        primitives.push_cubic(curve![(-1.0, 0.0), (-1.0, -1.0), (-1.0, -1.0), (0.0, -1.0)]);
1358        primitives.push_cubic(curve![(0.0, -1.0), (1.0, -1.0), (1.0, -1.0), (1.0, 0.0)]);
1359        primitives.push_cubic(curve![(1.0, 0.0), (1.0, 1.0), (1.0, 1.0), (0.0, 1.0)]);
1360
1361        let segments = primitives.into_segments();
1362
1363        assert_eq!(segments.start_new_contour.len(), 30);
1364
1365        assert_eq!(
1366            segments
1367                .start_new_contour
1368                .iter()
1369                .filter(|&&start_new_contour| start_new_contour)
1370                .count(),
1371            2
1372        );
1373
1374        assert!(segments.start_new_contour[16]);
1375        assert!(segments.start_new_contour[29]);
1376    }
1377
1378    #[test]
1379    fn ring_overlapping_start() {
1380        let mut primitives = Primitives::default();
1381
1382        primitives.push_cubic(curve![(0.0, 1.0), (-1.0, 1.0), (-1.0, 1.0), (-1.0, 0.0)]);
1383        primitives.push_cubic(curve![(-1.0, 0.0), (-1.0, -1.0), (-1.0, -1.0), (0.0, -1.0)]);
1384        primitives.push_cubic(curve![(0.0, -1.0), (1.0, -1.0), (1.0, -1.0), (1.0, 0.0)]);
1385        primitives.push_cubic(curve![(1.0, 0.0), (1.0, 1.0), (1.0, 1.0), (0.0, 1.0)]);
1386
1387        primitives.push_contour(Contour);
1388
1389        primitives.push_cubic(curve![(0.0, 1.0), (1.0, 1.0), (1.0, 1.0), (1.0, 2.0)]);
1390        primitives.push_cubic(curve![(1.0, 2.0), (1.0, 3.0), (1.0, 3.0), (0.0, 3.0)]);
1391        primitives.push_cubic(curve![(0.0, 3.0), (-1.0, 3.0), (-1.0, 3.0), (-1.0, 2.0)]);
1392        primitives.push_cubic(curve![(-1.0, 2.0), (-1.0, 1.0), (-1.0, 1.0), (0.0, 1.0)]);
1393
1394        let segments = primitives.into_segments();
1395
1396        assert_eq!(segments.start_new_contour.len(), 26);
1397
1398        assert_eq!(
1399            segments
1400                .start_new_contour
1401                .iter()
1402                .filter(|&&start_new_contour| start_new_contour)
1403                .count(),
1404            2
1405        );
1406
1407        assert!(segments.start_new_contour[12]);
1408        assert!(segments.start_new_contour[25]);
1409    }
1410
1411    #[test]
1412    fn circle() {
1413        let mut primitives = Primitives::default();
1414        let radius = 50.0;
1415        let weight = 2.0f32.sqrt() / 2.0;
1416
1417        primitives.push_quad([
1418            WeightedPoint {
1419                point: Point::new(radius, 0.0),
1420                weight: 1.0,
1421            },
1422            WeightedPoint {
1423                point: Point::new(0.0, 0.0),
1424                weight,
1425            },
1426            WeightedPoint {
1427                point: Point::new(0.0, radius),
1428                weight: 1.0,
1429            },
1430        ]);
1431        primitives.push_quad([
1432            WeightedPoint {
1433                point: Point::new(0.0, radius),
1434                weight: 1.0,
1435            },
1436            WeightedPoint {
1437                point: Point::new(0.0, 2.0 * radius * weight),
1438                weight,
1439            },
1440            WeightedPoint {
1441                point: Point::new(radius, 2.0 * radius),
1442                weight: 1.0,
1443            },
1444        ]);
1445        primitives.push_quad([
1446            WeightedPoint {
1447                point: Point::new(radius, 2.0 * radius),
1448                weight: 1.0,
1449            },
1450            WeightedPoint {
1451                point: Point::new(2.0 * radius * weight, 2.0 * radius * weight),
1452                weight,
1453            },
1454            WeightedPoint {
1455                point: Point::new(2.0 * radius, radius),
1456                weight: 1.0,
1457            },
1458        ]);
1459        primitives.push_quad([
1460            WeightedPoint {
1461                point: Point::new(2.0 * radius, radius),
1462                weight: 1.0,
1463            },
1464            WeightedPoint {
1465                point: Point::new(2.0 * radius * weight, 0.0),
1466                weight,
1467            },
1468            WeightedPoint {
1469                point: Point::new(radius, 0.0),
1470                weight: 1.0,
1471            },
1472        ]);
1473
1474        let segments = primitives.into_segments();
1475
1476        assert_eq!(segments.x.len(), 66);
1477
1478        let points: Vec<_> = segments
1479            .x
1480            .iter()
1481            .zip(segments.y.iter())
1482            .map(|(&x, &y)| Point::new(x, y))
1483            .collect();
1484
1485        let max_distance = points
1486            .windows(2)
1487            .map(|window| (window[1] - window[0]).len())
1488            .max_by(|a, b| a.partial_cmp(b).unwrap())
1489            .unwrap();
1490
1491        assert!(max_distance < 5.0);
1492    }
1493
1494    #[test]
1495    fn transform_path() {
1496        let weight = 2.0f32.sqrt() / 2.0;
1497        let radius = 10.0;
1498
1499        let mut builder = PathBuilder::new();
1500
1501        builder.move_to(Point::new(radius, 0.0));
1502        builder.rat_quad_to(
1503            Point::new(radius, -radius),
1504            Point::new(0.0, -radius),
1505            weight,
1506        );
1507        builder.rat_quad_to(
1508            Point::new(-radius, -radius),
1509            Point::new(-radius, 0.0),
1510            weight,
1511        );
1512        builder.rat_quad_to(Point::new(-radius, radius), Point::new(0.0, radius), weight);
1513        builder.rat_quad_to(Point::new(radius, radius), Point::new(radius, 0.0), weight);
1514
1515        let path = builder.build();
1516
1517        let mut x = Vec::new();
1518        let mut y = Vec::new();
1519        let mut ids = Vec::new();
1520
1521        let id0 = GeomId::default();
1522        let id1 = id0.next();
1523
1524        path.push_segments_to(&mut x, &mut y, id1, &mut ids);
1525
1526        let orig_len = x.len();
1527
1528        assert_eq!(ids[..ids.len() - 1], vec![Some(id1); ids.len() - 1]);
1529        assert_eq!(ids[ids.len() - 1], None);
1530
1531        for (&x, &y) in x.iter().zip(y.iter()) {
1532            assert!((Point::new(x, y).len() - radius).abs() <= 0.1);
1533        }
1534
1535        let dx = 5.0;
1536        let dy = 20.0;
1537        let translated_path = path.transform(&[1.0, 0.0, dx, 0.0, 1.0, dy, 0.0, 0.0, 1.0]);
1538
1539        x.clear();
1540        y.clear();
1541
1542        translated_path.push_segments_to(&mut x, &mut y, id0, &mut ids);
1543
1544        for (&x, &y) in x.iter().zip(y.iter()) {
1545            assert!((Point::new(x - dx, y - dy).len() - radius).abs() <= 0.1);
1546        }
1547
1548        let s = 2.0;
1549        let scaled_path = path.transform(&[s, 0.0, 0.0, 0.0, s, 0.0, 0.0, 0.0, 1.0]);
1550
1551        x.clear();
1552        y.clear();
1553
1554        scaled_path.push_segments_to(&mut x, &mut y, id0, &mut ids);
1555
1556        for (&x, &y) in x.iter().zip(y.iter()) {
1557            assert!((Point::new(x, y).len() - s * radius).abs() <= 0.1);
1558        }
1559
1560        let scaled_len = x.len();
1561
1562        assert!(scaled_len > orig_len);
1563    }
1564
1565    #[test]
1566    fn perspective_transform_path() {
1567        let weight = 2.0f32.sqrt() / 2.0;
1568        let radius = 10.0;
1569        let translation = 1000.0;
1570
1571        let mut builder = PathBuilder::new();
1572
1573        builder.move_to(Point::new(radius + translation, 0.0));
1574        builder.rat_quad_to(
1575            Point::new(radius + translation, -radius),
1576            Point::new(translation, -radius),
1577            weight,
1578        );
1579        builder.rat_quad_to(
1580            Point::new(-radius + translation, -radius),
1581            Point::new(-radius + translation, 0.0),
1582            weight,
1583        );
1584        builder.rat_quad_to(
1585            Point::new(-radius + translation, radius),
1586            Point::new(translation, radius),
1587            weight,
1588        );
1589        builder.rat_quad_to(
1590            Point::new(radius + translation, radius),
1591            Point::new(radius + translation, 0.0),
1592            weight,
1593        );
1594
1595        let path = builder
1596            .build()
1597            .transform(&[1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.001, 0.0, 1.0]);
1598
1599        let mut x = Vec::new();
1600        let mut y = Vec::new();
1601        let mut ids = Vec::new();
1602
1603        path.push_segments_to(&mut x, &mut y, GeomId::default(), &mut ids);
1604
1605        let mut points: Vec<_> = x
1606            .iter()
1607            .zip(y.iter())
1608            .map(|(&x, &y)| Point::new(x, y))
1609            .collect();
1610        points.pop(); // Remove duplicate point.
1611
1612        let half_len = points.len() / 2;
1613
1614        let min = (0..half_len)
1615            .into_iter()
1616            .map(|i| (points[i] - points[(i + half_len) % points.len()]).len())
1617            .min_by(|a, b| a.partial_cmp(b).unwrap())
1618            .unwrap();
1619        let max = (0..half_len)
1620            .into_iter()
1621            .map(|i| (points[i] - points[(i + half_len) % points.len()]).len())
1622            .max_by(|a, b| a.partial_cmp(b).unwrap())
1623            .unwrap();
1624
1625        assert!((min - radius / 2.0).abs() <= 0.2);
1626        assert!((max - radius).abs() <= 0.2);
1627    }
1628}