1 # SPDX-FileCopyrightText: 2019-2022 Alan Odom (Clockmender)
2 # SPDX-FileCopyrightText: 2019-2022 Rune Morling (ermo)
4 # SPDX-License-Identifier: GPL-2.0-or-later
8 from bpy
.types
import Operator
, SpaceView3D
9 from mathutils
import Vector
, Matrix
11 from .pdt_functions
import view_coords
, draw_callback_3d
12 from .pdt_msg_strings
import (
22 class PDT_OT_ModalDrawOperator(bpy
.types
.Operator
):
23 """Show/Hide Pivot Point"""
25 bl_idname
= "pdt.modaldraw"
26 bl_label
= "PDT Modal Draw"
27 bl_options
= {"REGISTER", "UNDO"}
29 _handle
= None # keep function handler
32 def handle_add(self
, context
):
33 """Draw Pivot Point Graphic if not displayed.
36 Draws 7 element Pivot Point Graphic
39 context: Blender bpy.context instance.
45 if PDT_OT_ModalDrawOperator
._handle
is None:
46 PDT_OT_ModalDrawOperator
._handle
= SpaceView3D
.draw_handler_add(
47 draw_callback_3d
, (self
, context
), "WINDOW", "POST_VIEW"
49 context
.window_manager
.pdt_run_opengl
= True
52 def handle_remove(self
, context
):
53 """Remove Pivot Point Graphic if displayed.
56 Removes 7 element Pivot Point Graphic
59 context: Blender bpy.context instance.
65 if PDT_OT_ModalDrawOperator
._handle
is not None:
66 SpaceView3D
.draw_handler_remove(PDT_OT_ModalDrawOperator
._handle
, "WINDOW")
67 PDT_OT_ModalDrawOperator
._handle
= None
68 context
.window_manager
.pdt_run_opengl
= False
70 def execute(self
, context
):
71 """Pivot Point Show/Hide Button Function.
74 Operational execute function for Show/Hide Pivot Point function
77 context: Blender bpy.context instance.
83 if context
.area
.type == "VIEW_3D":
84 if context
.window_manager
.pdt_run_opengl
is False:
85 self
.handle_add(self
, context
)
86 context
.area
.tag_redraw()
88 self
.handle_remove(self
, context
)
89 context
.area
.tag_redraw()
93 self
.report({"ERROR"}, PDT_ERR_NO3DVIEW
)
97 class PDT_OT_ViewPlaneRotate(Operator
):
98 """Rotate Selected Vertices about Pivot Point in View Plane"""
100 bl_idname
= "pdt.viewplanerot"
101 bl_label
= "PDT View Rotate"
102 bl_options
= {"REGISTER", "UNDO"}
105 def poll(cls
, context
):
106 """Check Object Status.
109 context: Blender bpy.context instance.
118 return all([bool(obj
), obj
.type == "MESH", obj
.mode
== "EDIT"])
121 def execute(self
, context
):
122 """Rotate Selected Vertices about Pivot Point.
125 Rotates any selected vertices about the Pivot Point
126 in View Oriented coordinates, works in any view orientation.
129 context: Blender bpy.context instance.
132 Uses pg.pivot_loc, pg.pivot_ang scene variables
138 scene
= context
.scene
140 obj
= bpy
.context
.view_layer
.objects
.active
142 self
.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ
)
144 if obj
.mode
!= "EDIT":
145 error_message
= f
"{PDT_ERR_EDIT_MODE} {obj.mode})"
146 self
.report({"ERROR"}, error_message
)
148 bm
= bmesh
.from_edit_mesh(obj
.data
)
149 v1
= Vector((0, 0, 0))
150 v2
= view_coords(0, 0, 1)
151 axis
= (v2
- v1
).normalized()
152 rot
= Matrix
.Rotation((pg
.pivot_ang
* pi
/ 180), 4, axis
)
153 verts
= verts
= [v
for v
in bm
.verts
if v
.select
]
155 bm
, cent
=pg
.pivot_loc
- obj
.matrix_world
.decompose()[0], matrix
=rot
, verts
=verts
157 bmesh
.update_edit_mesh(obj
.data
)
161 class PDT_OT_ViewPlaneScale(Operator
):
162 """Scale Selected Vertices about Pivot Point"""
164 bl_idname
= "pdt.viewscale"
165 bl_label
= "PDT View Scale"
166 bl_options
= {"REGISTER", "UNDO"}
169 def poll(cls
, context
):
170 """Check Object Status.
173 context: Blender bpy.context instance.
182 return all([bool(obj
), obj
.type == "MESH", obj
.mode
== "EDIT"])
185 def execute(self
, context
):
186 """Scales Selected Vertices about Pivot Point.
189 Scales any selected vertices about the Pivot Point
190 in View Oriented coordinates, works in any view orientation
193 context: Blender bpy.context instance.
196 Uses pg.pivot_loc, pg.pivot_scale scene variables
202 scene
= context
.scene
204 obj
= bpy
.context
.view_layer
.objects
.active
206 self
.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ
)
208 if obj
.mode
!= "EDIT":
209 error_message
= f
"{PDT_ERR_EDIT_MODE} {obj.mode})"
210 self
.report({"ERROR"}, error_message
)
212 bm
= bmesh
.from_edit_mesh(obj
.data
)
213 verts
= verts
= [v
for v
in bm
.verts
if v
.select
]
215 delta_x
= (pg
.pivot_loc
.x
- obj
.matrix_world
.decompose()[0].x
- v
.co
.x
) * (
218 delta_y
= (pg
.pivot_loc
.y
- obj
.matrix_world
.decompose()[0].y
- v
.co
.y
) * (
221 delta_z
= (pg
.pivot_loc
.z
- obj
.matrix_world
.decompose()[0].z
- v
.co
.z
) * (
224 delta_v
= Vector((delta_x
, delta_y
, delta_z
))
225 v
.co
= v
.co
+ delta_v
226 bmesh
.update_edit_mesh(obj
.data
)
230 class PDT_OT_PivotToCursor(Operator
):
231 """Set The Pivot Point to Cursor Location"""
233 bl_idname
= "pdt.pivotcursor"
234 bl_label
= "PDT Pivot To Cursor"
235 bl_options
= {"REGISTER", "UNDO"}
237 def execute(self
, context
):
238 """Moves Pivot Point to Cursor Location.
241 Moves Pivot Point to Cursor Location in active scene
244 context: Blender bpy.context instance.
250 scene
= context
.scene
252 old_cursor_loc
= scene
.cursor
.location
.copy()
253 pg
.pivot_loc
= scene
.cursor
.location
254 scene
.cursor
.location
= old_cursor_loc
258 class PDT_OT_CursorToPivot(Operator
):
259 """Set The Cursor Location to Pivot Point"""
261 bl_idname
= "pdt.cursorpivot"
262 bl_label
= "PDT Cursor To Pivot"
263 bl_options
= {"REGISTER", "UNDO"}
265 def execute(self
, context
):
266 """Moves Cursor to Pivot Point Location.
269 Moves Cursor to Pivot Point Location in active scene
272 context: Blender bpy.context instance.
278 scene
= context
.scene
280 scene
.cursor
.location
= pg
.pivot_loc
284 class PDT_OT_PivotSelected(Operator
):
285 """Set Pivot Point to Selected Geometry"""
287 bl_idname
= "pdt.pivotselected"
288 bl_label
= "PDT Pivot to Selected"
289 bl_options
= {"REGISTER", "UNDO"}
292 def poll(cls
, context
):
293 """Check Object Status.
296 context: Blender bpy.context instance.
305 return all([bool(obj
), obj
.type == "MESH", obj
.mode
== "EDIT"])
308 def execute(self
, context
):
309 """Moves Pivot Point centroid of Selected Geometry.
312 Moves Pivot Point centroid of Selected Geometry in active scene
313 using Snap_Cursor_To_Selected, then puts cursor back to original location.
316 context: Blender bpy.context instance.
322 scene
= context
.scene
324 obj
= bpy
.context
.view_layer
.objects
.active
326 self
.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ
)
328 if obj
.mode
!= "EDIT":
329 error_message
= f
"{PDT_ERR_EDIT_MODE} {obj.mode})"
330 self
.report({"ERROR"}, error_message
)
332 bm
= bmesh
.from_edit_mesh(obj
.data
)
333 verts
= verts
= [v
for v
in bm
.verts
if v
.select
]
335 old_cursor_loc
= scene
.cursor
.location
.copy()
336 bpy
.ops
.view3d
.snap_cursor_to_selected()
337 pg
.pivot_loc
= scene
.cursor
.location
338 scene
.cursor
.location
= old_cursor_loc
341 self
.report({"ERROR"}, PDT_ERR_NO_SEL_GEOM
)
345 class PDT_OT_PivotOrigin(Operator
):
346 """Set Pivot Point at Object Origin"""
348 bl_idname
= "pdt.pivotorigin"
349 bl_label
= "PDT Pivot to Object Origin"
350 bl_options
= {"REGISTER", "UNDO"}
353 def poll(cls
, context
):
354 """Check Object Status.
357 context: Blender bpy.context instance.
366 return all([bool(obj
), obj
.type == "MESH"])
368 def execute(self
, context
):
369 """Moves Pivot Point to Object Origin.
372 Moves Pivot Point to Object Origin in active scene
375 context: Blender bpy.context instance.
381 scene
= context
.scene
383 obj
= bpy
.context
.view_layer
.objects
.active
385 self
.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ
)
387 old_cursor_loc
= scene
.cursor
.location
.copy()
388 obj_loc
= obj
.matrix_world
.decompose()[0]
389 pg
.pivot_loc
= obj_loc
390 scene
.cursor
.location
= old_cursor_loc
394 class PDT_OT_PivotWrite(Operator
):
395 """Write Pivot Point Location to Object"""
397 bl_idname
= "pdt.pivotwrite"
398 bl_label
= "PDT Write PP to Object?"
399 bl_options
= {"REGISTER", "UNDO"}
402 def poll(cls
, context
):
403 """Check Object Status.
406 context: Blender bpy.context instance.
415 return all([bool(obj
), obj
.type == "MESH"])
417 def execute(self
, context
):
418 """Writes Pivot Point Location to Object's Custom Properties.
421 Writes Pivot Point Location to Object's Custom Properties
422 as Vector to 'PDT_PP_LOC' - Requires Confirmation through dialogue
425 context: Blender bpy.context instance.
428 Uses pg.pivot_loc scene variable
434 scene
= context
.scene
436 obj
= bpy
.context
.view_layer
.objects
.active
438 self
.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ
)
440 obj
["PDT_PP_LOC"] = pg
.pivot_loc
443 def invoke(self
, context
, event
):
444 return context
.window_manager
.invoke_props_dialog(self
)
446 def draw(self
, context
):
448 row
.label(text
=PDT_CON_AREYOURSURE
)
451 class PDT_OT_PivotRead(Operator
):
452 """Read Pivot Point Location from Object"""
454 bl_idname
= "pdt.pivotread"
455 bl_label
= "PDT Read PP"
456 bl_options
= {"REGISTER", "UNDO"}
459 def poll(cls
, context
):
460 """Check Object Status.
463 context: Blender bpy.context instance.
472 return all([bool(obj
), obj
.type == "MESH"])
474 def execute(self
, context
):
475 """Reads Pivot Point Location from Object's Custom Properties.
478 Sets Pivot Point Location from Object's Custom Properties
482 context: Blender bpy.context instance.
485 Uses pg.pivot_loc scene variable
491 scene
= context
.scene
493 obj
= bpy
.context
.view_layer
.objects
.active
495 self
.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ
)
497 if "PDT_PP_LOC" in obj
:
498 pg
.pivot_loc
= obj
["PDT_PP_LOC"]
501 self
.report({"ERROR"}, PDT_ERR_NOPPLOC
)