use crate::linalg::Vec3;
use super::super::exact::backend::rat_new;
use super::super::exact::rational::{rat, R3};
use super::{dominant_axis, lift_to_plane, tri_tri_intersect, TriTriIsect};
use crate::robust::exact::predicates::tri_normal_r;
fn v(x: f64, y: f64, z: f64) -> Vec3 {
Vec3::new(x, y, z)
}
fn r3(x: f64, y: f64, z: f64) -> R3 {
R3::from_vec3(v(x, y, z))
}
fn assert_same_isect(a: &TriTriIsect, b: &TriTriIsect) {
use TriTriIsect::*;
match (a, b) {
(None, None) => {}
(Point(p), Point(q)) => assert_eq!(p, q),
(Segment(p0, p1), Segment(q0, q1)) => {
assert!(
(p0 == q0 && p1 == q1) || (p0 == q1 && p1 == q0),
"segments differ: {p0:?}-{p1:?} vs {q0:?}-{q1:?}"
);
}
(
Coplanar {
polygon: pa,
same_orientation: sa,
},
Coplanar {
polygon: pb,
same_orientation: sb,
},
) => {
assert_eq!(sa, sb);
assert_eq!(pa.len(), pb.len(), "polygon vertex counts differ");
let mut sa: Vec<R3> = pa.clone();
let mut sb: Vec<R3> = pb.clone();
sa.sort();
sb.sort();
assert_eq!(sa, sb, "polygon vertex sets differ");
}
_ => panic!("intersection kinds differ: {a:?} vs {b:?}"),
}
}
fn isect_sym(t1: [Vec3; 3], t2: [Vec3; 3]) -> TriTriIsect {
let ab = tri_tri_intersect(t1, t2);
let ba = tri_tri_intersect(t2, t1);
assert_same_isect(&ab, &ba);
ab
}
#[test]
fn disjoint_triangles() {
let t1 = [v(0.0, 0.0, 0.0), v(1.0, 0.0, 0.0), v(0.0, 1.0, 0.0)];
let t2 = [v(0.0, 0.0, 1.0), v(1.0, 0.0, 1.0), v(0.0, 1.0, 1.0)];
assert_eq!(isect_sym(t1, t2), TriTriIsect::None);
let t3 = [v(10.0, 10.0, -1.0), v(11.0, 10.0, 1.0), v(10.0, 11.0, 1.0)];
assert_eq!(isect_sym(t1, t3), TriTriIsect::None);
let t4 = [v(5.0, 5.0, 0.0), v(6.0, 5.0, 0.0), v(5.0, 6.0, 0.0)];
assert_eq!(isect_sym(t1, t4), TriTriIsect::None);
}
#[test]
fn generic_crossing_produces_segment() {
let t1 = [v(0.0, 0.0, 0.0), v(4.0, 0.0, 0.0), v(0.0, 4.0, 0.0)];
let t2 = [v(1.0, -1.0, -1.0), v(1.0, 3.0, -1.0), v(1.0, 1.0, 2.0)];
match isect_sym(t1, t2) {
TriTriIsect::Segment(p, q) => {
assert_ne!(p, q);
for e in [&p, &q] {
assert_eq!(e.z, rat(0.0));
assert_eq!(e.x, rat(1.0));
}
}
other => panic!("expected Segment, got {other:?}"),
}
}
#[test]
fn known_crossing_segment_values() {
let t1 = [v(0.0, 0.0, 0.0), v(4.0, 0.0, 0.0), v(0.0, 4.0, 0.0)];
let t2 = [v(1.0, 0.0, -1.0), v(1.0, 2.0, -1.0), v(1.0, 1.0, 1.0)];
let expect0 = R3::new(rat(1.0), rat_new(1.into(), 2.into()), rat(0.0));
let expect1 = R3::new(rat(1.0), rat_new(3.into(), 2.into()), rat(0.0));
match isect_sym(t1, t2) {
TriTriIsect::Segment(p, q) => {
let (lo, hi) = if p.y < q.y { (p, q) } else { (q, p) };
assert_eq!(lo, expect0);
assert_eq!(hi, expect1);
}
other => panic!("expected Segment, got {other:?}"),
}
}
#[test]
fn vertex_touching_face_is_point() {
let t1 = [v(0.0, 0.0, 0.0), v(4.0, 0.0, 0.0), v(0.0, 4.0, 0.0)];
let t2 = [v(1.0, 1.0, 0.0), v(2.0, 1.0, 3.0), v(1.0, 2.0, 3.0)];
assert_eq!(isect_sym(t1, t2), TriTriIsect::Point(r3(1.0, 1.0, 0.0)));
}
#[test]
fn vertex_touching_vertex_is_point() {
let t1 = [v(0.0, 0.0, 0.0), v(4.0, 0.0, 0.0), v(0.0, 4.0, 0.0)];
let t2 = [v(0.0, 0.0, 0.0), v(-2.0, -1.0, 3.0), v(-1.0, -2.0, 3.0)];
assert_eq!(isect_sym(t1, t2), TriTriIsect::Point(r3(0.0, 0.0, 0.0)));
}
#[test]
fn edge_lying_in_plane_clipped_to_face() {
let t1 = [v(0.0, 0.0, 0.0), v(4.0, 0.0, 0.0), v(0.0, 4.0, 0.0)];
let t2 = [v(1.0, -1.0, 0.0), v(1.0, 5.0, 0.0), v(1.0, 1.0, 3.0)];
match isect_sym(t1, t2) {
TriTriIsect::Segment(p, q) => {
let (lo, hi) = if p.y < q.y { (p, q) } else { (q, p) };
assert_eq!(lo, r3(1.0, 0.0, 0.0));
assert_eq!(hi, r3(1.0, 3.0, 0.0));
}
other => panic!("expected Segment, got {other:?}"),
}
}
#[test]
fn shared_edge_between_meshes_is_segment() {
let t1 = [v(0.0, 0.0, 0.0), v(2.0, 0.0, 0.0), v(0.0, 0.0, 2.0)];
let t2 = [v(0.0, 0.0, 0.0), v(2.0, 0.0, 0.0), v(0.0, 3.0, 0.0)];
match isect_sym(t1, t2) {
TriTriIsect::Segment(p, q) => {
let (lo, hi) = if p.x < q.x { (p, q) } else { (q, p) };
assert_eq!(lo, r3(0.0, 0.0, 0.0));
assert_eq!(hi, r3(2.0, 0.0, 0.0));
}
other => panic!("expected Segment, got {other:?}"),
}
}
#[test]
fn coplanar_identical_triangles() {
let t1 = [v(0.0, 0.0, 1.0), v(4.0, 0.0, 1.0), v(0.0, 4.0, 1.0)];
match isect_sym(t1, t1) {
TriTriIsect::Coplanar {
polygon,
same_orientation,
} => {
assert!(same_orientation);
assert_eq!(polygon.len(), 3);
let mut got = polygon;
got.sort();
let mut want = vec![r3(0.0, 0.0, 1.0), r3(4.0, 0.0, 1.0), r3(0.0, 4.0, 1.0)];
want.sort();
assert_eq!(got, want);
}
other => panic!("expected Coplanar, got {other:?}"),
}
}
#[test]
fn coplanar_opposite_orientation_detected() {
let t1 = [v(0.0, 0.0, 1.0), v(4.0, 0.0, 1.0), v(0.0, 4.0, 1.0)];
let t2 = [v(0.0, 0.0, 1.0), v(0.0, 4.0, 1.0), v(4.0, 0.0, 1.0)]; match isect_sym(t1, t2) {
TriTriIsect::Coplanar {
same_orientation, ..
} => assert!(!same_orientation),
other => panic!("expected Coplanar, got {other:?}"),
}
}
#[test]
fn coplanar_partial_overlap_is_convex_polygon() {
let t1 = [v(0.0, 0.0, 0.0), v(4.0, 0.0, 0.0), v(0.0, 4.0, 0.0)];
let t2 = [v(1.0, 1.0, 0.0), v(5.0, 1.0, 0.0), v(1.0, 5.0, 0.0)];
match isect_sym(t1, t2) {
TriTriIsect::Coplanar {
polygon,
same_orientation,
} => {
assert!(same_orientation);
let mut got = polygon;
got.sort();
let mut want = vec![r3(1.0, 1.0, 0.0), r3(3.0, 1.0, 0.0), r3(1.0, 3.0, 0.0)];
want.sort();
assert_eq!(got, want);
}
other => panic!("expected Coplanar, got {other:?}"),
}
}
#[test]
fn coplanar_hexagonal_overlap() {
let t1 = [v(0.0, 0.0, 0.0), v(6.0, 0.0, 0.0), v(3.0, 6.0, 0.0)];
let t2 = [v(0.0, 4.0, 0.0), v(6.0, 4.0, 0.0), v(3.0, -2.0, 0.0)];
match isect_sym(t1, t2) {
TriTriIsect::Coplanar { polygon, .. } => assert_eq!(polygon.len(), 6),
other => panic!("expected Coplanar, got {other:?}"),
}
}
#[test]
fn coplanar_shared_edge_only_is_segment() {
let t1 = [v(0.0, 0.0, 0.0), v(4.0, 0.0, 0.0), v(0.0, 4.0, 0.0)];
let t2 = [v(0.0, 0.0, 0.0), v(4.0, 0.0, 0.0), v(2.0, -4.0, 0.0)];
match isect_sym(t1, t2) {
TriTriIsect::Segment(p, q) => {
let (lo, hi) = if p.x < q.x { (p, q) } else { (q, p) };
assert_eq!(lo, r3(0.0, 0.0, 0.0));
assert_eq!(hi, r3(4.0, 0.0, 0.0));
}
other => panic!("expected Segment, got {other:?}"),
}
}
#[test]
fn coplanar_vertex_touch_is_point() {
let t1 = [v(0.0, 0.0, 0.0), v(4.0, 0.0, 0.0), v(0.0, 4.0, 0.0)];
let t2 = [v(4.0, 0.0, 0.0), v(8.0, 0.0, 0.0), v(4.0, -4.0, 0.0)];
assert_eq!(isect_sym(t1, t2), TriTriIsect::Point(r3(4.0, 0.0, 0.0)));
}
#[test]
fn sliver_crossing_stays_exact() {
let eps = 2f64.powi(-40);
let t1 = [v(-10.0, -10.0, 0.0), v(10.0, -10.0, 0.0), v(0.0, 10.0, 0.0)];
let t2 = [
v(-5.0, 0.0, -eps),
v(5.0, 0.0, -eps),
v(0.0, eps, 2.0 * eps),
];
match isect_sym(t1, t2) {
TriTriIsect::Segment(p, q) => assert_ne!(p, q),
other => panic!("expected Segment, got {other:?}"),
}
}
#[test]
fn dominant_axis_and_lift_round_trip() {
let a = r3(0.1, 0.2, 0.3);
let b = r3(1.7, -0.4, 0.9);
let c = r3(-0.6, 1.1, 2.2);
let n = tri_normal_r(&a, &b, &c);
let axis = dominant_axis(&n);
for p in [&a, &b, &c] {
let lifted = lift_to_plane(&p.project_drop(axis), axis, &a, &n);
assert_eq!(&lifted, p);
}
}