Initial commit

This commit is contained in:
redclawsystems
2026-03-04 00:08:42 +00:00
commit 4d88dc0584
4449 changed files with 1556714 additions and 0 deletions
@@ -0,0 +1,180 @@
//! Geometric predicates for Delaunay triangulation.
//!
//! These predicates determine orientation and circumsphere tests.
//! For production use, these should be replaced with adaptive-precision
//! or exact arithmetic implementations (e.g., from the `robust` crate).
use nalgebra::Point3;
/// Compute the orientation of four points in 3D.
///
/// Returns:
/// - Positive if d is on the positive side of the plane defined by (a,b,c)
/// - Negative if on the negative side
/// - Zero if coplanar
pub fn orient3d(a: &Point3<f64>, b: &Point3<f64>, c: &Point3<f64>, d: &Point3<f64>) -> f64 {
// Standard orient3d using the signed volume of the tetrahedron
// Positive when d is above the plane (a,b,c) with ccw orientation
let ab = b - a;
let ac = c - a;
let ad = d - a;
// det = ab · (ac × ad)
ab.dot(&ac.cross(&ad))
}
/// Test if point e is inside the circumsphere of tetrahedron (a,b,c,d).
///
/// Returns:
/// - Positive if e is inside the circumsphere
/// - Negative if outside
/// - Zero if on the sphere
///
/// Assumes (a,b,c,d) has positive orientation (orient3d > 0).
pub fn in_circumsphere(
a: &Point3<f64>,
b: &Point3<f64>,
c: &Point3<f64>,
d: &Point3<f64>,
e: &Point3<f64>,
) -> f64 {
// InSphere determinant test
// If orient3d(a,b,c,d) > 0, then insphere(a,b,c,d,e) > 0 means e is inside
let aex = a.x - e.x;
let aey = a.y - e.y;
let aez = a.z - e.z;
let bex = b.x - e.x;
let bey = b.y - e.y;
let bez = b.z - e.z;
let cex = c.x - e.x;
let cey = c.y - e.y;
let cez = c.z - e.z;
let dex = d.x - e.x;
let dey = d.y - e.y;
let dez = d.z - e.z;
let ae_sq = aex * aex + aey * aey + aez * aez;
let be_sq = bex * bex + bey * bey + bez * bez;
let ce_sq = cex * cex + cey * cey + cez * cez;
let de_sq = dex * dex + dey * dey + dez * dez;
// 4x4 determinant using cofactor expansion
let ab = aex * bey - bex * aey;
let bc = bex * cey - cex * bey;
let cd = cex * dey - dex * cey;
let da = dex * aey - aex * dey;
let ac = aex * cey - cex * aey;
let bd = bex * dey - dex * bey;
let abc = aez * bc - bez * ac + cez * ab;
let bcd = bez * cd - cez * bd + dez * bc;
let cda = cez * da + dez * ac + aez * cd;
let dab = dez * ab + aez * bd + bez * da;
ae_sq * bcd - be_sq * cda + ce_sq * dab - de_sq * abc
}
/// Compute the circumcenter of a tetrahedron.
pub fn circumcenter(
a: &Point3<f64>,
b: &Point3<f64>,
c: &Point3<f64>,
d: &Point3<f64>,
) -> Option<Point3<f64>> {
let ba = b - a;
let ca = c - a;
let da = d - a;
let ba_sq = ba.norm_squared();
let ca_sq = ca.norm_squared();
let da_sq = da.norm_squared();
// Determinant (denominator)
let denom = 2.0 * ba.dot(&ca.cross(&da));
if denom.abs() < 1e-15 {
return None; // Degenerate tetrahedron
}
// Circumcenter = a + (ba² * (ca × da) + ca² * (da × ba) + da² * (ba × ca)) / denom
let cc = a + (ca.cross(&da) * ba_sq + da.cross(&ba) * ca_sq + ba.cross(&ca) * da_sq) / denom;
Some(cc)
}
/// Compute the circumradius of a tetrahedron.
pub fn circumradius(
a: &Point3<f64>,
b: &Point3<f64>,
c: &Point3<f64>,
d: &Point3<f64>,
) -> Option<f64> {
let cc = circumcenter(a, b, c, d)?;
Some((a - cc).norm())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_orient3d() {
let a = Point3::new(0.0, 0.0, 0.0);
let b = Point3::new(1.0, 0.0, 0.0);
let c = Point3::new(0.0, 1.0, 0.0);
let d = Point3::new(0.0, 0.0, 1.0);
// d is above the plane (a,b,c)
let o = orient3d(&a, &b, &c, &d);
assert!(o > 0.0);
// Flip d below
let d_below = Point3::new(0.0, 0.0, -1.0);
let o2 = orient3d(&a, &b, &c, &d_below);
assert!(o2 < 0.0);
}
#[test]
fn test_in_circumsphere() {
// Regular tetrahedron
let a = Point3::new(1.0, 1.0, 1.0);
let b = Point3::new(-1.0, -1.0, 1.0);
let c = Point3::new(-1.0, 1.0, -1.0);
let d = Point3::new(1.0, -1.0, -1.0);
// Center should be inside
let center = Point3::new(0.0, 0.0, 0.0);
let inside = in_circumsphere(&a, &b, &c, &d, &center);
assert!(inside > 0.0);
// Far point should be outside
let far = Point3::new(10.0, 10.0, 10.0);
let outside = in_circumsphere(&a, &b, &c, &d, &far);
assert!(outside < 0.0);
}
#[test]
fn test_circumcenter() {
// Regular tetrahedron centered at origin
let a = Point3::new(1.0, 1.0, 1.0);
let b = Point3::new(-1.0, -1.0, 1.0);
let c = Point3::new(-1.0, 1.0, -1.0);
let d = Point3::new(1.0, -1.0, -1.0);
let cc = circumcenter(&a, &b, &c, &d).unwrap();
// Center should be near origin
assert!(cc.coords.norm() < 0.01);
// All vertices should be equidistant from circumcenter
let ra = (a - cc).norm();
let rb = (b - cc).norm();
let rc = (c - cc).norm();
let rd = (d - cc).norm();
assert!((ra - rb).abs() < 1e-10);
assert!((ra - rc).abs() < 1e-10);
assert!((ra - rd).abs() < 1e-10);
}
}