mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 18:16:40 +00:00
Closes #6621: Fix IfcRelAssignsToProduct on duplicate
When duplicating a product+annotation pair linked via IfcRelAssignsToProduct, copy_class's generic else-branch appended the new annotation to every existing rel that referenced the original, leaving both old and new products sharing stale RelatedObjects. Adds fix_assigns_to_product_after_duplication to tool.Geometry, called at the end of duplicate_ifc_objects. It corrects the three cases: new annotation in old product's rel (remove it), stale original in new product's rel (remove it), and annotation-only duplicate (build a fresh unique rel). Generated with the assistance of an AI coding tool.
This commit is contained in:
@@ -2533,6 +2533,7 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
# is complete.
|
||||
cls._recalculate_walls_with_new_connections(old_to_new)
|
||||
|
||||
cls.fix_assigns_to_product_after_duplication(old_to_new)
|
||||
bonsai.bim.handler.refresh_ui_data()
|
||||
tool.Root.reload_grid_decorator()
|
||||
return old_to_new, new_active_obj or active_object
|
||||
@@ -2825,6 +2826,49 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
if entity in old_to_new.keys() or single_obj:
|
||||
cls.remove_connection(connection)
|
||||
|
||||
@classmethod
|
||||
def fix_assigns_to_product_after_duplication(
|
||||
cls, old_to_new: dict[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]]
|
||||
) -> None:
|
||||
"""Fix IfcRelAssignsToProduct relationships broken by copy_class duplication.
|
||||
|
||||
copy_class hits the generic else-branch for IfcRelAssignsToProduct and
|
||||
appends the new element into every existing rel that referenced the original,
|
||||
causing both old and new products to share stale RelatedObjects lists.
|
||||
|
||||
Three cases, identified by whether the RelatingProduct was also duplicated:
|
||||
A) RelatingProduct is OLD → new annotation was incorrectly appended → remove it.
|
||||
B) RelatingProduct is NEW → stale original annotation was carried over → remove it.
|
||||
C) RelatingProduct unchanged → create a genuinely new rel for the new annotation.
|
||||
"""
|
||||
ifc_file = tool.Ifc.get()
|
||||
old_elements = set(old_to_new.keys())
|
||||
new_to_old = {new: old for old, new_list in old_to_new.items() for new in new_list}
|
||||
new_elements = set(new_to_old.keys())
|
||||
|
||||
for new_elem in new_elements:
|
||||
original_elem = new_to_old[new_elem]
|
||||
for rel in list(ifc_file.get_inverse(new_elem)):
|
||||
if not rel.is_a("IfcRelAssignsToProduct") or new_elem not in rel.RelatedObjects:
|
||||
continue
|
||||
relating_product = rel.RelatingProduct
|
||||
if relating_product in old_elements:
|
||||
# Case A: remove new elem from the old product's rel
|
||||
rel.RelatedObjects = [e for e in rel.RelatedObjects if e != new_elem]
|
||||
elif relating_product in new_elements:
|
||||
# Case B: remove the stale original from the new product's rel
|
||||
rel.RelatedObjects = [e for e in rel.RelatedObjects if e != original_elem]
|
||||
else:
|
||||
# Case C: product not duplicated — build a fresh unique rel
|
||||
rel.RelatedObjects = [e for e in rel.RelatedObjects if e != new_elem]
|
||||
ifc_file.create_entity(
|
||||
"IfcRelAssignsToProduct",
|
||||
GlobalId=ifcopenshell.guid.new(),
|
||||
OwnerHistory=rel.OwnerHistory,
|
||||
RelatedObjects=[new_elem],
|
||||
RelatingProduct=relating_product,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def remove_linked_aggregate_data(cls, old_to_new):
|
||||
ifc_file = tool.Ifc.get()
|
||||
|
||||
Reference in New Issue
Block a user