Cord/crates/cordial/tests/dsl_pipeline.rs

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();
}