1 # -*- coding: utf-8 -*-
2 # ##### BEGIN GPL LICENSE BLOCK #####
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # ##### END GPL LICENSE BLOCK #####
19 # Contributed to by gabhead, Lell, Anfeo, meta-androcto
22 "name": "Align Tools",
23 "author": "gabhead, Lell, Anfeo",
25 "blender": (2, 80, 0),
26 "location": "View3D > Sidebar > Item Tab",
27 "description": "Align Selected Objects to Active Object",
29 "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
30 "object/align_tools.html",
35 from bpy
.types
import (
40 from bpy
.props
import (
46 from mathutils
import (
56 for i
in bpy
.context
.selected_objects
:
57 i
.matrix_world
.translation
= bpy
.context
.active_object
.matrix_world
.translation
.copy()
58 i
.rotation_euler
= bpy
.context
.active_object
.rotation_euler
63 for i
in bpy
.context
.selected_objects
:
64 i
.matrix_world
.translation
= bpy
.context
.active_object
.matrix_world
.translation
.copy()
68 for i
in bpy
.context
.selected_objects
:
69 i
.matrix_world
.translation
.x
= bpy
.context
.active_object
.matrix_world
.translation
.x
73 for i
in bpy
.context
.selected_objects
:
74 i
.matrix_world
.translation
.y
= bpy
.context
.active_object
.matrix_world
.translation
.y
78 for i
in bpy
.context
.selected_objects
:
79 i
.matrix_world
.translation
.z
= bpy
.context
.active_object
.matrix_world
.translation
.z
84 for i
in bpy
.context
.selected_objects
:
85 i
.rotation_euler
= bpy
.context
.active_object
.rotation_euler
89 for i
in bpy
.context
.selected_objects
:
90 i
.rotation_euler
.x
= bpy
.context
.active_object
.rotation_euler
.x
94 for i
in bpy
.context
.selected_objects
:
95 i
.rotation_euler
.y
= bpy
.context
.active_object
.rotation_euler
.y
99 for i
in bpy
.context
.selected_objects
:
100 i
.rotation_euler
.z
= bpy
.context
.active_object
.rotation_euler
.z
104 def ScaleAll(context
):
105 for i
in bpy
.context
.selected_objects
:
106 i
.scale
= bpy
.context
.active_object
.scale
110 for i
in bpy
.context
.selected_objects
:
111 i
.scale
.x
= bpy
.context
.active_object
.scale
.x
115 for i
in bpy
.context
.selected_objects
:
116 i
.scale
.y
= bpy
.context
.active_object
.scale
.y
120 for i
in bpy
.context
.selected_objects
:
121 i
.scale
.z
= bpy
.context
.active_object
.scale
.z
124 # Advanced Align Defs #
126 # subject to object 0, 1 and 2 to pivot for cursor
127 def align_function(subject
, active_too
, consistent
, self_or_active
, loc_x
, loc_y
, loc_z
, ref1
, ref2
, loc_offset
,
128 rot_x
, rot_y
, rot_z
, rot_offset
, scale_x
, scale_y
, scale_z
, scale_offset
,
129 fit_x
, fit_y
, fit_z
):
131 sel_obj
= bpy
.context
.selected_objects
132 act_obj
= bpy
.context
.active_object
139 def get_reference_points(obj
, space
):
143 # let's get all the points coordinates
144 if space
== "global":
146 obj_mtx
= obj
.matrix_world
147 if obj
.type == 'MESH' and len(me
.vertices
) > 0:
149 for p
in me
.vertices
:
150 co_list
.append((obj_mtx
@ p
.co
))
152 elif obj
.type == 'SURFACE' and len(me
.splines
) > 0:
156 co_list
.append((obj_mtx
@ p
.co
))
157 elif obj
.type == 'FONT' and len(me
.splines
) > 0:
160 for p
in s
.bezier_points
:
161 co_list
.append((obj_mtx
@ p
.co
))
163 elif space
== "local":
165 if obj
.type == 'MESH' and len(me
.vertices
) > 0:
167 for p
in me
.vertices
:
170 elif obj
.type == 'SURFACE' and len(me
.splines
) > 0:
175 elif obj
.type == 'FONT' and len(obj
.data
.splines
) > 0:
178 for p
in s
.bezier_points
:
181 # if a valid point found
182 # proceed to calculate the extremes
184 max_x
= co_list
[0][0]
185 min_x
= co_list
[0][0]
186 max_y
= co_list
[0][1]
187 min_y
= co_list
[0][1]
188 max_z
= co_list
[0][2]
189 min_z
= co_list
[0][2]
192 # the strings of the list compared with the smaller and more found
193 # in order to find the minor and major for each axis
213 # otherwise use the pivot object
214 a
= obj
.matrix_world
.translation
222 center_x
= min_x
+ ((max_x
- min_x
) / 2)
223 center_y
= min_y
+ ((max_y
- min_y
) / 2)
224 center_z
= min_z
+ ((max_z
- min_z
) / 2)
226 reference_points
= [min_x
, center_x
, max_x
, min_y
, center_y
, max_y
, min_z
, center_z
, max_z
]
227 return reference_points
229 def get_sel_ref(ref_co
, sel_obj
): # I look for the selection end points
231 sel_min
= ref_co
.copy()
232 sel_max
= ref_co
.copy()
235 if obj
!= act_obj
or (active_too
and obj
== act_obj
):
237 ref_points
= get_reference_points(obj
, "global")
238 ref_min
= Vector([ref_points
[0], ref_points
[3], ref_points
[6]])
239 ref_max
= Vector([ref_points
[2], ref_points
[5], ref_points
[8]])
241 if ref_min
[0] < sel_min
[0]:
242 sel_min
[0] = ref_min
[0]
243 if ref_max
[0] > sel_max
[0]:
244 sel_max
[0] = ref_max
[0]
245 if ref_min
[1] < sel_min
[1]:
246 sel_min
[1] = ref_min
[1]
247 if ref_max
[1] > sel_max
[1]:
248 sel_max
[1] = ref_max
[1]
249 if ref_min
[2] < sel_min
[2]:
250 sel_min
[2] = ref_min
[2]
251 if ref_max
[2] > sel_max
[2]:
252 sel_max
[2] = ref_max
[2]
254 return sel_min
, sel_max
256 def find_ref2_co(act_obj
):
257 # It contains the coordinates of the reference point for the positioning
259 ref_points
= get_reference_points(act_obj
, "global")
260 ref2_co
= [ref_points
[0], ref_points
[3], ref_points
[6]]
261 ref2_co
= Vector(ref2_co
)
263 ref_points
= get_reference_points(act_obj
, "global")
264 ref2_co
= [ref_points
[1], ref_points
[4], ref_points
[7]]
265 ref2_co
= Vector(ref2_co
)
267 ref2_co
= act_obj
.location
268 ref2_co
= Vector(ref2_co
)
270 ref_points
= get_reference_points(act_obj
, "global")
271 ref2_co
= [ref_points
[2], ref_points
[5], ref_points
[8]]
272 ref2_co
= Vector(ref2_co
)
274 ref2_co
= bpy
.context
.scene
.cursor
.location
278 def find_new_coord(obj
):
280 ref_points
= get_reference_points(obj
, "global")
284 min_x
= ref_points
[0]
285 new_x
= ref2_co
[0] + (obj
.location
[0] - min_x
) + loc_offset
[0]
287 center_x
= ref_points
[1]
288 new_x
= ref2_co
[0] + (obj
.location
[0] - center_x
) + loc_offset
[0]
290 new_x
= ref2_co
[0] + loc_offset
[0]
292 max_x
= ref_points
[2]
293 new_x
= ref2_co
[0] - (max_x
- obj
.location
[0]) + loc_offset
[0]
294 obj
.matrix_world
.translation
[0] = new_x
297 min_y
= ref_points
[3]
298 new_y
= ref2_co
[1] + (obj
.location
[1] - min_y
) + loc_offset
[1]
300 center_y
= ref_points
[4]
301 new_y
= ref2_co
[1] + (obj
.location
[1] - center_y
) + loc_offset
[1]
303 new_y
= ref2_co
[1] + loc_offset
[1]
305 max_y
= ref_points
[5]
306 new_y
= ref2_co
[1] - (max_y
- obj
.location
[1]) + loc_offset
[1]
307 obj
.matrix_world
.translation
[1] = new_y
310 min_z
= ref_points
[6]
311 new_z
= ref2_co
[2] + (obj
.location
[2] - min_z
) + loc_offset
[2]
313 center_z
= ref_points
[7]
314 new_z
= ref2_co
[2] + (obj
.location
[2] - center_z
) + loc_offset
[2]
316 new_z
= ref2_co
[2] + loc_offset
[2]
318 max_z
= ref_points
[8]
319 new_z
= ref2_co
[2] - (max_z
- obj
.location
[2]) + loc_offset
[2]
320 obj
.matrix_world
.translation
[2] = new_z
322 def find_new_rotation(obj
):
324 obj
.rotation_euler
[0] = act_obj
.rotation_euler
[0] + rot_offset
[0]
326 obj
.rotation_euler
[1] = act_obj
.rotation_euler
[1] + rot_offset
[1]
328 obj
.rotation_euler
[2] = act_obj
.rotation_euler
[2] + rot_offset
[2]
330 def find_new_scale(obj
):
332 obj
.scale
[0] = act_obj
.scale
[0] + scale_offset
[0]
334 obj
.scale
[1] = act_obj
.scale
[1] + scale_offset
[1]
336 obj
.scale
[2] = act_obj
.scale
[2] + scale_offset
[2]
338 def find_new_dimensions(obj
, ref_dim
):
339 ref_points
= get_reference_points(obj
, "local")
341 dim
= ref_points
[2] - ref_points
[0]
342 obj
.scale
[0] = (ref_dim
[0] / dim
) * act_obj
.scale
[0]
344 dim
= ref_points
[5] - ref_points
[3]
345 obj
.scale
[1] = (ref_dim
[1] / dim
) * act_obj
.scale
[1]
347 dim
= ref_points
[8] - ref_points
[6]
348 obj
.scale
[2] = (ref_dim
[2] / dim
) * act_obj
.scale
[2]
352 vec_ref2_co
= Vector(ref2_co
)
353 offset
= vec_ref2_co
- obj
.location
354 offset_x
= [offset
[0] + loc_offset
[0], 0, 0]
355 offset_y
= [0, offset
[1] + loc_offset
[1], 0]
356 offset_z
= [0, 0, offset
[2] + loc_offset
[2]]
359 obj_mtx
= obj
.matrix_world
.copy()
360 # What's the displacement vector for the pivot?
361 move_pivot
= Vector(vec
)
363 # Move the pivot point (which is the object's location)
367 nm
= obj_mtx
.inverted() @ Matrix
.Translation(-move_pivot
) @ obj_mtx
369 # Transform the mesh now
379 def point_in_selection(act_obj
, sel_obj
):
384 obj_mtx
= o
.matrix_world
385 if o
.type == 'MESH' and len(o
.data
.vertices
) > 0:
386 ref_co
= o
.data
.vertices
[0].co
.copy()
387 ref_co
= obj_mtx
@ ref_co
390 elif o
.type == 'CURVE' and len(o
.data
.splines
) > 0:
391 ref_co
= o
.data
.splines
[0].bezier_point
[0].co
.copy()
392 ref_co
= obj_mtx
@ ref_co
395 elif o
.type == 'SURFACE' and len(o
.data
.splines
) > 0:
396 ref_co
= o
.data
.splines
[0].points
[0].co
.copy()
397 ref_co
= obj_mtx
@ ref_co
400 elif o
.type == 'FONT' and len(o
.data
.splines
) > 0:
401 ref_co
= o
.data
.splines
[0].bezier_points
[0].co
.copy()
402 ref_co
= obj_mtx
@ ref_co
405 # if no object had data, use the position of an object that was not active as an internal
408 ref_co
= ref_ob
.matrix_world
.translation
413 # if act_obj.type == ('MESH' or 'FONT' or 'CURVE' or 'SURFACE'):
414 if act_obj
.type == 'MESH' or act_obj
.type == 'FONT' or act_obj
.type == 'SURFACE':
415 ref2_co
= find_ref2_co(act_obj
)
418 ref2_co
= bpy
.context
.scene
.cursor
.location
420 ref2_co
= act_obj
.matrix_world
.translation
422 # in the case of substantial selection
424 # I am seeking a point that is in the selection space
425 ref_co
= point_in_selection(act_obj
, sel_obj
)
427 sel_min
, sel_max
= get_sel_ref(ref_co
, sel_obj
)
429 sel_center
= sel_min
+ ((sel_max
- sel_min
) / 2)
430 translate
= [0, 0, 0]
432 # calculating how much to move the selection
434 translate
= ref2_co
- sel_min
+ loc_offset
436 translate
= ref2_co
- sel_center
+ loc_offset
438 translate
= ref2_co
- sel_max
+ loc_offset
440 # Move the various objects
443 if obj
!= act_obj
or (active_too
and obj
== act_obj
):
446 obj
.location
[0] += translate
[0]
448 obj
.location
[1] += translate
[1]
450 obj
.location
[2] += translate
[2]
451 else: # not consistent
454 if rot_x
or rot_y
or rot_z
:
455 find_new_rotation(obj
)
457 if fit_x
or fit_y
or fit_z
:
459 ref_points
= get_reference_points(act_obj
, "local")
460 dim
[0] = ref_points
[2] - ref_points
[0]
461 dim
[1] = ref_points
[5] - ref_points
[3]
462 dim
[2] = ref_points
[8] - ref_points
[6]
463 find_new_dimensions(obj
, dim
)
465 if scale_x
or scale_y
or scale_z
:
468 if loc_x
or loc_y
or loc_z
:
469 # print("ehy", ref2_co)
472 if active_too
is True:
473 if loc_x
or loc_y
or loc_z
:
474 find_new_coord(act_obj
)
475 if rot_x
or rot_y
or rot_z
:
476 find_new_rotation(act_obj
)
477 if scale_x
or scale_y
or scale_z
:
478 find_new_scale(act_obj
)
479 # add dimensions if dim offset will be added
482 if self_or_active
== "1":
483 if act_obj
.type == 'MESH':
484 ref2_co
= find_ref2_co(act_obj
)
486 if self_or_active
== "0":
487 ref2_co
= find_ref2_co(obj
)
488 if loc_x
or loc_y
or loc_z
:
489 if obj
!= act_obj
and obj
.type == 'MESH':
492 if active_too
is True:
493 if act_obj
.type == 'MESH':
494 if loc_x
or loc_y
or loc_z
:
495 if self_or_active
== "0":
496 ref2_co
= find_ref2_co(act_obj
)
500 if self_or_active
== "1":
501 if act_obj
.type == 'MESH' or act_obj
.type == 'FONT' or act_obj
.type == 'SURFACE':
502 ref2_co
= find_ref2_co(act_obj
)
503 ref_points
= get_reference_points(act_obj
, "global")
505 ref2_co
= act_obj
.matrix_world
.translation
506 ref_points
= [ref2_co
[0], ref2_co
[0], ref2_co
[0],
507 ref2_co
[1], ref2_co
[1], ref2_co
[1],
508 ref2_co
[2], ref2_co
[2], ref2_co
[2]]
512 bpy
.context
.scene
.cursor
.location
[0] = ref_points
[0] + loc_offset
[0]
514 bpy
.context
.scene
.cursor
.location
[1] = ref_points
[3] + loc_offset
[1]
516 bpy
.context
.scene
.cursor
.location
[2] = ref_points
[6] + loc_offset
[2]
519 bpy
.context
.scene
.cursor
.location
[0] = ref_points
[1] + loc_offset
[0]
521 bpy
.context
.scene
.cursor
.location
[1] = ref_points
[4] + loc_offset
[1]
523 bpy
.context
.scene
.cursor
.location
[2] = ref_points
[7] + loc_offset
[2]
526 bpy
.context
.scene
.cursor
.location
[0] = act_obj
.location
[0] + loc_offset
[0]
528 bpy
.context
.scene
.cursor
.location
[1] = act_obj
.location
[1] + loc_offset
[1]
530 bpy
.context
.scene
.cursor
.location
[2] = act_obj
.location
[2] + loc_offset
[2]
533 bpy
.context
.scene
.cursor
.location
[0] = ref_points
[2] + loc_offset
[0]
535 bpy
.context
.scene
.cursor
.location
[1] = ref_points
[5] + loc_offset
[1]
537 bpy
.context
.scene
.cursor
.location
[2] = ref_points
[8] + loc_offset
[2]
538 elif self_or_active
== "2":
539 ref_co
= point_in_selection(act_obj
, sel_obj
)
541 sel_min
, sel_max
= get_sel_ref(ref_co
, sel_obj
)
542 sel_center
= sel_min
+ ((sel_max
- sel_min
) / 2)
546 bpy
.context
.scene
.cursor
.location
[0] = sel_min
[0] + loc_offset
[0]
548 bpy
.context
.scene
.cursor
.location
[1] = sel_min
[1] + loc_offset
[1]
550 bpy
.context
.scene
.cursor
.location
[2] = sel_min
[2] + loc_offset
[2]
553 bpy
.context
.scene
.cursor
.location
[0] = sel_center
[0] + loc_offset
[0]
555 bpy
.context
.scene
.cursor
.location
[1] = sel_center
[1] + loc_offset
[1]
557 bpy
.context
.scene
.cursor
.location
[2] = sel_center
[2] + loc_offset
[2]
560 bpy
.context
.scene
.cursor
.location
[0] = sel_max
[0] + loc_offset
[0]
562 bpy
.context
.scene
.cursor
.location
[1] = sel_max
[1] + loc_offset
[1]
564 bpy
.context
.scene
.cursor
.location
[2] = sel_max
[2] + loc_offset
[2]
570 class OBJECT_OT_align_tools(Operator
):
571 bl_idname
= "object.align_tools"
572 bl_label
= "Align Operator"
573 bl_description
= "Align Object Tools"
574 bl_options
= {'REGISTER', 'UNDO'}
576 # property definitions
578 # Object-Pivot-Cursor:
579 subject
: EnumProperty(
580 items
=(("0", "Object", "Align Objects"),
581 ("1", "Pivot", "Align Objects Pivot"),
582 ("2", "Cursor", "Align Cursor To Active")),
584 description
="What will be moved"
587 active_too
: BoolProperty(
590 description
="Move the active object too"
593 advanced
: BoolProperty(
594 name
="Advanced Options",
596 description
="Show advanced options"
598 consistent
: BoolProperty(
599 name
="Consistent Selection",
601 description
="Use consistent selection"
605 name
="Align to X axis",
607 description
="Enable X axis alignment"
610 name
="Align to Y axis",
612 description
="Enable Y axis alignment"
615 name
="Align to Z axis",
617 description
="Enable Z axis alignment"
621 items
=(("3", "Max", "Align the maximum point"),
622 ("1", "Center", "Align the center point"),
623 ("2", "Pivot", "Align the pivot"),
624 ("0", "Min", "Align the minimum point")),
625 name
="Selection reference",
626 description
="Moved objects reference point"
628 # Active Object Option:
630 items
=(("3", "Max", "Align to the maximum point"),
631 ("1", "Center", "Align to the center point"),
632 ("2", "Pivot", "Align to the pivot"),
633 ("0", "Min", "Align to the minimum point"),
634 ("4", "Cursor", "Description")),
635 name
="Active reference",
636 description
="Destination point"
638 self_or_active
: EnumProperty(
639 items
=(("0", "Self", "In relation of itself"),
640 ("1", "Active", "In relation of the active object"),
641 ("2", "Selection", "In relation of the entire selection")),
644 description
="To what the pivot will be aligned"
647 loc_offset
: FloatVectorProperty(
648 name
="Location Offset",
649 description
="Offset for location align position",
650 default
=(0.0, 0.0, 0.0),
651 subtype
='XYZ', size
=3
654 rot_offset
: FloatVectorProperty(
655 name
="Rotation Offset",
656 description
="Offset for rotation alignment",
657 default
=(0.0, 0.0, 0.0),
658 subtype
='EULER', size
=3
661 scale_offset
: FloatVectorProperty(
663 description
="Offset for scale match",
664 default
=(0.0, 0.0, 0.0),
665 subtype
='XYZ', size
=3
667 # Fit Dimension Prop:
669 name
="Fit Dimension to X axis",
674 name
="Fit Dimension to Y axis",
679 name
="Fit Dimension to Z axis",
683 # Apply Fit Dimension:
684 apply_dim
: BoolProperty(
685 name
="Apply Dimension",
691 name
="Align Rotation to X axis",
696 name
="Align Rotation to Y axis",
701 name
="Align Rotation to Z axis",
706 apply_rot
: BoolProperty(
707 name
="Apply Rotation",
712 scale_x
: BoolProperty(
713 name
="Match Scale to X axis",
717 scale_y
: BoolProperty(
718 name
="Match Scale to Y axis",
722 scale_z
: BoolProperty(
723 name
="match Scale to Z axis",
728 apply_scale
: BoolProperty(
734 def draw(self
, context
):
738 row
.label(text
="Active object is: ", icon
='OBJECT_DATA')
740 box
.label(text
=obj
.name
, icon
='EDITMODE_HLT')
741 # Object-Pivot-Cursor:
743 row0
.prop(self
, 'subject', expand
=True)
747 row1
.prop(self
, 'active_too')
748 row1
.prop(self
, 'advanced')
751 row1b
.prop(self
, 'consistent')
754 row2
.label(text
="Align Location:")
758 row3
.prop(self
, "loc_x", text
="X", toggle
=True)
759 row3
.prop(self
, "loc_y", text
="Y", toggle
=True)
760 row3
.prop(self
, "loc_z", text
="Z", toggle
=True)
763 if self
.advanced
is True:
765 # row8.label(text='Location Offset')
767 row9
.prop(self
, 'loc_offset', text
='')
770 if self
.advanced
is True:
771 sel
= bpy
.context
.selected_objects
775 row4
.label(text
="Selected: " + str(sel_obs
) + " Objects", icon
='OBJECT_DATA')
776 if self
.subject
== "1" or self
.subject
== "2":
778 row5b
.prop(self
, 'self_or_active', expand
=True)
781 row5
.prop(self
, 'ref1', expand
=True)
783 # Active Object Options: Number of select objects
784 act
= bpy
.context
.active_object
786 if self
.advanced
is True:
789 row6
.label(text
="Active: " + act
.name
, icon
='OBJECT_DATA')
791 row7
.prop(self
, 'ref2', expand
=True)
793 if self
.subject
== "0":
795 row12
.label(text
='Align Rotation:')
796 row13
= layout
.row(align
=True)
797 row13
.prop(self
, 'rot_x', text
='X', toggle
=True)
798 row13
.prop(self
, 'rot_y', text
='Y', toggle
=True)
799 row13
.prop(self
, 'rot_z', text
='Z', toggle
=True)
800 row13
.prop(self
, 'apply_rot', text
='Apply', toggle
=True)
801 if self
.advanced
is True:
802 row13b
= layout
.row()
803 row13b
.prop(self
, 'rot_offset', text
='')
806 row14
.label(text
='Match Scale:')
807 row15
= layout
.row(align
=True)
808 row15
.prop(self
, 'scale_x', text
='X', toggle
=True)
809 row15
.prop(self
, 'scale_y', text
='Y', toggle
=True)
810 row15
.prop(self
, 'scale_z', text
='Z', toggle
=True)
811 row15
.prop(self
, 'apply_scale', text
='Apply', toggle
=True)
812 if self
.advanced
is True:
813 row15b
= layout
.row()
814 row15b
.prop(self
, 'scale_offset', text
='')
817 row10
.label(text
='Fit Dimensions:')
818 row11
= layout
.row(align
=True)
819 row11
.prop(self
, 'fit_x', text
='X', toggle
=True)
820 row11
.prop(self
, 'fit_y', text
='Y', toggle
=True)
821 row11
.prop(self
, 'fit_z', text
='Z', toggle
=True)
822 row11
.prop(self
, 'apply_dim', text
='Apply', toggle
=True)
824 def execute(self
, context
):
826 self
.subject
, self
.active_too
, self
.consistent
,
827 self
.self_or_active
, self
.loc_x
, self
.loc_y
, self
.loc_z
,
828 self
.ref1
, self
.ref2
, self
.loc_offset
,
829 self
.rot_x
, self
.rot_y
, self
.rot_z
, self
.rot_offset
,
830 self
.scale_x
, self
.scale_y
, self
.scale_z
, self
.scale_offset
,
831 self
.fit_x
, self
.fit_y
, self
.fit_z
837 # Simple Align Classes #
839 # Align All Rotation And Location
840 class OBJECT_OT_AlignOperator(Operator
):
841 bl_idname
= "object.align"
842 bl_label
= "Align Selected To Active"
843 bl_description
= "Align Selected To Active"
846 def poll(cls
, context
):
847 return context
.active_object
is not None
849 def execute(self
, context
):
855 class OBJECT_OT_AlignLocationOperator(Operator
):
856 bl_idname
= "object.align_location_all"
857 bl_label
= "Align Selected Location To Active"
858 bl_description
= "Align Selected Location To Active"
861 def poll(cls
, context
):
862 return context
.active_object
is not None
864 def execute(self
, context
):
870 class OBJECT_OT_AlignLocationXOperator(Operator
):
871 bl_idname
= "object.align_location_x"
872 bl_label
= "Align Selected Location X To Active"
873 bl_description
= "Align Selected Location X To Active"
876 def poll(cls
, context
):
877 return context
.active_object
is not None
879 def execute(self
, context
):
885 class OBJECT_OT_AlignLocationYOperator(Operator
):
886 bl_idname
= "object.align_location_y"
887 bl_label
= "Align Selected Location Y To Active"
888 bl_description
= "Align Selected Location Y To Active"
891 def poll(cls
, context
):
892 return context
.active_object
is not None
894 def execute(self
, context
):
900 class OBJECT_OT_AlignLocationZOperator(Operator
):
901 bl_idname
= "object.align_location_z"
902 bl_label
= "Align Selected Location Z To Active"
903 bl_description
= "Align Selected Location Z To Active"
906 def poll(cls
, context
):
907 return context
.active_object
is not None
909 def execute(self
, context
):
915 class OBJECT_OT_AlignRotationOperator(Operator
):
916 bl_idname
= "object.align_rotation_all"
917 bl_label
= "Align Selected Rotation To Active"
918 bl_description
= "Align Selected Rotation To Active"
921 def poll(cls
, context
):
922 return context
.active_object
is not None
924 def execute(self
, context
):
930 class OBJECT_OT_AlignRotationXOperator(Operator
):
931 bl_idname
= "object.align_rotation_x"
932 bl_label
= "Align Selected Rotation X To Active"
933 bl_description
= "Align Selected Rotation X To Active"
936 def poll(cls
, context
):
937 return context
.active_object
is not None
939 def execute(self
, context
):
945 class OBJECT_OT_AlignRotationYOperator(Operator
):
946 bl_idname
= "object.align_rotation_y"
947 bl_label
= "Align Selected Rotation Y To Active"
948 bl_description
= "Align Selected Rotation Y To Active"
951 def poll(cls
, context
):
952 return context
.active_object
is not None
954 def execute(self
, context
):
960 class OBJECT_OT_AlignRotationZOperator(Operator
):
961 bl_idname
= "object.align_rotation_z"
962 bl_label
= "Align Selected Rotation Z To Active"
963 bl_description
= "Align Selected Rotation Z To Active"
966 def poll(cls
, context
):
967 return context
.active_object
is not None
969 def execute(self
, context
):
975 class OBJECT_OT_AlignScaleOperator(Operator
):
976 bl_idname
= "object.align_objects_scale_all"
977 bl_label
= "Align Selected Scale To Active"
978 bl_description
= "Align Selected Scale To Active"
981 def poll(cls
, context
):
982 return context
.active_object
is not None
984 def execute(self
, context
):
990 class OBJECT_OT_AlignScaleXOperator(Operator
):
991 bl_idname
= "object.align_objects_scale_x"
992 bl_label
= "Align Selected Scale X To Active"
993 bl_description
= "Align Selected Scale X To Active"
996 def poll(cls
, context
):
997 return context
.active_object
is not None
999 def execute(self
, context
):
1005 class OBJECT_OT_AlignScaleYOperator(Operator
):
1006 bl_idname
= "object.align_objects_scale_y"
1007 bl_label
= "Align Selected Scale Y To Active"
1008 bl_description
= "Align Selected Scale Y To Active"
1011 def poll(cls
, context
):
1012 return context
.active_object
is not None
1014 def execute(self
, context
):
1020 class OBJECT_OT_AlignScaleZOperator(Operator
):
1021 bl_idname
= "object.align_objects_scale_z"
1022 bl_label
= "Align Selected Scale Z To Active"
1023 bl_description
= "Align Selected Scale Z To Active"
1026 def poll(cls
, context
):
1027 return context
.active_object
is not None
1029 def execute(self
, context
):
1036 class VIEW3D_PT_AlignUi(Panel
):
1037 bl_space_type
= 'VIEW_3D'
1038 bl_region_type
= 'UI'
1039 bl_label
= "Align Tools"
1040 bl_context
= "objectmode"
1041 bl_category
= 'Item'
1042 bl_options
= {'DEFAULT_CLOSED'}
1044 def draw(self
, context
):
1045 layout
= self
.layout
1046 obj
= context
.object
1050 row
.label(text
="Active object is: ", icon
='OBJECT_DATA')
1052 box
.label(text
=obj
.name
, icon
='EDITMODE_HLT')
1054 col
= layout
.column()
1055 col
.label(text
="Align Loc + Rot:")
1057 col
= layout
.column(align
=False)
1058 col
.operator("object.align", text
="XYZ")
1060 col
= layout
.column()
1061 col
.label(text
="Align Location:")
1063 col
= layout
.column_flow(columns
=4, align
=True)
1064 col
.operator("object.align_location_x", text
="X")
1065 col
.operator("object.align_location_y", text
="Y")
1066 col
.operator("object.align_location_z", text
="Z")
1067 col
.operator("object.align_location_all", text
="All")
1069 col
= layout
.column()
1070 col
.label(text
="Align Rotation:")
1072 col
= layout
.column_flow(columns
=4, align
=True)
1073 col
.operator("object.align_rotation_x", text
="X")
1074 col
.operator("object.align_rotation_y", text
="Y")
1075 col
.operator("object.align_rotation_z", text
="Z")
1076 col
.operator("object.align_rotation_all", text
="All")
1078 col
= layout
.column()
1079 col
.label(text
="Align Scale:")
1081 col
= layout
.column_flow(columns
=4, align
=True)
1082 col
.operator("object.align_objects_scale_x", text
="X")
1083 col
.operator("object.align_objects_scale_y", text
="Y")
1084 col
.operator("object.align_objects_scale_z", text
="Z")
1085 col
.operator("object.align_objects_scale_all", text
="All")
1088 col
= layout
.column()
1089 col
.label(text
="Advanced Align Operations")
1090 layout
= self
.layout
1091 self
.layout
.operator("object.align_tools", text
="Advanced")
1094 # Add-ons Preferences Update Panel
1096 # Define Panel classes for updating
1102 def update_panel(self
, context
):
1103 message
= "Align Tools: Updating Panel locations has failed"
1105 for panel
in panels
:
1106 if "bl_rna" in panel
.__dict
__:
1107 bpy
.utils
.unregister_class(panel
)
1109 for panel
in panels
:
1110 panel
.bl_category
= context
.preferences
.addons
[__name__
].preferences
.category
1111 bpy
.utils
.register_class(panel
)
1113 except Exception as e
:
1114 print("\n[{}]\n{}\n\nError:\n{}".format(__name__
, message
, e
))
1118 class AlignAddonPreferences(AddonPreferences
):
1119 # this must match the addon name, use '__package__'
1120 # when defining this in a submodule of a python package.
1121 bl_idname
= __name__
1123 category
: StringProperty(
1124 name
="Tab Category",
1125 description
="Choose a name for the category of the panel",
1130 def draw(self
, context
):
1131 layout
= self
.layout
1135 col
.label(text
="Tab Category:")
1136 col
.prop(self
, "category", text
="")
1142 OBJECT_OT_AlignOperator
,
1143 OBJECT_OT_AlignLocationOperator
,
1144 OBJECT_OT_AlignLocationXOperator
,
1145 OBJECT_OT_AlignLocationYOperator
,
1146 OBJECT_OT_AlignLocationZOperator
,
1147 OBJECT_OT_AlignRotationOperator
,
1148 OBJECT_OT_AlignRotationXOperator
,
1149 OBJECT_OT_AlignRotationYOperator
,
1150 OBJECT_OT_AlignRotationZOperator
,
1151 OBJECT_OT_AlignScaleOperator
,
1152 OBJECT_OT_AlignScaleXOperator
,
1153 OBJECT_OT_AlignScaleYOperator
,
1154 OBJECT_OT_AlignScaleZOperator
,
1155 OBJECT_OT_align_tools
,
1156 AlignAddonPreferences
,
1160 # Register all operators and panels
1163 bpy
.utils
.register_class(cls
)
1168 bpy
.utils
.unregister_class(cls
)
1171 if __name__
== "__main__":