1use crate::{error::Result, prelude::*};
29#[cfg(feature = "serde")]
30use serde::{de::DeserializeOwned, Deserialize, Serialize};
31
32#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
40#[repr(transparent)]
41#[must_use]
42#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
43#[cfg_attr(feature = "serde", serde(bound = "T: Serialize + DeserializeOwned"))]
44pub struct Quad<T = i32, const N: usize = 2>(pub(crate) [Point<T, N>; 4]);
45
46#[macro_export]
69macro_rules! quad {
70 ($p1:expr, $p2:expr, $p3:expr, $p4:expr$(,)?) => {
71 $crate::prelude::Quad::new($p1, $p2, $p3, $p4)
72 };
73 ($x1:expr, $y1:expr, $x2:expr, $y2:expr, $x3:expr, $y3:expr, $x4:expr, $y4:expr$(,)?) => {
74 $crate::prelude::Quad::from_xy($x1, $y1, $x2, $y2, $x3, $y3, $x4, $y4)
75 };
76 ($x1:expr, $y1:expr, $z1:expr, $x2:expr, $y2:expr, $z2:expr, $x3:expr, $y3:expr, $z3:expr, $x4:expr, $y4:expr, $z4:expr$(,)?) => {
77 $crate::prelude::Quad::from_xy($x1, $y1, $z1, $x2, $y2, $z2, $x3, $y3, $z3, $x4, $y4, $z4)
78 };
79}
80
81impl<T, const N: usize> Quad<T, N> {
82 pub fn new<P1, P2, P3, P4>(p1: P1, p2: P2, p3: P3, p4: P4) -> Self
93 where
94 P1: Into<Point<T, N>>,
95 P2: Into<Point<T, N>>,
96 P3: Into<Point<T, N>>,
97 P4: Into<Point<T, N>>,
98 {
99 Self([p1.into(), p2.into(), p3.into(), p4.into()])
100 }
101}
102
103impl<T> Quad<T> {
104 #[allow(clippy::too_many_arguments)]
106 #[inline]
107 pub const fn from_xy(x1: T, y1: T, x2: T, y2: T, x3: T, y3: T, x4: T, y4: T) -> Self {
108 Self([
109 point!(x1, y1),
110 point!(x2, y2),
111 point!(x3, y3),
112 point!(x4, y4),
113 ])
114 }
115}
116
117impl<T: Copy> Quad<T> {
118 #[inline]
128 pub fn coords(&self) -> [T; 8] {
129 let [p1, p2, p3, p4] = self.points();
130 let [x1, y1] = p1.coords();
131 let [x2, y2] = p2.coords();
132 let [x3, y3] = p3.coords();
133 let [x4, y4] = p4.coords();
134 [x1, y1, x2, y2, x3, y3, x4, y4]
135 }
136}
137
138impl<T> Quad<T, 3> {
139 #[allow(clippy::too_many_arguments)]
141 #[inline]
142 pub const fn from_xyz(
143 x1: T,
144 y1: T,
145 z1: T,
146 x2: T,
147 y2: T,
148 z2: T,
149 x3: T,
150 y3: T,
151 z3: T,
152 x4: T,
153 y4: T,
154 z4: T,
155 ) -> Self {
156 Self([
157 point!(x1, y1, z1),
158 point!(x2, y2, z2),
159 point!(x3, y3, z3),
160 point!(x4, y4, z4),
161 ])
162 }
163}
164
165impl<T: Copy> Quad<T, 3> {
166 #[inline]
176 pub fn coords(&self) -> [T; 12] {
177 let [p1, p2, p3, p4] = self.points();
178 let [x1, y1, z1] = p1.coords();
179 let [x2, y2, z2] = p2.coords();
180 let [x3, y3, z3] = p3.coords();
181 let [x4, y4, z4] = p4.coords();
182 [x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4]
183 }
184}
185
186impl<T: Copy, const N: usize> Quad<T, N> {
187 #[inline]
189 pub fn p1(&self) -> Point<T, N> {
190 self.0[0]
191 }
192
193 #[inline]
195 pub fn set_p1<P>(&mut self, p: P)
196 where
197 P: Into<Point<T, N>>,
198 {
199 self.0[0] = p.into();
200 }
201
202 #[inline]
204 pub fn p2(&self) -> Point<T, N> {
205 self.0[1]
206 }
207
208 #[inline]
210 pub fn set_p2<P>(&mut self, p: P)
211 where
212 P: Into<Point<T, N>>,
213 {
214 self.0[1] = p.into();
215 }
216
217 #[inline]
219 pub fn p3(&self) -> Point<T, N> {
220 self.0[2]
221 }
222
223 #[inline]
225 pub fn set_p3<P>(&mut self, p: P)
226 where
227 P: Into<Point<T, N>>,
228 {
229 self.0[2] = p.into();
230 }
231
232 #[inline]
234 pub fn p4(&self) -> Point<T, N> {
235 self.0[3]
236 }
237
238 #[inline]
240 pub fn set_p4<P>(&mut self, p: P)
241 where
242 P: Into<Point<T, N>>,
243 {
244 self.0[3] = p.into();
245 }
246
247 #[inline]
262 pub fn points(&self) -> [Point<T, N>; 4] {
263 self.0
264 }
265
266 #[inline]
284 pub fn points_mut(&mut self) -> &mut [Point<T, N>; 4] {
285 &mut self.0
286 }
287
288 pub fn to_vec(self) -> Vec<Point<T, N>> {
303 self.0.to_vec()
304 }
305}
306
307impl Draw for Quad<i32> {
308 fn draw(&self, s: &mut PixState) -> Result<()> {
310 s.quad(*self)
311 }
312}
313
314impl<T: Copy> From<[T; 8]> for Quad<T> {
315 #[inline]
317 fn from([x1, y1, x2, y2, x3, y3, x4, y4]: [T; 8]) -> Self {
318 Self::from_xy(x1, y1, x2, y2, x3, y3, x4, y4)
319 }
320}
321
322impl<T: Copy> From<[T; 12]> for Quad<T, 3> {
323 #[inline]
325 fn from([x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4]: [T; 12]) -> Self {
326 Self::from_xyz(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4)
327 }
328}
329
330impl<T: Copy> From<[[T; 2]; 4]> for Quad<T> {
331 #[inline]
333 fn from([[x1, y1], [x2, y2], [x3, y3], [x4, y4]]: [[T; 2]; 4]) -> Self {
334 Self::from_xy(x1, y1, x2, y2, x3, y3, x4, y4)
335 }
336}
337
338impl<T: Copy> From<[[T; 3]; 4]> for Quad<T, 3> {
339 #[inline]
341 fn from([[x1, y1, z1], [x2, y2, z2], [x3, y3, z3], [x4, y4, z4]]: [[T; 3]; 4]) -> Self {
342 Self::from_xyz(x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4)
343 }
344}
345
346#[cfg(test)]
347mod tests {
348 use crate::prelude::*;
349
350 macro_rules! assert_approx_eq {
351 ($i1:expr, $i2:expr, $e:expr) => {{
352 match ($i1, $i2) {
353 (Some((p1, t1)), Some((p2, t2))) => {
354 let [x1, y1]: [f64; 2] = p1.coords();
355 let [x2, y2]: [f64; 2] = p2.coords();
356 let xd = (x1 - x2).abs();
357 let yd = (y1 - y2).abs();
358 let td = (t1 - t2).abs();
359 assert!(xd < $e, "x: ({} - {}) < {}", x1, x2, $e);
360 assert!(yd < $e, "y: ({} - {}) < {}", y1, y2, $e);
361 assert!(td < $e, "t: ({} - {}) < {}", t1, t2, $e);
362 }
363 _ => assert_eq!($i1, $i2),
364 }
365 }};
366 }
367
368 #[test]
369 fn test_intersects_line() {
370 let rect = rect!(10.0, 10.0, 100.0, 100.0);
371
372 let line: Line<f64> = line_!([3.0, 7.0], [20.0, 30.0]);
374 assert_approx_eq!(
375 rect.intersects(line),
376 Some((point!(10.0, 16.471), 0.411)),
377 0.001
378 );
379
380 let line: Line<f64> = line_!([150.0, 50.0], [90.0, 30.0]);
382 assert_approx_eq!(
383 rect.intersects(line),
384 Some((point!(110.0, 36.667), 0.667)),
385 0.001
386 );
387
388 let line: Line<f64> = line_!([50.0, 5.0], [70.0, 30.0]);
390 assert_approx_eq!(
391 rect.intersects(line),
392 Some((point!(54.0, 10.0), 0.2)),
393 0.001
394 );
395
396 let line: Line<f64> = line_!([50.0, 150.0], [30.0, 30.0]);
398 assert_approx_eq!(
399 rect.intersects(line),
400 Some((point!(43.3333, 110.0), 0.333)),
401 0.001
402 );
403 }
404}