1 # SPDX-FileCopyrightText: 2009-2010 gabhead, Lell, Anfeo.
3 # SPDX-License-Identifier: GPL-2.0-or-later
7 "author": "gabhead, Lell, Anfeo",
10 "location": "View3D > Sidebar > Item Tab",
11 "description": "Align Selected Objects to Active Object",
13 "doc_url": "{BLENDER_MANUAL_URL}/addons/object/align_tools.html",
18 from bpy
.types
import (
23 from bpy
.props
import (
29 from mathutils
import (
39 for i
in bpy
.context
.selected_objects
:
40 i
.matrix_world
.translation
= bpy
.context
.active_object
.matrix_world
.translation
.copy()
41 i
.rotation_euler
= bpy
.context
.active_object
.rotation_euler
46 for i
in bpy
.context
.selected_objects
:
47 i
.matrix_world
.translation
= bpy
.context
.active_object
.matrix_world
.translation
.copy()
51 for i
in bpy
.context
.selected_objects
:
52 i
.matrix_world
.translation
.x
= bpy
.context
.active_object
.matrix_world
.translation
.x
56 for i
in bpy
.context
.selected_objects
:
57 i
.matrix_world
.translation
.y
= bpy
.context
.active_object
.matrix_world
.translation
.y
61 for i
in bpy
.context
.selected_objects
:
62 i
.matrix_world
.translation
.z
= bpy
.context
.active_object
.matrix_world
.translation
.z
67 for i
in bpy
.context
.selected_objects
:
68 i
.rotation_euler
= bpy
.context
.active_object
.rotation_euler
72 for i
in bpy
.context
.selected_objects
:
73 i
.rotation_euler
.x
= bpy
.context
.active_object
.rotation_euler
.x
77 for i
in bpy
.context
.selected_objects
:
78 i
.rotation_euler
.y
= bpy
.context
.active_object
.rotation_euler
.y
82 for i
in bpy
.context
.selected_objects
:
83 i
.rotation_euler
.z
= bpy
.context
.active_object
.rotation_euler
.z
87 def ScaleAll(context
):
88 for i
in bpy
.context
.selected_objects
:
89 i
.scale
= bpy
.context
.active_object
.scale
93 for i
in bpy
.context
.selected_objects
:
94 i
.scale
.x
= bpy
.context
.active_object
.scale
.x
98 for i
in bpy
.context
.selected_objects
:
99 i
.scale
.y
= bpy
.context
.active_object
.scale
.y
103 for i
in bpy
.context
.selected_objects
:
104 i
.scale
.z
= bpy
.context
.active_object
.scale
.z
107 # Advanced Align Defs #
109 # subject to object 0, 1 and 2 to pivot for cursor
110 def align_function(subject
, active_too
, consistent
, self_or_active
, loc_x
, loc_y
, loc_z
, ref1
, ref2
, loc_offset
,
111 rot_x
, rot_y
, rot_z
, rot_offset
, scale_x
, scale_y
, scale_z
, scale_offset
,
112 fit_x
, fit_y
, fit_z
):
114 sel_obj
= bpy
.context
.selected_objects
115 act_obj
= bpy
.context
.active_object
122 def get_reference_points(obj
, space
):
126 # let's get all the points coordinates
127 if space
== "global":
129 obj_mtx
= obj
.matrix_world
130 if obj
.type == 'MESH' and len(me
.vertices
) > 0:
132 for p
in me
.vertices
:
133 co_list
.append((obj_mtx
@ p
.co
))
135 elif obj
.type == 'SURFACE' and len(me
.splines
) > 0:
139 co_list
.append((obj_mtx
@ p
.co
))
140 elif obj
.type == 'FONT' and len(me
.splines
) > 0:
143 for p
in s
.bezier_points
:
144 co_list
.append((obj_mtx
@ p
.co
))
146 elif space
== "local":
148 if obj
.type == 'MESH' and len(me
.vertices
) > 0:
150 for p
in me
.vertices
:
153 elif obj
.type == 'SURFACE' and len(me
.splines
) > 0:
158 elif obj
.type == 'FONT' and len(obj
.data
.splines
) > 0:
161 for p
in s
.bezier_points
:
164 # if a valid point found
165 # proceed to calculate the extremes
167 max_x
= co_list
[0][0]
168 min_x
= co_list
[0][0]
169 max_y
= co_list
[0][1]
170 min_y
= co_list
[0][1]
171 max_z
= co_list
[0][2]
172 min_z
= co_list
[0][2]
175 # the strings of the list compared with the smaller and more found
176 # in order to find the minor and major for each axis
196 # otherwise use the pivot object
197 a
= obj
.matrix_world
.translation
205 center_x
= min_x
+ ((max_x
- min_x
) / 2)
206 center_y
= min_y
+ ((max_y
- min_y
) / 2)
207 center_z
= min_z
+ ((max_z
- min_z
) / 2)
209 reference_points
= [min_x
, center_x
, max_x
, min_y
, center_y
, max_y
, min_z
, center_z
, max_z
]
210 return reference_points
212 def get_sel_ref(ref_co
, sel_obj
): # I look for the selection end points
214 sel_min
= ref_co
.copy()
215 sel_max
= ref_co
.copy()
218 if obj
!= act_obj
or (active_too
and obj
== act_obj
):
220 ref_points
= get_reference_points(obj
, "global")
221 ref_min
= Vector([ref_points
[0], ref_points
[3], ref_points
[6]])
222 ref_max
= Vector([ref_points
[2], ref_points
[5], ref_points
[8]])
224 if ref_min
[0] < sel_min
[0]:
225 sel_min
[0] = ref_min
[0]
226 if ref_max
[0] > sel_max
[0]:
227 sel_max
[0] = ref_max
[0]
228 if ref_min
[1] < sel_min
[1]:
229 sel_min
[1] = ref_min
[1]
230 if ref_max
[1] > sel_max
[1]:
231 sel_max
[1] = ref_max
[1]
232 if ref_min
[2] < sel_min
[2]:
233 sel_min
[2] = ref_min
[2]
234 if ref_max
[2] > sel_max
[2]:
235 sel_max
[2] = ref_max
[2]
237 return sel_min
, sel_max
239 def find_ref2_co(act_obj
):
240 # It contains the coordinates of the reference point for the positioning
242 ref_points
= get_reference_points(act_obj
, "global")
243 ref2_co
= [ref_points
[0], ref_points
[3], ref_points
[6]]
244 ref2_co
= Vector(ref2_co
)
246 ref_points
= get_reference_points(act_obj
, "global")
247 ref2_co
= [ref_points
[1], ref_points
[4], ref_points
[7]]
248 ref2_co
= Vector(ref2_co
)
250 ref2_co
= act_obj
.location
251 ref2_co
= Vector(ref2_co
)
253 ref_points
= get_reference_points(act_obj
, "global")
254 ref2_co
= [ref_points
[2], ref_points
[5], ref_points
[8]]
255 ref2_co
= Vector(ref2_co
)
257 ref2_co
= bpy
.context
.scene
.cursor
.location
261 def find_new_coord(obj
):
263 ref_points
= get_reference_points(obj
, "global")
267 min_x
= ref_points
[0]
268 new_x
= ref2_co
[0] + (obj
.location
[0] - min_x
) + loc_offset
[0]
270 center_x
= ref_points
[1]
271 new_x
= ref2_co
[0] + (obj
.location
[0] - center_x
) + loc_offset
[0]
273 new_x
= ref2_co
[0] + loc_offset
[0]
275 max_x
= ref_points
[2]
276 new_x
= ref2_co
[0] - (max_x
- obj
.location
[0]) + loc_offset
[0]
277 obj
.matrix_world
.translation
[0] = new_x
280 min_y
= ref_points
[3]
281 new_y
= ref2_co
[1] + (obj
.location
[1] - min_y
) + loc_offset
[1]
283 center_y
= ref_points
[4]
284 new_y
= ref2_co
[1] + (obj
.location
[1] - center_y
) + loc_offset
[1]
286 new_y
= ref2_co
[1] + loc_offset
[1]
288 max_y
= ref_points
[5]
289 new_y
= ref2_co
[1] - (max_y
- obj
.location
[1]) + loc_offset
[1]
290 obj
.matrix_world
.translation
[1] = new_y
293 min_z
= ref_points
[6]
294 new_z
= ref2_co
[2] + (obj
.location
[2] - min_z
) + loc_offset
[2]
296 center_z
= ref_points
[7]
297 new_z
= ref2_co
[2] + (obj
.location
[2] - center_z
) + loc_offset
[2]
299 new_z
= ref2_co
[2] + loc_offset
[2]
301 max_z
= ref_points
[8]
302 new_z
= ref2_co
[2] - (max_z
- obj
.location
[2]) + loc_offset
[2]
303 obj
.matrix_world
.translation
[2] = new_z
305 def find_new_rotation(obj
):
307 obj
.rotation_euler
[0] = act_obj
.rotation_euler
[0] + rot_offset
[0]
309 obj
.rotation_euler
[1] = act_obj
.rotation_euler
[1] + rot_offset
[1]
311 obj
.rotation_euler
[2] = act_obj
.rotation_euler
[2] + rot_offset
[2]
313 def find_new_scale(obj
):
315 obj
.scale
[0] = act_obj
.scale
[0] + scale_offset
[0]
317 obj
.scale
[1] = act_obj
.scale
[1] + scale_offset
[1]
319 obj
.scale
[2] = act_obj
.scale
[2] + scale_offset
[2]
321 def find_new_dimensions(obj
, ref_dim
):
322 ref_points
= get_reference_points(obj
, "local")
324 dim
= ref_points
[2] - ref_points
[0]
325 obj
.scale
[0] = (ref_dim
[0] / dim
) * act_obj
.scale
[0]
327 dim
= ref_points
[5] - ref_points
[3]
328 obj
.scale
[1] = (ref_dim
[1] / dim
) * act_obj
.scale
[1]
330 dim
= ref_points
[8] - ref_points
[6]
331 obj
.scale
[2] = (ref_dim
[2] / dim
) * act_obj
.scale
[2]
335 vec_ref2_co
= Vector(ref2_co
)
336 offset
= vec_ref2_co
- obj
.location
337 offset_x
= [offset
[0] + loc_offset
[0], 0, 0]
338 offset_y
= [0, offset
[1] + loc_offset
[1], 0]
339 offset_z
= [0, 0, offset
[2] + loc_offset
[2]]
342 obj_mtx
= obj
.matrix_world
.copy()
343 # What's the displacement vector for the pivot?
344 move_pivot
= Vector(vec
)
346 # Move the pivot point (which is the object's location)
350 nm
= obj_mtx
.inverted() @ Matrix
.Translation(-move_pivot
) @ obj_mtx
352 # Transform the mesh now
362 def point_in_selection(act_obj
, sel_obj
):
367 obj_mtx
= o
.matrix_world
368 if o
.type == 'MESH' and len(o
.data
.vertices
) > 0:
369 ref_co
= o
.data
.vertices
[0].co
.copy()
370 ref_co
= obj_mtx
@ ref_co
373 elif o
.type == 'CURVE' and len(o
.data
.splines
) > 0:
374 ref_co
= o
.data
.splines
[0].bezier_point
[0].co
.copy()
375 ref_co
= obj_mtx
@ ref_co
378 elif o
.type == 'SURFACE' and len(o
.data
.splines
) > 0:
379 ref_co
= o
.data
.splines
[0].points
[0].co
.copy()
380 ref_co
= obj_mtx
@ ref_co
383 elif o
.type == 'FONT' and len(o
.data
.splines
) > 0:
384 ref_co
= o
.data
.splines
[0].bezier_points
[0].co
.copy()
385 ref_co
= obj_mtx
@ ref_co
388 # if no object had data, use the position of an object that was not active as an internal
391 ref_co
= ref_ob
.matrix_world
.translation
396 # if act_obj.type == ('MESH' or 'FONT' or 'CURVE' or 'SURFACE'):
397 if act_obj
.type == 'MESH' or act_obj
.type == 'FONT' or act_obj
.type == 'SURFACE':
398 ref2_co
= find_ref2_co(act_obj
)
401 ref2_co
= bpy
.context
.scene
.cursor
.location
403 ref2_co
= act_obj
.matrix_world
.translation
405 # in the case of substantial selection
407 # I am seeking a point that is in the selection space
408 ref_co
= point_in_selection(act_obj
, sel_obj
)
410 sel_min
, sel_max
= get_sel_ref(ref_co
, sel_obj
)
412 sel_center
= sel_min
+ ((sel_max
- sel_min
) / 2)
413 translate
= [0, 0, 0]
415 # calculating how much to move the selection
417 translate
= ref2_co
- sel_min
+ loc_offset
419 translate
= ref2_co
- sel_center
+ loc_offset
421 translate
= ref2_co
- sel_max
+ loc_offset
423 # Move the various objects
426 if obj
!= act_obj
or (active_too
and obj
== act_obj
):
429 obj
.location
[0] += translate
[0]
431 obj
.location
[1] += translate
[1]
433 obj
.location
[2] += translate
[2]
434 else: # not consistent
437 if rot_x
or rot_y
or rot_z
:
438 find_new_rotation(obj
)
440 if fit_x
or fit_y
or fit_z
:
442 ref_points
= get_reference_points(act_obj
, "local")
443 dim
[0] = ref_points
[2] - ref_points
[0]
444 dim
[1] = ref_points
[5] - ref_points
[3]
445 dim
[2] = ref_points
[8] - ref_points
[6]
446 find_new_dimensions(obj
, dim
)
448 if scale_x
or scale_y
or scale_z
:
451 if loc_x
or loc_y
or loc_z
:
452 # print("ehy", ref2_co)
455 if active_too
is True:
456 if loc_x
or loc_y
or loc_z
:
457 find_new_coord(act_obj
)
458 if rot_x
or rot_y
or rot_z
:
459 find_new_rotation(act_obj
)
460 if scale_x
or scale_y
or scale_z
:
461 find_new_scale(act_obj
)
462 # add dimensions if dim offset will be added
465 if self_or_active
== "1":
466 if act_obj
.type == 'MESH':
467 ref2_co
= find_ref2_co(act_obj
)
469 if self_or_active
== "0":
470 ref2_co
= find_ref2_co(obj
)
471 if loc_x
or loc_y
or loc_z
:
472 if obj
!= act_obj
and obj
.type == 'MESH':
475 if active_too
is True:
476 if act_obj
.type == 'MESH':
477 if loc_x
or loc_y
or loc_z
:
478 if self_or_active
== "0":
479 ref2_co
= find_ref2_co(act_obj
)
483 if self_or_active
== "1":
484 if act_obj
.type == 'MESH' or act_obj
.type == 'FONT' or act_obj
.type == 'SURFACE':
485 ref2_co
= find_ref2_co(act_obj
)
486 ref_points
= get_reference_points(act_obj
, "global")
488 ref2_co
= act_obj
.matrix_world
.translation
489 ref_points
= [ref2_co
[0], ref2_co
[0], ref2_co
[0],
490 ref2_co
[1], ref2_co
[1], ref2_co
[1],
491 ref2_co
[2], ref2_co
[2], ref2_co
[2]]
495 bpy
.context
.scene
.cursor
.location
[0] = ref_points
[0] + loc_offset
[0]
497 bpy
.context
.scene
.cursor
.location
[1] = ref_points
[3] + loc_offset
[1]
499 bpy
.context
.scene
.cursor
.location
[2] = ref_points
[6] + loc_offset
[2]
502 bpy
.context
.scene
.cursor
.location
[0] = ref_points
[1] + loc_offset
[0]
504 bpy
.context
.scene
.cursor
.location
[1] = ref_points
[4] + loc_offset
[1]
506 bpy
.context
.scene
.cursor
.location
[2] = ref_points
[7] + loc_offset
[2]
509 bpy
.context
.scene
.cursor
.location
[0] = act_obj
.location
[0] + loc_offset
[0]
511 bpy
.context
.scene
.cursor
.location
[1] = act_obj
.location
[1] + loc_offset
[1]
513 bpy
.context
.scene
.cursor
.location
[2] = act_obj
.location
[2] + loc_offset
[2]
516 bpy
.context
.scene
.cursor
.location
[0] = ref_points
[2] + loc_offset
[0]
518 bpy
.context
.scene
.cursor
.location
[1] = ref_points
[5] + loc_offset
[1]
520 bpy
.context
.scene
.cursor
.location
[2] = ref_points
[8] + loc_offset
[2]
521 elif self_or_active
== "2":
522 ref_co
= point_in_selection(act_obj
, sel_obj
)
524 sel_min
, sel_max
= get_sel_ref(ref_co
, sel_obj
)
525 sel_center
= sel_min
+ ((sel_max
- sel_min
) / 2)
529 bpy
.context
.scene
.cursor
.location
[0] = sel_min
[0] + loc_offset
[0]
531 bpy
.context
.scene
.cursor
.location
[1] = sel_min
[1] + loc_offset
[1]
533 bpy
.context
.scene
.cursor
.location
[2] = sel_min
[2] + loc_offset
[2]
536 bpy
.context
.scene
.cursor
.location
[0] = sel_center
[0] + loc_offset
[0]
538 bpy
.context
.scene
.cursor
.location
[1] = sel_center
[1] + loc_offset
[1]
540 bpy
.context
.scene
.cursor
.location
[2] = sel_center
[2] + loc_offset
[2]
543 bpy
.context
.scene
.cursor
.location
[0] = sel_max
[0] + loc_offset
[0]
545 bpy
.context
.scene
.cursor
.location
[1] = sel_max
[1] + loc_offset
[1]
547 bpy
.context
.scene
.cursor
.location
[2] = sel_max
[2] + loc_offset
[2]
553 class OBJECT_OT_align_tools(Operator
):
554 bl_idname
= "object.align_tools"
555 bl_label
= "Align Operator"
556 bl_description
= "Align Object Tools"
557 bl_options
= {'REGISTER', 'UNDO', 'PRESET'}
559 # property definitions
561 # Object-Pivot-Cursor:
562 subject
: EnumProperty(
563 items
=(("0", "Object", "Align Objects"),
564 ("1", "Pivot", "Align Objects Pivot"),
565 ("2", "Cursor", "Align Cursor To Active")),
567 description
="What will be moved"
570 active_too
: BoolProperty(
573 description
="Move the active object too"
576 advanced
: BoolProperty(
577 name
="Advanced Options",
579 description
="Show advanced options"
581 consistent
: BoolProperty(
582 name
="Consistent Selection",
584 description
="Use consistent selection"
588 name
="Align to X axis",
590 description
="Enable X axis alignment"
593 name
="Align to Y axis",
595 description
="Enable Y axis alignment"
598 name
="Align to Z axis",
600 description
="Enable Z axis alignment"
604 items
=(("3", "Max", "Align the maximum point"),
605 ("1", "Center", "Align the center point"),
606 ("2", "Pivot", "Align the pivot"),
607 ("0", "Min", "Align the minimum point")),
608 name
="Selection reference",
609 description
="Moved objects reference point"
611 # Active Object Option:
613 items
=(("3", "Max", "Align to the maximum point"),
614 ("1", "Center", "Align to the center point"),
615 ("2", "Pivot", "Align to the pivot"),
616 ("0", "Min", "Align to the minimum point"),
617 ("4", "Cursor", "Description")),
618 name
="Active reference",
619 description
="Destination point"
621 self_or_active
: EnumProperty(
622 items
=(("0", "Self", "In relation of itself"),
623 ("1", "Active", "In relation of the active object"),
624 ("2", "Selection", "In relation of the entire selection")),
627 description
="To what the pivot will be aligned"
630 loc_offset
: FloatVectorProperty(
631 name
="Location Offset",
632 description
="Offset for location align position",
633 default
=(0.0, 0.0, 0.0),
634 subtype
='XYZ', size
=3
637 rot_offset
: FloatVectorProperty(
638 name
="Rotation Offset",
639 description
="Offset for rotation alignment",
640 default
=(0.0, 0.0, 0.0),
641 subtype
='EULER', size
=3
644 scale_offset
: FloatVectorProperty(
646 description
="Offset for scale match",
647 default
=(0.0, 0.0, 0.0),
648 subtype
='XYZ', size
=3
650 # Fit Dimension Prop:
652 name
="Fit Dimension to X axis",
657 name
="Fit Dimension to Y axis",
662 name
="Fit Dimension to Z axis",
666 # Apply Fit Dimension:
667 apply_dim
: BoolProperty(
668 name
="Apply Dimension",
674 name
="Align Rotation to X axis",
679 name
="Align Rotation to Y axis",
684 name
="Align Rotation to Z axis",
689 apply_rot
: BoolProperty(
690 name
="Apply Rotation",
695 scale_x
: BoolProperty(
696 name
="Match Scale to X axis",
700 scale_y
: BoolProperty(
701 name
="Match Scale to Y axis",
705 scale_z
: BoolProperty(
706 name
="match Scale to Z axis",
711 apply_scale
: BoolProperty(
717 def draw(self
, context
):
721 row
.label(text
="Active object is: ", icon
='OBJECT_DATA')
723 box
.label(text
=obj
.name
, icon
='EDITMODE_HLT')
724 # Object-Pivot-Cursor:
726 row0
.prop(self
, 'subject', expand
=True)
730 row1
.prop(self
, 'active_too')
731 row1
.prop(self
, 'advanced')
734 row1b
.prop(self
, 'consistent')
737 row2
.label(text
="Align Location:")
741 row3
.prop(self
, "loc_x", text
="X", toggle
=True)
742 row3
.prop(self
, "loc_y", text
="Y", toggle
=True)
743 row3
.prop(self
, "loc_z", text
="Z", toggle
=True)
746 if self
.advanced
is True:
748 # row8.label(text='Location Offset')
750 row9
.prop(self
, 'loc_offset', text
='')
753 if self
.advanced
is True:
754 sel
= bpy
.context
.selected_objects
758 row4
.label(text
="Selected: " + str(sel_obs
) + " Objects", icon
='OBJECT_DATA')
759 if self
.subject
== "1" or self
.subject
== "2":
761 row5b
.prop(self
, 'self_or_active', expand
=True)
764 row5
.prop(self
, 'ref1', expand
=True)
766 # Active Object Options: Number of select objects
767 act
= bpy
.context
.active_object
769 if self
.advanced
is True:
772 row6
.label(text
="Active: " + act
.name
, icon
='OBJECT_DATA')
774 row7
.prop(self
, 'ref2', expand
=True)
776 if self
.subject
== "0":
778 row12
.label(text
='Align Rotation:')
779 row13
= layout
.row(align
=True)
780 row13
.prop(self
, 'rot_x', text
='X', toggle
=True)
781 row13
.prop(self
, 'rot_y', text
='Y', toggle
=True)
782 row13
.prop(self
, 'rot_z', text
='Z', toggle
=True)
783 row13
.prop(self
, 'apply_rot', text
='Apply', toggle
=True)
784 if self
.advanced
is True:
785 row13b
= layout
.row()
786 row13b
.prop(self
, 'rot_offset', text
='')
789 row14
.label(text
='Match Scale:')
790 row15
= layout
.row(align
=True)
791 row15
.prop(self
, 'scale_x', text
='X', toggle
=True)
792 row15
.prop(self
, 'scale_y', text
='Y', toggle
=True)
793 row15
.prop(self
, 'scale_z', text
='Z', toggle
=True)
794 row15
.prop(self
, 'apply_scale', text
='Apply', toggle
=True)
795 if self
.advanced
is True:
796 row15b
= layout
.row()
797 row15b
.prop(self
, 'scale_offset', text
='')
800 row10
.label(text
='Fit Dimensions:')
801 row11
= layout
.row(align
=True)
802 row11
.prop(self
, 'fit_x', text
='X', toggle
=True)
803 row11
.prop(self
, 'fit_y', text
='Y', toggle
=True)
804 row11
.prop(self
, 'fit_z', text
='Z', toggle
=True)
805 row11
.prop(self
, 'apply_dim', text
='Apply', toggle
=True)
807 def execute(self
, context
):
809 self
.subject
, self
.active_too
, self
.consistent
,
810 self
.self_or_active
, self
.loc_x
, self
.loc_y
, self
.loc_z
,
811 self
.ref1
, self
.ref2
, self
.loc_offset
,
812 self
.rot_x
, self
.rot_y
, self
.rot_z
, self
.rot_offset
,
813 self
.scale_x
, self
.scale_y
, self
.scale_z
, self
.scale_offset
,
814 self
.fit_x
, self
.fit_y
, self
.fit_z
820 # Simple Align Classes #
822 # Align All Rotation And Location
823 class OBJECT_OT_AlignOperator(Operator
):
824 bl_idname
= "object.align"
825 bl_label
= "Align Selected To Active"
826 bl_description
= "Align Selected To Active"
827 bl_options
= {'REGISTER', 'UNDO'}
830 def poll(cls
, context
):
831 return context
.active_object
is not None
833 def execute(self
, context
):
839 class OBJECT_OT_AlignLocationOperator(Operator
):
840 bl_idname
= "object.align_location_all"
841 bl_label
= "Align Selected Location To Active"
842 bl_description
= "Align Selected Location To Active"
843 bl_options
= {'REGISTER', 'UNDO'}
846 def poll(cls
, context
):
847 return context
.active_object
is not None
849 def execute(self
, context
):
855 class OBJECT_OT_AlignLocationXOperator(Operator
):
856 bl_idname
= "object.align_location_x"
857 bl_label
= "Align Selected Location X To Active"
858 bl_description
= "Align Selected Location X To Active"
859 bl_options
= {'REGISTER', 'UNDO'}
862 def poll(cls
, context
):
863 return context
.active_object
is not None
865 def execute(self
, context
):
871 class OBJECT_OT_AlignLocationYOperator(Operator
):
872 bl_idname
= "object.align_location_y"
873 bl_label
= "Align Selected Location Y To Active"
874 bl_description
= "Align Selected Location Y To Active"
875 bl_options
= {'REGISTER', 'UNDO'}
878 def poll(cls
, context
):
879 return context
.active_object
is not None
881 def execute(self
, context
):
887 class OBJECT_OT_AlignLocationZOperator(Operator
):
888 bl_idname
= "object.align_location_z"
889 bl_label
= "Align Selected Location Z To Active"
890 bl_description
= "Align Selected Location Z To Active"
891 bl_options
= {'REGISTER', 'UNDO'}
894 def poll(cls
, context
):
895 return context
.active_object
is not None
897 def execute(self
, context
):
903 class OBJECT_OT_AlignRotationOperator(Operator
):
904 bl_idname
= "object.align_rotation_all"
905 bl_label
= "Align Selected Rotation To Active"
906 bl_description
= "Align Selected Rotation To Active"
907 bl_options
= {'REGISTER', 'UNDO'}
910 def poll(cls
, context
):
911 return context
.active_object
is not None
913 def execute(self
, context
):
919 class OBJECT_OT_AlignRotationXOperator(Operator
):
920 bl_idname
= "object.align_rotation_x"
921 bl_label
= "Align Selected Rotation X To Active"
922 bl_description
= "Align Selected Rotation X To Active"
923 bl_options
= {'REGISTER', 'UNDO'}
926 def poll(cls
, context
):
927 return context
.active_object
is not None
929 def execute(self
, context
):
935 class OBJECT_OT_AlignRotationYOperator(Operator
):
936 bl_idname
= "object.align_rotation_y"
937 bl_label
= "Align Selected Rotation Y To Active"
938 bl_description
= "Align Selected Rotation Y To Active"
939 bl_options
= {'REGISTER', 'UNDO'}
942 def poll(cls
, context
):
943 return context
.active_object
is not None
945 def execute(self
, context
):
951 class OBJECT_OT_AlignRotationZOperator(Operator
):
952 bl_idname
= "object.align_rotation_z"
953 bl_label
= "Align Selected Rotation Z To Active"
954 bl_description
= "Align Selected Rotation Z To Active"
955 bl_options
= {'REGISTER', 'UNDO'}
958 def poll(cls
, context
):
959 return context
.active_object
is not None
961 def execute(self
, context
):
967 class OBJECT_OT_AlignScaleOperator(Operator
):
968 bl_idname
= "object.align_objects_scale_all"
969 bl_label
= "Align Selected Scale To Active"
970 bl_description
= "Align Selected Scale To Active"
971 bl_options
= {'REGISTER', 'UNDO'}
974 def poll(cls
, context
):
975 return context
.active_object
is not None
977 def execute(self
, context
):
983 class OBJECT_OT_AlignScaleXOperator(Operator
):
984 bl_idname
= "object.align_objects_scale_x"
985 bl_label
= "Align Selected Scale X To Active"
986 bl_description
= "Align Selected Scale X To Active"
987 bl_options
= {'REGISTER', 'UNDO'}
990 def poll(cls
, context
):
991 return context
.active_object
is not None
993 def execute(self
, context
):
999 class OBJECT_OT_AlignScaleYOperator(Operator
):
1000 bl_idname
= "object.align_objects_scale_y"
1001 bl_label
= "Align Selected Scale Y To Active"
1002 bl_description
= "Align Selected Scale Y To Active"
1003 bl_options
= {'REGISTER', 'UNDO'}
1006 def poll(cls
, context
):
1007 return context
.active_object
is not None
1009 def execute(self
, context
):
1015 class OBJECT_OT_AlignScaleZOperator(Operator
):
1016 bl_idname
= "object.align_objects_scale_z"
1017 bl_label
= "Align Selected Scale Z To Active"
1018 bl_description
= "Align Selected Scale Z To Active"
1019 bl_options
= {'REGISTER', 'UNDO'}
1022 def poll(cls
, context
):
1023 return context
.active_object
is not None
1025 def execute(self
, context
):
1032 class VIEW3D_PT_AlignUi(Panel
):
1033 bl_space_type
= 'VIEW_3D'
1034 bl_region_type
= 'UI'
1035 bl_label
= "Align Tools"
1036 bl_context
= "objectmode"
1037 bl_category
= 'Item'
1038 bl_options
= {'DEFAULT_CLOSED'}
1040 def draw(self
, context
):
1041 layout
= self
.layout
1042 obj
= context
.object
1046 row
.label(text
="Active object is: ", icon
='OBJECT_DATA')
1048 box
.label(text
=obj
.name
, icon
='EDITMODE_HLT')
1050 col
= layout
.column()
1051 col
.label(text
="Align Loc + Rot:")
1053 col
= layout
.column(align
=False)
1054 col
.operator("object.align", text
="XYZ")
1056 col
= layout
.column()
1057 col
.label(text
="Align Location:")
1059 col
= layout
.column_flow(columns
=4, align
=True)
1060 col
.operator("object.align_location_x", text
="X")
1061 col
.operator("object.align_location_y", text
="Y")
1062 col
.operator("object.align_location_z", text
="Z")
1063 col
.operator("object.align_location_all", text
="All")
1065 col
= layout
.column()
1066 col
.label(text
="Align Rotation:")
1068 col
= layout
.column_flow(columns
=4, align
=True)
1069 col
.operator("object.align_rotation_x", text
="X")
1070 col
.operator("object.align_rotation_y", text
="Y")
1071 col
.operator("object.align_rotation_z", text
="Z")
1072 col
.operator("object.align_rotation_all", text
="All")
1074 col
= layout
.column()
1075 col
.label(text
="Align Scale:")
1077 col
= layout
.column_flow(columns
=4, align
=True)
1078 col
.operator("object.align_objects_scale_x", text
="X")
1079 col
.operator("object.align_objects_scale_y", text
="Y")
1080 col
.operator("object.align_objects_scale_z", text
="Z")
1081 col
.operator("object.align_objects_scale_all", text
="All")
1084 col
= layout
.column()
1085 col
.label(text
="Advanced Align Operations")
1086 layout
= self
.layout
1087 self
.layout
.operator("object.align_tools", text
="Advanced")
1090 # Add-ons Preferences Update Panel
1092 # Define Panel classes for updating
1098 def update_panel(self
, context
):
1099 message
= "Align Tools: Updating Panel locations has failed"
1101 for panel
in panels
:
1102 if "bl_rna" in panel
.__dict
__:
1103 bpy
.utils
.unregister_class(panel
)
1105 for panel
in panels
:
1106 panel
.bl_category
= context
.preferences
.addons
[__name__
].preferences
.category
1107 bpy
.utils
.register_class(panel
)
1109 except Exception as e
:
1110 print("\n[{}]\n{}\n\nError:\n{}".format(__name__
, message
, e
))
1114 class AlignAddonPreferences(AddonPreferences
):
1115 # this must match the addon name, use '__package__'
1116 # when defining this in a submodule of a python package.
1117 bl_idname
= __name__
1119 category
: StringProperty(
1120 name
="Tab Category",
1121 description
="Choose a name for the category of the panel",
1126 def draw(self
, context
):
1127 layout
= self
.layout
1131 col
.label(text
="Tab Category:")
1132 col
.prop(self
, "category", text
="")
1138 OBJECT_OT_AlignOperator
,
1139 OBJECT_OT_AlignLocationOperator
,
1140 OBJECT_OT_AlignLocationXOperator
,
1141 OBJECT_OT_AlignLocationYOperator
,
1142 OBJECT_OT_AlignLocationZOperator
,
1143 OBJECT_OT_AlignRotationOperator
,
1144 OBJECT_OT_AlignRotationXOperator
,
1145 OBJECT_OT_AlignRotationYOperator
,
1146 OBJECT_OT_AlignRotationZOperator
,
1147 OBJECT_OT_AlignScaleOperator
,
1148 OBJECT_OT_AlignScaleXOperator
,
1149 OBJECT_OT_AlignScaleYOperator
,
1150 OBJECT_OT_AlignScaleZOperator
,
1151 OBJECT_OT_align_tools
,
1152 AlignAddonPreferences
,
1156 # Register all operators and panels
1159 bpy
.utils
.register_class(cls
)
1164 bpy
.utils
.unregister_class(cls
)
1167 if __name__
== "__main__":