use crate::{error::Result, prelude::*};
#[cfg(feature = "serde")]
use serde::{de::DeserializeOwned, Deserialize, Serialize};
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[repr(transparent)]
#[must_use]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(bound = "T: Serialize + DeserializeOwned"))]
pub struct Line<T = i32, const N: usize = 2>(pub(crate) [Point<T, N>; 2]);
#[macro_export]
macro_rules! line_ {
($p1:expr, $p2:expr$(,)?) => {
$crate::prelude::Line::new($p1, $p2)
};
($x1:expr, $y1:expr, $x2:expr, $y2:expr$(,)?) => {
$crate::prelude::Line::from_xy($x1, $y1, $x2, $y2)
};
($x1:expr, $y1:expr, $z1:expr, $x2:expr, $y2:expr, $z2:expr$(,)?) => {
$crate::prelude::Line::from_xyz($x1, $y1, $z2, $x2, $y2, $z2)
};
}
impl<T, const N: usize> Line<T, N> {
pub fn new<P1, P2>(start: P1, end: P2) -> Self
where
P1: Into<Point<T, N>>,
P2: Into<Point<T, N>>,
{
Self([start.into(), end.into()])
}
}
impl<T> Line<T> {
#[inline]
pub const fn from_xy(x1: T, y1: T, x2: T, y2: T) -> Self {
Self([point!(x1, y1), point!(x2, y2)])
}
}
impl<T: Copy> Line<T> {
#[inline]
pub fn coords(&self) -> [T; 4] {
let [p1, p2] = self.points();
let [x1, y1] = p1.coords();
let [x2, y2] = p2.coords();
[x1, y1, x2, y2]
}
}
impl<T> Line<T, 3> {
#[inline]
pub const fn from_xyz(x1: T, y1: T, z1: T, x2: T, y2: T, z2: T) -> Self {
Self([point!(x1, y1, z1), point!(x2, y2, z2)])
}
}
impl<T: Copy> Line<T, 3> {
#[inline]
pub fn coords(&self) -> [T; 6] {
let [p1, p2] = self.points();
let [x1, y1, z1] = p1.coords();
let [x2, y2, z2] = p2.coords();
[x1, y1, z1, x2, y2, z2]
}
}
impl<T: Copy, const N: usize> Line<T, N> {
#[inline]
pub fn start(&self) -> Point<T, N> {
self.0[0]
}
#[inline]
pub fn set_start<P: Into<Point<T, N>>>(&mut self, start: P) {
self.0[0] = start.into();
}
#[inline]
pub fn end(&self) -> Point<T, N> {
self.0[1]
}
#[inline]
pub fn set_end<P: Into<Point<T, N>>>(&mut self, end: P) {
self.0[1] = end.into();
}
#[inline]
pub fn points(&self) -> [Point<T, N>; 2] {
self.0
}
#[inline]
pub fn points_mut(&mut self) -> &mut [Point<T, N>; 2] {
&mut self.0
}
pub fn to_vec(self) -> Vec<Vec<T>> {
let start = self.start().to_vec();
let end = self.end().to_vec();
vec![start, end]
}
}
impl<T: Float> Intersects<Line<T>> for Line<T> {
type Result = (Point<T>, T);
#[allow(clippy::many_single_char_names)]
fn intersects(&self, line: Line<T>) -> Option<Self::Result> {
let [x1, y1, x2, y2] = self.coords();
let [x3, y3, x4, y4] = line.coords();
let d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
if d == T::zero() {
return None;
}
let t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / d;
let u = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / d;
if (T::zero()..).contains(&t) && (T::zero()..=T::one()).contains(&u) {
let x = x1 + t * (x2 - x1);
let y = y1 + t * (y2 - y1);
Some((point!(x, y), t))
} else {
None
}
}
}
impl Draw for Line<i32> {
fn draw(&self, s: &mut PixState) -> Result<()> {
s.line(*self)
}
}
impl<T: Copy> From<[T; 4]> for Line<T> {
#[inline]
fn from([x1, y1, x2, y2]: [T; 4]) -> Self {
Self::from_xy(x1, y1, x2, y2)
}
}
impl<T: Copy> From<[T; 6]> for Line<T, 3> {
#[inline]
fn from([x1, y1, z1, x2, y2, z2]: [T; 6]) -> Self {
Self::from_xyz(x1, y1, z1, x2, y2, z2)
}
}
impl<T: Copy> From<[[T; 2]; 2]> for Line<T> {
#[inline]
fn from([[x1, y1], [x2, y2]]: [[T; 2]; 2]) -> Self {
Self::from_xy(x1, y1, x2, y2)
}
}
impl<T: Copy> From<[[T; 3]; 2]> for Line<T, 3> {
#[inline]
fn from([[x1, y1, z1], [x2, y2, z2]]: [[T; 3]; 2]) -> Self {
Self::from_xyz(x1, y1, z1, x2, y2, z2)
}
}