pix_engine/shape/
line.rs

1//! A shape type representing lines used for drawing.
2//!
3//! # Examples
4//!
5//! You can create a [Line] using [`Line::new`]:
6//!
7//! ```
8//! use pix_engine::prelude::*;
9//!
10//! // 2D
11//! let line = Line::new([10, 20], [30, 10]);
12//!
13//! let p1 = point![10, 20];
14//! let p2 = point![30, 10];
15//! let line = Line::new(p1, p2);
16//!
17//! // 3D
18//! let line = Line::new([10, 20, 5], [30, 10, 5]);
19//! ```
20
21use crate::{error::Result, prelude::*};
22#[cfg(feature = "serde")]
23use serde::{de::DeserializeOwned, Deserialize, Serialize};
24
25/// A `Line` with start and end [Point]s.
26///
27/// Please see the [module-level documentation] for examples.
28///
29/// [module-level documentation]: crate::shape::line
30#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
31#[repr(transparent)]
32#[must_use]
33#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
34#[cfg_attr(feature = "serde", serde(bound = "T: Serialize + DeserializeOwned"))]
35pub struct Line<T = i32, const N: usize = 2>(pub(crate) [Point<T, N>; 2]);
36
37/// Constructs a [Line] with two points.
38///
39/// ```
40/// # use pix_engine::prelude::*;
41///
42/// let l = line_!([10, 20], [30, 10]);
43/// assert_eq!(l.points(), [
44///   point!(10, 20),
45///   point!(30, 10),
46/// ]);
47///
48/// let l = line_!([10, 20, 10], [30, 10, 40]);
49/// assert_eq!(l.points(), [
50///   point!(10, 20, 10),
51///   point!(30, 10, 40),
52/// ]);
53/// ```
54#[macro_export]
55macro_rules! line_ {
56    ($p1:expr, $p2:expr$(,)?) => {
57        $crate::prelude::Line::new($p1, $p2)
58    };
59    ($x1:expr, $y1:expr, $x2:expr, $y2:expr$(,)?) => {
60        $crate::prelude::Line::from_xy($x1, $y1, $x2, $y2)
61    };
62    ($x1:expr, $y1:expr, $z1:expr, $x2:expr, $y2:expr, $z2:expr$(,)?) => {
63        $crate::prelude::Line::from_xyz($x1, $y1, $z2, $x2, $y2, $z2)
64    };
65}
66
67impl<T, const N: usize> Line<T, N> {
68    /// Constructs a `Line` from `start` to `end` [Point]s.
69    ///
70    /// # Example
71    /// ```
72    /// # use pix_engine::prelude::*;
73    /// // 2D
74    /// let line = Line::new([10, 20], [30, 10]);
75    ///
76    /// let p1 = point![10, 20];
77    /// let p2 = point![30, 10];
78    /// let line = Line::new(p1, p2);
79    ///
80    /// // 3D
81    /// let line: Line<i32, 3> = Line::new([10, 20, 5], [30, 10, 5]);
82    /// ```
83    pub fn new<P1, P2>(start: P1, end: P2) -> Self
84    where
85        P1: Into<Point<T, N>>,
86        P2: Into<Point<T, N>>,
87    {
88        Self([start.into(), end.into()])
89    }
90}
91
92impl<T> Line<T> {
93    /// Constructs a `Line` from individual x/y coordinates.
94    #[inline]
95    pub const fn from_xy(x1: T, y1: T, x2: T, y2: T) -> Self {
96        Self([point!(x1, y1), point!(x2, y2)])
97    }
98}
99
100impl<T: Copy> Line<T> {
101    /// Returns `Line` coordinates as `[x1, y1, x2, y2]`.
102    ///
103    /// # Example
104    ///
105    /// ```
106    /// # use pix_engine::prelude::*;
107    /// let p1 = point!(5, 10);
108    /// let p2 = point!(100, 100);
109    /// let l = Line::new(p1, p2);
110    /// assert_eq!(l.coords(), [5, 10, 100, 100]);
111    /// ```
112    #[inline]
113    pub fn coords(&self) -> [T; 4] {
114        let [p1, p2] = self.points();
115        let [x1, y1] = p1.coords();
116        let [x2, y2] = p2.coords();
117        [x1, y1, x2, y2]
118    }
119}
120
121impl<T> Line<T, 3> {
122    /// Constructs a `Line` from individual x/y/z coordinates.
123    #[inline]
124    pub const fn from_xyz(x1: T, y1: T, z1: T, x2: T, y2: T, z2: T) -> Self {
125        Self([point!(x1, y1, z1), point!(x2, y2, z2)])
126    }
127}
128
129impl<T: Copy> Line<T, 3> {
130    /// Returns `Line` coordinates as `[x1, y1, z1, x2, y2, z2]`.
131    ///
132    /// # Example
133    ///
134    /// ```
135    /// # use pix_engine::prelude::*;
136    /// let p1 = point!(5, 10);
137    /// let p2 = point!(100, 100);
138    /// let l = Line::new(p1, p2);
139    /// assert_eq!(l.coords(), [5, 10, 100, 100]);
140    /// ```
141    #[inline]
142    pub fn coords(&self) -> [T; 6] {
143        let [p1, p2] = self.points();
144        let [x1, y1, z1] = p1.coords();
145        let [x2, y2, z2] = p2.coords();
146        [x1, y1, z1, x2, y2, z2]
147    }
148}
149
150impl<T: Copy, const N: usize> Line<T, N> {
151    /// Returns the starting point of the line.
152    #[inline]
153    pub fn start(&self) -> Point<T, N> {
154        self.0[0]
155    }
156
157    /// Sets the starting point of the line.
158    #[inline]
159    pub fn set_start<P: Into<Point<T, N>>>(&mut self, start: P) {
160        self.0[0] = start.into();
161    }
162
163    /// Returns the ending point of the line.
164    #[inline]
165    pub fn end(&self) -> Point<T, N> {
166        self.0[1]
167    }
168
169    /// Sets the ending point of the line.
170    #[inline]
171    pub fn set_end<P: Into<Point<T, N>>>(&mut self, end: P) {
172        self.0[1] = end.into();
173    }
174
175    /// Returns `Line` points as `[Point<T, N>; 3]`.
176    ///
177    /// # Example
178    ///
179    /// ```
180    /// # use pix_engine::prelude::*;
181    /// let p1 = point!(5, 10);
182    /// let p2 = point!(100, 100);
183    /// let l = Line::new(p1, p2);
184    /// assert_eq!(l.points(), [point!(5, 10), point!(100, 100)]);
185    /// ```
186    #[inline]
187    pub fn points(&self) -> [Point<T, N>; 2] {
188        self.0
189    }
190
191    /// Returns `Line` points as a mutable slice `&mut [Point<T, N>; 3]`.
192    ///
193    /// # Example
194    ///
195    /// ```
196    /// # use pix_engine::prelude::*;
197    /// let p1 = point!(5, 10);
198    /// let p2 = point!(100, 100);
199    /// let mut l = Line::new(p1, p2);
200    /// for p in l.points_mut() {
201    ///     *p += 5;
202    /// }
203    /// assert_eq!(l.points(), [point!(10, 15), point!(105, 105)]);
204    /// ```
205    #[inline]
206    pub fn points_mut(&mut self) -> &mut [Point<T, N>; 2] {
207        &mut self.0
208    }
209
210    /// Returns `Line` as a [Vec].
211    ///
212    /// # Example
213    ///
214    /// ```
215    /// # use pix_engine::prelude::*;
216    /// let p1 = point!(5, 10);
217    /// let p2 = point!(100, 100);
218    /// let l = Line::new(p1, p2);
219    /// assert_eq!(l.to_vec(), vec![[5, 10], [100, 100]]);
220    /// ```
221    pub fn to_vec(self) -> Vec<Vec<T>> {
222        let start = self.start().to_vec();
223        let end = self.end().to_vec();
224        vec![start, end]
225    }
226}
227
228impl<T: Float> Intersects<Line<T>> for Line<T> {
229    type Result = (Point<T>, T);
230
231    /// Returns the closest intersection point with a given line and distance along the line or
232    /// `None` if there is no intersection.
233    #[allow(clippy::many_single_char_names)]
234    fn intersects(&self, line: Line<T>) -> Option<Self::Result> {
235        let [x1, y1, x2, y2] = self.coords();
236        let [x3, y3, x4, y4] = line.coords();
237        let d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
238        if d == T::zero() {
239            return None;
240        }
241        let t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / d;
242        let u = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / d;
243        if (T::zero()..).contains(&t) && (T::zero()..=T::one()).contains(&u) {
244            let x = x1 + t * (x2 - x1);
245            let y = y1 + t * (y2 - y1);
246            Some((point!(x, y), t))
247        } else {
248            None
249        }
250    }
251}
252
253impl Draw for Line<i32> {
254    /// Draw `Line` to the current [`PixState`] canvas.
255    fn draw(&self, s: &mut PixState) -> Result<()> {
256        s.line(*self)
257    }
258}
259
260impl<T: Copy> From<[T; 4]> for Line<T> {
261    /// Converts `[T; 4]` into `Line<T>`.
262    #[inline]
263    fn from([x1, y1, x2, y2]: [T; 4]) -> Self {
264        Self::from_xy(x1, y1, x2, y2)
265    }
266}
267
268impl<T: Copy> From<[T; 6]> for Line<T, 3> {
269    /// Converts `[T; 6]` into `Line<T, 3>`.
270    #[inline]
271    fn from([x1, y1, z1, x2, y2, z2]: [T; 6]) -> Self {
272        Self::from_xyz(x1, y1, z1, x2, y2, z2)
273    }
274}
275
276impl<T: Copy> From<[[T; 2]; 2]> for Line<T> {
277    /// Converts `[[T; 2]; 2]` into `Line<T>`.
278    #[inline]
279    fn from([[x1, y1], [x2, y2]]: [[T; 2]; 2]) -> Self {
280        Self::from_xy(x1, y1, x2, y2)
281    }
282}
283
284impl<T: Copy> From<[[T; 3]; 2]> for Line<T, 3> {
285    /// Converts `[[T; 3]; 2]` into `Line<T, 3>`.
286    #[inline]
287    fn from([[x1, y1, z1], [x2, y2, z2]]: [[T; 3]; 2]) -> Self {
288        Self::from_xyz(x1, y1, z1, x2, y2, z2)
289    }
290}