use crate::aabb::AABB;
use crate::envelope::Envelope;
use crate::object::PointDistance;
use crate::object::RTreeObject;
use crate::point::{Point, PointExt};
use num_traits::{One, Zero};
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Line<P>
where
P: Point,
{
pub from: P,
pub to: P,
}
impl<P> Line<P>
where
P: Point,
{
pub fn new(from: P, to: P) -> Self {
Line { from, to }
}
}
impl<P> RTreeObject for Line<P>
where
P: Point,
{
type Envelope = AABB<P>;
fn envelope(&self) -> Self::Envelope {
AABB::from_corners(self.from.clone(), self.to.clone())
}
}
impl<P> Line<P>
where
P: Point,
{
pub fn length_2(&self) -> P::Scalar {
self.from.sub(&self.to).length_2()
}
fn project_point(&self, query_point: &P) -> P::Scalar {
let (ref p1, ref p2) = (self.from.clone(), self.to.clone());
let dir = p2.sub(p1);
query_point.sub(p1).dot(&dir) / dir.length_2()
}
pub fn nearest_point(&self, query_point: &P) -> P {
let (p1, p2) = (self.from.clone(), self.to.clone());
let dir = p2.sub(&p1);
let s = self.project_point(query_point);
if P::Scalar::zero() < s && s < One::one() {
p1.add(&dir.mul(s))
} else if s <= P::Scalar::zero() {
p1
} else {
p2
}
}
}
impl<P> PointDistance for Line<P>
where
P: Point,
{
fn distance_2(
&self,
point: &<Self::Envelope as Envelope>::Point,
) -> <<Self::Envelope as Envelope>::Point as Point>::Scalar {
self.nearest_point(point).sub(point).length_2()
}
}
#[cfg(test)]
mod test {
use super::Line;
use crate::object::PointDistance;
use approx::*;
#[test]
fn edge_distance() {
let edge = Line::new([0.5, 0.5], [0.5, 2.0]);
assert_abs_diff_eq!(edge.distance_2(&[0.5, 0.5]), 0.0);
assert_abs_diff_eq!(edge.distance_2(&[0.0, 0.5]), 0.5 * 0.5);
assert_abs_diff_eq!(edge.distance_2(&[0.5, 1.0]), 0.0);
assert_abs_diff_eq!(edge.distance_2(&[0.0, 0.0]), 0.5);
assert_abs_diff_eq!(edge.distance_2(&[0.0, 1.0]), 0.5 * 0.5);
assert_abs_diff_eq!(edge.distance_2(&[1.0, 1.0]), 0.5 * 0.5);
assert_abs_diff_eq!(edge.distance_2(&[1.0, 3.0]), 0.5 * 0.5 + 1.0);
}
#[test]
fn length_2() {
let line = Line::new([1, -1], [5, 5]);
assert_eq!(line.length_2(), 16 + 36);
}
}