zng_layout/unit/
line.rs

1use std::{fmt, ops};
2
3use zng_var::{animation::Transitionable, impl_from_and_into_var};
4
5use super::{Factor2d, LayoutMask, Length, Point, Px, PxPoint, PxRect};
6
7/// 2D line in [`Length`] units.
8#[derive(Clone, Default, PartialEq, serde::Serialize, serde::Deserialize, Transitionable)]
9pub struct Line {
10    /// Start point in length units.
11    pub start: Point,
12    /// End point in length units.
13    pub end: Point,
14}
15impl fmt::Debug for Line {
16    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17        if f.alternate() {
18            f.debug_struct("Line").field("start", &self.start).field("end", &self.end).finish()
19        } else {
20            write!(f, "{:?}.to{:?}", self.start, self.end)
21        }
22    }
23}
24impl fmt::Display for Line {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        if let Some(p) = f.precision() {
27            write!(f, "{:.p$} to {:.p$}", self.start, self.end, p = p)
28        } else {
29            write!(f, "{} to {}", self.start, self.end)
30        }
31    }
32}
33impl Line {
34    /// New line defined by two points of any type that converts to [`Point`].
35    ///
36    /// Also see [`LineFromTuplesBuilder`] for another way of initializing a line value.
37    pub fn new<S: Into<Point>, E: Into<Point>>(start: S, end: E) -> Self {
38        Line {
39            start: start.into(),
40            end: end.into(),
41        }
42    }
43
44    /// Line from [zero](Point::zero) to [zero](Point::zero).
45    pub fn zero() -> Line {
46        Line {
47            start: Point::zero(),
48            end: Point::zero(),
49        }
50    }
51
52    /// Line that fills the available length from [bottom](Point::bottom) to [top](Point::top).
53    pub fn to_top() -> Line {
54        Line {
55            start: Point::bottom(),
56            end: Point::top(),
57        }
58    }
59
60    /// Line that traces the length from [top](Point::top) to [bottom](Point::bottom).
61    pub fn to_bottom() -> Line {
62        Line {
63            start: Point::top(),
64            end: Point::bottom(),
65        }
66    }
67
68    /// Line that traces the length from [left](Point::left) to [right](Point::right).
69    pub fn to_right() -> Line {
70        Line {
71            start: Point::left(),
72            end: Point::right(),
73        }
74    }
75
76    /// Line that traces the length from [right](Point::right) to [left](Point::left).
77    pub fn to_left() -> Line {
78        Line {
79            start: Point::right(),
80            end: Point::left(),
81        }
82    }
83
84    /// Line that traces the length from [bottom-right](Point::bottom_right) to [top-left](Point::top_left).
85    pub fn to_top_left() -> Line {
86        Line {
87            start: Point::bottom_right(),
88            end: Point::top_left(),
89        }
90    }
91
92    /// Line that traces the length from [bottom-left](Point::bottom_left) to [top-right](Point::top_right).
93    pub fn to_top_right() -> Line {
94        Line {
95            start: Point::bottom_left(),
96            end: Point::top_right(),
97        }
98    }
99
100    /// Line that traces the length from [top-right](Point::top_right) to [bottom-left](Point::bottom_left).
101    pub fn to_bottom_left() -> Line {
102        Line {
103            start: Point::top_right(),
104            end: Point::bottom_left(),
105        }
106    }
107
108    /// Line that traces the length from [top-left](Point::top_left) to [bottom-right](Point::bottom_right).
109    pub fn to_bottom_right() -> Line {
110        Line {
111            start: Point::top_left(),
112            end: Point::bottom_right(),
113        }
114    }
115}
116impl super::Layout2d for Line {
117    type Px = PxLine;
118
119    fn layout_dft(&self, default: Self::Px) -> Self::Px {
120        PxLine {
121            start: self.start.layout_dft(default.start),
122            end: self.end.layout_dft(default.end),
123        }
124    }
125
126    fn affect_mask(&self) -> LayoutMask {
127        self.start.affect_mask() | self.end.affect_mask()
128    }
129}
130impl_from_and_into_var! {
131    /// From exact lengths.
132    fn from(line: PxLine) -> Line {
133        Line::new(line.start, line.end)
134    }
135}
136
137/// Computed [`Line`].
138#[derive(Clone, Default, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
139pub struct PxLine {
140    /// Start point in layout units.
141    pub start: PxPoint,
142    /// End point in layout units.
143    pub end: PxPoint,
144}
145impl PxLine {
146    /// New layout line defined by two layout points.
147    pub fn new(start: PxPoint, end: PxPoint) -> Self {
148        Self { start, end }
149    }
150
151    /// Line from (0, 0) to (0, 0).
152    pub fn zero() -> Self {
153        Self::new(PxPoint::zero(), PxPoint::zero())
154    }
155
156    /// Line length in rounded pixels.
157    pub fn length(self) -> Px {
158        let s = self.start.cast::<f32>();
159        let e = self.end.cast::<f32>();
160        Px(s.distance_to(e).round() as i32)
161    }
162
163    /// Bounding box that fits the line points, in layout units.
164    pub fn bounds(self) -> PxRect {
165        PxRect::from_points([self.start, self.end])
166    }
167
168    /// Returns a line that starts from the left-top most point and ends at the bottom-right most point.
169    pub fn normalize(self) -> PxLine {
170        let start = self.start.min(self.end);
171        let end = self.start.max(self.end);
172        PxLine { start, end }
173    }
174}
175
176/// Build a [`Line`] using the syntax `(x1, y1).to(x2, y2)`.
177///
178/// # Examples
179///
180/// ```
181/// # use zng_layout::unit::*;
182/// let line = (10, 20).to(100, 120);
183/// assert_eq!(Line::new(Point::new(10, 20), Point::new(100, 120)), line);
184/// ```
185pub trait LineFromTuplesBuilder {
186    /// New [`Line`] from `self` as a start point to `x2, y2` end point.
187    fn to<X2: Into<Length>, Y2: Into<Length>>(self, x2: X2, y2: Y2) -> Line;
188}
189impl<X1: Into<Length>, Y1: Into<Length>> LineFromTuplesBuilder for (X1, Y1) {
190    fn to<X2: Into<Length>, Y2: Into<Length>>(self, x2: X2, y2: Y2) -> Line {
191        Line::new(self, (x2, y2))
192    }
193}
194
195impl<S: Into<Factor2d>> ops::Mul<S> for Line {
196    type Output = Self;
197
198    fn mul(mut self, rhs: S) -> Self {
199        self *= rhs;
200        self
201    }
202}
203impl<S: Into<Factor2d>> ops::Mul<S> for &Line {
204    type Output = Line;
205
206    fn mul(self, rhs: S) -> Self::Output {
207        self.clone() * rhs
208    }
209}
210impl<S: Into<Factor2d>> ops::MulAssign<S> for Line {
211    fn mul_assign(&mut self, rhs: S) {
212        let rhs = rhs.into();
213        self.start *= rhs;
214        self.end *= rhs;
215    }
216}
217impl<S: Into<Factor2d>> ops::Div<S> for Line {
218    type Output = Self;
219
220    fn div(mut self, rhs: S) -> Self {
221        self /= rhs;
222        self
223    }
224}
225impl<S: Into<Factor2d>> ops::Div<S> for &Line {
226    type Output = Line;
227
228    fn div(self, rhs: S) -> Self::Output {
229        self.clone() / rhs
230    }
231}
232impl<S: Into<Factor2d>> ops::DivAssign<S> for Line {
233    fn div_assign(&mut self, rhs: S) {
234        let rhs = rhs.into();
235        self.start /= rhs;
236        self.end /= rhs;
237    }
238}
239
240impl ops::Add for Line {
241    type Output = Self;
242
243    fn add(mut self, rhs: Self) -> Self {
244        self += rhs;
245        self
246    }
247}
248impl ops::AddAssign for Line {
249    fn add_assign(&mut self, rhs: Self) {
250        self.start += rhs.start;
251        self.end += rhs.end;
252    }
253}
254impl ops::Sub for Line {
255    type Output = Self;
256
257    fn sub(mut self, rhs: Self) -> Self {
258        self -= rhs;
259        self
260    }
261}
262impl ops::SubAssign for Line {
263    fn sub_assign(&mut self, rhs: Self) {
264        self.start -= rhs.start;
265        self.end -= rhs.end;
266    }
267}