forked from 1b-agent/opencadstudio-upstream
perf(undo): record entity deltas instead of cloning the whole document
Frequent entity-only edits (MOVE/ROTATE/SCALE/MIRROR/ALIGN, COPY/ARRAY, draw-add, ERASE) captured a full ~800k-entity CadDocument clone (~46ms) on every edit — the last big per-edit cost after the incremental-render work. They now record only the touched entities' before/after images: - Scene self-records through the five mutation primitives when a recording is open (UndoRecording + is_recording_undo guard, zero cost off-path). - apply_entity_delta installs one side per handle (in-place / re-insert with the original handle / remove) plus a per-handle cache reseed, then a single bump_entities so the incremental caches patch instead of a full re-tess — undo/redo of a small edit is fast too, not just the edit. - HistorySnapshot is now Full | Delta; a Delta is self-inverting and moves between the undo/redo stacks without re-capturing. Commands that touch non-entity state (new layer, group, *D block, viewport) fall back to the full snapshot via per-command predicates, and a poison flag debug-asserts any mis-scope. remove_entity leaves the handle dangling in its owner block record's entity_handles (harmless on its own, a lookup miss is skipped), so a re-insert strips stale entries first — otherwise add_entity would list the handle twice and the DXF/DWG writer would emit the entity twice. Round-trip tests cover transform/erase/add/copy (do->undo->redo equality, handle preservation, no duplication). Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
aea1fd2caa
commit
1490fe4ea9
9 files changed
+761
-26
No files matched your search
@@ -2047,7 +2047,10 @@ impl OpenCADStudio {
|
||||
self.tabs[i].scene.selection.borrow_mut().context_menu = None;
|
||||
let handles: Vec<_> = self.tabs[i].scene.selected.iter().cloned().collect();
|
||||
if !handles.is_empty() {
|
||||
self.push_undo_snapshot(i, "ERASE");
|
||||
// Erase is delta-safe unless a target is in a group (group
|
||||
// cleanup rewrites document.objects).
|
||||
let delta_safe = self.delta_erase_safe(i, &handles);
|
||||
let pending = self.begin_undo(i, "ERASE", handles.len(), delta_safe);
|
||||
// Stash the erased entities so OOPS can restore them.
|
||||
self.oops_cache = handles
|
||||
.iter()
|
||||
@@ -2056,6 +2059,9 @@ impl OpenCADStudio {
|
||||
self.tabs[i].scene.erase_entities(&handles);
|
||||
self.tabs[i].dirty = true;
|
||||
self.refresh_properties();
|
||||
if let Some(pd) = pending {
|
||||
self.commit_undo_delta(i, pd);
|
||||
}
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user