use super::line::*;
use super::super::geo::*;
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct LineCoefficients(pub f64, pub f64, pub f64);
impl Into<(f64, f64, f64)> for LineCoefficients {
#[inline]
fn into(self) -> (f64, f64, f64) {
(self.0, self.1, self.2)
}
}
pub fn line_coefficients_2d_unnormalized<L: Line+?Sized>(line: &L) -> LineCoefficients
where
L::Point: Coordinate+Coordinate2D,
{
let (from, to) = line.points();
let offset = to - from;
let LineCoefficients(a, b, c) = if offset.x() == 0.0 && offset.y() == 0.0 {
return LineCoefficients(0.0, 0.0, 0.0);
} else if offset.x().abs() > offset.y().abs() {
let a = offset.y() / offset.x();
let b = -1.0;
let c = -(a*from.x() + b*from.y());
if offset.x() > 0.0 {
LineCoefficients(-a, -b, -c)
} else {
LineCoefficients(a, b, c)
}
} else {
let a = -1.0;
let b = offset.x() / offset.y();
let c = -(a*from.x() + b*from.y());
if offset.y() > 0.0 {
LineCoefficients(-a, -b, -c)
} else {
LineCoefficients(a, b, c)
}
};
LineCoefficients(a, b, c)
}
pub fn line_coefficients_2d<L: Line+?Sized>(line: &L) -> LineCoefficients
where
L::Point: Coordinate+Coordinate2D,
{
let LineCoefficients(a, b, c) = line_coefficients_2d_unnormalized(line);
let factor = (a*a + b*b).sqrt();
let (a, b, c) = (a/factor, b/factor, c/factor);
LineCoefficients(a, b, c)
}
impl LineCoefficients {
#[inline]
pub fn is_point(&self) -> bool {
self.0 == 0.0 && self.1 == 0.0 && self.2 == 0.0
}
#[inline]
pub fn distance_to<Point>(&self, p: &Point) -> f64
where
Point: Coordinate2D,
{
let LineCoefficients(a, b, c) = self;
a*p.x() + b*p.y() + c
}
#[inline]
pub fn to_perpendicular_line<Point>(&self, pass_through: &Point) -> LineCoefficients
where
Point: Coordinate2D,
{
let LineCoefficients(a, b, _c) = self;
let a2 = *b;
let b2 = *a;
let c2 = -a2 * pass_through.x() - b2 * pass_through.y();
LineCoefficients(a2, b2, c2)
}
#[inline]
pub fn nearest_point<Point>(&self, p: &Point) -> Point
where
Point: Coordinate + Coordinate2D,
{
let x = p.x();
let y = p.y();
let LineCoefficients(a, b, c) = self;
let c2 = -b*x + a*y;
let near_x = (-a*c - b*c2) / (a*a + b*b);
let near_y = (a*c2 - b*c) / (a*a + b*b);
Point::from_components(&[near_x, near_y])
}
#[inline]
pub fn x_for_y(&self, y: f64) -> f64 {
let LineCoefficients(a, b, c) = self;
(-b*y - c) / a
}
#[inline]
pub fn y_for_x(&self, x: f64) -> f64 {
let LineCoefficients(a, b, c) = self;
(-a*x - c) / b
}
}