Skip to content
Snippets Groups Projects
pdt_pivot_point.py 14.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • # ***** 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, draw_callback_3d
    
    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"
    
        bl_options = {"REGISTER", "UNDO"}
    
    
        _handle = None  # keep function handler
    
        @staticmethod
        def handle_add(self, context):
            """Draw Pivot Point Graphic if not displayed.
    
    
            Note:
                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(
    
                    draw_callback_3d, (self, context), "WINDOW", "POST_VIEW"
    
                )
                context.window_manager.pdt_run_opengl = True
    
        @staticmethod
        def handle_remove(self, context):
            """Remove Pivot Point Graphic if displayed.
    
    
            Note:
                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.
    
    
            Note:
                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"}
    
    
            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"
    
        bl_options = {"REGISTER", "UNDO"}
    
    
        @classmethod
        def poll(cls, context):
    
            """Check Object Status.
    
            Args:
                context: Blender bpy.context instance.
    
            Returns:
                Nothing.
            """
    
    
            obj = context.object
            if obj is None:
    
            return all([bool(obj), obj.type == "MESH", obj.mode == "EDIT"])
    
    
    
        def execute(self, context):
            """Rotate Selected Vertices about Pivot Point.
    
    
            Note:
                Rotates any selected vertices about the Pivot Point
                in View Oriented coordinates, works in any view orientation.
    
    
            Args:
                context: Blender bpy.context instance.
    
    
                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":
    
                error_message = f"{PDT_ERR_EDIT_MODE} {obj.mode})"
                self.report({"ERROR"}, error_message)
    
                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"
    
        bl_options = {"REGISTER", "UNDO"}
    
    
        @classmethod
        def poll(cls, context):
    
            """Check Object Status.
    
            Args:
                context: Blender bpy.context instance.
    
            Returns:
                Nothing.
            """
    
    
            obj = context.object
            if obj is None:
    
            return all([bool(obj), obj.type == "MESH", obj.mode == "EDIT"])
    
    
    
        def execute(self, context):
            """Scales Selected Vertices about Pivot Point.
    
    
            Note:
                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":
    
                error_message = f"{PDT_ERR_EDIT_MODE} {obj.mode})"
                self.report({"ERROR"}, error_message)
    
                return {"FINISHED"}
            bm = bmesh.from_edit_mesh(obj.data)
            verts = verts = [v for v in bm.verts if v.select]
            for v in verts:
    
                delta_x = (pg.pivot_loc.x - obj.matrix_world.decompose()[0].x - v.co.x) * (
    
                delta_y = (pg.pivot_loc.y - obj.matrix_world.decompose()[0].y - v.co.y) * (
    
                delta_z = (pg.pivot_loc.z - obj.matrix_world.decompose()[0].z - v.co.z) * (
    
                delta_v = Vector((delta_x, delta_y, delta_z))
                v.co = v.co + delta_v
    
            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"
    
        bl_options = {"REGISTER", "UNDO"}
    
    
        def execute(self, context):
            """Moves Pivot Point to Cursor Location.
    
    
            Note:
                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
    
            old_cursor_loc = scene.cursor.location.copy()
    
            pg.pivot_loc = scene.cursor.location
    
            scene.cursor.location = old_cursor_loc
    
            return {"FINISHED"}
    
    
    class PDT_OT_CursorToPivot(Operator):
        """Set The Cursor Location to Pivot Point."""
    
        bl_idname = "pdt.cursorpivot"
        bl_label = "PDT Cursor To Pivot"
    
        bl_options = {"REGISTER", "UNDO"}
    
    
        def execute(self, context):
            """Moves Cursor to Pivot Point Location.
    
    
            Note:
                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"
    
        bl_options = {"REGISTER", "UNDO"}
    
    
        @classmethod
        def poll(cls, context):
    
            """Check Object Status.
    
            Args:
                context: Blender bpy.context instance.
    
            Returns:
                Nothing.
            """
    
    
            obj = context.object
            if obj is None:
    
            return all([bool(obj), obj.type == "MESH", obj.mode == "EDIT"])
    
    
    
        def execute(self, context):
            """Moves Pivot Point centroid of Selected Geometry.
    
    
            Note:
                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":
    
                error_message = f"{PDT_ERR_EDIT_MODE} {obj.mode})"
                self.report({"ERROR"}, error_message)
    
                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"}
    
    
            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"
    
        bl_options = {"REGISTER", "UNDO"}
    
    
        @classmethod
        def poll(cls, context):
    
            """Check Object Status.
    
            Args:
                context: Blender bpy.context instance.
    
            Returns:
                Nothing.
            """
    
    
            obj = context.object
            if obj is None:
    
            return all([bool(obj), obj.type == "MESH"])
    
    
        def execute(self, context):
            """Moves Pivot Point to Object Origin.
    
    
            Note:
                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"}
    
            old_cursor_loc = scene.cursor.location.copy()
    
            obj_loc = obj.matrix_world.decompose()[0]
            pg.pivot_loc = obj_loc
    
            scene.cursor.location = old_cursor_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?"
    
        bl_options = {"REGISTER", "UNDO"}
    
    
        @classmethod
        def poll(cls, context):
    
            """Check Object Status.
    
            Args:
                context: Blender bpy.context instance.
    
            Returns:
                Nothing.
            """
    
    
            obj = context.object
            if obj is None:
    
            return all([bool(obj), obj.type == "MESH"])
    
    
        def execute(self, context):
            """Writes Pivot Point Location to Object's Custom Properties.
    
    
            Note:
                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"
    
        bl_options = {"REGISTER", "UNDO"}
    
    
        @classmethod
        def poll(cls, context):
    
            """Check Object Status.
    
            Args:
                context: Blender bpy.context instance.
    
            Returns:
                Nothing.
            """
    
    
            obj = context.object
            if obj is None:
    
            return all([bool(obj), obj.type == "MESH"])
    
    
        def execute(self, context):
            """Reads Pivot Point Location from Object's Custom Properties.
    
    
            Note:
                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"}
    
    
            self.report({"ERROR"}, PDT_ERR_NOPPLOC)
            return {"FINISHED"}