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}