Fix error in rigify property generation
[blender-addons.git] / mesh_snap_utilities_line / common_classes.py
blob611b07da4b22fc3874c80d45d4f5fd1cff5315b0
1 ### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 3
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 # ##### END GPL LICENSE BLOCK #####
17 import bpy
19 from mathutils import (
20 Vector,
21 Matrix,
23 from mathutils.geometry import intersect_point_line
24 from .drawing_utilities import SnapDrawn
25 from .common_utilities import (
26 convert_distance,
27 get_units_info,
28 location_3d_to_region_2d,
32 class SnapNavigation():
33 __slots__ = (
34 'use_ndof',
35 '_rotate',
36 '_move',
37 '_zoom',
38 '_ndof_all',
39 '_ndof_orbit',
40 '_ndof_orbit_zoom',
41 '_ndof_pan')
44 @staticmethod
45 def debug_key(key):
46 for member in dir(key):
47 print(member, getattr(key, member))
49 @staticmethod
50 def convert_to_flag(shift, ctrl, alt):
51 return (shift << 0) | (ctrl << 1) | (alt << 2)
53 def __init__(self, context, use_ndof):
54 # TO DO:
55 # 'View Orbit', 'View Pan', 'NDOF Orbit View', 'NDOF Pan View'
56 self.use_ndof = use_ndof and context.preferences.inputs.use_ndof
58 self._rotate = set()
59 self._move = set()
60 self._zoom = set()
62 if self.use_ndof:
63 self._ndof_all = set()
64 self._ndof_orbit = set()
65 self._ndof_orbit_zoom = set()
66 self._ndof_pan = set()
68 for key in context.window_manager.keyconfigs.user.keymaps['3D View'].keymap_items:
69 if key.idname == 'view3d.rotate':
70 self._rotate.add((self.convert_to_flag(key.shift, key.ctrl, key.alt), key.type, key.value))
71 elif key.idname == 'view3d.move':
72 self._move.add((self.convert_to_flag(key.shift, key.ctrl, key.alt), key.type, key.value))
73 elif key.idname == 'view3d.zoom':
74 if key.type == 'WHEELINMOUSE':
75 self._zoom.add((self.convert_to_flag(key.shift, key.ctrl, key.alt), 'WHEELUPMOUSE', key.value, key.properties.delta))
76 elif key.type == 'WHEELOUTMOUSE':
77 self._zoom.add((self.convert_to_flag(key.shift, key.ctrl, key.alt), 'WHEELDOWNMOUSE', key.value, key.properties.delta))
78 else:
79 self._zoom.add((self.convert_to_flag(key.shift, key.ctrl, key.alt), key.type, key.value, key.properties.delta))
81 elif self.use_ndof:
82 if key.idname == 'view3d.ndof_all':
83 self._ndof_all.add((self.convert_to_flag(key.shift, key.ctrl, key.alt), key.type))
84 elif key.idname == 'view3d.ndof_orbit':
85 self._ndof_orbit.add((self.convert_to_flag(key.shift, key.ctrl, key.alt), key.type))
86 elif key.idname == 'view3d.ndof_orbit_zoom':
87 self._ndof_orbit_zoom.add((self.convert_to_flag(key.shift, key.ctrl, key.alt), key.type))
88 elif key.idname == 'view3d.ndof_pan':
89 self._ndof_pan.add((self.convert_to_flag(key.shift, key.ctrl, key.alt), key.type))
92 def run(self, context, event, snap_location):
93 evkey = (self.convert_to_flag(event.shift, event.ctrl, event.alt), event.type, event.value)
95 if evkey in self._rotate:
96 if snap_location:
97 bpy.ops.view3d.rotate_custom_pivot('INVOKE_DEFAULT', pivot=snap_location)
98 else:
99 bpy.ops.view3d.rotate('INVOKE_DEFAULT', use_cursor_init=True)
100 return True
102 if evkey in self._move:
103 bpy.ops.view3d.move('INVOKE_DEFAULT')
104 return True
106 for key in self._zoom:
107 if evkey == key[0:3]:
108 if key[3]:
109 if snap_location:
110 bpy.ops.view3d.zoom_custom_target('INVOKE_DEFAULT', delta=key[3], target=snap_location)
111 else:
112 bpy.ops.view3d.zoom('INVOKE_DEFAULT', delta=key[3])
113 else:
114 bpy.ops.view3d.zoom('INVOKE_DEFAULT')
115 return True
117 if self.use_ndof:
118 ndofkey = evkey[:2]
119 if ndofkey in self._ndof_all:
120 bpy.ops.view3d.ndof_all('INVOKE_DEFAULT')
121 return True
122 if ndofkey in self._ndof_orbit:
123 bpy.ops.view3d.ndof_orbit('INVOKE_DEFAULT')
124 return True
125 if ndofkey in self._ndof_orbit_zoom:
126 bpy.ops.view3d.ndof_orbit_zoom('INVOKE_DEFAULT')
127 return True
128 if ndofkey in self._ndof_pan:
129 bpy.ops.view3d.ndof_pan('INVOKE_DEFAULT')
130 return True
132 return False
135 class CharMap:
136 __slots__ = (
137 'unit_system',
138 'uinfo',
139 'length_entered',
140 'length_entered_value',
141 'line_pos')
143 ascii = {
144 ".", ",", "-", "+", "1", "2", "3",
145 "4", "5", "6", "7", "8", "9", "0",
146 "c", "m", "d", "k", "h", "a",
147 " ", "/", "*", "'", "\""
148 # "="
150 type = {
151 'BACK_SPACE', 'DEL',
152 'LEFT_ARROW', 'RIGHT_ARROW'
155 def __init__(self, context):
156 scale = context.scene.unit_settings.scale_length
157 separate_units = context.scene.unit_settings.use_separate
158 self.unit_system = context.scene.unit_settings.system
159 self.uinfo = get_units_info(scale, self.unit_system, separate_units)
161 self.clear()
163 def modal_(self, context, event):
164 if event.value == 'PRESS':
165 type = event.type
166 ascii = event.ascii
167 if (type in self.type) or (ascii in self.ascii):
168 if ascii:
169 pos = self.line_pos
170 if ascii == ",":
171 ascii = "."
172 self.length_entered = self.length_entered[:pos] + ascii + self.length_entered[pos:]
173 self.line_pos += 1
175 if self.length_entered:
176 pos = self.line_pos
177 if type == 'BACK_SPACE':
178 self.length_entered = self.length_entered[:pos - 1] + self.length_entered[pos:]
179 self.line_pos -= 1
181 elif type == 'DEL':
182 self.length_entered = self.length_entered[:pos] + self.length_entered[pos + 1:]
184 elif type == 'LEFT_ARROW':
185 self.line_pos = (pos - 1) % (len(self.length_entered) + 1)
187 elif type == 'RIGHT_ARROW':
188 self.line_pos = (pos + 1) % (len(self.length_entered) + 1)
190 try:
191 self.length_entered_value = bpy.utils.units.to_value(self.unit_system, 'LENGTH', self.length_entered)
192 except: # ValueError:
193 self.length_entered_value = 0.0 #invalid
194 #self.report({'INFO'}, "Operation not supported yet")
195 else:
196 self.length_entered_value = 0.0
198 return True
200 return False
202 def get_converted_length_str(self, length):
203 if self.length_entered:
204 pos = self.line_pos
205 ret = self.length_entered[:pos] + '|' + self.length_entered[pos:]
206 else:
207 ret = convert_distance(length, self.uinfo)
209 return ret
211 def clear(self):
212 self.length_entered = ''
213 self.length_entered_value = 0.0
214 self.line_pos = 0
217 class Constrain:
218 def __init__(self, peferences, scene, obj):
219 self.last_type = None
220 self.last_vec = None
221 self.rotMat = None
222 self.preferences = peferences
223 trans_orient = scene.transform_orientation_slots[0]
224 self.orientation = [None, None]
225 if trans_orient.type == 'LOCAL':
226 self.orientation[0] = obj.matrix_world.to_3x3().transposed()
227 self.orientation[1] = Matrix.Identity(3)
228 else:
229 self.orientation[0] = Matrix.Identity(3)
230 self.orientation[1] = obj.matrix_world.to_3x3().transposed()
232 self.orientation_id = 0
233 self.center = Vector((0.0, 0.0, 0.0))
234 self.center_2d = Vector((0.0, 0.0))
235 self.projected_vecs = Matrix(([0.0, 0.0], [0.0, 0.0], [0.0, 0.0]))
237 def _constrain_set(self, mcursor):
238 vec = (mcursor - self.center_2d)
239 vec.normalize()
241 dot_x = abs(vec.dot(self.projected_vecs[0]))
242 dot_y = abs(vec.dot(self.projected_vecs[1]))
243 dot_z = abs(vec.dot(self.projected_vecs[2]))
245 if dot_x > dot_y and dot_x > dot_z:
246 vec = self.orientation[self.orientation_id][0]
247 type = 'X'
249 elif dot_y > dot_x and dot_y > dot_z:
250 vec = self.orientation[self.orientation_id][1]
251 type = 'Y'
253 else: # dot_z > dot_y and dot_z > dot_x:
254 vec = self.orientation[self.orientation_id][2]
255 type = 'Z'
257 return vec, type
259 def modal(self, event, shift_callback):
260 type = event.type
261 if self.last_type == type:
262 self.orientation_id += 1
264 if type == 'X':
265 if self.orientation_id < 2:
266 self.last_vec = self.orientation[self.orientation_id][0]
267 else:
268 self.orientation_id = 0
269 self.last_vec = type = None
270 elif type == 'Y':
271 if self.orientation_id < 2:
272 self.last_vec = self.orientation[self.orientation_id][1]
273 else:
274 self.orientation_id = 0
275 self.last_vec = type = None
276 elif type == 'Z':
277 if self.orientation_id < 2:
278 self.last_vec = self.orientation[self.orientation_id][2]
279 else:
280 self.orientation_id = 0
281 self.last_vec = type = None
282 elif shift_callback and type in {'RIGHT_SHIFT', 'LEFT_SHIFT'}:
283 if self.orientation_id < 1:
284 type = 'shift'
285 self.last_vec = shift_callback()
286 else:
287 self.orientation_id = 0
288 self.last_vec = type = None
289 else:
290 return False
292 self.preferences.auto_constrain = False
293 self.last_type = type
294 return True
296 def toogle(self):
297 self.rotMat = None # update
298 if self.preferences.auto_constrain:
299 self.orientation_id = (self.orientation_id + 1) % 2
300 self.preferences.auto_constrain = self.orientation_id != 0
301 else:
302 self.preferences.auto_constrain = True
304 def update(self, region, rv3d, mcursor, center):
305 if rv3d.view_matrix != self.rotMat or self.center != center:
306 self.rotMat = rv3d.view_matrix.copy()
308 self.center = center.copy()
309 self.center_2d = location_3d_to_region_2d(region, rv3d, self.center)
311 vec = self.center + self.orientation[self.orientation_id][0]
312 self.projected_vecs[0] = location_3d_to_region_2d(region, rv3d, vec) - self.center_2d
313 vec = self.center + self.orientation[self.orientation_id][1]
314 self.projected_vecs[1] = location_3d_to_region_2d(region, rv3d, vec) - self.center_2d
315 vec = self.center + self.orientation[self.orientation_id][2]
316 self.projected_vecs[2] = location_3d_to_region_2d(region, rv3d, vec) - self.center_2d
318 self.projected_vecs[0].normalize()
319 self.projected_vecs[1].normalize()
320 self.projected_vecs[2].normalize()
322 return self._constrain_set(mcursor)
325 class SnapUtilities:
327 __slots__ = (
328 "sctx",
329 "draw_cache",
330 "outer_verts",
331 "unit_system",
332 "rd",
333 "obj",
334 "bm",
335 "geom",
336 "type",
337 "location",
338 "preferences",
339 "normal",
340 "snap_vert",
341 "snap_edge",
342 "snap_face",
343 "incremental",
347 constrain_keys = {
348 'X': Vector((1,0,0)),
349 'Y': Vector((0,1,0)),
350 'Z': Vector((0,0,1)),
351 'RIGHT_SHIFT': 'shift',
352 'LEFT_SHIFT': 'shift',
355 snapwidgets = []
356 constrain = None
358 @staticmethod
359 def set_contrain(context, key):
360 widget = SnapUtilities.snapwidgets[-1] if SnapUtilities.snapwidgets else None
361 if SnapUtilities.constrain == key:
362 SnapUtilities.constrain = None
363 if hasattr(widget, "get_normal"):
364 widget.get_normal(context)
365 return
367 if hasattr(widget, "normal"):
368 if key == 'shift':
369 import bmesh
370 if isinstance(widget.geom, bmesh.types.BMEdge):
371 verts = widget.geom.verts
372 widget.normal = verts[1].co - verts[0].co
373 widget.normal.normalise()
374 else:
375 return
376 else:
377 widget.normal = SnapUtilities.constrain_keys[key]
379 SnapUtilities.constrain = key
382 def snap_context_update_and_return_moving_objects(self, context):
383 moving_objects = set()
384 moving_snp_objects = set()
385 children = set()
386 for obj in context.view_layer.objects.selected:
387 moving_objects.add(obj)
389 temp_children = set()
390 for obj in context.visible_objects:
391 temp_children.clear()
392 while obj.parent is not None:
393 temp_children.add(obj)
394 parent = obj.parent
395 if parent in moving_objects:
396 children.update(temp_children)
397 temp_children.clear()
398 obj = parent
400 del temp_children
402 moving_objects.difference_update(children)
404 self.sctx.clear_snap_objects(True)
406 for obj in context.visible_objects:
407 is_moving = obj in moving_objects or obj in children
408 snap_obj = self.sctx.add_obj(obj, obj.matrix_world)
409 if is_moving:
410 moving_snp_objects.add(snap_obj)
412 if obj.instance_type == 'COLLECTION':
413 mat = obj.matrix_world.copy()
414 for ob in obj.instance_collection.objects:
415 snap_obj = self.sctx.add_obj(ob, mat @ ob.matrix_world)
416 if is_moving:
417 moving_snp_objects.add(snap_obj)
419 del children
420 return moving_objects, moving_snp_objects
423 def snap_context_update(self, context):
424 def visible_objects_and_duplis():
425 if self.preferences.outer_verts:
426 for obj in context.visible_objects:
427 yield (obj, obj.matrix_world)
429 if obj.instance_type == 'COLLECTION':
430 mat = obj.matrix_world.copy()
431 for ob in obj.instance_collection.objects:
432 yield (ob, mat @ ob.matrix_world)
433 else:
434 for obj in context.objects_in_mode_unique_data:
435 yield (obj, obj.matrix_world)
437 self.sctx.clear_snap_objects(True)
439 for obj, matrix in visible_objects_and_duplis():
440 self.sctx.add_obj(obj, matrix)
443 def snap_context_init(self, context, snap_edge_and_vert=True):
444 from .snap_context_l import global_snap_context_get
446 #Create Snap Context
447 self.sctx = global_snap_context_get(context.evaluated_depsgraph_get(), context.region, context.space_data)
448 self.sctx.set_pixel_dist(12)
449 self.sctx.use_clip_planes(True)
451 if SnapUtilities.snapwidgets:
452 widget = SnapUtilities.snapwidgets[-1]
454 self.obj = widget.snap_obj.data[0] if widget.snap_obj else context.active_object
455 self.bm = widget.bm
456 self.geom = widget.geom
457 self.type = widget.type
458 self.location = widget.location
459 self.preferences = widget.preferences
460 self.draw_cache = widget.draw_cache
461 if hasattr(widget, "normal"):
462 self.normal = widget.normal
464 else:
465 #init these variables to avoid errors
466 self.obj = context.active_object
467 self.bm = None
468 self.geom = None
469 self.type = 'OUT'
470 self.location = Vector()
472 preferences = context.preferences.addons[__package__].preferences
473 self.preferences = preferences
474 #Init DrawCache
475 self.draw_cache = SnapDrawn(
476 preferences.out_color,
477 preferences.face_color,
478 preferences.edge_color,
479 preferences.vert_color,
480 preferences.center_color,
481 preferences.perpendicular_color,
482 preferences.constrain_shift_color,
483 tuple(context.preferences.themes[0].user_interface.axis_x) + (1.0,),
484 tuple(context.preferences.themes[0].user_interface.axis_y) + (1.0,),
485 tuple(context.preferences.themes[0].user_interface.axis_z) + (1.0,))
487 self.snap_vert = self.snap_edge = snap_edge_and_vert
489 shading = context.space_data.shading
490 self.snap_face = not (snap_edge_and_vert and (shading.show_xray or shading.type == 'WIREFRAME'))
492 self.sctx.set_snap_mode(self.snap_vert, self.snap_edge, self.snap_face)
494 #Configure the unit of measure
495 unit_system = context.scene.unit_settings.system
496 scale = context.scene.unit_settings.scale_length
497 scale /= context.space_data.overlay.grid_scale
498 self.rd = bpy.utils.units.to_value(unit_system, 'LENGTH', str(1 / scale))
500 self.incremental = bpy.utils.units.to_value(unit_system, 'LENGTH', str(self.preferences.incremental))
502 def snap_to_grid(self):
503 if self.type == 'OUT' and self.preferences.increments_grid:
504 loc = self.location / self.rd
505 self.location = Vector((round(loc.x),
506 round(loc.y),
507 round(loc.z))) * self.rd
509 def snap_context_free(self):
510 self.sctx = None
511 del self.sctx
513 del self.bm
514 del self.draw_cache
515 del self.geom
516 del self.location
517 del self.rd
518 del self.snap_face
519 del self.snap_obj
520 del self.type
522 del self.preferences
524 SnapUtilities.constrain = None