zng_layout/unit/
line.rs

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/// 2D line in [`Length`] units.
10#[derive(Clone, Default, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, Transitionable)]
11pub struct Line {
12    /// Start point in length units.
13    pub start: Point,
14    /// End point in length units.
15    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    /// New line defined by two points of any type that converts to [`Point`].
46    ///
47    /// Also see [`LineFromTuplesBuilder`] for another way of initializing a line value.
48    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    /// Line from [zero](Point::zero) to [zero](Point::zero).
56    pub fn zero() -> Line {
57        Line {
58            start: Point::zero(),
59            end: Point::zero(),
60        }
61    }
62
63    /// Line that fills the available length from [bottom](Point::bottom) to [top](Point::top).
64    pub fn to_top() -> Line {
65        Line {
66            start: Point::bottom(),
67            end: Point::top(),
68        }
69    }
70
71    /// Line that traces the length from [top](Point::top) to [bottom](Point::bottom).
72    pub fn to_bottom() -> Line {
73        Line {
74            start: Point::top(),
75            end: Point::bottom(),
76        }
77    }
78
79    /// Line that traces the length from [left](Point::left) to [right](Point::right).
80    pub fn to_right() -> Line {
81        Line {
82            start: Point::left(),
83            end: Point::right(),
84        }
85    }
86
87    /// Line that traces the length from [right](Point::right) to [left](Point::left).
88    pub fn to_left() -> Line {
89        Line {
90            start: Point::right(),
91            end: Point::left(),
92        }
93    }
94
95    /// Line that traces the length from [bottom-right](Point::bottom_right) to [top-left](Point::top_left).
96    pub fn to_top_left() -> Line {
97        Line {
98            start: Point::bottom_right(),
99            end: Point::top_left(),
100        }
101    }
102
103    /// Line that traces the length from [bottom-left](Point::bottom_left) to [top-right](Point::top_right).
104    pub fn to_top_right() -> Line {
105        Line {
106            start: Point::bottom_left(),
107            end: Point::top_right(),
108        }
109    }
110
111    /// Line that traces the length from [top-right](Point::top_right) to [bottom-left](Point::bottom_left).
112    pub fn to_bottom_left() -> Line {
113        Line {
114            start: Point::top_right(),
115            end: Point::bottom_left(),
116        }
117    }
118
119    /// Line that traces the length from [top-left](Point::top_left) to [bottom-right](Point::bottom_right).
120    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    /// From exact lengths.
143    fn from(line: PxLine) -> Line {
144        Line::new(line.start, line.end)
145    }
146}
147
148/// Computed [`Line`].
149#[derive(Clone, Default, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
150pub struct PxLine {
151    /// Start point in layout units.
152    pub start: PxPoint,
153    /// End point in layout units.
154    pub end: PxPoint,
155}
156impl PxLine {
157    /// New layout line defined by two layout points.
158    pub fn new(start: PxPoint, end: PxPoint) -> Self {
159        Self { start, end }
160    }
161
162    /// Line from (0, 0) to (0, 0).
163    pub fn zero() -> Self {
164        Self::new(PxPoint::zero(), PxPoint::zero())
165    }
166
167    /// Line length in rounded pixels.
168    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    /// Bounding box that fits the line points, in layout units.
175    pub fn bounds(self) -> PxRect {
176        PxRect::from_points([self.start, self.end])
177    }
178
179    /// Returns a line that starts from the left-top most point and ends at the bottom-right most point.
180    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
187/// Build a [`Line`] using the syntax `(x1, y1).to(x2, y2)`.
188///
189/// # Examples
190///
191/// ```
192/// # use zng_layout::unit::*;
193/// let line = (10, 20).to(100, 120);
194/// assert_eq!(Line::new(Point::new(10, 20), Point::new(100, 120)), line);
195/// ```
196pub trait LineFromTuplesBuilder {
197    /// New [`Line`] from `self` as a start point to `x2, y2` end point.
198    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}