From bf4551159e247882f5a6e56b3a88f8e49d9dd7c6 Mon Sep 17 00:00:00 2001 From: Mateusz Sych <151755914+SychPL@users.noreply.github.com> Date: Fri, 16 Oct 2026 17:26:17 +0200 Subject: [PATCH] Split Body and Combine Intersect keep their faces' sources, so sketches attach (#565) (#1732) Thanks @SychPL. --- apps/solidcraft-cli/tests/action_matrix.rs | 1 - crates/doc/src/eval.rs | 4 ++++ crates/kernel/src/ops.rs | 7 +++++-- docs/action-matrix.md | 6 +++--- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/apps/solidcraft-cli/tests/action_matrix.rs b/apps/solidcraft-cli/tests/action_matrix.rs index 35d82d96..23781cdc 100644 --- a/apps/solidcraft-cli/tests/action_matrix.rs +++ b/apps/solidcraft-cli/tests/action_matrix.rs @@ -1559,7 +1559,6 @@ fn a_sketch_on_the_rim_a_shell_leaves() { } #[test] -#[ignore = "fails until #565 is fixed"] fn sketches_after_split_body_and_intersect() { reproduces( None, diff --git a/crates/doc/src/eval.rs b/crates/doc/src/eval.rs index 60ef47f8..aaf39d76 100644 --- a/crates/doc/src/eval.rs +++ b/crates/doc/src/eval.rs @@ -4436,7 +4436,11 @@ fn eval_feature(doc: &Document, vals: &BTreeMap, f: &Feature, st: let tb = st.body(&t.body).ok_or_else(|| DocError::Unknown(format!("body `{}`", t.body)))?; let face = crate::appearance::face_index_at(tb, t.point.0) .ok_or_else(|| DocError::Invalid(format!("no face of `{}` at the tool point", t.body)))?; + // The pieces keep the planar roles of the faces they share with the body (#565). kernel::split_body(&mb.body, &kernel::SplitTool::Face { body: &tb.body, face })? + .into_iter() + .map(|part| part.with_preserved_planar_sources(&mb.body)) + .collect::, _>>()? } None => { let pl = doc.resolve_plane_in(vals, plane, 0, Some(st))?; diff --git a/crates/kernel/src/ops.rs b/crates/kernel/src/ops.rs index 5cfc713c..84ea44e8 100644 --- a/crates/kernel/src/ops.rs +++ b/crates/kernel/src/ops.rs @@ -363,7 +363,9 @@ fn boolean_via(a: &Body, b: &Body, op: BoolOp, whole: &dyn Fn(&Body, &Body, Bool match op { BoolOp::Cut => r = r.and_then(|result| result.map(|body| body.with_preserved_boolean_planar_sources(a, b, true)).transpose()), BoolOp::Union => r = r.and_then(|result| result.map(|body| body.with_preserved_union_planar_sources(a, b)).transpose()), - BoolOp::Intersect => {} + // Every face of an intersection lies on one operand's face, as a union's does: the same + // roles carry over (#565). + BoolOp::Intersect => r = r.and_then(|result| result.map(|body| body.with_preserved_union_planar_sources(a, b)).transpose()), } // A retry's result is held to what the operation can do at all; one that fails is refused // rather than returned wrong (issue #616). @@ -1434,7 +1436,8 @@ pub fn split_by_plane(body: &Body, plane: &solidcraft_geom::Plane) -> Result Result> { - boolean(body, &half_box(body, plane, positive)?, BoolOp::Intersect) + // The box's own construction roles describe no face of the design: the cut face has none. + boolean(body, &half_box(body, plane, positive)?.with_face_sources(None), BoolOp::Intersect) } /// A box covering everything of `body` on one side of a plane, its start cap in the plane. diff --git a/docs/action-matrix.md b/docs/action-matrix.md index f4ae77f6..71c38075 100644 --- a/docs/action-matrix.md +++ b/docs/action-matrix.md @@ -257,7 +257,7 @@ resolves; it is recorded as `+edit`. ## Causes -Every failure, grouped by cause (5025 in all). Faces that can't take an attached sketch fail four +Every failure, grouped by cause (5017 in all). Faces that can't take an attached sketch fail four actions each (`face.sketch`, `face.offset_plane`, and `face.press_pull` and `face.extrude`, which make a legacy face sketch there). @@ -283,10 +283,10 @@ make a legacy face sketch there). | 56 | #563 | Draft and tapered Extrude drop the faces' sources | `face.extrude` 14, `face.offset_plane` 14, `face.press_pull` 14, `face.sketch` 14 | 3 | oracle/05-taper-extrude `Frustum/F2:start` | | 48 | #564 | The rim a Shell leaves has no source | `face.extrude` 12, `face.offset_plane` 12, `face.press_pull` 12, `face.sketch` 12 | 8 | demo/pi-case `Body1/F2:top` | | 18 | #567 | Axis or tangent plane on a drill tip: no face at NaN | `curved.axis` 9, `curved.tangent_plane` 9 | 3 | oracle/18-hole-features `Plate/F5:face:15` | -| 12 | #565 | Split Body and Combine Intersect drop the faces' sources | `face.offset_plane` 4, `face.sketch` 4, `face.extrude` 2, `face.press_pull` 2 | 1 | oracle/22-split-body `Cyl (1)/F2:bottom` | | 9 | #568 | Construction geometry on model edges, faces and vertices is lost when a parameter moves it | `vertex.plane_three_points+edit` 5, `edge.normal_plane+edit` 2, `face.midplane+edit` 1, `face.shell+edit` 1 | 4 | demo/enclosure_steps `Enclosure/v[F2:top & F4:inner:F2:side:l4 & F4:inner:F3:blend:F2:side:l` | | 9 | | Axis through a face the kernel sees as a cylinder that the command doesn't (sensor enclosure slot ends; not filed yet) | `curved.axis` 9 | 1 | examples/sensor_enclosure `Lid/F15:blend:F14:side:l2\|F14:side:l3` | | 7 | | Other failures in the edit round: the hole cut and blend sizes above, and an extrude from the M0 box's top face whose profile isn't found again (not filed yet) | `face.hole+edit` 3, `face.extrude+edit` 1, `face.press_pull+edit` 1, `edge.chamfer+edit` 1 | 4 | corpus/m0-box-fillet-hole `Body1/F2:top` | +| 4 | #565 | The face a Split Body cuts has no source (the faces it leaves whole keep theirs) | `face.offset_plane` 2, `face.sketch` 2 | 1 | oracle/22-split-body `Cyl (1)/F4:face:3` | | 2 | | A helix edge projected along its axis is a point (expected) | `edge.project` 2 | 1 | oracle/50-sweep-tight-bend `Bend/F3:side:c1#1\|F3:side:c1#2~2` | Where the face history stands (`crates/kernel/src/history.rs`, [face attachments](face-attachments.md)): @@ -297,5 +297,5 @@ chamfers, pattern and mirror copies, sides a Join merged, revolves, lofts, sweep shell rims, the plastic features' faces and the faces around them, and pieces of a split face (by the piece's own name where Offset Face moved it off the others' plane). Sheet metal panels, their rims and the flat ends of bends attach by name. What still refuses a sketch is what makes faces -without recording their history (some cuts, Split Body, Intersect): press-pull and +without recording their history (some cuts, the face a Split Body cuts): press-pull and extrude-from-face then fall back to a legacy face sketch found again by a point.