Merge pull request #124 from CypherNaught-0x/fix/shell-four-face-corner
Shell works where four or more flat faces meet at a corner
This commit is contained in:
3 files changed
+325
-31
No files matched your search
@@ -1697,3 +1697,104 @@ fn orientation_mesh_reuses_missing_face_check_at_matching_tolerance() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A trough whose floor falls about 1° toward a central drain, the fall starting right at the
|
||||
/// trough edges: four faces meet at each bottom corner (two walls, two fall facets) and at the
|
||||
/// drain (four facets). A tab on one end makes the body non-convex. The moved planes of four
|
||||
/// faces don't meet at one point, so each such corner of the cavity becomes two corners joined
|
||||
/// by a short edge. Checked against the volume integrated by hand.
|
||||
#[test]
|
||||
fn shell_where_four_faces_meet_at_a_corner() {
|
||||
let (w, l, ht, t) = (100.0, 60.0, 40.0, 2.0);
|
||||
// Drain depth: the long facets fall 1°.
|
||||
let h = 30.0 * 1f64.to_radians().tan();
|
||||
let facet = |n: Vec3, p: Vec3| {
|
||||
let n = n.normalized().unwrap();
|
||||
HalfSpace { n, d: n.dot(p) }
|
||||
};
|
||||
let facets = [
|
||||
facet(Vec3::new(0.0, -h, -30.0), Vec3::ZERO),
|
||||
facet(Vec3::new(0.0, h, -30.0), Vec3::new(0.0, l, 0.0)),
|
||||
facet(Vec3::new(-h, 0.0, -50.0), Vec3::ZERO),
|
||||
facet(Vec3::new(h, 0.0, -50.0), Vec3::new(w, 0.0, 0.0)),
|
||||
];
|
||||
let mut hs = vec![
|
||||
HalfSpace { n: -Vec3::X, d: 0.0 },
|
||||
HalfSpace { n: Vec3::X, d: w },
|
||||
HalfSpace { n: -Vec3::Y, d: 0.0 },
|
||||
HalfSpace { n: Vec3::Y, d: l },
|
||||
HalfSpace { n: Vec3::Z, d: ht },
|
||||
];
|
||||
hs.extend(facets);
|
||||
let trough = convex_polyhedron(&hs).unwrap();
|
||||
let pyramid = w * l * h / 3.0;
|
||||
assert!(rel(measure(&trough).unwrap().volume, w * l * ht + pyramid) < 1e-9);
|
||||
let tab = box_solid(Vec3::new(w, 20.0, 20.0), Vec3::new(w + 20.0, 40.0, ht)).unwrap();
|
||||
let body = boolean(&trough, &tab, BoolOp::Union).unwrap().unwrap();
|
||||
let s = shell(&body, &[Vec3::new(50.0, 30.0, ht)], t).unwrap();
|
||||
// The cavity: the walls in by t, above the floor facets moved in by t (midpoint rule; the
|
||||
// floor is linear between its creases).
|
||||
let floor = |x: f64, y: f64| facets.iter().map(|f| (f.d - t - f.n.x * x - f.n.y * y) / f.n.z).fold(f64::NEG_INFINITY, f64::max);
|
||||
let (nx, ny) = (960, 560);
|
||||
let (dx, dy) = ((w - 2.0 * t) / nx as f64, (l - 2.0 * t) / ny as f64);
|
||||
let mut cavity = 0.0;
|
||||
for i in 0..nx {
|
||||
for j in 0..ny {
|
||||
cavity += (ht - floor(t + (i as f64 + 0.5) * dx, t + (j as f64 + 0.5) * dy)) * dx * dy;
|
||||
}
|
||||
}
|
||||
let tab_cavity = 20.0 * (20.0 - 2.0 * t) * (20.0 - t);
|
||||
let want = w * l * ht + pyramid + 8000.0 - cavity - tab_cavity;
|
||||
let m = measure(&s).unwrap();
|
||||
assert!(rel(m.volume, want) < 1e-5, "{} vs {want}", m.volume);
|
||||
}
|
||||
|
||||
/// The same trough cut as a pocket into a slab (four faces meet at each concave corner of the
|
||||
/// pocket floor), shelled with its top open.
|
||||
#[test]
|
||||
fn shell_of_a_pocket_whose_floor_falls_to_a_drain() {
|
||||
let (px, py, pw, pl, top, depth, t) = (20.0, 15.0, 100.0, 60.0, 50.0, 30.0, 3.0);
|
||||
let h = 30.0 * 1f64.to_radians().tan();
|
||||
let z0 = top - depth;
|
||||
let facet = |n: Vec3, p: Vec3| {
|
||||
let n = n.normalized().unwrap();
|
||||
HalfSpace { n, d: n.dot(p) }
|
||||
};
|
||||
let facets = [
|
||||
facet(Vec3::new(0.0, -h, -30.0), Vec3::new(px, py, z0)),
|
||||
facet(Vec3::new(0.0, h, -30.0), Vec3::new(px, py + pl, z0)),
|
||||
facet(Vec3::new(-h, 0.0, -50.0), Vec3::new(px, py, z0)),
|
||||
facet(Vec3::new(h, 0.0, -50.0), Vec3::new(px + pw, py, z0)),
|
||||
];
|
||||
let mut hs = vec![
|
||||
HalfSpace { n: -Vec3::X, d: -px },
|
||||
HalfSpace { n: Vec3::X, d: px + pw },
|
||||
HalfSpace { n: -Vec3::Y, d: -py },
|
||||
HalfSpace { n: Vec3::Y, d: py + pl },
|
||||
HalfSpace { n: Vec3::Z, d: top + 10.0 },
|
||||
];
|
||||
hs.extend(facets);
|
||||
let pocket = convex_polyhedron(&hs).unwrap();
|
||||
let slab = box_solid(Vec3::ZERO, Vec3::new(140.0, 90.0, top)).unwrap();
|
||||
let basin = boolean(&slab, &pocket, BoolOp::Cut).unwrap().unwrap();
|
||||
let pocket_vol = pw * pl * depth + pw * pl * h / 3.0;
|
||||
assert!(rel(measure(&basin).unwrap().volume, 140.0 * 90.0 * top - pocket_vol) < 1e-9);
|
||||
let s = shell(&basin, &[Vec3::new(5.0, 5.0, top)], t).unwrap();
|
||||
// The cavity's columns: from t up to the top, or up to the pocket floor moved down by t
|
||||
// inside the pocket's walls moved out by t.
|
||||
let floor = |x: f64, y: f64| facets.iter().map(|f| (f.d + t - f.n.x * x - f.n.y * y) / f.n.z).fold(f64::NEG_INFINITY, f64::max);
|
||||
let (nx, ny) = (1340, 840);
|
||||
let (dx, dy) = ((140.0 - 2.0 * t) / nx as f64, (90.0 - 2.0 * t) / ny as f64);
|
||||
let mut cavity = 0.0;
|
||||
for i in 0..nx {
|
||||
for j in 0..ny {
|
||||
let (x, y) = (t + (i as f64 + 0.5) * dx, t + (j as f64 + 0.5) * dy);
|
||||
let inside = x > px - t && x < px + pw + t && y > py - t && y < py + pl + t;
|
||||
let roof = if inside { floor(x, y) } else { top };
|
||||
cavity += (roof - t) * dx * dy;
|
||||
}
|
||||
}
|
||||
let want = 140.0 * 90.0 * top - pocket_vol - cavity;
|
||||
let m = measure(&s).unwrap();
|
||||
assert!(rel(m.volume, want) < 1e-4, "{} vs {want}", m.volume);
|
||||
}
|
||||
Reference in new issue
Block a user