diff --git a/src/scene/annotative.rs b/src/scene/annotative.rs index 9d0ecd461..bb565c98f 100644 --- a/src/scene/annotative.rs +++ b/src/scene/annotative.rs @@ -1335,6 +1335,21 @@ pub fn translate_annotation_contexts( doc: &mut CadDocument, entity_handle: Handle, 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 { let Some(base_entity) = doc.get_entity(entity_handle).cloned() else { return false; @@ -1361,14 +1376,13 @@ pub fn translate_annotation_contexts( let mut changed = false; for (_, scale) in leaves { let mut placed = entity_for_annotation_context(doc, &base_entity, Some(scale)).into_owned(); - crate::scene::view::dispatch::apply_transform( - &mut placed, - &crate::command::EntityTransform::Translate(delta), - ); + crate::scene::view::dispatch::apply_transform(&mut placed, t); // The entity translator keeps the compatibility break list in sync, // 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); for root in &mut mleader.context.leader_roots { for line in &mut root.lines { diff --git a/src/scene/modify.rs b/src/scene/modify.rs index 1f33bc604..632493ad5 100644 --- a/src/scene/modify.rs +++ b/src/scene/modify.rs @@ -342,7 +342,9 @@ impl Scene { } } 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(); } } @@ -649,23 +651,6 @@ impl Scene { pub fn copy_entities(&mut self, handles: &[Handle], t: &EntityTransform) -> Vec { 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 = 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. let clones: Vec<(Handle, EntityType, Vec)> = copy_handles .iter() @@ -673,11 +658,11 @@ impl Scene { .filter_map(|&h| { let entity = self.document.get_entity(h)?.clone(); - let annotation_scales = if leader_pair_handles.contains(&h) { - crate::scene::annotative::annotation_scale_handles_for_entity(&self.document, h) - } else { - Vec::new() - }; + // Any annotative entity (not only a LEADER/MTEXT pair) needs + // its own context tree; a shared one ties the copy to the + // source. (#700) + let annotation_scales = + crate::scene::annotative::annotation_scale_handles_for_entity(&self.document, h); Some((h, entity, annotation_scales)) })