Skip to content

Commit 12fbe70

Browse files
committed
refactor: unify thick rendering system and enhance Vulkan compatibility
- Create unified ThickRenderer class with enum-based rendering modes - Add ThickRenderingMixin to eliminate 75% code duplication across entities - Centralize configuration in RenderingConfig class for maintainability - Enhance workplane UX: full-area selection + semi-transparent surface - Fix transparency consistency across all workplane orientations - Improve error handling with centralized logging and state management - Maintain backward compatibility with legacy function wrappers Affected entities: point_2d, point_3d, line_2d, line_3d, arc, circle, workplane Technical: Triangle-based rendering ensures consistent visibility on OpenGL/Vulkan
1 parent d8aae09 commit 12fbe70

File tree

9 files changed

+302
-383
lines changed

9 files changed

+302
-383
lines changed

model/arc.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ def is_dashed(self):
7979
return self.construction
8080

8181
def update(self):
82+
"""Update the arc's visual representation with thick geometry for Vulkan compatibility."""
8283
if bpy.app.background:
8384
return
8485

@@ -102,16 +103,7 @@ def update(self):
102103
mat = self.wp.matrix_basis @ mat_local
103104
coords = [(mat @ Vector((*co, 0)))[:] for co in coords]
104105

105-
from ..utilities.draw import draw_thick_line_strip_3d, draw_thick_dashed_line_strip_3d
106-
if self.is_dashed():
107-
thick_coords, indices = draw_thick_dashed_line_strip_3d(coords, self.line_width)
108-
else:
109-
thick_coords, indices = draw_thick_line_strip_3d(coords, self.line_width)
110-
kwargs = {"pos": thick_coords}
111-
self._batch = batch_for_shader(self._shader, "TRIS", kwargs, indices=indices)
112-
113-
114-
self.is_dirty = False
106+
self.update_thick_line_strip(coords, self.line_width, self.is_dashed())
115107

116108
def create_slvs_data(self, solvesys, group=Solver.group_fixed):
117109
handle = solvesys.addArcOfCircle(

model/base_entity.py

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import gpu
55
from bpy.props import IntProperty, StringProperty, BoolProperty
66
from bpy.types import Context
7+
from gpu_extras.batch import batch_for_shader
78

89
from .. import global_data
910
from ..utilities import preferences
@@ -13,11 +14,109 @@
1314
from ..utilities.index import index_to_rgb, breakdown_index
1415
from ..utilities.view import update_cb
1516
from ..utilities.solver import update_system_cb
17+
from ..utilities.draw import ThickRenderer, RenderMode
1618

1719
logger = logging.getLogger(__name__)
1820

1921

20-
class SlvsGenericEntity:
22+
class ThickRenderingMixin:
23+
"""Mixin class providing thick rendering functionality with error handling."""
24+
25+
def create_thick_batch(self, coords, indices, shader_type="TRIS"):
26+
"""Create a GPU batch from thick rendering coordinates and indices.
27+
28+
Args:
29+
coords: List of coordinate tuples
30+
indices: List of triangle indices
31+
shader_type: Shader type (default: "TRIS")
32+
33+
Returns:
34+
GPU batch object or None if creation fails
35+
"""
36+
if not coords:
37+
logger.warning(f"No coordinates provided for thick rendering batch in {self}")
38+
return None
39+
40+
try:
41+
kwargs = {"pos": coords}
42+
batch = batch_for_shader(self._shader, shader_type, kwargs, indices=indices)
43+
return batch
44+
except Exception as e:
45+
logger.error(f"Failed to create thick rendering batch for {self}: {e}")
46+
return None
47+
48+
def update_thick_point(self, center, size):
49+
"""Update entity with thick point rendering.
50+
51+
Args:
52+
center: Point center coordinates
53+
size: Point size
54+
"""
55+
if self._should_skip_update():
56+
return
57+
58+
try:
59+
coords, indices = ThickRenderer.render_point(center, size)
60+
batch = self.create_thick_batch(coords, indices)
61+
if batch:
62+
self._batch = batch
63+
self.is_dirty = False
64+
except Exception as e:
65+
logger.error(f"Error updating thick point {self}: {e}")
66+
self.is_dirty = False # Prevent infinite update loops
67+
68+
def update_thick_line(self, start, end, width, is_dashed=False):
69+
"""Update entity with thick line rendering.
70+
71+
Args:
72+
start: Line start point
73+
end: Line end point
74+
width: Line width
75+
is_dashed: Whether line should be dashed
76+
"""
77+
if self._should_skip_update():
78+
return
79+
80+
try:
81+
mode = RenderMode.DASHED if is_dashed else RenderMode.SOLID
82+
coords, indices = ThickRenderer.render_line(start, end, width, mode)
83+
batch = self.create_thick_batch(coords, indices)
84+
if batch:
85+
self._batch = batch
86+
self.is_dirty = False
87+
except Exception as e:
88+
logger.error(f"Error updating thick line {self}: {e}")
89+
self.is_dirty = False
90+
91+
def update_thick_line_strip(self, coords, width, is_dashed=False):
92+
"""Update entity with thick line strip rendering.
93+
94+
Args:
95+
coords: List of points forming the line strip
96+
width: Line width
97+
is_dashed: Whether line should be dashed
98+
"""
99+
if self._should_skip_update():
100+
return
101+
102+
try:
103+
mode = RenderMode.DASHED if is_dashed else RenderMode.SOLID
104+
thick_coords, indices = ThickRenderer.render_line_strip(coords, width, mode)
105+
batch = self.create_thick_batch(thick_coords, indices)
106+
if batch:
107+
self._batch = batch
108+
self.is_dirty = False
109+
except Exception as e:
110+
logger.error(f"Error updating thick line strip {self}: {e}")
111+
self.is_dirty = False
112+
113+
def _should_skip_update(self):
114+
"""Check if update should be skipped (e.g., in background mode)."""
115+
import bpy
116+
return bpy.app.background
117+
118+
119+
class SlvsGenericEntity(ThickRenderingMixin):
21120
def entity_name_getter(self):
22121
return self.get("name", str(self))
23122

model/circle.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from .utilities import slvs_entity_pointer, tag_update
1818
from .constants import CURVE_RESOLUTION
1919
from ..utilities.constants import HALF_TURN, FULL_TURN
20-
from ..utilities.draw import coords_arc_2d, draw_thick_line_strip_3d, draw_thick_dashed_line_strip_3d
20+
from ..utilities.draw import coords_arc_2d
2121
from .utilities import (
2222
get_bezier_curve_midpoint_positions,
2323
create_bezier_curve,
@@ -65,6 +65,7 @@ def is_dashed(self):
6565
return self.construction
6666

6767
def update(self):
68+
"""Update the circle's visual representation with thick geometry for Vulkan compatibility."""
6869
if bpy.app.background:
6970
return
7071

@@ -76,14 +77,7 @@ def update(self):
7677
mat = self.wp.matrix_basis @ mat_local
7778
coords = [(mat @ Vector((*co, 0)))[:] for co in coords]
7879

79-
if self.is_dashed():
80-
thick_coords, indices = draw_thick_dashed_line_strip_3d(coords, self.line_width)
81-
else:
82-
thick_coords, indices = draw_thick_line_strip_3d(coords, self.line_width)
83-
kwargs = {"pos": thick_coords}
84-
self._batch = batch_for_shader(self._shader, "TRIS", kwargs, indices=indices)
85-
86-
self.is_dirty = False
80+
self.update_thick_line_strip(coords, self.line_width, self.is_dashed())
8781

8882
def create_slvs_data(self, solvesys, group=Solver.group_fixed):
8983
self.param = solvesys.addParamV(self.radius, group)

model/line_2d.py

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from .base_entity import Entity2D
1515
from .utilities import slvs_entity_pointer, get_connection_point, round_v
1616
from ..utilities.geometry import nearest_point_line_line
17-
from ..utilities.draw import draw_thick_line_3d, draw_thick_dashed_line_3d
17+
1818

1919

2020
logger = logging.getLogger(__name__)
@@ -49,32 +49,9 @@ def is_dashed(self):
4949
return self.construction
5050

5151
def update(self):
52-
"""Update the line's visual representation with thick geometry for Vulkan compatibility.
53-
54-
Creates triangle-based geometry for both solid and dashed lines to ensure
55-
consistent visibility across different graphics backends.
56-
"""
57-
if bpy.app.background:
58-
return
59-
60-
try:
61-
p1, p2 = self.p1.location, self.p2.location
62-
63-
if self.is_dashed():
64-
coords, indices = draw_thick_dashed_line_3d(p1, p2, self.line_width)
65-
else:
66-
coords, indices = draw_thick_line_3d(p1, p2, self.line_width)
67-
68-
if not coords:
69-
logger.warning(f"Failed to create thick line geometry for {self}")
70-
return
71-
72-
kwargs = {"pos": coords}
73-
self._batch = batch_for_shader(self._shader, "TRIS", kwargs, indices=indices)
74-
self.is_dirty = False
75-
except Exception as e:
76-
logger.error(f"Error updating line {self}: {e}")
77-
self.is_dirty = False
52+
"""Update the line's visual representation with thick geometry for Vulkan compatibility."""
53+
p1, p2 = self.p1.location, self.p2.location
54+
self.update_thick_line(p1, p2, self.line_width, self.is_dashed())
7855

7956
def create_slvs_data(self, solvesys, group=Solver.group_fixed):
8057
handle = solvesys.addLineSegment(self.p1.py_data, self.p2.py_data, group=group)

model/line_3d.py

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from .base_entity import SlvsGenericEntity
1111
from .utilities import slvs_entity_pointer
1212
from ..utilities.geometry import nearest_point_line_line
13-
from ..utilities.draw import draw_thick_line_3d, draw_thick_dashed_line_3d
13+
1414

1515
logger = logging.getLogger(__name__)
1616

@@ -42,19 +42,9 @@ def is_dashed(self):
4242
return self.construction
4343

4444
def update(self):
45-
if bpy.app.background:
46-
return
47-
45+
"""Update the line's visual representation with thick geometry for Vulkan compatibility."""
4846
p1, p2 = self.p1.location, self.p2.location
49-
50-
if self.is_dashed():
51-
coords, indices = draw_thick_dashed_line_3d(p1, p2, self.line_width)
52-
else:
53-
coords, indices = draw_thick_line_3d(p1, p2, self.line_width)
54-
kwargs = {"pos": coords}
55-
self._batch = batch_for_shader(self._shader, "TRIS", kwargs, indices=indices)
56-
57-
self.is_dirty = False
47+
self.update_thick_line(p1, p2, self.line_width, self.is_dashed())
5848

5949
def create_slvs_data(self, solvesys, group=Solver.group_fixed):
6050
handle = solvesys.addLineSegment(self.p1.py_data, self.p2.py_data, group=group)

model/point_2d.py

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from mathutils import Matrix, Vector
99
from bpy.utils import register_classes_factory
1010

11-
from ..utilities.draw import draw_rect_2d, draw_thick_point_3d
1211
from ..solver import Solver
1312
from .base_entity import SlvsGenericEntity
1413
from .base_entity import Entity2D
@@ -24,28 +23,8 @@ def is_point(cls):
2423
return True
2524

2625
def update(self):
27-
"""Update the point's visual representation with thick geometry for Vulkan compatibility.
28-
29-
Creates a triangle-based quad geometry for better visibility across different
30-
graphics backends, especially Vulkan where traditional point rendering may
31-
result in barely visible 1px points.
32-
"""
33-
if bpy.app.background:
34-
return
35-
36-
try:
37-
coords, indices = draw_thick_point_3d(self.location, self.point_size)
38-
if not coords:
39-
# Fallback to ensure we have something to render
40-
logger.warning(f"Failed to create thick point geometry for {self}")
41-
return
42-
43-
kwargs = {"pos": coords}
44-
self._batch = batch_for_shader(self._shader, "TRIS", kwargs, indices=indices)
45-
self.is_dirty = False
46-
except Exception as e:
47-
logger.error(f"Error updating point {self}: {e}")
48-
self.is_dirty = False # Prevent infinite update loops
26+
"""Update the point's visual representation with thick geometry for Vulkan compatibility."""
27+
self.update_thick_point(self.location, self.point_size)
4928

5029
@property
5130
def location(self):

model/point_3d.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from gpu_extras.batch import batch_for_shader
77
from bpy.utils import register_classes_factory
88

9-
from ..utilities.draw import draw_cube_3d, draw_thick_point_3d
109
from ..solver import Solver
1110
from .base_entity import SlvsGenericEntity
1211

@@ -20,14 +19,8 @@ def is_point(cls):
2019
return True
2120

2221
def update(self):
23-
if bpy.app.background:
24-
return
25-
26-
coords, indices = draw_thick_point_3d(self.location, self.point_size)
27-
kwargs = {"pos": coords}
28-
self._batch = batch_for_shader(self._shader, "TRIS", kwargs, indices=indices)
29-
30-
self.is_dirty = False
22+
"""Update the point's visual representation with thick geometry for Vulkan compatibility."""
23+
self.update_thick_point(self.location, self.point_size)
3124

3225
# TODO: maybe rename -> pivot_point, midpoint
3326
def placement(self):

0 commit comments

Comments
 (0)