Cleanup: strip trailing space, remove BOM
[blender-addons.git] / precision_drawing_tools / pdt_pivot_point.py
blob357b573580bf969a8037a3f7aac4313a97810408
1 # ***** BEGIN GPL LICENSE BLOCK *****
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # ***** END GPL LICENCE BLOCK *****
20 # -----------------------------------------------------------------------
21 # Author: Alan Odom (Clockmender), Rune Morling (ermo) Copyright (c) 2019
22 # -----------------------------------------------------------------------
24 import bpy
25 import bmesh
26 from bpy.types import Operator, SpaceView3D
27 from mathutils import Vector, Matrix
28 from math import pi
29 from .pdt_functions import view_coords, draw_callback_3d
30 from .pdt_msg_strings import (
31 PDT_CON_AREYOURSURE,
32 PDT_ERR_EDIT_MODE,
33 PDT_ERR_NO3DVIEW,
34 PDT_ERR_NOPPLOC,
35 PDT_ERR_NO_ACT_OBJ,
36 PDT_ERR_NO_SEL_GEOM
40 class PDT_OT_ModalDrawOperator(bpy.types.Operator):
41 """Show/Hide Pivot Point"""
43 bl_idname = "pdt.modaldraw"
44 bl_label = "PDT Modal Draw"
45 bl_options = {"REGISTER", "UNDO"}
47 _handle = None # keep function handler
49 @staticmethod
50 def handle_add(self, context):
51 """Draw Pivot Point Graphic if not displayed.
53 Note:
54 Draws 7 element Pivot Point Graphic
56 Args:
57 context: Blender bpy.context instance.
59 Returns:
60 Nothing.
61 """
63 if PDT_OT_ModalDrawOperator._handle is None:
64 PDT_OT_ModalDrawOperator._handle = SpaceView3D.draw_handler_add(
65 draw_callback_3d, (self, context), "WINDOW", "POST_VIEW"
67 context.window_manager.pdt_run_opengl = True
69 @staticmethod
70 def handle_remove(self, context):
71 """Remove Pivot Point Graphic if displayed.
73 Note:
74 Removes 7 element Pivot Point Graphic
76 Args:
77 context: Blender bpy.context instance.
79 Returns:
80 Nothing.
81 """
83 if PDT_OT_ModalDrawOperator._handle is not None:
84 SpaceView3D.draw_handler_remove(PDT_OT_ModalDrawOperator._handle, "WINDOW")
85 PDT_OT_ModalDrawOperator._handle = None
86 context.window_manager.pdt_run_opengl = False
88 def execute(self, context):
89 """Pivot Point Show/Hide Button Function.
91 Note:
92 Operational execute function for Show/Hide Pivot Point function
94 Args:
95 context: Blender bpy.context instance.
97 Returns:
98 Status Set.
99 """
101 if context.area.type == "VIEW_3D":
102 if context.window_manager.pdt_run_opengl is False:
103 self.handle_add(self, context)
104 context.area.tag_redraw()
105 else:
106 self.handle_remove(self, context)
107 context.area.tag_redraw()
109 return {"FINISHED"}
111 self.report({"ERROR"}, PDT_ERR_NO3DVIEW)
112 return {"CANCELLED"}
115 class PDT_OT_ViewPlaneRotate(Operator):
116 """Rotate Selected Vertices about Pivot Point in View Plane"""
118 bl_idname = "pdt.viewplanerot"
119 bl_label = "PDT View Rotate"
120 bl_options = {"REGISTER", "UNDO"}
122 @classmethod
123 def poll(cls, context):
124 """Check Object Status.
126 Args:
127 context: Blender bpy.context instance.
129 Returns:
130 Nothing.
133 obj = context.object
134 if obj is None:
135 return False
136 return all([bool(obj), obj.type == "MESH", obj.mode == "EDIT"])
139 def execute(self, context):
140 """Rotate Selected Vertices about Pivot Point.
142 Note:
143 Rotates any selected vertices about the Pivot Point
144 in View Oriented coordinates, works in any view orientation.
146 Args:
147 context: Blender bpy.context instance.
149 Note:
150 Uses pg.pivot_loc, pg.pivot_ang scene variables
152 Returns:
153 Status Set.
156 scene = context.scene
157 pg = scene.pdt_pg
158 obj = bpy.context.view_layer.objects.active
159 if obj is None:
160 self.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ)
161 return {"FINISHED"}
162 if obj.mode != "EDIT":
163 error_message = f"{PDT_ERR_EDIT_MODE} {obj.mode})"
164 self.report({"ERROR"}, error_message)
165 return {"FINISHED"}
166 bm = bmesh.from_edit_mesh(obj.data)
167 v1 = Vector((0, 0, 0))
168 v2 = view_coords(0, 0, 1)
169 axis = (v2 - v1).normalized()
170 rot = Matrix.Rotation((pg.pivot_ang * pi / 180), 4, axis)
171 verts = verts = [v for v in bm.verts if v.select]
172 bmesh.ops.rotate(
173 bm, cent=pg.pivot_loc - obj.matrix_world.decompose()[0], matrix=rot, verts=verts
175 bmesh.update_edit_mesh(obj.data)
176 return {"FINISHED"}
179 class PDT_OT_ViewPlaneScale(Operator):
180 """Scale Selected Vertices about Pivot Point"""
182 bl_idname = "pdt.viewscale"
183 bl_label = "PDT View Scale"
184 bl_options = {"REGISTER", "UNDO"}
186 @classmethod
187 def poll(cls, context):
188 """Check Object Status.
190 Args:
191 context: Blender bpy.context instance.
193 Returns:
194 Nothing.
197 obj = context.object
198 if obj is None:
199 return False
200 return all([bool(obj), obj.type == "MESH", obj.mode == "EDIT"])
203 def execute(self, context):
204 """Scales Selected Vertices about Pivot Point.
206 Note:
207 Scales any selected vertices about the Pivot Point
208 in View Oriented coordinates, works in any view orientation
210 Args:
211 context: Blender bpy.context instance.
213 Note:
214 Uses pg.pivot_loc, pg.pivot_scale scene variables
216 Returns:
217 Status Set.
220 scene = context.scene
221 pg = scene.pdt_pg
222 obj = bpy.context.view_layer.objects.active
223 if obj is None:
224 self.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ)
225 return {"FINISHED"}
226 if obj.mode != "EDIT":
227 error_message = f"{PDT_ERR_EDIT_MODE} {obj.mode})"
228 self.report({"ERROR"}, error_message)
229 return {"FINISHED"}
230 bm = bmesh.from_edit_mesh(obj.data)
231 verts = verts = [v for v in bm.verts if v.select]
232 for v in verts:
233 delta_x = (pg.pivot_loc.x - obj.matrix_world.decompose()[0].x - v.co.x) * (
234 1 - pg.pivot_scale.x
236 delta_y = (pg.pivot_loc.y - obj.matrix_world.decompose()[0].y - v.co.y) * (
237 1 - pg.pivot_scale.y
239 delta_z = (pg.pivot_loc.z - obj.matrix_world.decompose()[0].z - v.co.z) * (
240 1 - pg.pivot_scale.z
242 delta_v = Vector((delta_x, delta_y, delta_z))
243 v.co = v.co + delta_v
244 bmesh.update_edit_mesh(obj.data)
245 return {"FINISHED"}
248 class PDT_OT_PivotToCursor(Operator):
249 """Set The Pivot Point to Cursor Location"""
251 bl_idname = "pdt.pivotcursor"
252 bl_label = "PDT Pivot To Cursor"
253 bl_options = {"REGISTER", "UNDO"}
255 def execute(self, context):
256 """Moves Pivot Point to Cursor Location.
258 Note:
259 Moves Pivot Point to Cursor Location in active scene
261 Args:
262 context: Blender bpy.context instance.
264 Returns:
265 Status Set.
268 scene = context.scene
269 pg = scene.pdt_pg
270 old_cursor_loc = scene.cursor.location.copy()
271 pg.pivot_loc = scene.cursor.location
272 scene.cursor.location = old_cursor_loc
273 return {"FINISHED"}
276 class PDT_OT_CursorToPivot(Operator):
277 """Set The Cursor Location to Pivot Point"""
279 bl_idname = "pdt.cursorpivot"
280 bl_label = "PDT Cursor To Pivot"
281 bl_options = {"REGISTER", "UNDO"}
283 def execute(self, context):
284 """Moves Cursor to Pivot Point Location.
286 Note:
287 Moves Cursor to Pivot Point Location in active scene
289 Args:
290 context: Blender bpy.context instance.
292 Returns:
293 Status Set.
296 scene = context.scene
297 pg = scene.pdt_pg
298 scene.cursor.location = pg.pivot_loc
299 return {"FINISHED"}
302 class PDT_OT_PivotSelected(Operator):
303 """Set Pivot Point to Selected Geometry"""
305 bl_idname = "pdt.pivotselected"
306 bl_label = "PDT Pivot to Selected"
307 bl_options = {"REGISTER", "UNDO"}
309 @classmethod
310 def poll(cls, context):
311 """Check Object Status.
313 Args:
314 context: Blender bpy.context instance.
316 Returns:
317 Nothing.
320 obj = context.object
321 if obj is None:
322 return False
323 return all([bool(obj), obj.type == "MESH", obj.mode == "EDIT"])
326 def execute(self, context):
327 """Moves Pivot Point centroid of Selected Geometry.
329 Note:
330 Moves Pivot Point centroid of Selected Geometry in active scene
331 using Snap_Cursor_To_Selected, then puts cursor back to original location.
333 Args:
334 context: Blender bpy.context instance.
336 Returns:
337 Status Set.
340 scene = context.scene
341 pg = scene.pdt_pg
342 obj = bpy.context.view_layer.objects.active
343 if obj is None:
344 self.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ)
345 return {"FINISHED"}
346 if obj.mode != "EDIT":
347 error_message = f"{PDT_ERR_EDIT_MODE} {obj.mode})"
348 self.report({"ERROR"}, error_message)
349 return {"FINISHED"}
350 bm = bmesh.from_edit_mesh(obj.data)
351 verts = verts = [v for v in bm.verts if v.select]
352 if len(verts) > 0:
353 old_cursor_loc = scene.cursor.location.copy()
354 bpy.ops.view3d.snap_cursor_to_selected()
355 pg.pivot_loc = scene.cursor.location
356 scene.cursor.location = old_cursor_loc
357 return {"FINISHED"}
359 self.report({"ERROR"}, PDT_ERR_NO_SEL_GEOM)
360 return {"FINISHED"}
363 class PDT_OT_PivotOrigin(Operator):
364 """Set Pivot Point at Object Origin"""
366 bl_idname = "pdt.pivotorigin"
367 bl_label = "PDT Pivot to Object Origin"
368 bl_options = {"REGISTER", "UNDO"}
370 @classmethod
371 def poll(cls, context):
372 """Check Object Status.
374 Args:
375 context: Blender bpy.context instance.
377 Returns:
378 Nothing.
381 obj = context.object
382 if obj is None:
383 return False
384 return all([bool(obj), obj.type == "MESH"])
386 def execute(self, context):
387 """Moves Pivot Point to Object Origin.
389 Note:
390 Moves Pivot Point to Object Origin in active scene
392 Args:
393 context: Blender bpy.context instance.
395 Returns:
396 Status Set.
399 scene = context.scene
400 pg = scene.pdt_pg
401 obj = bpy.context.view_layer.objects.active
402 if obj is None:
403 self.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ)
404 return {"FINISHED"}
405 old_cursor_loc = scene.cursor.location.copy()
406 obj_loc = obj.matrix_world.decompose()[0]
407 pg.pivot_loc = obj_loc
408 scene.cursor.location = old_cursor_loc
409 return {"FINISHED"}
412 class PDT_OT_PivotWrite(Operator):
413 """Write Pivot Point Location to Object"""
415 bl_idname = "pdt.pivotwrite"
416 bl_label = "PDT Write PP to Object?"
417 bl_options = {"REGISTER", "UNDO"}
419 @classmethod
420 def poll(cls, context):
421 """Check Object Status.
423 Args:
424 context: Blender bpy.context instance.
426 Returns:
427 Nothing.
430 obj = context.object
431 if obj is None:
432 return False
433 return all([bool(obj), obj.type == "MESH"])
435 def execute(self, context):
436 """Writes Pivot Point Location to Object's Custom Properties.
438 Note:
439 Writes Pivot Point Location to Object's Custom Properties
440 as Vector to 'PDT_PP_LOC' - Requires Confirmation through dialogue
442 Args:
443 context: Blender bpy.context instance.
445 Note:
446 Uses pg.pivot_loc scene variable
448 Returns:
449 Status Set.
452 scene = context.scene
453 pg = scene.pdt_pg
454 obj = bpy.context.view_layer.objects.active
455 if obj is None:
456 self.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ)
457 return {"FINISHED"}
458 obj["PDT_PP_LOC"] = pg.pivot_loc
459 return {"FINISHED"}
461 def invoke(self, context, event):
462 return context.window_manager.invoke_props_dialog(self)
464 def draw(self, context):
465 row = self.layout
466 row.label(text=PDT_CON_AREYOURSURE)
469 class PDT_OT_PivotRead(Operator):
470 """Read Pivot Point Location from Object"""
472 bl_idname = "pdt.pivotread"
473 bl_label = "PDT Read PP"
474 bl_options = {"REGISTER", "UNDO"}
476 @classmethod
477 def poll(cls, context):
478 """Check Object Status.
480 Args:
481 context: Blender bpy.context instance.
483 Returns:
484 Nothing.
487 obj = context.object
488 if obj is None:
489 return False
490 return all([bool(obj), obj.type == "MESH"])
492 def execute(self, context):
493 """Reads Pivot Point Location from Object's Custom Properties.
495 Note:
496 Sets Pivot Point Location from Object's Custom Properties
497 using 'PDT_PP_LOC'
499 Args:
500 context: Blender bpy.context instance.
502 Note:
503 Uses pg.pivot_loc scene variable
505 Returns:
506 Status Set.
509 scene = context.scene
510 pg = scene.pdt_pg
511 obj = bpy.context.view_layer.objects.active
512 if obj is None:
513 self.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ)
514 return {"FINISHED"}
515 if "PDT_PP_LOC" in obj:
516 pg.pivot_loc = obj["PDT_PP_LOC"]
517 return {"FINISHED"}
519 self.report({"ERROR"}, PDT_ERR_NOPPLOC)
520 return {"FINISHED"}