use axiolid_core::{Point2, Point3};
use axiolid_guarantees::Sign;
const EPSILON: f64 = f64::EPSILON / 2.0;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct StaticFilter {
bound: f64,
orient2d: f64,
orient3d: f64,
}
impl StaticFilter {
#[must_use]
pub fn new(bound: f64) -> Option<Self> {
if !bound.is_finite() || bound <= 0.0 {
return None;
}
let span = 2.0 * bound;
let orient2d = (3.0 + 16.0 * EPSILON) * EPSILON * (2.0 * span * span);
let orient3d = (7.0 + 56.0 * EPSILON) * EPSILON * (6.0 * span * span * span);
if !orient2d.is_finite() || !orient3d.is_finite() {
return None;
}
Some(Self {
bound,
orient2d,
orient3d,
})
}
#[must_use]
pub const fn bound(self) -> f64 {
self.bound
}
#[must_use]
fn covers2(self, p: Point2) -> bool {
p.x.abs() <= self.bound && p.y.abs() <= self.bound
}
#[must_use]
fn covers3(self, p: Point3) -> bool {
p.x.abs() <= self.bound && p.y.abs() <= self.bound && p.z.abs() <= self.bound
}
}
impl StaticFilter {
#[must_use]
pub fn orient2d(self, a: Point2, b: Point2, c: Point2) -> Option<Sign> {
if !(self.covers2(a) && self.covers2(b) && self.covers2(c)) {
return None;
}
let determinant = (a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x);
decide(determinant, self.orient2d)
}
#[must_use]
pub fn orient3d(self, a: Point3, b: Point3, c: Point3, d: Point3) -> Option<Sign> {
if !(self.covers3(a) && self.covers3(b) && self.covers3(c) && self.covers3(d)) {
return None;
}
let (adx, ady, adz) = (a.x - d.x, a.y - d.y, a.z - d.z);
let (bdx, bdy, bdz) = (b.x - d.x, b.y - d.y, b.z - d.z);
let (cdx, cdy, cdz) = (c.x - d.x, c.y - d.y, c.z - d.z);
let determinant = adz * (bdx * cdy - cdx * bdy)
+ bdz * (cdx * ady - adx * cdy)
+ cdz * (adx * bdy - bdx * ady);
decide(determinant, self.orient3d)
}
}
#[inline]
#[must_use]
fn decide(determinant: f64, bound: f64) -> Option<Sign> {
if determinant > bound {
Some(Sign::Positive)
} else if determinant < -bound {
Some(Sign::Negative)
} else {
None
}
}