use crate::{Coord, CoordNum, Point};
#[derive(Eq, PartialEq, Clone, Copy, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Line<T: CoordNum = f64> {
pub start: Coord<T>,
pub end: Coord<T>,
}
impl<T: CoordNum> Line<T> {
pub fn new<C>(start: C, end: C) -> Self
where
C: Into<Coord<T>>,
{
Self {
start: start.into(),
end: end.into(),
}
}
pub fn delta(&self) -> Coord<T> {
self.end - self.start
}
pub fn dx(&self) -> T {
self.delta().x
}
pub fn dy(&self) -> T {
self.delta().y
}
pub fn slope(&self) -> T {
self.dy() / self.dx()
}
pub fn determinant(&self) -> T {
self.start.x * self.end.y - self.start.y * self.end.x
}
pub fn start_point(&self) -> Point<T> {
Point::from(self.start)
}
pub fn end_point(&self) -> Point<T> {
Point::from(self.end)
}
pub fn points(&self) -> (Point<T>, Point<T>) {
(self.start_point(), self.end_point())
}
}
impl<T: CoordNum> From<[(T, T); 2]> for Line<T> {
fn from(coord: [(T, T); 2]) -> Self {
Line::new(coord[0], coord[1])
}
}
#[cfg(any(feature = "approx", test))]
mod approx_integration {
use super::*;
use approx::{AbsDiffEq, RelativeEq, UlpsEq};
impl<T> RelativeEq for Line<T>
where
T: CoordNum + RelativeEq<Epsilon = T>,
{
#[inline]
fn default_max_relative() -> Self::Epsilon {
T::default_max_relative()
}
#[inline]
fn relative_eq(
&self,
other: &Self,
epsilon: Self::Epsilon,
max_relative: Self::Epsilon,
) -> bool {
self.start.relative_eq(&other.start, epsilon, max_relative)
&& self.end.relative_eq(&other.end, epsilon, max_relative)
}
}
impl<T> AbsDiffEq for Line<T>
where
T: CoordNum + AbsDiffEq<Epsilon = T>,
{
type Epsilon = T;
#[inline]
fn default_epsilon() -> Self::Epsilon {
T::default_epsilon()
}
#[inline]
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
self.start.abs_diff_eq(&other.start, epsilon)
&& self.end.abs_diff_eq(&other.end, epsilon)
}
}
impl<T> UlpsEq for Line<T>
where
T: CoordNum + UlpsEq<Epsilon = T>,
{
fn default_max_ulps() -> u32 {
T::default_max_ulps()
}
fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
self.start.ulps_eq(&other.start, epsilon, max_ulps)
&& self.end.ulps_eq(&other.end, epsilon, max_ulps)
}
}
}
#[cfg(any(
feature = "rstar_0_8",
feature = "rstar_0_9",
feature = "rstar_0_10",
feature = "rstar_0_11",
feature = "rstar_0_12",
feature = "rstar_0_13"
))]
macro_rules! impl_rstar_line {
($rstar:ident) => {
impl<T> ::$rstar::RTreeObject for Line<T>
where
T: ::num_traits::Float + ::$rstar::RTreeNum,
{
type Envelope = ::$rstar::AABB<Point<T>>;
fn envelope(&self) -> Self::Envelope {
::$rstar::AABB::from_corners(self.start_point(), self.end_point())
}
}
impl<T> ::$rstar::PointDistance for Line<T>
where
T: ::num_traits::Float + ::$rstar::RTreeNum,
{
fn distance_2(&self, point: &Point<T>) -> T {
let d = crate::private_utils::point_line_euclidean_distance(*point, *self);
d.powi(2)
}
}
};
}
#[cfg(feature = "rstar_0_8")]
impl_rstar_line!(rstar_0_8);
#[cfg(feature = "rstar_0_9")]
impl_rstar_line!(rstar_0_9);
#[cfg(feature = "rstar_0_10")]
impl_rstar_line!(rstar_0_10);
#[cfg(feature = "rstar_0_11")]
impl_rstar_line!(rstar_0_11);
#[cfg(feature = "rstar_0_12")]
impl_rstar_line!(rstar_0_12);
#[cfg(feature = "rstar_0_13")]
impl_rstar_line!(rstar_0_13);
#[cfg(test)]
mod test {
use super::*;
use crate::{coord, point};
use approx::{AbsDiffEq, RelativeEq};
#[test]
fn test_abs_diff_eq() {
let delta = 1e-6;
let line = Line::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 1. });
let line_start_x = Line::new(
point! {
x: 0. + delta,
y: 0.,
},
point! { x: 1., y: 1. },
);
assert!(line.abs_diff_eq(&line_start_x, 1e-2));
assert!(line.abs_diff_ne(&line_start_x, 1e-12));
let line_start_y = Line::new(
coord! {
x: 0.,
y: 0. + delta,
},
coord! { x: 1., y: 1. },
);
assert!(line.abs_diff_eq(&line_start_y, 1e-2));
assert!(line.abs_diff_ne(&line_start_y, 1e-12));
let line_end_x = Line::new(
coord! { x: 0., y: 0. },
coord! {
x: 1. + delta,
y: 1.,
},
);
assert!(line.abs_diff_eq(&line_end_x, 1e-2));
assert!(line.abs_diff_ne(&line_end_x, 1e-12));
let line_end_y = Line::new(
coord! { x: 0., y: 0. },
coord! {
x: 1.,
y: 1. + delta,
},
);
assert!(line.abs_diff_eq(&line_end_y, 1e-2));
assert!(line.abs_diff_ne(&line_end_y, 1e-12));
}
#[test]
fn test_relative_eq() {
let delta = 1e-6;
let line = Line::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 1. });
let line_start_x = Line::new(
point! {
x: 0. + delta,
y: 0.,
},
point! { x: 1., y: 1. },
);
let line_start_y = Line::new(
coord! {
x: 0.,
y: 0. + delta,
},
coord! { x: 1., y: 1. },
);
assert!(line.relative_eq(&line_start_x, 1e-2, 1e-2));
assert!(line.relative_ne(&line_start_x, 1e-12, 1e-12));
assert!(line.relative_eq(&line_start_y, 1e-2, 1e-2));
assert!(line.relative_ne(&line_start_y, 1e-12, 1e-12));
}
}