mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-12 02:23:34 +00:00
Bonsai: hand the duplicated annotation to the duplicated product (#9049)
Case A only removed the new element from the original product's relationship. That was enough while copy_class also cloned the product's relationship onto the duplicate, because case B then stripped the stale original from it and the duplicate was left holding the new annotation. PR #9054 stops copy_class sharing annotations that way, since that sharing is what leaves one annotation assigned to two products (#4014, #9049). With that clone gone, nothing recreates the link, and duplicating a product together with its annotation leaves the annotation copy assigned to no product at all, which is worse than the duplicate it replaced. Case A now follows the product: when the relating product was duplicated in the same operation, the new element is assigned to the corresponding copy, pairing them by index so the i-th copy of an annotation labels the i-th copy of the product. It appends to the new product's existing relationship when it has one and creates a fresh one otherwise, and skips entirely when the element is already assigned to that product, which is what happens for related objects copy_class still carries over, such as the task that outputs the product. That last guard is what makes this safe to merge in either order relative to #9054. Verified over five scenarios against both the old and the new copy_class: duplicating product and annotation together, the annotation alone, the product alone, and the two task equivalents so the 4D output link is not disturbed. With the old copy_class every case this PR already handled still passes; the only failure is duplicating the product alone, which is the bug #9054 fixes and which this PR never covered. Generated with the assistance of an AI coding tool. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2837,9 +2837,16 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
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.
|
||||
A) RelatingProduct is OLD → new annotation was incorrectly appended → remove it,
|
||||
and hand it to the duplicate of that product, which is where it belongs.
|
||||
B) RelatingProduct is NEW → stale original was carried over → remove it.
|
||||
C) RelatingProduct unchanged → create a genuinely new rel for the new annotation.
|
||||
|
||||
Case A used to only remove, because copy_class also cloned the product's rel onto
|
||||
the duplicate, so case B did the re-linking. copy_class no longer shares annotations
|
||||
that way (#9049), so case A has to make the link itself or the duplicated annotation
|
||||
would end up assigned to nothing. Case B still fires for related objects that are
|
||||
not annotations, such as the task that outputs the product.
|
||||
"""
|
||||
ifc_file = tool.Ifc.get()
|
||||
old_elements = set(old_to_new.keys())
|
||||
@@ -2855,6 +2862,30 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
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]
|
||||
# The product was duplicated too, so follow it: the i-th copy of the
|
||||
# annotation belongs to the i-th copy of the product it labels.
|
||||
new_products = old_to_new.get(relating_product) or []
|
||||
index = old_to_new[original_elem].index(new_elem)
|
||||
if index < len(new_products):
|
||||
new_product = new_products[index]
|
||||
if not any(
|
||||
r.is_a("IfcRelAssignsToProduct") and r.RelatingProduct == new_product
|
||||
for r in new_elem.HasAssignments or []
|
||||
):
|
||||
new_rel = next(
|
||||
(r for r in new_product.ReferencedBy or [] if r.is_a("IfcRelAssignsToProduct")),
|
||||
None,
|
||||
)
|
||||
if new_rel:
|
||||
new_rel.RelatedObjects = list(new_rel.RelatedObjects) + [new_elem]
|
||||
else:
|
||||
ifc_file.create_entity(
|
||||
"IfcRelAssignsToProduct",
|
||||
GlobalId=ifcopenshell.guid.new(),
|
||||
OwnerHistory=rel.OwnerHistory,
|
||||
RelatedObjects=[new_elem],
|
||||
RelatingProduct=new_product,
|
||||
)
|
||||
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]
|
||||
|
||||
Reference in New Issue
Block a user