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'}
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"
828 def poll(cls
, context
):
829 return context
.active_object
is not None
831 def execute(self
, context
):
837 class OBJECT_OT_AlignLocationOperator(Operator
):
838 bl_idname
= "object.align_location_all"
839 bl_label
= "Align Selected Location To Active"
840 bl_description
= "Align Selected Location To Active"
843 def poll(cls
, context
):
844 return context
.active_object
is not None
846 def execute(self
, context
):
852 class OBJECT_OT_AlignLocationXOperator(Operator
):
853 bl_idname
= "object.align_location_x"
854 bl_label
= "Align Selected Location X To Active"
855 bl_description
= "Align Selected Location X To Active"
858 def poll(cls
, context
):
859 return context
.active_object
is not None
861 def execute(self
, context
):
867 class OBJECT_OT_AlignLocationYOperator(Operator
):
868 bl_idname
= "object.align_location_y"
869 bl_label
= "Align Selected Location Y To Active"
870 bl_description
= "Align Selected Location Y To Active"
873 def poll(cls
, context
):
874 return context
.active_object
is not None
876 def execute(self
, context
):
882 class OBJECT_OT_AlignLocationZOperator(Operator
):
883 bl_idname
= "object.align_location_z"
884 bl_label
= "Align Selected Location Z To Active"
885 bl_description
= "Align Selected Location Z To Active"
888 def poll(cls
, context
):
889 return context
.active_object
is not None
891 def execute(self
, context
):
897 class OBJECT_OT_AlignRotationOperator(Operator
):
898 bl_idname
= "object.align_rotation_all"
899 bl_label
= "Align Selected Rotation To Active"
900 bl_description
= "Align Selected Rotation To Active"
903 def poll(cls
, context
):
904 return context
.active_object
is not None
906 def execute(self
, context
):
912 class OBJECT_OT_AlignRotationXOperator(Operator
):
913 bl_idname
= "object.align_rotation_x"
914 bl_label
= "Align Selected Rotation X To Active"
915 bl_description
= "Align Selected Rotation X To Active"
918 def poll(cls
, context
):
919 return context
.active_object
is not None
921 def execute(self
, context
):
927 class OBJECT_OT_AlignRotationYOperator(Operator
):
928 bl_idname
= "object.align_rotation_y"
929 bl_label
= "Align Selected Rotation Y To Active"
930 bl_description
= "Align Selected Rotation Y To Active"
933 def poll(cls
, context
):
934 return context
.active_object
is not None
936 def execute(self
, context
):
942 class OBJECT_OT_AlignRotationZOperator(Operator
):
943 bl_idname
= "object.align_rotation_z"
944 bl_label
= "Align Selected Rotation Z To Active"
945 bl_description
= "Align Selected Rotation Z To Active"
948 def poll(cls
, context
):
949 return context
.active_object
is not None
951 def execute(self
, context
):
957 class OBJECT_OT_AlignScaleOperator(Operator
):
958 bl_idname
= "object.align_objects_scale_all"
959 bl_label
= "Align Selected Scale To Active"
960 bl_description
= "Align Selected Scale To Active"
963 def poll(cls
, context
):
964 return context
.active_object
is not None
966 def execute(self
, context
):
972 class OBJECT_OT_AlignScaleXOperator(Operator
):
973 bl_idname
= "object.align_objects_scale_x"
974 bl_label
= "Align Selected Scale X To Active"
975 bl_description
= "Align Selected Scale X To Active"
978 def poll(cls
, context
):
979 return context
.active_object
is not None
981 def execute(self
, context
):
987 class OBJECT_OT_AlignScaleYOperator(Operator
):
988 bl_idname
= "object.align_objects_scale_y"
989 bl_label
= "Align Selected Scale Y To Active"
990 bl_description
= "Align Selected Scale Y To Active"
993 def poll(cls
, context
):
994 return context
.active_object
is not None
996 def execute(self
, context
):
1002 class OBJECT_OT_AlignScaleZOperator(Operator
):
1003 bl_idname
= "object.align_objects_scale_z"
1004 bl_label
= "Align Selected Scale Z To Active"
1005 bl_description
= "Align Selected Scale Z To Active"
1008 def poll(cls
, context
):
1009 return context
.active_object
is not None
1011 def execute(self
, context
):
1018 class VIEW3D_PT_AlignUi(Panel
):
1019 bl_space_type
= 'VIEW_3D'
1020 bl_region_type
= 'UI'
1021 bl_label
= "Align Tools"
1022 bl_context
= "objectmode"
1023 bl_category
= 'Item'
1024 bl_options
= {'DEFAULT_CLOSED'}
1026 def draw(self
, context
):
1027 layout
= self
.layout
1028 obj
= context
.object
1032 row
.label(text
="Active object is: ", icon
='OBJECT_DATA')
1034 box
.label(text
=obj
.name
, icon
='EDITMODE_HLT')
1036 col
= layout
.column()
1037 col
.label(text
="Align Loc + Rot:")
1039 col
= layout
.column(align
=False)
1040 col
.operator("object.align", text
="XYZ")
1042 col
= layout
.column()
1043 col
.label(text
="Align Location:")
1045 col
= layout
.column_flow(columns
=4, align
=True)
1046 col
.operator("object.align_location_x", text
="X")
1047 col
.operator("object.align_location_y", text
="Y")
1048 col
.operator("object.align_location_z", text
="Z")
1049 col
.operator("object.align_location_all", text
="All")
1051 col
= layout
.column()
1052 col
.label(text
="Align Rotation:")
1054 col
= layout
.column_flow(columns
=4, align
=True)
1055 col
.operator("object.align_rotation_x", text
="X")
1056 col
.operator("object.align_rotation_y", text
="Y")
1057 col
.operator("object.align_rotation_z", text
="Z")
1058 col
.operator("object.align_rotation_all", text
="All")
1060 col
= layout
.column()
1061 col
.label(text
="Align Scale:")
1063 col
= layout
.column_flow(columns
=4, align
=True)
1064 col
.operator("object.align_objects_scale_x", text
="X")
1065 col
.operator("object.align_objects_scale_y", text
="Y")
1066 col
.operator("object.align_objects_scale_z", text
="Z")
1067 col
.operator("object.align_objects_scale_all", text
="All")
1070 col
= layout
.column()
1071 col
.label(text
="Advanced Align Operations")
1072 layout
= self
.layout
1073 self
.layout
.operator("object.align_tools", text
="Advanced")
1076 # Add-ons Preferences Update Panel
1078 # Define Panel classes for updating
1084 def update_panel(self
, context
):
1085 message
= "Align Tools: Updating Panel locations has failed"
1087 for panel
in panels
:
1088 if "bl_rna" in panel
.__dict
__:
1089 bpy
.utils
.unregister_class(panel
)
1091 for panel
in panels
:
1092 panel
.bl_category
= context
.preferences
.addons
[__name__
].preferences
.category
1093 bpy
.utils
.register_class(panel
)
1095 except Exception as e
:
1096 print("\n[{}]\n{}\n\nError:\n{}".format(__name__
, message
, e
))
1100 class AlignAddonPreferences(AddonPreferences
):
1101 # this must match the addon name, use '__package__'
1102 # when defining this in a submodule of a python package.
1103 bl_idname
= __name__
1105 category
: StringProperty(
1106 name
="Tab Category",
1107 description
="Choose a name for the category of the panel",
1112 def draw(self
, context
):
1113 layout
= self
.layout
1117 col
.label(text
="Tab Category:")
1118 col
.prop(self
, "category", text
="")
1124 OBJECT_OT_AlignOperator
,
1125 OBJECT_OT_AlignLocationOperator
,
1126 OBJECT_OT_AlignLocationXOperator
,
1127 OBJECT_OT_AlignLocationYOperator
,
1128 OBJECT_OT_AlignLocationZOperator
,
1129 OBJECT_OT_AlignRotationOperator
,
1130 OBJECT_OT_AlignRotationXOperator
,
1131 OBJECT_OT_AlignRotationYOperator
,
1132 OBJECT_OT_AlignRotationZOperator
,
1133 OBJECT_OT_AlignScaleOperator
,
1134 OBJECT_OT_AlignScaleXOperator
,
1135 OBJECT_OT_AlignScaleYOperator
,
1136 OBJECT_OT_AlignScaleZOperator
,
1137 OBJECT_OT_align_tools
,
1138 AlignAddonPreferences
,
1142 # Register all operators and panels
1145 bpy
.utils
.register_class(cls
)
1150 bpy
.utils
.unregister_class(cls
)
1153 if __name__
== "__main__":