1use core::ops::{Add, Mul, Range, Sub};
7
8use arrayvec::ArrayVec;
9
10use crate::{
11 Affine, Nearest, ParamCurve, ParamCurveArclen, ParamCurveArea, ParamCurveCurvature,
12 ParamCurveDeriv, ParamCurveExtrema, ParamCurveNearest, PathEl, Point, Rect, Shape, Vec2,
13 DEFAULT_ACCURACY, MAX_EXTREMA,
14};
15
16#[derive(Clone, Copy, Debug, PartialEq)]
18#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20pub struct Line {
21 pub p0: Point,
23 pub p1: Point,
25}
26
27impl Line {
28 #[inline(always)]
30 pub fn new(p0: impl Into<Point>, p1: impl Into<Point>) -> Line {
31 Line {
32 p0: p0.into(),
33 p1: p1.into(),
34 }
35 }
36
37 #[must_use]
40 #[inline(always)]
41 pub fn reversed(&self) -> Line {
42 Self {
43 p0: self.p1,
44 p1: self.p0,
45 }
46 }
47
48 #[inline]
50 pub fn length(self) -> f64 {
51 self.arclen(DEFAULT_ACCURACY)
52 }
53
54 #[must_use]
59 #[inline]
60 pub fn midpoint(&self) -> Point {
61 self.p0.midpoint(self.p1)
62 }
63
64 pub fn crossing_point(self, other: Line) -> Option<Point> {
66 let ab = self.p1 - self.p0;
67 let cd = other.p1 - other.p0;
68 let pcd = ab.cross(cd);
69 if pcd == 0.0 {
70 return None;
71 }
72 let h = ab.cross(self.p0 - other.p0) / pcd;
73 Some(other.p0 + cd * h)
74 }
75
76 #[inline]
80 pub fn is_finite(self) -> bool {
81 self.p0.is_finite() && self.p1.is_finite()
82 }
83
84 #[inline]
88 pub fn is_nan(self) -> bool {
89 self.p0.is_nan() || self.p1.is_nan()
90 }
91}
92
93impl From<(Point, Point)> for Line {
94 #[inline(always)]
95 fn from((from, to): (Point, Point)) -> Self {
96 Line::new(from, to)
97 }
98}
99
100impl From<(Point, Vec2)> for Line {
101 #[inline(always)]
102 fn from((origin, displacement): (Point, Vec2)) -> Self {
103 Line::new(origin, origin + displacement)
104 }
105}
106
107impl ParamCurve for Line {
108 #[inline]
109 fn eval(&self, t: f64) -> Point {
110 self.p0.lerp(self.p1, t)
111 }
112
113 #[inline]
114 fn subsegment(&self, range: Range<f64>) -> Line {
115 Line {
116 p0: self.eval(range.start),
117 p1: self.eval(range.end),
118 }
119 }
120
121 #[inline(always)]
122 fn start(&self) -> Point {
123 self.p0
124 }
125
126 #[inline(always)]
127 fn end(&self) -> Point {
128 self.p1
129 }
130}
131
132impl ParamCurveDeriv for Line {
133 type DerivResult = ConstPoint;
134
135 #[inline]
136 fn deriv(&self) -> ConstPoint {
137 ConstPoint((self.p1 - self.p0).to_point())
138 }
139}
140
141impl ParamCurveArclen for Line {
142 #[inline]
143 fn arclen(&self, _accuracy: f64) -> f64 {
144 (self.p1 - self.p0).hypot()
145 }
146
147 #[inline]
148 fn inv_arclen(&self, arclen: f64, _accuracy: f64) -> f64 {
149 arclen / (self.p1 - self.p0).hypot()
150 }
151}
152
153impl ParamCurveArea for Line {
154 #[inline]
155 fn signed_area(&self) -> f64 {
156 self.p0.to_vec2().cross(self.p1.to_vec2()) * 0.5
157 }
158}
159
160impl ParamCurveNearest for Line {
161 fn nearest(&self, p: Point, _accuracy: f64) -> Nearest {
162 let d = self.p1 - self.p0;
163 let dotp = d.dot(p - self.p0);
164 let d_squared = d.dot(d);
165 let (t, distance_sq) = if dotp <= 0.0 {
166 (0.0, (p - self.p0).hypot2())
167 } else if dotp >= d_squared {
168 (1.0, (p - self.p1).hypot2())
169 } else {
170 let t = dotp / d_squared;
171 let dist = (p - self.eval(t)).hypot2();
172 (t, dist)
173 };
174 Nearest { distance_sq, t }
175 }
176}
177
178impl ParamCurveCurvature for Line {
179 #[inline(always)]
180 fn curvature(&self, _t: f64) -> f64 {
181 0.0
182 }
183}
184
185impl ParamCurveExtrema for Line {
186 #[inline]
187 fn extrema(&self) -> ArrayVec<f64, MAX_EXTREMA> {
188 ArrayVec::new()
189 }
190}
191
192#[derive(Clone, Copy, Debug)]
194#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
195#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
196pub struct ConstPoint(Point);
197
198impl ConstPoint {
199 #[inline]
203 pub fn is_finite(self) -> bool {
204 self.0.is_finite()
205 }
206
207 #[inline]
211 pub fn is_nan(self) -> bool {
212 self.0.is_nan()
213 }
214}
215
216impl ParamCurve for ConstPoint {
217 #[inline(always)]
218 fn eval(&self, _t: f64) -> Point {
219 self.0
220 }
221
222 #[inline(always)]
223 fn subsegment(&self, _range: Range<f64>) -> ConstPoint {
224 *self
225 }
226}
227
228impl ParamCurveDeriv for ConstPoint {
229 type DerivResult = ConstPoint;
230
231 #[inline(always)]
232 fn deriv(&self) -> ConstPoint {
233 ConstPoint(Point::new(0.0, 0.0))
234 }
235}
236
237impl ParamCurveArclen for ConstPoint {
238 #[inline(always)]
239 fn arclen(&self, _accuracy: f64) -> f64 {
240 0.0
241 }
242
243 #[inline(always)]
244 fn inv_arclen(&self, _arclen: f64, _accuracy: f64) -> f64 {
245 0.0
246 }
247}
248
249impl Mul<Line> for Affine {
250 type Output = Line;
251
252 #[inline]
253 fn mul(self, other: Line) -> Line {
254 Line {
255 p0: self * other.p0,
256 p1: self * other.p1,
257 }
258 }
259}
260
261impl Add<Vec2> for Line {
262 type Output = Line;
263
264 #[inline]
265 fn add(self, v: Vec2) -> Line {
266 Line::new(self.p0 + v, self.p1 + v)
267 }
268}
269
270impl Sub<Vec2> for Line {
271 type Output = Line;
272
273 #[inline]
274 fn sub(self, v: Vec2) -> Line {
275 Line::new(self.p0 - v, self.p1 - v)
276 }
277}
278
279#[doc(hidden)]
281pub struct LinePathIter {
282 line: Line,
283 ix: usize,
284}
285
286impl Shape for Line {
287 type PathElementsIter<'iter> = LinePathIter;
288
289 #[inline]
290 fn path_elements(&self, _tolerance: f64) -> LinePathIter {
291 LinePathIter { line: *self, ix: 0 }
292 }
293
294 #[inline(always)]
299 fn area(&self) -> f64 {
300 0.0
301 }
302
303 #[inline]
304 fn perimeter(&self, _accuracy: f64) -> f64 {
305 (self.p1 - self.p0).hypot()
306 }
307
308 #[inline(always)]
310 fn winding(&self, _pt: Point) -> i32 {
311 0
312 }
313
314 #[inline(always)]
315 fn bounding_box(&self) -> Rect {
316 Rect::from_points(self.p0, self.p1)
317 }
318
319 #[inline(always)]
320 fn as_line(&self) -> Option<Line> {
321 Some(*self)
322 }
323}
324
325impl Iterator for LinePathIter {
326 type Item = PathEl;
327
328 fn next(&mut self) -> Option<PathEl> {
329 self.ix += 1;
330 match self.ix {
331 1 => Some(PathEl::MoveTo(self.line.p0)),
332 2 => Some(PathEl::LineTo(self.line.p1)),
333 _ => None,
334 }
335 }
336}
337
338#[cfg(test)]
339mod tests {
340 use crate::{Line, ParamCurveArclen, Point};
341
342 #[test]
343 fn line_reversed() {
344 let l = Line::new((0.0, 0.0), (1.0, 1.0));
345 let f = l.reversed();
346
347 assert_eq!(l.p0, f.p1);
348 assert_eq!(l.p1, f.p0);
349
350 assert_eq!(l, f.reversed());
352 }
353
354 #[test]
355 fn line_arclen() {
356 let l = Line::new((0.0, 0.0), (1.0, 1.0));
357 let true_len = 2.0f64.sqrt();
358 let epsilon = 1e-9;
359 assert!(l.arclen(epsilon) - true_len < epsilon);
360
361 let t = l.inv_arclen(true_len / 3.0, epsilon);
362 assert!((t - 1.0 / 3.0).abs() < epsilon);
363 }
364
365 #[test]
366 fn line_midpoint() {
367 let l = Line::new((0.0, 0.0), (2.0, 4.0));
368 assert_eq!(l.midpoint(), Point::new(1.0, 2.0));
369 }
370
371 #[test]
372 fn line_is_finite() {
373 assert!((Line {
374 p0: Point { x: 0., y: 0. },
375 p1: Point { x: 1., y: 1. }
376 })
377 .is_finite());
378
379 assert!(!(Line {
380 p0: Point { x: 0., y: 0. },
381 p1: Point {
382 x: f64::INFINITY,
383 y: 1.
384 }
385 })
386 .is_finite());
387
388 assert!(!(Line {
389 p0: Point { x: 0., y: 0. },
390 p1: Point {
391 x: 0.,
392 y: f64::INFINITY
393 }
394 })
395 .is_finite());
396 }
397}