-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcopy_pivot.py
More file actions
106 lines (81 loc) · 3.25 KB
/
copy_pivot.py
File metadata and controls
106 lines (81 loc) · 3.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
"""
Copy Pivot - Copies the active object's origin to all other selected objects.
The objects themselves do not move — only their pivot point (origin) is
repositioned to exactly match the active object's origin. Useful when a group
of objects all need the same pivot reference point.
"""
import bpy
from typing import Optional
def copy_pivot(context) -> Optional[str]:
"""Copy the active object's origin to all other selected mesh objects.
Uses the 3D cursor as an intermediary (saves and restores its state).
Args:
context: Current Blender context.
Returns:
Error string on failure, None on success.
"""
template = context.active_object
if not template:
return "No active object — the active object's origin is used as the source"
targets = [o for o in context.selected_objects
if o != template and o.type == 'MESH']
if not targets:
return (
"No other mesh objects selected — "
"select the objects that should receive the pivot copy"
)
cursor = context.scene.cursor
saved_loc = cursor.location.copy()
saved_rot = cursor.rotation_euler.copy()
active_obj = context.view_layer.objects.active
sel_states = {o: o.select_get() for o in bpy.data.objects}
try:
# Temporarily move cursor to the template's world origin
cursor.location = template.matrix_world.translation.copy()
cursor.rotation_euler = template.rotation_euler.copy()
bpy.ops.object.select_all(action='DESELECT')
for obj in targets:
obj.select_set(True)
context.view_layer.objects.active = obj
bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
obj.select_set(False)
finally:
cursor.location = saved_loc
cursor.rotation_euler = saved_rot
for o, was_sel in sel_states.items():
o.select_set(was_sel)
if active_obj:
context.view_layer.objects.active = active_obj
return None
class OBJECT_OT_copy_pivot(bpy.types.Operator):
"""Copy the active object's origin to all other selected mesh objects"""
bl_idname = "object.copy_pivot"
bl_label = "Copy Pivot from Active"
bl_description = (
"Reposition the origin of every other selected mesh object to exactly "
"match the active object's pivot point. The objects themselves do not move"
)
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return (
context.mode == 'OBJECT' and
context.active_object is not None and
len(context.selected_objects) >= 2
)
def execute(self, context):
error = copy_pivot(context)
if error:
self.report({'WARNING'}, error)
return {'CANCELLED'}
return {'FINISHED'}
# ─── Registration ─────────────────────────────────────────────────────────────
classes = (
OBJECT_OT_copy_pivot,
)
def register():
for cls in classes:
bpy.utils.register_class(cls)
def unregister():
for cls in reversed(classes):
bpy.utils.unregister_class(cls)