mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
Bonsai: delete representation items nested in an IfcGeometricSet (#6807)
Deleting a part (segment) of a dimension in item mode silently failed and the part reappeared when the representation reloaded. Multi-segment dimension annotations store their segments as Elements INSIDE an IfcGeometricCurveSet; tool.Geometry.remove_representation_item only recognized IfcShapeRepresentation and IfcBooleanResult inverses, so for a nested segment the item was never detached and remove_deep2 refused to purge it (an external inverse remained). The Blender object vanished, the IFC entity stayed, and the reload brought it back. Recognize IfcGeometricSet inverses: detach the item from Elements and let remove_deep2 purge it. If the item is the set's last element (Elements is SET [1:?]), remove the whole set recursively instead, which keeps the representation valid. This affects all GeometricSet-based annotations (dimensions, leaders, etc.), not just the reported model. Verified live in headless Blender on the reporter's model: deleting item #286487 of annotation #82782 left the entity in the file before the fix ("victim entity STILL EXISTS") and removes it after ("victim entity gone", Elements reduced, saved file verified clean with pure ifcopenshell); deleting both elements removes the entire curve set leaving a valid Items list. TestRemoveRepresentationItem + shape-aspect tests: 4 passed. Generated with the assistance of an AI coding tool. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1774,6 +1774,7 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
[consider_inverses.append(texture := t) for t in getattr(representation_item, "HasTextures", [])]
|
||||
|
||||
representation = None
|
||||
geometric_set = None
|
||||
boolean_results_to_remove: set[ifcopenshell.entity_instance] = set()
|
||||
for inverse in ifc_file.get_inverse(representation_item):
|
||||
if inverse.is_a("IfcShapeRepresentation"):
|
||||
@@ -1781,6 +1782,10 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
shape_aspects.append(inverse.OfShapeAspect[0])
|
||||
else:
|
||||
representation = inverse
|
||||
elif inverse.is_a("IfcGeometricSet"):
|
||||
# E.g. a dimension segment (curve) nested inside an
|
||||
# IfcGeometricCurveSet of an annotation representation.
|
||||
geometric_set = inverse
|
||||
elif inverse.is_a("IfcBooleanResult"):
|
||||
if inverse.SecondOperand == representation_item:
|
||||
other_operand = inverse.FirstOperand
|
||||
@@ -1812,6 +1817,15 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
for shape_aspect in shape_aspects:
|
||||
cls.remove_representation_items_from_shape_aspect([representation_item], shape_aspect)
|
||||
|
||||
if geometric_set:
|
||||
new_elements = tuple(e for e in geometric_set.Elements if e != representation_item)
|
||||
if not new_elements:
|
||||
# An IfcGeometricSet must keep at least one element, so
|
||||
# removing the set's last element removes the set itself
|
||||
# (which purges the element together with the set's subgraph).
|
||||
cls.remove_representation_item(geometric_set, element)
|
||||
return
|
||||
geometric_set.Elements = new_elements
|
||||
if representation:
|
||||
new_items = tuple(set(representation.Items) - {representation_item})
|
||||
if not new_items:
|
||||
|
||||
Reference in New Issue
Block a user