1 //! Lines.
2 
3 use std::ops::{Mul, Range};
4 
5 use arrayvec::ArrayVec;
6 
7 use crate::MAX_EXTREMA;
8 use crate::{
9     Affine, ParamCurve, ParamCurveArclen, ParamCurveArea, ParamCurveCurvature, ParamCurveDeriv,
10     ParamCurveExtrema, ParamCurveNearest, PathEl, Point, Rect, Shape,
11 };
12 
13 /// A single line.
14 #[derive(Clone, Copy, Debug, PartialEq)]
15 pub struct Line {
16     /// The line's start point.
17     pub p0: Point,
18     /// The line's end point.
19     pub p1: Point,
20 }
21 
22 impl Line {
23     /// Create a new line.
24     #[inline]
new(p0: impl Into<Point>, p1: impl Into<Point>) -> Line25     pub fn new(p0: impl Into<Point>, p1: impl Into<Point>) -> Line {
26         Line {
27             p0: p0.into(),
28             p1: p1.into(),
29         }
30     }
31 }
32 
33 impl ParamCurve for Line {
34     #[inline]
eval(&self, t: f64) -> Point35     fn eval(&self, t: f64) -> Point {
36         self.p0.lerp(self.p1, t)
37     }
38 
39     #[inline]
start(&self) -> Point40     fn start(&self) -> Point {
41         self.p0
42     }
43 
44     #[inline]
end(&self) -> Point45     fn end(&self) -> Point {
46         self.p1
47     }
48 
49     #[inline]
subsegment(&self, range: Range<f64>) -> Line50     fn subsegment(&self, range: Range<f64>) -> Line {
51         Line {
52             p0: self.eval(range.start),
53             p1: self.eval(range.end),
54         }
55     }
56 }
57 
58 impl ParamCurveDeriv for Line {
59     type DerivResult = ConstPoint;
60 
61     #[inline]
deriv(&self) -> ConstPoint62     fn deriv(&self) -> ConstPoint {
63         ConstPoint((self.p1 - self.p0).to_point())
64     }
65 }
66 
67 impl ParamCurveArclen for Line {
68     #[inline]
arclen(&self, _accuracy: f64) -> f6469     fn arclen(&self, _accuracy: f64) -> f64 {
70         (self.p1 - self.p0).hypot()
71     }
72 }
73 
74 impl ParamCurveArea for Line {
75     #[inline]
signed_area(&self) -> f6476     fn signed_area(&self) -> f64 {
77         self.p0.to_vec2().cross(self.p1.to_vec2()) * 0.5
78     }
79 }
80 
81 impl ParamCurveNearest for Line {
nearest(&self, p: Point, _accuracy: f64) -> (f64, f64)82     fn nearest(&self, p: Point, _accuracy: f64) -> (f64, f64) {
83         let d = self.p1 - self.p0;
84         let dotp = d.dot(p - self.p0);
85         let d_squared = d.dot(d);
86         if dotp <= 0.0 {
87             (0.0, (p - self.p0).hypot2())
88         } else if dotp >= d_squared {
89             (1.0, (p - self.p1).hypot2())
90         } else {
91             let t = dotp / d_squared;
92             let dist = (p - self.eval(t)).hypot2();
93             (t, dist)
94         }
95     }
96 }
97 
98 impl ParamCurveCurvature for Line {
99     #[inline]
curvature(&self, _t: f64) -> f64100     fn curvature(&self, _t: f64) -> f64 {
101         0.0
102     }
103 }
104 
105 impl ParamCurveExtrema for Line {
106     #[inline]
extrema(&self) -> ArrayVec<[f64; MAX_EXTREMA]>107     fn extrema(&self) -> ArrayVec<[f64; MAX_EXTREMA]> {
108         ArrayVec::new()
109     }
110 }
111 
112 /// A trivial "curve" that is just a constant.
113 #[derive(Clone, Copy, Debug)]
114 pub struct ConstPoint(Point);
115 
116 impl ParamCurve for ConstPoint {
117     #[inline]
eval(&self, _t: f64) -> Point118     fn eval(&self, _t: f64) -> Point {
119         self.0
120     }
121 
122     #[inline]
subsegment(&self, _range: Range<f64>) -> ConstPoint123     fn subsegment(&self, _range: Range<f64>) -> ConstPoint {
124         *self
125     }
126 }
127 
128 impl ParamCurveDeriv for ConstPoint {
129     type DerivResult = ConstPoint;
130 
131     #[inline]
deriv(&self) -> ConstPoint132     fn deriv(&self) -> ConstPoint {
133         ConstPoint(Point::new(0.0, 0.0))
134     }
135 }
136 
137 impl ParamCurveArclen for ConstPoint {
138     #[inline]
arclen(&self, _accuracy: f64) -> f64139     fn arclen(&self, _accuracy: f64) -> f64 {
140         0.0
141     }
142 }
143 
144 impl Mul<Line> for Affine {
145     type Output = Line;
146 
147     #[inline]
mul(self, other: Line) -> Line148     fn mul(self, other: Line) -> Line {
149         Line {
150             p0: self * other.p0,
151             p1: self * other.p1,
152         }
153     }
154 }
155 
156 /// An iterator yielding the path for a single line.
157 #[doc(hidden)]
158 pub struct LinePathIter {
159     line: Line,
160     ix: usize,
161 }
162 
163 impl Shape for Line {
164     type BezPathIter = LinePathIter;
165 
166     #[inline]
to_bez_path(&self, _tolerance: f64) -> LinePathIter167     fn to_bez_path(&self, _tolerance: f64) -> LinePathIter {
168         LinePathIter { line: *self, ix: 0 }
169     }
170 
171     /// Returning zero here is consistent with the contract (area is
172     /// only meaningful for closed shapes), but an argument can be made
173     /// that the contract should be tightened to include the Green's
174     /// theorem contribution.
area(&self) -> f64175     fn area(&self) -> f64 {
176         0.0
177     }
178 
179     #[inline]
perimeter(&self, _accuracy: f64) -> f64180     fn perimeter(&self, _accuracy: f64) -> f64 {
181         (self.p1 - self.p0).hypot()
182     }
183 
184     /// Same consideration as `area`.
winding(&self, _pt: Point) -> i32185     fn winding(&self, _pt: Point) -> i32 {
186         0
187     }
188 
189     #[inline]
bounding_box(&self) -> Rect190     fn bounding_box(&self) -> Rect {
191         Rect::from_points(self.p0, self.p1)
192     }
193 
194     #[inline]
as_line(&self) -> Option<Line>195     fn as_line(&self) -> Option<Line> {
196         Some(*self)
197     }
198 }
199 
200 impl Iterator for LinePathIter {
201     type Item = PathEl;
202 
next(&mut self) -> Option<PathEl>203     fn next(&mut self) -> Option<PathEl> {
204         self.ix += 1;
205         match self.ix {
206             1 => Some(PathEl::MoveTo(self.line.p0)),
207             2 => Some(PathEl::LineTo(self.line.p1)),
208             _ => None,
209         }
210     }
211 }
212 
213 #[cfg(test)]
214 mod tests {
215     use crate::{Line, ParamCurveArclen};
216 
217     #[test]
line_arclen()218     fn line_arclen() {
219         let l = Line::new((0.0, 0.0), (1.0, 1.0));
220         let true_len = 2.0f64.sqrt();
221         let epsilon = 1e-9;
222         assert!(l.arclen(epsilon) - true_len < epsilon);
223 
224         let t = l.inv_arclen(true_len / 3.0, epsilon);
225         assert!((t - 1.0 / 3.0).abs() < epsilon);
226     }
227 }
228