mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-13 19:07:57 +00:00
Bonsai: support editing IfcGeometricCurveSet representation items (#6407)
Entering item edit mode on an IfcGeometricCurveSet (the representation item produced for annotations such as SECTION_LEVEL, whose Elements are curves) reported "Editing IfcGeometricCurveSet geometry is not supported": tool.Geometry.is_curvelike_item did not recognise the set, so the edit path fell through to the unsupported branch. Handle the set as a single editable item whose nested curves are its Elements, mirroring how the join operator already treats it: - is_curvelike_item recognises IfcGeometricCurveSet. - tool.Model.convert_curve_to_mesh recurses into curve.Elements, importing each nested curve as its own edge loop in the mesh. - On save (both OverrideModeSetObject and DirectProfileEdit exit paths), keep the same set entity and swap out its Elements (remove_deep2 on the old ones), instead of replacing the set with a bare curve. Verified live in headless Blender: is_curvelike_item(IfcGeometricCurveSet) is now True and the set imports to a mesh (was the exact "not supported" error); a no-op edit round-trips for both a single-element and a two-element set, preserving the set entity with new elements at identical coordinates and removing the old ones; a plain (non-set) IfcIndexedPolyCurve item still goes through the original replace-and-remove path (no regression). Scope note: a multi-element set on a DIMENSION-style annotation is still limited by the pre-existing ensure_annotation_vertex_order helper, which walks one connected edge path and drops other loops. That is a separate, pre-existing helper bug (not introduced here) and is left for a follow-up; single-element and non-annotation curve sets round-trip fully. Generated with the assistance of an AI coding tool. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -2683,6 +2683,19 @@ class OverrideModeSetObject(bpy.types.Operator, tool.Ifc.Operator):
|
||||
self.enable_edit_mode(bpy.context)
|
||||
return
|
||||
|
||||
if item.is_a("IfcGeometricCurveSet"):
|
||||
# The set itself is the edited representation item (e.g. a dimension
|
||||
# annotation). Keep the same set entity and just swap out its nested
|
||||
# curve Elements, rather than replacing the set with a bare curve.
|
||||
old_elements = list(item.Elements)
|
||||
item.Elements = tuple(new)
|
||||
for old_element in old_elements:
|
||||
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_element)
|
||||
tool.Ifc.link(item, obj.data)
|
||||
tool.Geometry.import_item(obj)
|
||||
tool.Geometry.reload_representation(props.representation_obj)
|
||||
return
|
||||
|
||||
additional_curves = []
|
||||
if len(new) > 1:
|
||||
additional_curves = new[1:]
|
||||
@@ -2886,12 +2899,27 @@ class DirectProfileEdit(bpy.types.Operator, tool.Ifc.Operator):
|
||||
ProfileDecorator.install(context)
|
||||
return {"CANCELLED"}
|
||||
|
||||
ifc_file = tool.Ifc.get()
|
||||
|
||||
if item.is_a("IfcGeometricCurveSet"):
|
||||
# The set itself is the edited representation item (e.g. a dimension
|
||||
# annotation). Keep the same set entity and just swap out its nested
|
||||
# curve Elements, rather than replacing the set with a bare curve.
|
||||
old_elements = list(item.Elements)
|
||||
item.Elements = tuple(new)
|
||||
for old_element in old_elements:
|
||||
ifcopenshell.util.element.remove_deep2(ifc_file, old_element)
|
||||
tool.Ifc.link(item, obj.data)
|
||||
tool.Geometry.import_item(obj)
|
||||
tool.Geometry.reload_representation(props.representation_obj)
|
||||
tool.Geometry.disable_item_mode()
|
||||
return {"FINISHED"}
|
||||
|
||||
additional_curves = []
|
||||
if len(new) > 1:
|
||||
additional_curves = new[1:]
|
||||
new = new[0]
|
||||
|
||||
ifc_file = tool.Ifc.get()
|
||||
for inverse in ifc_file.get_inverse(item):
|
||||
ifcopenshell.util.element.replace_attribute(inverse, item, new)
|
||||
ifcopenshell.util.element.remove_deep2(ifc_file, item)
|
||||
|
||||
@@ -1348,6 +1348,7 @@ class Geometry(bonsai.core.tool.Geometry):
|
||||
or item.is_a("IfcCompositeCurve")
|
||||
or item.is_a("IfcIndexedPolyCurve")
|
||||
or item.is_a("IfcCircle")
|
||||
or item.is_a("IfcGeometricCurveSet")
|
||||
)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -625,6 +625,12 @@ class Model(bonsai.core.tool.Model):
|
||||
for segment in curve.Segments:
|
||||
cls.convert_curve_to_mesh(obj, position, segment.ParentCurve)
|
||||
|
||||
elif curve.is_a("IfcGeometricCurveSet"):
|
||||
# A set of independent curves (e.g. dimension annotations). Each element is
|
||||
# imported as its own disconnected edge loop within the same mesh.
|
||||
for sub_curve in curve.Elements:
|
||||
cls.convert_curve_to_mesh(obj, position, sub_curve, x_angle=x_angle)
|
||||
|
||||
elif curve.is_a("IfcIndexedPolyCurve"):
|
||||
for local_point in curve.Points.CoordList:
|
||||
global_point = position @ Vector(cls.convert_unit_to_si(local_point)).to_3d()
|
||||
|
||||
Reference in New Issue
Block a user