644f30a4745b3aa91ec5fef5ae665f2ac1132772
[blender-staging.git] / release / scripts / startup / bl_operators / object_align.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21 import bpy
22 from mathutils import Vector
23
24
25 def align_objects(align_x, align_y, align_z, align_mode, relative_to):
26
27     cursor = bpy.context.scene.cursor_location
28
29     Left_Up_Front_SEL = [0.0, 0.0, 0.0]
30     Right_Down_Back_SEL = [0.0, 0.0, 0.0]
31
32     flag_first = True
33
34     objs = []
35
36     for obj in bpy.context.selected_objects:
37         matrix_world = obj.matrix_world
38         bb_world = [Vector(v[:]) * matrix_world for v in obj.bound_box]
39         objs.append((obj, bb_world))
40
41     if not objs:
42         return False
43
44     for obj, bb_world in objs:
45         Left_Up_Front = bb_world[1]
46         Right_Down_Back = bb_world[7]
47
48         # Active Center
49
50         if obj == bpy.context.active_object:
51
52             center_active_x = (Left_Up_Front[0] + Right_Down_Back[0]) / 2.0
53             center_active_y = (Left_Up_Front[1] + Right_Down_Back[1]) / 2.0
54             center_active_z = (Left_Up_Front[2] + Right_Down_Back[2]) / 2.0
55
56             size_active_x = (Right_Down_Back[0] - Left_Up_Front[0]) / 2.0
57             size_active_y = (Right_Down_Back[1] - Left_Up_Front[1]) / 2.0
58             size_active_z = (Left_Up_Front[2] - Right_Down_Back[2]) / 2.0
59
60         # Selection Center
61
62         if flag_first:
63             flag_first = False
64
65             Left_Up_Front_SEL[0] = Left_Up_Front[0]
66             Left_Up_Front_SEL[1] = Left_Up_Front[1]
67             Left_Up_Front_SEL[2] = Left_Up_Front[2]
68
69             Right_Down_Back_SEL[0] = Right_Down_Back[0]
70             Right_Down_Back_SEL[1] = Right_Down_Back[1]
71             Right_Down_Back_SEL[2] = Right_Down_Back[2]
72
73         else:
74             # X axis
75             if Left_Up_Front[0] < Left_Up_Front_SEL[0]:
76                 Left_Up_Front_SEL[0] = Left_Up_Front[0]
77             # Y axis
78             if Left_Up_Front[1] < Left_Up_Front_SEL[1]:
79                 Left_Up_Front_SEL[1] = Left_Up_Front[1]
80             # Z axis
81             if Left_Up_Front[2] > Left_Up_Front_SEL[2]:
82                 Left_Up_Front_SEL[2] = Left_Up_Front[2]
83
84             # X axis
85             if Right_Down_Back[0] > Right_Down_Back_SEL[0]:
86                 Right_Down_Back_SEL[0] = Right_Down_Back[0]
87             # Y axis
88             if Right_Down_Back[1] > Right_Down_Back_SEL[1]:
89                 Right_Down_Back_SEL[1] = Right_Down_Back[1]
90             # Z axis
91             if Right_Down_Back[2] < Right_Down_Back_SEL[2]:
92                 Right_Down_Back_SEL[2] = Right_Down_Back[2]
93
94     center_sel_x = (Left_Up_Front_SEL[0] + Right_Down_Back_SEL[0]) / 2.0
95     center_sel_y = (Left_Up_Front_SEL[1] + Right_Down_Back_SEL[1]) / 2.0
96     center_sel_z = (Left_Up_Front_SEL[2] + Right_Down_Back_SEL[2]) / 2.0
97
98     # Main Loop
99
100     for obj, bb_world in objs:
101
102         loc_world = obj.location
103         bb_world = [Vector(v[:]) * obj.matrix_world for v in obj.bound_box]
104
105         Left_Up_Front = bb_world[1]
106         Right_Down_Back = bb_world[7]
107
108         center_x = (Left_Up_Front[0] + Right_Down_Back[0]) / 2.0
109         center_y = (Left_Up_Front[1] + Right_Down_Back[1]) / 2.0
110         center_z = (Left_Up_Front[2] + Right_Down_Back[2]) / 2.0
111
112         positive_x = Right_Down_Back[0]
113         positive_y = Right_Down_Back[1]
114         positive_z = Left_Up_Front[2]
115
116         negative_x = Left_Up_Front[0]
117         negative_y = Left_Up_Front[1]
118         negative_z = Right_Down_Back[2]
119
120         obj_loc = obj.location
121
122         if align_x:
123
124             # Align Mode
125
126             if relative_to == 'OPT_4':  # Active relative
127                 if align_mode == 'OPT_1':
128                     obj_x = obj_loc[0] - negative_x - size_active_x
129
130                 elif align_mode == 'OPT_3':
131                     obj_x = obj_loc[0] - positive_x + size_active_x
132
133             else:  # Everything else relative
134                 if align_mode == 'OPT_1':
135                     obj_x = obj_loc[0] - negative_x
136
137                 elif align_mode == 'OPT_3':
138                     obj_x = obj_loc[0] - positive_x
139
140             if align_mode == 'OPT_2':  # All relative
141                 obj_x = obj_loc[0] - center_x
142
143             # Relative To
144
145             if relative_to == 'OPT_1':
146                 loc_x = obj_x
147
148             elif relative_to == 'OPT_2':
149                 loc_x = obj_x + cursor[0]
150
151             elif relative_to == 'OPT_3':
152                 loc_x = obj_x + center_sel_x
153
154             elif relative_to == 'OPT_4':
155                 loc_x = obj_x + center_active_x
156
157             obj.location[0] = loc_x
158
159         if align_y:
160             # Align Mode
161
162             if relative_to == 'OPT_4':  # Active relative
163                 if align_mode == 'OPT_1':
164                     obj_y = obj_loc[1] - negative_y - size_active_y
165
166                 elif align_mode == 'OPT_3':
167                     obj_y = obj_loc[1] - positive_y + size_active_y
168
169             else:  # Everything else relative
170                 if align_mode == 'OPT_1':
171                     obj_y = obj_loc[1] - negative_y
172
173                 elif align_mode == 'OPT_3':
174                     obj_y = obj_loc[1] - positive_y
175
176             if align_mode == 'OPT_2':  # All relative
177                 obj_y = obj_loc[1] - center_y
178
179             # Relative To
180
181             if relative_to == 'OPT_1':
182                 loc_y = obj_y
183
184             elif relative_to == 'OPT_2':
185                 loc_y = obj_y + cursor[1]
186
187             elif relative_to == 'OPT_3':
188                 loc_y = obj_y + center_sel_y
189
190             elif relative_to == 'OPT_4':
191                 loc_y = obj_y + center_active_y
192
193             obj.location[1] = loc_y
194
195         if align_z:
196             # Align Mode
197             if relative_to == 'OPT_4':  # Active relative
198                 if align_mode == 'OPT_1':
199                     obj_z = obj_loc[2] - negative_z - size_active_z
200
201                 elif align_mode == 'OPT_3':
202                     obj_z = obj_loc[2] - positive_z + size_active_z
203
204             else:  # Everything else relative
205                 if align_mode == 'OPT_1':
206                     obj_z = obj_loc[2] - negative_z
207
208                 elif align_mode == 'OPT_3':
209                     obj_z = obj_loc[2] - positive_z
210
211             if align_mode == 'OPT_2':  # All relative
212                 obj_z = obj_loc[2] - center_z
213
214             # Relative To
215
216             if relative_to == 'OPT_1':
217                 loc_z = obj_z
218
219             elif relative_to == 'OPT_2':
220                 loc_z = obj_z + cursor[2]
221
222             elif relative_to == 'OPT_3':
223                 loc_z = obj_z + center_sel_z
224
225             elif relative_to == 'OPT_4':
226                 loc_z = obj_z + center_active_z
227
228             obj.location[2] = loc_z
229
230     return True
231
232
233 from bpy.props import EnumProperty
234
235
236 class AlignObjects(bpy.types.Operator):
237     '''Align Objects'''
238     bl_idname = "object.align"
239     bl_label = "Align Objects"
240     bl_options = {'REGISTER', 'UNDO'}
241
242     align_mode = EnumProperty(items=(
243             ('OPT_1', "Negative Sides", ""),
244             ('OPT_2', "Centers", ""),
245             ('OPT_3', "Positive Sides", "")),
246         name="Align Mode:",
247         description="",
248         default='OPT_2')
249
250     relative_to = EnumProperty(items=(
251             ('OPT_1', "Scene Origin", ""),
252             ('OPT_2', "3D Cursor", ""),
253             ('OPT_3', "Selection", ""),
254             ('OPT_4', "Active", "")),
255         name="Relative To:",
256         description="",
257         default='OPT_4')
258
259     align_axis = EnumProperty(items=(
260             ('X', "X", ""),
261             ('Y', "Y", ""),
262             ('Z', "Z", ""),
263             ),
264                 name="Align",
265                 description="Align to axis",
266                 options={'ENUM_FLAG'})
267
268     @classmethod
269     def poll(cls, context):
270         return context.mode == 'OBJECT'
271
272     def execute(self, context):
273         align_axis = self.align_axis
274         ret = align_objects('X' in align_axis, 'Y' in align_axis, 'Z' in align_axis, self.align_mode, self.relative_to)
275
276         if not ret:
277             self.report({'WARNING'}, "No objects with bound-box selected")
278             return {'CANCELLED'}
279         else:
280             return {'FINISHED'}