use super::*;
fn box_cutter_mesh(half: [f64; 3], garbage: &[[f64; 3]]) -> Mesh {
let mut m = Mesh::new();
for sx in [-1.0, 1.0] {
for sy in [-1.0, 1.0] {
for sz in [-1.0, 1.0] {
m.positions.extend_from_slice(&[
(sx * half[0]) as f32,
(sy * half[1]) as f32,
(sz * half[2]) as f32,
]);
}
}
}
for g in garbage {
m.positions
.extend_from_slice(&[g[0] as f32, g[1] as f32, g[2] as f32]);
}
m
}
#[test]
fn opening_obb_detects_malformed_and_recovers_box() {
let cutter = box_cutter_mesh([1.0, 0.1, 1.2], &[[0.0, 9.0, 0.0], [0.0, -9.0, 0.0]]);
let b = opening_obb_if_malformed(&cutter).expect("malformed cutter -> box");
let mut half = b.half;
half.sort_by(|a, c| a.partial_cmp(c).unwrap());
assert!((half[0] - 0.1).abs() < 0.05, "thin half {:?}", b.half);
assert!((half[1] - 1.0).abs() < 0.05, "mid half {:?}", b.half);
assert!((half[2] - 1.2).abs() < 0.05, "long half {:?}", b.half);
}
#[test]
fn opening_obb_skips_wellformed_cutter() {
let cutter = box_cutter_mesh([1.0, 0.1, 1.2], &[]);
assert!(opening_obb_if_malformed(&cutter).is_none());
}
#[test]
fn watertight_tall_roof_cutter_is_not_flagged_malformed() {
let tall = aabb_box([0.6, 0.15, 450.0]);
assert!(
cutter_is_closed_manifold(&tall),
"a closed box prism must read as a valid solid"
);
assert!(
opening_obb_if_malformed(&tall).is_none(),
"a watertight tall cutter must never be reshaped as 'malformed'"
);
}
fn signed_volume(m: &Mesh) -> f64 {
let v = |i: u32| {
let b = i as usize * 3;
[m.positions[b] as f64, m.positions[b + 1] as f64, m.positions[b + 2] as f64]
};
m.indices
.chunks_exact(3)
.map(|t| {
let (a, b, c) = (v(t[0]), v(t[1]), v(t[2]));
(a[0] * (b[1] * c[2] - b[2] * c[1]) + a[1] * (b[2] * c[0] - b[0] * c[2])
+ a[2] * (b[0] * c[1] - b[1] * c[0]))
/ 6.0
})
.sum::<f64>()
.abs()
}
fn aabb_box(half: [f64; 3]) -> Mesh {
let axes = [Vector3::x(), Vector3::y(), Vector3::z()];
let bx = OpeningBox { center: Vector3::zeros(), axes, half };
bx.extended_box_mesh([0.0; 3], 0.0)
}
#[test]
fn recut_carves_opening_and_preserves_wall_around_it() {
let mut host = aabb_box([2.0, 0.15, 1.5]);
let host_vol = signed_volume(&host);
let bx = OpeningBox {
center: Vector3::zeros(),
axes: [Vector3::x(), Vector3::y(), Vector3::z()],
half: [0.5, 0.079, 0.5],
};
recut_malformed_openings(&mut host, std::slice::from_ref(&bx));
assert!(!host.is_empty(), "recut emptied the wall");
let (lo, hi) = host.bounds();
assert!((hi.z - 1.5).abs() < 0.02, "wall top removed (z max {})", hi.z);
assert!((lo.z + 1.5).abs() < 0.02, "wall bottom removed (z min {})", lo.z);
assert!((hi.x - 2.0).abs() < 0.02, "wall side removed (x max {})", hi.x);
let cut_vol = signed_volume(&host);
assert!(
cut_vol < host_vol - 0.2,
"opening not carved (host {host_vol:.3}, cut {cut_vol:.3})"
);
}
#[test]
fn world_host_bounds_folds_the_origin_back_in() {
let mut host = aabb_box([2.0, 0.15, 1.5]);
host.origin = [1000.0, 2000.0, 3000.0];
let ((mnx, mny, mnz), (mxx, mxy, mxz)) = world_host_bounds(&host);
assert!((mnx - 998.0).abs() < 0.01, "min.x {mnx}");
assert!((mxx - 1002.0).abs() < 0.01, "max.x {mxx}");
assert!((mny - 1999.85).abs() < 0.01, "min.y {mny}");
assert!((mxy - 2000.15).abs() < 0.01, "max.y {mxy}");
assert!((mnz - 2998.5).abs() < 0.01, "min.z {mnz}");
assert!((mxz - 3001.5).abs() < 0.01, "max.z {mxz}");
}
#[test]
fn nonzero_origin_host_records_world_space_bbox() {
let router = GeometryRouter::new();
let mut host = aabb_box([2.0, 0.15, 1.5]);
host.origin = [1000.0, 2000.0, 3000.0];
let o = host.origin;
let opening = OpeningType::Rectangular(
Point3::new(o[0] - 0.5, o[1] - 0.5, o[2] - 0.5),
Point3::new(o[0] + 0.5, o[1] + 0.5, o[2] + 0.5),
None,
);
let ctx = VoidContext {
merged_openings: vec![opening.clone()],
openings: vec![opening],
param: None,
bool2d: None,
};
let element_id = 4242u32;
let _ = router.apply_void_context(host, &ctx, element_id);
let diags = router.take_host_opening_diagnostics();
let hd = diags.get(&element_id).expect("host cut effect recorded");
let ((mnx, mny, mnz), (mxx, mxy, mxz)) =
hd.host_bounds.expect("host_bounds captured on the cut path");
assert!(mnx > 900.0 && mxx < 1100.0, "x not world: [{mnx}, {mxx}]");
assert!(mny > 1900.0 && mxy < 2100.0, "y not world: [{mny}, {mxy}]");
assert!(mnz > 2900.0 && mxz < 3100.0, "z not world: [{mnz}, {mxz}]");
}
fn box_mesh_at(cx: f64, half: [f64; 3]) -> Mesh {
OpeningBox {
center: Vector3::new(cx, 0.0, 0.0),
axes: [Vector3::x(), Vector3::y(), Vector3::z()],
half,
}
.extended_box_mesh([0.0; 3], 0.0)
}
#[test]
fn budget_tripped_engulfing_cutter_skips_aabb_fallback() {
use crate::kernel::budget;
let _cap_guard = budget::GLOBAL_CAP_LOCK.lock().unwrap();
struct RestoreBudget {
cap: Option<u64>,
ecap: Option<u64>,
}
impl Drop for RestoreBudget {
fn drop(&mut self) {
budget::set_element_cap(None);
budget::begin_element();
budget::set_cap(self.cap);
budget::set_element_cap(self.ecap);
}
}
let _restore = RestoreBudget {
cap: budget::cap(),
ecap: budget::element_cap(),
};
let router = GeometryRouter::new();
let host = aabb_box([1.0, 0.15, 1.0]);
let host_tris = host.triangle_count();
let dy = 1.3e-4;
let dz = 1.7e-4;
let cutter = {
let mut m = box_mesh_at(-0.8, [0.5, 0.15 + dy, 1.0 + dz]); m.merge(&box_mesh_at(0.8, [0.5, 0.15 + dy, 1.0 + dz])); m
};
assert!(
opening_obb_if_malformed(&cutter).is_none(),
"engulfing cutter must be well-formed"
);
let (cmn, cmx) = cutter.bounds();
let engulf_open = OpeningType::NonRectangular(
cutter,
Point3::new(cmn.x as f64, cmn.y as f64, cmn.z as f64),
Point3::new(cmx.x as f64, cmx.y as f64, cmx.z as f64),
None,
);
let engulf_ctx = VoidContext {
merged_openings: vec![engulf_open.clone()],
openings: vec![engulf_open],
param: None,
bool2d: None,
};
budget::set_element_cap(Some(1));
let engulf_id = 91_109_u32;
budget::begin_element(); let engulf_result = router.apply_void_context(host.clone(), &engulf_ctx, engulf_id);
assert!(
!engulf_result.is_empty(),
"engulfing cutter DELETED the host (box-cut fallback not suppressed)"
);
assert_eq!(
engulf_result.triangle_count(),
host_tris,
"host must be left exactly un-cut (got {} tris, host {host_tris})",
engulf_result.triangle_count(),
);
let failures = router.take_csg_failures();
let engulf_fails = failures
.get(&engulf_id)
.expect("the tripped engulfing cut must record a failure");
assert_eq!(
engulf_fails.len(),
1,
"exactly one failure for the single tripped cut (got {engulf_fails:?})"
);
assert!(
matches!(
engulf_fails[0].reason,
crate::diagnostics::BoolFailureReason::OperandTooLarge { .. }
),
"the tripped cut must record OperandTooLarge (got {:?})",
engulf_fails[0].reason,
);
let small = box_mesh_at(0.0, [0.15, 0.2, 0.15]); let small_open = OpeningType::NonRectangular(
small,
Point3::new(-0.15, -0.2, -0.15),
Point3::new(0.15, 0.2, 0.15),
None,
);
let small_ctx = VoidContext {
merged_openings: vec![small_open.clone()],
openings: vec![small_open],
param: None,
bool2d: None,
};
let small_id = 91_635_u32;
budget::begin_element();
let small_result = router.apply_void_context(host.clone(), &small_ctx, small_id);
assert!(
!small_result.is_empty(),
"a non-engulfing cut must keep the wall"
);
assert_ne!(
small_result.triangle_count(),
host_tris,
"a non-engulfing opening under the tripped cap must still cut the host (#635 box-cut)"
);
}
#[test]
fn opening_obb_if_malformed_bails_on_nan_vertex_not_panic() {
let cutter = box_cutter_mesh([1.0, 1.0, 1.0], &[[f64::NAN, 0.0, 0.0]]);
assert!(opening_obb_if_malformed(&cutter).is_none());
}