DRY transform-modal draw gate + polyline helper

Two small refactors:

- apply_transform_modal_draw_gate(group, context) replaces the
  three-line _is_transform_modal_active + _hide_all_non_modal_gizmos
  pair that BillboardingGizmoGroupMixin, BaseParametricGizmoGroup
  and BaseSchematicGizmoGroup all repeat in draw_prepare.
- decorator.py renames _stroke_lines_alpha to a public-scope
  draw_polyline_segments and drops the no-longer-private companion
  docstring reference; the function is now usable by sibling
  decorators that draw polyline overlays.

Plus a few one-liner tweaks in tool/model.py and opening.py
following the helper rename.

Generated with the assistance of an AI coding tool.
This commit is contained in:
Gorgious56
2026-06-10 12:26:48 +02:00
parent 0d703039a6
commit 3a0abbab95
4 changed files with 44 additions and 63 deletions
+17 -6
View File
@@ -204,6 +204,20 @@ def _hide_all_non_modal_gizmos(group) -> None:
gz.hide = True
def apply_transform_modal_draw_gate(group, context) -> bool:
"""Combined gate for ``draw_prepare`` overrides: hide non-modal gizmos and
return ``True`` when a Blender transform modal is dragging matrix_world.
Returns ``False`` when no transform modal is active so callers can fall
through to their normal positioning logic. ``True`` means the caller must
early-return without touching matrix_basis the hidden gizmos will be
re-shown on the next idle frame once the modal exits."""
if not _is_transform_modal_active(context):
return False
_hide_all_non_modal_gizmos(group)
return True
class GizmoColor(Enum):
"""Color identifiers for dimension gizmos.
@@ -5075,8 +5089,7 @@ class BillboardingGizmoGroupMixin:
self.position_gizmos(context)
def draw_prepare(self, context: bpy.types.Context) -> None:
if _is_transform_modal_active(context):
_hide_all_non_modal_gizmos(self)
if apply_transform_modal_draw_gate(self, context):
return
self.position_gizmos(context)
@@ -6498,8 +6511,7 @@ class BaseParametricGizmoGroup:
"""
if not self.is_setup_complete():
return
if _is_transform_modal_active(context):
_hide_all_non_modal_gizmos(self)
if apply_transform_modal_draw_gate(self, context):
return
obj = context.active_object
if not obj:
@@ -6707,8 +6719,7 @@ class BaseSchematicGizmoGroup(BaseParametricGizmoGroup):
def draw_prepare(self, context: bpy.types.Context) -> None:
if not self.is_setup_complete():
return
if _is_transform_modal_active(context):
_hide_all_non_modal_gizmos(self)
if apply_transform_modal_draw_gate(self, context):
return
obj = context.active_object
if not obj:
+20 -53
View File
@@ -2032,42 +2032,6 @@ class BoundingBoxDecorator:
co2.y -= y_overlap / 2 + min_spacing
def _stroke_lines_alpha(
context: bpy.types.Context,
segments: list[tuple[tuple[float, float, float], tuple[float, float, float]]],
color_rgb: tuple[float, float, float],
line_width: float,
line_alpha: float,
) -> None:
"""Render ``segments`` (a list of ``(start, end)`` tuples) as one
anti-aliased LINES batch in world space. Early-returns when
``context.region`` is unavailable (e.g. when called from a
``_RestrictContext``)."""
if not segments:
return
verts: list[tuple[float, float, float]] = []
indices: list[tuple[int, int]] = []
for start, end in segments:
base = len(verts)
verts.append(tuple(start))
verts.append(tuple(end))
indices.append((base, base + 1))
if not tool.Blender.validate_shader_batch_data(verts, indices):
return
region = getattr(context, "region", None)
if region is None:
return
shader = gpu.shader.from_builtin("POLYLINE_UNIFORM_COLOR")
shader.bind()
shader.uniform_float("viewportSize", (region.width, region.height))
shader.uniform_float("lineWidth", line_width)
shader.uniform_float("color", (*color_rgb, line_alpha))
batch = batch_for_shader(shader, "LINES", {"pos": verts}, indices=indices)
gpu.state.blend_set("ALPHA")
batch.draw(shader)
gpu.state.blend_set("NONE")
def _fill_quads_alpha(
context: bpy.types.Context,
quads: list[
@@ -2082,8 +2046,7 @@ def _fill_quads_alpha(
alpha: float,
) -> None:
"""Render ``quads`` (each a 4-tuple of world-space corner verts in CCW
order) as one TRIS batch with two triangles per quad. Companion to
``_stroke_lines_alpha`` for filled previews."""
order) as one TRIS batch with two triangles per quad."""
if not quads:
return
verts: list[tuple[float, float, float]] = []
@@ -2179,12 +2142,12 @@ class MEPSegmentExtendPreviewDecorator(tool.Blender.ViewportDecorator):
return
start_world, end_world = line
color = tuple(prefs.decorator_color_selected[:3])
_stroke_lines_alpha(
draw_polyline_segments(
context,
[(tuple(start_world), tuple(end_world))],
color,
self.LINE_WIDTH,
self.LINE_ALPHA,
self.LINE_WIDTH,
)
@staticmethod
@@ -2250,9 +2213,11 @@ class BendPreviewDecorator(tool.Blender.ViewportDecorator):
# Late import: decorator.py loads at addon enable but mep.py imports
# this module for the extend preview, so a module-level import would
# cycle.
from bonsai.bim.module.model.mep import compute_bend_preview_polylines
from bonsai.bim.module.model.mep import cached_compute_bend_preview_polylines
preview = compute_bend_preview_polylines(start_obj, end_obj, props.start_length, props.end_length, props.radius)
preview = cached_compute_bend_preview_polylines(
start_obj, end_obj, props.start_length, props.end_length, props.radius
)
prefs = tool.Blender.get_addon_preferences()
if not preview["valid"]:
@@ -2260,7 +2225,7 @@ class BendPreviewDecorator(tool.Blender.ViewportDecorator):
axes = preview.get("invalid_axes") or []
if axes:
segments = [(tuple(a), tuple(b)) for a, b in axes]
_stroke_lines_alpha(context, segments, warning_color, self.LINE_WIDTH_ARC, self.LINE_ALPHA)
draw_polyline_segments(context, segments, warning_color, self.LINE_ALPHA, self.LINE_WIDTH_ARC)
return
leg_color = tuple(prefs.decorations_colour[:3])
@@ -2268,18 +2233,18 @@ class BendPreviewDecorator(tool.Blender.ViewportDecorator):
leg_a_far, leg_a_end = preview["leg_a"]
leg_b_far, leg_b_end = preview["leg_b"]
_stroke_lines_alpha(
draw_polyline_segments(
context,
[(tuple(leg_a_far), tuple(leg_a_end)), (tuple(leg_b_far), tuple(leg_b_end))],
leg_color,
self.LINE_WIDTH_LEG,
self.LINE_ALPHA,
self.LINE_WIDTH_LEG,
)
arc = preview["arc"]
if len(arc) >= 2:
arc_segments = [(tuple(arc[i]), tuple(arc[i + 1])) for i in range(len(arc) - 1)]
_stroke_lines_alpha(context, arc_segments, arc_color, self.LINE_WIDTH_ARC, self.LINE_ALPHA)
draw_polyline_segments(context, arc_segments, arc_color, self.LINE_ALPHA, self.LINE_WIDTH_ARC)
class WallFilletPreviewDecorator(tool.Blender.ViewportDecorator):
@@ -2343,15 +2308,15 @@ class WallFilletPreviewDecorator(tool.Blender.ViewportDecorator):
(tuple(far_a), tuple(tangent_a)),
(tuple(far_b), tuple(tangent_b)),
]
_stroke_lines_alpha(context, legs, warning_color, self.LINE_WIDTH_LEG, self.LINE_ALPHA)
draw_polyline_segments(context, legs, warning_color, self.LINE_ALPHA, self.LINE_WIDTH_LEG)
arc = geom.get("arc") or []
if len(arc) >= 2:
arc_segments = [(tuple(arc[i]), tuple(arc[i + 1])) for i in range(len(arc) - 1)]
_stroke_lines_alpha(context, arc_segments, warning_color, self.LINE_WIDTH_ARC, self.LINE_ALPHA)
draw_polyline_segments(context, arc_segments, warning_color, self.LINE_ALPHA, self.LINE_WIDTH_ARC)
elif geom.get("invalid_axes"):
axes = geom["invalid_axes"]
segments = [(tuple(a), tuple(b)) for a, b in axes]
_stroke_lines_alpha(context, segments, warning_color, self.LINE_WIDTH_ARC, self.LINE_ALPHA)
draw_polyline_segments(context, segments, warning_color, self.LINE_ALPHA, self.LINE_WIDTH_ARC)
return
leg_color = tuple(prefs.decorations_colour[:3])
@@ -2368,12 +2333,12 @@ class WallFilletPreviewDecorator(tool.Blender.ViewportDecorator):
(tuple(far_a), tuple(geom["tangent_a"])),
(tuple(far_b), tuple(geom["tangent_b"])),
]
_stroke_lines_alpha(context, legs, leg_color, self.LINE_WIDTH_LEG, self.LINE_ALPHA)
draw_polyline_segments(context, legs, leg_color, self.LINE_ALPHA, self.LINE_WIDTH_LEG)
arc = geom["arc"]
if len(arc) >= 2:
arc_segments = [(tuple(arc[i]), tuple(arc[i + 1])) for i in range(len(arc) - 1)]
_stroke_lines_alpha(context, arc_segments, arc_color, self.LINE_WIDTH_ARC, self.LINE_ALPHA)
draw_polyline_segments(context, arc_segments, arc_color, self.LINE_ALPHA, self.LINE_WIDTH_ARC)
# Dim construction lines from arc_center to each tangent point so
# the radius reads as concrete during drag.
@@ -2383,7 +2348,9 @@ class WallFilletPreviewDecorator(tool.Blender.ViewportDecorator):
(tuple(arc_center), tuple(geom["tangent_a"])),
(tuple(arc_center), tuple(geom["tangent_b"])),
]
_stroke_lines_alpha(context, construction, arc_color, self.LINE_WIDTH_CONSTRUCTION, self.CONSTRUCTION_ALPHA)
draw_polyline_segments(
context, construction, arc_color, self.CONSTRUCTION_ALPHA, self.LINE_WIDTH_CONSTRUCTION
)
@staticmethod
def _far_endpoint(reference_line, intersection):
@@ -2505,7 +2472,7 @@ class DoorSwingReadonlyDecorator(tool.Blender.ViewportDecorator):
pts = [world_main @ p for p in _DOOR_SWING_ARC_UNIT_POINTS]
for i in range(len(pts) - 1):
segments.append((tuple(pts[i]), tuple(pts[i + 1])))
_stroke_lines_alpha(context, segments, main_color, self.LINE_WIDTH, self.LINE_ALPHA)
draw_polyline_segments(context, segments, main_color, self.LINE_ALPHA, self.LINE_WIDTH)
_BBOX_EDGES = (
@@ -219,6 +219,10 @@ _DASH_WIDTH_METERS: float = 0.10
# overlay biases the depth buffer at outline pixels.
_DASH_LINE_WIDTH: float = 1.5
_SOLID_LINE_WIDTH: float = 2.5
# Per-iteration default line width used by every non-occlusion draw call in
# this decorator's ``__call__``. Restored after each occlusion pair so the
# next draw isn't silently inheriting the wider solid-pass override.
_DEFAULT_LINE_WIDTH: float = 2.0
def _get_cached_batch_or_none(cache_key: tuple[int, str]) -> "gpu.types.GPUBatch | None":
@@ -1250,7 +1254,7 @@ class DecorationsHandler:
# Restore the per-iteration default set at the top of __call__ so
# subsequent draws (the HalfSpaceSolid arrow, future call-sites) are
# not silently affected by the front-pass width override.
self.line_shader.uniform_float("lineWidth", 2.0)
self.line_shader.uniform_float("lineWidth", _DEFAULT_LINE_WIDTH)
gpu.state.depth_test_set(original_depth_test)
def __call__(self, context):
@@ -1286,7 +1290,7 @@ class DecorationsHandler:
self.line_shader.bind() # required to be able to change uniforms of the shader
# POLYLINE_UNIFORM_COLOR specific uniforms
self.line_shader.uniform_float("viewportSize", (context.region.width, context.region.height))
self.line_shader.uniform_float("lineWidth", 2.0)
self.line_shader.uniform_float("lineWidth", _DEFAULT_LINE_WIDTH)
# general shader
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
+1 -2
View File
@@ -1588,8 +1588,7 @@ class Model(bonsai.core.tool.Model):
element = tool.Ifc.get_entity(object)
if not element:
return
psets = ifcopenshell.util.element.get_psets(element)
pset_data = psets.get(pset_name, None)
pset_data = ifcopenshell.util.element.get_pset(element, pset_name)
if not pset_data:
return
pset_data["data_dict"] = json.loads(pset_data.get("Data", "[]") or "[]")