mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 01:41:57 +00:00
Forgot to commit these files
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
# ==============================================================================
|
||||
# Saikei Civil - Civil Engineering Tools for Blender
|
||||
# Copyright (c) 2025 Michael Yoder / Desert Springs Civil Engineering PLLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
# You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Primary Author: Michael Yoder
|
||||
# Company: Desert Springs Civil Engineering PLLC
|
||||
# ==============================================================================
|
||||
|
||||
|
||||
"""Data caching layer for the alignment module
|
||||
|
||||
This module provides cached access to alignment data for UI display,
|
||||
following Bonsai's data loading pattern.
|
||||
"""
|
||||
|
||||
|
||||
def get_ifc_file():
|
||||
"""Get the current IFC file from Bonsai"""
|
||||
try:
|
||||
import bonsai.tool as tool
|
||||
|
||||
return tool.Ifc.get()
|
||||
except (ImportError, AttributeError):
|
||||
return None
|
||||
|
||||
|
||||
class AlignmentData:
|
||||
"""Cached alignment data for UI display"""
|
||||
|
||||
data = {}
|
||||
is_loaded = False
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
"""Load alignment data from IFC file"""
|
||||
cls.data = {
|
||||
"alignments": [],
|
||||
"active_alignment": None,
|
||||
"segments": [],
|
||||
}
|
||||
|
||||
ifc = get_ifc_file()
|
||||
if ifc is None:
|
||||
cls.is_loaded = True
|
||||
return
|
||||
|
||||
# Load all alignments
|
||||
alignments = ifc.by_type("IfcAlignment")
|
||||
cls.data["alignments"] = [
|
||||
{
|
||||
"id": a.id(),
|
||||
"name": a.Name or f"Alignment {a.id()}",
|
||||
"global_id": a.GlobalId,
|
||||
}
|
||||
for a in alignments
|
||||
]
|
||||
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
def refresh(cls):
|
||||
"""Force refresh of alignment data"""
|
||||
cls.is_loaded = False
|
||||
cls.load()
|
||||
@@ -0,0 +1,258 @@
|
||||
# ==============================================================================
|
||||
# Saikei Civil - Civil Engineering Tools for Blender
|
||||
# Copyright (c) 2025 Michael Yoder / Desert Springs Civil Engineering PLLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
# You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Primary Author: Michael Yoder
|
||||
# Company: Desert Springs Civil Engineering PLLC
|
||||
# ==============================================================================
|
||||
|
||||
|
||||
"""Property groups for the alignment module"""
|
||||
|
||||
import bpy
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
StringProperty,
|
||||
FloatProperty,
|
||||
IntProperty,
|
||||
BoolProperty,
|
||||
CollectionProperty,
|
||||
EnumProperty,
|
||||
)
|
||||
|
||||
|
||||
def get_pi_type_items(self, context):
|
||||
"""Get available PI types based on position in list"""
|
||||
# First and last PIs are always endpoints (no curve)
|
||||
# Interior PIs can have curves
|
||||
return [
|
||||
("ENDPOINT", "Endpoint", "Start or end point (no curve)"),
|
||||
("TANGENT", "Tangent", "Pass-through point (no curve)"),
|
||||
("CURVE", "Curve", "Point of intersection with curve"),
|
||||
]
|
||||
|
||||
|
||||
def _on_radius_update(self, context):
|
||||
"""Callback when radius property changes.
|
||||
|
||||
This dynamically imports the operator module to call on_radius_changed,
|
||||
avoiding circular imports since prop.py is imported before operator.py.
|
||||
"""
|
||||
from . import operator as ops
|
||||
|
||||
ops.on_radius_changed(self, context)
|
||||
|
||||
|
||||
class AlignmentPI(PropertyGroup):
|
||||
"""Property group for a single PI (Point of Intersection)
|
||||
|
||||
In the PI method, alignments are defined by:
|
||||
- Endpoint PIs: Start (POB) and End (POE) points
|
||||
- Interior PIs: Points where tangents intersect, optionally with curves
|
||||
"""
|
||||
|
||||
# Coordinates
|
||||
x: FloatProperty(
|
||||
name="X",
|
||||
description="X coordinate (Easting)",
|
||||
default=0.0,
|
||||
precision=3,
|
||||
unit="LENGTH",
|
||||
)
|
||||
|
||||
y: FloatProperty(
|
||||
name="Y",
|
||||
description="Y coordinate (Northing)",
|
||||
default=0.0,
|
||||
precision=3,
|
||||
unit="LENGTH",
|
||||
)
|
||||
|
||||
# PI Type
|
||||
pi_type: EnumProperty(
|
||||
name="Type",
|
||||
description="Type of PI point",
|
||||
items=[
|
||||
("ENDPOINT", "Endpoint", "Start or end point (no curve)"),
|
||||
("TANGENT", "Tangent", "Pass-through point (no curve)"),
|
||||
("CURVE", "Curve", "Point of intersection with curve"),
|
||||
],
|
||||
default="TANGENT",
|
||||
)
|
||||
|
||||
# Curve parameters (only used when pi_type == "CURVE")
|
||||
radius: FloatProperty(
|
||||
name="Radius",
|
||||
description="Curve radius (0 = no curve, sharp angle)",
|
||||
default=0.0,
|
||||
min=0.0,
|
||||
precision=3,
|
||||
unit="LENGTH",
|
||||
update=_on_radius_update,
|
||||
)
|
||||
|
||||
# Computed/display values (updated by recalculate operator)
|
||||
length_to_next: FloatProperty(
|
||||
name="Length",
|
||||
description="Length of tangent to next PI",
|
||||
default=0.0,
|
||||
precision=3,
|
||||
unit="LENGTH",
|
||||
)
|
||||
|
||||
direction_to_next: FloatProperty(
|
||||
name="Direction",
|
||||
description="Bearing/direction to next PI (degrees)",
|
||||
default=0.0,
|
||||
precision=4,
|
||||
subtype="ANGLE",
|
||||
)
|
||||
|
||||
# Station at this PI (computed)
|
||||
station: FloatProperty(
|
||||
name="Station",
|
||||
description="Station value at this PI",
|
||||
default=0.0,
|
||||
precision=2,
|
||||
)
|
||||
|
||||
# Selection state
|
||||
is_selected: BoolProperty(
|
||||
name="Selected",
|
||||
description="Whether this PI is selected for editing",
|
||||
default=False,
|
||||
)
|
||||
|
||||
|
||||
class AlignmentSegmentItem(PropertyGroup):
|
||||
"""Property group for displaying alignment segments in a UIList"""
|
||||
|
||||
name: StringProperty(name="Name", default="")
|
||||
segment_type: StringProperty(name="Type", default="LINE")
|
||||
length: FloatProperty(name="Length", default=0.0, unit="LENGTH")
|
||||
ifc_id: IntProperty(name="IFC ID", default=0)
|
||||
|
||||
|
||||
class AlignmentDisplayRow(PropertyGroup):
|
||||
"""Property group for interleaved point/segment display in the table.
|
||||
|
||||
This creates the Civil 3D-style view where points and segments
|
||||
are shown on separate rows:
|
||||
Point 1 (End)
|
||||
Segment 1 (Tan)
|
||||
Point 2 (Tan)
|
||||
Segment 2 (Tan)
|
||||
...
|
||||
"""
|
||||
|
||||
# Row type discriminator
|
||||
row_type: EnumProperty(
|
||||
name="Row Type",
|
||||
items=[
|
||||
("POINT", "Point", "A PI point row"),
|
||||
("SEGMENT", "Segment", "A segment row between points"),
|
||||
],
|
||||
default="POINT",
|
||||
)
|
||||
|
||||
# Segment number (1, 2, 3...) - only for SEGMENT rows
|
||||
segment_number: IntProperty(name="Segment #", default=0)
|
||||
|
||||
# Point index in the pis collection - for both types
|
||||
# For POINT rows: the PI index
|
||||
# For SEGMENT rows: the starting PI index of this segment
|
||||
pi_index: IntProperty(name="PI Index", default=0)
|
||||
|
||||
# Display type string (End, Tan, Curve for points; Tan, Curve for segments)
|
||||
display_type: StringProperty(name="Type", default="")
|
||||
|
||||
# Point coordinates (only for POINT rows)
|
||||
x: FloatProperty(name="X", default=0.0, precision=3, unit="LENGTH")
|
||||
y: FloatProperty(name="Y", default=0.0, precision=3, unit="LENGTH")
|
||||
|
||||
# Segment properties (only for SEGMENT rows)
|
||||
length: FloatProperty(name="Length", default=0.0, precision=2, unit="LENGTH")
|
||||
radius: FloatProperty(name="Radius", default=0.0, precision=2, unit="LENGTH")
|
||||
arc_length: FloatProperty(name="Arc Length", default=0.0, precision=2, unit="LENGTH")
|
||||
|
||||
|
||||
class SaikeiAlignmentProperties(PropertyGroup):
|
||||
"""Properties for the alignment module"""
|
||||
|
||||
# Active alignment selection
|
||||
active_alignment_id: IntProperty(
|
||||
name="Active Alignment ID",
|
||||
description="IFC entity ID of the active alignment",
|
||||
default=0,
|
||||
)
|
||||
|
||||
active_alignment_name: StringProperty(
|
||||
name="Active Alignment",
|
||||
description="Name of the currently active alignment",
|
||||
default="",
|
||||
)
|
||||
|
||||
# New alignment creation properties
|
||||
new_alignment_name: StringProperty(
|
||||
name="Name",
|
||||
description="Name for new alignment",
|
||||
default="Alignment 1",
|
||||
)
|
||||
|
||||
start_station: FloatProperty(
|
||||
name="Start Station",
|
||||
description="Starting station value (e.g., 10000 for 100+00)",
|
||||
default=10000.0,
|
||||
min=0.0,
|
||||
)
|
||||
|
||||
# PI collection for PI method creation
|
||||
pis: CollectionProperty(type=AlignmentPI)
|
||||
active_pi_index: IntProperty(name="Active PI", default=0)
|
||||
|
||||
# Segment display
|
||||
segments: CollectionProperty(type=AlignmentSegmentItem)
|
||||
active_segment_index: IntProperty(name="Active Segment", default=0)
|
||||
|
||||
# Combined point/segment display rows (for Civil 3D-style table)
|
||||
display_rows: CollectionProperty(type=AlignmentDisplayRow)
|
||||
active_display_row_index: IntProperty(name="Active Display Row", default=0)
|
||||
|
||||
# Editing state
|
||||
is_editing: BoolProperty(
|
||||
name="Is Editing",
|
||||
description="Whether alignment is being edited",
|
||||
default=False,
|
||||
)
|
||||
|
||||
# Display options
|
||||
show_pi_markers: BoolProperty(
|
||||
name="Show PI Markers",
|
||||
description="Show PI markers in viewport",
|
||||
default=True,
|
||||
)
|
||||
|
||||
show_station_labels: BoolProperty(
|
||||
name="Show Station Labels",
|
||||
description="Show station labels along alignment",
|
||||
default=True,
|
||||
)
|
||||
|
||||
station_interval: FloatProperty(
|
||||
name="Station Interval",
|
||||
description="Interval between station markers",
|
||||
default=100.0,
|
||||
min=1.0,
|
||||
unit="LENGTH",
|
||||
)
|
||||
@@ -0,0 +1,366 @@
|
||||
# ==============================================================================
|
||||
# Saikei Civil - Civil Engineering Tools for Blender
|
||||
# Copyright (c) 2025 Michael Yoder / Desert Springs Civil Engineering PLLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
# You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Primary Author: Michael Yoder
|
||||
# Company: Desert Springs Civil Engineering PLLC
|
||||
# ==============================================================================
|
||||
|
||||
|
||||
"""UI panels for the alignment module
|
||||
|
||||
All panels appear in the VIEW_3D N-panel under the "Saikei Civil" tab.
|
||||
"""
|
||||
|
||||
import bpy
|
||||
from bpy.types import Panel, UIList
|
||||
|
||||
|
||||
def get_ifc_file():
|
||||
"""Get the current IFC file from Bonsai"""
|
||||
try:
|
||||
import bonsai.tool as tool
|
||||
|
||||
return tool.Ifc.get()
|
||||
except (ImportError, AttributeError):
|
||||
return None
|
||||
|
||||
|
||||
def is_ifc4x3():
|
||||
"""Check if the current IFC file is IFC4X3 schema"""
|
||||
ifc = get_ifc_file()
|
||||
return ifc is not None and ifc.schema == "IFC4X3"
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# UILists
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class SAIKEI_UL_alignment_pis(UIList):
|
||||
"""UIList for displaying interleaved points and segments (Civil 3D style)
|
||||
|
||||
Row types:
|
||||
- POINT rows: End (endpoint), Mid (interior PI without curve)
|
||||
- SEGMENT rows: Tan (tangent line), Curve (circular arc)
|
||||
|
||||
When a Mid point has radius > 0, it becomes a Curve segment row.
|
||||
"""
|
||||
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
|
||||
if self.layout_type in {"DEFAULT", "COMPACT"}:
|
||||
row = layout.row(align=True)
|
||||
|
||||
if item.row_type == "POINT":
|
||||
# Point row: No., Type, X, Y, Length, Radius
|
||||
row.label(text="") # No segment number for points
|
||||
|
||||
# Type with point/dot icon
|
||||
# "End" = endpoint (POB/POE), "Mid" = interior PI point
|
||||
row.label(text=item.display_type, icon="DOT")
|
||||
|
||||
# X, Y coordinates - get actual PI for editing
|
||||
pi = data.pis[item.pi_index] if item.pi_index < len(data.pis) else None
|
||||
if pi:
|
||||
sub = row.row(align=True)
|
||||
sub.prop(pi, "x", text="")
|
||||
sub.prop(pi, "y", text="")
|
||||
else:
|
||||
row.label(text=f"{item.x:.2f}")
|
||||
row.label(text=f"{item.y:.2f}")
|
||||
|
||||
# Length column - empty for point rows
|
||||
row.label(text="")
|
||||
|
||||
# Radius column - editable for Mid points (where curves can be added)
|
||||
if item.display_type == "Mid" and pi:
|
||||
row.prop(pi, "radius", text="")
|
||||
else:
|
||||
row.label(text="")
|
||||
|
||||
elif item.row_type == "SEGMENT":
|
||||
if item.display_type == "Curve":
|
||||
# Curve segment row: No., Type (arc icon), X, Y, Arc Length, Radius
|
||||
row.label(text=f"{item.segment_number}")
|
||||
row.label(text="Curve", icon="SPHERECURVE")
|
||||
|
||||
# Show PI coordinates on curve row
|
||||
row.label(text=f"{item.x:.2f}")
|
||||
row.label(text=f"{item.y:.2f}")
|
||||
|
||||
# Arc length
|
||||
row.label(text=f"{item.arc_length:.2f}")
|
||||
|
||||
# Radius - editable so user can modify or delete curve (set to 0)
|
||||
pi = data.pis[item.pi_index] if item.pi_index < len(data.pis) else None
|
||||
if pi:
|
||||
row.prop(pi, "radius", text="")
|
||||
else:
|
||||
row.label(text=f"{item.radius:.2f}")
|
||||
else:
|
||||
# Tangent segment row: No., Type (line icon), -, -, Length, -
|
||||
row.label(text=f"{item.segment_number}")
|
||||
row.label(text="Tan", icon="IPO_LINEAR")
|
||||
|
||||
# No X, Y for tangent segments
|
||||
row.label(text="")
|
||||
row.label(text="")
|
||||
|
||||
# Length
|
||||
row.label(text=f"{item.length:.2f}")
|
||||
|
||||
# No radius for tangent segments
|
||||
row.label(text="-")
|
||||
|
||||
elif self.layout_type == "GRID":
|
||||
layout.alignment = "CENTER"
|
||||
layout.label(text="", icon="DECORATE")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Main Panel
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class SAIKEI_PT_horizontal_alignment(Panel):
|
||||
"""Main Horizontal Alignment panel in the N-panel"""
|
||||
|
||||
bl_label = "Horizontal Alignment"
|
||||
bl_idname = "SAIKEI_PT_horizontal_alignment"
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "Saikei Civil"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
props = context.scene.SaikeiAlignmentProperties
|
||||
|
||||
# Status box
|
||||
box = layout.box()
|
||||
ifc = get_ifc_file()
|
||||
|
||||
if ifc is None:
|
||||
box.label(text="No IFC file loaded", icon="ERROR")
|
||||
box.label(text="Open an IFC4X3 file via Bonsai")
|
||||
return
|
||||
|
||||
if ifc.schema != "IFC4X3":
|
||||
box.label(text=f"Schema: {ifc.schema}", icon="ERROR")
|
||||
box.label(text="Alignments require IFC4X3")
|
||||
return
|
||||
|
||||
# IFC file is loaded and correct schema
|
||||
row = box.row()
|
||||
row.label(text="IFC4X3", icon="CHECKMARK")
|
||||
|
||||
# Count alignments
|
||||
alignments = ifc.by_type("IfcAlignment")
|
||||
row.label(text=f"Alignments: {len(alignments)}")
|
||||
|
||||
# Active alignment selector
|
||||
if alignments:
|
||||
box = layout.box()
|
||||
box.label(text="Active Alignment:", icon="CURVE_PATH")
|
||||
row = box.row()
|
||||
row.prop(props, "active_alignment_name", text="")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Creation Sub-Panel
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class SAIKEI_PT_alignment_creation(Panel):
|
||||
"""Sub-panel for alignment creation tools"""
|
||||
|
||||
bl_label = "Creation"
|
||||
bl_idname = "SAIKEI_PT_alignment_creation"
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "Saikei Civil"
|
||||
bl_parent_id = "SAIKEI_PT_horizontal_alignment"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return is_ifc4x3()
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
props = context.scene.SaikeiAlignmentProperties
|
||||
|
||||
# New alignment properties
|
||||
box = layout.box()
|
||||
box.label(text="New Alignment:", icon="ADD")
|
||||
box.prop(props, "new_alignment_name")
|
||||
box.prop(props, "start_station")
|
||||
|
||||
# Creation operators
|
||||
col = layout.column(align=True)
|
||||
col.operator("saikei.create_alignment", icon="ADD")
|
||||
col.operator("saikei.create_alignment_by_pi", icon="CURVE_PATH")
|
||||
col.operator("saikei.import_alignment_csv", icon="IMPORT")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# PI Editor Sub-Panel
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class SAIKEI_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_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "Saikei Civil"
|
||||
bl_parent_id = "SAIKEI_PT_horizontal_alignment"
|
||||
bl_options = set() # Open by default
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return is_ifc4x3()
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
props = context.scene.SaikeiAlignmentProperties
|
||||
|
||||
# Header row with column labels
|
||||
header = layout.row(align=True)
|
||||
header.label(text="No.")
|
||||
header.label(text="Type")
|
||||
header.label(text="X")
|
||||
header.label(text="Y")
|
||||
header.label(text="Length")
|
||||
header.label(text="Radius")
|
||||
|
||||
# Combined point/segment list (interleaved view)
|
||||
row = layout.row()
|
||||
row.template_list(
|
||||
"SAIKEI_UL_alignment_pis",
|
||||
"",
|
||||
props,
|
||||
"display_rows",
|
||||
props,
|
||||
"active_display_row_index",
|
||||
rows=8,
|
||||
)
|
||||
|
||||
# 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.separator()
|
||||
col.operator("saikei.pick_pi_from_viewport", icon="EYEDROPPER", text="")
|
||||
|
||||
# Active item details - show details based on selected row
|
||||
if props.display_rows and 0 <= props.active_display_row_index < len(props.display_rows):
|
||||
active_row = props.display_rows[props.active_display_row_index]
|
||||
|
||||
if active_row.row_type == "POINT" and active_row.pi_index < len(props.pis):
|
||||
pi = props.pis[active_row.pi_index]
|
||||
|
||||
box = layout.box()
|
||||
box.label(text=f"PI {active_row.pi_index + 1} Details:", icon="PROPERTIES")
|
||||
|
||||
row = box.row()
|
||||
row.prop(pi, "pi_type", text="Type")
|
||||
|
||||
row = box.row(align=True)
|
||||
row.prop(pi, "x", text="X")
|
||||
row.prop(pi, "y", text="Y")
|
||||
|
||||
# Show radius for interior points (can add curve)
|
||||
if pi.pi_type != "ENDPOINT":
|
||||
row = box.row()
|
||||
row.prop(pi, "radius", text="Radius")
|
||||
|
||||
# Display computed values
|
||||
row = box.row()
|
||||
row.label(text=f"Station: {pi.station:.2f}")
|
||||
row.label(text=f"Length: {pi.length_to_next:.2f}")
|
||||
|
||||
elif active_row.row_type == "SEGMENT":
|
||||
if active_row.display_type == "Curve":
|
||||
# Curve segment - show curve details with editable radius
|
||||
pi = props.pis[active_row.pi_index] if active_row.pi_index < len(props.pis) else None
|
||||
|
||||
box = layout.box()
|
||||
box.label(text=f"Curve {active_row.segment_number} Details:", icon="SPHERECURVE")
|
||||
|
||||
row = box.row()
|
||||
row.label(text=f"PI Location: ({active_row.x:.2f}, {active_row.y:.2f})")
|
||||
|
||||
row = box.row()
|
||||
row.label(text=f"Arc Length: {active_row.arc_length:.2f}")
|
||||
|
||||
# Editable radius
|
||||
if pi:
|
||||
row = box.row()
|
||||
row.prop(pi, "radius", text="Radius")
|
||||
else:
|
||||
row = box.row()
|
||||
row.label(text=f"Radius: {active_row.radius:.2f}")
|
||||
else:
|
||||
# Tangent segment
|
||||
box = layout.box()
|
||||
box.label(text=f"Tangent {active_row.segment_number} Details:", icon="IPO_LINEAR")
|
||||
|
||||
row = box.row()
|
||||
row.label(text=f"Length: {active_row.length:.2f}")
|
||||
|
||||
# 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")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Stationing Sub-Panel
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class SAIKEI_PT_alignment_stationing(Panel):
|
||||
"""Sub-panel for stationing and referents"""
|
||||
|
||||
bl_label = "Stationing"
|
||||
bl_idname = "SAIKEI_PT_alignment_stationing"
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "Saikei Civil"
|
||||
bl_parent_id = "SAIKEI_PT_horizontal_alignment"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return is_ifc4x3()
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
props = context.scene.SaikeiAlignmentProperties
|
||||
|
||||
# Station display options
|
||||
box = layout.box()
|
||||
box.label(text="Display:", icon="HIDE_OFF")
|
||||
box.prop(props, "show_station_labels")
|
||||
box.prop(props, "station_interval")
|
||||
|
||||
layout.separator()
|
||||
|
||||
# Stationing operators
|
||||
col = layout.column(align=True)
|
||||
col.operator("saikei.add_stationing_referent", icon="EMPTY_AXIS")
|
||||
col.operator("saikei.name_segments", icon="FONT_DATA")
|
||||
Reference in New Issue
Block a user