| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  | # ##### BEGIN GPL LICENSE BLOCK ##### | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #  This program is free software; you can redistribute it and/or | 
					
						
							|  |  |  | #  modify it under the terms of the GNU General Public License | 
					
						
							|  |  |  | #  as published by the Free Software Foundation; either version 2 | 
					
						
							|  |  |  | #  of the License, or (at your option) any later version. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #  This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | #  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  | #  GNU General Public License for more details. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #  You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  | #  along with this program; if not, write to the Free Software Foundation, | 
					
						
							|  |  |  | #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # ##### END GPL LICENSE BLOCK ##### | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # <pep8-80 compliant> | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import bpy | 
					
						
							|  |  |  | from bpy.types import Operator | 
					
						
							|  |  |  | from bpy.props import IntProperty | 
					
						
							|  |  |  | from bpy.props import EnumProperty | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class CopyRigidbodySettings(Operator): | 
					
						
							|  |  |  |     '''Copy Rigid Body settings from active object to selected''' | 
					
						
							|  |  |  |     bl_idname = "rigidbody.object_settings_copy" | 
					
						
							| 
									
										
										
										
											2013-01-27 18:14:24 +00:00
										 |  |  |     bl_label = "Copy Rigid Body Settings" | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  |     bl_options = {'REGISTER', 'UNDO'} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-20 13:22:08 +00:00
										 |  |  |     _attrs = ( | 
					
						
							|  |  |  |         "type", | 
					
						
							|  |  |  |         "kinematic", | 
					
						
							|  |  |  |         "mass", | 
					
						
							|  |  |  |         "collision_shape", | 
					
						
							|  |  |  |         "use_margin", | 
					
						
							|  |  |  |         "collision_margin", | 
					
						
							|  |  |  |         "friction", | 
					
						
							|  |  |  |         "restitution", | 
					
						
							|  |  |  |         "use_deactivation", | 
					
						
							| 
									
										
										
										
											2013-04-23 07:06:29 +00:00
										 |  |  |         "use_start_deactivated", | 
					
						
							| 
									
										
										
										
											2013-04-20 13:22:08 +00:00
										 |  |  |         "deactivate_linear_velocity", | 
					
						
							|  |  |  |         "deactivate_angular_velocity", | 
					
						
							|  |  |  |         "linear_damping", | 
					
						
							|  |  |  |         "angular_damping", | 
					
						
							|  |  |  |         "collision_groups", | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  |     @classmethod | 
					
						
							|  |  |  |     def poll(cls, context): | 
					
						
							| 
									
										
										
										
											2013-01-23 07:52:31 +00:00
										 |  |  |         obj = context.object | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  |         return (obj and obj.rigid_body) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def execute(self, context): | 
					
						
							| 
									
										
										
										
											2013-04-20 13:22:08 +00:00
										 |  |  |         obj_act = context.object | 
					
						
							| 
									
										
										
										
											2013-01-28 12:15:50 +00:00
										 |  |  |         scene = context.scene | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # deselect all but mesh objects | 
					
						
							|  |  |  |         for o in context.selected_objects: | 
					
						
							|  |  |  |             if o.type != 'MESH': | 
					
						
							|  |  |  |                 o.select = False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-28 12:15:50 +00:00
										 |  |  |         objects = context.selected_objects | 
					
						
							|  |  |  |         if objects: | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  |             # add selected objects to active one groups and recalculate | 
					
						
							|  |  |  |             bpy.ops.group.objects_add_active() | 
					
						
							| 
									
										
										
										
											2013-01-28 12:15:50 +00:00
										 |  |  |             scene.frame_set(scene.frame_current) | 
					
						
							| 
									
										
										
										
											2013-04-20 13:22:08 +00:00
										 |  |  |             rb_from = obj_act.rigid_body | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  |             # copy settings | 
					
						
							| 
									
										
										
										
											2013-01-28 12:15:50 +00:00
										 |  |  |             for o in objects: | 
					
						
							| 
									
										
										
										
											2013-04-20 13:22:08 +00:00
										 |  |  |                 rb_to = o.rigid_body | 
					
						
							|  |  |  |                 if (o == obj_act) or (rb_to is None): | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  |                     continue | 
					
						
							| 
									
										
										
										
											2013-04-20 13:22:08 +00:00
										 |  |  |                 for attr in self._attrs: | 
					
						
							|  |  |  |                     setattr(rb_to, attr, getattr(rb_from, attr)) | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return {'FINISHED'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class BakeToKeyframes(Operator): | 
					
						
							|  |  |  |     '''Bake rigid body transformations of selected objects to keyframes''' | 
					
						
							|  |  |  |     bl_idname = "rigidbody.bake_to_keyframes" | 
					
						
							|  |  |  |     bl_label = "Bake To Keyframes" | 
					
						
							|  |  |  |     bl_options = {'REGISTER', 'UNDO'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     frame_start = IntProperty( | 
					
						
							|  |  |  |             name="Start Frame", | 
					
						
							|  |  |  |             description="Start frame for baking", | 
					
						
							|  |  |  |             min=0, max=300000, | 
					
						
							|  |  |  |             default=1, | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |     frame_end = IntProperty( | 
					
						
							|  |  |  |             name="End Frame", | 
					
						
							|  |  |  |             description="End frame for baking", | 
					
						
							|  |  |  |             min=1, max=300000, | 
					
						
							|  |  |  |             default=250, | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |     step = IntProperty( | 
					
						
							|  |  |  |             name="Frame Step", | 
					
						
							|  |  |  |             description="Frame Step", | 
					
						
							|  |  |  |             min=1, max=120, | 
					
						
							|  |  |  |             default=1, | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def poll(cls, context): | 
					
						
							| 
									
										
										
										
											2013-01-23 07:52:31 +00:00
										 |  |  |         obj = context.object | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  |         return (obj and obj.rigid_body) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def execute(self, context): | 
					
						
							|  |  |  |         bake = [] | 
					
						
							| 
									
										
										
										
											2013-01-28 12:15:50 +00:00
										 |  |  |         objects = [] | 
					
						
							| 
									
										
										
										
											2013-01-23 07:52:31 +00:00
										 |  |  |         scene = context.scene | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  |         frame_orig = scene.frame_current | 
					
						
							| 
									
										
										
										
											2013-02-21 17:12:08 +00:00
										 |  |  |         frames_step = range(self.frame_start, self.frame_end + 1, self.step) | 
					
						
							|  |  |  |         frames_full = range(self.frame_start, self.frame_end + 1) | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # filter objects selection | 
					
						
							| 
									
										
										
										
											2013-01-23 07:52:31 +00:00
										 |  |  |         for obj in context.selected_objects: | 
					
						
							|  |  |  |             if not obj.rigid_body or obj.rigid_body.type != 'ACTIVE': | 
					
						
							|  |  |  |                 obj.select = False | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-28 12:15:50 +00:00
										 |  |  |         objects = context.selected_objects | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-28 12:15:50 +00:00
										 |  |  |         if objects: | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  |             # store transformation data | 
					
						
							| 
									
										
										
										
											2013-02-17 18:13:22 +00:00
										 |  |  |             # need to start at scene start frame so simulation is run from the beginning | 
					
						
							| 
									
										
										
										
											2013-02-21 17:12:08 +00:00
										 |  |  |             for f in frames_full: | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  |                 scene.frame_set(f) | 
					
						
							| 
									
										
										
										
											2013-02-21 17:12:08 +00:00
										 |  |  |                 if f in frames_step: | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  |                     mat = {} | 
					
						
							| 
									
										
										
										
											2013-01-28 12:15:50 +00:00
										 |  |  |                     for i, obj in enumerate(objects): | 
					
						
							| 
									
										
										
										
											2013-01-23 07:52:31 +00:00
										 |  |  |                         mat[i] = obj.matrix_world.copy() | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  |                     bake.append(mat) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # apply transformations as keyframes | 
					
						
							| 
									
										
										
										
											2013-02-21 17:12:08 +00:00
										 |  |  |             for i, f in enumerate(frames_step): | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  |                 scene.frame_set(f) | 
					
						
							| 
									
										
										
										
											2013-01-28 12:15:50 +00:00
										 |  |  |                 obj_prev = objects[0] | 
					
						
							|  |  |  |                 for j, obj in enumerate(objects): | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  |                     mat = bake[i][j] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-23 07:52:31 +00:00
										 |  |  |                     obj.location = mat.to_translation() | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-23 07:52:31 +00:00
										 |  |  |                     rot_mode = obj.rotation_mode | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  |                     if rot_mode == 'QUATERNION': | 
					
						
							| 
									
										
										
										
											2013-01-23 07:52:31 +00:00
										 |  |  |                         obj.rotation_quaternion = mat.to_quaternion() | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  |                     elif rot_mode == 'AXIS_ANGLE': | 
					
						
							|  |  |  |                         # this is a little roundabout but there's no better way right now | 
					
						
							|  |  |  |                         aa = mat.to_quaternion().to_axis_angle() | 
					
						
							| 
									
										
										
										
											2013-01-23 07:52:31 +00:00
										 |  |  |                         obj.rotation_axis_angle = (aa[1], ) + aa[0][:] | 
					
						
							| 
									
										
										
										
											2013-03-28 19:33:14 +00:00
										 |  |  |                     else:  # euler | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  |                         # make sure euler rotation is compatible to previous frame | 
					
						
							| 
									
										
										
										
											2013-01-23 07:52:31 +00:00
										 |  |  |                         obj.rotation_euler = mat.to_euler(rot_mode, obj_prev.rotation_euler) | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-23 07:52:31 +00:00
										 |  |  |                     obj_prev = obj | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 bpy.ops.anim.keyframe_insert(type='BUILTIN_KSI_LocRot', confirm_success=False) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # remove baked objects from simulation | 
					
						
							|  |  |  |             bpy.ops.rigidbody.objects_remove() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # clean up keyframes | 
					
						
							| 
									
										
										
										
											2013-01-28 12:15:50 +00:00
										 |  |  |             for obj in objects: | 
					
						
							| 
									
										
										
										
											2013-01-23 07:52:31 +00:00
										 |  |  |                 action = obj.animation_data.action | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:49 +00:00
										 |  |  |                 for fcu in action.fcurves: | 
					
						
							|  |  |  |                     keyframe_points = fcu.keyframe_points | 
					
						
							|  |  |  |                     i = 1 | 
					
						
							|  |  |  |                     # remove unneeded keyframes | 
					
						
							|  |  |  |                     while i < len(keyframe_points) - 1: | 
					
						
							|  |  |  |                         val_prev = keyframe_points[i - 1].co[1] | 
					
						
							|  |  |  |                         val_next = keyframe_points[i + 1].co[1] | 
					
						
							|  |  |  |                         val = keyframe_points[i].co[1] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                         if abs(val - val_prev) + abs(val - val_next) < 0.0001: | 
					
						
							|  |  |  |                             keyframe_points.remove(keyframe_points[i]) | 
					
						
							|  |  |  |                         else: | 
					
						
							|  |  |  |                             i += 1 | 
					
						
							|  |  |  |                     # use linear interpolation for better visual results | 
					
						
							|  |  |  |                     for keyframe in keyframe_points: | 
					
						
							|  |  |  |                         keyframe.interpolation = 'LINEAR' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # return to the frame we started on | 
					
						
							|  |  |  |             scene.frame_set(frame_orig) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return {'FINISHED'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def invoke(self, context, event): | 
					
						
							|  |  |  |         scene = context.scene | 
					
						
							|  |  |  |         self.frame_start = scene.frame_start | 
					
						
							|  |  |  |         self.frame_end = scene.frame_end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         wm = context.window_manager | 
					
						
							|  |  |  |         return wm.invoke_props_dialog(self) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:56 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class ConnectRigidBodies(Operator): | 
					
						
							| 
									
										
										
										
											2013-02-21 19:04:39 +00:00
										 |  |  |     '''Create rigid body constraints between selected rigid bodies''' | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:56 +00:00
										 |  |  |     bl_idname = "rigidbody.connect" | 
					
						
							| 
									
										
										
										
											2013-01-27 18:14:24 +00:00
										 |  |  |     bl_label = "Connect Rigid Bodies" | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:56 +00:00
										 |  |  |     bl_options = {'REGISTER', 'UNDO'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     con_type = EnumProperty( | 
					
						
							| 
									
										
										
										
											2013-04-20 13:22:08 +00:00
										 |  |  |             name="Type", | 
					
						
							|  |  |  |             description="Type of generated constraint", | 
					
						
							|  |  |  |             # XXX Would be nice to get icons too, but currently not possible ;) | 
					
						
							|  |  |  |             items=tuple((e.identifier, e.name, e.description, e. value) | 
					
						
							|  |  |  |                         for e in bpy.types.RigidBodyConstraint.bl_rna.properties["type"].enum_items), | 
					
						
							|  |  |  |             default='FIXED', | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:56 +00:00
										 |  |  |     pivot_type = EnumProperty( | 
					
						
							| 
									
										
										
										
											2013-04-20 13:22:08 +00:00
										 |  |  |             name="Location", | 
					
						
							|  |  |  |             description="Constraint pivot location", | 
					
						
							|  |  |  |             items=(('CENTER', "Center", "Pivot location is between the constrained rigid bodies"), | 
					
						
							|  |  |  |                    ('ACTIVE', "Active", "Pivot location is at the active object position"), | 
					
						
							|  |  |  |                    ('SELECTED', "Selected", "Pivot location is at the selected object position")), | 
					
						
							|  |  |  |             default='CENTER', | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2013-02-21 19:04:39 +00:00
										 |  |  |     connection_pattern = EnumProperty( | 
					
						
							| 
									
										
										
										
											2013-04-20 13:22:08 +00:00
										 |  |  |             name="Connection Pattern", | 
					
						
							|  |  |  |             description="Pattern used to connect objects", | 
					
						
							|  |  |  |             items=(('SELECTED_TO_ACTIVE', "Selected to Active", "Connect selected objects to the active object"), | 
					
						
							|  |  |  |                    ('CHAIN_DISTANCE', "Chain by Distance", "Connect objects as a chain based on distance, starting at the active object")), | 
					
						
							|  |  |  |             default='SELECTED_TO_ACTIVE', | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2013-02-21 19:04:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:56 +00:00
										 |  |  |     @classmethod | 
					
						
							|  |  |  |     def poll(cls, context): | 
					
						
							| 
									
										
										
										
											2013-01-23 07:52:31 +00:00
										 |  |  |         obj = context.object | 
					
						
							| 
									
										
										
										
											2013-01-28 12:24:25 +00:00
										 |  |  |         return (obj and obj.rigid_body) | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-21 19:04:39 +00:00
										 |  |  |     def _add_constraint(self, context, object1, object2): | 
					
						
							|  |  |  |         if object1 == object2: | 
					
						
							| 
									
										
										
										
											2013-02-23 23:40:51 +00:00
										 |  |  |             return | 
					
						
							| 
									
										
										
										
											2013-02-21 19:04:39 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if self.pivot_type == 'ACTIVE': | 
					
						
							|  |  |  |             loc = object1.location | 
					
						
							|  |  |  |         elif self.pivot_type == 'SELECTED': | 
					
						
							|  |  |  |             loc = object2.location | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             loc = (object1.location + object2.location) / 2.0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ob = bpy.data.objects.new("Constraint", object_data=None) | 
					
						
							|  |  |  |         ob.location = loc | 
					
						
							|  |  |  |         context.scene.objects.link(ob) | 
					
						
							|  |  |  |         context.scene.objects.active = ob | 
					
						
							|  |  |  |         ob.select = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         bpy.ops.rigidbody.constraint_add() | 
					
						
							|  |  |  |         con_obj = context.active_object | 
					
						
							|  |  |  |         con_obj.empty_draw_type = 'ARROWS' | 
					
						
							|  |  |  |         con = con_obj.rigid_body_constraint | 
					
						
							|  |  |  |         con.type = self.con_type | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         con.object1 = object1 | 
					
						
							|  |  |  |         con.object2 = object2 | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-21 19:04:39 +00:00
										 |  |  |     def execute(self, context): | 
					
						
							| 
									
										
										
										
											2013-01-28 12:24:25 +00:00
										 |  |  |         scene = context.scene | 
					
						
							| 
									
										
										
										
											2013-01-28 12:15:50 +00:00
										 |  |  |         objects = context.selected_objects | 
					
						
							| 
									
										
										
										
											2013-01-23 07:52:31 +00:00
										 |  |  |         obj_act = context.active_object | 
					
						
							| 
									
										
										
										
											2013-01-28 12:24:25 +00:00
										 |  |  |         change = False | 
					
						
							| 
									
										
										
										
											2013-01-23 05:56:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-21 19:04:39 +00:00
										 |  |  |         if self.connection_pattern == 'CHAIN_DISTANCE': | 
					
						
							|  |  |  |             objs_sorted = [obj_act] | 
					
						
							|  |  |  |             objects_tmp = context.selected_objects | 
					
						
							| 
									
										
										
										
											2013-04-20 13:22:08 +00:00
										 |  |  |             try: | 
					
						
							| 
									
										
										
										
											2013-02-21 19:04:39 +00:00
										 |  |  |                 objects_tmp.remove(obj_act) | 
					
						
							| 
									
										
										
										
											2013-04-20 13:22:08 +00:00
										 |  |  |             except ValueError: | 
					
						
							|  |  |  |                 pass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-02-21 19:04:39 +00:00
										 |  |  |             last_obj = obj_act | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-20 13:22:08 +00:00
										 |  |  |             while objects_tmp: | 
					
						
							| 
									
										
										
										
											2013-02-21 19:04:39 +00:00
										 |  |  |                 objects_tmp.sort(key=lambda o: (last_obj.location - o.location).length) | 
					
						
							| 
									
										
										
										
											2013-04-20 13:22:08 +00:00
										 |  |  |                 last_obj = objects_tmp.pop(0) | 
					
						
							|  |  |  |                 objs_sorted.append(last_obj) | 
					
						
							| 
									
										
										
										
											2013-02-21 19:04:39 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             for i in range(1, len(objs_sorted)): | 
					
						
							| 
									
										
										
										
											2013-04-20 13:22:08 +00:00
										 |  |  |                 self._add_constraint(context, objs_sorted[i - 1], objs_sorted[i]) | 
					
						
							| 
									
										
										
										
											2013-02-23 23:40:51 +00:00
										 |  |  |                 change = True | 
					
						
							| 
									
										
										
										
											2013-02-21 19:04:39 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-28 19:33:14 +00:00
										 |  |  |         else:  # SELECTED_TO_ACTIVE | 
					
						
							| 
									
										
										
										
											2013-02-21 19:04:39 +00:00
										 |  |  |             for obj in objects: | 
					
						
							| 
									
										
										
										
											2013-02-23 23:40:51 +00:00
										 |  |  |                 self._add_constraint(context, obj_act, obj) | 
					
						
							| 
									
										
										
										
											2013-03-28 19:33:14 +00:00
										 |  |  |                 change = True | 
					
						
							| 
									
										
										
										
											2013-02-10 08:54:10 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-01-28 12:24:25 +00:00
										 |  |  |         if change: | 
					
						
							|  |  |  |             # restore selection | 
					
						
							|  |  |  |             bpy.ops.object.select_all(action='DESELECT') | 
					
						
							|  |  |  |             for obj in objects: | 
					
						
							| 
									
										
										
										
											2013-02-10 08:54:10 +00:00
										 |  |  |                 obj.select = True | 
					
						
							| 
									
										
										
										
											2013-01-28 12:24:25 +00:00
										 |  |  |             scene.objects.active = obj_act | 
					
						
							|  |  |  |             return {'FINISHED'} | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.report({'WARNING'}, "No other objects selected") | 
					
						
							|  |  |  |             return {'CANCELLED'} |