use geometry_coords::CoordinateScalar;
use geometry_trait::Point;
use super::orientation::Sign;
#[must_use]
pub fn in_circle_2d<P>(a: &P, b: &P, c: &P, d: &P) -> Sign
where
P: Point,
P::Scalar: CoordinateScalar,
{
let ax = a.get::<0>();
let ay = a.get::<1>();
let bx = b.get::<0>();
let by = b.get::<1>();
let cx = c.get::<0>();
let cy = c.get::<1>();
let dx = d.get::<0>();
let dy = d.get::<1>();
let adx = ax - dx;
let ady = ay - dy;
let bdx = bx - dx;
let bdy = by - dy;
let cdx = cx - dx;
let cdy = cy - dy;
let a_lift = adx * adx + ady * ady;
let b_lift = bdx * bdx + bdy * bdy;
let c_lift = cdx * cdx + cdy * cdy;
let det = adx * (bdy * c_lift - b_lift * cdy) - ady * (bdx * c_lift - b_lift * cdx)
+ a_lift * (bdx * cdy - bdy * cdx);
let base = orientation_of(adx, ady, bdx, bdy, cdx, cdy);
match base {
Sign::Collinear => Sign::Collinear, Sign::Positive => sign_of(det), Sign::Negative => flip(sign_of(det)), }
}
fn orientation_of<T: CoordinateScalar>(ax: T, ay: T, bx: T, by: T, cx: T, cy: T) -> Sign {
let area = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax);
sign_of(area)
}
fn sign_of<T: CoordinateScalar>(v: T) -> Sign {
if v > T::ZERO {
Sign::Positive
} else if v < T::ZERO {
Sign::Negative
} else {
Sign::Collinear
}
}
fn flip(s: Sign) -> Sign {
match s {
Sign::Positive => Sign::Negative,
Sign::Negative => Sign::Positive,
Sign::Collinear => Sign::Collinear,
}
}
#[cfg(test)]
mod tests {
use super::in_circle_2d;
use crate::predicate::orientation::Sign;
use geometry_cs::Cartesian;
use geometry_model::Point2D;
type P = Point2D<f64, Cartesian>;
#[test]
fn ccw_unit_circle() {
let a = P::new(1.0, 0.0);
let b = P::new(0.0, 1.0);
let c = P::new(-1.0, 0.0);
assert_eq!(in_circle_2d(&a, &b, &c, &P::new(0.0, 0.0)), Sign::Positive);
assert_eq!(
in_circle_2d(&a, &b, &c, &P::new(0.0, -1.0)),
Sign::Collinear
);
assert_eq!(in_circle_2d(&a, &b, &c, &P::new(3.0, 3.0)), Sign::Negative);
}
#[test]
fn cw_triangle_gives_same_answer() {
let a = P::new(-1.0, 0.0);
let b = P::new(0.0, 1.0);
let c = P::new(1.0, 0.0);
assert_eq!(in_circle_2d(&a, &b, &c, &P::new(0.0, 0.0)), Sign::Positive);
assert_eq!(
in_circle_2d(&a, &b, &c, &P::new(0.0, -1.0)),
Sign::Collinear
);
assert_eq!(in_circle_2d(&a, &b, &c, &P::new(9.0, 0.0)), Sign::Negative);
}
#[test]
fn collinear_triangle_is_degenerate() {
let a = P::new(0.0, 0.0);
let b = P::new(1.0, 0.0);
let c = P::new(2.0, 0.0);
assert_eq!(in_circle_2d(&a, &b, &c, &P::new(0.5, 5.0)), Sign::Collinear);
}
#[test]
fn larger_circle_containment() {
let a = P::new(5.0, 0.0);
let b = P::new(0.0, 5.0);
let c = P::new(-5.0, 0.0);
assert_eq!(in_circle_2d(&a, &b, &c, &P::new(3.0, 3.0)), Sign::Positive); assert_eq!(in_circle_2d(&a, &b, &c, &P::new(4.0, 4.0)), Sign::Negative); }
}