forked from 1b-agent/opencadstudio-upstream
fix(hatch): keep the association when TRIM splits a boundary
Splitting a boundary object erased it and added the pieces, so the hatch lost that source and rebuilt from what was left, often the whole circle. The pieces now take over the old object's association before it is erased. Fixes #1572 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fd01a36ad3
commit
6b5c2758e1
3 files changed
+58
-4
No files matched your search
@@ -170,11 +170,20 @@ impl OpenCADStudio {
|
||||
}
|
||||
let label = self.history_label_from_active_cmd(i, "TRIM");
|
||||
self.push_undo_snapshot(i, label);
|
||||
self.tabs[i].scene.erase_entities(&[handle]);
|
||||
// A replacement reusing the original handle must land after the erase;
|
||||
// fresh pieces take over its hatch associations before it goes.
|
||||
let reuses_handle = new_entities.iter().any(|e| e.common().handle == handle);
|
||||
if reuses_handle {
|
||||
self.tabs[i].scene.erase_entities(&[handle]);
|
||||
}
|
||||
let new_handles: Vec<codec::Handle> = new_entities
|
||||
.into_iter()
|
||||
.map(|e| self.tabs[i].scene.add_entity(e))
|
||||
.collect();
|
||||
if !reuses_handle {
|
||||
self.tabs[i].scene.split_hatch_source(handle, &new_handles);
|
||||
self.tabs[i].scene.erase_entities(&[handle]);
|
||||
}
|
||||
// Rebuild replaced dimensions from edited data.
|
||||
for &nh in &new_handles {
|
||||
if matches!(
|
||||
|
||||
@@ -381,8 +381,7 @@ impl OpenCADStudio {
|
||||
.document
|
||||
.get_entity(handle)
|
||||
.map(|entity| entity.common().owner_handle);
|
||||
self.tabs[tab].scene.erase_entities(&[handle]);
|
||||
entities
|
||||
let pieces: Vec<Handle> = entities
|
||||
.into_iter()
|
||||
.map(|mut entity| {
|
||||
entity.common_mut().handle = Handle::NULL;
|
||||
@@ -391,7 +390,11 @@ impl OpenCADStudio {
|
||||
}
|
||||
self.tabs[tab].scene.add_entity(entity)
|
||||
})
|
||||
.collect()
|
||||
.collect();
|
||||
// Hand the hatch associations over before the original goes.
|
||||
self.tabs[tab].scene.split_hatch_source(handle, &pieces);
|
||||
self.tabs[tab].scene.erase_entities(&[handle]);
|
||||
pieces
|
||||
};
|
||||
for &updated in &handles {
|
||||
if matches!(
|
||||
|
||||
@@ -816,6 +816,48 @@ impl Scene {
|
||||
true
|
||||
}
|
||||
|
||||
/// A boundary source split into pieces (TRIM, BREAK): every associative
|
||||
/// hatch bounded by `old` is bounded by all of `pieces` instead, so the
|
||||
/// region survives `old` being erased.
|
||||
pub(crate) fn split_hatch_source(&mut self, old: Handle, pieces: &[Handle]) {
|
||||
if pieces.is_empty() {
|
||||
return;
|
||||
}
|
||||
let dependents = self.associative_hatch_dependents(&std::iter::once(old).collect());
|
||||
if dependents.is_empty() {
|
||||
return;
|
||||
}
|
||||
for &hatch_handle in &dependents {
|
||||
if self.is_recording_undo() {
|
||||
let before = self.document.get_entity_arc(hatch_handle);
|
||||
self.record_undo_before(hatch_handle, before);
|
||||
}
|
||||
let Some(EntityType::Hatch(hatch)) = self.document.get_entity_mut(hatch_handle) else {
|
||||
continue;
|
||||
};
|
||||
for path in &mut hatch.paths {
|
||||
if !path.boundary_handles.contains(&old) {
|
||||
continue;
|
||||
}
|
||||
path.boundary_handles.retain(|source| *source != old);
|
||||
for &piece in pieces {
|
||||
if !path.boundary_handles.contains(&piece) {
|
||||
path.boundary_handles.push(piece);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.associative_hatch_source_cache.borrow_mut().take();
|
||||
let changes: Vec<_> = pieces
|
||||
.iter()
|
||||
.map(|&piece| (piece, ChangeKind::Modified))
|
||||
.collect();
|
||||
let refreshed = self.refresh_associative_hatches(&changes);
|
||||
if !refreshed.is_empty() {
|
||||
self.bump_entities(&refreshed);
|
||||
}
|
||||
}
|
||||
|
||||
fn associative_hatch_dependents(
|
||||
&self,
|
||||
changed: &rustc_hash::FxHashSet<Handle>,
|
||||
|
||||
Reference in new issue
Block a user