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://wiki.blender.org/index.php/Extensions:2.6/Py/"
30 "Scripts/3D interaction/Align_Tools",
31 "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",
32 "category": "3D View",
36 from bpy
.types
import (
41 from bpy
.props
import (
47 from mathutils
import (
57 for i
in bpy
.context
.selected_objects
:
58 i
.matrix_world
.translation
= bpy
.context
.active_object
.matrix_world
.translation
.copy()
59 i
.rotation_euler
= bpy
.context
.active_object
.rotation_euler
64 for i
in bpy
.context
.selected_objects
:
65 i
.matrix_world
.translation
= bpy
.context
.active_object
.matrix_world
.translation
.copy()
69 for i
in bpy
.context
.selected_objects
:
70 i
.matrix_world
.translation
.x
= bpy
.context
.active_object
.matrix_world
.translation
.x
74 for i
in bpy
.context
.selected_objects
:
75 i
.matrix_world
.translation
.y
= bpy
.context
.active_object
.matrix_world
.translation
.y
79 for i
in bpy
.context
.selected_objects
:
80 i
.matrix_world
.translation
.z
= bpy
.context
.active_object
.matrix_world
.translation
.z
85 for i
in bpy
.context
.selected_objects
:
86 i
.rotation_euler
= bpy
.context
.active_object
.rotation_euler
90 for i
in bpy
.context
.selected_objects
:
91 i
.rotation_euler
.x
= bpy
.context
.active_object
.rotation_euler
.x
95 for i
in bpy
.context
.selected_objects
:
96 i
.rotation_euler
.y
= bpy
.context
.active_object
.rotation_euler
.y
100 for i
in bpy
.context
.selected_objects
:
101 i
.rotation_euler
.z
= bpy
.context
.active_object
.rotation_euler
.z
105 def ScaleAll(context
):
106 for i
in bpy
.context
.selected_objects
:
107 i
.scale
= bpy
.context
.active_object
.scale
111 for i
in bpy
.context
.selected_objects
:
112 i
.scale
.x
= bpy
.context
.active_object
.scale
.x
116 for i
in bpy
.context
.selected_objects
:
117 i
.scale
.y
= bpy
.context
.active_object
.scale
.y
121 for i
in bpy
.context
.selected_objects
:
122 i
.scale
.z
= bpy
.context
.active_object
.scale
.z
125 # Advanced Align Defs #
127 # subject to object 0, 1 and 2 to pivot for cursor
128 def align_function(subject
, active_too
, consistent
, self_or_active
, loc_x
, loc_y
, loc_z
, ref1
, ref2
, loc_offset
,
129 rot_x
, rot_y
, rot_z
, rot_offset
, scale_x
, scale_y
, scale_z
, scale_offset
,
130 fit_x
, fit_y
, fit_z
):
132 sel_obj
= bpy
.context
.selected_objects
133 act_obj
= bpy
.context
.active_object
140 def get_reference_points(obj
, space
):
144 # let's get all the points coodinates
145 if space
== "global":
147 obj_mtx
= obj
.matrix_world
148 if obj
.type == 'MESH' and len(me
.vertices
) > 0:
150 for p
in me
.vertices
:
151 co_list
.append((obj_mtx
@ p
.co
))
153 elif obj
.type == 'SURFACE' and len(me
.splines
) > 0:
157 co_list
.append((obj_mtx
@ p
.co
))
158 elif obj
.type == 'FONT' and len(me
.splines
) > 0:
161 for p
in s
.bezier_points
:
162 co_list
.append((obj_mtx
@ p
.co
))
164 elif space
== "local":
166 if obj
.type == 'MESH' and len(me
.vertices
) > 0:
168 for p
in me
.vertices
:
171 elif obj
.type == 'SURFACE' and len(me
.splines
) > 0:
176 elif obj
.type == 'FONT' and len(obj
.data
.splines
) > 0:
179 for p
in s
.bezier_points
:
182 # if a valid point found
183 # proceed to calculate the extremes
185 max_x
= co_list
[0][0]
186 min_x
= co_list
[0][0]
187 max_y
= co_list
[0][1]
188 min_y
= co_list
[0][1]
189 max_z
= co_list
[0][2]
190 min_z
= co_list
[0][2]
193 # the strings of the list compared with the smaller and more found
194 # in order to find the minor and major for each axis
214 # otherwise use the pivot object
215 a
= obj
.matrix_world
.translation
223 center_x
= min_x
+ ((max_x
- min_x
) / 2)
224 center_y
= min_y
+ ((max_y
- min_y
) / 2)
225 center_z
= min_z
+ ((max_z
- min_z
) / 2)
227 reference_points
= [min_x
, center_x
, max_x
, min_y
, center_y
, max_y
, min_z
, center_z
, max_z
]
228 return reference_points
230 def get_sel_ref(ref_co
, sel_obj
): # I look for the selection end points
232 sel_min
= ref_co
.copy()
233 sel_max
= ref_co
.copy()
236 if obj
!= act_obj
or (active_too
and obj
== act_obj
):
238 ref_points
= get_reference_points(obj
, "global")
239 ref_min
= Vector([ref_points
[0], ref_points
[3], ref_points
[6]])
240 ref_max
= Vector([ref_points
[2], ref_points
[5], ref_points
[8]])
242 if ref_min
[0] < sel_min
[0]:
243 sel_min
[0] = ref_min
[0]
244 if ref_max
[0] > sel_max
[0]:
245 sel_max
[0] = ref_max
[0]
246 if ref_min
[1] < sel_min
[1]:
247 sel_min
[1] = ref_min
[1]
248 if ref_max
[1] > sel_max
[1]:
249 sel_max
[1] = ref_max
[1]
250 if ref_min
[2] < sel_min
[2]:
251 sel_min
[2] = ref_min
[2]
252 if ref_max
[2] > sel_max
[2]:
253 sel_max
[2] = ref_max
[2]
255 return sel_min
, sel_max
257 def find_ref2_co(act_obj
):
258 # It contains the coordinates of the reference point for the positioning
260 ref_points
= get_reference_points(act_obj
, "global")
261 ref2_co
= [ref_points
[0], ref_points
[3], ref_points
[6]]
262 ref2_co
= Vector(ref2_co
)
264 ref_points
= get_reference_points(act_obj
, "global")
265 ref2_co
= [ref_points
[1], ref_points
[4], ref_points
[7]]
266 ref2_co
= Vector(ref2_co
)
268 ref2_co
= act_obj
.location
269 ref2_co
= Vector(ref2_co
)
271 ref_points
= get_reference_points(act_obj
, "global")
272 ref2_co
= [ref_points
[2], ref_points
[5], ref_points
[8]]
273 ref2_co
= Vector(ref2_co
)
275 ref2_co
= bpy
.context
.scene
.cursor
.location
279 def find_new_coord(obj
):
281 ref_points
= get_reference_points(obj
, "global")
285 min_x
= ref_points
[0]
286 new_x
= ref2_co
[0] + (obj
.location
[0] - min_x
) + loc_offset
[0]
288 center_x
= ref_points
[1]
289 new_x
= ref2_co
[0] + (obj
.location
[0] - center_x
) + loc_offset
[0]
291 new_x
= ref2_co
[0] + loc_offset
[0]
293 max_x
= ref_points
[2]
294 new_x
= ref2_co
[0] - (max_x
- obj
.location
[0]) + loc_offset
[0]
295 obj
.matrix_world
.translation
[0] = new_x
298 min_y
= ref_points
[3]
299 new_y
= ref2_co
[1] + (obj
.location
[1] - min_y
) + loc_offset
[1]
301 center_y
= ref_points
[4]
302 new_y
= ref2_co
[1] + (obj
.location
[1] - center_y
) + loc_offset
[1]
304 new_y
= ref2_co
[1] + loc_offset
[1]
306 max_y
= ref_points
[5]
307 new_y
= ref2_co
[1] - (max_y
- obj
.location
[1]) + loc_offset
[1]
308 obj
.matrix_world
.translation
[1] = new_y
311 min_z
= ref_points
[6]
312 new_z
= ref2_co
[2] + (obj
.location
[2] - min_z
) + loc_offset
[2]
314 center_z
= ref_points
[7]
315 new_z
= ref2_co
[2] + (obj
.location
[2] - center_z
) + loc_offset
[2]
317 new_z
= ref2_co
[2] + loc_offset
[2]
319 max_z
= ref_points
[8]
320 new_z
= ref2_co
[2] - (max_z
- obj
.location
[2]) + loc_offset
[2]
321 obj
.matrix_world
.translation
[2] = new_z
323 def find_new_rotation(obj
):
325 obj
.rotation_euler
[0] = act_obj
.rotation_euler
[0] + rot_offset
[0]
327 obj
.rotation_euler
[1] = act_obj
.rotation_euler
[1] + rot_offset
[1]
329 obj
.rotation_euler
[2] = act_obj
.rotation_euler
[2] + rot_offset
[2]
331 def find_new_scale(obj
):
333 obj
.scale
[0] = act_obj
.scale
[0] + scale_offset
[0]
335 obj
.scale
[1] = act_obj
.scale
[1] + scale_offset
[1]
337 obj
.scale
[2] = act_obj
.scale
[2] + scale_offset
[2]
339 def find_new_dimensions(obj
, ref_dim
):
340 ref_points
= get_reference_points(obj
, "local")
342 dim
= ref_points
[2] - ref_points
[0]
343 obj
.scale
[0] = (ref_dim
[0] / dim
) * act_obj
.scale
[0]
345 dim
= ref_points
[5] - ref_points
[3]
346 obj
.scale
[1] = (ref_dim
[1] / dim
) * act_obj
.scale
[1]
348 dim
= ref_points
[8] - ref_points
[6]
349 obj
.scale
[2] = (ref_dim
[2] / dim
) * act_obj
.scale
[2]
353 vec_ref2_co
= Vector(ref2_co
)
354 offset
= vec_ref2_co
- obj
.location
355 offset_x
= [offset
[0] + loc_offset
[0], 0, 0]
356 offset_y
= [0, offset
[1] + loc_offset
[1], 0]
357 offset_z
= [0, 0, offset
[2] + loc_offset
[2]]
360 obj_mtx
= obj
.matrix_world
.copy()
361 # What's the displacement vector for the pivot?
362 move_pivot
= Vector(vec
)
364 # Move the pivot point (which is the object's location)
368 nm
= obj_mtx
.inverted() @ Matrix
.Translation(-move_pivot
) @ obj_mtx
370 # Transform the mesh now
380 def point_in_selection(act_obj
, sel_obj
):
385 obj_mtx
= o
.matrix_world
386 if o
.type == 'MESH' and len(o
.data
.vertices
) > 0:
387 ref_co
= o
.data
.vertices
[0].co
.copy()
388 ref_co
= obj_mtx
@ ref_co
391 elif o
.type == 'CURVE' and len(o
.data
.splines
) > 0:
392 ref_co
= o
.data
.splines
[0].bezier_point
[0].co
.copy()
393 ref_co
= obj_mtx
@ ref_co
396 elif o
.type == 'SURFACE' and len(o
.data
.splines
) > 0:
397 ref_co
= o
.data
.splines
[0].points
[0].co
.copy()
398 ref_co
= obj_mtx
@ ref_co
401 elif o
.type == 'FONT' and len(o
.data
.splines
) > 0:
402 ref_co
= o
.data
.splines
[0].bezier_points
[0].co
.copy()
403 ref_co
= obj_mtx
@ ref_co
406 # if no object had data, use the position of an object that was not active as an internal
409 ref_co
= ref_ob
.matrix_world
.translation
414 # if act_obj.type == ('MESH' or 'FONT' or 'CURVE' or 'SURFACE'):
415 if act_obj
.type == 'MESH' or act_obj
.type == 'FONT' or act_obj
.type == 'SURFACE':
416 ref2_co
= find_ref2_co(act_obj
)
419 ref2_co
= bpy
.context
.scene
.cursor
.location
421 ref2_co
= act_obj
.matrix_world
.translation
423 # in the case of substantial selection
425 # I am seeking a point that is in the selection space
426 ref_co
= point_in_selection(act_obj
, sel_obj
)
428 sel_min
, sel_max
= get_sel_ref(ref_co
, sel_obj
)
430 sel_center
= sel_min
+ ((sel_max
- sel_min
) / 2)
431 translate
= [0, 0, 0]
433 # calculating how much to move the selection
435 translate
= ref2_co
- sel_min
+ loc_offset
437 translate
= ref2_co
- sel_center
+ loc_offset
439 translate
= ref2_co
- sel_max
+ loc_offset
441 # Move the various objects
444 if obj
!= act_obj
or (active_too
and obj
== act_obj
):
447 obj
.location
[0] += translate
[0]
449 obj
.location
[1] += translate
[1]
451 obj
.location
[2] += translate
[2]
452 else: # not consistent
455 if rot_x
or rot_y
or rot_z
:
456 find_new_rotation(obj
)
458 if fit_x
or fit_y
or fit_z
:
460 ref_points
= get_reference_points(act_obj
, "local")
461 dim
[0] = ref_points
[2] - ref_points
[0]
462 dim
[1] = ref_points
[5] - ref_points
[3]
463 dim
[2] = ref_points
[8] - ref_points
[6]
464 find_new_dimensions(obj
, dim
)
466 if scale_x
or scale_y
or scale_z
:
469 if loc_x
or loc_y
or loc_z
:
470 # print("ehy", ref2_co)
473 if active_too
is True:
474 if loc_x
or loc_y
or loc_z
:
475 find_new_coord(act_obj
)
476 if rot_x
or rot_y
or rot_z
:
477 find_new_rotation(act_obj
)
478 if scale_x
or scale_y
or scale_z
:
479 find_new_scale(act_obj
)
480 # add dimensions if dim offset will be added
483 if self_or_active
== "1":
484 if act_obj
.type == 'MESH':
485 ref2_co
= find_ref2_co(act_obj
)
487 if self_or_active
== "0":
488 ref2_co
= find_ref2_co(obj
)
489 if loc_x
or loc_y
or loc_z
:
490 if obj
!= act_obj
and obj
.type == 'MESH':
493 if active_too
is True:
494 if act_obj
.type == 'MESH':
495 if loc_x
or loc_y
or loc_z
:
496 if self_or_active
== "0":
497 ref2_co
= find_ref2_co(act_obj
)
501 if self_or_active
== "1":
502 if act_obj
.type == 'MESH' or act_obj
.type == 'FONT' or act_obj
.type == 'SURFACE':
503 ref2_co
= find_ref2_co(act_obj
)
504 ref_points
= get_reference_points(act_obj
, "global")
506 ref2_co
= act_obj
.matrix_world
.translation
507 ref_points
= [ref2_co
[0], ref2_co
[0], ref2_co
[0],
508 ref2_co
[1], ref2_co
[1], ref2_co
[1],
509 ref2_co
[2], ref2_co
[2], ref2_co
[2]]
513 bpy
.context
.scene
.cursor
.location
[0] = ref_points
[0] + loc_offset
[0]
515 bpy
.context
.scene
.cursor
.location
[1] = ref_points
[3] + loc_offset
[1]
517 bpy
.context
.scene
.cursor
.location
[2] = ref_points
[6] + loc_offset
[2]
520 bpy
.context
.scene
.cursor
.location
[0] = ref_points
[1] + loc_offset
[0]
522 bpy
.context
.scene
.cursor
.location
[1] = ref_points
[4] + loc_offset
[1]
524 bpy
.context
.scene
.cursor
.location
[2] = ref_points
[7] + loc_offset
[2]
527 bpy
.context
.scene
.cursor
.location
[0] = act_obj
.location
[0] + loc_offset
[0]
529 bpy
.context
.scene
.cursor
.location
[1] = act_obj
.location
[1] + loc_offset
[1]
531 bpy
.context
.scene
.cursor
.location
[2] = act_obj
.location
[2] + loc_offset
[2]
534 bpy
.context
.scene
.cursor
.location
[0] = ref_points
[2] + loc_offset
[0]
536 bpy
.context
.scene
.cursor
.location
[1] = ref_points
[5] + loc_offset
[1]
538 bpy
.context
.scene
.cursor
.location
[2] = ref_points
[8] + loc_offset
[2]
539 elif self_or_active
== "2":
540 ref_co
= point_in_selection(act_obj
, sel_obj
)
542 sel_min
, sel_max
= get_sel_ref(ref_co
, sel_obj
)
543 sel_center
= sel_min
+ ((sel_max
- sel_min
) / 2)
547 bpy
.context
.scene
.cursor
.location
[0] = sel_min
[0] + loc_offset
[0]
549 bpy
.context
.scene
.cursor
.location
[1] = sel_min
[1] + loc_offset
[1]
551 bpy
.context
.scene
.cursor
.location
[2] = sel_min
[2] + loc_offset
[2]
554 bpy
.context
.scene
.cursor
.location
[0] = sel_center
[0] + loc_offset
[0]
556 bpy
.context
.scene
.cursor
.location
[1] = sel_center
[1] + loc_offset
[1]
558 bpy
.context
.scene
.cursor
.location
[2] = sel_center
[2] + loc_offset
[2]
561 bpy
.context
.scene
.cursor
.location
[0] = sel_max
[0] + loc_offset
[0]
563 bpy
.context
.scene
.cursor
.location
[1] = sel_max
[1] + loc_offset
[1]
565 bpy
.context
.scene
.cursor
.location
[2] = sel_max
[2] + loc_offset
[2]
571 class OBJECT_OT_align_tools(Operator
):
572 bl_idname
= "object.align_tools"
573 bl_label
= "Align Operator"
574 bl_description
= "Align Object Tools"
575 bl_options
= {'REGISTER', 'UNDO'}
577 # property definitions
579 # Object-Pivot-Cursor:
580 subject
: EnumProperty(
581 items
=(("0", "Object", "Align Objects"),
582 ("1", "Pivot", "Align Objects Pivot"),
583 ("2", "Cursor", "Align Cursor To Active")),
585 description
="What will be moved"
588 active_too
: BoolProperty(
591 description
="Move the active object too"
594 advanced
: BoolProperty(
595 name
="Advanced Options",
597 description
="Show advanced options"
599 consistent
: BoolProperty(
600 name
="Consistent Selection",
602 description
="Use consistent selection"
606 name
="Align to X axis",
608 description
="Enable X axis alignment"
611 name
="Align to Y axis",
613 description
="Enable Y axis alignment"
616 name
="Align to Z axis",
618 description
="Enable Z axis alignment"
622 items
=(("3", "Max", "Align the maximum point"),
623 ("1", "Center", "Align the center point"),
624 ("2", "Pivot", "Align the pivot"),
625 ("0", "Min", "Align the minimum point")),
626 name
="Selection reference",
627 description
="Moved objects reference point"
629 # Active Oject Option:
631 items
=(("3", "Max", "Align to the maximum point"),
632 ("1", "Center", "Align to the center point"),
633 ("2", "Pivot", "Align to the pivot"),
634 ("0", "Min", "Align to the minimum point"),
635 ("4", "Cursor", "Description")),
636 name
="Active reference",
637 description
="Destination point"
639 self_or_active
: EnumProperty(
640 items
=(("0", "Self", "In relation of itself"),
641 ("1", "Active", "In relation of the active object"),
642 ("2", "Selection", "In relation of the entire selection")),
645 description
="To what the pivot will be aligned"
648 loc_offset
: FloatVectorProperty(
649 name
="Location Offset",
650 description
="Offset for location align position",
651 default
=(0.0, 0.0, 0.0),
652 subtype
='XYZ', size
=3
655 rot_offset
: FloatVectorProperty(
656 name
="Rotation Offset",
657 description
="Offset for rotation alignment",
658 default
=(0.0, 0.0, 0.0),
659 subtype
='EULER', size
=3
662 scale_offset
: FloatVectorProperty(
664 description
="Offset for scale match",
665 default
=(0.0, 0.0, 0.0),
666 subtype
='XYZ', size
=3
668 # Fit Dimension Prop:
670 name
="Fit Dimension to X axis",
675 name
="Fit Dimension to Y axis",
680 name
="Fit Dimension to Z axis",
684 # Apply Fit Dimension:
685 apply_dim
: BoolProperty(
686 name
="Apply Dimension",
692 name
="Align Rotation to X axis",
697 name
="Align Rotation to Y axis",
702 name
="Align Rotation to Z axis",
707 apply_rot
: BoolProperty(
708 name
="Apply Rotation",
713 scale_x
: BoolProperty(
714 name
="Match Scale to X axis",
718 scale_y
: BoolProperty(
719 name
="Match Scale to Y axis",
723 scale_z
: BoolProperty(
724 name
="match Scale to Z axis",
729 apply_scale
: BoolProperty(
735 def draw(self
, context
):
739 row
.label(text
="Active object is: ", icon
='OBJECT_DATA')
741 box
.label(text
=obj
.name
, icon
='EDITMODE_HLT')
742 # Object-Pivot-Cursor:
744 row0
.prop(self
, 'subject', expand
=True)
748 row1
.prop(self
, 'active_too')
749 row1
.prop(self
, 'advanced')
752 row1b
.prop(self
, 'consistent')
755 row2
.label(text
="Align Location:")
759 row3
.prop(self
, "loc_x", text
="X", toggle
=True)
760 row3
.prop(self
, "loc_y", text
="Y", toggle
=True)
761 row3
.prop(self
, "loc_z", text
="Z", toggle
=True)
764 if self
.advanced
is True:
766 # row8.label(text='Location Offset')
768 row9
.prop(self
, 'loc_offset', text
='')
771 if self
.advanced
is True:
772 sel
= bpy
.context
.selected_objects
776 row4
.label(text
="Selected: " + str(sel_obs
) + " Objects", icon
='OBJECT_DATA')
777 if self
.subject
== "1" or self
.subject
== "2":
779 row5b
.prop(self
, 'self_or_active', expand
=True)
782 row5
.prop(self
, 'ref1', expand
=True)
784 # Active Object Options: Number of select objects
785 act
= bpy
.context
.active_object
787 if self
.advanced
is True:
790 row6
.label(text
="Active: " + act
.name
, icon
='OBJECT_DATA')
792 row7
.prop(self
, 'ref2', expand
=True)
794 if self
.subject
== "0":
796 row12
.label(text
='Align Rotation:')
797 row13
= layout
.row(align
=True)
798 row13
.prop(self
, 'rot_x', text
='X', toggle
=True)
799 row13
.prop(self
, 'rot_y', text
='Y', toggle
=True)
800 row13
.prop(self
, 'rot_z', text
='Z', toggle
=True)
801 row13
.prop(self
, 'apply_rot', text
='Apply', toggle
=True)
802 if self
.advanced
is True:
803 row13b
= layout
.row()
804 row13b
.prop(self
, 'rot_offset', text
='')
807 row14
.label(text
='Match Scale:')
808 row15
= layout
.row(align
=True)
809 row15
.prop(self
, 'scale_x', text
='X', toggle
=True)
810 row15
.prop(self
, 'scale_y', text
='Y', toggle
=True)
811 row15
.prop(self
, 'scale_z', text
='Z', toggle
=True)
812 row15
.prop(self
, 'apply_scale', text
='Apply', toggle
=True)
813 if self
.advanced
is True:
814 row15b
= layout
.row()
815 row15b
.prop(self
, 'scale_offset', text
='')
818 row10
.label(text
='Fit Dimensions:')
819 row11
= layout
.row(align
=True)
820 row11
.prop(self
, 'fit_x', text
='X', toggle
=True)
821 row11
.prop(self
, 'fit_y', text
='Y', toggle
=True)
822 row11
.prop(self
, 'fit_z', text
='Z', toggle
=True)
823 row11
.prop(self
, 'apply_dim', text
='Apply', toggle
=True)
825 def execute(self
, context
):
827 self
.subject
, self
.active_too
, self
.consistent
,
828 self
.self_or_active
, self
.loc_x
, self
.loc_y
, self
.loc_z
,
829 self
.ref1
, self
.ref2
, self
.loc_offset
,
830 self
.rot_x
, self
.rot_y
, self
.rot_z
, self
.rot_offset
,
831 self
.scale_x
, self
.scale_y
, self
.scale_z
, self
.scale_offset
,
832 self
.fit_x
, self
.fit_y
, self
.fit_z
838 # Simple Align Classes #
840 # Align All Rotation And Location
841 class OBJECT_OT_AlignOperator(Operator
):
842 bl_idname
= "object.align"
843 bl_label
= "Align Selected To Active"
844 bl_description
= "Align Selected To Active"
847 def poll(cls
, context
):
848 return context
.active_object
is not None
850 def execute(self
, context
):
856 class OBJECT_OT_AlignLocationOperator(Operator
):
857 bl_idname
= "object.align_location_all"
858 bl_label
= "Align Selected Location To Active"
859 bl_description
= "Align Selected Location To Active"
862 def poll(cls
, context
):
863 return context
.active_object
is not None
865 def execute(self
, context
):
871 class OBJECT_OT_AlignLocationXOperator(Operator
):
872 bl_idname
= "object.align_location_x"
873 bl_label
= "Align Selected Location X To Active"
874 bl_description
= "Align Selected Location X To Active"
877 def poll(cls
, context
):
878 return context
.active_object
is not None
880 def execute(self
, context
):
886 class OBJECT_OT_AlignLocationYOperator(Operator
):
887 bl_idname
= "object.align_location_y"
888 bl_label
= "Align Selected Location Y To Active"
889 bl_description
= "Align Selected Location Y To Active"
892 def poll(cls
, context
):
893 return context
.active_object
is not None
895 def execute(self
, context
):
901 class OBJECT_OT_AlignLocationZOperator(Operator
):
902 bl_idname
= "object.align_location_z"
903 bl_label
= "Align Selected Location Z To Active"
904 bl_description
= "Align Selected Location Z To Active"
907 def poll(cls
, context
):
908 return context
.active_object
is not None
910 def execute(self
, context
):
916 class OBJECT_OT_AlignRotationOperator(Operator
):
917 bl_idname
= "object.align_rotation_all"
918 bl_label
= "Align Selected Rotation To Active"
919 bl_description
= "Align Selected Rotation To Active"
922 def poll(cls
, context
):
923 return context
.active_object
is not None
925 def execute(self
, context
):
931 class OBJECT_OT_AlignRotationXOperator(Operator
):
932 bl_idname
= "object.align_rotation_x"
933 bl_label
= "Align Selected Rotation X To Active"
934 bl_description
= "Align Selected Rotation X To Active"
937 def poll(cls
, context
):
938 return context
.active_object
is not None
940 def execute(self
, context
):
946 class OBJECT_OT_AlignRotationYOperator(Operator
):
947 bl_idname
= "object.align_rotation_y"
948 bl_label
= "Align Selected Rotation Y To Active"
949 bl_description
= "Align Selected Rotation Y To Active"
952 def poll(cls
, context
):
953 return context
.active_object
is not None
955 def execute(self
, context
):
961 class OBJECT_OT_AlignRotationZOperator(Operator
):
962 bl_idname
= "object.align_rotation_z"
963 bl_label
= "Align Selected Rotation Z To Active"
964 bl_description
= "Align Selected Rotation Z To Active"
967 def poll(cls
, context
):
968 return context
.active_object
is not None
970 def execute(self
, context
):
976 class OBJECT_OT_AlignScaleOperator(Operator
):
977 bl_idname
= "object.align_objects_scale_all"
978 bl_label
= "Align Selected Scale To Active"
979 bl_description
= "Align Selected Scale To Active"
982 def poll(cls
, context
):
983 return context
.active_object
is not None
985 def execute(self
, context
):
991 class OBJECT_OT_AlignScaleXOperator(Operator
):
992 bl_idname
= "object.align_objects_scale_x"
993 bl_label
= "Align Selected Scale X To Active"
994 bl_description
= "Align Selected Scale X To Active"
997 def poll(cls
, context
):
998 return context
.active_object
is not None
1000 def execute(self
, context
):
1006 class OBJECT_OT_AlignScaleYOperator(Operator
):
1007 bl_idname
= "object.align_objects_scale_y"
1008 bl_label
= "Align Selected Scale Y To Active"
1009 bl_description
= "Align Selected Scale Y To Active"
1012 def poll(cls
, context
):
1013 return context
.active_object
is not None
1015 def execute(self
, context
):
1021 class OBJECT_OT_AlignScaleZOperator(Operator
):
1022 bl_idname
= "object.align_objects_scale_z"
1023 bl_label
= "Align Selected Scale Z To Active"
1024 bl_description
= "Align Selected Scale Z To Active"
1027 def poll(cls
, context
):
1028 return context
.active_object
is not None
1030 def execute(self
, context
):
1037 class VIEW3D_PT_AlignUi(Panel
):
1038 bl_space_type
= 'VIEW_3D'
1039 bl_region_type
= 'UI'
1040 bl_label
= "Align Tools"
1041 bl_context
= "objectmode"
1042 bl_category
= 'Item'
1043 bl_options
= {'DEFAULT_CLOSED'}
1045 def draw(self
, context
):
1046 layout
= self
.layout
1047 obj
= context
.object
1051 row
.label(text
="Active object is: ", icon
='OBJECT_DATA')
1053 box
.label(text
=obj
.name
, icon
='EDITMODE_HLT')
1055 col
= layout
.column()
1056 col
.label(text
="Align Loc + Rot:")
1058 col
= layout
.column(align
=False)
1059 col
.operator("object.align", text
="XYZ")
1061 col
= layout
.column()
1062 col
.label(text
="Align Location:")
1064 col
= layout
.column_flow(columns
=4, align
=True)
1065 col
.operator("object.align_location_x", text
="X")
1066 col
.operator("object.align_location_y", text
="Y")
1067 col
.operator("object.align_location_z", text
="Z")
1068 col
.operator("object.align_location_all", text
="All")
1070 col
= layout
.column()
1071 col
.label(text
="Align Rotation:")
1073 col
= layout
.column_flow(columns
=4, align
=True)
1074 col
.operator("object.align_rotation_x", text
="X")
1075 col
.operator("object.align_rotation_y", text
="Y")
1076 col
.operator("object.align_rotation_z", text
="Z")
1077 col
.operator("object.align_rotation_all", text
="All")
1079 col
= layout
.column()
1080 col
.label(text
="Align Scale:")
1082 col
= layout
.column_flow(columns
=4, align
=True)
1083 col
.operator("object.align_objects_scale_x", text
="X")
1084 col
.operator("object.align_objects_scale_y", text
="Y")
1085 col
.operator("object.align_objects_scale_z", text
="Z")
1086 col
.operator("object.align_objects_scale_all", text
="All")
1089 col
= layout
.column()
1090 col
.label(text
="Advanced Align Operations")
1091 layout
= self
.layout
1092 self
.layout
.operator("object.align_tools", text
="Advanced")
1095 # Add-ons Preferences Update Panel
1097 # Define Panel classes for updating
1103 def update_panel(self
, context
):
1104 message
= "Align Tools: Updating Panel locations has failed"
1106 for panel
in panels
:
1107 if "bl_rna" in panel
.__dict
__:
1108 bpy
.utils
.unregister_class(panel
)
1110 for panel
in panels
:
1111 panel
.bl_category
= context
.preferences
.addons
[__name__
].preferences
.category
1112 bpy
.utils
.register_class(panel
)
1114 except Exception as e
:
1115 print("\n[{}]\n{}\n\nError:\n{}".format(__name__
, message
, e
))
1119 class AlignAddonPreferences(AddonPreferences
):
1120 # this must match the addon name, use '__package__'
1121 # when defining this in a submodule of a python package.
1122 bl_idname
= __name__
1124 category
: StringProperty(
1125 name
="Tab Category",
1126 description
="Choose a name for the category of the panel",
1131 def draw(self
, context
):
1132 layout
= self
.layout
1136 col
.label(text
="Tab Category:")
1137 col
.prop(self
, "category", text
="")
1143 OBJECT_OT_AlignOperator
,
1144 OBJECT_OT_AlignLocationOperator
,
1145 OBJECT_OT_AlignLocationXOperator
,
1146 OBJECT_OT_AlignLocationYOperator
,
1147 OBJECT_OT_AlignLocationZOperator
,
1148 OBJECT_OT_AlignRotationOperator
,
1149 OBJECT_OT_AlignRotationXOperator
,
1150 OBJECT_OT_AlignRotationYOperator
,
1151 OBJECT_OT_AlignRotationZOperator
,
1152 OBJECT_OT_AlignScaleOperator
,
1153 OBJECT_OT_AlignScaleXOperator
,
1154 OBJECT_OT_AlignScaleYOperator
,
1155 OBJECT_OT_AlignScaleZOperator
,
1156 OBJECT_OT_align_tools
,
1157 AlignAddonPreferences
,
1161 # Register all operators and panels
1164 bpy
.utils
.register_class(cls
)
1169 bpy
.utils
.unregister_class(cls
)
1172 if __name__
== "__main__":