use geometry_coords::{CoordinateScalar, precise_math};
use geometry_trait::Point;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Sign {
Positive,
Negative,
Collinear,
}
#[must_use]
pub fn orientation_2d<P>(p: &P, q: &P, r: &P) -> Sign
where
P: Point,
P::Scalar: CoordinateScalar + Into<f64>,
{
let px = p.get::<0>();
let py = p.get::<1>();
let qx = q.get::<0>();
let qy = q.get::<1>();
let rx = r.get::<0>();
let ry = r.get::<1>();
let area = precise_math::orient2d(
[px.into(), py.into()],
[qx.into(), qy.into()],
[rx.into(), ry.into()],
);
if area > 0.0 {
Sign::Positive
} else if area < 0.0 {
Sign::Negative
} else {
Sign::Collinear
}
}
#[cfg(test)]
mod tests {
use super::{Sign, orientation_2d};
use geometry_cs::Cartesian;
use geometry_model::Point2D;
type P = Point2D<f64, Cartesian>;
#[test]
fn left_right_collinear_unit_segment() {
let p = P::new(0.0, 0.0);
let q = P::new(1.0, 0.0);
assert_eq!(orientation_2d(&p, &q, &P::new(0.5, 1.0)), Sign::Positive);
assert_eq!(orientation_2d(&p, &q, &P::new(0.5, -1.0)), Sign::Negative);
assert_eq!(orientation_2d(&p, &q, &P::new(0.5, 0.0)), Sign::Collinear);
}
#[test]
fn sign_flips_with_base_direction() {
let a = P::new(0.0, 0.0);
let b = P::new(4.0, 4.0);
let c = P::new(4.0, 0.0);
assert_eq!(orientation_2d(&a, &b, &c), Sign::Negative);
assert_eq!(orientation_2d(&b, &a, &c), Sign::Positive);
}
#[test]
fn coincident_points_are_collinear() {
let p = P::new(2.0, 3.0);
let r = P::new(9.0, 9.0);
assert_eq!(orientation_2d(&p, &p, &r), Sign::Collinear);
assert_eq!(orientation_2d(&p, &r, &p), Sign::Collinear);
assert_eq!(orientation_2d(&r, &p, &p), Sign::Collinear);
}
#[test]
fn diagonal_line_sides() {
let p = P::new(0.0, 0.0);
let q = P::new(2.0, 2.0);
assert_eq!(orientation_2d(&p, &q, &P::new(0.0, 2.0)), Sign::Positive);
assert_eq!(orientation_2d(&p, &q, &P::new(2.0, 0.0)), Sign::Negative);
assert_eq!(orientation_2d(&p, &q, &P::new(5.0, 5.0)), Sign::Collinear);
}
}