mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
feat: Add alignment representation templates to AddElement dialog
Wire IfcAlignment creation into Bonsai's Add IFC Element flow with representation template options (Horizontal, Gradient, Cant, 3D/2D Polyline). Adds create_representation_structure() to tool.Alignment which creates layout containers, geometric representation, and polyline placeholders. Guards create_representation against empty layout nests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -138,7 +138,40 @@ class IfcClassData:
|
||||
("EMPTY", "No Geometry", "Start with an empty object"),
|
||||
]
|
||||
|
||||
if ifc_class in ("IfcAlignmentHorizontal", "IfcAlignmentVertical", "IfcAlignmentCant"):
|
||||
return templates # layout containers have no direct representation per IFC 4.3 spec
|
||||
|
||||
if ifc_class == "IfcAlignment":
|
||||
templates.extend(
|
||||
[
|
||||
None,
|
||||
(
|
||||
"ALIGNMENT_HORIZONTAL",
|
||||
"Horizontal Alignment (2D)",
|
||||
"2D horizontal-only alignment (IfcCompositeCurve)",
|
||||
),
|
||||
(
|
||||
"ALIGNMENT_GRADIENT",
|
||||
"Gradient Curve (3D)",
|
||||
"3D alignment with horizontal and vertical layouts (IfcGradientCurve)",
|
||||
),
|
||||
(
|
||||
"ALIGNMENT_CANT",
|
||||
"Segmented Reference Curve",
|
||||
"3D alignment with horizontal, vertical, and cant layouts (IfcSegmentedReferenceCurve)",
|
||||
),
|
||||
(
|
||||
"ALIGNMENT_POLYLINE_3D",
|
||||
"3D Survey Polyline",
|
||||
"3D alignment from survey data (IfcPolyline with 3D points)",
|
||||
),
|
||||
(
|
||||
"ALIGNMENT_POLYLINE_2D",
|
||||
"2D Planning Polyline",
|
||||
"2D alignment for early planning phases (IfcPolyline with 2D points)",
|
||||
),
|
||||
]
|
||||
)
|
||||
return templates
|
||||
elif ifc_class in ("IfcWindowType", "IfcWindowStyle", "IfcWindow"):
|
||||
templates.extend([None, ("WINDOW", "Window", "Parametric window")])
|
||||
|
||||
@@ -542,6 +542,11 @@ class AddElement(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
if representation_template in (
|
||||
"EMPTY",
|
||||
"ALIGNMENT_HORIZONTAL",
|
||||
"ALIGNMENT_GRADIENT",
|
||||
"ALIGNMENT_CANT",
|
||||
"ALIGNMENT_POLYLINE_3D",
|
||||
"ALIGNMENT_POLYLINE_2D",
|
||||
"LAYERSET_AXIS2",
|
||||
"LAYERSET_AXIS3",
|
||||
"PROFILESET",
|
||||
@@ -566,7 +571,17 @@ class AddElement(bpy.types.Operator, tool.Ifc.Operator):
|
||||
)
|
||||
element.Description = props.description or None
|
||||
|
||||
if representation_template == "EMTPY" or not ifc_context:
|
||||
if representation_template == "EMPTY":
|
||||
pass
|
||||
elif representation_template in (
|
||||
"ALIGNMENT_HORIZONTAL",
|
||||
"ALIGNMENT_GRADIENT",
|
||||
"ALIGNMENT_CANT",
|
||||
"ALIGNMENT_POLYLINE_3D",
|
||||
"ALIGNMENT_POLYLINE_2D",
|
||||
):
|
||||
tool.Alignment.create_representation_structure(element, representation_template)
|
||||
elif not ifc_context:
|
||||
pass
|
||||
elif representation_template == "OBJ" and props.representation_obj:
|
||||
obj.matrix_world = props.representation_obj.matrix_world.copy()
|
||||
@@ -862,5 +877,12 @@ class AddElement(bpy.types.Operator, tool.Ifc.Operator):
|
||||
elif props.representation_template == "PROFILESET":
|
||||
row = self.layout.row()
|
||||
prop_with_search(self.layout, props, "profile", text="Profile", should_click_ok=True)
|
||||
if props.representation_template != "EMPTY":
|
||||
alignment_templates = {
|
||||
"ALIGNMENT_HORIZONTAL",
|
||||
"ALIGNMENT_GRADIENT",
|
||||
"ALIGNMENT_CANT",
|
||||
"ALIGNMENT_POLYLINE_3D",
|
||||
"ALIGNMENT_POLYLINE_2D",
|
||||
}
|
||||
if props.representation_template != "EMPTY" and props.representation_template not in alignment_templates:
|
||||
prop_with_search(self.layout, props, "contexts", should_click_ok=True)
|
||||
|
||||
@@ -388,6 +388,68 @@ class Alignment:
|
||||
ifc_file = tool.Ifc.get()
|
||||
align_api.layout_horizontal_alignment_by_pi_method(ifc_file, layout, hpoints, radii)
|
||||
|
||||
@classmethod
|
||||
def create_representation_structure(cls, alignment: "ifcopenshell.entity_instance", template: str) -> None:
|
||||
"""Create semantic layout containers and geometric representation for an IfcAlignment.
|
||||
|
||||
Called from the AddElement operator when the user selects a representation type
|
||||
for a newly-created IfcAlignment. For layout-based templates (HORIZONTAL, GRADIENT,
|
||||
CANT), the appropriate IfcAlignmentHorizontal / Vertical / Cant entities are nested to
|
||||
the alignment and ifcopenshell.api.alignment.create_representation is invoked to build
|
||||
the matching geometric curves. For polyline templates, an IfcPolyLine with two
|
||||
placeholder origin points is created directly.
|
||||
|
||||
Layout containers are IFC entities only — no Blender objects are created for them.
|
||||
|
||||
Args:
|
||||
alignment: The IfcAlignment entity that was just created by AddElement.
|
||||
template: One of ALIGNMENT_HORIZONTAL, ALIGNMENT_GRADIENT, ALIGNMENT_CANT,
|
||||
ALIGNMENT_POLYLINE_3D, or ALIGNMENT_POLYLINE_2D.
|
||||
"""
|
||||
import ifcopenshell.api.alignment as align_api
|
||||
import ifcopenshell.api.nest
|
||||
import ifcopenshell.guid
|
||||
from ifcopenshell.api.alignment._create_polyline_representation import (
|
||||
_create_polyline_representation,
|
||||
)
|
||||
|
||||
ifc_file = tool.Ifc.get()
|
||||
|
||||
if template == "ALIGNMENT_HORIZONTAL":
|
||||
h_layout = ifc_file.createIfcAlignmentHorizontal(GlobalId=ifcopenshell.guid.new())
|
||||
ifcopenshell.api.nest.assign_object(ifc_file, related_objects=[h_layout], relating_object=alignment)
|
||||
align_api.create_representation(ifc_file, alignment)
|
||||
|
||||
elif template == "ALIGNMENT_GRADIENT":
|
||||
h_layout = ifc_file.createIfcAlignmentHorizontal(GlobalId=ifcopenshell.guid.new())
|
||||
ifcopenshell.api.nest.assign_object(ifc_file, related_objects=[h_layout], relating_object=alignment)
|
||||
v_layout = ifc_file.createIfcAlignmentVertical(GlobalId=ifcopenshell.guid.new())
|
||||
ifcopenshell.api.nest.assign_object(ifc_file, related_objects=[v_layout], relating_object=alignment)
|
||||
align_api.create_representation(ifc_file, alignment)
|
||||
|
||||
elif template == "ALIGNMENT_CANT":
|
||||
h_layout = ifc_file.createIfcAlignmentHorizontal(GlobalId=ifcopenshell.guid.new())
|
||||
ifcopenshell.api.nest.assign_object(ifc_file, related_objects=[h_layout], relating_object=alignment)
|
||||
v_layout = ifc_file.createIfcAlignmentVertical(GlobalId=ifcopenshell.guid.new())
|
||||
ifcopenshell.api.nest.assign_object(ifc_file, related_objects=[v_layout], relating_object=alignment)
|
||||
c_layout = ifc_file.createIfcAlignmentCant(GlobalId=ifcopenshell.guid.new())
|
||||
ifcopenshell.api.nest.assign_object(ifc_file, related_objects=[c_layout], relating_object=alignment)
|
||||
align_api.create_representation(ifc_file, alignment)
|
||||
|
||||
elif template == "ALIGNMENT_POLYLINE_3D":
|
||||
pts = [
|
||||
ifc_file.createIfcCartesianPoint(Coordinates=(0.0, 0.0, 0.0)),
|
||||
ifc_file.createIfcCartesianPoint(Coordinates=(1.0, 0.0, 0.0)),
|
||||
]
|
||||
_create_polyline_representation(ifc_file, alignment, pts)
|
||||
|
||||
elif template == "ALIGNMENT_POLYLINE_2D":
|
||||
pts = [
|
||||
ifc_file.createIfcCartesianPoint(Coordinates=(0.0, 0.0)),
|
||||
ifc_file.createIfcCartesianPoint(Coordinates=(1.0, 0.0)),
|
||||
]
|
||||
_create_polyline_representation(ifc_file, alignment, pts)
|
||||
|
||||
# =========================================================================
|
||||
# Zero-Length Segment Utilities
|
||||
# =========================================================================
|
||||
|
||||
@@ -52,5 +52,6 @@ def create_representation(
|
||||
for layout in layouts:
|
||||
curve = ifcopenshell.api.alignment.get_layout_curve(layout)
|
||||
layout_nest = ifcopenshell.api.alignment.get_alignment_segment_nest(layout)
|
||||
for segment in layout_nest.RelatedObjects:
|
||||
_add_segment_to_curve(file, segment, curve)
|
||||
if layout_nest: # None when no segments exist yet (by design of get_alignment_segment_nest)
|
||||
for segment in layout_nest.RelatedObjects:
|
||||
_add_segment_to_curve(file, segment, curve)
|
||||
|
||||
Reference in New Issue
Block a user