forked from 1b-agent/opencadstudio-upstream
fix(model): preserve solid history lifecycle
This commit is contained in:
1 parent
d2993fd054
commit
4ac10187c2
15 files changed
+292
-63
No files matched your search
+63
-32
@@ -107,11 +107,25 @@ impl OpenCADStudio {
|
||||
|
||||
/// Roll a hot grip back to its pre-drag image and remove every grip-owned
|
||||
/// overlay. Shared by Escape and drawing-space transitions.
|
||||
pub(super) fn capture_grip_history_originals(&mut self, i: usize, handles: &[Handle]) {
|
||||
if !self.grip_history_originals.is_empty() {
|
||||
return;
|
||||
}
|
||||
self.grip_history_originals = handles
|
||||
.iter()
|
||||
.filter_map(|&handle| {
|
||||
let objects = self.tabs[i].scene.solid_history_objects(handle);
|
||||
(!objects.is_empty()).then_some((handle, objects))
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
pub(super) fn cancel_active_grip_edit(&mut self) -> bool {
|
||||
let i = self.active_tab;
|
||||
let had_grip = self.tabs[i].active_grip.take().is_some()
|
||||
|| self.grip_add_provisional.is_some()
|
||||
|| !self.grip_preview_handles.is_empty();
|
||||
|| !self.grip_preview_handles.is_empty()
|
||||
|| !self.grip_history_originals.is_empty();
|
||||
if !had_grip {
|
||||
return false;
|
||||
}
|
||||
@@ -132,31 +146,62 @@ impl OpenCADStudio {
|
||||
|
||||
let handles = std::mem::take(&mut self.grip_preview_handles);
|
||||
let originals = std::mem::take(&mut self.grip_originals);
|
||||
let history_originals = std::mem::take(&mut self.grip_history_originals);
|
||||
let history_handles: rustc_hash::FxHashSet<_> = history_originals
|
||||
.iter()
|
||||
.map(|(handle, _)| *handle)
|
||||
.collect();
|
||||
for (_, objects) in history_originals {
|
||||
for (object_handle, object) in objects {
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.objects
|
||||
.insert(object_handle, object);
|
||||
}
|
||||
}
|
||||
let mut changed_handles: rustc_hash::FxHashSet<_> = handles.iter().copied().collect();
|
||||
for (handle, original) in originals {
|
||||
changed_handles.insert(handle);
|
||||
let current = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.get_entity(handle)
|
||||
.and_then(crate::entities::solid3d::point_of_reference)
|
||||
.map(|point| [point.x, point.y, point.z]);
|
||||
let target = crate::entities::solid3d::point_of_reference(&original)
|
||||
.map(|point| [point.x, point.y, point.z]);
|
||||
if let (Some(current), Some(target)) = (current, target) {
|
||||
self.tabs[i].scene.translate_solid_geometry(
|
||||
handle,
|
||||
[
|
||||
target[0] - current[0],
|
||||
target[1] - current[1],
|
||||
target[2] - current[2],
|
||||
],
|
||||
);
|
||||
if !history_handles.contains(&handle) {
|
||||
let current = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.get_entity(handle)
|
||||
.and_then(crate::entities::solid3d::point_of_reference)
|
||||
.map(|point| [point.x, point.y, point.z]);
|
||||
let target = crate::entities::solid3d::point_of_reference(&original)
|
||||
.map(|point| [point.x, point.y, point.z]);
|
||||
if let (Some(current), Some(target)) = (current, target) {
|
||||
self.tabs[i].scene.translate_solid_geometry(
|
||||
handle,
|
||||
[
|
||||
target[0] - current[0],
|
||||
target[1] - current[1],
|
||||
target[2] - current[2],
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
if let Some(entity) = self.tabs[i].scene.document.get_entity_mut(handle) {
|
||||
*entity = original;
|
||||
}
|
||||
}
|
||||
for handle in history_handles {
|
||||
let restored = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.solid_history_operation(handle)
|
||||
.cloned()
|
||||
.is_some_and(|operation| {
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.rebuild_solid_history(handle, operation)
|
||||
});
|
||||
if !restored {
|
||||
self.tabs[i].scene.reseed_derived_caches(handle);
|
||||
}
|
||||
}
|
||||
for &handle in &handles {
|
||||
self.tabs[i].scene.preview_hidden.remove(&handle);
|
||||
}
|
||||
@@ -962,13 +1007,6 @@ impl OpenCADStudio {
|
||||
// transform_entities — always delta-safe.
|
||||
let pending = self.begin_undo(i, label, handles.len(), true);
|
||||
self.tabs[i].scene.transform_entities(&handles, &transform);
|
||||
// ACIS solids render from a cached mesh, so a move/rotate/
|
||||
// scale/mirror needs the mesh re-tessellated from the now-moved
|
||||
// body — wire re-tessellation alone leaves the solid drawn at
|
||||
// its old spot. (#135)
|
||||
if self.tabs[i].scene.any_solid(&handles) {
|
||||
self.tabs[i].scene.refresh_meshes_for_handles(&handles);
|
||||
}
|
||||
self.tabs[i].dirty = true;
|
||||
self.tabs[i].scene.clear_preview_wire();
|
||||
self.tabs[i].active_cmd = None;
|
||||
@@ -986,9 +1024,6 @@ impl OpenCADStudio {
|
||||
let delta_safe = self.delta_copy_safe(i, &handles);
|
||||
let pending = self.begin_undo(i, label, handles.len(), delta_safe);
|
||||
let new_handles = self.tabs[i].scene.copy_entities(&handles, &transform);
|
||||
if self.tabs[i].scene.any_solid(&new_handles) {
|
||||
self.tabs[i].scene.refresh_meshes_for_handles(&new_handles);
|
||||
}
|
||||
self.tabs[i].dirty = true;
|
||||
self.tabs[i].scene.deselect_all();
|
||||
for h in new_handles {
|
||||
@@ -2689,7 +2724,6 @@ impl OpenCADStudio {
|
||||
let new_handle = self.tabs[i].scene.add_entity(s3d);
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.create_solid_history(new_handle, history);
|
||||
self.tabs[i].scene.register_solid_model(new_handle, solid);
|
||||
let _ = mesh;
|
||||
@@ -2752,7 +2786,6 @@ impl OpenCADStudio {
|
||||
let new_handle = self.tabs[i].scene.add_entity(s3d);
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.create_solid_history(new_handle, history);
|
||||
self.tabs[i].scene.register_solid_model(new_handle, solid);
|
||||
let _ = mesh;
|
||||
@@ -2813,7 +2846,6 @@ impl OpenCADStudio {
|
||||
if let Some(history) = history {
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.create_solid_history(new_handle, history);
|
||||
}
|
||||
for mesh in &mut set.lods {
|
||||
@@ -2856,7 +2888,6 @@ impl OpenCADStudio {
|
||||
let new_handle = self.tabs[i].scene.add_entity(entity);
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.create_solid_history(new_handle, history);
|
||||
for mesh in &mut set.lods {
|
||||
mesh.name = format!("{}", new_handle.value());
|
||||
|
||||
+14
-2
@@ -189,6 +189,7 @@ impl OpenCADStudio {
|
||||
i: usize,
|
||||
label: impl Into<String>,
|
||||
before: Vec<(Handle, Arc<EntityType>)>,
|
||||
object_before: Vec<(Handle, acadrust::objects::ObjectType)>,
|
||||
dirty_before: bool,
|
||||
) {
|
||||
self.finish_pending_history(i);
|
||||
@@ -199,7 +200,18 @@ impl OpenCADStudio {
|
||||
Some((handle, Some(original), Some(after)))
|
||||
})
|
||||
.collect();
|
||||
if entities.is_empty() {
|
||||
let objects: Vec<_> = object_before
|
||||
.into_iter()
|
||||
.filter_map(|(handle, before)| {
|
||||
let after = self.tabs[i].scene.document.objects.get(&handle).cloned();
|
||||
(Some(before.clone()) != after).then_some(ObjectEntryDelta {
|
||||
handle,
|
||||
before: Some(before),
|
||||
after,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
if entities.is_empty() && objects.is_empty() {
|
||||
return;
|
||||
}
|
||||
let selected: Vec<Handle> = self.tabs[i].scene.selected.iter().copied().collect();
|
||||
@@ -211,7 +223,7 @@ impl OpenCADStudio {
|
||||
selected_after: selected,
|
||||
dirty_before,
|
||||
dirty_after: true,
|
||||
structure: None,
|
||||
structure: (!objects.is_empty()).then_some(StructureSnapshot::Objects(objects)),
|
||||
label: label.into(),
|
||||
};
|
||||
self.push_undo_entry(i, HistorySnapshot::Delta(delta));
|
||||
|
||||
@@ -543,6 +543,11 @@ pub(super) struct OpenCADStudio {
|
||||
/// Snapshots of edited entities taken at the start of a grip drag. The drag
|
||||
/// mutates the document live, so Escape restores this group atomically.
|
||||
grip_originals: Vec<(acadrust::Handle, acadrust::EntityType)>,
|
||||
/// Solid-history objects paired with their owning entity before a grip drag.
|
||||
grip_history_originals: Vec<(
|
||||
acadrust::Handle,
|
||||
Vec<(acadrust::Handle, acadrust::objects::ObjectType)>,
|
||||
)>,
|
||||
/// Document dirty state before the live grip mutation began.
|
||||
grip_dirty_before: Option<bool>,
|
||||
/// Frozen wire geometry of the entities being grip-edited.
|
||||
@@ -3136,6 +3141,7 @@ impl OpenCADStudio {
|
||||
grip_preview_handles: Vec::new(),
|
||||
hover_dwell: None,
|
||||
grip_originals: Vec::new(),
|
||||
grip_history_originals: Vec::new(),
|
||||
grip_dirty_before: None,
|
||||
grip_snap_wires: Vec::new(),
|
||||
grip_text_verts: Vec::new(),
|
||||
|
||||
@@ -31,10 +31,7 @@ impl super::OpenCADStudio {
|
||||
let Some(handle) = self.commit_entity_handle(entity) else {
|
||||
return Handle::NULL;
|
||||
};
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.create_solid_history(handle, history);
|
||||
self.tabs[i].scene.create_solid_history(handle, history);
|
||||
self.tabs[i].scene.register_solid_model(handle, solid);
|
||||
handle
|
||||
}
|
||||
|
||||
@@ -369,6 +369,8 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||
.collect();
|
||||
}
|
||||
|
||||
self.capture_grip_history_originals(i, &edited_handles);
|
||||
|
||||
self.grip_preview_handles = edited_handles;
|
||||
}
|
||||
let delta = target - grip.last_world;
|
||||
|
||||
@@ -1070,6 +1070,7 @@ impl OpenCADStudio {
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
self.capture_grip_history_originals(i, &edited_handles);
|
||||
for &handle in &edited_handles {
|
||||
if !self.tabs[i].scene.meshes.contains_key(&handle) {
|
||||
self.tabs[i].scene.preview_hidden.insert(handle);
|
||||
@@ -2733,6 +2734,7 @@ impl OpenCADStudio {
|
||||
// edited entity back into the resident tessellation.
|
||||
let handles = std::mem::take(&mut self.grip_preview_handles);
|
||||
let originals = std::mem::take(&mut self.grip_originals);
|
||||
let history_originals = std::mem::take(&mut self.grip_history_originals);
|
||||
let dirty_before = self.grip_dirty_before.take().unwrap_or(self.tabs[i].dirty);
|
||||
if !handles.is_empty() {
|
||||
if !originals.is_empty() {
|
||||
@@ -2743,6 +2745,10 @@ impl OpenCADStudio {
|
||||
.into_iter()
|
||||
.map(|(handle, entity)| (handle, std::sync::Arc::new(entity)))
|
||||
.collect(),
|
||||
history_originals
|
||||
.into_iter()
|
||||
.flat_map(|(_, objects)| objects)
|
||||
.collect(),
|
||||
dirty_before,
|
||||
);
|
||||
self.tabs[i].dirty = true;
|
||||
|
||||
Reference in new issue
Block a user