Files
OneCAD/crates/kernel/src/outline_tests.rs
T
Que 1cc65cbc91 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.
2026-10-14 18:49:45 -04:00

57 lines
2.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 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());
}