use super::coefficients::*;
use crate::geo::*;
use std::f64;
pub trait Line : Geo {
fn from_points(p1: Self::Point, p2: Self::Point) -> Self;
fn points(&self) -> (Self::Point, Self::Point);
fn point_at_pos(&self, t: f64) -> Self::Point {
let (p1, p2) = self.points();
let delta = p2-p1;
p1 + delta*t
}
fn pos_for_point(&self, point: &Self::Point) -> f64 {
let (p1, p2) = self.points();
let delta_line = p2-p1;
let delta_point = *point-p1;
for component_idx in 0..Self::Point::len() {
let line_component = delta_line.get(component_idx);
let point_component = delta_point.get(component_idx);
if line_component.abs() > 0.000001 && point_component.abs() > 0.000001 {
return point_component/line_component;
}
}
0.0
}
}
pub trait Line2D {
type Point: Coordinate+Coordinate2D;
fn coefficients(&self) -> LineCoefficients;
fn distance_to(&self, p: &Self::Point) -> f64;
fn which_side(&self, p: &Self::Point) -> i8;
fn nearest_point(&self, point: &Self::Point) -> Self::Point;
fn nearest_pos(&self, point: &Self::Point) -> f64;
fn angle_to(&self, other_line: &impl Line<Point=Self::Point>) -> f64;
fn angle(&self) -> f64;
}
impl<Point> Geo for (Point, Point)
where
Point: Coordinate + Clone,
{
type Point = Point;
}
impl<Point> Line for (Point, Point)
where
Point: Coordinate+ Clone,
{
#[inline]
fn from_points(p1: Self::Point, p2: Self::Point) -> Self {
(p1, p2)
}
#[inline]
fn points(&self) -> (Self::Point, Self::Point) {
*self
}
}
impl<L> Line2D for L
where
L: Line,
L::Point: Coordinate2D + Coordinate + Clone,
{
type Point = L::Point;
#[inline]
fn coefficients(&self) -> LineCoefficients {
line_coefficients_2d(self)
}
#[inline]
fn distance_to(&self, p: &Self::Point) -> f64 {
self.coefficients().distance_to(p)
}
#[inline]
fn which_side(&self, p: &Self::Point) -> i8 {
let (start, end) = self.points();
let side = ((p.x()-start.x())*(end.y()-start.y()) - (p.y()-start.y())*(end.x()-start.x())).signum();
if side < 0.0 {
-1
} else if side > 0.0 {
1
} else {
0
}
}
#[inline]
fn nearest_point(&self, point: &Self::Point) -> Self::Point {
self.coefficients().nearest_point(point)
}
#[inline]
fn nearest_pos(&self, point: &Self::Point) -> f64 {
self.pos_for_point(&self.nearest_point(point))
}
fn angle(&self) -> f64 {
let (sp, ep) = self.points();
let vector = ep - sp;
let angle = f64::atan2(vector.y(), vector.x());
if angle >= 0.0 {
angle
} else {
angle + 2.0*f64::consts::PI
}
}
#[inline]
fn angle_to(&self, other_line: &impl Line<Point=Self::Point>) -> f64 {
let angle1 = self.angle();
let angle2 = other_line.angle();
let angle_between = angle1 - angle2;
if angle_between >= 0.0 {
angle_between
} else {
angle_between + 2.0*f64::consts::PI
}
}
}