mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-03 02:25:55 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c0236187d1 |
+19
-6
@@ -1532,23 +1532,36 @@ impl Model {
|
||||
/// Follow a rename of body `old` to `new` without evaluating anything (#3044): `doc` is the
|
||||
/// design after the rename, which changed nothing but names, so every result keeps its
|
||||
/// geometry and meshes and only the body's name (and the names worked out from it) changes.
|
||||
/// Later features that change the body (a pattern, a chamfer, a join) keep their results
|
||||
/// too (#2467): a feature's geometry doesn't depend on its bodies' names.
|
||||
/// False, leaving the model as it was, when the rename can't be followed that way and the
|
||||
/// design must be evaluated: the model isn't the design's, the body is changed by a later
|
||||
/// feature, or is sheet metal, threaded, or named in a later feature's face references.
|
||||
/// design must be evaluated: the model isn't the design's, a body of that name comes and goes
|
||||
/// or isn't the same body throughout, a later body's name is worked out from it (`X (2)`), a
|
||||
/// message quotes it, or it is sheet metal, threaded, or named in a later feature's face
|
||||
/// references.
|
||||
pub fn rename_body(&mut self, doc: &Document, old: &str, new: &str) -> bool {
|
||||
if self.results.len() != doc.features.len() || self.results.iter().zip(&doc.features).any(|(r, f)| r.id != f.id || r.stale) {
|
||||
return false;
|
||||
}
|
||||
let Some(at) = self.results.iter().position(|r| r.state.body(old).is_some()) else { return false };
|
||||
let Some(made) = self.results.get(at).and_then(|r| r.state.body(old)).map(|b| Arc::as_ptr(&b.mesh)) else { return false };
|
||||
// Untouched from the feature that made it to the end, and nothing else holds its name.
|
||||
let untouched = self.results.iter().skip(at).all(|r| r.state.body(old).is_some_and(|b| Arc::as_ptr(&b.mesh) == made));
|
||||
let Some(feature) = self.results.get(at).and_then(|r| r.state.body(old)).map(|b| b.feature) else { return false };
|
||||
// One body from the feature that made it until it is gone (joined into another), and no
|
||||
// other body's name comes from its name.
|
||||
let held: Vec<bool> = self.results.iter().skip(at).map(|r| r.state.body(old).is_some()).collect();
|
||||
let one_run = held.iter().skip_while(|h| **h).all(|h| !*h);
|
||||
let derived = format!("{old} (");
|
||||
let same_body = self
|
||||
.results
|
||||
.iter()
|
||||
.skip(at)
|
||||
.all(|r| r.state.bodies.iter().all(|b| if b.name == old { b.feature == feature } else { !b.name.starts_with(&derived) }));
|
||||
let quoted = self.results.iter().skip(at).any(|r| [&r.error, &r.warning].into_iter().flatten().any(|m| m.contains(old)));
|
||||
let named_elsewhere = self
|
||||
.results
|
||||
.iter()
|
||||
.any(|r| r.state.body(new).is_some() || r.state.sheets.iter().any(|x| x.body == old) || r.state.threads.iter().any(|t| t.body == old));
|
||||
let faces_named = doc.features.iter().skip(at + 1).any(|f| f.face_names.iter().any(|n| n.contains(old)));
|
||||
if !untouched || named_elsewhere || faces_named {
|
||||
if !one_run || !same_body || quoted || named_elsewhere || faces_named {
|
||||
return false;
|
||||
}
|
||||
// States shared between results stay shared.
|
||||
|
||||
@@ -3851,6 +3851,64 @@ fn renaming_an_imported_assembly_body_reads_nothing_again() {
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
/// #2467: renaming a body that later features change (a hole patterned over it, then a chamfer)
|
||||
/// evaluates nothing, and leaves the bodies, volumes and face names evaluating the renamed
|
||||
/// design gives; undo and redo do the same.
|
||||
#[test]
|
||||
fn renaming_a_body_later_features_change_evaluates_nothing() {
|
||||
let mut s = Session::default();
|
||||
run(&mut s, "sketch.create", json!({"plane": "XY"}));
|
||||
run(&mut s, "sketch.rectangle.two_point", json!({"p0": [0, 0], "p1": [80, 50]}));
|
||||
run(&mut s, "sketch.finish", json!({}));
|
||||
run(&mut s, "solid.extrude", json!({"distance": 10}));
|
||||
run(
|
||||
&mut s,
|
||||
"solid.hole",
|
||||
json!({"position": [15, 12, 10], "diameter": 5, "type": "counterbore", "cb_diameter": 9, "cb_depth": 3, "name": "Hole1"}),
|
||||
);
|
||||
run(
|
||||
&mut s,
|
||||
"solid.pattern.rectangular",
|
||||
json!({"features": ["Hole1"], "dir1": [1, 0, 0], "count1": 3, "spacing1": 25, "dir2": [0, 1, 0], "count2": 2, "spacing2": 25}),
|
||||
);
|
||||
run(&mut s, "solid.chamfer", json!({"edges": [[40, 0, 10], [80, 25, 10]], "distance": 1}));
|
||||
let looks = |st: &solidcraft_doc::ModelState| {
|
||||
st.bodies.iter().map(|b| (b.name.clone(), (*solidcraft_doc::naming::face_names(b)).clone())).collect::<Vec<_>>()
|
||||
};
|
||||
let before = looks(&s.model.state());
|
||||
let v = body_volume(&s.model.state());
|
||||
let old = before.first().map(|b| b.0.clone()).unwrap();
|
||||
s.evaluations = 0;
|
||||
run(&mut s, "body.rename", json!({"body": old, "name": "Plate"}));
|
||||
assert_eq!(s.evaluations, 0, "a rename evaluates nothing");
|
||||
let after = looks(&s.model.state());
|
||||
assert_eq!(after.first().map(|b| b.0.as_str()), Some("Plate"));
|
||||
let mut fresh = Session::new((*s.doc).clone());
|
||||
fresh.refresh();
|
||||
assert_eq!(looks(&fresh.model.state()), after, "the bodies and face names evaluating the renamed design gives");
|
||||
assert!(rel(body_volume(&fresh.model.state()), v) < 1e-9);
|
||||
run(&mut s, "edit.undo", json!({}));
|
||||
assert_eq!(looks(&s.model.state()), before);
|
||||
run(&mut s, "edit.redo", json!({}));
|
||||
assert_eq!(looks(&s.model.state()), after);
|
||||
assert_eq!(s.evaluations, 0, "nor do its undo and redo");
|
||||
}
|
||||
|
||||
/// A body whose name later bodies' names are worked out from (`X` patterned into `X (2)`) is still
|
||||
/// renamed by evaluating the design, so the copies follow it as they would on opening it.
|
||||
#[test]
|
||||
fn renaming_a_body_whose_copies_take_its_name_evaluates_the_design() {
|
||||
let mut s = Session::default();
|
||||
run(&mut s, "solid.box", json!({"length": 10, "width": 10, "height": 10, "body_name": "X"}));
|
||||
run(&mut s, "solid.pattern.rectangular", json!({"bodies": ["X"], "dir1": [1, 0, 0], "count1": 3, "spacing1": 20}));
|
||||
let names = |s: &Session| s.model.state().bodies.iter().map(|b| b.name.clone()).collect::<Vec<_>>();
|
||||
assert!(names(&s).iter().any(|n| n.starts_with("X (")), "{:?}", names(&s));
|
||||
run(&mut s, "body.rename", json!({"body": "X", "name": "First"}));
|
||||
let mut fresh = Session::new((*s.doc).clone());
|
||||
fresh.refresh();
|
||||
assert_eq!(names(&fresh), names(&s));
|
||||
}
|
||||
|
||||
/// #3044: on a design with thousands of bodies the cheap commands stay cheap: showing, hiding,
|
||||
/// selecting, colouring and renaming a body or a feature, and undoing them, recompute no feature
|
||||
/// (renames don't even look over the timeline) and leave every body's display mesh as it was, so
|
||||
|
||||
Reference in New Issue
Block a user