1 # SPDX-License-Identifier: GPL-2.0-or-later
2 # Contributed to by gabhead, Lell, Anfeo, meta-androcto.
6 "author": "gabhead, Lell, Anfeo",
9 "location": "View3D > Sidebar > Item Tab",
10 "description": "Align Selected Objects to Active Object",
12 "doc_url": "{BLENDER_MANUAL_URL}/addons/object/align_tools.html",
17 from bpy
.types
import (
22 from bpy
.props
import (
28 from mathutils
import (
38 for i
in bpy
.context
.selected_objects
:
39 i
.matrix_world
.translation
= bpy
.context
.active_object
.matrix_world
.translation
.copy()
40 i
.rotation_euler
= bpy
.context
.active_object
.rotation_euler
45 for i
in bpy
.context
.selected_objects
:
46 i
.matrix_world
.translation
= bpy
.context
.active_object
.matrix_world
.translation
.copy()
50 for i
in bpy
.context
.selected_objects
:
51 i
.matrix_world
.translation
.x
= bpy
.context
.active_object
.matrix_world
.translation
.x
55 for i
in bpy
.context
.selected_objects
:
56 i
.matrix_world
.translation
.y
= bpy
.context
.active_object
.matrix_world
.translation
.y
60 for i
in bpy
.context
.selected_objects
:
61 i
.matrix_world
.translation
.z
= bpy
.context
.active_object
.matrix_world
.translation
.z
66 for i
in bpy
.context
.selected_objects
:
67 i
.rotation_euler
= bpy
.context
.active_object
.rotation_euler
71 for i
in bpy
.context
.selected_objects
:
72 i
.rotation_euler
.x
= bpy
.context
.active_object
.rotation_euler
.x
76 for i
in bpy
.context
.selected_objects
:
77 i
.rotation_euler
.y
= bpy
.context
.active_object
.rotation_euler
.y
81 for i
in bpy
.context
.selected_objects
:
82 i
.rotation_euler
.z
= bpy
.context
.active_object
.rotation_euler
.z
86 def ScaleAll(context
):
87 for i
in bpy
.context
.selected_objects
:
88 i
.scale
= bpy
.context
.active_object
.scale
92 for i
in bpy
.context
.selected_objects
:
93 i
.scale
.x
= bpy
.context
.active_object
.scale
.x
97 for i
in bpy
.context
.selected_objects
:
98 i
.scale
.y
= bpy
.context
.active_object
.scale
.y
102 for i
in bpy
.context
.selected_objects
:
103 i
.scale
.z
= bpy
.context
.active_object
.scale
.z
106 # Advanced Align Defs #
108 # subject to object 0, 1 and 2 to pivot for cursor
109 def align_function(subject
, active_too
, consistent
, self_or_active
, loc_x
, loc_y
, loc_z
, ref1
, ref2
, loc_offset
,
110 rot_x
, rot_y
, rot_z
, rot_offset
, scale_x
, scale_y
, scale_z
, scale_offset
,
111 fit_x
, fit_y
, fit_z
):
113 sel_obj
= bpy
.context
.selected_objects
114 act_obj
= bpy
.context
.active_object
121 def get_reference_points(obj
, space
):
125 # let's get all the points coordinates
126 if space
== "global":
128 obj_mtx
= obj
.matrix_world
129 if obj
.type == 'MESH' and len(me
.vertices
) > 0:
131 for p
in me
.vertices
:
132 co_list
.append((obj_mtx
@ p
.co
))
134 elif obj
.type == 'SURFACE' and len(me
.splines
) > 0:
138 co_list
.append((obj_mtx
@ p
.co
))
139 elif obj
.type == 'FONT' and len(me
.splines
) > 0:
142 for p
in s
.bezier_points
:
143 co_list
.append((obj_mtx
@ p
.co
))
145 elif space
== "local":
147 if obj
.type == 'MESH' and len(me
.vertices
) > 0:
149 for p
in me
.vertices
:
152 elif obj
.type == 'SURFACE' and len(me
.splines
) > 0:
157 elif obj
.type == 'FONT' and len(obj
.data
.splines
) > 0:
160 for p
in s
.bezier_points
:
163 # if a valid point found
164 # proceed to calculate the extremes
166 max_x
= co_list
[0][0]
167 min_x
= co_list
[0][0]
168 max_y
= co_list
[0][1]
169 min_y
= co_list
[0][1]
170 max_z
= co_list
[0][2]
171 min_z
= co_list
[0][2]
174 # the strings of the list compared with the smaller and more found
175 # in order to find the minor and major for each axis
195 # otherwise use the pivot object
196 a
= obj
.matrix_world
.translation
204 center_x
= min_x
+ ((max_x
- min_x
) / 2)
205 center_y
= min_y
+ ((max_y
- min_y
) / 2)
206 center_z
= min_z
+ ((max_z
- min_z
) / 2)
208 reference_points
= [min_x
, center_x
, max_x
, min_y
, center_y
, max_y
, min_z
, center_z
, max_z
]
209 return reference_points
211 def get_sel_ref(ref_co
, sel_obj
): # I look for the selection end points
213 sel_min
= ref_co
.copy()
214 sel_max
= ref_co
.copy()
217 if obj
!= act_obj
or (active_too
and obj
== act_obj
):
219 ref_points
= get_reference_points(obj
, "global")
220 ref_min
= Vector([ref_points
[0], ref_points
[3], ref_points
[6]])
221 ref_max
= Vector([ref_points
[2], ref_points
[5], ref_points
[8]])
223 if ref_min
[0] < sel_min
[0]:
224 sel_min
[0] = ref_min
[0]
225 if ref_max
[0] > sel_max
[0]:
226 sel_max
[0] = ref_max
[0]
227 if ref_min
[1] < sel_min
[1]:
228 sel_min
[1] = ref_min
[1]
229 if ref_max
[1] > sel_max
[1]:
230 sel_max
[1] = ref_max
[1]
231 if ref_min
[2] < sel_min
[2]:
232 sel_min
[2] = ref_min
[2]
233 if ref_max
[2] > sel_max
[2]:
234 sel_max
[2] = ref_max
[2]
236 return sel_min
, sel_max
238 def find_ref2_co(act_obj
):
239 # It contains the coordinates of the reference point for the positioning
241 ref_points
= get_reference_points(act_obj
, "global")
242 ref2_co
= [ref_points
[0], ref_points
[3], ref_points
[6]]
243 ref2_co
= Vector(ref2_co
)
245 ref_points
= get_reference_points(act_obj
, "global")
246 ref2_co
= [ref_points
[1], ref_points
[4], ref_points
[7]]
247 ref2_co
= Vector(ref2_co
)
249 ref2_co
= act_obj
.location
250 ref2_co
= Vector(ref2_co
)
252 ref_points
= get_reference_points(act_obj
, "global")
253 ref2_co
= [ref_points
[2], ref_points
[5], ref_points
[8]]
254 ref2_co
= Vector(ref2_co
)
256 ref2_co
= bpy
.context
.scene
.cursor
.location
260 def find_new_coord(obj
):
262 ref_points
= get_reference_points(obj
, "global")
266 min_x
= ref_points
[0]
267 new_x
= ref2_co
[0] + (obj
.location
[0] - min_x
) + loc_offset
[0]
269 center_x
= ref_points
[1]
270 new_x
= ref2_co
[0] + (obj
.location
[0] - center_x
) + loc_offset
[0]
272 new_x
= ref2_co
[0] + loc_offset
[0]
274 max_x
= ref_points
[2]
275 new_x
= ref2_co
[0] - (max_x
- obj
.location
[0]) + loc_offset
[0]
276 obj
.matrix_world
.translation
[0] = new_x
279 min_y
= ref_points
[3]
280 new_y
= ref2_co
[1] + (obj
.location
[1] - min_y
) + loc_offset
[1]
282 center_y
= ref_points
[4]
283 new_y
= ref2_co
[1] + (obj
.location
[1] - center_y
) + loc_offset
[1]
285 new_y
= ref2_co
[1] + loc_offset
[1]
287 max_y
= ref_points
[5]
288 new_y
= ref2_co
[1] - (max_y
- obj
.location
[1]) + loc_offset
[1]
289 obj
.matrix_world
.translation
[1] = new_y
292 min_z
= ref_points
[6]
293 new_z
= ref2_co
[2] + (obj
.location
[2] - min_z
) + loc_offset
[2]
295 center_z
= ref_points
[7]
296 new_z
= ref2_co
[2] + (obj
.location
[2] - center_z
) + loc_offset
[2]
298 new_z
= ref2_co
[2] + loc_offset
[2]
300 max_z
= ref_points
[8]
301 new_z
= ref2_co
[2] - (max_z
- obj
.location
[2]) + loc_offset
[2]
302 obj
.matrix_world
.translation
[2] = new_z
304 def find_new_rotation(obj
):
306 obj
.rotation_euler
[0] = act_obj
.rotation_euler
[0] + rot_offset
[0]
308 obj
.rotation_euler
[1] = act_obj
.rotation_euler
[1] + rot_offset
[1]
310 obj
.rotation_euler
[2] = act_obj
.rotation_euler
[2] + rot_offset
[2]
312 def find_new_scale(obj
):
314 obj
.scale
[0] = act_obj
.scale
[0] + scale_offset
[0]
316 obj
.scale
[1] = act_obj
.scale
[1] + scale_offset
[1]
318 obj
.scale
[2] = act_obj
.scale
[2] + scale_offset
[2]
320 def find_new_dimensions(obj
, ref_dim
):
321 ref_points
= get_reference_points(obj
, "local")
323 dim
= ref_points
[2] - ref_points
[0]
324 obj
.scale
[0] = (ref_dim
[0] / dim
) * act_obj
.scale
[0]
326 dim
= ref_points
[5] - ref_points
[3]
327 obj
.scale
[1] = (ref_dim
[1] / dim
) * act_obj
.scale
[1]
329 dim
= ref_points
[8] - ref_points
[6]
330 obj
.scale
[2] = (ref_dim
[2] / dim
) * act_obj
.scale
[2]
334 vec_ref2_co
= Vector(ref2_co
)
335 offset
= vec_ref2_co
- obj
.location
336 offset_x
= [offset
[0] + loc_offset
[0], 0, 0]
337 offset_y
= [0, offset
[1] + loc_offset
[1], 0]
338 offset_z
= [0, 0, offset
[2] + loc_offset
[2]]
341 obj_mtx
= obj
.matrix_world
.copy()
342 # What's the displacement vector for the pivot?
343 move_pivot
= Vector(vec
)
345 # Move the pivot point (which is the object's location)
349 nm
= obj_mtx
.inverted() @ Matrix
.Translation(-move_pivot
) @ obj_mtx
351 # Transform the mesh now
361 def point_in_selection(act_obj
, sel_obj
):
366 obj_mtx
= o
.matrix_world
367 if o
.type == 'MESH' and len(o
.data
.vertices
) > 0:
368 ref_co
= o
.data
.vertices
[0].co
.copy()
369 ref_co
= obj_mtx
@ ref_co
372 elif o
.type == 'CURVE' and len(o
.data
.splines
) > 0:
373 ref_co
= o
.data
.splines
[0].bezier_point
[0].co
.copy()
374 ref_co
= obj_mtx
@ ref_co
377 elif o
.type == 'SURFACE' and len(o
.data
.splines
) > 0:
378 ref_co
= o
.data
.splines
[0].points
[0].co
.copy()
379 ref_co
= obj_mtx
@ ref_co
382 elif o
.type == 'FONT' and len(o
.data
.splines
) > 0:
383 ref_co
= o
.data
.splines
[0].bezier_points
[0].co
.copy()
384 ref_co
= obj_mtx
@ ref_co
387 # if no object had data, use the position of an object that was not active as an internal
390 ref_co
= ref_ob
.matrix_world
.translation
395 # if act_obj.type == ('MESH' or 'FONT' or 'CURVE' or 'SURFACE'):
396 if act_obj
.type == 'MESH' or act_obj
.type == 'FONT' or act_obj
.type == 'SURFACE':
397 ref2_co
= find_ref2_co(act_obj
)
400 ref2_co
= bpy
.context
.scene
.cursor
.location
402 ref2_co
= act_obj
.matrix_world
.translation
404 # in the case of substantial selection
406 # I am seeking a point that is in the selection space
407 ref_co
= point_in_selection(act_obj
, sel_obj
)
409 sel_min
, sel_max
= get_sel_ref(ref_co
, sel_obj
)
411 sel_center
= sel_min
+ ((sel_max
- sel_min
) / 2)
412 translate
= [0, 0, 0]
414 # calculating how much to move the selection
416 translate
= ref2_co
- sel_min
+ loc_offset
418 translate
= ref2_co
- sel_center
+ loc_offset
420 translate
= ref2_co
- sel_max
+ loc_offset
422 # Move the various objects
425 if obj
!= act_obj
or (active_too
and obj
== act_obj
):
428 obj
.location
[0] += translate
[0]
430 obj
.location
[1] += translate
[1]
432 obj
.location
[2] += translate
[2]
433 else: # not consistent
436 if rot_x
or rot_y
or rot_z
:
437 find_new_rotation(obj
)
439 if fit_x
or fit_y
or fit_z
:
441 ref_points
= get_reference_points(act_obj
, "local")
442 dim
[0] = ref_points
[2] - ref_points
[0]
443 dim
[1] = ref_points
[5] - ref_points
[3]
444 dim
[2] = ref_points
[8] - ref_points
[6]
445 find_new_dimensions(obj
, dim
)
447 if scale_x
or scale_y
or scale_z
:
450 if loc_x
or loc_y
or loc_z
:
451 # print("ehy", ref2_co)
454 if active_too
is True:
455 if loc_x
or loc_y
or loc_z
:
456 find_new_coord(act_obj
)
457 if rot_x
or rot_y
or rot_z
:
458 find_new_rotation(act_obj
)
459 if scale_x
or scale_y
or scale_z
:
460 find_new_scale(act_obj
)
461 # add dimensions if dim offset will be added
464 if self_or_active
== "1":
465 if act_obj
.type == 'MESH':
466 ref2_co
= find_ref2_co(act_obj
)
468 if self_or_active
== "0":
469 ref2_co
= find_ref2_co(obj
)
470 if loc_x
or loc_y
or loc_z
:
471 if obj
!= act_obj
and obj
.type == 'MESH':
474 if active_too
is True:
475 if act_obj
.type == 'MESH':
476 if loc_x
or loc_y
or loc_z
:
477 if self_or_active
== "0":
478 ref2_co
= find_ref2_co(act_obj
)
482 if self_or_active
== "1":
483 if act_obj
.type == 'MESH' or act_obj
.type == 'FONT' or act_obj
.type == 'SURFACE':
484 ref2_co
= find_ref2_co(act_obj
)
485 ref_points
= get_reference_points(act_obj
, "global")
487 ref2_co
= act_obj
.matrix_world
.translation
488 ref_points
= [ref2_co
[0], ref2_co
[0], ref2_co
[0],
489 ref2_co
[1], ref2_co
[1], ref2_co
[1],
490 ref2_co
[2], ref2_co
[2], ref2_co
[2]]
494 bpy
.context
.scene
.cursor
.location
[0] = ref_points
[0] + loc_offset
[0]
496 bpy
.context
.scene
.cursor
.location
[1] = ref_points
[3] + loc_offset
[1]
498 bpy
.context
.scene
.cursor
.location
[2] = ref_points
[6] + loc_offset
[2]
501 bpy
.context
.scene
.cursor
.location
[0] = ref_points
[1] + loc_offset
[0]
503 bpy
.context
.scene
.cursor
.location
[1] = ref_points
[4] + loc_offset
[1]
505 bpy
.context
.scene
.cursor
.location
[2] = ref_points
[7] + loc_offset
[2]
508 bpy
.context
.scene
.cursor
.location
[0] = act_obj
.location
[0] + loc_offset
[0]
510 bpy
.context
.scene
.cursor
.location
[1] = act_obj
.location
[1] + loc_offset
[1]
512 bpy
.context
.scene
.cursor
.location
[2] = act_obj
.location
[2] + loc_offset
[2]
515 bpy
.context
.scene
.cursor
.location
[0] = ref_points
[2] + loc_offset
[0]
517 bpy
.context
.scene
.cursor
.location
[1] = ref_points
[5] + loc_offset
[1]
519 bpy
.context
.scene
.cursor
.location
[2] = ref_points
[8] + loc_offset
[2]
520 elif self_or_active
== "2":
521 ref_co
= point_in_selection(act_obj
, sel_obj
)
523 sel_min
, sel_max
= get_sel_ref(ref_co
, sel_obj
)
524 sel_center
= sel_min
+ ((sel_max
- sel_min
) / 2)
528 bpy
.context
.scene
.cursor
.location
[0] = sel_min
[0] + loc_offset
[0]
530 bpy
.context
.scene
.cursor
.location
[1] = sel_min
[1] + loc_offset
[1]
532 bpy
.context
.scene
.cursor
.location
[2] = sel_min
[2] + loc_offset
[2]
535 bpy
.context
.scene
.cursor
.location
[0] = sel_center
[0] + loc_offset
[0]
537 bpy
.context
.scene
.cursor
.location
[1] = sel_center
[1] + loc_offset
[1]
539 bpy
.context
.scene
.cursor
.location
[2] = sel_center
[2] + loc_offset
[2]
542 bpy
.context
.scene
.cursor
.location
[0] = sel_max
[0] + loc_offset
[0]
544 bpy
.context
.scene
.cursor
.location
[1] = sel_max
[1] + loc_offset
[1]
546 bpy
.context
.scene
.cursor
.location
[2] = sel_max
[2] + loc_offset
[2]
552 class OBJECT_OT_align_tools(Operator
):
553 bl_idname
= "object.align_tools"
554 bl_label
= "Align Operator"
555 bl_description
= "Align Object Tools"
556 bl_options
= {'REGISTER', 'UNDO', 'PRESET'}
558 # property definitions
560 # Object-Pivot-Cursor:
561 subject
: EnumProperty(
562 items
=(("0", "Object", "Align Objects"),
563 ("1", "Pivot", "Align Objects Pivot"),
564 ("2", "Cursor", "Align Cursor To Active")),
566 description
="What will be moved"
569 active_too
: BoolProperty(
572 description
="Move the active object too"
575 advanced
: BoolProperty(
576 name
="Advanced Options",
578 description
="Show advanced options"
580 consistent
: BoolProperty(
581 name
="Consistent Selection",
583 description
="Use consistent selection"
587 name
="Align to X axis",
589 description
="Enable X axis alignment"
592 name
="Align to Y axis",
594 description
="Enable Y axis alignment"
597 name
="Align to Z axis",
599 description
="Enable Z axis alignment"
603 items
=(("3", "Max", "Align the maximum point"),
604 ("1", "Center", "Align the center point"),
605 ("2", "Pivot", "Align the pivot"),
606 ("0", "Min", "Align the minimum point")),
607 name
="Selection reference",
608 description
="Moved objects reference point"
610 # Active Object Option:
612 items
=(("3", "Max", "Align to the maximum point"),
613 ("1", "Center", "Align to the center point"),
614 ("2", "Pivot", "Align to the pivot"),
615 ("0", "Min", "Align to the minimum point"),
616 ("4", "Cursor", "Description")),
617 name
="Active reference",
618 description
="Destination point"
620 self_or_active
: EnumProperty(
621 items
=(("0", "Self", "In relation of itself"),
622 ("1", "Active", "In relation of the active object"),
623 ("2", "Selection", "In relation of the entire selection")),
626 description
="To what the pivot will be aligned"
629 loc_offset
: FloatVectorProperty(
630 name
="Location Offset",
631 description
="Offset for location align position",
632 default
=(0.0, 0.0, 0.0),
633 subtype
='XYZ', size
=3
636 rot_offset
: FloatVectorProperty(
637 name
="Rotation Offset",
638 description
="Offset for rotation alignment",
639 default
=(0.0, 0.0, 0.0),
640 subtype
='EULER', size
=3
643 scale_offset
: FloatVectorProperty(
645 description
="Offset for scale match",
646 default
=(0.0, 0.0, 0.0),
647 subtype
='XYZ', size
=3
649 # Fit Dimension Prop:
651 name
="Fit Dimension to X axis",
656 name
="Fit Dimension to Y axis",
661 name
="Fit Dimension to Z axis",
665 # Apply Fit Dimension:
666 apply_dim
: BoolProperty(
667 name
="Apply Dimension",
673 name
="Align Rotation to X axis",
678 name
="Align Rotation to Y axis",
683 name
="Align Rotation to Z axis",
688 apply_rot
: BoolProperty(
689 name
="Apply Rotation",
694 scale_x
: BoolProperty(
695 name
="Match Scale to X axis",
699 scale_y
: BoolProperty(
700 name
="Match Scale to Y axis",
704 scale_z
: BoolProperty(
705 name
="match Scale to Z axis",
710 apply_scale
: BoolProperty(
716 def draw(self
, context
):
720 row
.label(text
="Active object is: ", icon
='OBJECT_DATA')
722 box
.label(text
=obj
.name
, icon
='EDITMODE_HLT')
723 # Object-Pivot-Cursor:
725 row0
.prop(self
, 'subject', expand
=True)
729 row1
.prop(self
, 'active_too')
730 row1
.prop(self
, 'advanced')
733 row1b
.prop(self
, 'consistent')
736 row2
.label(text
="Align Location:")
740 row3
.prop(self
, "loc_x", text
="X", toggle
=True)
741 row3
.prop(self
, "loc_y", text
="Y", toggle
=True)
742 row3
.prop(self
, "loc_z", text
="Z", toggle
=True)
745 if self
.advanced
is True:
747 # row8.label(text='Location Offset')
749 row9
.prop(self
, 'loc_offset', text
='')
752 if self
.advanced
is True:
753 sel
= bpy
.context
.selected_objects
757 row4
.label(text
="Selected: " + str(sel_obs
) + " Objects", icon
='OBJECT_DATA')
758 if self
.subject
== "1" or self
.subject
== "2":
760 row5b
.prop(self
, 'self_or_active', expand
=True)
763 row5
.prop(self
, 'ref1', expand
=True)
765 # Active Object Options: Number of select objects
766 act
= bpy
.context
.active_object
768 if self
.advanced
is True:
771 row6
.label(text
="Active: " + act
.name
, icon
='OBJECT_DATA')
773 row7
.prop(self
, 'ref2', expand
=True)
775 if self
.subject
== "0":
777 row12
.label(text
='Align Rotation:')
778 row13
= layout
.row(align
=True)
779 row13
.prop(self
, 'rot_x', text
='X', toggle
=True)
780 row13
.prop(self
, 'rot_y', text
='Y', toggle
=True)
781 row13
.prop(self
, 'rot_z', text
='Z', toggle
=True)
782 row13
.prop(self
, 'apply_rot', text
='Apply', toggle
=True)
783 if self
.advanced
is True:
784 row13b
= layout
.row()
785 row13b
.prop(self
, 'rot_offset', text
='')
788 row14
.label(text
='Match Scale:')
789 row15
= layout
.row(align
=True)
790 row15
.prop(self
, 'scale_x', text
='X', toggle
=True)
791 row15
.prop(self
, 'scale_y', text
='Y', toggle
=True)
792 row15
.prop(self
, 'scale_z', text
='Z', toggle
=True)
793 row15
.prop(self
, 'apply_scale', text
='Apply', toggle
=True)
794 if self
.advanced
is True:
795 row15b
= layout
.row()
796 row15b
.prop(self
, 'scale_offset', text
='')
799 row10
.label(text
='Fit Dimensions:')
800 row11
= layout
.row(align
=True)
801 row11
.prop(self
, 'fit_x', text
='X', toggle
=True)
802 row11
.prop(self
, 'fit_y', text
='Y', toggle
=True)
803 row11
.prop(self
, 'fit_z', text
='Z', toggle
=True)
804 row11
.prop(self
, 'apply_dim', text
='Apply', toggle
=True)
806 def execute(self
, context
):
808 self
.subject
, self
.active_too
, self
.consistent
,
809 self
.self_or_active
, self
.loc_x
, self
.loc_y
, self
.loc_z
,
810 self
.ref1
, self
.ref2
, self
.loc_offset
,
811 self
.rot_x
, self
.rot_y
, self
.rot_z
, self
.rot_offset
,
812 self
.scale_x
, self
.scale_y
, self
.scale_z
, self
.scale_offset
,
813 self
.fit_x
, self
.fit_y
, self
.fit_z
819 # Simple Align Classes #
821 # Align All Rotation And Location
822 class OBJECT_OT_AlignOperator(Operator
):
823 bl_idname
= "object.align"
824 bl_label
= "Align Selected To Active"
825 bl_description
= "Align Selected To Active"
826 bl_options
= {'REGISTER', 'UNDO'}
829 def poll(cls
, context
):
830 return context
.active_object
is not None
832 def execute(self
, context
):
838 class OBJECT_OT_AlignLocationOperator(Operator
):
839 bl_idname
= "object.align_location_all"
840 bl_label
= "Align Selected Location To Active"
841 bl_description
= "Align Selected Location To Active"
842 bl_options
= {'REGISTER', 'UNDO'}
845 def poll(cls
, context
):
846 return context
.active_object
is not None
848 def execute(self
, context
):
854 class OBJECT_OT_AlignLocationXOperator(Operator
):
855 bl_idname
= "object.align_location_x"
856 bl_label
= "Align Selected Location X To Active"
857 bl_description
= "Align Selected Location X To Active"
858 bl_options
= {'REGISTER', 'UNDO'}
861 def poll(cls
, context
):
862 return context
.active_object
is not None
864 def execute(self
, context
):
870 class OBJECT_OT_AlignLocationYOperator(Operator
):
871 bl_idname
= "object.align_location_y"
872 bl_label
= "Align Selected Location Y To Active"
873 bl_description
= "Align Selected Location Y To Active"
874 bl_options
= {'REGISTER', 'UNDO'}
877 def poll(cls
, context
):
878 return context
.active_object
is not None
880 def execute(self
, context
):
886 class OBJECT_OT_AlignLocationZOperator(Operator
):
887 bl_idname
= "object.align_location_z"
888 bl_label
= "Align Selected Location Z To Active"
889 bl_description
= "Align Selected Location Z To Active"
890 bl_options
= {'REGISTER', 'UNDO'}
893 def poll(cls
, context
):
894 return context
.active_object
is not None
896 def execute(self
, context
):
902 class OBJECT_OT_AlignRotationOperator(Operator
):
903 bl_idname
= "object.align_rotation_all"
904 bl_label
= "Align Selected Rotation To Active"
905 bl_description
= "Align Selected Rotation To Active"
906 bl_options
= {'REGISTER', 'UNDO'}
909 def poll(cls
, context
):
910 return context
.active_object
is not None
912 def execute(self
, context
):
918 class OBJECT_OT_AlignRotationXOperator(Operator
):
919 bl_idname
= "object.align_rotation_x"
920 bl_label
= "Align Selected Rotation X To Active"
921 bl_description
= "Align Selected Rotation X To Active"
922 bl_options
= {'REGISTER', 'UNDO'}
925 def poll(cls
, context
):
926 return context
.active_object
is not None
928 def execute(self
, context
):
934 class OBJECT_OT_AlignRotationYOperator(Operator
):
935 bl_idname
= "object.align_rotation_y"
936 bl_label
= "Align Selected Rotation Y To Active"
937 bl_description
= "Align Selected Rotation Y To Active"
938 bl_options
= {'REGISTER', 'UNDO'}
941 def poll(cls
, context
):
942 return context
.active_object
is not None
944 def execute(self
, context
):
950 class OBJECT_OT_AlignRotationZOperator(Operator
):
951 bl_idname
= "object.align_rotation_z"
952 bl_label
= "Align Selected Rotation Z To Active"
953 bl_description
= "Align Selected Rotation Z To Active"
954 bl_options
= {'REGISTER', 'UNDO'}
957 def poll(cls
, context
):
958 return context
.active_object
is not None
960 def execute(self
, context
):
966 class OBJECT_OT_AlignScaleOperator(Operator
):
967 bl_idname
= "object.align_objects_scale_all"
968 bl_label
= "Align Selected Scale To Active"
969 bl_description
= "Align Selected Scale To Active"
970 bl_options
= {'REGISTER', 'UNDO'}
973 def poll(cls
, context
):
974 return context
.active_object
is not None
976 def execute(self
, context
):
982 class OBJECT_OT_AlignScaleXOperator(Operator
):
983 bl_idname
= "object.align_objects_scale_x"
984 bl_label
= "Align Selected Scale X To Active"
985 bl_description
= "Align Selected Scale X To Active"
986 bl_options
= {'REGISTER', 'UNDO'}
989 def poll(cls
, context
):
990 return context
.active_object
is not None
992 def execute(self
, context
):
998 class OBJECT_OT_AlignScaleYOperator(Operator
):
999 bl_idname
= "object.align_objects_scale_y"
1000 bl_label
= "Align Selected Scale Y To Active"
1001 bl_description
= "Align Selected Scale Y To Active"
1002 bl_options
= {'REGISTER', 'UNDO'}
1005 def poll(cls
, context
):
1006 return context
.active_object
is not None
1008 def execute(self
, context
):
1014 class OBJECT_OT_AlignScaleZOperator(Operator
):
1015 bl_idname
= "object.align_objects_scale_z"
1016 bl_label
= "Align Selected Scale Z To Active"
1017 bl_description
= "Align Selected Scale Z To Active"
1018 bl_options
= {'REGISTER', 'UNDO'}
1021 def poll(cls
, context
):
1022 return context
.active_object
is not None
1024 def execute(self
, context
):
1031 class VIEW3D_PT_AlignUi(Panel
):
1032 bl_space_type
= 'VIEW_3D'
1033 bl_region_type
= 'UI'
1034 bl_label
= "Align Tools"
1035 bl_context
= "objectmode"
1036 bl_category
= 'Item'
1037 bl_options
= {'DEFAULT_CLOSED'}
1039 def draw(self
, context
):
1040 layout
= self
.layout
1041 obj
= context
.object
1045 row
.label(text
="Active object is: ", icon
='OBJECT_DATA')
1047 box
.label(text
=obj
.name
, icon
='EDITMODE_HLT')
1049 col
= layout
.column()
1050 col
.label(text
="Align Loc + Rot:")
1052 col
= layout
.column(align
=False)
1053 col
.operator("object.align", text
="XYZ")
1055 col
= layout
.column()
1056 col
.label(text
="Align Location:")
1058 col
= layout
.column_flow(columns
=4, align
=True)
1059 col
.operator("object.align_location_x", text
="X")
1060 col
.operator("object.align_location_y", text
="Y")
1061 col
.operator("object.align_location_z", text
="Z")
1062 col
.operator("object.align_location_all", text
="All")
1064 col
= layout
.column()
1065 col
.label(text
="Align Rotation:")
1067 col
= layout
.column_flow(columns
=4, align
=True)
1068 col
.operator("object.align_rotation_x", text
="X")
1069 col
.operator("object.align_rotation_y", text
="Y")
1070 col
.operator("object.align_rotation_z", text
="Z")
1071 col
.operator("object.align_rotation_all", text
="All")
1073 col
= layout
.column()
1074 col
.label(text
="Align Scale:")
1076 col
= layout
.column_flow(columns
=4, align
=True)
1077 col
.operator("object.align_objects_scale_x", text
="X")
1078 col
.operator("object.align_objects_scale_y", text
="Y")
1079 col
.operator("object.align_objects_scale_z", text
="Z")
1080 col
.operator("object.align_objects_scale_all", text
="All")
1083 col
= layout
.column()
1084 col
.label(text
="Advanced Align Operations")
1085 layout
= self
.layout
1086 self
.layout
.operator("object.align_tools", text
="Advanced")
1089 # Add-ons Preferences Update Panel
1091 # Define Panel classes for updating
1097 def update_panel(self
, context
):
1098 message
= "Align Tools: Updating Panel locations has failed"
1100 for panel
in panels
:
1101 if "bl_rna" in panel
.__dict
__:
1102 bpy
.utils
.unregister_class(panel
)
1104 for panel
in panels
:
1105 panel
.bl_category
= context
.preferences
.addons
[__name__
].preferences
.category
1106 bpy
.utils
.register_class(panel
)
1108 except Exception as e
:
1109 print("\n[{}]\n{}\n\nError:\n{}".format(__name__
, message
, e
))
1113 class AlignAddonPreferences(AddonPreferences
):
1114 # this must match the addon name, use '__package__'
1115 # when defining this in a submodule of a python package.
1116 bl_idname
= __name__
1118 category
: StringProperty(
1119 name
="Tab Category",
1120 description
="Choose a name for the category of the panel",
1125 def draw(self
, context
):
1126 layout
= self
.layout
1130 col
.label(text
="Tab Category:")
1131 col
.prop(self
, "category", text
="")
1137 OBJECT_OT_AlignOperator
,
1138 OBJECT_OT_AlignLocationOperator
,
1139 OBJECT_OT_AlignLocationXOperator
,
1140 OBJECT_OT_AlignLocationYOperator
,
1141 OBJECT_OT_AlignLocationZOperator
,
1142 OBJECT_OT_AlignRotationOperator
,
1143 OBJECT_OT_AlignRotationXOperator
,
1144 OBJECT_OT_AlignRotationYOperator
,
1145 OBJECT_OT_AlignRotationZOperator
,
1146 OBJECT_OT_AlignScaleOperator
,
1147 OBJECT_OT_AlignScaleXOperator
,
1148 OBJECT_OT_AlignScaleYOperator
,
1149 OBJECT_OT_AlignScaleZOperator
,
1150 OBJECT_OT_align_tools
,
1151 AlignAddonPreferences
,
1155 # Register all operators and panels
1158 bpy
.utils
.register_class(cls
)
1163 bpy
.utils
.unregister_class(cls
)
1166 if __name__
== "__main__":