Thanks @gvocale.
This commit is contained in:
1 parent
5c0351b1f4
commit
48322f6464
7 files changed
+753
-12
No files matched your search
@@ -96,3 +96,61 @@ fn a_disc_joined_into_the_hole_it_fills() {
|
||||
assert!(used(&how, BooleanStrategy::GrownBoss) && !used(&how, BooleanStrategy::ToolNudge), "{z0}..{z1}: {how:?}");
|
||||
}
|
||||
}
|
||||
|
||||
/// A hemisphere of radius `r` standing on the plane z = 0.
|
||||
fn hemisphere(r: f64) -> Body {
|
||||
let ball = sphere(Vec3::ZERO, r).unwrap();
|
||||
let below = box_solid(Vec3::new(-2.0 * r, -2.0 * r, -2.0 * r), Vec3::new(2.0 * r, 2.0 * r, 0.0)).unwrap();
|
||||
boolean(&ball, &below, BoolOp::Cut).unwrap().unwrap()
|
||||
}
|
||||
|
||||
/// A hemisphere (r 20) and a block (x 5 to 35, y -30 to 30, z 0 to 25) that both stand on z = 0, the block's foot over most of the
|
||||
/// hemisphere's flat face. The neighbour of that face is the sphere, which cannot be stretched, so its face is grown instead (a
|
||||
/// strip along its rim) and what the join leaves below the plane is cut off by editing faces. The block takes the part of the dome
|
||||
/// beyond x = 5, half of a cap 15 high: the union is the dome and the block less that part.
|
||||
#[test]
|
||||
fn a_block_flush_with_the_flat_face_of_a_hemisphere_joins_exactly() {
|
||||
let (r, x0) = (20.0, 5.0);
|
||||
let block = box_solid(Vec3::new(x0, -30.0, 0.0), Vec3::new(35.0, 30.0, 25.0)).unwrap();
|
||||
let dome = hemisphere(r);
|
||||
let h = r - x0;
|
||||
let expected = 2.0 / 3.0 * std::f64::consts::PI * r.powi(3) - std::f64::consts::PI * h * h * (3.0 * r - h) / 6.0 + 30.0 * 60.0 * 25.0;
|
||||
for (a, b) in [(&dome, &block), (&block, &dome)] {
|
||||
let joined = crate::coplanar::boolean_grown(a, b, BoolOp::Union).expect("the route applies").unwrap().unwrap();
|
||||
let v = measure(&joined).unwrap().volume;
|
||||
assert!((v - expected).abs() < expected * 1e-4, "{v} against {expected}");
|
||||
assert_eq!(joined.lumps().unwrap().len(), 1);
|
||||
assert!(joined.validity().is_empty(), "{:?}", joined.validity());
|
||||
// The flat faces are one plane again: nothing of the join stays below z = 0.
|
||||
let mesh = joined.tessellate(0.05).unwrap();
|
||||
let lowest = mesh.positions.iter().map(|p| p.z).fold(f64::MAX, f64::min);
|
||||
assert!(lowest > -1e-6, "the join reaches down to z = {lowest}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Growing the flat face of a dome past its plane and cutting what that adds off again gives the dome back: the same volume, the same
|
||||
/// faces, nothing below the plane.
|
||||
#[test]
|
||||
fn what_growing_a_face_adds_is_cut_off_again_exactly() {
|
||||
let dome = hemisphere(20.0);
|
||||
let measured = measure(&dome).unwrap().volume;
|
||||
let grown = crate::coplanar::grow_faces_exact(&dome, -Vec3::Z, 0.0, 3.0).unwrap();
|
||||
assert!(measure(&grown).unwrap().volume > measured + 3.0 * 300.0, "the stub adds volume");
|
||||
let back = crate::coplanar::trim_exact(&grown, -Vec3::Z, 0.0).unwrap();
|
||||
let v = measure(&back).unwrap().volume;
|
||||
assert!((v - measured).abs() < measured * 1e-6, "{v} against {measured}");
|
||||
assert_eq!(back.face_count(), dome.face_count());
|
||||
assert!(back.validity().is_empty(), "{:?}", back.validity());
|
||||
let lowest = back.tessellate(0.05).unwrap().positions.iter().map(|p| p.z).fold(f64::MAX, f64::min);
|
||||
assert!(lowest > -1e-6, "the body reaches down to z = {lowest}");
|
||||
}
|
||||
|
||||
/// A plane the body stands on has nothing beyond it, and one clear of the body is refused (nothing is cut off).
|
||||
#[test]
|
||||
fn trimming_at_a_plane_with_nothing_beyond_it() {
|
||||
let dome = hemisphere(20.0);
|
||||
let measured = measure(&dome).unwrap().volume;
|
||||
let same = crate::coplanar::trim_exact(&dome, -Vec3::Z, 0.0).unwrap();
|
||||
assert!((measure(&same).unwrap().volume - measured).abs() < measured * 1e-9);
|
||||
assert!(crate::coplanar::trim_exact(&dome, -Vec3::Z, 5.0).is_err());
|
||||
}
|
||||
Reference in new issue
Block a user