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 <noreply@anthropic.com>
This commit is contained in:
parent
aea1fd2caa
commit
1490fe4ea9
9 changed files with 761 additions and 26 deletions
|
|
@ -696,7 +696,8 @@ impl OpenCADStudio {
|
|||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||
} else {
|
||||
let n = handles.len();
|
||||
self.push_undo_snapshot(i, "ERASE");
|
||||
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()
|
||||
|
|
@ -707,6 +708,9 @@ impl OpenCADStudio {
|
|||
self.refresh_properties();
|
||||
self.command_line
|
||||
.push_output(&format!("{n} object(s) erased."));
|
||||
if let Some(pd) = pending {
|
||||
self.commit_undo_delta(i, pd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue