use robust::{Coord, Coord3D};
#[inline]
pub fn orient2d(a: [f64; 2], b: [f64; 2], c: [f64; 2]) -> f64 {
robust::orient2d(
Coord { x: a[0], y: a[1] },
Coord { x: b[0], y: b[1] },
Coord { x: c[0], y: c[1] },
)
}
#[inline]
pub fn orient3d(a: [f64; 3], b: [f64; 3], c: [f64; 3], d: [f64; 3]) -> f64 {
robust::orient3d(
Coord3D {
x: a[0],
y: a[1],
z: a[2],
},
Coord3D {
x: b[0],
y: b[1],
z: b[2],
},
Coord3D {
x: c[0],
y: c[1],
z: c[2],
},
Coord3D {
x: d[0],
y: d[1],
z: d[2],
},
)
}
#[inline]
pub fn incircle(a: [f64; 2], b: [f64; 2], c: [f64; 2], d: [f64; 2]) -> f64 {
robust::incircle(
Coord { x: a[0], y: a[1] },
Coord { x: b[0], y: b[1] },
Coord { x: c[0], y: c[1] },
Coord { x: d[0], y: d[1] },
)
}
#[inline]
pub fn insphere(a: [f64; 3], b: [f64; 3], c: [f64; 3], d: [f64; 3], e: [f64; 3]) -> f64 {
robust::insphere(
Coord3D {
x: a[0],
y: a[1],
z: a[2],
},
Coord3D {
x: b[0],
y: b[1],
z: b[2],
},
Coord3D {
x: c[0],
y: c[1],
z: c[2],
},
Coord3D {
x: d[0],
y: d[1],
z: d[2],
},
Coord3D {
x: e[0],
y: e[1],
z: e[2],
},
)
}
#[inline]
pub fn signed_area2d(a: [f64; 2], b: [f64; 2], c: [f64; 2]) -> f64 {
0.5 * orient2d(a, b, c)
}
#[inline]
pub fn triangle_area_2d(a: [f64; 2], b: [f64; 2], c: [f64; 2]) -> f64 {
signed_area2d(a, b, c).abs()
}
#[inline]
pub fn is_ccw2d(a: [f64; 2], b: [f64; 2], c: [f64; 2]) -> bool {
orient2d(a, b, c) > 0.0
}
#[inline]
pub fn is_collinear2d(a: [f64; 2], b: [f64; 2], c: [f64; 2]) -> bool {
orient2d(a, b, c) == 0.0
}
#[inline]
pub fn is_convex_vertex2d(a: [f64; 2], b: [f64; 2], c: [f64; 2]) -> bool {
orient2d(a, b, c) > 0.0
}
pub fn point_in_triangle_2d(p: [f64; 2], a: [f64; 2], b: [f64; 2], c: [f64; 2]) -> bool {
let o1 = orient2d(a, b, p);
let o2 = orient2d(b, c, p);
let o3 = orient2d(c, a, p);
let tri_sign = orient2d(a, b, c);
if tri_sign == 0.0 {
return false;
}
if tri_sign > 0.0 {
o1 >= 0.0 && o2 >= 0.0 && o3 >= 0.0
} else {
o1 <= 0.0 && o2 <= 0.0 && o3 <= 0.0
}
}
#[inline]
pub fn tet_signed_volume(a: [f64; 3], b: [f64; 3], c: [f64; 3], d: [f64; 3]) -> f64 {
-orient3d(a, b, c, d) / 6.0
}
#[inline]
pub fn is_coplanar(a: [f64; 3], b: [f64; 3], c: [f64; 3], d: [f64; 3]) -> bool {
orient3d(a, b, c, d) == 0.0
}
pub fn is_triangle_degenerate_3d(a: [f64; 3], b: [f64; 3], c: [f64; 3]) -> bool {
let ab = [b[0] - a[0], b[1] - a[1], b[2] - a[2]];
let ac = [c[0] - a[0], c[1] - a[1], c[2] - a[2]];
let nx = (ab[1] * ac[2] - ab[2] * ac[1]).abs();
let ny = (ab[2] * ac[0] - ab[0] * ac[2]).abs();
let nz = (ab[0] * ac[1] - ab[1] * ac[0]).abs();
if nx >= ny && nx >= nz {
orient2d([a[1], a[2]], [b[1], b[2]], [c[1], c[2]]) == 0.0
} else if ny >= nz {
orient2d([a[0], a[2]], [b[0], b[2]], [c[0], c[2]]) == 0.0
} else {
orient2d([a[0], a[1]], [b[0], b[1]], [c[0], c[1]]) == 0.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn orient2d_ccw() {
let a = [0.0, 0.0];
let b = [1.0, 0.0];
let c = [0.0, 1.0];
assert!(orient2d(a, b, c) > 0.0);
assert!(is_ccw2d(a, b, c));
}
#[test]
fn orient2d_cw() {
let a = [0.0, 0.0];
let b = [0.0, 1.0];
let c = [1.0, 0.0];
assert!(orient2d(a, b, c) < 0.0);
assert!(!is_ccw2d(a, b, c));
}
#[test]
fn orient2d_collinear_exact() {
let a = [0.0, 0.0];
let b = [1.0, 0.0];
let c = [2.0, 0.0];
assert_eq!(orient2d(a, b, c), 0.0);
assert!(is_collinear2d(a, b, c));
}
#[test]
fn orient2d_collinear_near() {
let a = [1e20, 1.0];
let b = [1e20 + 1.0, 1.0];
let c = [1e20 + 2.0, 1.0];
assert_eq!(orient2d(a, b, c), 0.0);
}
#[test]
fn signed_area_2d_unit_triangle() {
let a = [0.0, 0.0];
let b = [1.0, 0.0];
let c = [0.0, 1.0];
let area = signed_area2d(a, b, c);
assert!((area - 0.5).abs() < 1e-15);
assert!((triangle_area_2d(a, b, c) - 0.5).abs() < 1e-15);
}
#[test]
fn point_in_triangle_interior() {
let a = [0.0, 0.0];
let b = [1.0, 0.0];
let c = [0.0, 1.0];
let p = [0.25, 0.25];
assert!(point_in_triangle_2d(p, a, b, c));
}
#[test]
fn point_in_triangle_vertex() {
let a = [0.0, 0.0];
let b = [1.0, 0.0];
let c = [0.0, 1.0];
assert!(point_in_triangle_2d(a, a, b, c));
assert!(point_in_triangle_2d(b, a, b, c));
assert!(point_in_triangle_2d(c, a, b, c));
}
#[test]
fn point_in_triangle_edge() {
let a = [0.0, 0.0];
let b = [1.0, 0.0];
let c = [0.0, 1.0];
let p = [0.5, 0.0]; assert!(point_in_triangle_2d(p, a, b, c));
}
#[test]
fn point_outside_triangle() {
let a = [0.0, 0.0];
let b = [1.0, 0.0];
let c = [0.0, 1.0];
assert!(!point_in_triangle_2d([0.6, 0.6], a, b, c));
assert!(!point_in_triangle_2d([-0.1, 0.5], a, b, c));
assert!(!point_in_triangle_2d([0.5, -0.1], a, b, c));
}
#[test]
fn point_in_triangle_cw_orientation() {
let a = [0.0, 0.0];
let b = [1.0, 0.0];
let c = [0.0, 1.0];
let p = [0.25, 0.25];
assert!(point_in_triangle_2d(p, a, c, b));
}
#[test]
fn point_in_triangle_degenerate() {
let a = [0.0, 0.0];
let b = [1.0, 0.0];
let c = [2.0, 0.0];
let p = [0.5, 0.0];
assert!(!point_in_triangle_2d(p, a, b, c));
}
#[test]
fn orient3d_basic() {
let a = [0.0, 0.0, 0.0];
let b = [1.0, 0.0, 0.0];
let c = [0.0, 1.0, 0.0];
let d_below = [0.0, 0.0, -1.0];
assert!(orient3d(a, b, c, d_below) > 0.0);
let d_above = [0.0, 0.0, 1.0];
assert!(orient3d(a, b, c, d_above) < 0.0);
let d_on = [0.5, 0.5, 0.0];
assert_eq!(orient3d(a, b, c, d_on), 0.0);
}
#[test]
fn tet_signed_volume_unit() {
let a = [0.0, 0.0, 0.0];
let b = [1.0, 0.0, 0.0];
let c = [0.0, 1.0, 0.0];
let d = [0.0, 0.0, 1.0];
let v = tet_signed_volume(a, b, c, d);
assert!((v - 1.0 / 6.0).abs() < 1e-15);
}
#[test]
fn orient3d_coplanar() {
let a = [0.0, 0.0, 0.0];
let b = [1.0, 0.0, 0.0];
let c = [0.0, 1.0, 0.0];
let d = [1.0, 1.0, 0.0]; assert!(is_coplanar(a, b, c, d));
}
#[test]
fn incircle_inside() {
let a = [0.0, 0.0];
let b = [1.0, 0.0];
let c = [0.0, 1.0];
let d = [0.25, 0.25]; assert!(incircle(a, b, c, d) > 0.0);
}
#[test]
fn incircle_outside() {
let a = [0.0, 0.0];
let b = [1.0, 0.0];
let c = [0.0, 1.0];
let d = [2.0, 2.0]; assert!(incircle(a, b, c, d) < 0.0);
}
#[test]
fn incircle_on_circle() {
let a = [1.0, 0.0];
let b = [0.0, 1.0];
let c = [-1.0, 0.0];
let d = [0.0, -1.0]; assert_eq!(incircle(a, b, c, d), 0.0);
}
#[test]
fn insphere_inside() {
let a = [0.0, 0.0, 0.0];
let b = [0.0, 1.0, 0.0];
let c = [1.0, 0.0, 0.0];
let d = [0.0, 0.0, 1.0]; assert!(orient3d(a, b, c, d) > 0.0);
let e = [0.1, 0.1, 0.1]; assert!(insphere(a, b, c, d, e) > 0.0);
}
#[test]
fn insphere_outside() {
let a = [0.0, 0.0, 0.0];
let b = [0.0, 1.0, 0.0];
let c = [1.0, 0.0, 0.0];
let d = [0.0, 0.0, 1.0];
assert!(orient3d(a, b, c, d) > 0.0);
let e = [2.0, 2.0, 2.0]; assert!(insphere(a, b, c, d, e) < 0.0);
}
#[test]
fn convex_vertex_ccw() {
let a = [0.0, 0.0];
let b = [1.0, 0.0];
let c = [0.0, 1.0];
assert!(is_convex_vertex2d(a, b, c));
let a2 = [0.0, 0.0];
let b2 = [0.0, 1.0];
let c2 = [1.0, 0.0];
assert!(!is_convex_vertex2d(a2, b2, c2));
}
#[test]
fn triangle_degenerate_3d_normal() {
let a = [0.0, 0.0, 0.0];
let b = [1.0, 0.0, 0.0];
let c = [0.0, 1.0, 0.0];
assert!(!is_triangle_degenerate_3d(a, b, c));
}
#[test]
fn triangle_degenerate_3d_collinear() {
let a = [0.0, 0.0, 0.0];
let b = [1.0, 1.0, 1.0];
let c = [2.0, 2.0, 2.0];
assert!(is_triangle_degenerate_3d(a, b, c));
}
#[test]
fn triangle_degenerate_3d_coincident() {
let a = [1.0, 2.0, 3.0];
assert!(is_triangle_degenerate_3d(a, a, a));
}
#[test]
fn triangle_degenerate_3d_axis_aligned() {
let a = [0.0, 0.0, 5.0];
let b = [1.0, 0.0, 5.0];
let c = [0.0, 1.0, 5.0];
assert!(!is_triangle_degenerate_3d(a, b, c));
let a2 = [0.0, 0.0, 5.0];
let b2 = [1.0, 0.0, 5.0];
let c2 = [2.0, 0.0, 5.0];
assert!(is_triangle_degenerate_3d(a2, b2, c2));
}
}