1use std::{fmt, ops};
2
3use zng_var::{animation::Transitionable, impl_from_and_into_var};
4
5use crate::unit::ParseCompositeError;
6
7use super::{Factor2d, LayoutMask, Length, Point, Px, PxPoint, PxRect};
8
9#[derive(Clone, Default, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, Transitionable)]
11pub struct Line {
12 pub start: Point,
14 pub end: Point,
16}
17impl fmt::Debug for Line {
18 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19 if f.alternate() {
20 f.debug_struct("Line").field("start", &self.start).field("end", &self.end).finish()
21 } else {
22 write!(f, "{:.p$?}.to{:.p$?}", self.start, self.end, p = f.precision().unwrap_or(0))
23 }
24 }
25}
26impl fmt::Display for Line {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 write!(f, "{:.p$} to {:.p$}", self.start, self.end, p = f.precision().unwrap_or(0))
29 }
30}
31impl std::str::FromStr for Line {
32 type Err = ParseCompositeError;
33
34 fn from_str(s: &str) -> Result<Self, Self::Err> {
35 if let Some((a, b)) = s.split_once(".to") {
36 Ok(Self::new(Point::from_str(a)?, Point::from_str(b)?))
37 } else if let Some((a, b)) = s.split_once(" to ") {
38 Ok(Self::new(Point::from_str(a.trim())?, Point::from_str(b.trim())?))
39 } else {
40 Err(ParseCompositeError::UnknownFormat)
41 }
42 }
43}
44impl Line {
45 pub fn new<S: Into<Point>, E: Into<Point>>(start: S, end: E) -> Self {
49 Line {
50 start: start.into(),
51 end: end.into(),
52 }
53 }
54
55 pub fn zero() -> Line {
57 Line {
58 start: Point::zero(),
59 end: Point::zero(),
60 }
61 }
62
63 pub fn to_top() -> Line {
65 Line {
66 start: Point::bottom(),
67 end: Point::top(),
68 }
69 }
70
71 pub fn to_bottom() -> Line {
73 Line {
74 start: Point::top(),
75 end: Point::bottom(),
76 }
77 }
78
79 pub fn to_right() -> Line {
81 Line {
82 start: Point::left(),
83 end: Point::right(),
84 }
85 }
86
87 pub fn to_left() -> Line {
89 Line {
90 start: Point::right(),
91 end: Point::left(),
92 }
93 }
94
95 pub fn to_top_left() -> Line {
97 Line {
98 start: Point::bottom_right(),
99 end: Point::top_left(),
100 }
101 }
102
103 pub fn to_top_right() -> Line {
105 Line {
106 start: Point::bottom_left(),
107 end: Point::top_right(),
108 }
109 }
110
111 pub fn to_bottom_left() -> Line {
113 Line {
114 start: Point::top_right(),
115 end: Point::bottom_left(),
116 }
117 }
118
119 pub fn to_bottom_right() -> Line {
121 Line {
122 start: Point::top_left(),
123 end: Point::bottom_right(),
124 }
125 }
126}
127impl super::Layout2d for Line {
128 type Px = PxLine;
129
130 fn layout_dft(&self, default: Self::Px) -> Self::Px {
131 PxLine {
132 start: self.start.layout_dft(default.start),
133 end: self.end.layout_dft(default.end),
134 }
135 }
136
137 fn affect_mask(&self) -> LayoutMask {
138 self.start.affect_mask() | self.end.affect_mask()
139 }
140}
141impl_from_and_into_var! {
142 fn from(line: PxLine) -> Line {
144 Line::new(line.start, line.end)
145 }
146}
147
148#[derive(Clone, Default, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
150pub struct PxLine {
151 pub start: PxPoint,
153 pub end: PxPoint,
155}
156impl PxLine {
157 pub fn new(start: PxPoint, end: PxPoint) -> Self {
159 Self { start, end }
160 }
161
162 pub fn zero() -> Self {
164 Self::new(PxPoint::zero(), PxPoint::zero())
165 }
166
167 pub fn length(self) -> Px {
169 let s = self.start.cast::<f32>();
170 let e = self.end.cast::<f32>();
171 Px(s.distance_to(e).round() as i32)
172 }
173
174 pub fn bounds(self) -> PxRect {
176 PxRect::from_points([self.start, self.end])
177 }
178
179 pub fn normalize(self) -> PxLine {
181 let start = self.start.min(self.end);
182 let end = self.start.max(self.end);
183 PxLine { start, end }
184 }
185}
186
187pub trait LineFromTuplesBuilder {
197 fn to<X2: Into<Length>, Y2: Into<Length>>(self, x2: X2, y2: Y2) -> Line;
199}
200impl<X1: Into<Length>, Y1: Into<Length>> LineFromTuplesBuilder for (X1, Y1) {
201 fn to<X2: Into<Length>, Y2: Into<Length>>(self, x2: X2, y2: Y2) -> Line {
202 Line::new(self, (x2, y2))
203 }
204}
205
206impl<S: Into<Factor2d>> ops::Mul<S> for Line {
207 type Output = Self;
208
209 fn mul(mut self, rhs: S) -> Self {
210 self *= rhs;
211 self
212 }
213}
214impl<S: Into<Factor2d>> ops::Mul<S> for &Line {
215 type Output = Line;
216
217 fn mul(self, rhs: S) -> Self::Output {
218 self.clone() * rhs
219 }
220}
221impl<S: Into<Factor2d>> ops::MulAssign<S> for Line {
222 fn mul_assign(&mut self, rhs: S) {
223 let rhs = rhs.into();
224 self.start *= rhs;
225 self.end *= rhs;
226 }
227}
228impl<S: Into<Factor2d>> ops::Div<S> for Line {
229 type Output = Self;
230
231 fn div(mut self, rhs: S) -> Self {
232 self /= rhs;
233 self
234 }
235}
236impl<S: Into<Factor2d>> ops::Div<S> for &Line {
237 type Output = Line;
238
239 fn div(self, rhs: S) -> Self::Output {
240 self.clone() / rhs
241 }
242}
243impl<S: Into<Factor2d>> ops::DivAssign<S> for Line {
244 fn div_assign(&mut self, rhs: S) {
245 let rhs = rhs.into();
246 self.start /= rhs;
247 self.end /= rhs;
248 }
249}
250
251impl ops::Add for Line {
252 type Output = Self;
253
254 fn add(mut self, rhs: Self) -> Self {
255 self += rhs;
256 self
257 }
258}
259impl ops::AddAssign for Line {
260 fn add_assign(&mut self, rhs: Self) {
261 self.start += rhs.start;
262 self.end += rhs.end;
263 }
264}
265impl ops::Sub for Line {
266 type Output = Self;
267
268 fn sub(mut self, rhs: Self) -> Self {
269 self -= rhs;
270 self
271 }
272}
273impl ops::SubAssign for Line {
274 fn sub_assign(&mut self, rhs: Self) {
275 self.start -= rhs.start;
276 self.end -= rhs.end;
277 }
278}