use geometry_coords::{CoordinateScalar, precise_math};
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 + Into<f64>,
{
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 a = [ax.into(), ay.into()];
let b = [bx.into(), by.into()];
let c = [cx.into(), cy.into()];
let d = [dx.into(), dy.into()];
let determinant = precise_math::incircle(a, b, c, d);
let base = sign_of(precise_math::orient2d(a, b, c));
match base {
Sign::Collinear => Sign::Collinear, Sign::Positive => sign_of(determinant), Sign::Negative => flip(sign_of(determinant)), }
}
fn sign_of(value: f64) -> Sign {
if value > 0.0 {
Sign::Positive
} else if value < 0.0 {
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); }
}