mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-26 10:11:46 +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
|
# 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.
|
# This file is part of Bonsai.
|
||||||
#
|
#
|
||||||
@@ -25,28 +25,28 @@ classes = (
|
|||||||
# Property groups (must be registered before classes that use them)
|
# Property groups (must be registered before classes that use them)
|
||||||
prop.AlignmentPI,
|
prop.AlignmentPI,
|
||||||
prop.AlignmentDisplayRow,
|
prop.AlignmentDisplayRow,
|
||||||
prop.SaikeiAlignmentProperties,
|
prop.CivilAlignmentProperties,
|
||||||
# UILists
|
# UILists
|
||||||
ui.SAIKEI_UL_alignment_pis,
|
ui.CIVIL_UL_alignment_pis,
|
||||||
operator.ImportAlignmentCSV,
|
operator.ImportAlignmentCSV,
|
||||||
# Operators - PI Management
|
# Operators - PI Management
|
||||||
operator.SAIKEI_OT_add_pi,
|
operator.CIVIL_OT_add_pi,
|
||||||
operator.SAIKEI_OT_remove_pi,
|
operator.CIVIL_OT_remove_pi,
|
||||||
operator.SAIKEI_OT_pick_pi_from_viewport,
|
operator.CIVIL_OT_pick_pi_from_viewport,
|
||||||
operator.SAIKEI_OT_recalculate_pis,
|
operator.CIVIL_OT_recalculate_pis,
|
||||||
operator.SAIKEI_OT_clear_pis,
|
operator.CIVIL_OT_clear_pis,
|
||||||
# Operators - Creation
|
# Operators - Creation
|
||||||
operator.SAIKEI_OT_create_alignment_by_pi,
|
operator.CIVIL_OT_create_alignment_by_pi,
|
||||||
operator.SAIKEI_OT_import_alignment_csv,
|
operator.CIVIL_OT_import_alignment_csv,
|
||||||
# Operators - Stationing
|
# Operators - Stationing
|
||||||
operator.SAIKEI_OT_add_stationing_referent,
|
operator.CIVIL_OT_add_stationing_referent,
|
||||||
operator.SAIKEI_OT_name_segments,
|
operator.CIVIL_OT_name_segments,
|
||||||
# Operators - PI Edit Mode
|
# 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 Panels (appear in Properties sidebar under CIVIL tab)
|
||||||
ui.SAIKEI_PT_alignment_creation,
|
ui.CIVIL_PT_alignment_creation,
|
||||||
ui.SAIKEI_PT_pi_editor,
|
ui.CIVIL_PT_pi_editor,
|
||||||
ui.SAIKEI_PT_alignment_stationing,
|
ui.CIVIL_PT_alignment_stationing,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -55,10 +55,10 @@ def menu_func_import(self, context):
|
|||||||
|
|
||||||
|
|
||||||
def register():
|
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)
|
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
|
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
|
# 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.
|
# 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)
|
2. Rebuild of display_rows (Mid point becomes Curve segment)
|
||||||
3. If an active alignment exists, regeneration of IFC entities
|
3. If an active alignment exists, regeneration of IFC entities
|
||||||
"""
|
"""
|
||||||
props = context.scene.SaikeiAlignmentProperties
|
props = context.scene.CivilAlignmentProperties
|
||||||
recalculate_pi_geometry(props)
|
recalculate_pi_geometry(props)
|
||||||
|
|
||||||
# If there's an active alignment, trigger IFC regeneration
|
# 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"""
|
"""Add a new PI point to the list"""
|
||||||
|
|
||||||
bl_idname = "saikei.add_pi"
|
bl_idname = "civil.add_pi"
|
||||||
bl_label = "Add PI"
|
bl_label = "Add PI"
|
||||||
bl_description = "Add a new PI (Point of Intersection) to the alignment"
|
bl_description = "Add a new PI (Point of Intersection) to the alignment"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
@@ -324,7 +324,7 @@ class SAIKEI_OT_add_pi(Operator):
|
|||||||
return poll_ifc4x3(cls, context)
|
return poll_ifc4x3(cls, context)
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
props = context.scene.SaikeiAlignmentProperties
|
props = context.scene.CivilAlignmentProperties
|
||||||
|
|
||||||
# Add new PI
|
# Add new PI
|
||||||
pi = props.pis.add()
|
pi = props.pis.add()
|
||||||
@@ -363,10 +363,10 @@ class SAIKEI_OT_add_pi(Operator):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class SAIKEI_OT_remove_pi(Operator):
|
class CIVIL_OT_remove_pi(Operator):
|
||||||
"""Remove the selected PI point"""
|
"""Remove the selected PI point"""
|
||||||
|
|
||||||
bl_idname = "saikei.remove_pi"
|
bl_idname = "civil.remove_pi"
|
||||||
bl_label = "Remove PI"
|
bl_label = "Remove PI"
|
||||||
bl_description = "Remove the selected PI from the alignment"
|
bl_description = "Remove the selected PI from the alignment"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
@@ -375,7 +375,7 @@ class SAIKEI_OT_remove_pi(Operator):
|
|||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
if not poll_ifc4x3(cls, context):
|
if not poll_ifc4x3(cls, context):
|
||||||
return False
|
return False
|
||||||
props = context.scene.SaikeiAlignmentProperties
|
props = context.scene.CivilAlignmentProperties
|
||||||
if len(props.pis) == 0:
|
if len(props.pis) == 0:
|
||||||
cls.poll_message_set("No PIs to remove")
|
cls.poll_message_set("No PIs to remove")
|
||||||
return False
|
return False
|
||||||
@@ -389,7 +389,7 @@ class SAIKEI_OT_remove_pi(Operator):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
props = context.scene.SaikeiAlignmentProperties
|
props = context.scene.CivilAlignmentProperties
|
||||||
|
|
||||||
# Get the PI index from the selected display row
|
# Get the PI index from the selected display row
|
||||||
pi_index = -1
|
pi_index = -1
|
||||||
@@ -420,10 +420,10 @@ class SAIKEI_OT_remove_pi(Operator):
|
|||||||
return {"FINISHED"}
|
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"""
|
"""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_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_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"}
|
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
|
Polyline points are in Blender coordinate space. This method converts
|
||||||
each point to IFC coordinate space before storing in props.pis.
|
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_props = tool.Model.get_polyline_props()
|
||||||
polyline_data = polyline_props.insertion_polyline
|
polyline_data = polyline_props.insertion_polyline
|
||||||
if not polyline_data:
|
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)
|
num_points = len(polyline_points)
|
||||||
for i, point in enumerate(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))
|
ifc_coord = tool.Georeference.xyz2enh((point.x, point.y, 0.0))
|
||||||
|
|
||||||
pi = props.pis.add()
|
pi = props.pis.add()
|
||||||
@@ -584,10 +584,10 @@ class SAIKEI_OT_pick_pi_from_viewport(bpy.types.Operator, PolylineOperator, tool
|
|||||||
rebuild_display_rows(props)
|
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"""
|
"""Recalculate PI geometry and update IFC/visualization"""
|
||||||
|
|
||||||
bl_idname = "saikei.recalculate_pis"
|
bl_idname = "civil.recalculate_pis"
|
||||||
bl_label = "Recalculate PIs"
|
bl_label = "Recalculate PIs"
|
||||||
bl_description = "Recalculate geometry, update IFC segments, and refresh visualization"
|
bl_description = "Recalculate geometry, update IFC segments, and refresh visualization"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
@@ -596,7 +596,7 @@ class SAIKEI_OT_recalculate_pis(Operator, tool.Ifc.Operator):
|
|||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
if not poll_ifc4x3(cls, context):
|
if not poll_ifc4x3(cls, context):
|
||||||
return False
|
return False
|
||||||
props = context.scene.SaikeiAlignmentProperties
|
props = context.scene.CivilAlignmentProperties
|
||||||
if len(props.pis) < 2:
|
if len(props.pis) < 2:
|
||||||
cls.poll_message_set("Need at least 2 PIs to recalculate")
|
cls.poll_message_set("Need at least 2 PIs to recalculate")
|
||||||
return False
|
return False
|
||||||
@@ -606,7 +606,7 @@ class SAIKEI_OT_recalculate_pis(Operator, tool.Ifc.Operator):
|
|||||||
import ifcopenshell.api.alignment as align_api
|
import ifcopenshell.api.alignment as align_api
|
||||||
|
|
||||||
ifc = tool.Ifc.get()
|
ifc = tool.Ifc.get()
|
||||||
props = context.scene.SaikeiAlignmentProperties
|
props = context.scene.CivilAlignmentProperties
|
||||||
|
|
||||||
# Recalculate geometry in UI properties
|
# Recalculate geometry in UI properties
|
||||||
recalculate_pi_geometry(props)
|
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")
|
self.report({"ERROR"}, "Alignment has no horizontal layout")
|
||||||
return
|
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]
|
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]]
|
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}")
|
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"""
|
"""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_label = "Clear All PIs"
|
||||||
bl_description = "Remove all PI points and clear segment visualization"
|
bl_description = "Remove all PI points and clear segment visualization"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
@@ -660,7 +660,7 @@ class SAIKEI_OT_clear_pis(Operator, tool.Ifc.Operator):
|
|||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
if not poll_ifc4x3(cls, context):
|
if not poll_ifc4x3(cls, context):
|
||||||
return False
|
return False
|
||||||
props = context.scene.SaikeiAlignmentProperties
|
props = context.scene.CivilAlignmentProperties
|
||||||
if len(props.pis) == 0:
|
if len(props.pis) == 0:
|
||||||
cls.poll_message_set("No PIs to clear")
|
cls.poll_message_set("No PIs to clear")
|
||||||
return False
|
return False
|
||||||
@@ -671,7 +671,7 @@ class SAIKEI_OT_clear_pis(Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
ifc = tool.Ifc.get()
|
ifc = tool.Ifc.get()
|
||||||
props = context.scene.SaikeiAlignmentProperties
|
props = context.scene.CivilAlignmentProperties
|
||||||
|
|
||||||
removed_objects = 0
|
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"""
|
"""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_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_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"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
@@ -715,7 +715,7 @@ class SAIKEI_OT_create_alignment_by_pi(Operator, tool.Ifc.Operator):
|
|||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
if not poll_ifc4x3(cls, context):
|
if not poll_ifc4x3(cls, context):
|
||||||
return False
|
return False
|
||||||
props = context.scene.SaikeiAlignmentProperties
|
props = context.scene.CivilAlignmentProperties
|
||||||
if len(props.pis) < 2:
|
if len(props.pis) < 2:
|
||||||
cls.poll_message_set("Need at least 2 PI points")
|
cls.poll_message_set("Need at least 2 PI points")
|
||||||
return False
|
return False
|
||||||
@@ -725,8 +725,9 @@ class SAIKEI_OT_create_alignment_by_pi(Operator, tool.Ifc.Operator):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def _execute(self, context):
|
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]
|
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]]
|
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"""
|
"""Import alignment from CSV file"""
|
||||||
|
|
||||||
bl_idname = "saikei.import_alignment_csv"
|
bl_idname = "civil.import_alignment_csv"
|
||||||
bl_label = "Import Alignment CSV"
|
bl_label = "Import Alignment CSV"
|
||||||
bl_description = "Import alignment definition from a CSV file"
|
bl_description = "Import alignment definition from a CSV file"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
@@ -776,7 +777,7 @@ class SAIKEI_OT_import_alignment_csv(Operator, tool.Ifc.Operator, ImportHelper):
|
|||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
ifc = tool.Ifc.get()
|
ifc = tool.Ifc.get()
|
||||||
props = context.scene.SaikeiAlignmentProperties
|
props = context.scene.CivilAlignmentProperties
|
||||||
|
|
||||||
alignment = ifcopenshell.api.alignment.create_from_csv(ifc, self.filepath)
|
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"""
|
"""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_label = "Add Stationing Referent"
|
||||||
bl_description = "Add an IfcReferent for stationing"
|
bl_description = "Add an IfcReferent for stationing"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
@@ -819,7 +820,7 @@ class SAIKEI_OT_add_stationing_referent(Operator, tool.Ifc.Operator):
|
|||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
if not poll_ifc4x3(cls, context):
|
if not poll_ifc4x3(cls, context):
|
||||||
return False
|
return False
|
||||||
props = context.scene.SaikeiAlignmentProperties
|
props = context.scene.CivilAlignmentProperties
|
||||||
if props.active_alignment_id == 0:
|
if props.active_alignment_id == 0:
|
||||||
cls.poll_message_set("Select an alignment first")
|
cls.poll_message_set("Select an alignment first")
|
||||||
return False
|
return False
|
||||||
@@ -827,7 +828,7 @@ class SAIKEI_OT_add_stationing_referent(Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
# Default station to start_station from props
|
# Default station to start_station from props
|
||||||
props = context.scene.SaikeiAlignmentProperties
|
props = context.scene.CivilAlignmentProperties
|
||||||
self.station = props.start_station
|
self.station = props.start_station
|
||||||
return context.window_manager.invoke_props_dialog(self)
|
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):
|
def _execute(self, context):
|
||||||
ifc = tool.Ifc.get()
|
ifc = tool.Ifc.get()
|
||||||
props = context.scene.SaikeiAlignmentProperties
|
props = context.scene.CivilAlignmentProperties
|
||||||
|
|
||||||
alignment = tool.Alignment.get_active_alignment()
|
alignment = tool.Alignment.get_active_alignment()
|
||||||
if alignment is None:
|
if alignment is None:
|
||||||
@@ -883,10 +884,10 @@ def format_station(station_value):
|
|||||||
return f"{main}+{offset:05.2f}"
|
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"""
|
"""Auto-name segments based on station values"""
|
||||||
|
|
||||||
bl_idname = "saikei.name_segments"
|
bl_idname = "civil.name_segments"
|
||||||
bl_label = "Name Segments"
|
bl_label = "Name Segments"
|
||||||
bl_description = "Automatically name segments with station-based labels"
|
bl_description = "Automatically name segments with station-based labels"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
@@ -895,7 +896,7 @@ class SAIKEI_OT_name_segments(Operator, tool.Ifc.Operator):
|
|||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
if not poll_ifc4x3(cls, context):
|
if not poll_ifc4x3(cls, context):
|
||||||
return False
|
return False
|
||||||
props = context.scene.SaikeiAlignmentProperties
|
props = context.scene.CivilAlignmentProperties
|
||||||
if props.active_alignment_id == 0:
|
if props.active_alignment_id == 0:
|
||||||
cls.poll_message_set("Select an alignment first")
|
cls.poll_message_set("Select an alignment first")
|
||||||
return False
|
return False
|
||||||
@@ -903,7 +904,7 @@ class SAIKEI_OT_name_segments(Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
ifc = tool.Ifc.get()
|
ifc = tool.Ifc.get()
|
||||||
props = context.scene.SaikeiAlignmentProperties
|
props = context.scene.CivilAlignmentProperties
|
||||||
|
|
||||||
alignment = tool.Alignment.get_active_alignment()
|
alignment = tool.Alignment.get_active_alignment()
|
||||||
if alignment is None:
|
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"""
|
"""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_label = "Edit PIs"
|
||||||
bl_description = "Enter PI edit mode. Move PI points with G key. Press Enter to apply changes, Escape to cancel."
|
bl_description = "Enter PI edit mode. Move PI points with G key. Press Enter to apply changes, Escape to cancel."
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
@@ -938,7 +939,7 @@ class SAIKEI_OT_enter_pi_edit_mode(Operator, tool.Ifc.Operator):
|
|||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
if not poll_ifc4x3(cls, context):
|
if not poll_ifc4x3(cls, context):
|
||||||
return False
|
return False
|
||||||
props = context.scene.SaikeiAlignmentProperties
|
props = context.scene.CivilAlignmentProperties
|
||||||
if props.is_pi_edit_mode:
|
if props.is_pi_edit_mode:
|
||||||
cls.poll_message_set("Already in PI edit mode")
|
cls.poll_message_set("Already in PI edit mode")
|
||||||
return False
|
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")
|
return IfcStore.execute_ifc_operator(self, context, event, method="INVOKE")
|
||||||
|
|
||||||
def _invoke(self, context, event):
|
def _invoke(self, context, event):
|
||||||
props = context.scene.SaikeiAlignmentProperties
|
props = context.scene.CivilAlignmentProperties
|
||||||
self._alignment_id = props.active_alignment_id
|
self._alignment_id = props.active_alignment_id
|
||||||
|
|
||||||
# Enter edit mode via core layer (validates and creates empties)
|
# 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")
|
return IfcStore.execute_ifc_operator(self, context, event, method="MODAL")
|
||||||
|
|
||||||
def _modal(self, context, event):
|
def _modal(self, context, event):
|
||||||
props = context.scene.SaikeiAlignmentProperties
|
props = context.scene.CivilAlignmentProperties
|
||||||
|
|
||||||
# Safety: check if empties still exist (handles undo edge case)
|
# Safety: check if empties still exist (handles undo edge case)
|
||||||
if not self._empties_still_exist():
|
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):
|
def _cleanup_and_finish(self, context, apply: bool):
|
||||||
"""Exit edit mode, optionally applying changes."""
|
"""Exit edit mode, optionally applying changes."""
|
||||||
props = context.scene.SaikeiAlignmentProperties
|
props = context.scene.CivilAlignmentProperties
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if apply:
|
if apply:
|
||||||
|
|||||||
@@ -50,9 +50,11 @@ class AlignmentPI(PropertyGroup):
|
|||||||
- Interior PIs: Points where tangents intersect, optionally with curves
|
- Interior PIs: Points where tangents intersect, optionally with curves
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Coordinates
|
# Coordinates stored as global easting/northing (map coordinates).
|
||||||
e: StringProperty(name="E", description="Easting", default="0.0")
|
# Coordinate flow: Blender coords -> xyz2enh() -> global E/N (stored here)
|
||||||
n: StringProperty(name="N", description="Northing", default="0.0")
|
# 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
|
||||||
pi_type: EnumProperty(
|
pi_type: EnumProperty(
|
||||||
@@ -146,7 +148,7 @@ class AlignmentDisplayRow(PropertyGroup):
|
|||||||
arc_length: FloatProperty(name="Arc Length", default=0.0, precision=2, unit="LENGTH")
|
arc_length: FloatProperty(name="Arc Length", default=0.0, precision=2, unit="LENGTH")
|
||||||
|
|
||||||
|
|
||||||
class SaikeiAlignmentProperties(PropertyGroup):
|
class CivilAlignmentProperties(PropertyGroup):
|
||||||
"""Properties for the alignment module"""
|
"""Properties for the alignment module"""
|
||||||
|
|
||||||
# Active alignment selection
|
# 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)
|
"""UIList for displaying interleaved points and segments (Civil 3D style)
|
||||||
|
|
||||||
Row types:
|
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"""
|
"""Sub-panel for alignment creation tools"""
|
||||||
|
|
||||||
bl_label = "Creation"
|
bl_label = "Creation"
|
||||||
bl_idname = "SAIKEI_PT_alignment_creation"
|
bl_idname = "CIVIL_PT_alignment_creation"
|
||||||
bl_space_type = "PROPERTIES"
|
bl_space_type = "PROPERTIES"
|
||||||
bl_region_type = "WINDOW"
|
bl_region_type = "WINDOW"
|
||||||
bl_context = "scene"
|
bl_context = "scene"
|
||||||
@@ -140,7 +140,7 @@ class SAIKEI_PT_alignment_creation(Panel):
|
|||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
props = context.scene.SaikeiAlignmentProperties
|
props = context.scene.CivilAlignmentProperties
|
||||||
|
|
||||||
# New alignment properties
|
# New alignment properties
|
||||||
box = layout.box()
|
box = layout.box()
|
||||||
@@ -150,7 +150,7 @@ class SAIKEI_PT_alignment_creation(Panel):
|
|||||||
|
|
||||||
# Creation operators
|
# Creation operators
|
||||||
col = layout.column(align=True)
|
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)"""
|
"""Sub-panel for PI point table editor (Civil 3D style grid view)"""
|
||||||
|
|
||||||
bl_label = "PI Editor"
|
bl_label = "PI Editor"
|
||||||
bl_idname = "SAIKEI_PT_pi_editor"
|
bl_idname = "CIVIL_PT_pi_editor"
|
||||||
bl_space_type = "PROPERTIES"
|
bl_space_type = "PROPERTIES"
|
||||||
bl_region_type = "WINDOW"
|
bl_region_type = "WINDOW"
|
||||||
bl_context = "scene"
|
bl_context = "scene"
|
||||||
@@ -175,7 +175,7 @@ class SAIKEI_PT_pi_editor(Panel):
|
|||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
props = context.scene.SaikeiAlignmentProperties
|
props = context.scene.CivilAlignmentProperties
|
||||||
|
|
||||||
# PI Edit Mode indicator
|
# PI Edit Mode indicator
|
||||||
if props.is_pi_edit_mode:
|
if props.is_pi_edit_mode:
|
||||||
@@ -193,7 +193,7 @@ class SAIKEI_PT_pi_editor(Panel):
|
|||||||
if props.active_alignment_id != 0:
|
if props.active_alignment_id != 0:
|
||||||
box = layout.box()
|
box = layout.box()
|
||||||
box.label(text="Edit Alignment:", icon="EDITMODE_HLT")
|
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()
|
layout.separator()
|
||||||
|
|
||||||
# Header row with column labels
|
# Header row with column labels
|
||||||
@@ -208,7 +208,7 @@ class SAIKEI_PT_pi_editor(Panel):
|
|||||||
# Combined point/segment list (interleaved view)
|
# Combined point/segment list (interleaved view)
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.template_list(
|
row.template_list(
|
||||||
"SAIKEI_UL_alignment_pis",
|
"CIVIL_UL_alignment_pis",
|
||||||
"",
|
"",
|
||||||
props,
|
props,
|
||||||
"display_rows",
|
"display_rows",
|
||||||
@@ -219,16 +219,16 @@ class SAIKEI_PT_pi_editor(Panel):
|
|||||||
|
|
||||||
# Side buttons for list management
|
# Side buttons for list management
|
||||||
col = row.column(align=True)
|
col = row.column(align=True)
|
||||||
col.operator("saikei.add_pi", icon="ADD", text="")
|
col.operator("civil.add_pi", icon="ADD", text="")
|
||||||
col.operator("saikei.remove_pi", icon="REMOVE", text="")
|
col.operator("civil.remove_pi", icon="REMOVE", text="")
|
||||||
col.separator()
|
col.separator()
|
||||||
col.operator("saikei.pick_pi_from_viewport", icon="EYEDROPPER", text="")
|
col.operator("civil.pick_pi_from_viewport", icon="EYEDROPPER", text="")
|
||||||
|
|
||||||
# Bottom actions
|
# Bottom actions
|
||||||
layout.separator()
|
layout.separator()
|
||||||
row = layout.row(align=True)
|
row = layout.row(align=True)
|
||||||
row.operator("saikei.recalculate_pis", icon="FILE_REFRESH", text="Recalculate")
|
row.operator("civil.recalculate_pis", icon="FILE_REFRESH", text="Recalculate")
|
||||||
row.operator("saikei.clear_pis", icon="TRASH", text="Clear All")
|
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"""
|
"""Sub-panel for stationing and referents"""
|
||||||
|
|
||||||
bl_label = "Stationing"
|
bl_label = "Stationing"
|
||||||
bl_idname = "SAIKEI_PT_alignment_stationing"
|
bl_idname = "CIVIL_PT_alignment_stationing"
|
||||||
bl_space_type = "PROPERTIES"
|
bl_space_type = "PROPERTIES"
|
||||||
bl_region_type = "WINDOW"
|
bl_region_type = "WINDOW"
|
||||||
bl_context = "scene"
|
bl_context = "scene"
|
||||||
@@ -253,7 +253,7 @@ class SAIKEI_PT_alignment_stationing(Panel):
|
|||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
props = context.scene.SaikeiAlignmentProperties
|
props = context.scene.CivilAlignmentProperties
|
||||||
|
|
||||||
# Station display options
|
# Station display options
|
||||||
box = layout.box()
|
box = layout.box()
|
||||||
@@ -265,5 +265,5 @@ class SAIKEI_PT_alignment_stationing(Panel):
|
|||||||
|
|
||||||
# Stationing operators
|
# Stationing operators
|
||||||
col = layout.column(align=True)
|
col = layout.column(align=True)
|
||||||
col.operator("saikei.add_stationing_referent", icon="EMPTY_AXIS")
|
col.operator("civil.add_stationing_referent", icon="EMPTY_AXIS")
|
||||||
col.operator("saikei.name_segments", icon="FONT_DATA")
|
col.operator("civil.name_segments", icon="FONT_DATA")
|
||||||
|
|||||||
@@ -273,17 +273,10 @@ class Alignment:
|
|||||||
return None
|
return None
|
||||||
return (point[0] / unit_scale, point[1] / unit_scale)
|
return (point[0] / unit_scale, point[1] / unit_scale)
|
||||||
|
|
||||||
try:
|
result = align_api.segment_vertices(ifc_file, segment)
|
||||||
start, end, ti, ni = align_api.segment_vertices(ifc_file, segment)
|
if result is None:
|
||||||
except Exception:
|
return None
|
||||||
try:
|
start, end, ti, ni = result
|
||||||
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
|
|
||||||
|
|
||||||
return (convert(start), convert(end), convert(ti), convert(ni))
|
return (convert(start), convert(end), convert(ti), convert(ni))
|
||||||
|
|
||||||
@@ -658,7 +651,7 @@ class Alignment:
|
|||||||
the Blender UI properties.
|
the Blender UI properties.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
props: The SaikeiAlignmentProperties PropertyGroup
|
props: The CivilAlignmentProperties PropertyGroup
|
||||||
geometry_result: PIGeometryResult from core.alignment
|
geometry_result: PIGeometryResult from core.alignment
|
||||||
"""
|
"""
|
||||||
pis = props.pis
|
pis = props.pis
|
||||||
@@ -780,12 +773,9 @@ class Alignment:
|
|||||||
Returns:
|
Returns:
|
||||||
The parent IfcAlignment if found, None otherwise
|
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)
|
return align_api.get_alignment(layout)
|
||||||
except Exception:
|
|
||||||
return None
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def safe_layout_horizontal_by_pi_method(
|
def safe_layout_horizontal_by_pi_method(
|
||||||
@@ -970,11 +960,11 @@ class Alignment:
|
|||||||
empty.location = blender_pos
|
empty.location = blender_pos
|
||||||
|
|
||||||
# Tag with custom properties for identification
|
# Tag with custom properties for identification
|
||||||
empty["saikei_is_pi_empty"] = True
|
empty["civil_is_pi_empty"] = True
|
||||||
empty["saikei_pi_index"] = i
|
empty["civil_pi_index"] = i
|
||||||
empty["saikei_pi_radius"] = pi["radius"]
|
empty["civil_pi_radius"] = pi["radius"]
|
||||||
empty["saikei_alignment_id"] = alignment_id
|
empty["civil_alignment_id"] = alignment_id
|
||||||
empty["saikei_pi_type"] = pi["pi_type"]
|
empty["civil_pi_type"] = pi["pi_type"]
|
||||||
|
|
||||||
# Parent to alignment object
|
# Parent to alignment object
|
||||||
empty.parent = alignment_obj
|
empty.parent = alignment_obj
|
||||||
@@ -1002,11 +992,11 @@ class Alignment:
|
|||||||
empties = []
|
empties = []
|
||||||
|
|
||||||
for obj in bpy.data.objects:
|
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)
|
empties.append(obj)
|
||||||
|
|
||||||
# Sort by PI index
|
# 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
|
return empties
|
||||||
|
|
||||||
@@ -1059,7 +1049,7 @@ class Alignment:
|
|||||||
|
|
||||||
# Collect radii for interior PIs only (not first or last)
|
# Collect radii for interior PIs only (not first or last)
|
||||||
if 0 < i < len(empties) - 1:
|
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)
|
radii.append(radius)
|
||||||
|
|
||||||
return (hpoints, radii)
|
return (hpoints, radii)
|
||||||
|
|||||||
Reference in New Issue
Block a user