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 # -----------------------------------------------------------------------
26 from bpy
.types
import Operator
, SpaceView3D
27 from mathutils
import Vector
, Matrix
29 from .pdt_functions
import view_coords
, draw_callback_3d
30 from .pdt_msg_strings
import (
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
50 def handle_add(self
, context
):
51 """Draw Pivot Point Graphic if not displayed.
54 Draws 7 element Pivot Point Graphic
57 context: Blender bpy.context instance.
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
70 def handle_remove(self
, context
):
71 """Remove Pivot Point Graphic if displayed.
74 Removes 7 element Pivot Point Graphic
77 context: Blender bpy.context instance.
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.
92 Operational execute function for Show/Hide Pivot Point function
95 context: Blender bpy.context instance.
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()
106 self
.handle_remove(self
, context
)
107 context
.area
.tag_redraw()
111 self
.report({"ERROR"}, PDT_ERR_NO3DVIEW
)
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"}
123 def poll(cls
, context
):
124 """Check Object Status.
127 context: Blender bpy.context instance.
136 return all([bool(obj
), obj
.type == "MESH", obj
.mode
== "EDIT"])
139 def execute(self
, context
):
140 """Rotate Selected Vertices about Pivot Point.
143 Rotates any selected vertices about the Pivot Point
144 in View Oriented coordinates, works in any view orientation.
147 context: Blender bpy.context instance.
150 Uses pg.pivot_loc, pg.pivot_ang scene variables
156 scene
= context
.scene
158 obj
= bpy
.context
.view_layer
.objects
.active
160 self
.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ
)
162 if obj
.mode
!= "EDIT":
163 error_message
= f
"{PDT_ERR_EDIT_MODE} {obj.mode})"
164 self
.report({"ERROR"}, error_message
)
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
]
173 bm
, cent
=pg
.pivot_loc
- obj
.matrix_world
.decompose()[0], matrix
=rot
, verts
=verts
175 bmesh
.update_edit_mesh(obj
.data
)
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"}
187 def poll(cls
, context
):
188 """Check Object Status.
191 context: Blender bpy.context instance.
200 return all([bool(obj
), obj
.type == "MESH", obj
.mode
== "EDIT"])
203 def execute(self
, context
):
204 """Scales Selected Vertices about Pivot Point.
207 Scales any selected vertices about the Pivot Point
208 in View Oriented coordinates, works in any view orientation
211 context: Blender bpy.context instance.
214 Uses pg.pivot_loc, pg.pivot_scale scene variables
220 scene
= context
.scene
222 obj
= bpy
.context
.view_layer
.objects
.active
224 self
.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ
)
226 if obj
.mode
!= "EDIT":
227 error_message
= f
"{PDT_ERR_EDIT_MODE} {obj.mode})"
228 self
.report({"ERROR"}, error_message
)
230 bm
= bmesh
.from_edit_mesh(obj
.data
)
231 verts
= verts
= [v
for v
in bm
.verts
if v
.select
]
233 delta_x
= (pg
.pivot_loc
.x
- obj
.matrix_world
.decompose()[0].x
- v
.co
.x
) * (
236 delta_y
= (pg
.pivot_loc
.y
- obj
.matrix_world
.decompose()[0].y
- v
.co
.y
) * (
239 delta_z
= (pg
.pivot_loc
.z
- obj
.matrix_world
.decompose()[0].z
- v
.co
.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
)
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.
259 Moves Pivot Point to Cursor Location in active scene
262 context: Blender bpy.context instance.
268 scene
= context
.scene
270 old_cursor_loc
= scene
.cursor
.location
.copy()
271 pg
.pivot_loc
= scene
.cursor
.location
272 scene
.cursor
.location
= old_cursor_loc
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.
287 Moves Cursor to Pivot Point Location in active scene
290 context: Blender bpy.context instance.
296 scene
= context
.scene
298 scene
.cursor
.location
= pg
.pivot_loc
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"}
310 def poll(cls
, context
):
311 """Check Object Status.
314 context: Blender bpy.context instance.
323 return all([bool(obj
), obj
.type == "MESH", obj
.mode
== "EDIT"])
326 def execute(self
, context
):
327 """Moves Pivot Point centroid of Selected Geometry.
330 Moves Pivot Point centroid of Selected Geometry in active scene
331 using Snap_Cursor_To_Selected, then puts cursor back to original location.
334 context: Blender bpy.context instance.
340 scene
= context
.scene
342 obj
= bpy
.context
.view_layer
.objects
.active
344 self
.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ
)
346 if obj
.mode
!= "EDIT":
347 error_message
= f
"{PDT_ERR_EDIT_MODE} {obj.mode})"
348 self
.report({"ERROR"}, error_message
)
350 bm
= bmesh
.from_edit_mesh(obj
.data
)
351 verts
= verts
= [v
for v
in bm
.verts
if v
.select
]
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
359 self
.report({"ERROR"}, PDT_ERR_NO_SEL_GEOM
)
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"}
371 def poll(cls
, context
):
372 """Check Object Status.
375 context: Blender bpy.context instance.
384 return all([bool(obj
), obj
.type == "MESH"])
386 def execute(self
, context
):
387 """Moves Pivot Point to Object Origin.
390 Moves Pivot Point to Object Origin in active scene
393 context: Blender bpy.context instance.
399 scene
= context
.scene
401 obj
= bpy
.context
.view_layer
.objects
.active
403 self
.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ
)
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
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"}
420 def poll(cls
, context
):
421 """Check Object Status.
424 context: Blender bpy.context instance.
433 return all([bool(obj
), obj
.type == "MESH"])
435 def execute(self
, context
):
436 """Writes Pivot Point Location to Object's Custom Properties.
439 Writes Pivot Point Location to Object's Custom Properties
440 as Vector to 'PDT_PP_LOC' - Requires Confirmation through dialogue
443 context: Blender bpy.context instance.
446 Uses pg.pivot_loc scene variable
452 scene
= context
.scene
454 obj
= bpy
.context
.view_layer
.objects
.active
456 self
.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ
)
458 obj
["PDT_PP_LOC"] = pg
.pivot_loc
461 def invoke(self
, context
, event
):
462 return context
.window_manager
.invoke_props_dialog(self
)
464 def draw(self
, context
):
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"}
477 def poll(cls
, context
):
478 """Check Object Status.
481 context: Blender bpy.context instance.
490 return all([bool(obj
), obj
.type == "MESH"])
492 def execute(self
, context
):
493 """Reads Pivot Point Location from Object's Custom Properties.
496 Sets Pivot Point Location from Object's Custom Properties
500 context: Blender bpy.context instance.
503 Uses pg.pivot_loc scene variable
509 scene
= context
.scene
511 obj
= bpy
.context
.view_layer
.objects
.active
513 self
.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ
)
515 if "PDT_PP_LOC" in obj
:
516 pg
.pivot_loc
= obj
["PDT_PP_LOC"]
519 self
.report({"ERROR"}, PDT_ERR_NOPPLOC
)