makepad_vector/geometry/
quadratic_segment.rs

1use crate::geometry::{Point, Transform, Transformation};
2use crate::internal_iter::InternalIterator;
3
4/// A quadratic bezier curve segment in 2-dimensional Euclidian space.
5#[derive(Clone, Copy, Debug, PartialEq)]
6#[repr(C)]
7pub struct QuadraticSegment {
8    pub p0: Point,
9    pub p1: Point,
10    pub p2: Point,
11}
12
13impl QuadraticSegment {
14    /// Creates a new quadratic bezier curve segment with the given control points.
15    pub fn new(p0: Point, p1: Point, p2: Point) -> QuadraticSegment {
16        QuadraticSegment { p0, p1, p2 }
17    }
18
19    /// Returns true if `self` is approximately linear with tolerance `epsilon`.
20    pub fn is_approximately_linear(self, epsilon: f64) -> bool {
21        let v1 = self.p1 - self.p0;
22        if let Some(vx) = (self.p2 - self.p0).normalize() {
23            // If the baseline is a line segment, the segment is approximately linear if the
24            // rejection of the control point from the baseline is less than `epsilon`.
25            v1.cross(vx).abs() < epsilon
26        } else {
27            // If the baseline is a single point, the segment is approximately linear if the
28            // distance of the control point from the baseline is less than `epsilon`.
29            v1.length() < epsilon
30        }
31    }
32
33    /// Splits `self` into two quadratic Bezier curve segments, at parameter `t`.
34    pub fn split(self, t: f64) -> (QuadraticSegment, QuadraticSegment) {
35        let p01 = self.p0.lerp(self.p1, t);
36        let p12 = self.p1.lerp(self.p2, t);
37        let p012 = p01.lerp(p12, t);
38        (
39            QuadraticSegment::new(self.p0, p01, p012),
40            QuadraticSegment::new(p012, p12, self.p2),
41        )
42    }
43
44    /// Returns an iterator over the points of a polyline that approximates `self` with tolerance
45    /// `epsilon`, *excluding* the first point.
46    pub fn linearize(self, epsilon: f64) -> Linearize {
47        Linearize {
48            segment: self,
49            epsilon,
50        }
51    }
52}
53
54impl Transform for QuadraticSegment {
55    fn transform<T>(self, t: &T) -> QuadraticSegment
56    where
57        T: Transformation,
58    {
59        QuadraticSegment::new(
60            self.p0.transform(t),
61            self.p1.transform(t),
62            self.p2.transform(t),
63        )
64    }
65
66    fn transform_mut<T>(&mut self, t: &T)
67    where
68        T: Transformation,
69    {
70        *self = self.transform(t);
71    }
72}
73
74/// An iterator over the points of a polyline that approximates `self` with tolerance `epsilon`,
75/// *excluding* the first point.
76#[derive(Clone, Copy)]
77pub struct Linearize {
78    segment: QuadraticSegment,
79    epsilon: f64,
80}
81
82impl InternalIterator for Linearize {
83    type Item = Point;
84
85    fn for_each<F>(self, f: &mut F) -> bool
86    where
87        F: FnMut(Point) -> bool,
88    {
89        if self.segment.is_approximately_linear(self.epsilon) {
90            return f(self.segment.p2);
91        }
92        let (segment_0, segment_1) = self.segment.split(0.5);
93        if !segment_0.linearize(self.epsilon).for_each(f) {
94            return false;
95        }
96        segment_1.linearize(self.epsilon).for_each(f)
97    }
98}