forked from 1b-agent/opencadstudio-upstream
fix(blocks): a copied dynamic reference changes state on its own (#1818)
COPY cloned the reference's AcDbRepData without its handle, owner or representation index, and both references kept sharing one evaluated *U block, so a state picked on either changed both. The clone now names itself and is indexed; a state change gives a reference whose *U other references share its own copy first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b947f99a87
commit
98cce048c7
3 files changed
+49
-26
No files matched your search
+5
-17
@@ -186,19 +186,11 @@ impl OpenCADStudio {
|
||||
return;
|
||||
}
|
||||
|
||||
// A reference to the definition itself gets an evaluated copy first,
|
||||
// so the state picked for it leaves the definition alone.
|
||||
let direct = {
|
||||
let doc = &self.tabs[i].scene.document;
|
||||
matches!(doc.get_entity(insert_handle), Some(EntityType::Insert(ins))
|
||||
if doc.block_records.get(&ins.block_name).is_some_and(|record| {
|
||||
crate::scene::block_visibility::parameter(doc, record.handle).is_some()
|
||||
}))
|
||||
};
|
||||
if direct {
|
||||
self.push_undo_snapshot(i, "Visibility State");
|
||||
self.tabs[i].scene.visibility_representation(insert_handle);
|
||||
}
|
||||
// A reference to the definition itself, or to a copy other
|
||||
// references share, gets its own evaluated copy first, so the state
|
||||
// picked for it leaves the definition and the others alone.
|
||||
self.push_undo_snapshot(i, "Visibility State");
|
||||
self.tabs[i].scene.visibility_representation(insert_handle);
|
||||
|
||||
// Resolve everything against an immutable borrow first.
|
||||
let mapping = {
|
||||
@@ -216,10 +208,6 @@ impl OpenCADStudio {
|
||||
return;
|
||||
};
|
||||
|
||||
if !direct {
|
||||
self.push_undo_snapshot(i, "Visibility State");
|
||||
}
|
||||
|
||||
// Apply invisibility flags to the anonymous block's members.
|
||||
{
|
||||
let doc = &mut self.tabs[i].scene.document;
|
||||
|
||||
@@ -464,18 +464,31 @@ pub fn finish_editing(document: &mut CadDocument, definition: Handle) {
|
||||
impl Scene {
|
||||
/// Give a reference to a dynamic definition its own evaluated copy, so a
|
||||
/// state picked for it leaves the definition and the other references
|
||||
/// alone. Returns the copy's name; a reference already showing a copy,
|
||||
/// or not dynamic, keeps its block and gets `None`.
|
||||
/// alone. A copy shared with other references (after COPY) is copied
|
||||
/// again with its current state. Returns the copy's name; a reference
|
||||
/// already showing its own copy, or not dynamic, gets `None`.
|
||||
pub fn visibility_representation(&mut self, insert: Handle) -> Option<String> {
|
||||
let Some(EntityType::Insert(reference)) = self.document.get_entity(insert) else {
|
||||
return None;
|
||||
};
|
||||
let definition = self
|
||||
let source = self
|
||||
.document
|
||||
.block_records
|
||||
.get(&reference.block_name)?
|
||||
.handle;
|
||||
let definition = self.document.dynamic_definition_for_insert(insert)?;
|
||||
parameter(&self.document, definition)?;
|
||||
let direct = source == definition;
|
||||
let shared = || {
|
||||
self.document.entities().any(|entity| {
|
||||
matches!(entity, EntityType::Insert(other)
|
||||
if other.common.handle != insert
|
||||
&& other.block_name == reference.block_name)
|
||||
})
|
||||
};
|
||||
if !direct && !shared() {
|
||||
return None;
|
||||
}
|
||||
let name = {
|
||||
let next = self
|
||||
.document
|
||||
@@ -490,7 +503,7 @@ impl Scene {
|
||||
.document
|
||||
.block_records
|
||||
.iter()
|
||||
.find(|record| record.handle == definition)
|
||||
.find(|record| record.handle == source)
|
||||
.and_then(
|
||||
|record| match self.document.get_entity(record.block_entity_handle) {
|
||||
Some(EntityType::Block(block)) => Some(block.base_point),
|
||||
@@ -498,7 +511,7 @@ impl Scene {
|
||||
},
|
||||
)
|
||||
.unwrap_or(Vector3::ZERO);
|
||||
let originals = members(&self.document, definition);
|
||||
let originals = members(&self.document, source);
|
||||
let copies: Vec<EntityType> = originals
|
||||
.iter()
|
||||
.filter_map(|handle| self.document.get_entity(*handle).cloned())
|
||||
@@ -518,6 +531,11 @@ impl Scene {
|
||||
tag.add_value(XDataValue::Handle(definition));
|
||||
self.document.set_object_xdata_record(copy, tag);
|
||||
self.document.ensure_extension_dictionary(copy);
|
||||
if !direct {
|
||||
// Already linked to its definition; only its block changes.
|
||||
self.repoint_representation(insert, source, copy, &name);
|
||||
return Some(name);
|
||||
}
|
||||
|
||||
// The reference: AcDbBlockRepresentation / AcDbRepData names the
|
||||
// definition it was evaluated from.
|
||||
@@ -550,18 +568,22 @@ impl Scene {
|
||||
.objects
|
||||
.insert(data, ObjectType::DynamicBlock(link));
|
||||
self.document.block_representations.insert(data, definition);
|
||||
self.repoint_representation(insert, source, copy, &name);
|
||||
Some(name)
|
||||
}
|
||||
|
||||
fn repoint_representation(&mut self, insert: Handle, from: Handle, to: Handle, name: &str) {
|
||||
if let Some(EntityType::Insert(reference)) = self.document.get_entity_mut(insert) {
|
||||
reference.block_name = name.clone();
|
||||
reference.block_name = name.to_string();
|
||||
}
|
||||
for record in self.document.block_records.iter_mut() {
|
||||
if record.handle == definition {
|
||||
if record.handle == from {
|
||||
record.insert_handles.retain(|handle| *handle != insert);
|
||||
} else if record.handle == copy {
|
||||
} else if record.handle == to {
|
||||
record.insert_handles.push(insert);
|
||||
}
|
||||
}
|
||||
self.bump_geometry();
|
||||
Some(name)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -688,6 +688,7 @@ impl Scene {
|
||||
}
|
||||
};
|
||||
let mut clones = Vec::with_capacity(order.len());
|
||||
let mut representations = Vec::new();
|
||||
for &h in &order {
|
||||
let new_h = remap[&h];
|
||||
let obj = doc.objects.get(&h)?;
|
||||
@@ -726,6 +727,17 @@ impl Scene {
|
||||
x.raw_dwg_version = None;
|
||||
x.preserve_object_reference_stream = false;
|
||||
}
|
||||
// A dynamic reference's AcDbRepData naming its definition.
|
||||
codec::objects::ObjectType::DynamicBlock(b) => {
|
||||
b.handle = new_h;
|
||||
b.owner = rewrite(b.owner);
|
||||
for r in &mut b.reactors {
|
||||
*r = rewrite(*r);
|
||||
}
|
||||
if let Some(&definition) = doc.block_representations.get(&h) {
|
||||
representations.push((new_h, definition));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
clones.push((new_h, obj));
|
||||
@@ -734,6 +746,7 @@ impl Scene {
|
||||
doc.objects.insert(new_h, obj);
|
||||
created.push(new_h);
|
||||
}
|
||||
doc.block_representations.extend(representations);
|
||||
Some(remap[&src_root])
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user