svn merge -r38200:38300 https://svn.blender.org/svnroot/bf-blender/trunk/blender...
[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         bb_world = [Vector(v[:]) * obj.matrix_world for v in obj.bound_box]
102
103         Left_Up_Front = bb_world[1]
104         Right_Down_Back = bb_world[7]
105
106         center_x = (Left_Up_Front[0] + Right_Down_Back[0]) / 2.0
107         center_y = (Left_Up_Front[1] + Right_Down_Back[1]) / 2.0
108         center_z = (Left_Up_Front[2] + Right_Down_Back[2]) / 2.0
109
110         positive_x = Right_Down_Back[0]
111         positive_y = Right_Down_Back[1]
112         positive_z = Left_Up_Front[2]
113
114         negative_x = Left_Up_Front[0]
115         negative_y = Left_Up_Front[1]
116         negative_z = Right_Down_Back[2]
117
118         obj_loc = obj.location
119
120         if align_x:
121
122             # Align Mode
123
124             if relative_to == 'OPT_4':  # Active relative
125                 if align_mode == 'OPT_1':
126                     obj_x = obj_loc[0] - negative_x - size_active_x
127
128                 elif align_mode == 'OPT_3':
129                     obj_x = obj_loc[0] - positive_x + size_active_x
130
131             else:  # Everything else relative
132                 if align_mode == 'OPT_1':
133                     obj_x = obj_loc[0] - negative_x
134
135                 elif align_mode == 'OPT_3':
136                     obj_x = obj_loc[0] - positive_x
137
138             if align_mode == 'OPT_2':  # All relative
139                 obj_x = obj_loc[0] - center_x
140
141             # Relative To
142
143             if relative_to == 'OPT_1':
144                 loc_x = obj_x
145
146             elif relative_to == 'OPT_2':
147                 loc_x = obj_x + cursor[0]
148
149             elif relative_to == 'OPT_3':
150                 loc_x = obj_x + center_sel_x
151
152             elif relative_to == 'OPT_4':
153                 loc_x = obj_x + center_active_x
154
155             obj.location[0] = loc_x
156
157         if align_y:
158             # Align Mode
159
160             if relative_to == 'OPT_4':  # Active relative
161                 if align_mode == 'OPT_1':
162                     obj_y = obj_loc[1] - negative_y - size_active_y
163
164                 elif align_mode == 'OPT_3':
165                     obj_y = obj_loc[1] - positive_y + size_active_y
166
167             else:  # Everything else relative
168                 if align_mode == 'OPT_1':
169                     obj_y = obj_loc[1] - negative_y
170
171                 elif align_mode == 'OPT_3':
172                     obj_y = obj_loc[1] - positive_y
173
174             if align_mode == 'OPT_2':  # All relative
175                 obj_y = obj_loc[1] - center_y
176
177             # Relative To
178
179             if relative_to == 'OPT_1':
180                 loc_y = obj_y
181
182             elif relative_to == 'OPT_2':
183                 loc_y = obj_y + cursor[1]
184
185             elif relative_to == 'OPT_3':
186                 loc_y = obj_y + center_sel_y
187
188             elif relative_to == 'OPT_4':
189                 loc_y = obj_y + center_active_y
190
191             obj.location[1] = loc_y
192
193         if align_z:
194             # Align Mode
195             if relative_to == 'OPT_4':  # Active relative
196                 if align_mode == 'OPT_1':
197                     obj_z = obj_loc[2] - negative_z - size_active_z
198
199                 elif align_mode == 'OPT_3':
200                     obj_z = obj_loc[2] - positive_z + size_active_z
201
202             else:  # Everything else relative
203                 if align_mode == 'OPT_1':
204                     obj_z = obj_loc[2] - negative_z
205
206                 elif align_mode == 'OPT_3':
207                     obj_z = obj_loc[2] - positive_z
208
209             if align_mode == 'OPT_2':  # All relative
210                 obj_z = obj_loc[2] - center_z
211
212             # Relative To
213
214             if relative_to == 'OPT_1':
215                 loc_z = obj_z
216
217             elif relative_to == 'OPT_2':
218                 loc_z = obj_z + cursor[2]
219
220             elif relative_to == 'OPT_3':
221                 loc_z = obj_z + center_sel_z
222
223             elif relative_to == 'OPT_4':
224                 loc_z = obj_z + center_active_z
225
226             obj.location[2] = loc_z
227
228     return True
229
230
231 from bpy.props import EnumProperty
232
233
234 class AlignObjects(bpy.types.Operator):
235     '''Align Objects'''
236     bl_idname = "object.align"
237     bl_label = "Align Objects"
238     bl_options = {'REGISTER', 'UNDO'}
239
240     align_mode = EnumProperty(items=(
241             ('OPT_1', "Negative Sides", ""),
242             ('OPT_2', "Centers", ""),
243             ('OPT_3', "Positive Sides", "")),
244         name="Align Mode:",
245         description="",
246         default='OPT_2')
247
248     relative_to = EnumProperty(items=(
249             ('OPT_1', "Scene Origin", ""),
250             ('OPT_2', "3D Cursor", ""),
251             ('OPT_3', "Selection", ""),
252             ('OPT_4', "Active", "")),
253         name="Relative To:",
254         description="",
255         default='OPT_4')
256
257     align_axis = EnumProperty(items=(
258             ('X', "X", ""),
259             ('Y', "Y", ""),
260             ('Z', "Z", ""),
261             ),
262                 name="Align",
263                 description="Align to axis",
264                 options={'ENUM_FLAG'})
265
266     @classmethod
267     def poll(cls, context):
268         return context.mode == 'OBJECT'
269
270     def execute(self, context):
271         align_axis = self.align_axis
272         ret = align_objects('X' in align_axis, 'Y' in align_axis, 'Z' in align_axis, self.align_mode, self.relative_to)
273
274         if not ret:
275             self.report({'WARNING'}, "No objects with bound-box selected")
276             return {'CANCELLED'}
277         else:
278             return {'FINISHED'}