Fix T71100: Node Wrangler creates nodes on linked node trees
[blender-addons.git] / precision_drawing_tools / pdt_pivot_point.py
blob93a0c59bb7586b785cd31c60cfc6a5c65b3ac999
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 # -----------------------------------------------------------------------
4 # Author: Alan Odom (Clockmender), Rune Morling (ermo) Copyright (c) 2019
5 # -----------------------------------------------------------------------
7 import bpy
8 import bmesh
9 from bpy.types import Operator, SpaceView3D
10 from mathutils import Vector, Matrix
11 from math import pi
12 from .pdt_functions import view_coords, draw_callback_3d
13 from .pdt_msg_strings import (
14 PDT_CON_AREYOURSURE,
15 PDT_ERR_EDIT_MODE,
16 PDT_ERR_NO3DVIEW,
17 PDT_ERR_NOPPLOC,
18 PDT_ERR_NO_ACT_OBJ,
19 PDT_ERR_NO_SEL_GEOM
23 class PDT_OT_ModalDrawOperator(bpy.types.Operator):
24 """Show/Hide Pivot Point"""
26 bl_idname = "pdt.modaldraw"
27 bl_label = "PDT Modal Draw"
28 bl_options = {"REGISTER", "UNDO"}
30 _handle = None # keep function handler
32 @staticmethod
33 def handle_add(self, context):
34 """Draw Pivot Point Graphic if not displayed.
36 Note:
37 Draws 7 element Pivot Point Graphic
39 Args:
40 context: Blender bpy.context instance.
42 Returns:
43 Nothing.
44 """
46 if PDT_OT_ModalDrawOperator._handle is None:
47 PDT_OT_ModalDrawOperator._handle = SpaceView3D.draw_handler_add(
48 draw_callback_3d, (self, context), "WINDOW", "POST_VIEW"
50 context.window_manager.pdt_run_opengl = True
52 @staticmethod
53 def handle_remove(self, context):
54 """Remove Pivot Point Graphic if displayed.
56 Note:
57 Removes 7 element Pivot Point Graphic
59 Args:
60 context: Blender bpy.context instance.
62 Returns:
63 Nothing.
64 """
66 if PDT_OT_ModalDrawOperator._handle is not None:
67 SpaceView3D.draw_handler_remove(PDT_OT_ModalDrawOperator._handle, "WINDOW")
68 PDT_OT_ModalDrawOperator._handle = None
69 context.window_manager.pdt_run_opengl = False
71 def execute(self, context):
72 """Pivot Point Show/Hide Button Function.
74 Note:
75 Operational execute function for Show/Hide Pivot Point function
77 Args:
78 context: Blender bpy.context instance.
80 Returns:
81 Status Set.
82 """
84 if context.area.type == "VIEW_3D":
85 if context.window_manager.pdt_run_opengl is False:
86 self.handle_add(self, context)
87 context.area.tag_redraw()
88 else:
89 self.handle_remove(self, context)
90 context.area.tag_redraw()
92 return {"FINISHED"}
94 self.report({"ERROR"}, PDT_ERR_NO3DVIEW)
95 return {"CANCELLED"}
98 class PDT_OT_ViewPlaneRotate(Operator):
99 """Rotate Selected Vertices about Pivot Point in View Plane"""
101 bl_idname = "pdt.viewplanerot"
102 bl_label = "PDT View Rotate"
103 bl_options = {"REGISTER", "UNDO"}
105 @classmethod
106 def poll(cls, context):
107 """Check Object Status.
109 Args:
110 context: Blender bpy.context instance.
112 Returns:
113 Nothing.
116 obj = context.object
117 if obj is None:
118 return False
119 return all([bool(obj), obj.type == "MESH", obj.mode == "EDIT"])
122 def execute(self, context):
123 """Rotate Selected Vertices about Pivot Point.
125 Note:
126 Rotates any selected vertices about the Pivot Point
127 in View Oriented coordinates, works in any view orientation.
129 Args:
130 context: Blender bpy.context instance.
132 Note:
133 Uses pg.pivot_loc, pg.pivot_ang scene variables
135 Returns:
136 Status Set.
139 scene = context.scene
140 pg = scene.pdt_pg
141 obj = bpy.context.view_layer.objects.active
142 if obj is None:
143 self.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ)
144 return {"FINISHED"}
145 if obj.mode != "EDIT":
146 error_message = f"{PDT_ERR_EDIT_MODE} {obj.mode})"
147 self.report({"ERROR"}, error_message)
148 return {"FINISHED"}
149 bm = bmesh.from_edit_mesh(obj.data)
150 v1 = Vector((0, 0, 0))
151 v2 = view_coords(0, 0, 1)
152 axis = (v2 - v1).normalized()
153 rot = Matrix.Rotation((pg.pivot_ang * pi / 180), 4, axis)
154 verts = verts = [v for v in bm.verts if v.select]
155 bmesh.ops.rotate(
156 bm, cent=pg.pivot_loc - obj.matrix_world.decompose()[0], matrix=rot, verts=verts
158 bmesh.update_edit_mesh(obj.data)
159 return {"FINISHED"}
162 class PDT_OT_ViewPlaneScale(Operator):
163 """Scale Selected Vertices about Pivot Point"""
165 bl_idname = "pdt.viewscale"
166 bl_label = "PDT View Scale"
167 bl_options = {"REGISTER", "UNDO"}
169 @classmethod
170 def poll(cls, context):
171 """Check Object Status.
173 Args:
174 context: Blender bpy.context instance.
176 Returns:
177 Nothing.
180 obj = context.object
181 if obj is None:
182 return False
183 return all([bool(obj), obj.type == "MESH", obj.mode == "EDIT"])
186 def execute(self, context):
187 """Scales Selected Vertices about Pivot Point.
189 Note:
190 Scales any selected vertices about the Pivot Point
191 in View Oriented coordinates, works in any view orientation
193 Args:
194 context: Blender bpy.context instance.
196 Note:
197 Uses pg.pivot_loc, pg.pivot_scale scene variables
199 Returns:
200 Status Set.
203 scene = context.scene
204 pg = scene.pdt_pg
205 obj = bpy.context.view_layer.objects.active
206 if obj is None:
207 self.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ)
208 return {"FINISHED"}
209 if obj.mode != "EDIT":
210 error_message = f"{PDT_ERR_EDIT_MODE} {obj.mode})"
211 self.report({"ERROR"}, error_message)
212 return {"FINISHED"}
213 bm = bmesh.from_edit_mesh(obj.data)
214 verts = verts = [v for v in bm.verts if v.select]
215 for v in verts:
216 delta_x = (pg.pivot_loc.x - obj.matrix_world.decompose()[0].x - v.co.x) * (
217 1 - pg.pivot_scale.x
219 delta_y = (pg.pivot_loc.y - obj.matrix_world.decompose()[0].y - v.co.y) * (
220 1 - pg.pivot_scale.y
222 delta_z = (pg.pivot_loc.z - obj.matrix_world.decompose()[0].z - v.co.z) * (
223 1 - pg.pivot_scale.z
225 delta_v = Vector((delta_x, delta_y, delta_z))
226 v.co = v.co + delta_v
227 bmesh.update_edit_mesh(obj.data)
228 return {"FINISHED"}
231 class PDT_OT_PivotToCursor(Operator):
232 """Set The Pivot Point to Cursor Location"""
234 bl_idname = "pdt.pivotcursor"
235 bl_label = "PDT Pivot To Cursor"
236 bl_options = {"REGISTER", "UNDO"}
238 def execute(self, context):
239 """Moves Pivot Point to Cursor Location.
241 Note:
242 Moves Pivot Point to Cursor Location in active scene
244 Args:
245 context: Blender bpy.context instance.
247 Returns:
248 Status Set.
251 scene = context.scene
252 pg = scene.pdt_pg
253 old_cursor_loc = scene.cursor.location.copy()
254 pg.pivot_loc = scene.cursor.location
255 scene.cursor.location = old_cursor_loc
256 return {"FINISHED"}
259 class PDT_OT_CursorToPivot(Operator):
260 """Set The Cursor Location to Pivot Point"""
262 bl_idname = "pdt.cursorpivot"
263 bl_label = "PDT Cursor To Pivot"
264 bl_options = {"REGISTER", "UNDO"}
266 def execute(self, context):
267 """Moves Cursor to Pivot Point Location.
269 Note:
270 Moves Cursor to Pivot Point Location in active scene
272 Args:
273 context: Blender bpy.context instance.
275 Returns:
276 Status Set.
279 scene = context.scene
280 pg = scene.pdt_pg
281 scene.cursor.location = pg.pivot_loc
282 return {"FINISHED"}
285 class PDT_OT_PivotSelected(Operator):
286 """Set Pivot Point to Selected Geometry"""
288 bl_idname = "pdt.pivotselected"
289 bl_label = "PDT Pivot to Selected"
290 bl_options = {"REGISTER", "UNDO"}
292 @classmethod
293 def poll(cls, context):
294 """Check Object Status.
296 Args:
297 context: Blender bpy.context instance.
299 Returns:
300 Nothing.
303 obj = context.object
304 if obj is None:
305 return False
306 return all([bool(obj), obj.type == "MESH", obj.mode == "EDIT"])
309 def execute(self, context):
310 """Moves Pivot Point centroid of Selected Geometry.
312 Note:
313 Moves Pivot Point centroid of Selected Geometry in active scene
314 using Snap_Cursor_To_Selected, then puts cursor back to original location.
316 Args:
317 context: Blender bpy.context instance.
319 Returns:
320 Status Set.
323 scene = context.scene
324 pg = scene.pdt_pg
325 obj = bpy.context.view_layer.objects.active
326 if obj is None:
327 self.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ)
328 return {"FINISHED"}
329 if obj.mode != "EDIT":
330 error_message = f"{PDT_ERR_EDIT_MODE} {obj.mode})"
331 self.report({"ERROR"}, error_message)
332 return {"FINISHED"}
333 bm = bmesh.from_edit_mesh(obj.data)
334 verts = verts = [v for v in bm.verts if v.select]
335 if len(verts) > 0:
336 old_cursor_loc = scene.cursor.location.copy()
337 bpy.ops.view3d.snap_cursor_to_selected()
338 pg.pivot_loc = scene.cursor.location
339 scene.cursor.location = old_cursor_loc
340 return {"FINISHED"}
342 self.report({"ERROR"}, PDT_ERR_NO_SEL_GEOM)
343 return {"FINISHED"}
346 class PDT_OT_PivotOrigin(Operator):
347 """Set Pivot Point at Object Origin"""
349 bl_idname = "pdt.pivotorigin"
350 bl_label = "PDT Pivot to Object Origin"
351 bl_options = {"REGISTER", "UNDO"}
353 @classmethod
354 def poll(cls, context):
355 """Check Object Status.
357 Args:
358 context: Blender bpy.context instance.
360 Returns:
361 Nothing.
364 obj = context.object
365 if obj is None:
366 return False
367 return all([bool(obj), obj.type == "MESH"])
369 def execute(self, context):
370 """Moves Pivot Point to Object Origin.
372 Note:
373 Moves Pivot Point to Object Origin in active scene
375 Args:
376 context: Blender bpy.context instance.
378 Returns:
379 Status Set.
382 scene = context.scene
383 pg = scene.pdt_pg
384 obj = bpy.context.view_layer.objects.active
385 if obj is None:
386 self.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ)
387 return {"FINISHED"}
388 old_cursor_loc = scene.cursor.location.copy()
389 obj_loc = obj.matrix_world.decompose()[0]
390 pg.pivot_loc = obj_loc
391 scene.cursor.location = old_cursor_loc
392 return {"FINISHED"}
395 class PDT_OT_PivotWrite(Operator):
396 """Write Pivot Point Location to Object"""
398 bl_idname = "pdt.pivotwrite"
399 bl_label = "PDT Write PP to Object?"
400 bl_options = {"REGISTER", "UNDO"}
402 @classmethod
403 def poll(cls, context):
404 """Check Object Status.
406 Args:
407 context: Blender bpy.context instance.
409 Returns:
410 Nothing.
413 obj = context.object
414 if obj is None:
415 return False
416 return all([bool(obj), obj.type == "MESH"])
418 def execute(self, context):
419 """Writes Pivot Point Location to Object's Custom Properties.
421 Note:
422 Writes Pivot Point Location to Object's Custom Properties
423 as Vector to 'PDT_PP_LOC' - Requires Confirmation through dialogue
425 Args:
426 context: Blender bpy.context instance.
428 Note:
429 Uses pg.pivot_loc scene variable
431 Returns:
432 Status Set.
435 scene = context.scene
436 pg = scene.pdt_pg
437 obj = bpy.context.view_layer.objects.active
438 if obj is None:
439 self.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ)
440 return {"FINISHED"}
441 obj["PDT_PP_LOC"] = pg.pivot_loc
442 return {"FINISHED"}
444 def invoke(self, context, event):
445 return context.window_manager.invoke_props_dialog(self)
447 def draw(self, context):
448 row = self.layout
449 row.label(text=PDT_CON_AREYOURSURE)
452 class PDT_OT_PivotRead(Operator):
453 """Read Pivot Point Location from Object"""
455 bl_idname = "pdt.pivotread"
456 bl_label = "PDT Read PP"
457 bl_options = {"REGISTER", "UNDO"}
459 @classmethod
460 def poll(cls, context):
461 """Check Object Status.
463 Args:
464 context: Blender bpy.context instance.
466 Returns:
467 Nothing.
470 obj = context.object
471 if obj is None:
472 return False
473 return all([bool(obj), obj.type == "MESH"])
475 def execute(self, context):
476 """Reads Pivot Point Location from Object's Custom Properties.
478 Note:
479 Sets Pivot Point Location from Object's Custom Properties
480 using 'PDT_PP_LOC'
482 Args:
483 context: Blender bpy.context instance.
485 Note:
486 Uses pg.pivot_loc scene variable
488 Returns:
489 Status Set.
492 scene = context.scene
493 pg = scene.pdt_pg
494 obj = bpy.context.view_layer.objects.active
495 if obj is None:
496 self.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ)
497 return {"FINISHED"}
498 if "PDT_PP_LOC" in obj:
499 pg.pivot_loc = obj["PDT_PP_LOC"]
500 return {"FINISHED"}
502 self.report({"ERROR"}, PDT_ERR_NOPPLOC)
503 return {"FINISHED"}