M0: truck-based kernel with local fillets and chamfers, parametric document timeline
The kernel moved from monstertruck to truck: truck's booleans handle blind holes, pockets and bosses that monstertruck's did not (see the ADR). Fillets and chamfers on straight convex edges between planar faces are our own exact local B-rep operation. The document holds parameters with unit-aware expressions and a timeline that re-evaluates incrementally. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
dafce1f4e2
commit
a97af44979
18 files changed
+2740
-625
No files matched your search
@@ -119,6 +119,32 @@ fn hostile_inputs_are_errors() {
|
||||
assert!(transform(&b, Vec3::new(f64::NAN, 0.0, 0.0), Vec3::ZERO, Vec3::Z, 0.0).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fillets_on_several_edges_and_angles() {
|
||||
let b = box_solid(Vec3::ZERO, Vec3::new(40.0, 30.0, 20.0)).unwrap();
|
||||
// Two opposite vertical edges, then a horizontal top edge between them is not touching either.
|
||||
let f = fillet(&b, &[Vec3::new(0.0, 0.0, 10.0), Vec3::new(40.0, 30.0, 10.0)], 4.0).unwrap();
|
||||
let corner = 16.0 - PI * 4.0;
|
||||
assert!(rel(measure(&f).unwrap().volume, 24000.0 - 2.0 * corner * 20.0) < 2e-4);
|
||||
let top = fillet(&b, &[Vec3::new(20.0, 0.0, 20.0)], 2.0).unwrap();
|
||||
assert!(rel(measure(&top).unwrap().volume, 24000.0 - (4.0 - PI) * 40.0) < 2e-4);
|
||||
// Triangular prism: 60° edges.
|
||||
let tri = Region2 { outer: Loop2::polygon(&[Vec2::new(0.0, 0.0), Vec2::new(30.0, 0.0), Vec2::new(15.0, 25.980762113533)]), holes: vec![] };
|
||||
let p = extrude(&Plane::XY, &[tri], 0.0, 10.0).unwrap().pop().unwrap();
|
||||
let r = 2.0;
|
||||
let fp = fillet(&p, &[Vec3::new(0.0, 0.0, 5.0)], r).unwrap();
|
||||
// Removed area for interior angle φ: r²(cot(φ/2)) − r²(π − φ)/2.
|
||||
let phi = PI / 3.0;
|
||||
let removed = r * r / (phi / 2.0).tan() - r * r * (PI - phi) / 2.0;
|
||||
let v0 = measure(&p).unwrap().volume;
|
||||
assert!(rel(measure(&fp).unwrap().volume, v0 - removed * 10.0) < 2e-4, "{} vs {}", measure(&fp).unwrap().volume, v0 - removed * 10.0);
|
||||
// Adjacent edges at a blended corner are not supported yet (an error, not a crash).
|
||||
assert!(fillet(&b, &[Vec3::new(0.0, 0.0, 10.0), Vec3::new(0.0, 15.0, 20.0)], 2.0).is_err());
|
||||
// Concave edge: not supported.
|
||||
let u = boolean(&b, &box_solid(Vec3::new(10.0, 10.0, 15.0), Vec3::new(20.0, 20.0, 30.0)).unwrap(), BoolOp::Union).unwrap().unwrap();
|
||||
assert!(fillet(&u, &[Vec3::new(15.0, 10.0, 20.0)], 1.0).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn debug_convergence() {
|
||||
@@ -135,3 +161,58 @@ fn debug_convergence() {
|
||||
println!("face {f}: {} normals, first {:?}, max |n.z| {}", ns.len(), ns.first(), ns.iter().map(|n| n.z.abs()).fold(0.0, f64::max));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn debug_coplanar() {
|
||||
let b = box_solid(Vec3::ZERO, Vec3::new(40.0, 30.0, 20.0)).unwrap();
|
||||
for (z0, z1) in [(0.0, 20.0), (-5.0, 20.0), (0.0, 25.0), (-5.0, 25.0), (5.0, 20.0), (5.0, 25.0)] {
|
||||
let c = cylinder(Vec3::new(20.0, 15.0, z0), Vec3::Z, 5.0, z1 - z0).unwrap();
|
||||
let r = boolean(&b, &c, BoolOp::Cut);
|
||||
println!("cut z {z0}..{z1}: {:?}", r.map(|o| o.map(|b| measure(&b).unwrap().volume)));
|
||||
let r = boolean(&b, &c, BoolOp::Union);
|
||||
println!("union z {z0}..{z1}: {:?}", r.map(|o| o.map(|b| measure(&b).unwrap().volume)));
|
||||
}
|
||||
let r =
|
||||
Region2 { outer: Loop2::polygon(&[Vec2::new(0.0, 0.0), Vec2::new(40.0, 0.0), Vec2::new(40.0, 30.0), Vec2::new(0.0, 30.0)]), holes: vec![] };
|
||||
for ang in [PI / 2.0, PI, 1.5 * PI, 2.0 * PI] {
|
||||
let q = revolve(&Plane::XY, std::slice::from_ref(&r), Vec2::ZERO, Vec2::Y, ang).unwrap().pop().unwrap();
|
||||
let m = measure(&q).unwrap();
|
||||
println!("revolve {ang}: vol {} expect {} faces {}", m.volume, ang / 2.0 * 1600.0 * 30.0, m.faces);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn debug_blind() {
|
||||
let b = box_solid(Vec3::ZERO, Vec3::new(40.0, 30.0, 20.0)).unwrap();
|
||||
let t = box_solid(Vec3::new(10.0, 10.0, 5.0), Vec3::new(20.0, 20.0, 25.0)).unwrap();
|
||||
println!("box pocket: {:?}", boolean(&b, &t, BoolOp::Cut).map(|o| o.map(|b| measure(&b).unwrap().volume)));
|
||||
let t = box_solid(Vec3::new(10.0, 10.0, 5.0), Vec3::new(20.0, 20.0, 15.0)).unwrap();
|
||||
println!("box void: {:?}", boolean(&b, &t, BoolOp::Cut).map(|o| o.map(|b| measure(&b).unwrap().volume)));
|
||||
for r in [5.0, 4.3] {
|
||||
for z in [5.0, 7.3, 13.1] {
|
||||
let c = cylinder(Vec3::new(20.3, 15.1, z), Vec3::Z, r, 30.0).unwrap();
|
||||
println!("cyl r{r} z{z}: {:?}", boolean(&b, &c, BoolOp::Cut).map(|o| o.map(|b| measure(&b).unwrap().volume)));
|
||||
}
|
||||
}
|
||||
let c = cylinder(Vec3::new(20.0, 15.0, 25.0), -Vec3::Z, 5.0, 20.0).unwrap();
|
||||
println!("cyl down: {:?}", boolean(&b, &c, BoolOp::Cut).map(|o| o.map(|b| measure(&b).unwrap().volume)));
|
||||
let s = sphere(Vec3::new(20.0, 15.0, 20.0), 5.0).unwrap();
|
||||
println!("sphere: {:?}", boolean(&b, &s, BoolOp::Cut).map(|o| o.map(|b| measure(&b).unwrap().volume)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn debug_revolve_doc_case() {
|
||||
let r =
|
||||
Region2 { outer: Loop2::polygon(&[Vec2::new(0.0, 0.0), Vec2::new(40.0, 0.0), Vec2::new(40.0, 30.0), Vec2::new(0.0, 30.0)]), holes: vec![] };
|
||||
for ang in [PI / 2.0, -PI / 2.0] {
|
||||
let q = revolve(&Plane::XY, std::slice::from_ref(&r), Vec2::ZERO, Vec2::Y, ang).unwrap().pop().unwrap();
|
||||
let m = measure(&q).unwrap();
|
||||
println!("ang {ang}: vol {} bbox {:?}", m.volume, m.bbox);
|
||||
}
|
||||
let r2 = Region2 { outer: r.outer.reversed(), holes: vec![] };
|
||||
let q = revolve(&Plane::XY, std::slice::from_ref(&r2), Vec2::ZERO, Vec2::Y, PI / 2.0).unwrap().pop().unwrap();
|
||||
println!("reversed: {}", measure(&q).unwrap().volume);
|
||||
}
|
||||
Reference in new issue
Block a user