Remove deprecated 2D_/3D_ prefix
[blender-addons.git] / space_view3d_math_vis / draw.py
blob7502adaf93684833456283c256c13a4932e77d54
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 import bpy
4 import blf
5 import gpu
6 from gpu_extras.batch import batch_for_shader
8 from . import utils
9 from mathutils import Vector
11 SpaceView3D = bpy.types.SpaceView3D
12 callback_handle = []
14 if not bpy.app.background:
15 single_color_shader = gpu.shader.from_builtin('UNIFORM_COLOR')
16 smooth_color_shader = gpu.shader.from_builtin('SMOOTH_COLOR')
17 else:
18 single_color_shader = None
19 smooth_color_shader = None
21 COLOR_POINT = (1.0, 0.0, 1.0, 1)
22 COLOR_LINE = (0.5, 0.5, 1, 1)
23 COLOR_LINE_ACTIVE = (1.0, 1.0, 0.5, 1)
24 COLOR_BOUNDING_BOX = (1.0, 1.0, 1.0, 1.0)
25 COLOR_BOUNDING_BOX_ACTIVE = (1.0, 0.5, 0.0, 1.0)
28 def tag_redraw_areas():
29 context = bpy.context
31 # Py can't access notifers
32 for window in context.window_manager.windows:
33 for area in window.screen.areas:
34 if area.type in ['VIEW_3D', 'PROPERTIES']:
35 area.tag_redraw()
38 def callback_enable():
39 if callback_handle:
40 return
42 handle_pixel = SpaceView3D.draw_handler_add(draw_callback_px, (), 'WINDOW', 'POST_PIXEL')
43 handle_view = SpaceView3D.draw_handler_add(draw_callback_view, (), 'WINDOW', 'POST_VIEW')
44 callback_handle[:] = handle_pixel, handle_view
46 tag_redraw_areas()
49 def callback_disable():
50 if not callback_handle:
51 return
53 handle_pixel, handle_view = callback_handle
54 SpaceView3D.draw_handler_remove(handle_pixel, 'WINDOW')
55 SpaceView3D.draw_handler_remove(handle_view, 'WINDOW')
56 callback_handle[:] = []
58 tag_redraw_areas()
61 def draw_callback_px():
62 context = bpy.context
64 if context.window_manager.MathVisProp.name_hide:
65 return
67 font_id = 0
68 ui_scale = context.preferences.system.ui_scale
69 blf.size(font_id, round(12 * ui_scale))
71 data_matrix, data_quat, data_euler, data_vector, data_vector_array = utils.console_math_data()
72 if not data_matrix and not data_quat and not data_euler and not data_vector and not data_vector_array:
73 return
75 region = context.region
76 region3d = context.space_data.region_3d
78 region_mid_width = region.width / 2.0
79 region_mid_height = region.height / 2.0
81 perspective_matrix = region3d.perspective_matrix.copy()
83 def draw_text(text, vec, dx=3.0, dy=-4.0):
84 vec_4d = perspective_matrix @ vec.to_4d()
85 if vec_4d.w > 0.0:
86 x = region_mid_width + region_mid_width * (vec_4d.x / vec_4d.w)
87 y = region_mid_height + region_mid_height * (vec_4d.y / vec_4d.w)
89 blf.position(font_id, x + dx, y + dy, 0.0)
90 blf.draw(font_id, text)
92 if data_vector:
93 for key, vec in data_vector.items():
94 draw_text(key, vec)
96 if data_vector_array:
97 for key, vec in data_vector_array.items():
98 if vec:
99 draw_text(key, vec[0])
101 if data_matrix:
102 for key, mat in data_matrix.items():
103 loc = Vector((mat[0][3], mat[1][3], mat[2][3]))
104 draw_text(key, loc, dx=10, dy=-20)
106 offset_y = 20
107 if data_quat:
108 loc = context.scene.cursor.location.copy()
109 for key, mat in data_quat.items():
110 draw_text(key, loc, dy=-offset_y)
111 offset_y += 20
113 if data_euler:
114 loc = context.scene.cursor.location.copy()
115 for key, mat in data_euler.items():
116 draw_text(key, loc, dy=-offset_y)
117 offset_y += 20
120 def draw_callback_view():
121 settings = bpy.context.window_manager.MathVisProp
122 prop_states = bpy.context.window_manager.MathVisStatePropList
124 scale = settings.bbox_scale
125 with_bounding_box = not settings.bbox_hide
127 if settings.in_front:
128 gpu.state.depth_test_set('ALWAYS')
129 else:
130 gpu.state.depth_test_set('LESS')
132 data_matrix, data_quat, data_euler, data_vector, data_vector_array = utils.console_math_data()
133 if settings.index in range(0,len(prop_states)):
134 active_index = settings.index
135 active_key = prop_states[active_index].name
136 else:
137 active_index = -1
138 active_key = None
140 if data_vector:
141 coords = [tuple(vec.to_3d()) for vec in data_vector.values()]
142 draw_points(coords)
144 if data_vector_array:
145 for key, line in data_vector_array.items():
146 coords = [tuple(vec.to_3d()) for vec in line]
147 if key == active_key:
148 draw_line(coords, COLOR_LINE_ACTIVE)
149 else:
150 draw_line(coords, COLOR_LINE)
152 if data_matrix:
153 draw_matrices(data_matrix, scale, with_bounding_box, active_key)
155 if data_euler or data_quat:
156 cursor = bpy.context.scene.cursor.location.copy()
157 derived_matrices = []
158 for key, quat in data_quat.values():
159 matrix = quat.to_matrix().to_4x4()
160 matrix.translation = cursor
161 derived_matrices[key] = matrix
162 for key, eul in data_euler.values():
163 matrix = eul.to_matrix().to_4x4()
164 matrix.translation = cursor
165 derived_matrices[key] = matrix
166 draw_matrices(derived_matrices, scale, with_bounding_box, active_key)
169 def draw_points(points):
170 batch = batch_from_points(points, "POINTS")
171 single_color_shader.bind()
172 single_color_shader.uniform_float("color", COLOR_POINT)
173 batch.draw(single_color_shader)
176 def draw_line(points, color):
177 batch = batch_from_points(points, "LINE_STRIP")
178 single_color_shader.bind()
179 single_color_shader.uniform_float("color", color)
180 batch.draw(single_color_shader)
183 def batch_from_points(points, type):
184 return batch_for_shader(single_color_shader, type, {"pos": points})
187 def draw_matrices(matrices, scale, with_bounding_box, active_key):
188 x_p = Vector((scale, 0.0, 0.0))
189 x_n = Vector((-scale, 0.0, 0.0))
190 y_p = Vector((0.0, scale, 0.0))
191 y_n = Vector((0.0, -scale, 0.0))
192 z_p = Vector((0.0, 0.0, scale))
193 z_n = Vector((0.0, 0.0, -scale))
195 red_dark = (0.2, 0.0, 0.0, 1.0)
196 red_light = (1.0, 0.2, 0.2, 1.0)
197 green_dark = (0.0, 0.2, 0.0, 1.0)
198 green_light = (0.2, 1.0, 0.2, 1.0)
199 blue_dark = (0.0, 0.0, 0.2, 1.0)
200 blue_light = (0.4, 0.4, 1.0, 1.0)
202 coords = []
203 colors = []
204 selected = []
205 active = []
206 for key, matrix in matrices.items():
207 coords.append(matrix @ x_n)
208 coords.append(matrix @ x_p)
209 colors.extend((red_dark, red_light))
210 coords.append(matrix @ y_n)
211 coords.append(matrix @ y_p)
212 colors.extend((green_dark, green_light))
213 coords.append(matrix @ z_n)
214 coords.append(matrix @ z_p)
215 colors.extend((blue_dark, blue_light))
216 if key == active_key:
217 active.append(matrix)
218 else:
219 selected.append(matrix)
221 batch = batch_for_shader(smooth_color_shader, "LINES", {
222 "pos": coords,
223 "color": colors
225 batch.draw(smooth_color_shader)
227 if with_bounding_box:
228 if selected:
229 draw_bounding_boxes(selected, scale, COLOR_BOUNDING_BOX)
230 if active:
231 draw_bounding_boxes(active, scale, COLOR_BOUNDING_BOX_ACTIVE)
234 def draw_bounding_boxes(matrices, scale, color):
235 boundbox_points = []
236 for x in (-scale, scale):
237 for y in (-scale, scale):
238 for z in (-scale, scale):
239 boundbox_points.append(Vector((x, y, z)))
241 boundbox_lines = [
242 (0, 1), (1, 3), (3, 2), (2, 0), (0, 4), (4, 5),
243 (5, 7), (7, 6), (6, 4), (1, 5), (2, 6), (3, 7)
246 points = []
247 for matrix in matrices:
248 for v1, v2 in boundbox_lines:
249 points.append(matrix @ boundbox_points[v1])
250 points.append(matrix @ boundbox_points[v2])
252 batch = batch_from_points(points, "LINES")
254 single_color_shader.bind()
255 single_color_shader.uniform_float("color", color)
256 batch.draw(single_color_shader)