Improves interactive editing of horizontal alignment

This commit is contained in:
Richard Brice
2026-09-13 11:26:26 -07:00
parent b8d8dcf6ba
commit 2cbb48e2ef
2 changed files with 34 additions and 2 deletions
+17 -2
View File
@@ -39,14 +39,28 @@ def _on_vertical_visibility_update(self, context):
VerticalProfileDecorator.tag_redraw() VerticalProfileDecorator.tag_redraw()
# Blender requires a dynamic EnumProperty callback to keep a reference to the
# items it returns — the strings are read by the C/RNA layer after the Python
# call returns, and if the list is only local to the function it can be
# garbage-collected before that happens. Without this cache, the dropdown can
# resolve its stored index against a stale/freed items list — e.g. right
# after adding and drawing a new alignment — leaving the dropdown (and the
# segment table, which reads its value) showing a different alignment than
# the one actually active in the viewport. See bpy.props.EnumProperty docs.
_alignment_enum_items_cache: list[tuple[str, str, str]] = []
def _alignment_enum_items(self, context): def _alignment_enum_items(self, context):
"""Dynamic items: all top-level IfcAlignment entities in the current file.""" """Dynamic items: all top-level IfcAlignment entities in the current file."""
import bonsai.tool as tool import bonsai.tool as tool
global _alignment_enum_items_cache
items = [("0", "— select alignment —", "")] items = [("0", "— select alignment —", "")]
ifc_file = tool.Ifc.get() ifc_file = tool.Ifc.get()
if not ifc_file: if not ifc_file:
return items _alignment_enum_items_cache = items
return _alignment_enum_items_cache
try: try:
for a in ifc_file.by_type("IfcAlignment"): for a in ifc_file.by_type("IfcAlignment"):
# Skip child alignments (used in multi-vertical template) # Skip child alignments (used in multi-vertical template)
@@ -59,7 +73,8 @@ def _alignment_enum_items(self, context):
items.append((str(a.id()), label, "")) items.append((str(a.id()), label, ""))
except Exception: except Exception:
pass pass
return items _alignment_enum_items_cache = items
return _alignment_enum_items_cache
def _on_active_alignment_update(self, context): def _on_active_alignment_update(self, context):
+17
View File
@@ -1057,6 +1057,23 @@ class Alignment:
@classmethod @classmethod
def get_active_alignment(cls) -> ifcopenshell.entity_instance | None: def get_active_alignment(cls) -> ifcopenshell.entity_instance | None:
if obj := tool.Blender.get_active_object(): if obj := tool.Blender.get_active_object():
# PI curve marker empties (see PICurveMarkerProperties) are never
# IFC-linked — they're transient viewport helpers — so they need
# their own lookup via the alignment_id they were tagged with,
# rather than falling through to tool.Ifc.get_entity() below.
# Without this, selecting a marker to press Apply Curve leaves
# this returning None, and the Alignments tab's dropdown/segment
# table (which sync from this) revert to "select an alignment".
marker = obj.bonsai_pi_curve_marker
if marker.is_pi_marker:
ifc_file = tool.Ifc.get()
if not ifc_file:
return None
try:
alignment = ifc_file.by_id(marker.alignment_id)
except RuntimeError:
return None
return cls._get_top_level_alignment(alignment) if alignment.is_a("IfcAlignment") else None
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
if not element: if not element:
return None return None