fix: a lip swept flush around a rounded rim joins the body it sits on (#1164)

fix: a lip swept flush around a rounded rim joins the body it sits on.
This commit is contained in:
Que authored and GitHub committed 2026-10-14 18:49:45 -04:00
1 parent 099d7e298a
commit 1cc65cbc91
10 files changed
+699 -5

No files matched your search

+56
View File
@@ -0,0 +1,56 @@
//! A lip swept along the rim of a round body joins it, flush with its side or set in from it:
//! the lip's foot lies on the body's top, and its sides are round where the rim is.
use std::f64::consts::{FRAC_PI_2, PI};
use solidcraft_geom::{Loop2, Plane, Region2, Vec2, Vec3};
use crate::*;
/// A 1 × 1 mm lip on the top (z = 10) of a round body of radius 10 about Z, its outer side
/// `inset` in from the body's side, swept a quarter turn from the XZ plane.
fn lip(inset: f64) -> Body {
let plane = Plane::new(Vec3::ZERO, Vec3::X, Vec3::Z).unwrap();
let (x0, x1) = (9.0 - inset, 10.0 - inset);
let outer = Loop2::polygon(&[Vec2::new(x0, 10.0), Vec2::new(x1, 10.0), Vec2::new(x1, 11.0), Vec2::new(x0, 11.0)]);
let path = [PathSeg::Arc { a: Vec3::new(10.0, 0.0, 10.0), center: Vec3::new(0.0, 0.0, 10.0), axis: Vec3::Z, angle: FRAC_PI_2 }];
sweep(&plane, &Region2 { outer, holes: vec![] }, &path).unwrap()
}
#[test]
fn a_lip_swept_on_a_round_rim_joins_it() {
let body = cylinder(Vec3::ZERO, Vec3::Z, 10.0, 10.0).unwrap();
for inset in [0.0, 0.5] {
let (r0, r1) = (9.0 - inset, 10.0 - inset);
// Measured as the result is (the round faces' volume from their meshes).
let mesh_volume = |b: &Body| b.tessellate(1e-5).unwrap().measure().volume;
let want = mesh_volume(&body) + mesh_volume(&lip(inset));
assert!((want - PI * 1000.0 - PI * (r1 * r1 - r0 * r0) / 4.0).abs() < 0.05);
for (a, b) in [(&body, &lip(inset)), (&lip(inset), &body)] {
let (r, d) = capture_boolean_diagnostics(|| boolean(a, b, BoolOp::Union));
let r = r.unwrap().unwrap();
assert_eq!(r.shell_count(), 1, "inset {inset}: one solid");
assert!(r.validity().is_empty(), "inset {inset}: {:?}", r.validity());
let v = mesh_volume(&r);
assert!((v - want).abs() < 1e-3, "inset {inset}: volume {v} vs {want}");
assert!(!d.events.iter().any(|e| e.strategy == BooleanStrategy::ToolNudge), "inset {inset}: an exact join: {d:?}");
}
}
}
/// The swept lip's foot over the turn is a plane, so it is found lying on the body's top.
#[test]
fn a_swept_lips_foot_is_a_plane() {
let l = lip(0.0);
let foot = crate::coplanar::faces_in(&l, -Vec3::Z, -10.0, 1e-6);
let m = l.tessellate(1e-5).unwrap();
let area: f64 = m
.triangles
.iter()
.zip(&m.tri_face)
.filter(|(_, f)| foot.contains(&(**f as usize)))
.filter_map(|(t, _)| m.tri(t))
.map(|[a, b, c]| (b - a).cross(c - a).len() / 2.0)
.sum();
assert!((area - PI * (100.0 - 81.0) / 4.0).abs() < 1e-3, "{} faces, {area} mm²", foot.len());
}