feat(annotation): integrate object contexts

This commit is contained in:
Hakan Seven committed 2026-08-02 12:00:15 +03:00
1 parent f39481ac49
commit e03e091b3c
37 files changed
+2823 -584

No files matched your search

+122 -2
View File
@@ -3155,7 +3155,11 @@ impl OpenCADStudio {
self.tabs[i].scene.add_entity_clone(entity)
})
.collect();
self.merge_clipboard_ext_objects(i, &by_index);
let annotation_delta = match translate {
Some(crate::command::EntityTransform::Translate(delta)) => delta,
_ => glam::DVec3::ZERO,
};
self.merge_clipboard_ext_objects(i, &by_index, annotation_delta);
// Recreate any group whose whole membership was copied, so a pasted
// group stays grouped — cross-drawing too, since the groups were
// snapshotted into the clipboard at copy time. `by_index` is aligned
@@ -3187,7 +3191,12 @@ impl OpenCADStudio {
/// references, and re-pointing the pasted entity's `xdictionary_handle` at
/// the new root. `by_index` is the paste's new entity handles, aligned with
/// the clipboard order (NULL where the add failed). No-op without captures.
pub(super) fn merge_clipboard_ext_objects(&mut self, i: usize, by_index: &[Handle]) {
pub(super) fn merge_clipboard_ext_objects(
&mut self,
i: usize,
by_index: &[Handle],
annotation_delta: glam::DVec3,
) {
if self.clipboard_deps.ext_objects.is_empty() {
return;
}
@@ -3204,6 +3213,11 @@ impl OpenCADStudio {
if let Some(e) = doc.get_entity_mut(new_entity) {
e.common_mut().xdictionary_handle = Some(new_root);
}
crate::scene::annotative::translate_annotation_contexts(
doc,
new_entity,
annotation_delta,
);
}
}
// The wires were tessellated before the filters existed; refresh only
@@ -3270,6 +3284,10 @@ fn recreate_ext_subtree(
if let Some(eh) = entity_handle {
remap.insert(cap.src_entity_handle, eh);
}
for (old, scale) in &cap.annotation_scales {
let target = crate::scene::annotative::ensure_scale_object(doc, scale);
remap.insert(*old, target);
}
for (old, _) in &cap.objects {
remap.insert(*old, doc.allocate_handle());
}
@@ -3282,6 +3300,50 @@ fn recreate_ext_subtree(
remap.get(&cap.root).copied()
}
/// Replace references to a clipboard entity inside one recreated extension
/// dictionary graph after its final block-owned handle becomes known.
pub(crate) fn remap_ext_subtree_reference(
doc: &mut acadrust::CadDocument,
root: Handle,
source_entity: Handle,
target_entity: Handle,
) {
use acadrust::objects::ObjectType;
use rustc_hash::FxHashSet;
use std::collections::HashMap;
let remap = HashMap::from([(source_entity, target_entity)]);
let mut seen = FxHashSet::default();
let mut pending = vec![root];
while let Some(handle) = pending.pop() {
if handle.is_null() || !seen.insert(handle) {
continue;
}
let children = match doc.objects.get(&handle) {
Some(ObjectType::Dictionary(dictionary)) => {
let mut children: Vec<_> =
dictionary.entries.iter().map(|(_, child)| *child).collect();
if let Some(extension) = dictionary.xdictionary_handle {
children.push(extension);
}
children
}
Some(ObjectType::DictionaryWithDefault(dictionary)) => {
let mut children: Vec<_> =
dictionary.entries.iter().map(|(_, child)| *child).collect();
children.push(dictionary.default_handle);
children
}
_ => Vec::new(),
};
pending.extend(children);
if let Some(mut object) = doc.objects.remove(&handle) {
remap_object(&mut object, handle, &remap);
doc.objects.insert(handle, object);
}
}
}
/// Rewrite a cloned extension-dictionary object onto fresh handles: set its own
/// handle to `new_handle` and remap its owner and any handle references it holds
/// through `remap` (a handle still in the source space stays unchanged, which is
@@ -3326,6 +3388,11 @@ fn remap_object(
ObjectType::XRecord(x) => {
x.handle = new_handle;
x.owner = map(x.owner);
for entry in &mut x.entries {
if let acadrust::objects::XRecordValue::Handle(handle) = &mut entry.value {
*handle = map(*handle);
}
}
}
ObjectType::Group(g) => {
g.handle = new_handle;
@@ -3334,6 +3401,59 @@ fn remap_object(
*h = map(*h);
}
}
ObjectType::ObjectContextData(context) => {
context.handle = new_handle;
context.owner_handle = map(context.owner_handle);
for reactor in &mut context.reactors {
*reactor = map(*reactor);
}
if let Some(dictionary) = &mut context.xdictionary_handle {
*dictionary = map(*dictionary);
}
context.scale = map(context.scale);
match &mut context.kind {
acadrust::objects::ObjectContextKind::Dim(dimension) => {
dimension.block = map(dimension.block);
}
acadrust::objects::ObjectContextKind::HatchView(hatch) => {
hatch.view = map(hatch.view);
}
acadrust::objects::ObjectContextKind::MTextAttribute(attribute) => {
if let Some(embedded) = &mut attribute.context {
embedded.owner_handle = map(embedded.owner_handle);
for reactor in &mut embedded.reactors {
*reactor = map(*reactor);
}
if let Some(dictionary) = &mut embedded.xdictionary_handle {
*dictionary = map(*dictionary);
}
embedded.scale = map(embedded.scale);
}
}
acadrust::objects::ObjectContextKind::MLeader(mleader) => {
if let Some(handle) = &mut mleader.text_style_handle {
*handle = map(*handle);
}
if let Some(handle) = &mut mleader.block_content_handle {
*handle = map(*handle);
}
if let Some(handle) = &mut mleader.scale_handle {
*handle = map(*handle);
}
for root in &mut mleader.leader_roots {
for line in &mut root.lines {
if let Some(handle) = &mut line.line_type_handle {
*handle = map(*handle);
}
if let Some(handle) = &mut line.arrowhead_handle {
*handle = map(*handle);
}
}
}
}
_ => {}
}
}
// Other leaf object kinds don't appear in an entity xdictionary; if one
// does, it's inserted with the fresh handle below via the caller's key,
// but its internal owner is left as-is (best effort).