Rework: Origins Pie #6

Merged
Demeter Dzadik merged 2 commits from origins-pie-rework into main 2024-08-21 02:31:22 +02:00

View File

@ -4,319 +4,173 @@
import bpy
from bpy.types import (
Menu,
Operator,
)
from bpy.types import Menu, Operator
from bpy.props import StringProperty
from .hotkeys import register_hotkey
# Pivot to selection
class PIE_OT_PivotToSelection(Operator):
bl_idname = "object.pivot2selection"
bl_label = "Pivot To Selection"
bl_description = "Pivot Point To Selection"
class PIE_OT_set_origin_to_selection(Operator):
bl_idname = "object.origin_set_to_selection"
bl_label = "Origin To Selection"
bl_description = "Snap the origin to the selection"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return context.active_object is not None
def execute(self, context):
org_mode = 'OBJECT'
if context.active_object:
org_mode = context.active_object.mode
saved_location = context.scene.cursor.location.copy()
bpy.ops.view3d.snap_cursor_to_selected()
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
bpy.ops.object.mode_set(mode=org_mode)
context.scene.cursor.location = saved_location
self.report({'INFO'}, "Snapped the origin point to the selection.")
return {'FINISHED'}
# Pivot to Bottom
def origin_to_bottom(ob):
if ob.type != 'MESH':
return
init = 0
for x in ob.data.vertices:
if init == 0:
a = x.co.z
init = 1
elif x.co.z < a:
a = x.co.z
for x in ob.data.vertices:
x.co.z -= a
ob.location.z += a
class PIE_OT_PivotBottom(Operator):
bl_idname = "object.pivotobottom"
bl_label = "Pivot To Bottom"
bl_description = (
"Set the Pivot Point To Lowest Point\n"
"Needs an Active Object of the Mesh type"
)
class PIE_OT_set_origin_to_bottom(Operator):
bl_idname = "object.origin_set_to_bottom"
bl_label = "Origin To Bottom"
bl_description = "Set the Object Origin to the lowest point of each selected object"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
obj = context.active_object
return obj is not None and obj.type == "MESH"
def execute(self, context):
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
for ob in context.selected_objects:
origin_to_bottom(ob)
return {'FINISHED'}
# Pivot to Bottom
class PIE_OT_PivotBottom_edit(Operator):
bl_idname = "object.pivotobottom_edit"
bl_label = "Pivot To Bottom"
bl_description = (
"Set the Pivot Point To Lowest Point\n"
"Needs an Active Object of the Mesh type"
)
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
obj = context.active_object
return obj is not None and obj.type == "MESH"
def execute(self, context):
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
for ob in context.selected_objects:
origin_to_bottom(ob)
bpy.ops.object.mode_set(mode='EDIT')
return {'FINISHED'}
# Pivot to Cursor Edit Mode
class PIE_OT_PivotToCursor_edit(Operator):
bl_idname = "object.pivot2cursor_edit"
bl_label = "Pivot To Cursor"
bl_description = "Pivot Point To Cursor"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return context.active_object is not None
def execute(self, context):
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
bpy.ops.object.mode_set(mode='EDIT')
return {'FINISHED'}
# Origin to Center of Mass Edit Mode
class PIE_OT_OriginToMass_edit(Operator):
bl_idname = "object.origintomass_edit"
bl_label = "Origin"
bl_description = "Origin to Center of Mass"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return context.active_object is not None
def execute(self, context):
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center='MEDIAN')
bpy.ops.object.mode_set(mode='EDIT')
return {'FINISHED'}
# Origin to Geometry Edit Mode
class PIE_OT_OriginToGeometry_edit(Operator):
bl_idname = "object.origintogeometry_edit"
bl_label = "Origin to Geometry"
bl_description = "Origin to Geometry"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return context.active_object is not None
def execute(self, context):
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='MEDIAN')
bpy.ops.object.mode_set(mode='EDIT')
return {'FINISHED'}
# Origin to Geometry Edit Mode
class PIE_OT_GeometryToOrigin_edit(Operator):
bl_idname = "object.geometrytoorigin_edit"
bl_label = "Geometry to Origin"
bl_description = "Geometry to Origin"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return context.active_object is not None
def execute(self, context):
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN', center='MEDIAN')
bpy.ops.object.mode_set(mode='EDIT')
return {'FINISHED'}
# Origin To Selected Edit Mode #
def vfeOrigin_pie(context):
try:
cursorPositionX = context.scene.cursor.location[0]
cursorPositionY = context.scene.cursor.location[1]
cursorPositionZ = context.scene.cursor.location[2]
bpy.ops.view3d.snap_cursor_to_selected()
bpy.ops.object.mode_set()
bpy.ops.object.origin_set(type='ORIGIN_CURSOR', center='MEDIAN')
bpy.ops.object.mode_set(mode='EDIT')
context.scene.cursor.location[0] = cursorPositionX
context.scene.cursor.location[1] = cursorPositionY
context.scene.cursor.location[2] = cursorPositionZ
if not any([ob.type in {'MESH', 'ARMATURE'} for ob in context.selected_objects]):
cls.poll_message_set("No mesh or armature objects selected.")
return False
return True
except:
return False
class PIE_OT_SetOriginToSelected_edit(Operator):
bl_idname = "object.setorigintoselected_edit"
bl_label = "Set Origin to Selected"
bl_description = "Set Origin to Selected"
@classmethod
def poll(cls, context):
return context.area.type == "VIEW_3D" and context.active_object is not None
def execute(self, context):
check = vfeOrigin_pie(context)
if not check:
self.report({"ERROR"}, "Set Origin to Selected could not be performed")
return {'CANCELLED'}
org_active_obj = context.active_object
counter = 0
for obj in context.selected_objects:
counter += int(self.origin_to_bottom(context, obj))
context.view_layer.objects.active = org_active_obj
self.report({'INFO'}, f"Moved the origins of {counter} objects to their lowest point.")
return {'FINISHED'}
@staticmethod
def origin_to_bottom(context, obj) -> bool:
if obj.type not in {'MESH', 'ARMATURE'}:
return False
org_mode = obj.mode
try:
if obj.type == 'MESH':
bpy.ops.object.mode_set(mode='OBJECT')
min_z = min([v.co.z for v in obj.data.vertices])
elif obj.type == 'ARMATURE':
context.view_layer.objects.active = obj
bpy.ops.object.mode_set(mode='EDIT')
min_z = min([min([bone.head.z, bone.tail.z]) for bone in obj.data.edit_bones])
else:
return False
except ValueError:
# min([]) would result in this error, so if the object is empty.
return False
if obj.type == 'MESH':
for vert in obj.data.vertices:
vert.co.z -= min_z
elif obj.type == 'ARMATURE':
for bone in obj.data.edit_bones:
bone.head.z -= min_z
bone.tail.z -= min_z
obj.location.z += min_z
bpy.ops.object.mode_set(mode=org_mode)
return True
class PIE_OT_set_origin_any_mode(Operator):
bl_idname = "object.origin_set_any_mode"
bl_label = "Set Origin"
bl_description = "Set the Object Origin"
bl_options = {'REGISTER', 'UNDO'}
type: StringProperty()
messages = {
'GEOMETRY_ORIGIN' : "Snapped the geometry to the object's origin.",
'ORIGIN_CURSOR' : "Snapped the object's origin to the 3D cursor.",
'ORIGIN_CENTER_OF_MASS': "Snapped the origin to the geometry's center of mass.",
'ORIGIN_CENTER_OF_VOLUME': "Snapped the origin to the geometry's center of volume.",
'ORIGIN_GEOMETRY': "Snapped the origin to the geometry.",
}
@classmethod
def description(cls, context, params):
return cls.messages[params.type].replace("Snapped", "Snap")[:-1]
def execute(self, context):
org_mode = 'OBJECT'
if context.active_object:
org_mode = context.active_object.mode
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.origin_set(type=self.type)
bpy.ops.object.mode_set(mode=org_mode)
self.report({'INFO'}, self.messages[self.type])
return {'FINISHED'}
# Pie Origin/Pivot - Shift + S
class PIE_MT_OriginPivot(Menu):
bl_idname = "ORIGIN_MT_pivotmenu"
bl_label = "Origin Pie"
def draw(self, context):
layout = self.layout
obj = context.object
pie = layout.menu_pie()
if obj and obj.type == 'MESH' and obj.mode in {'OBJECT'}:
# 4 - LEFT
pie.operator(
"object.origin_set", text="Origin to Center of Mass", icon='NONE'
).type = 'ORIGIN_CENTER_OF_MASS'
# 6 - RIGHT
pie.operator(
"object.origin_set", text="Origin to Cursor", icon='PIVOT_CURSOR'
).type = 'ORIGIN_CURSOR'
# 2 - BOTTOM
pie.operator(
"object.pivotobottom", text="Origin to Bottom", icon='TRIA_DOWN'
)
# 8 - TOP
pie.operator(
"object.pivot2selection",
text="Origin To Selection",
icon='SNAP_INCREMENT',
)
# 7 - TOP - LEFT
pie.operator(
"object.origin_set", text="Geometry To Origin", icon='NONE'
).type = 'GEOMETRY_ORIGIN'
# 9 - TOP - RIGHT
pie.operator(
"object.origin_set", text="Origin To Geometry", icon='NONE'
).type = 'ORIGIN_GEOMETRY'
pie = self.layout.menu_pie()
elif obj and obj.type == 'MESH' and obj.mode in {'EDIT'}:
# 4 - LEFT
pie.operator(
"object.origintomass_edit", text="Origin to Center of Mass", icon='NONE'
)
# 6 - RIGHT
pie.operator(
"object.pivot2cursor_edit", text="Origin to Cursor", icon='PIVOT_CURSOR'
)
# 2 - BOTTOM
pie.operator(
"object.pivotobottom_edit", text="Origin to Bottom", icon='TRIA_DOWN'
)
# 8 - TOP
pie.operator(
"object.setorigintoselected_edit",
text="Origin To Selected",
icon='SNAP_INCREMENT',
)
# 7 - TOP - LEFT
pie.operator(
"object.geometrytoorigin_edit", text="Geometry To Origin", icon='NONE'
)
# 9 - TOP - RIGHT
pie.operator(
"object.origintogeometry_edit", text="Origin To Geometry", icon='NONE'
)
else:
# 4 - LEFT
pie.operator(
"object.origin_set", text="Origin to Center of Mass", icon='NONE'
).type = 'ORIGIN_CENTER_OF_MASS'
# 6 - RIGHT
pie.operator(
"object.origin_set", text="Origin To 3D Cursor", icon='PIVOT_CURSOR'
).type = 'ORIGIN_CURSOR'
# 2 - BOTTOM
pie.operator(
"object.pivot2selection",
text="Origin To Selection",
icon='SNAP_INCREMENT',
)
# 8 - TOP
pie.operator(
"object.origin_set", text="Origin To Geometry", icon='NONE'
).type = 'ORIGIN_GEOMETRY'
# 7 - TOP - LEFT
pie.operator(
"object.origin_set", text="Geometry To Origin", icon='NONE'
).type = 'GEOMETRY_ORIGIN'
# 4 - LEFT
pie.operator(
"object.origin_set_any_mode", text="Geometry -> Origin", icon='TRANSFORM_ORIGINS'
).type = 'GEOMETRY_ORIGIN'
# 6 - RIGHT
pie.operator(
"object.origin_set_any_mode", text="Origin -> Geometry", icon='SNAP_PEEL_OBJECT'
).type = 'ORIGIN_GEOMETRY'
# 2 - BOTTOM
pie.operator(
"object.origin_set_to_bottom", text="Origin -> Bottom", icon='TRIA_DOWN'
)
# 8 - TOP
pie.operator(
"object.origin_set_to_selection",
text="Origin -> Selection",
icon='RESTRICT_SELECT_OFF',
)
# 7 - TOP - LEFT
pie.operator(
"object.origin_set_any_mode", text="Origin -> Cursor", icon='PIVOT_CURSOR'
).type = 'ORIGIN_CURSOR'
# 9 - TOP - RIGHT
pie.operator(
"object.origin_set_any_mode", text="Origin -> Mass", icon='SNAP_FACE_CENTER'
).type = 'ORIGIN_CENTER_OF_MASS'
# 1 - BOTTOM - LEFT
pie.separator()
# 3 - BOTTOM - RIGHT
pie.operator(
"object.origin_set_any_mode", text="Origin -> Volume", icon='SNAP_FACE_CENTER'
).type = 'ORIGIN_CENTER_OF_MASS'
registry = (
PIE_MT_OriginPivot,
PIE_OT_PivotToSelection,
PIE_OT_PivotBottom,
PIE_OT_PivotToCursor_edit,
PIE_OT_OriginToMass_edit,
PIE_OT_PivotBottom_edit,
PIE_OT_OriginToGeometry_edit,
PIE_OT_GeometryToOrigin_edit,
PIE_OT_SetOriginToSelected_edit,
PIE_OT_set_origin_to_selection,
PIE_OT_set_origin_to_bottom,
PIE_OT_set_origin_any_mode,
)