use num_traits::{Num, Signed, Float};
use crate::errors::GeometryError;
pub fn manhattan_distance<T>(x1: T, y1: T, x2: T, y2: T) -> T
where
T: Num + Copy + Signed,
{
(x1 - x2).abs() + (y1 - y2).abs()
}
pub fn euclidean<T>(x1: T, y1: T, x2: T, y2: T) -> T
where
T: Float,
{
((x1 - x2).powi(2) + (y1 - y2).powi(2)).sqrt()
}
pub fn squared_euclidean<T>(x1: T, y1: T, x2: T, y2: T) -> T
where
T: Float,
{
(x1 - x2).powi(2) + (y1 - y2).powi(2)
}
#[derive(Clone, Debug, PartialEq)]
pub struct Point {
pub x: f64,
pub y: f64,
}
#[derive(Clone, Debug, PartialEq)]
pub struct Polygon {
pub points: Vec<Point>, }
impl Polygon {
pub fn new(points: Vec<Point>) -> Result<Self, GeometryError> {
if points.len() < 3 {
return Err(GeometryError::InvalidPolygon);
}
Ok(Self { points })
}
pub fn contains(&self, point: &Point) -> bool {
let mut inside = false;
let n = self.points.len();
for i in 0..n {
let j = (i + 1) % n; let vi = &self.points[i];
let vj = &self.points[j];
if (vi.y > point.y) != (vj.y > point.y) &&
point.x < (vj.x - vi.x) * (point.y - vi.y) / (vj.y - vi.y) + vi.x {
inside = !inside;
}
}
inside
}
pub fn line_intersects(&self, start: &Point, end: &Point) -> bool {
let n = self.points.len();
for i in 0..n {
let j = (i + 1) % n; let vi = &self.points[i];
let vj = &self.points[j];
if segments_intersect(&start, &end, vi, vj) {
return true;
}
}
false
}
}
fn orientation(p: &Point, q: &Point, r: &Point) -> f64 {
(q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y)
}
fn on_segment(p: &Point, q: &Point, r: &Point) -> bool {
q.x <= f64::max(p.x, r.x) &&
q.x >= f64::min(p.x, r.x) &&
q.y <= f64::max(p.y, r.y) &&
q.y >= f64::min(p.y, r.y)
}
fn segments_intersect(p1: &Point, q1: &Point, p2: &Point, q2: &Point) -> bool {
let o1 = orientation(p1, q1, p2);
let o2 = orientation(p1, q1, q2);
let o3 = orientation(p2, q2, p1);
let o4 = orientation(p2, q2, q1);
if o1 * o2 < 0.0 && o3 * o4 < 0.0 {
return true;
}
if o1 == 0.0 && on_segment(p1, p2, q1) { return true; }
if o2 == 0.0 && on_segment(p1, q2, q1) { return true; }
if o3 == 0.0 && on_segment(p2, p1, q2) { return true; }
if o4 == 0.0 && on_segment(p2, q1, q2) { return true; }
false
}