perf(scene): grip drag previews the entity instead of re-tessellating
Phase 2.2 (partial). Dragging an entity's grip called scene.apply_grip on every mouse move, which bumped geometry and re-tessellated the whole model per move (plus an O(N) clone of every wire to exclude the edited one from snapping). On a large drawing that's the 30ms->400ms spike, per frame, for the duration of the drag. apply_grip no longer bumps. The drag flow now: - first move: hide the edited entity from the base tessellation (one re-tess) and record it in grip_preview_handle; - each move: apply the grip to the doc (no bump) and show the entity as a one-entity overlay preview (set_preview_wires) — the base is a cache hit; - commit on release / Esc: un-hide, drop the preview, one final re-tess. Because the edited entity is hidden, it's already absent from hit_test_wires, so snapping runs against the cached set directly — no per-move clone and no self-snap. Two re-tessellations per drag instead of one per move. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
ccb9bb832d
commit
8df9bef6f8
4 changed files with 57 additions and 8 deletions
|
|
@ -1313,6 +1313,14 @@ impl OpenCADStudio {
|
|||
self.tabs[i].scene.bump_geometry();
|
||||
self.refresh_selected_grips();
|
||||
}
|
||||
// Commit/cleanup an in-progress grip drag: un-hide the
|
||||
// edited entity, re-tessellate once, drop the preview.
|
||||
if let Some(h) = self.grip_preview_handle.take() {
|
||||
let i = self.active_tab;
|
||||
self.tabs[i].scene.hidden.remove(&h);
|
||||
self.tabs[i].scene.clear_preview_wire();
|
||||
self.tabs[i].scene.bump_geometry();
|
||||
}
|
||||
self.tabs[self.active_tab].snap_result = None;
|
||||
self.refresh_properties();
|
||||
return Task::none();
|
||||
|
|
@ -2100,14 +2108,24 @@ impl OpenCADStudio {
|
|||
drop(cam);
|
||||
let raw = self.tabs[i].scene.paper_to_model(raw_paper);
|
||||
|
||||
let edited_name = grip.handle.value().to_string();
|
||||
// First move of this drag: hide the edited entity from the
|
||||
// base tessellation (one re-tess) so subsequent moves only
|
||||
// refresh a cheap overlay preview instead of re-tessellating
|
||||
// the whole model on every move.
|
||||
if self.grip_preview_handle != Some(grip.handle) {
|
||||
if let Some(prev) = self.grip_preview_handle.take() {
|
||||
self.tabs[i].scene.hidden.remove(&prev);
|
||||
}
|
||||
self.tabs[i].scene.hidden.insert(grip.handle);
|
||||
self.tabs[i].scene.bump_geometry();
|
||||
self.grip_preview_handle = Some(grip.handle);
|
||||
}
|
||||
|
||||
// The edited entity is hidden, so it's already absent from
|
||||
// `hit_test_wires` — snap against the set directly, no clone
|
||||
// and no self-snap.
|
||||
let all_wires = self.tabs[i].scene.hit_test_wires();
|
||||
let snap_wires: Vec<_> = all_wires
|
||||
.iter()
|
||||
.filter(|w| w.name != edited_name)
|
||||
.cloned()
|
||||
.collect();
|
||||
let snap_hit = self.snapper.snap(raw, p, &snap_wires, vp_mat, bounds);
|
||||
let snap_hit = self.snapper.snap(raw, p, &all_wires[..], vp_mat, bounds);
|
||||
let mut snapped = snap_hit.map(|s| s.world).unwrap_or(raw);
|
||||
self.tabs[i].snap_result = snap_hit;
|
||||
if let Some(s) = self.tabs[i].snap_result.as_mut() {
|
||||
|
|
@ -2142,6 +2160,10 @@ impl OpenCADStudio {
|
|||
.apply_grip(grip.handle, grip.grip_id, apply);
|
||||
self.tabs[i].dirty = true;
|
||||
self.tabs[i].active_grip.as_mut().unwrap().last_world = snapped;
|
||||
// Overlay the moved entity (hidden from the base) — no base
|
||||
// re-tessellation, just a one-entity preview tessellation.
|
||||
let preview = self.tabs[i].scene.wire_models_for(&[grip.handle]);
|
||||
self.tabs[i].scene.set_preview_wires(preview);
|
||||
self.refresh_selected_grips();
|
||||
self.refresh_properties();
|
||||
return Task::none();
|
||||
|
|
@ -2611,6 +2633,14 @@ impl OpenCADStudio {
|
|||
return Task::none();
|
||||
}
|
||||
self.tabs[i].active_grip = None;
|
||||
// Commit the grip drag: un-hide the edited entity and
|
||||
// re-tessellate the base once with its final geometry,
|
||||
// dropping the overlay preview.
|
||||
if let Some(h) = self.grip_preview_handle.take() {
|
||||
self.tabs[i].scene.hidden.remove(&h);
|
||||
self.tabs[i].scene.clear_preview_wire();
|
||||
self.tabs[i].scene.bump_geometry();
|
||||
}
|
||||
// Placement confirmed — keep the just-added leader.
|
||||
self.grip_add_provisional = None;
|
||||
self.tabs[i].snap_result = None;
|
||||
|
|
|
|||
Loading…
Reference in a new issue