1 # SPDX-FileCopyrightText: 2016-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
6 "name": "Hotkey: 'Ctrl Alt X'",
7 "description": "Origin Snap/Place Menu",
8 "author": "pitiwazou, meta-androcto",
10 "blender": (2, 80, 0),
11 "location": "3D View",
14 "category": "Origin Pie"
18 from bpy
.types
import (
25 class PIE_OT_PivotToSelection(Operator
):
26 bl_idname
= "object.pivot2selection"
27 bl_label
= "Pivot To Selection"
28 bl_description
= "Pivot Point To Selection"
29 bl_options
= {'REGISTER', 'UNDO'}
32 def poll(cls
, context
):
33 return context
.active_object
is not None
35 def execute(self
, context
):
36 saved_location
= context
.scene
.cursor
.location
.copy()
37 bpy
.ops
.view3d
.snap_cursor_to_selected()
38 bpy
.ops
.object.mode_set(mode
='OBJECT')
39 bpy
.ops
.object.origin_set(type='ORIGIN_CURSOR')
40 context
.scene
.cursor
.location
= saved_location
46 def origin_to_bottom(ob
):
51 for x
in ob
.data
.vertices
:
58 for x
in ob
.data
.vertices
:
63 class PIE_OT_PivotBottom(Operator
):
64 bl_idname
= "object.pivotobottom"
65 bl_label
= "Pivot To Bottom"
66 bl_description
= ("Set the Pivot Point To Lowest Point\n"
67 "Needs an Active Object of the Mesh type")
68 bl_options
= {'REGISTER', 'UNDO'}
71 def poll(cls
, context
):
72 obj
= context
.active_object
73 return obj
is not None and obj
.type == "MESH"
75 def execute(self
, context
):
76 bpy
.ops
.object.transform_apply(location
=True, rotation
=True, scale
=True)
77 bpy
.ops
.object.origin_set(type='ORIGIN_GEOMETRY')
79 for ob
in context
.selected_objects
:
86 class PIE_OT_PivotBottom_edit(Operator
):
87 bl_idname
= "object.pivotobottom_edit"
88 bl_label
= "Pivot To Bottom"
89 bl_description
= ("Set the Pivot Point To Lowest Point\n"
90 "Needs an Active Object of the Mesh type")
91 bl_options
= {'REGISTER', 'UNDO'}
94 def poll(cls
, context
):
95 obj
= context
.active_object
96 return obj
is not None and obj
.type == "MESH"
98 def execute(self
, context
):
99 bpy
.ops
.object.mode_set(mode
='OBJECT')
100 bpy
.ops
.object.transform_apply(location
=True, rotation
=True, scale
=True)
101 bpy
.ops
.object.origin_set(type='ORIGIN_GEOMETRY')
103 for ob
in context
.selected_objects
:
106 bpy
.ops
.object.mode_set(mode
='EDIT')
111 # Pivot to Cursor Edit Mode
112 class PIE_OT_PivotToCursor_edit(Operator
):
113 bl_idname
= "object.pivot2cursor_edit"
114 bl_label
= "Pivot To Cursor"
115 bl_description
= "Pivot Point To Cursor"
116 bl_options
= {'REGISTER', 'UNDO'}
119 def poll(cls
, context
):
120 return context
.active_object
is not None
122 def execute(self
, context
):
123 bpy
.ops
.object.mode_set(mode
='OBJECT')
124 bpy
.ops
.object.origin_set(type='ORIGIN_CURSOR')
125 bpy
.ops
.object.mode_set(mode
='EDIT')
130 # Origin to Center of Mass Edit Mode
131 class PIE_OT_OriginToMass_edit(Operator
):
132 bl_idname
= "object.origintomass_edit"
134 bl_description
= "Origin to Center of Mass"
135 bl_options
= {'REGISTER', 'UNDO'}
138 def poll(cls
, context
):
139 return context
.active_object
is not None
141 def execute(self
, context
):
142 bpy
.ops
.object.mode_set(mode
='OBJECT')
143 bpy
.ops
.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center
='MEDIAN')
144 bpy
.ops
.object.mode_set(mode
='EDIT')
149 # Origin to Geometry Edit Mode
150 class PIE_OT_OriginToGeometry_edit(Operator
):
151 bl_idname
= "object.origintogeometry_edit"
152 bl_label
= "Origin to Geometry"
153 bl_description
= "Origin to Geometry"
154 bl_options
= {'REGISTER', 'UNDO'}
157 def poll(cls
, context
):
158 return context
.active_object
is not None
160 def execute(self
, context
):
161 bpy
.ops
.object.mode_set(mode
='OBJECT')
162 bpy
.ops
.object.origin_set(type='ORIGIN_GEOMETRY', center
='MEDIAN')
163 bpy
.ops
.object.mode_set(mode
='EDIT')
168 # Origin to Geometry Edit Mode
169 class PIE_OT_GeometryToOrigin_edit(Operator
):
170 bl_idname
= "object.geometrytoorigin_edit"
171 bl_label
= "Geometry to Origin"
172 bl_description
= "Geometry to Origin"
173 bl_options
= {'REGISTER', 'UNDO'}
176 def poll(cls
, context
):
177 return context
.active_object
is not None
179 def execute(self
, context
):
180 bpy
.ops
.object.mode_set(mode
='OBJECT')
181 bpy
.ops
.object.origin_set(type='GEOMETRY_ORIGIN', center
='MEDIAN')
182 bpy
.ops
.object.mode_set(mode
='EDIT')
187 # Origin To Selected Edit Mode #
188 def vfeOrigin_pie(context
):
190 cursorPositionX
= context
.scene
.cursor
.location
[0]
191 cursorPositionY
= context
.scene
.cursor
.location
[1]
192 cursorPositionZ
= context
.scene
.cursor
.location
[2]
193 bpy
.ops
.view3d
.snap_cursor_to_selected()
194 bpy
.ops
.object.mode_set()
195 bpy
.ops
.object.origin_set(type='ORIGIN_CURSOR', center
='MEDIAN')
196 bpy
.ops
.object.mode_set(mode
='EDIT')
197 context
.scene
.cursor
.location
[0] = cursorPositionX
198 context
.scene
.cursor
.location
[1] = cursorPositionY
199 context
.scene
.cursor
.location
[2] = cursorPositionZ
205 class PIE_OT_SetOriginToSelected_edit(Operator
):
206 bl_idname
= "object.setorigintoselected_edit"
207 bl_label
= "Set Origin to Selected"
208 bl_description
= "Set Origin to Selected"
211 def poll(cls
, context
):
212 return (context
.area
.type == "VIEW_3D" and context
.active_object
is not None)
214 def execute(self
, context
):
215 check
= vfeOrigin_pie(context
)
217 self
.report({"ERROR"}, "Set Origin to Selected could not be performed")
223 # Pie Origin/Pivot - Shift + S
224 class PIE_MT_OriginPivot(Menu
):
225 bl_idname
= "ORIGIN_MT_pivotmenu"
226 bl_label
= "Origin Menu"
228 def draw(self
, context
):
231 pie
= layout
.menu_pie()
232 if obj
and obj
.type == 'MESH' and obj
.mode
in {'OBJECT'}:
234 pie
.operator("object.origin_set", text
="Origin to Center of Mass",
235 icon
='NONE').type = 'ORIGIN_CENTER_OF_MASS'
237 pie
.operator("object.origin_set", text
="Origin to Cursor",
238 icon
='PIVOT_CURSOR').type = 'ORIGIN_CURSOR'
240 pie
.operator("object.pivotobottom", text
="Origin to Bottom",
243 pie
.operator("object.pivot2selection", text
="Origin To Selection",
244 icon
='SNAP_INCREMENT')
246 pie
.operator("object.origin_set", text
="Geometry To Origin",
247 icon
='NONE').type = 'GEOMETRY_ORIGIN'
249 pie
.operator("object.origin_set", text
="Origin To Geometry",
250 icon
='NONE').type = 'ORIGIN_GEOMETRY'
252 elif obj
and obj
.type == 'MESH' and obj
.mode
in {'EDIT'}:
254 pie
.operator("object.origintomass_edit", text
="Origin to Center of Mass",
257 pie
.operator("object.pivot2cursor_edit", text
="Origin to Cursor",
260 pie
.operator("object.pivotobottom_edit", text
="Origin to Bottom",
263 pie
.operator("object.setorigintoselected_edit", text
="Origin To Selected",
264 icon
='SNAP_INCREMENT')
266 pie
.operator("object.geometrytoorigin_edit", text
="Geometry To Origin",
269 pie
.operator("object.origintogeometry_edit", text
="Origin To Geometry",
274 pie
.operator("object.origin_set", text
="Origin to Center of Mass",
275 icon
='NONE').type = 'ORIGIN_CENTER_OF_MASS'
277 pie
.operator("object.origin_set", text
="Origin To 3D Cursor",
278 icon
='PIVOT_CURSOR').type = 'ORIGIN_CURSOR'
280 pie
.operator("object.pivot2selection", text
="Origin To Selection",
281 icon
='SNAP_INCREMENT')
283 pie
.operator("object.origin_set", text
="Origin To Geometry",
284 icon
='NONE').type = 'ORIGIN_GEOMETRY'
286 pie
.operator("object.origin_set", text
="Geometry To Origin",
287 icon
='NONE').type = 'GEOMETRY_ORIGIN'
292 PIE_OT_PivotToSelection
,
294 PIE_OT_PivotToCursor_edit
,
295 PIE_OT_OriginToMass_edit
,
296 PIE_OT_PivotBottom_edit
,
297 PIE_OT_OriginToGeometry_edit
,
298 PIE_OT_GeometryToOrigin_edit
,
299 PIE_OT_SetOriginToSelected_edit
307 bpy
.utils
.register_class(cls
)
309 wm
= bpy
.context
.window_manager
310 if wm
.keyconfigs
.addon
:
312 km
= wm
.keyconfigs
.addon
.keymaps
.new(name
='3D View Generic', space_type
='VIEW_3D')
313 kmi
= km
.keymap_items
.new('wm.call_menu_pie', 'X', 'PRESS', ctrl
=True, alt
=True)
314 kmi
.properties
.name
= "ORIGIN_MT_pivotmenu"
315 addon_keymaps
.append((km
, kmi
))
320 bpy
.utils
.unregister_class(cls
)
322 wm
= bpy
.context
.window_manager
323 kc
= wm
.keyconfigs
.addon
325 for km
, kmi
in addon_keymaps
:
326 km
.keymap_items
.remove(kmi
)
327 addon_keymaps
.clear()
330 if __name__
== "__main__":