mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 18:16:40 +00:00
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 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# Bonsai - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>, 2026 Michael Yoder <myoder@desertspringscivil.com>
|
||||
#
|
||||
# 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Bonsai - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>, 2022 Yassine Oualid <yassine@sigmadimensions.com>
|
||||
# Copyright (C) 2020, 2021 Dion Moult <dion@thinkmoult.com>, 2022 Yassine Oualid <yassine@sigmadimensions.com>, 2026 Michael Yoder <myoder@desertspringscivil.com>
|
||||
#
|
||||
# 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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user