#[allow(unused_imports)]
use super::functions::*;
#[cfg(test)]
mod tests_extended {
use super::*;
use crate::csg::CsgSmoothUnion;
use crate::csg::CsgTree;
use crate::csg::MarchingCell;
use crate::csg::SdfBox;
use crate::csg::SdfCappedCylinder;
use crate::csg::SdfCapsule;
use crate::csg::SdfCone;
use crate::csg::SdfRoundedBox;
use crate::csg::SdfSphere;
#[test]
fn capped_cylinder_inside_is_negative() {
let c = SdfCappedCylinder::new([0.0, 0.0, 0.0], 1.0, 2.0);
assert!(c.sdf([0.0, 0.0, 0.0]) < 0.0);
}
#[test]
fn capped_cylinder_outside_is_positive() {
let c = SdfCappedCylinder::new([0.0, 0.0, 0.0], 1.0, 2.0);
assert!(c.sdf([3.0, 0.0, 0.0]) > 0.0);
assert!(c.sdf([0.0, 5.0, 0.0]) > 0.0);
}
#[test]
fn capped_cylinder_surface_near_zero() {
let c = SdfCappedCylinder::new([0.0, 0.0, 0.0], 1.0, 2.0);
assert!(c.sdf([1.0, 0.0, 0.0]).abs() < 1e-10);
assert!(c.sdf([0.0, 2.0, 0.0]).abs() < 1e-10);
}
#[test]
fn capsule_inside_is_negative() {
let c = SdfCapsule::new([0.0, 0.0, 0.0], 1.0, 2.0);
assert!(c.sdf([0.0, 0.0, 0.0]) < 0.0);
}
#[test]
fn capsule_on_surface_near_zero() {
let c = SdfCapsule::new([0.0, 0.0, 0.0], 1.0, 1.0);
assert!(c.sdf([1.0, 0.0, 0.0]).abs() < 1e-10);
}
#[test]
fn capsule_outside_is_positive() {
let c = SdfCapsule::new([0.0, 0.0, 0.0], 0.5, 1.0);
assert!(c.sdf([5.0, 0.0, 0.0]) > 0.0);
}
#[test]
fn rounded_box_inside_is_negative() {
let b = SdfRoundedBox::new([0.0, 0.0, 0.0], [1.0, 1.0, 1.0], 0.1);
assert!(b.sdf([0.0, 0.0, 0.0]) < 0.0);
}
#[test]
fn rounded_box_outside_is_positive() {
let b = SdfRoundedBox::new([0.0, 0.0, 0.0], [1.0, 1.0, 1.0], 0.1);
assert!(b.sdf([5.0, 0.0, 0.0]) > 0.0);
}
#[test]
fn rounded_box_smaller_than_sharp_box() {
let sharp = SdfBox::new([0.0, 0.0, 0.0], [1.0, 1.0, 1.0]);
let rounded = SdfRoundedBox::new([0.0, 0.0, 0.0], [1.0, 1.0, 1.0], 0.2);
let p = [0.0, 0.0, 0.0];
assert!(
rounded.sdf(p) < sharp.sdf(p) + 1e-9,
"rounded box sdf ({}) should be <= sharp box sdf ({}) at center",
rounded.sdf(p),
sharp.sdf(p)
);
}
fn sphere_bounds_fn(s: &dyn ImplicitSurface) -> ([f64; 3], [f64; 3]) {
let _ = s;
([-2.0; 3], [2.0; 3])
}
#[test]
fn csg_tree_aabb_union_expands() {
let a = CsgTree::leaf(SdfSphere::new([-3.0, 0.0, 0.0], 1.0));
let b = CsgTree::leaf(SdfSphere::new([3.0, 0.0, 0.0], 1.0));
let u = CsgTree::union(a, b);
let (mn, mx) = csg_tree_aabb(&u, &sphere_bounds_fn);
for k in 0..3 {
assert!((mn[k] + 2.0).abs() < 1e-9, "mn[{k}]={}", mn[k]);
assert!((mx[k] - 2.0).abs() < 1e-9, "mx[{k}]={}", mx[k]);
}
}
#[test]
fn csg_tree_aabb_leaf_returns_bounds() {
let tree = CsgTree::leaf(SdfSphere::new([0.0, 0.0, 0.0], 1.0));
let (mn, mx) = csg_tree_aabb(&tree, &sphere_bounds_fn);
assert_eq!(mn, [-2.0; 3]);
assert_eq!(mx, [2.0; 3]);
}
#[test]
fn csg_tree_aabb_difference_uses_left() {
let a = CsgTree::leaf(SdfSphere::new([0.0, 0.0, 0.0], 3.0));
let b = CsgTree::leaf(SdfSphere::new([0.0, 0.0, 0.0], 1.5));
let d = CsgTree::difference(a, b);
let (mn, mx) = csg_tree_aabb(&d, &sphere_bounds_fn);
assert_eq!(mn, [-2.0; 3]);
assert_eq!(mx, [2.0; 3]);
}
#[test]
fn marching_cell_has_surface_mixed_signs() {
let mut vals = [-1.0f64; 8];
vals[7] = 1.0;
let cell = MarchingCell {
min: [0.0; 3],
step: 1.0,
corner_values: vals,
};
assert!(cell.has_surface());
}
#[test]
fn marching_cell_no_surface_all_negative() {
let cell = MarchingCell {
min: [0.0; 3],
step: 1.0,
corner_values: [-1.0; 8],
};
assert!(!cell.has_surface());
}
#[test]
fn marching_cell_no_surface_all_positive() {
let cell = MarchingCell {
min: [0.0; 3],
step: 1.0,
corner_values: [1.0; 8],
};
assert!(!cell.has_surface());
}
#[test]
fn marching_cell_extract_vertices_nonempty_on_crossing() {
let mut vals = [-1.0f64; 8];
vals[0] = 1.0;
let cell = MarchingCell {
min: [0.0; 3],
step: 1.0,
corner_values: vals,
};
let verts = cell.extract_vertices();
assert!(!verts.is_empty(), "should have crossing vertices");
}
#[test]
fn marching_cubes_sample_sphere_cells_nonempty() {
let s = SdfSphere::new([0.0, 0.0, 0.0], 1.0);
let cells = marching_cubes_sample(&s, [-1.5, -1.5, -1.5], [1.5, 1.5, 1.5], 8);
assert!(!cells.is_empty(), "should find surface cells for sphere");
}
#[test]
fn marching_cubes_vertices_nonempty_for_sphere() {
let s = SdfSphere::new([0.0, 0.0, 0.0], 1.0);
let verts = marching_cubes_vertices(&s, [-1.5, -1.5, -1.5], [1.5, 1.5, 1.5], 8);
assert!(!verts.is_empty(), "should find vertices on sphere surface");
}
#[test]
fn marching_cubes_all_inside_gives_no_surface() {
let s = SdfSphere::new([0.0, 0.0, 0.0], 100.0);
let cells = marching_cubes_sample(&s, [-1.0, -1.0, -1.0], [1.0, 1.0, 1.0], 4);
assert!(
cells.is_empty(),
"all-inside grid should have no surface cells"
);
}
#[test]
fn cone_inside_is_negative() {
let c = SdfCone::new([0.0, 0.0, 0.0], std::f64::consts::PI / 4.0, 2.0);
let d = c.sdf([0.0, 1.0, 0.0]);
assert!(
d < 0.0,
"point on axis inside cone should be negative, got {d}"
);
}
#[test]
fn cone_above_base_is_positive() {
let c = SdfCone::new([0.0, 0.0, 0.0], std::f64::consts::PI / 4.0, 1.0);
let d = c.sdf([0.0, 5.0, 0.0]);
assert!(d > 0.0, "point above cone base should be positive, got {d}");
}
#[test]
fn csg_union_sphere_and_cylinder() {
let s = CsgTree::leaf(SdfSphere::new([0.0, 0.0, 0.0], 1.0));
let c = CsgTree::leaf(SdfCappedCylinder::new([0.0, 0.0, 0.0], 0.5, 3.0));
let u = CsgTree::union(s, c);
assert!(
csg_sdf(&u, [0.0, 2.5, 0.0]) < 0.0,
"inside cylinder should be inside union"
);
assert!(
csg_sdf(&u, [5.0, 0.0, 0.0]) > 0.0,
"outside both should be outside union"
);
}
#[test]
fn csg_intersection_sphere_and_box() {
let s = CsgTree::leaf(SdfSphere::new([0.0, 0.0, 0.0], 2.0));
let b = CsgTree::leaf(SdfBox::new([0.0, 0.0, 0.0], [1.0, 1.0, 1.0]));
let i = CsgTree::intersection(s, b);
assert!(
csg_sdf(&i, [0.0, 0.0, 0.0]) < 0.0,
"center is in intersection"
);
assert!(
csg_sdf(&i, [1.5, 0.0, 0.0]) > 0.0,
"outside box is outside intersection"
);
}
#[test]
fn smooth_union_vs_sharp_at_midpoint() {
let a = Box::new(SdfSphere::new([-1.0, 0.0, 0.0], 1.0));
let b = Box::new(SdfSphere::new([1.0, 0.0, 0.0], 1.0));
let su = CsgSmoothUnion::new(a, b, 0.5);
let d = su.sdf([0.0, 0.0, 0.0]);
assert!(
d <= 0.5,
"smooth union at midpoint should be blended: d={d}"
);
}
#[test]
fn csg_surface_area_sphere_positive() {
let s = CsgTree::leaf(SdfSphere::new([0.0, 0.0, 0.0], 1.0));
let area = csg_surface_area(&s, [-1.5, -1.5, -1.5], [1.5, 1.5, 1.5], 10);
assert!(area > 0.0, "sphere surface area should be > 0, got {area}");
}
#[test]
fn csg_surface_area_increases_with_resolution() {
let s = CsgTree::leaf(SdfSphere::new([0.0, 0.0, 0.0], 1.0));
let a_low = csg_surface_area(&s, [-1.5, -1.5, -1.5], [1.5, 1.5, 1.5], 8);
let a_high = csg_surface_area(&s, [-1.5, -1.5, -1.5], [1.5, 1.5, 1.5], 16);
assert!(
a_low > 0.0 && a_high > 0.0,
"areas should be positive: low={a_low} high={a_high}"
);
}
#[test]
fn csg_node_surface_area_sphere_approx() {
let s = CsgTree::leaf(SdfSphere::new([0.0, 0.0, 0.0], 1.0));
let area = csg_node_surface_area(&s, [-1.5, -1.5, -1.5], [1.5, 1.5, 1.5]);
assert!(
area > 5.0 && area < 30.0,
"sphere SA estimate should be in [5,30], got {area}"
);
}
#[test]
fn csg_offset_sdf_outward_expands_shape() {
let s = CsgTree::leaf(SdfSphere::new([0.0, 0.0, 0.0], 1.0));
let p = [1.3, 0.0, 0.0];
let d_original = csg_sdf(&s, p);
let d_offset = csg_offset_sdf(&s, p, 0.5);
assert!(d_original > 0.0, "original should be outside: {d_original}");
assert!(
d_offset < 0.0,
"offset=0.5 outward should include point: {d_offset}"
);
}
#[test]
fn csg_offset_sdf_inward_shrinks_shape() {
let s = CsgTree::leaf(SdfSphere::new([0.0, 0.0, 0.0], 1.0));
let p = [0.0, 0.0, 0.0];
let d_offset = csg_offset_sdf(&s, p, -0.5);
assert!(
d_offset < 0.0,
"inward offset should keep origin inside: {d_offset}"
);
}
#[test]
fn csg_offset_contains_outward() {
let s = CsgTree::leaf(SdfSphere::new([0.0, 0.0, 0.0], 1.0));
let p = [1.1, 0.0, 0.0];
assert!(
csg_offset_contains(&s, p, 0.2),
"point at distance 0.1 should be inside offset 0.2 surface"
);
}
#[test]
fn csg_simplify_leaf_always_true() {
let s = CsgTree::leaf(SdfSphere::new([0.0, 0.0, 0.0], 1.0));
assert!(
csg_tree_simplify_check(&s, [0.0, 0.0, 0.0]),
"leaf node is always simplified"
);
}
#[test]
fn csg_simplify_union_inside_one_branch() {
let a = CsgTree::leaf(SdfSphere::new([0.0, 0.0, 0.0], 2.0));
let b = CsgTree::leaf(SdfSphere::new([10.0, 0.0, 0.0], 1.0));
let u = CsgTree::union(a, b);
assert!(
csg_tree_simplify_check(&u, [0.0, 0.0, 0.0]),
"union: probe inside left branch → simplified"
);
}
#[test]
fn csg_offset_gradient_is_unit_length() {
let s = CsgTree::leaf(SdfSphere::new([0.0, 0.0, 0.0], 1.0));
let p = [1.0, 0.0, 0.0];
let g = csg_offset_gradient(&s, p);
let len = (g[0].powi(2) + g[1].powi(2) + g[2].powi(2)).sqrt();
assert!(
(len - 1.0).abs() < 1e-3,
"offset gradient should be unit length, got {len}"
);
}
}