1 parent
91de39d3fe
commit
00baf1d883
2 files changed
+33
-2
No files matched your search
@@ -1,5 +1,7 @@
|
||||
//! Undo/redo, the timeline, deletion and selection.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use solidcraft_doc::{Feature, FeatureKind};
|
||||
|
||||
@@ -22,7 +24,7 @@ pub static COMMANDS: &[CommandSpec] = &[
|
||||
.icon("delete")
|
||||
.key("Delete")
|
||||
.params("features: [id or name] (a sketch takes its dependent features with it) → deleted: [ids]; failing?: [{feature, error}] (later features that worked and now fail)"),
|
||||
CommandSpec::new("timeline.rename", "Rename", rename).params("feature: id|name, name"),
|
||||
CommandSpec::new("timeline.rename", "Rename", rename).params("feature: id|name, name (references to it follow)"),
|
||||
CommandSpec::new("timeline.rollback", "Move Timeline Marker", rollback).params("position: number of features to keep active (omit = end)"),
|
||||
CommandSpec::new("timeline.roll_to", "Roll History Marker", rollback)
|
||||
.params("position: number of features to keep active (omit = end) | feature: id|name (the marker goes right after it)"),
|
||||
@@ -163,15 +165,25 @@ fn rename(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let id = feature_id(s, p.get("feature"), cmd)?;
|
||||
let name =
|
||||
str_(p, "name").map(str::trim).filter(|n| !n.is_empty() && n.len() <= 128).ok_or_else(|| bad(cmd, "`name` must be 1…128 characters"))?;
|
||||
let old = s.doc.feature(id).map(|f| f.name.clone()).ok_or_else(|| bad(cmd, "feature"))?;
|
||||
if s.doc.features.iter().any(|f| f.name == name && f.id != id) {
|
||||
return Err(bad(cmd, format!("another feature is called `{name}`")));
|
||||
}
|
||||
if s.doc.feature(id).is_some_and(|f| matches!(f.kind, FeatureKind::ConstructionPlane { .. })) {
|
||||
super::features::check_plane_name(name, cmd)?;
|
||||
}
|
||||
if let Some(f) = s.doc_mut().feature_mut(id) {
|
||||
if name == old {
|
||||
return Ok(json!({"feature": id, "name": name}));
|
||||
}
|
||||
let names = BTreeMap::from([(old.clone(), name.to_string())]);
|
||||
let sketches = BTreeMap::new();
|
||||
let doc = s.doc_mut();
|
||||
if let Some(f) = doc.feature_mut(id) {
|
||||
f.name = name.to_string();
|
||||
}
|
||||
for f in doc.features.iter_mut() {
|
||||
f.kind.remap_refs(&sketches, &names);
|
||||
}
|
||||
Ok(json!({"feature": id, "name": name}))
|
||||
}
|
||||
|
||||
|
||||
@@ -208,6 +208,25 @@ fn patterns_of_a_blind_hole_drill_every_copy() {
|
||||
assert!((c - 2.0 * one).abs() < 1e-3 * one, "{c} vs 2 × {one}");
|
||||
}
|
||||
|
||||
/// Feature patterns and mirrors reference features by name; a rename follows (#401).
|
||||
#[test]
|
||||
fn renaming_a_source_feature_follows_pattern_and_mirror_references() {
|
||||
let mut s = plate_with_hole();
|
||||
run(&mut s, "construct.plane.offset", json!({"base": "YZ", "offset": 0, "name": "Mirror plane"}));
|
||||
run(&mut s, "solid.pattern.rectangular", json!({"features": ["Hole1"], "dir1": [1, 0, 0], "count1": 2, "spacing1": 20, "name": "Copies"}));
|
||||
run(&mut s, "solid.mirror", json!({"features": ["Hole1"], "plane": "Mirror plane", "name": "Mirrored"}));
|
||||
let renamed = run(&mut s, "timeline.rename", json!({"feature": "Hole1", "name": "Single hole"}));
|
||||
assert_eq!(renamed["name"], "Single hole", "{renamed}");
|
||||
run(&mut s, "timeline.rename", json!({"feature": "Mirror plane", "name": "Renamed plane"}));
|
||||
for feature in ["Copies", "Mirrored"] {
|
||||
let inspected = run(&mut s, "timeline.inspect", json!({"feature": feature}));
|
||||
assert!(inspected["error"].is_null(), "{feature}: {inspected}");
|
||||
assert_eq!(inspected["definition"]["features"], json!(["Single hole"]), "{inspected}");
|
||||
}
|
||||
let inspected = run(&mut s, "timeline.inspect", json!({"feature": "Mirrored"}));
|
||||
assert_eq!(inspected["definition"]["plane"]["name"], "Renamed plane", "{inspected}");
|
||||
}
|
||||
|
||||
/// A plate with a `count1` × `count2` feature pattern of one through-hole cut: its volume and how
|
||||
/// long the pattern took. It runs on its own thread so a hang fails the test instead of CI.
|
||||
fn hole_pattern(count1: usize, count2: usize) -> (f64, std::time::Duration) {
|
||||
|
||||
Reference in new issue
Block a user