From 0b3967609c3664a62e486b215d9dee893412b2d7 Mon Sep 17 00:00:00 2001 From: DesertSpringsCivil Date: Tue, 3 Mar 2026 13:39:19 -0700 Subject: [PATCH] fix: Address PR #7589 review items - prefix rename, cleanup, copyright - Rename all saikei.* operator idnames to civil.* per Bonsai convention - Rename SAIKEI_OT_*, SAIKEI_PT_*, SAIKEI_UL_* classes to CIVIL_* prefix - Rename SaikeiAlignmentProperties -> CivilAlignmentProperties - Rename saikei_* Blender object custom property keys to civil_* - Remove IOS-version-compat try/except fallback in _get_segment_vertices_in_model_units() now that Rick's segment_vertices() API accepts IfcAlignmentSegment directly - Remove try/except wrapper around get_alignment() - call directly - Add Michael Yoder copyright to __init__.py and operator.py - Fix misleading coordinate comment (IFC -> global easting/northing) - Document props.pis coordinate system (global E/N) in AlignmentPI and operator comments Co-Authored-By: Claude Sonnet 4.6 --- .../bonsai/bim/module/alignment/__init__.py | 36 ++++---- .../bonsai/bim/module/alignment/operator.py | 89 ++++++++++--------- .../bonsai/bim/module/alignment/prop.py | 10 ++- src/bonsai/bonsai/bim/module/alignment/ui.py | 40 ++++----- src/bonsai/bonsai/tool/alignment.py | 40 ++++----- 5 files changed, 104 insertions(+), 111 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/alignment/__init__.py b/src/bonsai/bonsai/bim/module/alignment/__init__.py index 81bc3e60de..8c05ed2bcc 100644 --- a/src/bonsai/bonsai/bim/module/alignment/__init__.py +++ b/src/bonsai/bonsai/bim/module/alignment/__init__.py @@ -1,5 +1,5 @@ # Bonsai - OpenBIM Blender Add-on -# Copyright (C) 2020, 2021 Dion Moult +# Copyright (C) 2020, 2021 Dion Moult , 2026 Michael Yoder # # This file is part of Bonsai. # @@ -25,28 +25,28 @@ classes = ( # Property groups (must be registered before classes that use them) prop.AlignmentPI, prop.AlignmentDisplayRow, - prop.SaikeiAlignmentProperties, + prop.CivilAlignmentProperties, # UILists - ui.SAIKEI_UL_alignment_pis, + ui.CIVIL_UL_alignment_pis, operator.ImportAlignmentCSV, # Operators - PI Management - operator.SAIKEI_OT_add_pi, - operator.SAIKEI_OT_remove_pi, - operator.SAIKEI_OT_pick_pi_from_viewport, - operator.SAIKEI_OT_recalculate_pis, - operator.SAIKEI_OT_clear_pis, + operator.CIVIL_OT_add_pi, + operator.CIVIL_OT_remove_pi, + operator.CIVIL_OT_pick_pi_from_viewport, + operator.CIVIL_OT_recalculate_pis, + operator.CIVIL_OT_clear_pis, # Operators - Creation - operator.SAIKEI_OT_create_alignment_by_pi, - operator.SAIKEI_OT_import_alignment_csv, + operator.CIVIL_OT_create_alignment_by_pi, + operator.CIVIL_OT_import_alignment_csv, # Operators - Stationing - operator.SAIKEI_OT_add_stationing_referent, - operator.SAIKEI_OT_name_segments, + operator.CIVIL_OT_add_stationing_referent, + operator.CIVIL_OT_name_segments, # Operators - PI Edit Mode - operator.SAIKEI_OT_enter_pi_edit_mode, + operator.CIVIL_OT_enter_pi_edit_mode, # UI Panels (appear in Properties sidebar under CIVIL tab) - ui.SAIKEI_PT_alignment_creation, - ui.SAIKEI_PT_pi_editor, - ui.SAIKEI_PT_alignment_stationing, + ui.CIVIL_PT_alignment_creation, + ui.CIVIL_PT_pi_editor, + ui.CIVIL_PT_alignment_stationing, ) @@ -55,10 +55,10 @@ def menu_func_import(self, context): def register(): - bpy.types.Scene.SaikeiAlignmentProperties = bpy.props.PointerProperty(type=prop.SaikeiAlignmentProperties) + bpy.types.Scene.CivilAlignmentProperties = bpy.props.PointerProperty(type=prop.CivilAlignmentProperties) bpy.types.TOPBAR_MT_file_import.append(menu_func_import) def unregister(): bpy.types.TOPBAR_MT_file_import.remove(menu_func_import) - del bpy.types.Scene.SaikeiAlignmentProperties + del bpy.types.Scene.CivilAlignmentProperties diff --git a/src/bonsai/bonsai/bim/module/alignment/operator.py b/src/bonsai/bonsai/bim/module/alignment/operator.py index ad9293acb0..20833f6fe2 100644 --- a/src/bonsai/bonsai/bim/module/alignment/operator.py +++ b/src/bonsai/bonsai/bim/module/alignment/operator.py @@ -1,5 +1,5 @@ # Bonsai - OpenBIM Blender Add-on -# Copyright (C) 2020, 2021 Dion Moult , 2022 Yassine Oualid +# Copyright (C) 2020, 2021 Dion Moult , 2022 Yassine Oualid , 2026 Michael Yoder # # This file is part of Bonsai. # @@ -190,7 +190,7 @@ def on_radius_changed(pi, context): 2. Rebuild of display_rows (Mid point becomes Curve segment) 3. If an active alignment exists, regeneration of IFC entities """ - props = context.scene.SaikeiAlignmentProperties + props = context.scene.CivilAlignmentProperties recalculate_pi_geometry(props) # If there's an active alignment, trigger IFC regeneration @@ -311,10 +311,10 @@ def rebuild_display_rows(props): # ============================================================================= -class SAIKEI_OT_add_pi(Operator): +class CIVIL_OT_add_pi(Operator): """Add a new PI point to the list""" - bl_idname = "saikei.add_pi" + bl_idname = "civil.add_pi" bl_label = "Add PI" bl_description = "Add a new PI (Point of Intersection) to the alignment" bl_options = {"REGISTER", "UNDO"} @@ -324,7 +324,7 @@ class SAIKEI_OT_add_pi(Operator): return poll_ifc4x3(cls, context) def execute(self, context): - props = context.scene.SaikeiAlignmentProperties + props = context.scene.CivilAlignmentProperties # Add new PI pi = props.pis.add() @@ -363,10 +363,10 @@ class SAIKEI_OT_add_pi(Operator): return {"FINISHED"} -class SAIKEI_OT_remove_pi(Operator): +class CIVIL_OT_remove_pi(Operator): """Remove the selected PI point""" - bl_idname = "saikei.remove_pi" + bl_idname = "civil.remove_pi" bl_label = "Remove PI" bl_description = "Remove the selected PI from the alignment" bl_options = {"REGISTER", "UNDO"} @@ -375,7 +375,7 @@ class SAIKEI_OT_remove_pi(Operator): def poll(cls, context): if not poll_ifc4x3(cls, context): return False - props = context.scene.SaikeiAlignmentProperties + props = context.scene.CivilAlignmentProperties if len(props.pis) == 0: cls.poll_message_set("No PIs to remove") return False @@ -389,7 +389,7 @@ class SAIKEI_OT_remove_pi(Operator): return True def execute(self, context): - props = context.scene.SaikeiAlignmentProperties + props = context.scene.CivilAlignmentProperties # Get the PI index from the selected display row pi_index = -1 @@ -420,10 +420,10 @@ class SAIKEI_OT_remove_pi(Operator): return {"FINISHED"} -class SAIKEI_OT_pick_pi_from_viewport(bpy.types.Operator, PolylineOperator, tool.Ifc.Operator): +class CIVIL_OT_pick_pi_from_viewport(bpy.types.Operator, PolylineOperator, tool.Ifc.Operator): """Add PI points by clicking in the 3D viewport using polyline tools""" - bl_idname = "saikei.pick_pi_from_viewport" + bl_idname = "civil.pick_pi_from_viewport" bl_label = "Pick PI from Viewport" bl_description = "Click in the viewport to add PI points with snapping and numeric input. RMB/Enter to finish, ESC to cancel." bl_options = {"REGISTER", "UNDO"} @@ -554,7 +554,7 @@ class SAIKEI_OT_pick_pi_from_viewport(bpy.types.Operator, PolylineOperator, tool Polyline points are in Blender coordinate space. This method converts each point to IFC coordinate space before storing in props.pis. """ - props = context.scene.SaikeiAlignmentProperties + props = context.scene.CivilAlignmentProperties polyline_props = tool.Model.get_polyline_props() polyline_data = polyline_props.insertion_polyline if not polyline_data: @@ -566,7 +566,7 @@ class SAIKEI_OT_pick_pi_from_viewport(bpy.types.Operator, PolylineOperator, tool num_points = len(polyline_points) for i, point in enumerate(polyline_points): - # Convert Blender space -> IFC easting/northing + # Convert Blender space -> global easting/northing (props.pis stores global E/N coords) ifc_coord = tool.Georeference.xyz2enh((point.x, point.y, 0.0)) pi = props.pis.add() @@ -584,10 +584,10 @@ class SAIKEI_OT_pick_pi_from_viewport(bpy.types.Operator, PolylineOperator, tool rebuild_display_rows(props) -class SAIKEI_OT_recalculate_pis(Operator, tool.Ifc.Operator): +class CIVIL_OT_recalculate_pis(Operator, tool.Ifc.Operator): """Recalculate PI geometry and update IFC/visualization""" - bl_idname = "saikei.recalculate_pis" + bl_idname = "civil.recalculate_pis" bl_label = "Recalculate PIs" bl_description = "Recalculate geometry, update IFC segments, and refresh visualization" bl_options = {"REGISTER", "UNDO"} @@ -596,7 +596,7 @@ class SAIKEI_OT_recalculate_pis(Operator, tool.Ifc.Operator): def poll(cls, context): if not poll_ifc4x3(cls, context): return False - props = context.scene.SaikeiAlignmentProperties + props = context.scene.CivilAlignmentProperties if len(props.pis) < 2: cls.poll_message_set("Need at least 2 PIs to recalculate") return False @@ -606,7 +606,7 @@ class SAIKEI_OT_recalculate_pis(Operator, tool.Ifc.Operator): import ifcopenshell.api.alignment as align_api ifc = tool.Ifc.get() - props = context.scene.SaikeiAlignmentProperties + props = context.scene.CivilAlignmentProperties # Recalculate geometry in UI properties recalculate_pi_geometry(props) @@ -619,7 +619,7 @@ class SAIKEI_OT_recalculate_pis(Operator, tool.Ifc.Operator): self.report({"ERROR"}, "Alignment has no horizontal layout") return - # Collect updated PI data + # Convert global E/N coords (stored in props.pis) -> local IFC coords for the IfcOpenShell API hpoints = [[float(o) for o in tool.Georeference.enh2xyz((float(pi.e), float(pi.n), 0.), to_blender=False)[:2]] for pi in props.pis] radii = [pi.radius for pi in props.pis[1:-1]] @@ -648,10 +648,10 @@ class SAIKEI_OT_recalculate_pis(Operator, tool.Ifc.Operator): self.report({"INFO"}, f"Recalculated {len(props.pis)} PIs, total length: {total_length:.2f}") -class SAIKEI_OT_clear_pis(Operator, tool.Ifc.Operator): +class CIVIL_OT_clear_pis(Operator, tool.Ifc.Operator): """Clear all PI points and optionally remove visualization/IFC data""" - bl_idname = "saikei.clear_pis" + bl_idname = "civil.clear_pis" bl_label = "Clear All PIs" bl_description = "Remove all PI points and clear segment visualization" bl_options = {"REGISTER", "UNDO"} @@ -660,7 +660,7 @@ class SAIKEI_OT_clear_pis(Operator, tool.Ifc.Operator): def poll(cls, context): if not poll_ifc4x3(cls, context): return False - props = context.scene.SaikeiAlignmentProperties + props = context.scene.CivilAlignmentProperties if len(props.pis) == 0: cls.poll_message_set("No PIs to clear") return False @@ -671,7 +671,7 @@ class SAIKEI_OT_clear_pis(Operator, tool.Ifc.Operator): def _execute(self, context): ifc = tool.Ifc.get() - props = context.scene.SaikeiAlignmentProperties + props = context.scene.CivilAlignmentProperties removed_objects = 0 @@ -703,10 +703,10 @@ class SAIKEI_OT_clear_pis(Operator, tool.Ifc.Operator): # ============================================================================= -class SAIKEI_OT_create_alignment_by_pi(Operator, tool.Ifc.Operator): +class CIVIL_OT_create_alignment_by_pi(Operator, tool.Ifc.Operator): """Create alignment using the PI (Point of Intersection) method""" - bl_idname = "saikei.create_alignment_by_pi" + bl_idname = "civil.create_alignment_by_pi" bl_label = "Create by PI Method" bl_description = "Create alignment using PI points and curve radii. If an active alignment exists with no segments, adds to it instead of creating new." bl_options = {"REGISTER", "UNDO"} @@ -715,7 +715,7 @@ class SAIKEI_OT_create_alignment_by_pi(Operator, tool.Ifc.Operator): def poll(cls, context): if not poll_ifc4x3(cls, context): return False - props = context.scene.SaikeiAlignmentProperties + props = context.scene.CivilAlignmentProperties if len(props.pis) < 2: cls.poll_message_set("Need at least 2 PI points") return False @@ -725,8 +725,9 @@ class SAIKEI_OT_create_alignment_by_pi(Operator, tool.Ifc.Operator): return True def _execute(self, context): - props = context.scene.SaikeiAlignmentProperties + props = context.scene.CivilAlignmentProperties + # Convert global E/N coords (stored in props.pis) -> local IFC coords for the IfcOpenShell API hpoints = [[float(o) for o in tool.Georeference.enh2xyz((float(pi.e), float(pi.n), 0.), to_blender=False)[:2]] for pi in props.pis] radii = [pi.radius for pi in props.pis[1:-1]] @@ -759,10 +760,10 @@ class SAIKEI_OT_create_alignment_by_pi(Operator, tool.Ifc.Operator): ) -class SAIKEI_OT_import_alignment_csv(Operator, tool.Ifc.Operator, ImportHelper): +class CIVIL_OT_import_alignment_csv(Operator, tool.Ifc.Operator, ImportHelper): """Import alignment from CSV file""" - bl_idname = "saikei.import_alignment_csv" + bl_idname = "civil.import_alignment_csv" bl_label = "Import Alignment CSV" bl_description = "Import alignment definition from a CSV file" bl_options = {"REGISTER", "UNDO"} @@ -776,7 +777,7 @@ class SAIKEI_OT_import_alignment_csv(Operator, tool.Ifc.Operator, ImportHelper): def _execute(self, context): ifc = tool.Ifc.get() - props = context.scene.SaikeiAlignmentProperties + props = context.scene.CivilAlignmentProperties alignment = ifcopenshell.api.alignment.create_from_csv(ifc, self.filepath) @@ -795,10 +796,10 @@ class SAIKEI_OT_import_alignment_csv(Operator, tool.Ifc.Operator, ImportHelper): # ============================================================================= -class SAIKEI_OT_add_stationing_referent(Operator, tool.Ifc.Operator): +class CIVIL_OT_add_stationing_referent(Operator, tool.Ifc.Operator): """Add a stationing referent to the alignment""" - bl_idname = "saikei.add_stationing_referent" + bl_idname = "civil.add_stationing_referent" bl_label = "Add Stationing Referent" bl_description = "Add an IfcReferent for stationing" bl_options = {"REGISTER", "UNDO"} @@ -819,7 +820,7 @@ class SAIKEI_OT_add_stationing_referent(Operator, tool.Ifc.Operator): def poll(cls, context): if not poll_ifc4x3(cls, context): return False - props = context.scene.SaikeiAlignmentProperties + props = context.scene.CivilAlignmentProperties if props.active_alignment_id == 0: cls.poll_message_set("Select an alignment first") return False @@ -827,7 +828,7 @@ class SAIKEI_OT_add_stationing_referent(Operator, tool.Ifc.Operator): def invoke(self, context, event): # Default station to start_station from props - props = context.scene.SaikeiAlignmentProperties + props = context.scene.CivilAlignmentProperties self.station = props.start_station return context.window_manager.invoke_props_dialog(self) @@ -841,7 +842,7 @@ class SAIKEI_OT_add_stationing_referent(Operator, tool.Ifc.Operator): def _execute(self, context): ifc = tool.Ifc.get() - props = context.scene.SaikeiAlignmentProperties + props = context.scene.CivilAlignmentProperties alignment = tool.Alignment.get_active_alignment() if alignment is None: @@ -883,10 +884,10 @@ def format_station(station_value): return f"{main}+{offset:05.2f}" -class SAIKEI_OT_name_segments(Operator, tool.Ifc.Operator): +class CIVIL_OT_name_segments(Operator, tool.Ifc.Operator): """Auto-name segments based on station values""" - bl_idname = "saikei.name_segments" + bl_idname = "civil.name_segments" bl_label = "Name Segments" bl_description = "Automatically name segments with station-based labels" bl_options = {"REGISTER", "UNDO"} @@ -895,7 +896,7 @@ class SAIKEI_OT_name_segments(Operator, tool.Ifc.Operator): def poll(cls, context): if not poll_ifc4x3(cls, context): return False - props = context.scene.SaikeiAlignmentProperties + props = context.scene.CivilAlignmentProperties if props.active_alignment_id == 0: cls.poll_message_set("Select an alignment first") return False @@ -903,7 +904,7 @@ class SAIKEI_OT_name_segments(Operator, tool.Ifc.Operator): def _execute(self, context): ifc = tool.Ifc.get() - props = context.scene.SaikeiAlignmentProperties + props = context.scene.CivilAlignmentProperties alignment = tool.Alignment.get_active_alignment() if alignment is None: @@ -920,10 +921,10 @@ class SAIKEI_OT_name_segments(Operator, tool.Ifc.Operator): # ============================================================================= -class SAIKEI_OT_enter_pi_edit_mode(Operator, tool.Ifc.Operator): +class CIVIL_OT_enter_pi_edit_mode(Operator, tool.Ifc.Operator): """Enter PI editing mode - move PIs with G key, press Enter to apply or Escape to cancel""" - bl_idname = "saikei.enter_pi_edit_mode" + bl_idname = "civil.enter_pi_edit_mode" bl_label = "Edit PIs" bl_description = "Enter PI edit mode. Move PI points with G key. Press Enter to apply changes, Escape to cancel." bl_options = {"REGISTER", "UNDO"} @@ -938,7 +939,7 @@ class SAIKEI_OT_enter_pi_edit_mode(Operator, tool.Ifc.Operator): def poll(cls, context): if not poll_ifc4x3(cls, context): return False - props = context.scene.SaikeiAlignmentProperties + props = context.scene.CivilAlignmentProperties if props.is_pi_edit_mode: cls.poll_message_set("Already in PI edit mode") return False @@ -956,7 +957,7 @@ class SAIKEI_OT_enter_pi_edit_mode(Operator, tool.Ifc.Operator): return IfcStore.execute_ifc_operator(self, context, event, method="INVOKE") def _invoke(self, context, event): - props = context.scene.SaikeiAlignmentProperties + props = context.scene.CivilAlignmentProperties self._alignment_id = props.active_alignment_id # Enter edit mode via core layer (validates and creates empties) @@ -999,7 +1000,7 @@ class SAIKEI_OT_enter_pi_edit_mode(Operator, tool.Ifc.Operator): return IfcStore.execute_ifc_operator(self, context, event, method="MODAL") def _modal(self, context, event): - props = context.scene.SaikeiAlignmentProperties + props = context.scene.CivilAlignmentProperties # Safety: check if empties still exist (handles undo edge case) if not self._empties_still_exist(): @@ -1040,7 +1041,7 @@ class SAIKEI_OT_enter_pi_edit_mode(Operator, tool.Ifc.Operator): def _cleanup_and_finish(self, context, apply: bool): """Exit edit mode, optionally applying changes.""" - props = context.scene.SaikeiAlignmentProperties + props = context.scene.CivilAlignmentProperties try: if apply: diff --git a/src/bonsai/bonsai/bim/module/alignment/prop.py b/src/bonsai/bonsai/bim/module/alignment/prop.py index 5898719fcd..b673a870a9 100644 --- a/src/bonsai/bonsai/bim/module/alignment/prop.py +++ b/src/bonsai/bonsai/bim/module/alignment/prop.py @@ -50,9 +50,11 @@ class AlignmentPI(PropertyGroup): - Interior PIs: Points where tangents intersect, optionally with curves """ - # Coordinates - e: StringProperty(name="E", description="Easting", default="0.0") - n: StringProperty(name="N", description="Northing", default="0.0") + # Coordinates stored as global easting/northing (map coordinates). + # Coordinate flow: Blender coords -> xyz2enh() -> global E/N (stored here) + # global E/N -> enh2xyz(to_blender=False) -> local IFC coords (for IfcOpenShell API) + e: StringProperty(name="E", description="Easting (global map coordinates)", default="0.0") + n: StringProperty(name="N", description="Northing (global map coordinates)", default="0.0") # PI Type pi_type: EnumProperty( @@ -146,7 +148,7 @@ class AlignmentDisplayRow(PropertyGroup): arc_length: FloatProperty(name="Arc Length", default=0.0, precision=2, unit="LENGTH") -class SaikeiAlignmentProperties(PropertyGroup): +class CivilAlignmentProperties(PropertyGroup): """Properties for the alignment module""" # Active alignment selection diff --git a/src/bonsai/bonsai/bim/module/alignment/ui.py b/src/bonsai/bonsai/bim/module/alignment/ui.py index 335b9e255e..25feb11188 100644 --- a/src/bonsai/bonsai/bim/module/alignment/ui.py +++ b/src/bonsai/bonsai/bim/module/alignment/ui.py @@ -38,7 +38,7 @@ def is_ifc4x3(): # ============================================================================= -class SAIKEI_UL_alignment_pis(UIList): +class CIVIL_UL_alignment_pis(UIList): """UIList for displaying interleaved points and segments (Civil 3D style) Row types: @@ -123,11 +123,11 @@ class SAIKEI_UL_alignment_pis(UIList): # ============================================================================= -class SAIKEI_PT_alignment_creation(Panel): +class CIVIL_PT_alignment_creation(Panel): """Sub-panel for alignment creation tools""" bl_label = "Creation" - bl_idname = "SAIKEI_PT_alignment_creation" + bl_idname = "CIVIL_PT_alignment_creation" bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "scene" @@ -140,7 +140,7 @@ class SAIKEI_PT_alignment_creation(Panel): def draw(self, context): layout = self.layout - props = context.scene.SaikeiAlignmentProperties + props = context.scene.CivilAlignmentProperties # New alignment properties box = layout.box() @@ -150,7 +150,7 @@ class SAIKEI_PT_alignment_creation(Panel): # Creation operators col = layout.column(align=True) - col.operator("saikei.create_alignment_by_pi", icon="CURVE_DATA") + col.operator("civil.create_alignment_by_pi", icon="CURVE_DATA") # ============================================================================= @@ -158,11 +158,11 @@ class SAIKEI_PT_alignment_creation(Panel): # ============================================================================= -class SAIKEI_PT_pi_editor(Panel): +class CIVIL_PT_pi_editor(Panel): """Sub-panel for PI point table editor (Civil 3D style grid view)""" bl_label = "PI Editor" - bl_idname = "SAIKEI_PT_pi_editor" + bl_idname = "CIVIL_PT_pi_editor" bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "scene" @@ -175,7 +175,7 @@ class SAIKEI_PT_pi_editor(Panel): def draw(self, context): layout = self.layout - props = context.scene.SaikeiAlignmentProperties + props = context.scene.CivilAlignmentProperties # PI Edit Mode indicator if props.is_pi_edit_mode: @@ -193,7 +193,7 @@ class SAIKEI_PT_pi_editor(Panel): if props.active_alignment_id != 0: box = layout.box() box.label(text="Edit Alignment:", icon="EDITMODE_HLT") - box.operator("saikei.enter_pi_edit_mode", icon="PIVOT_CURSOR", text="Edit PIs (G key)") + box.operator("civil.enter_pi_edit_mode", icon="PIVOT_CURSOR", text="Edit PIs (G key)") layout.separator() # Header row with column labels @@ -208,7 +208,7 @@ class SAIKEI_PT_pi_editor(Panel): # Combined point/segment list (interleaved view) row = layout.row() row.template_list( - "SAIKEI_UL_alignment_pis", + "CIVIL_UL_alignment_pis", "", props, "display_rows", @@ -219,16 +219,16 @@ class SAIKEI_PT_pi_editor(Panel): # Side buttons for list management col = row.column(align=True) - col.operator("saikei.add_pi", icon="ADD", text="") - col.operator("saikei.remove_pi", icon="REMOVE", text="") + col.operator("civil.add_pi", icon="ADD", text="") + col.operator("civil.remove_pi", icon="REMOVE", text="") col.separator() - col.operator("saikei.pick_pi_from_viewport", icon="EYEDROPPER", text="") + col.operator("civil.pick_pi_from_viewport", icon="EYEDROPPER", text="") # Bottom actions layout.separator() row = layout.row(align=True) - row.operator("saikei.recalculate_pis", icon="FILE_REFRESH", text="Recalculate") - row.operator("saikei.clear_pis", icon="TRASH", text="Clear All") + row.operator("civil.recalculate_pis", icon="FILE_REFRESH", text="Recalculate") + row.operator("civil.clear_pis", icon="TRASH", text="Clear All") # ============================================================================= @@ -236,11 +236,11 @@ class SAIKEI_PT_pi_editor(Panel): # ============================================================================= -class SAIKEI_PT_alignment_stationing(Panel): +class CIVIL_PT_alignment_stationing(Panel): """Sub-panel for stationing and referents""" bl_label = "Stationing" - bl_idname = "SAIKEI_PT_alignment_stationing" + bl_idname = "CIVIL_PT_alignment_stationing" bl_space_type = "PROPERTIES" bl_region_type = "WINDOW" bl_context = "scene" @@ -253,7 +253,7 @@ class SAIKEI_PT_alignment_stationing(Panel): def draw(self, context): layout = self.layout - props = context.scene.SaikeiAlignmentProperties + props = context.scene.CivilAlignmentProperties # Station display options box = layout.box() @@ -265,5 +265,5 @@ class SAIKEI_PT_alignment_stationing(Panel): # Stationing operators col = layout.column(align=True) - col.operator("saikei.add_stationing_referent", icon="EMPTY_AXIS") - col.operator("saikei.name_segments", icon="FONT_DATA") + col.operator("civil.add_stationing_referent", icon="EMPTY_AXIS") + col.operator("civil.name_segments", icon="FONT_DATA") diff --git a/src/bonsai/bonsai/tool/alignment.py b/src/bonsai/bonsai/tool/alignment.py index 3b2d1c1c33..0e771973e7 100644 --- a/src/bonsai/bonsai/tool/alignment.py +++ b/src/bonsai/bonsai/tool/alignment.py @@ -273,17 +273,10 @@ class Alignment: return None return (point[0] / unit_scale, point[1] / unit_scale) - try: - start, end, ti, ni = align_api.segment_vertices(ifc_file, segment) - except Exception: - try: - mapped = align_api.get_mapped_segments(segment) - curve_segment = mapped[0] - if curve_segment is None: - return None - start, end, ti, ni = align_api.segment_vertices(ifc_file, curve_segment) - except Exception: - return None + result = align_api.segment_vertices(ifc_file, segment) + if result is None: + return None + start, end, ti, ni = result return (convert(start), convert(end), convert(ti), convert(ni)) @@ -658,7 +651,7 @@ class Alignment: the Blender UI properties. Args: - props: The SaikeiAlignmentProperties PropertyGroup + props: The CivilAlignmentProperties PropertyGroup geometry_result: PIGeometryResult from core.alignment """ pis = props.pis @@ -780,12 +773,9 @@ class Alignment: Returns: The parent IfcAlignment if found, None otherwise """ - try: - import ifcopenshell.api.alignment as align_api + import ifcopenshell.api.alignment as align_api - return align_api.get_alignment(layout) - except Exception: - return None + return align_api.get_alignment(layout) @classmethod def safe_layout_horizontal_by_pi_method( @@ -970,11 +960,11 @@ class Alignment: empty.location = blender_pos # Tag with custom properties for identification - empty["saikei_is_pi_empty"] = True - empty["saikei_pi_index"] = i - empty["saikei_pi_radius"] = pi["radius"] - empty["saikei_alignment_id"] = alignment_id - empty["saikei_pi_type"] = pi["pi_type"] + empty["civil_is_pi_empty"] = True + empty["civil_pi_index"] = i + empty["civil_pi_radius"] = pi["radius"] + empty["civil_alignment_id"] = alignment_id + empty["civil_pi_type"] = pi["pi_type"] # Parent to alignment object empty.parent = alignment_obj @@ -1002,11 +992,11 @@ class Alignment: empties = [] for obj in bpy.data.objects: - if obj.get("saikei_is_pi_empty") and obj.get("saikei_alignment_id") == alignment_id: + if obj.get("civil_is_pi_empty") and obj.get("civil_alignment_id") == alignment_id: empties.append(obj) # Sort by PI index - empties.sort(key=lambda e: e.get("saikei_pi_index", 0)) + empties.sort(key=lambda e: e.get("civil_pi_index", 0)) return empties @@ -1059,7 +1049,7 @@ class Alignment: # Collect radii for interior PIs only (not first or last) if 0 < i < len(empties) - 1: - radius = empty.get("saikei_pi_radius", 0.0) + radius = empty.get("civil_pi_radius", 0.0) radii.append(radius) return (hpoints, radii)