use crate::float::Float;
use crate::point::Pt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Line<F>
where
F: Float,
{
p0: Pt<F>,
p1: Pt<F>,
}
impl<F> Line<F>
where
F: Float,
{
pub fn new<P0, P1>(p0: P0, p1: P1) -> Self
where
P0: Into<Pt<F>>,
P1: Into<Pt<F>>,
{
Self {
p0: p0.into(),
p1: p1.into(),
}
}
pub fn canonical(self) -> Self {
let x0 = Line::new((F::zero(), F::zero()), (F::zero(), F::one()));
let x1 = Line::new((F::one(), F::zero()), (F::one(), F::one()));
let y0 = Line::new((F::zero(), F::zero()), (F::one(), F::zero()));
let y1 = Line::new((F::zero(), F::one()), (F::one(), F::one()));
match (self.intersection(x0), self.intersection(y0)) {
(Some(p0), Some(p1)) => {
if p0 == p1 {
match self.intersection(x1) {
Some(p1) => Line::new(p0, p1),
None => unreachable!(),
}
} else {
Line::new(p0, p1)
}
}
(Some(p0), None) => {
match self.intersection(x1) {
Some(p1) => Line::new(p0, p1),
None => unreachable!(),
}
}
(None, Some(p0)) => {
match self.intersection(y1) {
Some(p1) => Line::new(p0, p1),
None => unreachable!(),
}
}
_ => unreachable!(),
}
}
pub fn distance<P>(self, pt: P) -> F
where
P: Into<Pt<F>>,
{
let pt = pt.into();
let v0 = self.p1 - self.p0;
let v1 = pt - self.p0;
(v0 * v1).abs() / v0.mag()
}
pub fn intersection(self, rhs: Self) -> Option<Pt<F>> {
let v0 = self.p1 - self.p0;
let v1 = rhs.p1 - rhs.p0;
let den = v0 * v1;
if den != F::zero() {
let v2 = self.p0 - rhs.p0;
let num = v1 * v2;
let u = num / den;
let x = self.p0.x + u * v0.x;
let y = self.p0.y + u * v0.y;
Some(Pt::new(x, y))
} else {
None
}
}
pub fn project<P>(self, pt: P) -> Pt<F>
where
P: Into<Pt<F>>,
{
let pt = pt.into();
let perp = (self.p1 - self.p0).right();
let x1 = pt.x + perp.x;
let y1 = pt.y + perp.y;
let p1 = Pt::new(x1, y1);
self.intersection(Self::new(pt, p1)).unwrap()
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn distance() {
let a = Line::new((0.0, 0.0), (1.0, 0.0));
assert_eq!(a.distance((0.0, 1.0)), 1.0);
let b = Line::new((0.0, 0.0), (0.0, 1.0));
assert_eq!(b.distance((2.0, 0.0)), 2.0);
}
#[test]
fn intersection() {
let a = Line::new((0.0, 0.0), (1.0, 0.0));
assert_eq!(a.intersection(a), None);
let c = Line::new((1.0, 1.0), (1.0, 0.0));
assert_eq!(a.intersection(c), Some(Pt::new(1.0, 0.0)));
let b = Line::new((0.0, 0.0), (0.0, 1.0));
assert_eq!(b.intersection(b), None);
}
#[test]
fn projection() {
let d = Line::new((0.0, 0.0), (10.0, 0.0));
assert_eq!(d.project((0.0, 5.0)), Pt::new(0.0, 0.0));
assert_eq!(d.project((5.0, 5.0)), Pt::new(5.0, 0.0));
assert_eq!(d.project((10.0, 5.0)), Pt::new(10.0, 0.0));
assert_eq!(d.project((-5.0, 0.0)), Pt::new(-5.0, 0.0));
assert_eq!(d.project((15.0, 0.0)), Pt::new(15.0, 0.0));
assert_eq!(d.project((0.0, -5.0)), Pt::new(0.0, 0.0));
assert_eq!(d.project((5.0, -5.0)), Pt::new(5.0, 0.0));
assert_eq!(d.project((10.0, -5.0)), Pt::new(10.0, 0.0));
}
#[test]
fn canonical() {
let a = Line::new((0.0, 0.0), (1.0, 0.0));
assert_eq!(a, a.canonical());
let a1 = Line::new((0.0, 15.0), (37.0, 0.0));
assert_eq!(a1, a1.canonical());
let a2 = Line::new((23.0, 0.0), (0.0, -19.0));
assert_eq!(a2.canonical(), Line::new((0.0, -19.0), (23.0, 0.0)));
let h0 = Line::new((0.0, 0.0), (2.0, 0.0));
assert_eq!(h0.canonical(), Line::new((0.0, 0.0), (1.0, 0.0)));
let h1 = Line::new((0.0, 1.0), (5.0, 1.0));
assert_eq!(h1.canonical(), Line::new((0.0, 1.0), (1.0, 1.0)));
let v0 = Line::new((0.0, -1.0), (0.0, 1.0));
assert_eq!(v0.canonical(), Line::new((0.0, 0.0), (0.0, 1.0)));
let v1 = Line::new((1.0, -3.0), (1.0, 8.0));
assert_eq!(v1.canonical(), Line::new((1.0, 0.0), (1.0, 1.0)));
}
}