mirror of
https://github.com/blender/blender-addons.git
synced 2025-08-20 13:22:58 +00:00

Moved all PDT Design Operations and Tools to Command Line file to de-dupe code. All can now be called from the command line, e.g. a command of "otc" sets the active objects Origin To Cursor, etc. Needs extensive further testing to see if all Operations in all Modes still work exactly as before. Tools menu split out from PDT Design so it can be minimised when not in use.
444 lines
12 KiB
Python
444 lines
12 KiB
Python
# ***** BEGIN GPL LICENSE BLOCK *****
|
|
#
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software Foundation,
|
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
#
|
|
# ***** END GPL LICENCE BLOCK *****
|
|
#
|
|
# -----------------------------------------------------------------------
|
|
# Author: Alan Odom (Clockmender), Rune Morling (ermo) Copyright (c) 2019
|
|
# -----------------------------------------------------------------------
|
|
#
|
|
import bpy
|
|
import bmesh
|
|
from bpy.types import Operator, SpaceView3D
|
|
from mathutils import Vector, Matrix
|
|
from math import pi
|
|
from .pdt_functions import view_coords, drawCallback3D
|
|
from .pdt_msg_strings import (
|
|
PDT_CON_AREYOURSURE,
|
|
PDT_ERR_EDIT_MODE,
|
|
PDT_ERR_NO3DVIEW,
|
|
PDT_ERR_NOPPLOC,
|
|
PDT_ERR_NO_ACT_OBJ,
|
|
PDT_ERR_NO_SEL_GEOM
|
|
)
|
|
|
|
|
|
class PDT_OT_ModalDrawOperator(bpy.types.Operator):
|
|
"""Show/Hide Pivot Point."""
|
|
|
|
bl_idname = "pdt.modaldraw"
|
|
bl_label = "PDT Modal Draw"
|
|
|
|
_handle = None # keep function handler
|
|
|
|
@staticmethod
|
|
def handle_add(self, context):
|
|
"""Draw Pivot Point Graphic if not displayed.
|
|
|
|
Draws 7 element Pivot Point Graphic
|
|
|
|
Args:
|
|
context: Blender bpy.context instance.
|
|
|
|
Returns:
|
|
Nothing.
|
|
"""
|
|
|
|
if PDT_OT_ModalDrawOperator._handle is None:
|
|
PDT_OT_ModalDrawOperator._handle = SpaceView3D.draw_handler_add(
|
|
drawCallback3D, (self, context), "WINDOW", "POST_VIEW"
|
|
)
|
|
context.window_manager.pdt_run_opengl = True
|
|
|
|
@staticmethod
|
|
def handle_remove(self, context):
|
|
"""Remove Pivot Point Graphic if displayed.
|
|
|
|
Removes 7 element Pivot Point Graphic
|
|
|
|
Args:
|
|
context: Blender bpy.context instance.
|
|
|
|
Returns:
|
|
Nothing.
|
|
"""
|
|
|
|
if PDT_OT_ModalDrawOperator._handle is not None:
|
|
SpaceView3D.draw_handler_remove(PDT_OT_ModalDrawOperator._handle, "WINDOW")
|
|
PDT_OT_ModalDrawOperator._handle = None
|
|
context.window_manager.pdt_run_opengl = False
|
|
|
|
def execute(self, context):
|
|
"""Pivot Point Show/Hide Button Function.
|
|
|
|
Operational execute function for Show/Hide Pivot Point function
|
|
|
|
Args:
|
|
context: Blender bpy.context instance.
|
|
|
|
Returns:
|
|
Status Set.
|
|
"""
|
|
|
|
if context.area.type == "VIEW_3D":
|
|
if context.window_manager.pdt_run_opengl is False:
|
|
self.handle_add(self, context)
|
|
context.area.tag_redraw()
|
|
else:
|
|
self.handle_remove(self, context)
|
|
context.area.tag_redraw()
|
|
|
|
return {"FINISHED"}
|
|
else:
|
|
self.report({"ERROR"}, PDT_ERR_NO3DVIEW)
|
|
|
|
return {"CANCELLED"}
|
|
|
|
|
|
class PDT_OT_ViewPlaneRotate(Operator):
|
|
"""Rotate Selected Vertices about Pivot Point in View Plane."""
|
|
|
|
bl_idname = "pdt.viewplanerot"
|
|
bl_label = "PDT View Rotate"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
ob = context.object
|
|
if ob is None:
|
|
return False
|
|
return all([bool(ob), ob.type == "MESH", ob.mode == "EDIT"])
|
|
|
|
|
|
def execute(self, context):
|
|
"""Rotate Selected Vertices about Pivot Point.
|
|
|
|
Rotates any selected vertices about the Pivot Point
|
|
in View Oriented coordinates, works in any view orientation.
|
|
|
|
Args:
|
|
context: Blender bpy.context instance.
|
|
|
|
Notes:
|
|
Uses pg.pivot_loc, pg.pivot_ang scene variables
|
|
|
|
Returns:
|
|
Status Set.
|
|
"""
|
|
|
|
scene = context.scene
|
|
pg = scene.pdt_pg
|
|
obj = bpy.context.view_layer.objects.active
|
|
if obj is None:
|
|
self.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ)
|
|
return {"FINISHED"}
|
|
if obj.mode != "EDIT":
|
|
errmsg = f"{PDT_ERR_EDIT_MODE} {obj.mode})"
|
|
self.report({"ERROR"}, errmsg)
|
|
return {"FINISHED"}
|
|
bm = bmesh.from_edit_mesh(obj.data)
|
|
v1 = Vector((0, 0, 0))
|
|
v2 = view_coords(0, 0, 1)
|
|
axis = (v2 - v1).normalized()
|
|
rot = Matrix.Rotation((pg.pivot_ang * pi / 180), 4, axis)
|
|
verts = verts = [v for v in bm.verts if v.select]
|
|
bmesh.ops.rotate(
|
|
bm, cent=pg.pivot_loc - obj.matrix_world.decompose()[0], matrix=rot, verts=verts
|
|
)
|
|
bmesh.update_edit_mesh(obj.data)
|
|
return {"FINISHED"}
|
|
|
|
|
|
class PDT_OT_ViewPlaneScale(Operator):
|
|
"""Scale Selected Vertices about Pivot Point."""
|
|
|
|
bl_idname = "pdt.viewscale"
|
|
bl_label = "PDT View Scale"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
ob = context.object
|
|
if ob is None:
|
|
return False
|
|
return all([bool(ob), ob.type == "MESH", ob.mode == "EDIT"])
|
|
|
|
|
|
def execute(self, context):
|
|
"""Scales Selected Vertices about Pivot Point.
|
|
|
|
Scales any selected vertices about the Pivot Point
|
|
in View Oriented coordinates, works in any view orientation
|
|
|
|
Args:
|
|
context: Blender bpy.context instance.
|
|
|
|
Note:
|
|
Uses pg.pivot_loc, pg.pivot_scale scene variables
|
|
|
|
Returns:
|
|
Status Set.
|
|
"""
|
|
|
|
scene = context.scene
|
|
pg = scene.pdt_pg
|
|
obj = bpy.context.view_layer.objects.active
|
|
if obj is None:
|
|
self.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ)
|
|
return {"FINISHED"}
|
|
if obj.mode != "EDIT":
|
|
errmsg = f"{PDT_ERR_EDIT_MODE} {obj.mode})"
|
|
self.report({"ERROR"}, errmsg)
|
|
return {"FINISHED"}
|
|
bm = bmesh.from_edit_mesh(obj.data)
|
|
verts = verts = [v for v in bm.verts if v.select]
|
|
for v in verts:
|
|
dx = (pg.pivot_loc.x - obj.matrix_world.decompose()[0].x - v.co.x) * (
|
|
1 - pg.pivot_scale.x
|
|
)
|
|
dy = (pg.pivot_loc.y - obj.matrix_world.decompose()[0].y - v.co.y) * (
|
|
1 - pg.pivot_scale.y
|
|
)
|
|
dz = (pg.pivot_loc.z - obj.matrix_world.decompose()[0].z - v.co.z) * (
|
|
1 - pg.pivot_scale.z
|
|
)
|
|
dv = Vector((dx, dy, dz))
|
|
v.co = v.co + dv
|
|
bmesh.update_edit_mesh(obj.data)
|
|
return {"FINISHED"}
|
|
|
|
|
|
class PDT_OT_PivotToCursor(Operator):
|
|
"""Set The Pivot Point to Cursor Location."""
|
|
|
|
bl_idname = "pdt.pivotcursor"
|
|
bl_label = "PDT Pivot To Cursor"
|
|
|
|
def execute(self, context):
|
|
"""Moves Pivot Point to Cursor Location.
|
|
|
|
Moves Pivot Point to Cursor Location in active scene
|
|
|
|
Args:
|
|
context: Blender bpy.context instance.
|
|
|
|
Returns:
|
|
Status Set.
|
|
"""
|
|
|
|
scene = context.scene
|
|
pg = scene.pdt_pg
|
|
pg.pivot_loc = scene.cursor.location
|
|
return {"FINISHED"}
|
|
|
|
|
|
class PDT_OT_CursorToPivot(Operator):
|
|
"""Set The Cursor Location to Pivot Point."""
|
|
|
|
bl_idname = "pdt.cursorpivot"
|
|
bl_label = "PDT Cursor To Pivot"
|
|
|
|
def execute(self, context):
|
|
"""Moves Cursor to Pivot Point Location.
|
|
|
|
Moves Cursor to Pivot Point Location in active scene
|
|
|
|
Args:
|
|
context: Blender bpy.context instance.
|
|
|
|
Returns:
|
|
Status Set.
|
|
"""
|
|
|
|
scene = context.scene
|
|
pg = scene.pdt_pg
|
|
scene.cursor.location = pg.pivot_loc
|
|
return {"FINISHED"}
|
|
|
|
|
|
class PDT_OT_PivotSelected(Operator):
|
|
"""Set Pivot Point to Selected Geometry."""
|
|
|
|
bl_idname = "pdt.pivotselected"
|
|
bl_label = "PDT Pivot to Selected"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
ob = context.object
|
|
if ob is None:
|
|
return False
|
|
return all([bool(ob), ob.type == "MESH", ob.mode == "EDIT"])
|
|
|
|
|
|
def execute(self, context):
|
|
"""Moves Pivot Point centroid of Selected Geometry.
|
|
|
|
Moves Pivot Point centroid of Selected Geometry in active scene
|
|
using Snap_Cursor_To_Selected, then puts cursor back to original location.
|
|
|
|
Args:
|
|
context: Blender bpy.context instance.
|
|
|
|
Returns:
|
|
Status Set.
|
|
"""
|
|
|
|
scene = context.scene
|
|
pg = scene.pdt_pg
|
|
obj = bpy.context.view_layer.objects.active
|
|
if obj is None:
|
|
self.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ)
|
|
return {"FINISHED"}
|
|
if obj.mode != "EDIT":
|
|
errmsg = f"{PDT_ERR_EDIT_MODE} {obj.mode})"
|
|
self.report({"ERROR"}, errmsg)
|
|
return {"FINISHED"}
|
|
bm = bmesh.from_edit_mesh(obj.data)
|
|
verts = verts = [v for v in bm.verts if v.select]
|
|
if len(verts) > 0:
|
|
old_cursor_loc = scene.cursor.location.copy()
|
|
bpy.ops.view3d.snap_cursor_to_selected()
|
|
pg.pivot_loc = scene.cursor.location
|
|
scene.cursor.location = old_cursor_loc
|
|
return {"FINISHED"}
|
|
else:
|
|
self.report({"ERROR"}, PDT_ERR_NO_SEL_GEOM)
|
|
return {"FINISHED"}
|
|
|
|
|
|
class PDT_OT_PivotOrigin(Operator):
|
|
"""Set Pivot Point at Object Origin."""
|
|
|
|
bl_idname = "pdt.pivotorigin"
|
|
bl_label = "PDT Pivot to Object Origin"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
ob = context.object
|
|
if ob is None:
|
|
return False
|
|
return all([bool(ob), ob.type == "MESH"])
|
|
|
|
def execute(self, context):
|
|
"""Moves Pivot Point to Object Origin.
|
|
|
|
Moves Pivot Point to Object Origin in active scene
|
|
|
|
Args:
|
|
context: Blender bpy.context instance.
|
|
|
|
Returns:
|
|
Status Set.
|
|
"""
|
|
|
|
scene = context.scene
|
|
pg = scene.pdt_pg
|
|
obj = bpy.context.view_layer.objects.active
|
|
if obj is None:
|
|
self.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ)
|
|
return {"FINISHED"}
|
|
obj_loc = obj.matrix_world.decompose()[0]
|
|
pg.pivot_loc = obj_loc
|
|
return {"FINISHED"}
|
|
|
|
|
|
class PDT_OT_PivotWrite(Operator):
|
|
"""Write Pivot Point Location to Object."""
|
|
|
|
bl_idname = "pdt.pivotwrite"
|
|
bl_label = "PDT Write PP to Object?"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
ob = context.object
|
|
if ob is None:
|
|
return False
|
|
return all([bool(ob), ob.type == "MESH"])
|
|
|
|
def execute(self, context):
|
|
"""Writes Pivot Point Location to Object's Custom Properties.
|
|
|
|
Writes Pivot Point Location to Object's Custom Properties
|
|
as Vector to 'PDT_PP_LOC' - Requires Confirmation through dialogue
|
|
|
|
Args:
|
|
context: Blender bpy.context instance.
|
|
|
|
Note:
|
|
Uses pg.pivot_loc scene variable
|
|
|
|
Returns:
|
|
Status Set.
|
|
"""
|
|
|
|
scene = context.scene
|
|
pg = scene.pdt_pg
|
|
obj = bpy.context.view_layer.objects.active
|
|
if obj is None:
|
|
self.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ)
|
|
return {"FINISHED"}
|
|
obj["PDT_PP_LOC"] = pg.pivot_loc
|
|
return {"FINISHED"}
|
|
|
|
def invoke(self, context, event):
|
|
return context.window_manager.invoke_props_dialog(self)
|
|
|
|
def draw(self, context):
|
|
row = self.layout
|
|
row.label(text=PDT_CON_AREYOURSURE)
|
|
|
|
|
|
class PDT_OT_PivotRead(Operator):
|
|
"""Read Pivot Point Location from Object."""
|
|
|
|
bl_idname = "pdt.pivotread"
|
|
bl_label = "PDT Read PP"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
ob = context.object
|
|
if ob is None:
|
|
return False
|
|
return all([bool(ob), ob.type == "MESH"])
|
|
|
|
def execute(self, context):
|
|
"""Reads Pivot Point Location from Object's Custom Properties.
|
|
|
|
Sets Pivot Point Location from Object's Custom Properties
|
|
using 'PDT_PP_LOC'
|
|
|
|
Args:
|
|
context: Blender bpy.context instance.
|
|
|
|
Note:
|
|
Uses pg.pivot_loc scene variable
|
|
|
|
Returns:
|
|
Status Set.
|
|
"""
|
|
|
|
scene = context.scene
|
|
pg = scene.pdt_pg
|
|
obj = bpy.context.view_layer.objects.active
|
|
if obj is None:
|
|
self.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ)
|
|
return {"FINISHED"}
|
|
if "PDT_PP_LOC" in obj:
|
|
pg.pivot_loc = obj["PDT_PP_LOC"]
|
|
return {"FINISHED"}
|
|
else:
|
|
self.report({"ERROR"}, PDT_ERR_NOPPLOC)
|
|
return {"FINISHED"}
|