fix(annotative): MOVE/ROTATE/SCALE carry every scale representation; copies get their own

Only the displayed annotation context followed a transform, so other
scales reappeared at the old place, and a copied annotative entity other
than a LEADER/MTEXT pair shared the source's context tree.

Fixes #700

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 5.5 committed 2026-09-28 19:27:31 +03:00
1 parent 746edaa32b
commit b66b5100bd
2 files changed
+27 -28

No files matched your search

+19 -5
View File
@@ -1335,6 +1335,21 @@ pub fn translate_annotation_contexts(
doc: &mut CadDocument, doc: &mut CadDocument,
entity_handle: Handle, entity_handle: Handle,
delta: glam::DVec3, delta: glam::DVec3,
) -> bool {
transform_annotation_contexts(
doc,
entity_handle,
&crate::command::EntityTransform::Translate(delta),
)
}
/// Carry every annotation-scale representation of an entity through `t`, not
/// only the displayed one — otherwise MOVE/ROTATE/SCALE leave the other scales
/// where they were and they reappear there on a scale switch. (#700)
pub fn transform_annotation_contexts(
doc: &mut CadDocument,
entity_handle: Handle,
t: &crate::command::EntityTransform,
) -> bool { ) -> bool {
let Some(base_entity) = doc.get_entity(entity_handle).cloned() else { let Some(base_entity) = doc.get_entity(entity_handle).cloned() else {
return false; return false;
@@ -1361,14 +1376,13 @@ pub fn translate_annotation_contexts(
let mut changed = false; let mut changed = false;
for (_, scale) in leaves { for (_, scale) in leaves {
let mut placed = entity_for_annotation_context(doc, &base_entity, Some(scale)).into_owned(); let mut placed = entity_for_annotation_context(doc, &base_entity, Some(scale)).into_owned();
crate::scene::view::dispatch::apply_transform( crate::scene::view::dispatch::apply_transform(&mut placed, t);
&mut placed,
&crate::command::EntityTransform::Translate(delta),
);
// The entity translator keeps the compatibility break list in sync, // The entity translator keeps the compatibility break list in sync,
// while the complete per-segment list is a separate persisted field. // while the complete per-segment list is a separate persisted field.
if let EntityType::MultiLeader(mleader) = &mut placed { if let (EntityType::MultiLeader(mleader), crate::command::EntityTransform::Translate(delta)) =
(&mut placed, t)
{
let offset = Vector3::new(delta.x, delta.y, delta.z); let offset = Vector3::new(delta.x, delta.y, delta.z);
for root in &mut mleader.context.leader_roots { for root in &mut mleader.context.leader_roots {
for line in &mut root.lines { for line in &mut root.lines {
+8 -23
View File
@@ -342,7 +342,9 @@ impl Scene {
} }
} }
for &h in handles { for &h in handles {
if self.sync_displayed_annotation_context(h) { let others =
crate::scene::annotative::transform_annotation_contexts(&mut self.document, h, t);
if self.sync_displayed_annotation_context(h) || others {
self.poison_undo_recording(); self.poison_undo_recording();
} }
} }
@@ -649,23 +651,6 @@ impl Scene {
pub fn copy_entities(&mut self, handles: &[Handle], t: &EntityTransform) -> Vec<Handle> { pub fn copy_entities(&mut self, handles: &[Handle], t: &EntityTransform) -> Vec<Handle> {
let copy_handles = self.handles_expanded_for_leader_annotations(handles); let copy_handles = self.handles_expanded_for_leader_annotations(handles);
// LEADER + attached MTEXT are a logical pair. Their entity clones must not
// retain the source extension dictionary, otherwise both copies share the
// same annotation-context objects.
let leader_pair_handles: Vec<Handle> = copy_handles
.iter()
.flat_map(|&handle| {
let annotation = match self.document.get_entity(handle) {
Some(EntityType::Leader(leader)) if !leader.annotation_handle.is_null() => {
Some(leader.annotation_handle)
}
_ => None,
};
std::iter::once(handle).chain(annotation)
})
.collect();
// Objects on a locked layer can be selected but not copied. // Objects on a locked layer can be selected but not copied.
let clones: Vec<(Handle, EntityType, Vec<Handle>)> = copy_handles let clones: Vec<(Handle, EntityType, Vec<Handle>)> = copy_handles
.iter() .iter()
@@ -673,11 +658,11 @@ impl Scene {
.filter_map(|&h| { .filter_map(|&h| {
let entity = self.document.get_entity(h)?.clone(); let entity = self.document.get_entity(h)?.clone();
let annotation_scales = if leader_pair_handles.contains(&h) { // Any annotative entity (not only a LEADER/MTEXT pair) needs
crate::scene::annotative::annotation_scale_handles_for_entity(&self.document, h) // its own context tree; a shared one ties the copy to the
} else { // source. (#700)
Vec::new() let annotation_scales =
}; crate::scene::annotative::annotation_scale_handles_for_entity(&self.document, h);
Some((h, entity, annotation_scales)) Some((h, entity, annotation_scales))
}) })