mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-03 10:35:58 +00:00
engine: an extrude Join, Cut or Intersect leaves hidden bodies alone (#2464)
With no targets picked, an extrude's Join, Cut or Intersect changed every body of the active component it met, shown or hidden: a hidden coincident copy was joined in and disappeared. Where some bodies of the active component are hidden, the shown ones become the feature's participants, kept with it, so showing or hiding bodies later doesn't change the model. Explicit targets and participants, and a face's or a face sketch's own body, still win. Test: the issue's plate with a hidden copy and a rim joined on. Main reached the same fix for Cut and Intersect in #3961 (its targets-based shown_scope in extrude); this one, which also covers Join and keeps the shown bodies as participants, replaces it. Its test stays. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017itGh5fpMEousnZ91cYcVt
This commit is contained in:
@@ -693,6 +693,17 @@ fn add_feature_in(s: &mut Session, p: &Value, kind: FeatureKind, world: bool) ->
|
||||
} else {
|
||||
participants
|
||||
};
|
||||
// An extrude's Join, Cut or Intersect with no bodies picked changes the bodies of the active
|
||||
// component that are shown, never a hidden one (#2464). The shown ones are kept as its
|
||||
// participants, so showing or hiding bodies later doesn't change the model.
|
||||
let participants = match &kind {
|
||||
FeatureKind::Extrude { operation: Operation::Join | Operation::Cut | Operation::Intersect, targets, .. }
|
||||
if participants.is_empty() && targets.is_empty() =>
|
||||
{
|
||||
shown_scope(s).unwrap_or_default()
|
||||
}
|
||||
_ => participants,
|
||||
};
|
||||
if matches!(kind, FeatureKind::ConstructionPlane { .. })
|
||||
&& let Some(n) = name
|
||||
{
|
||||
@@ -828,9 +839,6 @@ fn extrude(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
} else if let Some(parts) = cut_scope(s, &p, operation, sketch, face_body)? {
|
||||
p.to_mut()["participants"] = json!(parts);
|
||||
}
|
||||
if matches!(operation, Operation::Cut | Operation::Intersect) && targets.is_empty() && string_list(&p, "participants").is_empty() {
|
||||
targets = shown_scope(s);
|
||||
}
|
||||
let kind = FeatureKind::Extrude {
|
||||
sketch: SketchId(sketch),
|
||||
profiles: match face_profile {
|
||||
@@ -975,17 +983,6 @@ fn face_body_reached(s: &Session, sketch: u64) -> Result<Option<String>> {
|
||||
Ok(Some(body.name.clone()))
|
||||
}
|
||||
|
||||
/// A cut or intersect that names no bodies leaves hidden bodies alone, as hiding a body to keep
|
||||
/// it out of a cut is the usual way to (#3961): while some of the active component's bodies
|
||||
/// are hidden, its shown ones, stored as the feature's targets so showing the others again
|
||||
/// later changes nothing. Empty (every body, as before) when none is hidden, or all are.
|
||||
fn shown_scope(s: &Session) -> Vec<String> {
|
||||
let st = s.model.state();
|
||||
let scope = active_scope(s, &st);
|
||||
let shown: Vec<String> = scope.iter().filter(|b| s.body_visible(&b.name)).map(|b| b.name.clone()).collect();
|
||||
if shown.len() == scope.len() { Vec::new() } else { shown }
|
||||
}
|
||||
|
||||
/// The body whose face `sketch` is attached to (on the face itself, not offset from it), when
|
||||
/// it is one the active component works on ([`active_scope`]).
|
||||
fn sketch_face_body(s: &Session, sketch: u64) -> Option<String> {
|
||||
@@ -2961,6 +2958,15 @@ fn offset_face(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
)
|
||||
}
|
||||
|
||||
/// The shown bodies of the active component, where some of its bodies are hidden and some shown;
|
||||
/// `None` where all of them are shown (or none), and the feature's usual scope applies.
|
||||
fn shown_scope(s: &Session) -> Option<Vec<String>> {
|
||||
let st = s.model.state();
|
||||
let (shown, hidden): (Vec<&solidcraft_doc::ModelBody>, Vec<_>) =
|
||||
st.bodies.iter().filter(|b| s.doc.model_body_component(b) == s.active_component).partition(|b| s.body_visible(&b.name));
|
||||
(!shown.is_empty() && !hidden.is_empty()).then(|| shown.iter().map(|b| b.name.clone()).collect())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "patterns_tests.rs"]
|
||||
mod patterns_tests;
|
||||
|
||||
@@ -4493,3 +4493,27 @@ fn a_cut_leaves_hidden_bodies_alone() {
|
||||
let shown = setup(false);
|
||||
assert!(vol(&shown, "Below") < full - 1.0 && vol(&shown, "Above") < full - 1.0, "with nothing hidden, both are cut");
|
||||
}
|
||||
|
||||
/// #2464: an extrude Join with no targets onto a plate with a hidden coincident copy changes the
|
||||
/// shown plate only, and keeps the shown bodies as its participants.
|
||||
#[test]
|
||||
fn join_leaves_a_hidden_body_alone() {
|
||||
let mut s = Session::default();
|
||||
run(&mut s, "solid.box", json!({"length": 40, "width": 40, "height": 5, "body_name": "Plate"}));
|
||||
run(&mut s, "solid.move", json!({"bodies": ["Plate"], "copy": true}));
|
||||
run(&mut s, "browser.visibility", json!({"items": ["Plate (2)"], "visible": false}));
|
||||
run(&mut s, "sketch.create", json!({"plane": "XY", "offset": 5, "name": "Rim"}));
|
||||
run(&mut s, "sketch.rectangle.two_point", json!({"p0": [2, 2], "p1": [38, 38]}));
|
||||
run(&mut s, "sketch.rectangle.two_point", json!({"p0": [4, 4], "p1": [36, 36]}));
|
||||
run(&mut s, "sketch.finish", json!({}));
|
||||
run(&mut s, "solid.extrude", json!({"sketch": "Rim", "profiles": [{"point": [3, 3]}], "distance": 3, "operation": "join"}));
|
||||
let st = s.model.state();
|
||||
assert_eq!(st.bodies.len(), 2);
|
||||
let v = |n: &str| solidcraft_kernel::measure(&st.body(n).unwrap().body).unwrap().volume;
|
||||
assert!(rel(v("Plate"), 8816.0) < 1e-9 && rel(v("Plate (2)"), 8000.0) < 1e-9);
|
||||
let f = s.doc.features.last().unwrap();
|
||||
assert_eq!(f.participants, vec!["Plate".to_string()]);
|
||||
// Showing the copy again doesn't change the model.
|
||||
run(&mut s, "browser.visibility", json!({"items": ["Plate (2)"], "visible": true}));
|
||||
assert_eq!(s.model.state().bodies.len(), 2);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user