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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A reference to the definition itself gets an evaluated copy first,
|
// A reference to the definition itself, or to a copy other
|
||||||
// so the state picked for it leaves the definition alone.
|
// references share, gets its own evaluated copy first, so the state
|
||||||
let direct = {
|
// picked for it leaves the definition and the others alone.
|
||||||
let doc = &self.tabs[i].scene.document;
|
self.push_undo_snapshot(i, "Visibility State");
|
||||||
matches!(doc.get_entity(insert_handle), Some(EntityType::Insert(ins))
|
self.tabs[i].scene.visibility_representation(insert_handle);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve everything against an immutable borrow first.
|
// Resolve everything against an immutable borrow first.
|
||||||
let mapping = {
|
let mapping = {
|
||||||
@@ -216,10 +208,6 @@ impl OpenCADStudio {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if !direct {
|
|
||||||
self.push_undo_snapshot(i, "Visibility State");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply invisibility flags to the anonymous block's members.
|
// Apply invisibility flags to the anonymous block's members.
|
||||||
{
|
{
|
||||||
let doc = &mut self.tabs[i].scene.document;
|
let doc = &mut self.tabs[i].scene.document;
|
||||||
|
|||||||
@@ -464,18 +464,31 @@ pub fn finish_editing(document: &mut CadDocument, definition: Handle) {
|
|||||||
impl Scene {
|
impl Scene {
|
||||||
/// Give a reference to a dynamic definition its own evaluated copy, so a
|
/// Give a reference to a dynamic definition its own evaluated copy, so a
|
||||||
/// state picked for it leaves the definition and the other references
|
/// state picked for it leaves the definition and the other references
|
||||||
/// alone. Returns the copy's name; a reference already showing a copy,
|
/// alone. A copy shared with other references (after COPY) is copied
|
||||||
/// or not dynamic, keeps its block and gets `None`.
|
/// 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> {
|
pub fn visibility_representation(&mut self, insert: Handle) -> Option<String> {
|
||||||
let Some(EntityType::Insert(reference)) = self.document.get_entity(insert) else {
|
let Some(EntityType::Insert(reference)) = self.document.get_entity(insert) else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
let definition = self
|
let source = self
|
||||||
.document
|
.document
|
||||||
.block_records
|
.block_records
|
||||||
.get(&reference.block_name)?
|
.get(&reference.block_name)?
|
||||||
.handle;
|
.handle;
|
||||||
|
let definition = self.document.dynamic_definition_for_insert(insert)?;
|
||||||
parameter(&self.document, definition)?;
|
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 name = {
|
||||||
let next = self
|
let next = self
|
||||||
.document
|
.document
|
||||||
@@ -490,7 +503,7 @@ impl Scene {
|
|||||||
.document
|
.document
|
||||||
.block_records
|
.block_records
|
||||||
.iter()
|
.iter()
|
||||||
.find(|record| record.handle == definition)
|
.find(|record| record.handle == source)
|
||||||
.and_then(
|
.and_then(
|
||||||
|record| match self.document.get_entity(record.block_entity_handle) {
|
|record| match self.document.get_entity(record.block_entity_handle) {
|
||||||
Some(EntityType::Block(block)) => Some(block.base_point),
|
Some(EntityType::Block(block)) => Some(block.base_point),
|
||||||
@@ -498,7 +511,7 @@ impl Scene {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
.unwrap_or(Vector3::ZERO);
|
.unwrap_or(Vector3::ZERO);
|
||||||
let originals = members(&self.document, definition);
|
let originals = members(&self.document, source);
|
||||||
let copies: Vec<EntityType> = originals
|
let copies: Vec<EntityType> = originals
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|handle| self.document.get_entity(*handle).cloned())
|
.filter_map(|handle| self.document.get_entity(*handle).cloned())
|
||||||
@@ -518,6 +531,11 @@ impl Scene {
|
|||||||
tag.add_value(XDataValue::Handle(definition));
|
tag.add_value(XDataValue::Handle(definition));
|
||||||
self.document.set_object_xdata_record(copy, tag);
|
self.document.set_object_xdata_record(copy, tag);
|
||||||
self.document.ensure_extension_dictionary(copy);
|
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
|
// The reference: AcDbBlockRepresentation / AcDbRepData names the
|
||||||
// definition it was evaluated from.
|
// definition it was evaluated from.
|
||||||
@@ -550,18 +568,22 @@ impl Scene {
|
|||||||
.objects
|
.objects
|
||||||
.insert(data, ObjectType::DynamicBlock(link));
|
.insert(data, ObjectType::DynamicBlock(link));
|
||||||
self.document.block_representations.insert(data, definition);
|
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) {
|
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() {
|
for record in self.document.block_records.iter_mut() {
|
||||||
if record.handle == definition {
|
if record.handle == from {
|
||||||
record.insert_handles.retain(|handle| *handle != insert);
|
record.insert_handles.retain(|handle| *handle != insert);
|
||||||
} else if record.handle == copy {
|
} else if record.handle == to {
|
||||||
record.insert_handles.push(insert);
|
record.insert_handles.push(insert);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.bump_geometry();
|
self.bump_geometry();
|
||||||
Some(name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -688,6 +688,7 @@ impl Scene {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut clones = Vec::with_capacity(order.len());
|
let mut clones = Vec::with_capacity(order.len());
|
||||||
|
let mut representations = Vec::new();
|
||||||
for &h in &order {
|
for &h in &order {
|
||||||
let new_h = remap[&h];
|
let new_h = remap[&h];
|
||||||
let obj = doc.objects.get(&h)?;
|
let obj = doc.objects.get(&h)?;
|
||||||
@@ -726,6 +727,17 @@ impl Scene {
|
|||||||
x.raw_dwg_version = None;
|
x.raw_dwg_version = None;
|
||||||
x.preserve_object_reference_stream = false;
|
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));
|
clones.push((new_h, obj));
|
||||||
@@ -734,6 +746,7 @@ impl Scene {
|
|||||||
doc.objects.insert(new_h, obj);
|
doc.objects.insert(new_h, obj);
|
||||||
created.push(new_h);
|
created.push(new_h);
|
||||||
}
|
}
|
||||||
|
doc.block_representations.extend(representations);
|
||||||
Some(remap[&src_root])
|
Some(remap[&src_root])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user