147 lines
4.0 KiB
Rust
147 lines
4.0 KiB
Rust
use cordial::prelude::*;
|
|
|
|
fn assert_finite(val: f64, label: &str) {
|
|
assert!(!val.is_nan(), "{label}: got NaN");
|
|
assert!(!val.is_infinite(), "{label}: got infinity");
|
|
}
|
|
|
|
#[test]
|
|
fn sphere_through_pipeline() {
|
|
let s = sphere(2.0);
|
|
|
|
let val_surface = s.eval(2.0, 0.0, 0.0);
|
|
let val_inside = s.eval(0.0, 0.0, 0.0);
|
|
let val_outside = s.eval(5.0, 0.0, 0.0);
|
|
|
|
assert_finite(val_surface, "surface");
|
|
assert_finite(val_inside, "inside");
|
|
assert_finite(val_outside, "outside");
|
|
|
|
assert!(val_surface.abs() < 1e-6, "surface: expected ~0, got {val_surface}");
|
|
assert!(val_inside < 0.0, "inside: expected negative, got {val_inside}");
|
|
assert!(val_outside > 0.0, "outside: expected positive, got {val_outside}");
|
|
|
|
let graph = s.to_trig();
|
|
assert!(graph.nodes.len() > 0);
|
|
|
|
let wgsl = s.to_wgsl();
|
|
assert!(wgsl.contains("fn scene_sdf"));
|
|
|
|
let cordic = s.to_cordic();
|
|
assert!(!cordic.instructions.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn complex_csg_through_pipeline() {
|
|
let body = cube(2.0)
|
|
.difference(sphere(2.5))
|
|
.union(cylinder(0.5, 6.0).rotate_x(90.0))
|
|
.translate(1.0, 2.0, 3.0);
|
|
|
|
let test_points: &[(f64, f64, f64)] = &[
|
|
(0.0, 0.0, 0.0),
|
|
(1.0, 2.0, 3.0),
|
|
(5.0, 5.0, 5.0),
|
|
(-3.0, -3.0, -3.0),
|
|
];
|
|
|
|
for &(x, y, z) in test_points {
|
|
let val = body.eval(x, y, z);
|
|
assert_finite(val, &format!("csg at ({x},{y},{z})"));
|
|
}
|
|
|
|
let graph = body.to_trig();
|
|
assert!(graph.nodes.len() > 10);
|
|
|
|
let wgsl = body.to_wgsl();
|
|
assert!(wgsl.contains("fn scene_sdf"));
|
|
assert!(wgsl.contains("fn fs_main"));
|
|
|
|
let cordic = body.to_cordic();
|
|
assert!(!cordic.instructions.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn operator_overloads() {
|
|
let a = sphere(2.0);
|
|
let b = cube(1.5).translate(1.0, 0.0, 0.0);
|
|
|
|
let union_shape = a.clone() | b.clone();
|
|
let inter_shape = a.clone() & b.clone();
|
|
let diff_shape = a.clone() - b.clone();
|
|
|
|
for (label, shape) in [("union", union_shape), ("inter", inter_shape), ("diff", diff_shape)] {
|
|
let val = shape.eval(0.0, 0.0, 0.0);
|
|
assert_finite(val, label);
|
|
let _ = shape.to_wgsl();
|
|
let _ = shape.to_cordic();
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn smooth_union_pipeline() {
|
|
let a = sphere(2.0);
|
|
let b = sphere(2.0).translate(3.0, 0.0, 0.0);
|
|
let blended = a.smooth_union(b, 1.0);
|
|
|
|
let midpoint = blended.eval(1.5, 0.0, 0.0);
|
|
assert_finite(midpoint, "smooth_union midpoint");
|
|
assert!(midpoint < 0.0, "smooth_union should blend; midpoint={midpoint}");
|
|
|
|
let _ = blended.to_wgsl();
|
|
let _ = blended.to_cordic();
|
|
}
|
|
|
|
#[test]
|
|
fn variadic_booleans() {
|
|
let shapes = vec![
|
|
sphere(1.0),
|
|
sphere(1.0).translate(3.0, 0.0, 0.0),
|
|
sphere(1.0).translate(0.0, 3.0, 0.0),
|
|
];
|
|
|
|
let union_all = Shape::union_all(shapes.clone());
|
|
assert!(union_all.eval(0.0, 0.0, 0.0) < 0.0);
|
|
assert!(union_all.eval(3.0, 0.0, 0.0) < 0.0);
|
|
assert!(union_all.eval(0.0, 3.0, 0.0) < 0.0);
|
|
|
|
let inter_all = Shape::intersection_all(shapes.clone());
|
|
assert!(inter_all.eval(1.5, 1.5, 0.0) > 0.0);
|
|
|
|
let base = sphere(5.0);
|
|
let diff_all = base.difference_all(shapes);
|
|
let val = diff_all.eval(0.0, 0.0, 0.0);
|
|
assert_finite(val, "diff_all origin");
|
|
assert!(val > 0.0, "origin should be carved out");
|
|
}
|
|
|
|
#[test]
|
|
fn pattern_linear_array() {
|
|
let s = sphere(0.5);
|
|
let arr = pattern::linear_array(&s, 5, [2.0, 0.0, 0.0]);
|
|
|
|
assert!(arr.eval(0.0, 0.0, 0.0) < 0.0);
|
|
assert!(arr.eval(8.0, 0.0, 0.0) < 0.0);
|
|
assert!(arr.eval(1.0, 0.0, 0.0) > 0.0);
|
|
|
|
let _ = arr.to_wgsl();
|
|
let _ = arr.to_cordic();
|
|
}
|
|
|
|
#[test]
|
|
fn transforms_chain() {
|
|
let s = sphere(1.0)
|
|
.rotate_x(45.0)
|
|
.rotate_y(30.0)
|
|
.rotate_z(15.0)
|
|
.scale_uniform(2.0)
|
|
.translate(5.0, 5.0, 5.0);
|
|
|
|
let val = s.eval(5.0, 5.0, 5.0);
|
|
assert_finite(val, "center of transformed sphere");
|
|
assert!(val < 0.0, "center should be inside; got {val}");
|
|
|
|
let _ = s.to_wgsl();
|
|
let _ = s.to_cordic();
|
|
}
|