Pattern and Mirror dialogs copy picked bodies, fillets included (#193)
With Object Type = Bodies, the Mirror, Rectangular Pattern and Circular
Pattern dialogs sent the feature that created each picked body
({"features": ["Extrude1"]}), so the copies replayed only that feature and
lost everything done to the body afterwards (a fillet, a chamfer, a cut).
They now send the bodies themselves ({"bodies": [...]}), which the commands
already support and the Path Pattern dialog already did. Picking Features
is unchanged.
Editing a body Mirror or Pattern from the timeline now reopens the dialog
with its bodies selected.
Copies made this way take the body-copy names ("Body1 (2)"), as the
commands already give; pattern_direction_edge now looks up "Body1 (3)".
Tests: UI scenarios mirror_body_keeps_fillets and pattern_body_keeps_fillets
(filleted cube, copy and edit through the dialogs); both fail without the
fix with the copies' sharp 8000 mm3 volume.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
cab698bf12
commit
8eda982489
4 files changed
+72
-15
No files matched your search
@@ -2526,9 +2526,10 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
Kind::Mirror => {
|
||||
need(0, "objects")?;
|
||||
need(1, "the mirror plane")?;
|
||||
let features = pattern_features(s, sels(d, 0));
|
||||
let plane = plane_value(s, sels(d, 1).first()).ok_or("the mirror plane must be a plane or a planar face")?;
|
||||
("solid.mirror", json!({"features": features, "plane": plane}))
|
||||
let mut p = json!({"plane": plane});
|
||||
pattern_objects(s, sels(d, 0), &mut p);
|
||||
("solid.mirror", p)
|
||||
}
|
||||
Kind::PatternRect { count, spacing, count2, spacing2 } => {
|
||||
need(0, "objects")?;
|
||||
@@ -2538,7 +2539,8 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
(None, Some(k)) => k,
|
||||
(None, None) => return Err("select a direction (an axis, a sketch line or a straight edge) first".into()),
|
||||
};
|
||||
let mut p = json!({"features": pattern_features(s, sels(d, 0)), "dir1": pt(d1), "count1": count, "spacing1": spacing});
|
||||
let mut p = json!({"dir1": pt(d1), "count1": count, "spacing1": spacing});
|
||||
pattern_objects(s, sels(d, 0), &mut p);
|
||||
if let Some((_, d2)) = sels(d, 2).first().and_then(|x| axis_of(app, x)) {
|
||||
p["dir2"] = pt(d2);
|
||||
p["count2"] = json!(count2);
|
||||
@@ -2557,7 +2559,9 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
}
|
||||
None => return Err("select an axis".into()),
|
||||
};
|
||||
("solid.pattern.circular", json!({"features": pattern_features(s, sels(d, 0)), "axis": axis, "count": count, "angle": angle}))
|
||||
let mut p = json!({"axis": axis, "count": count, "angle": angle});
|
||||
pattern_objects(s, sels(d, 0), &mut p);
|
||||
("solid.pattern.circular", p)
|
||||
}
|
||||
Kind::Loft { operation } => {
|
||||
need(0, "profiles of two or more sketches")?;
|
||||
@@ -2676,11 +2680,7 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
let path = curve_ids(d, 1);
|
||||
let sketch = curves_sketch(app, &path, None).ok_or("the path curves must be in one sketch")?;
|
||||
let mut p = json!({"path_sketch": sketch, "path": path, "count": count, "spacing": spacing});
|
||||
if sels(d, 0).iter().any(|x| matches!(x, Sel::Feature { .. })) {
|
||||
p["features"] = json!(pattern_features(s, sels(d, 0)));
|
||||
} else {
|
||||
p["bodies"] = json!(body_names(0));
|
||||
}
|
||||
pattern_objects(s, sels(d, 0), &mut p);
|
||||
("solid.pattern.path", p)
|
||||
}
|
||||
Kind::Pipe { diameter, wall } => {
|
||||
@@ -2874,6 +2874,17 @@ fn curve_ids(d: &Dialog, i: usize) -> Vec<String> {
|
||||
/// The features that made these bodies (what patterns and mirrors copy).
|
||||
/// The features a pattern repeats: picked features as they are, picked bodies by the features
|
||||
/// that made them.
|
||||
/// What a pattern or mirror copies: picked features are replayed; picked bodies are copied as
|
||||
/// they are now, with every feature that shaped them (a fillet after the extrude included).
|
||||
fn pattern_objects(s: &Session, objects: &[Sel], p: &mut Value) {
|
||||
if objects.iter().any(|x| matches!(x, Sel::Feature { .. })) {
|
||||
p["features"] = json!(pattern_features(s, objects));
|
||||
} else {
|
||||
let bodies: Vec<&str> = objects.iter().filter_map(|x| if let Sel::Body { name } = x { Some(name.as_str()) } else { None }).collect();
|
||||
p["bodies"] = json!(bodies);
|
||||
}
|
||||
}
|
||||
|
||||
fn pattern_features(s: &Session, objects: &[Sel]) -> Vec<String> {
|
||||
let mut out: Vec<String> = Vec::new();
|
||||
let bodies: Vec<String> = objects.iter().filter_map(|x| if let Sel::Body { name } = x { Some(name.clone()) } else { None }).collect();
|
||||
@@ -3387,13 +3398,15 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
}
|
||||
d
|
||||
}
|
||||
FeatureKind::Mirror { features, plane, .. } => {
|
||||
FeatureKind::Mirror { features, plane, bodies, .. } => {
|
||||
let mut d = start("solid.mirror")?;
|
||||
let ids: Vec<u64> = features.iter().filter_map(|n| s.doc.find_feature(n).map(|f| f.id)).collect();
|
||||
// Features that made no body of their own (a hole) were picked as features.
|
||||
let as_features = ids.iter().any(|id| !st.bodies.iter().any(|b| b.feature == *id));
|
||||
if let Some(inp) = d.inputs.get_mut(0) {
|
||||
if as_features {
|
||||
if !bodies.is_empty() {
|
||||
inp.items = bodies.iter().map(|name| Sel::Body { name: name.clone() }).collect();
|
||||
} else if as_features {
|
||||
inp.accept = selection::FEATURES;
|
||||
inp.label = objects_label(inp.accept);
|
||||
inp.items = ids.iter().map(|id| Sel::Feature { id: *id }).collect();
|
||||
@@ -3406,9 +3419,13 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
}
|
||||
d
|
||||
}
|
||||
FeatureKind::Pattern { features, pattern, .. } => {
|
||||
FeatureKind::Pattern { features, pattern, bodies: copied } => {
|
||||
let ids: Vec<u64> = features.iter().filter_map(|n| s.doc.find_feature(n).map(|f| f.id)).collect();
|
||||
let bodies: Vec<Sel> = st.bodies.iter().filter(|b| ids.contains(&b.feature)).map(|b| Sel::Body { name: b.name.clone() }).collect();
|
||||
let bodies: Vec<Sel> = if copied.is_empty() {
|
||||
st.bodies.iter().filter(|b| ids.contains(&b.feature)).map(|b| Sel::Body { name: b.name.clone() }).collect()
|
||||
} else {
|
||||
copied.iter().map(|name| Sel::Body { name: name.clone() }).collect()
|
||||
};
|
||||
let mut d = match pattern {
|
||||
PatternKind::Rectangular { dir1, count1, spacing1, dir2, count2, spacing2 } => {
|
||||
let mut d = start("solid.pattern.rectangular")?;
|
||||
@@ -3453,7 +3470,7 @@ pub fn for_feature(app: &SolveApp, id: u64, marker: Option<usize>) -> Option<Dia
|
||||
PatternKind::Path { .. } => return None,
|
||||
};
|
||||
// Features that made no body of their own (a hole, a fillet) were picked as features.
|
||||
let as_features = ids.iter().any(|id| !st.bodies.iter().any(|b| b.feature == *id));
|
||||
let as_features = copied.is_empty() && ids.iter().any(|id| !st.bodies.iter().any(|b| b.feature == *id));
|
||||
if let Some(inp) = d.inputs.first_mut() {
|
||||
if as_features {
|
||||
inp.accept = selection::FEATURES;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
[
|
||||
{"note": "#193: copies of a body keep the fillets made after its extrude."},
|
||||
{"call": "engine.execute", "params": {"command": "sketch.create", "params": {"plane": "XY"}}},
|
||||
{"call": "engine.execute", "params": {"command": "sketch.rectangle.two_point", "params": {"p0": [0, 0], "p1": [20, 20]}}},
|
||||
{"call": "engine.execute", "params": {"command": "sketch.finish", "params": {}}},
|
||||
{"call": "engine.execute", "params": {"command": "solid.extrude", "params": {"distance": 20}}},
|
||||
{"call": "engine.execute", "params": {"command": "solid.fillet", "params": {"edges": [[0, 0, 10], [20, 0, 10], [0, 20, 10], [20, 20, 10]], "radius": 3}}},
|
||||
{"call": "ui.view", "params": {"view": "iso"}},
|
||||
{"expect": {"bodies": 1, "volume": [7845.39, 0.001]}},
|
||||
{"start": "solid.mirror"},
|
||||
{"click": {"world": [10, 10, 20]}},
|
||||
{"call": "ui.dialogInput", "params": {"index": 1}},
|
||||
{"click": {"plane": "YZ"}},
|
||||
{"key": "Enter"},
|
||||
{"expect": {"bodies": 2, "errors": 0, "dialog": null, "volume": [15690.78, 0.001]}},
|
||||
{"call": "ui.editFeature", "params": {"feature": "Mirror1"}},
|
||||
{"expect": {"dialog": "Mirror"}},
|
||||
{"key": "Enter"},
|
||||
{"expect": {"bodies": 2, "errors": 0, "dialog": null, "volume": [15690.78, 0.001]}}
|
||||
]
|
||||
@@ -0,0 +1,20 @@
|
||||
[
|
||||
{"note": "#193: copies of a body keep the fillets made after its extrude."},
|
||||
{"call": "engine.execute", "params": {"command": "sketch.create", "params": {"plane": "XY"}}},
|
||||
{"call": "engine.execute", "params": {"command": "sketch.rectangle.two_point", "params": {"p0": [0, 0], "p1": [20, 20]}}},
|
||||
{"call": "engine.execute", "params": {"command": "sketch.finish", "params": {}}},
|
||||
{"call": "engine.execute", "params": {"command": "solid.extrude", "params": {"distance": 20}}},
|
||||
{"call": "engine.execute", "params": {"command": "solid.fillet", "params": {"edges": [[0, 0, 10], [20, 0, 10], [0, 20, 10], [20, 20, 10]], "radius": 3}}},
|
||||
{"call": "ui.view", "params": {"view": "iso"}},
|
||||
{"expect": {"bodies": 1, "volume": [7845.39, 0.001]}},
|
||||
{"start": "solid.pattern.rectangular"},
|
||||
{"click": {"world": [10, 10, 20]}},
|
||||
{"call": "ui.dialogInput", "params": {"index": 1}},
|
||||
{"click": {"axis": "X"}},
|
||||
{"key": "Enter"},
|
||||
{"expect": {"bodies": 3, "errors": 0, "dialog": null, "volume": [23536.17, 0.001]}},
|
||||
{"call": "ui.editFeature", "params": {"feature": "RectangularPattern1"}},
|
||||
{"expect": {"dialog": "PatternRect"}},
|
||||
{"key": "Enter"},
|
||||
{"expect": {"bodies": 3, "errors": 0, "dialog": null, "volume": [23536.17, 0.001]}}
|
||||
]
|
||||
@@ -62,7 +62,7 @@
|
||||
{
|
||||
"expect": {
|
||||
"bbox": {
|
||||
"body": "Body3",
|
||||
"body": "Body1 (3)",
|
||||
"min": [
|
||||
40,
|
||||
0,
|
||||
|
||||
Reference in new issue
Block a user