Rework: Origins Pie #6
@ -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,
|
||||
)
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user