Bonsai: support circle profiles when editing an extrusion profile (#3381)

Enabling extrusion-profile editing on an element whose profile is an
IfcCircleProfileDef showed nothing editable: tool.Model.import_profile
had branches for IfcArbitraryClosedProfileDef, IfcRectangleProfileDef and
IfcAnnotationFillArea, but none for circles, so the leftover 3D body mesh
leaked through (238 verts, z spread -1..1, no PROFILE subshape).

Add an import_circle branch using the module's existing 2-vertex-diameter
+ IFCCIRCLE vertex-group convention (the same one convert_curve_to_mesh
emits and auto_detect_profiles consumes on export), honouring
profile.Position and unit scale like the neighbouring rectangle branch.
IfcCircleHollowProfileDef gets a second inner circle at
Radius - WallThickness; the export path's containment logic already turns
two nested loops into IfcArbitraryProfileDefWithVoids.

Verified live in headless Blender: enable-editing now yields a 2-vert
IFCCIRCLE profile mesh (subshape_type PROFILE) at the exact SI radius; a
no-op edit round-trips to an IfcArbitraryClosedProfileDef wrapping an
IfcCircle with radius delta ~4e-8 and the element still tessellates
(238 verts as before). Rectangle profile editing is unaffected.

Note on scope: converting the specific profile class to a curve-based one
on save is the module's existing, accepted behaviour for rectangles; this
change only brings circles to parity for editing.

Generated with the assistance of an AI coding tool.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Petru Conduraru
2026-07-12 15:09:00 +03:00
parent 0b7e25a3ef
commit 6f59d0b9af
+32
View File
@@ -483,6 +483,8 @@ class Model(bonsai.core.tool.Model):
cls.convert_curve_to_mesh(obj, position, inner_curve, x_angle=x_angle)
elif profile.is_a() == "IfcRectangleProfileDef":
cls.import_rectangle(obj, position, profile)
elif profile.is_a("IfcCircleProfileDef"):
cls.import_circle(obj, position, profile)
elif profile.is_a() == "IfcAnnotationFillArea":
cls.convert_curve_to_mesh(obj, position, profile.OuterBoundary)
for inner_boundary in profile.InnerBoundaries or []:
@@ -685,6 +687,36 @@ class Model(bonsai.core.tool.Model):
cls.edges.extend([(i, i + 1) for i in range(0, len(cls.vertices))])
cls.edges[-1] = (len(cls.vertices) - 1, 0) # Close the loop
@classmethod
def import_circle(cls, obj: bpy.types.Object, position: Matrix, profile: ifcopenshell.entity_instance) -> None:
"""Import IfcCircleProfileDef/IfcCircleHollowProfileDef.
Uses the 2-vertex diameter representation used throughout this module
(see `convert_curve_to_mesh`'s `IfcCircle` branch and
`auto_detect_profiles`), so a no-op edit round-trips to an equivalent
circle.
"""
if profile.Position:
p_position = Matrix(ifcopenshell.util.placement.get_axis2placement(profile.Position).tolist())
p_position.translation *= cls.unit_scale
else:
p_position = Matrix()
def add_circle(radius: float) -> None:
offset = len(cls.vertices)
cls.vertices.extend(
[
position @ p_position @ Vector((0.0, -radius, 0.0)),
position @ p_position @ Vector((0.0, radius, 0.0)),
]
)
cls.circles.append([offset, offset + 1])
cls.edges.append((offset, offset + 1))
add_circle(cls.convert_unit_to_si(profile.Radius))
if profile.is_a("IfcCircleHollowProfileDef"):
add_circle(cls.convert_unit_to_si(profile.Radius - profile.WallThickness))
@classmethod
def load_openings(cls, openings: list[ifcopenshell.entity_instance]) -> Iterable[bpy.types.Object]:
if not openings: