mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-12 22:43:41 +00:00
Bonsai: add viewport decorator base helpers
Add two helpers to tool.Blender that 17+ existing viewport decorators re-implement byte-identically: - ViewportDecorator.draw_batch(shader_type, content_pos, color, indices=None) collapses the validate + batch_for_shader + uniform_float + draw cycle every shader-driven decorator needs. - Blender.transparent_color(color, alpha=0.1) is the RGBA-alpha-override helper duplicated across nest, project, aggregate, model, system module scopes plus six nested-def copies inside draw methods. Pure additions on the tool/ layer with direct unit tests covering the default-alpha, explicit-alpha, non-mutation, new-list-instance, and validation-guard branches. Generated with the assistance of an AI coding tool.
This commit is contained in:
@@ -538,6 +538,19 @@ class Blender(bonsai.core.tool.Blender):
|
|||||||
cls.handlers.clear()
|
cls.handlers.clear()
|
||||||
cls.is_installed = False
|
cls.is_installed = False
|
||||||
|
|
||||||
|
def draw_batch(self, shader_type, content_pos, color, indices=None):
|
||||||
|
"""Submit a GPU batch through ``self.line_shader`` (for ``"LINES"``)
|
||||||
|
or ``self.shader`` (for any other primitive). Skips empty batches
|
||||||
|
via ``validate_shader_batch_data`` so Blender 4.4+ doesn't crash on
|
||||||
|
empty ``indices``. Subclasses bind both shaders in their draw method
|
||||||
|
before calling this helper."""
|
||||||
|
if not Blender.validate_shader_batch_data(content_pos, indices):
|
||||||
|
return
|
||||||
|
shader = self.line_shader if shader_type == "LINES" else self.shader
|
||||||
|
batch = batch_for_shader(shader, shader_type, {"pos": content_pos}, indices=indices)
|
||||||
|
shader.uniform_float("color", color)
|
||||||
|
batch.draw(shader)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _lookup_active_instance(gizmo_cls: type, context: bpy.types.Context) -> Optional[Any]:
|
def _lookup_active_instance(gizmo_cls: type, context: bpy.types.Context) -> Optional[Any]:
|
||||||
"""Return the live ``GizmoGroup`` instance registered under
|
"""Return the live ``GizmoGroup`` instance registered under
|
||||||
@@ -2391,6 +2404,13 @@ class Blender(bonsai.core.tool.Blender):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def transparent_color(color: Iterable[float], alpha: float = 0.1) -> list[float]:
|
||||||
|
"""Copy an RGBA color with its alpha channel overridden."""
|
||||||
|
out = [c for c in color]
|
||||||
|
out[3] = alpha
|
||||||
|
return out
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def draw_bmesh_face_tris(
|
def draw_bmesh_face_tris(
|
||||||
cls,
|
cls,
|
||||||
|
|||||||
@@ -42,6 +42,46 @@ class TestImplementsTool(NewFile):
|
|||||||
assert isinstance(subject(), bonsai.core.tool.Blender)
|
assert isinstance(subject(), bonsai.core.tool.Blender)
|
||||||
|
|
||||||
|
|
||||||
|
class TestTransparentColor(NewFile):
|
||||||
|
def test_default_alpha_overrides_to_zero_one(self):
|
||||||
|
assert subject.transparent_color([1.0, 0.5, 0.25, 1.0]) == [1.0, 0.5, 0.25, 0.1]
|
||||||
|
|
||||||
|
def test_explicit_alpha_is_applied(self):
|
||||||
|
assert subject.transparent_color([1.0, 0.5, 0.25, 1.0], alpha=0.5) == [1.0, 0.5, 0.25, 0.5]
|
||||||
|
|
||||||
|
def test_does_not_mutate_input(self):
|
||||||
|
original = [1.0, 0.5, 0.25, 1.0]
|
||||||
|
subject.transparent_color(original)
|
||||||
|
assert original == [1.0, 0.5, 0.25, 1.0]
|
||||||
|
|
||||||
|
def test_returns_new_list_instance(self):
|
||||||
|
original = [1.0, 0.5, 0.25, 1.0]
|
||||||
|
result = subject.transparent_color(original)
|
||||||
|
assert result is not original
|
||||||
|
|
||||||
|
|
||||||
|
class TestViewportDecoratorDrawBatch(NewFile):
|
||||||
|
def test_empty_content_pos_skips_shader_calls(self):
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
decorator = subject.ViewportDecorator()
|
||||||
|
decorator.line_shader = MagicMock()
|
||||||
|
decorator.shader = MagicMock()
|
||||||
|
decorator.draw_batch("LINES", [], (1.0, 1.0, 1.0, 1.0))
|
||||||
|
decorator.line_shader.uniform_float.assert_not_called()
|
||||||
|
decorator.shader.uniform_float.assert_not_called()
|
||||||
|
|
||||||
|
def test_empty_indices_skips_shader_calls(self):
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
decorator = subject.ViewportDecorator()
|
||||||
|
decorator.line_shader = MagicMock()
|
||||||
|
decorator.shader = MagicMock()
|
||||||
|
decorator.draw_batch("LINES", [(0.0, 0.0, 0.0), (1.0, 0.0, 0.0)], (1.0, 1.0, 1.0, 1.0), indices=[])
|
||||||
|
decorator.line_shader.uniform_float.assert_not_called()
|
||||||
|
decorator.shader.uniform_float.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
class TestCopyNodeGraph(NewFile):
|
class TestCopyNodeGraph(NewFile):
|
||||||
def test_run(self):
|
def test_run(self):
|
||||||
material_to = bpy.data.materials.new("material_to")
|
material_to = bpy.data.materials.new("material_to")
|
||||||
|
|||||||
Reference in New Issue
Block a user