1use crate::LinearCmd;
2
3use super::{Cubic, Curve, CurveDasher, Point};
4
5#[repr(C)]
6#[derive(Clone, Copy, Debug, PartialEq, Hash)]
7pub struct Line {
8 pub p0: Point,
9 pub to: Point,
10}
11impl Line {
12 pub fn new(p0: Point, to: Point) -> Self {
13 Self { p0, to }
14 }
15 pub fn linearize(self, tolerance: f32) -> LineIter {
16 LineIter::new(self, tolerance)
17 }
18 pub fn dash(self, tolerance: f32) -> CurveDasher<Self> {
19 CurveDasher::new(self, tolerance)
20 }
21 pub fn its(&self, other: &Line) -> Option<LineIts> {
22 let d1 = self.to - self.p0;
23 let d2 = other.to - other.p0;
24 let cross = d1.x * d2.y - d1.y * d2.x;
25 if cross.abs() < f32::EPSILON {
26 return None;
27 }
28 let ac = other.p0 - self.p0;
29 let t = (ac.x * d2.y - ac.y * d2.x) / cross;
30 let s = (ac.x * d1.y - ac.y * d1.x) / cross;
31 let point = self.p0 + d1 * t;
32 Some(LineIts {
33 point,
34 cross,
35 t,
36 s,
37 is_on_this: t >= 0.0 && t <= 1.0,
38 is_on_other: s >= 0.0 && s <= 1.0,
39 })
40 }
41}
42impl Curve for Line {
43 fn start(&self) -> Point {
44 self.p0
45 }
46 fn end(&self) -> Point {
47 self.to
48 }
49 fn linear(&self, _tolerance: f32) -> bool {
50 true
51 }
52 fn split(self, at: f32) -> (Self, Self) {
53 let p01 = self.p0.lerp(self.to, at);
54 (Self::new(self.p0, p01), Self::new(p01, self.to))
55 }
56 fn slice(&self, start: f32, end: f32) -> Self {
59 let p0 = self.eval(start);
60 let to = self.eval(end);
61 Line { p0, to }
62 }
63 fn shift(&self, offset: f32) -> (Self, Self) {
64 if offset.abs() < f32::EPSILON {
65 return (*self, *self);
66 }
67 let normal = self.p0.normal_to(self.to).normalized();
68 let p0 = self.p0 + normal * offset;
69 let to = self.to + normal * offset;
70 let ccw = Line { p0, to };
71 let p0 = self.p0 + normal * -offset;
72 let to = self.to + normal * -offset;
73 let cw = Line { p0, to };
74 (ccw, cw)
75 }
76 fn reverse(&self) -> Self {
77 Self { p0: self.to, to: self.p0 }
78 }
79 fn length(&self) -> f32 {
81 if self.p0 == self.to {
82 return 0.0;
83 }
84 (self.to - self.p0).length()
85 }
86 fn eval(&self, at: f32) -> Point {
89 self.p0 + (self.to - self.p0) * at
91 }
92 fn derivative(&self, _at: f32) -> Point {
95 self.to - self.p0
96 }
97 fn normal(&self, at: f32) -> Point {
99 if self.p0 == self.to {
100 return Point::ZERO;
101 }
102 self.derivative(at).normal().normalized()
104 }
105 fn at(&self, distance: f32, _tolerance: f32) -> f32 {
109 if distance <= 0.0 {
110 return 0.0;
111 }
112 let total_length = self.length();
113 if distance >= total_length {
114 return 1.0;
115 }
116 distance / total_length
117 }
118}
119impl From<Line> for Cubic {
120 fn from(value: Line) -> Self {
121 Cubic::new(value.p0, value.p0, value.to, value.to)
122 }
123}
124impl From<Line> for LinearCmd {
125 fn from(value: Line) -> Self {
126 LinearCmd::LineTo(value.to)
127 }
128}
129
130#[derive(Debug, Clone, PartialEq)]
131pub struct LineIter {
132 to: Option<Point>,
133}
134impl LineIter {
135 pub fn empty(_tolerance: f32) -> Self {
136 Self { to: None }
137 }
138 pub fn new(value: Line, tolerance: f32) -> Self {
139 let mut this = Self::empty(tolerance);
140 this.to = Some(value.to);
141 this
142 }
143}
144impl Iterator for LineIter {
145 type Item = LinearCmd;
146 fn next(&mut self) -> Option<Self::Item> {
147 Some(LinearCmd::LineTo(self.to.take()?))
148 }
149}
150
151#[repr(C)]
152#[derive(Debug, Copy, Clone, PartialEq, Hash)]
153pub enum LineCmd {
154 Move(Point),
155 Line(Line),
156 Close,
157}
158impl From<Line> for LineCmd {
159 fn from(value: Line) -> Self {
160 LineCmd::Line(value)
161 }
162}
163impl From<LineCmd> for Option<Line> {
164 fn from(value: LineCmd) -> Self {
165 match value {
166 LineCmd::Line(line) => Some(line),
167 _ => None,
168 }
169 }
170}
171impl From<LineCmd> for LinearCmd {
172 fn from(value: LineCmd) -> Self {
173 match value {
174 LineCmd::Move(to) => LinearCmd::MoveTo(to),
175 LineCmd::Line(line) => LinearCmd::LineTo(line.to),
176 LineCmd::Close => LinearCmd::Close,
177 }
178 }
179}
180
181#[derive(Debug, Clone, Copy, PartialEq)]
182pub struct LineIts {
183 pub point: Point,
185 pub cross: f32,
187 pub t: f32,
189 pub s: f32,
191 pub is_on_this: bool,
193 pub is_on_other: bool,
195}