From 651e8e085399591f6ebd0a8162d265acf41370b3 Mon Sep 17 00:00:00 2001 From: Jonas Wrede Date: Tue, 13 Oct 2026 10:27:12 +0200 Subject: [PATCH] fix: draft a whole round wall from one pick (#547) (#603) Draft extends the picked faces to every face on the same surface, so one pick on a cylinder's side (stored as two halves) drafts the whole round wall into a frustum. Un-ignores the #547 regression test and adds a kernel test for a round hole. Fixes #547. Co-authored-by: jonaswre --- crates/engine/src/tests.rs | 1 - crates/kernel/src/polyhedron.rs | 2 ++ crates/kernel/src/tests.rs | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index 8ebfc667..ab539561 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -2955,7 +2955,6 @@ fn feature_replies_list_only_the_bodies_they_created() { /// Drafting a cylinder's side face 5° from the base plane makes a cone frustum (#547): radius 10 /// at the base, 10 − 20 · tan 5° at the top. #[test] -#[ignore = "fails until #547 is fixed"] fn draft_on_a_cylinder_side_makes_a_frustum() { let mut s = Session::default(); run(&mut s, "solid.cylinder", json!({"radius": 10, "height": 20})); diff --git a/crates/kernel/src/polyhedron.rs b/crates/kernel/src/polyhedron.rs index 30048809..6b7675ef 100644 --- a/crates/kernel/src/polyhedron.rs +++ b/crates/kernel/src/polyhedron.rs @@ -306,6 +306,8 @@ fn draft_walls(b: &Body, at: &[Vec3], neutral: &Plane, pull: Vec3, angle: f64) - _ => return Err(KernelError::Invalid(format!("no face at {:?}", [p.x, p.y, p.z]))), } } + // A round wall is stored in pieces (a cylinder's two halves): picking one drafts the whole wall. + let chosen = crate::offset::with_same_surface(&healed, &chosen); Ok(record::after(crate::offset::draft_walls(&healed, &chosen, neutral, pull, angle)?, &healed, &record::counts(&[b]))) } diff --git a/crates/kernel/src/tests.rs b/crates/kernel/src/tests.rs index 537e5450..a3cd0f7f 100644 --- a/crates/kernel/src/tests.rs +++ b/crates/kernel/src/tests.rs @@ -1536,6 +1536,21 @@ fn draft_of_a_round_ended_pocket() { assert!(rel(v, 80.0 * 60.0 * 25.0 - pocket) < 1e-4, "{v} vs {}", 80.0 * 60.0 * 25.0 - pocket); } +/// A round wall is stored in pieces (a cylinder's two halves); one pick drafts all of it (#547). +/// A hole drafted from the top narrows going down: a frustum from R 10 to 10 − 25 · tan 5°. +#[test] +fn draft_of_a_round_hole_from_one_pick() { + let block = box_solid(Vec3::ZERO, Vec3::new(60.0, 60.0, 25.0)).unwrap(); + let hole = cylinder(Vec3::new(30.0, 30.0, -1.0), Vec3::Z, 10.0, 27.0).unwrap(); + let b = boolean(&block, &hole, BoolOp::Cut).unwrap().unwrap(); + let a = 5f64.to_radians(); + let d = draft(&b, &[Vec3::new(40.0, 30.0, 12.5)], &Plane::XY.offset(25.0), Vec3::Z, a).unwrap(); + let (big, small) = (10.0, 10.0 - 25.0 * a.tan()); + let want = 60.0 * 60.0 * 25.0 - PI * 25.0 / 3.0 * (big * big + big * small + small * small); + let m = measure(&d).unwrap(); + assert!(rel(m.volume, want) < 1e-4, "{} vs {want}", m.volume); +} + #[test] fn rounded_corners_rounded_again_make_spheres() { // Vertical edges first, then the top and bottom loops with the same radius: the corners