re-commit temp workaround [#35920], this still fails for OSX retina display,
[blender-staging.git] / release / scripts / startup / bl_operators / object_align.py
index 89b02fc98b05dbb3cfe921aa7761483016aef326..e843209da3cd76d5a707ea7364632b9a724e26ac 100644 (file)
 #
 # ##### END GPL LICENSE BLOCK #####
 
-# <pep8 compliant>
+# <pep8-80 compliant>
 
 import bpy
+from bpy.types import Operator
 from mathutils import Vector
-from blf import gettext as _
 
-def align_objects(align_x, align_y, align_z, align_mode, relative_to):
 
-    cursor = bpy.context.scene.cursor_location
+def GlobalBB_LQ(bb_world):
 
-    Left_Up_Front_SEL = [0.0, 0.0, 0.0]
-    Right_Down_Back_SEL = [0.0, 0.0, 0.0]
+    # Initialize the variables with the 8th vertex
+    left, right, front, back, down, up = (bb_world[7][0],
+                                          bb_world[7][0],
+                                          bb_world[7][1],
+                                          bb_world[7][1],
+                                          bb_world[7][2],
+                                          bb_world[7][2],
+                                          )
+
+    # Test against the other 7 verts
+    for i in range(7):
+
+        # X Range
+        val = bb_world[i][0]
+        if val < left:
+            left = val
+
+        if val > right:
+            right = val
+
+        # Y Range
+        val = bb_world[i][1]
+        if val < front:
+            front = val
+
+        if val > back:
+            back = val
+
+        # Z Range
+        val = bb_world[i][2]
+        if val < down:
+            down = val
+
+        if val > up:
+            up = val
+
+    return (Vector((left, front, up)), Vector((right, back, down)))
+
+
+def GlobalBB_HQ(obj):
+
+    matrix_world = obj.matrix_world.copy()
+
+    # Initialize the variables with the last vertex
+
+    verts = obj.data.vertices
+
+    val = matrix_world * verts[-1].co
+
+    left, right, front, back, down, up = (val[0],
+                                          val[0],
+                                          val[1],
+                                          val[1],
+                                          val[2],
+                                          val[2],
+                                          )
+
+    # Test against all other verts
+    for i in range(len(verts) - 1):
+
+        vco = matrix_world * verts[i].co
+
+        # X Range
+        val = vco[0]
+        if val < left:
+            left = val
+
+        if val > right:
+            right = val
+
+        # Y Range
+        val = vco[1]
+        if val < front:
+            front = val
+
+        if val > back:
+            back = val
+
+        # Z Range
+        val = vco[2]
+        if val < down:
+            down = val
+
+        if val > up:
+            up = val
+
+    return Vector((left, front, up)), Vector((right, back, down))
+
+
+def align_objects(context,
+                  align_x,
+                  align_y,
+                  align_z,
+                  align_mode,
+                  relative_to,
+                  bb_quality):
+
+    cursor = context.scene.cursor_location
+
+    Left_Front_Up_SEL = [0.0, 0.0, 0.0]
+    Right_Back_Down_SEL = [0.0, 0.0, 0.0]
 
     flag_first = True
 
-    objs = []
+    objects = []
 
-    for obj in bpy.context.selected_objects:
-        matrix_world = obj.matrix_world
-        bb_world = [Vector(v[:]) * matrix_world for v in obj.bound_box]
-        objs.append((obj, bb_world))
+    for obj in context.selected_objects:
+        matrix_world = obj.matrix_world.copy()
+        bb_world = [matrix_world * Vector(v[:]) for v in obj.bound_box]
+        objects.append((obj, bb_world))
 
-    if not objs:
+    if not objects:
         return False
 
-    for obj, bb_world in objs:
-        Left_Up_Front = bb_world[1]
-        Right_Down_Back = bb_world[7]
+    for obj, bb_world in objects:
+
+        if bb_quality and obj.type == 'MESH':
+            GBB = GlobalBB_HQ(obj)
+        else:
+            GBB = GlobalBB_LQ(bb_world)
+
+        Left_Front_Up = GBB[0]
+        Right_Back_Down = GBB[1]
 
         # Active Center
 
-        if obj == bpy.context.active_object:
+        if obj == context.active_object:
 
-            center_active_x = (Left_Up_Front[0] + Right_Down_Back[0]) / 2.0
-            center_active_y = (Left_Up_Front[1] + Right_Down_Back[1]) / 2.0
-            center_active_z = (Left_Up_Front[2] + Right_Down_Back[2]) / 2.0
+            center_active_x = (Left_Front_Up[0] + Right_Back_Down[0]) / 2.0
+            center_active_y = (Left_Front_Up[1] + Right_Back_Down[1]) / 2.0
+            center_active_z = (Left_Front_Up[2] + Right_Back_Down[2]) / 2.0
 
-            size_active_x = (Right_Down_Back[0] - Left_Up_Front[0]) / 2.0
-            size_active_y = (Right_Down_Back[1] - Left_Up_Front[1]) / 2.0
-            size_active_z = (Left_Up_Front[2] - Right_Down_Back[2]) / 2.0
+            size_active_x = (Right_Back_Down[0] - Left_Front_Up[0]) / 2.0
+            size_active_y = (Right_Back_Down[1] - Left_Front_Up[1]) / 2.0
+            size_active_z = (Left_Front_Up[2] - Right_Back_Down[2]) / 2.0
 
         # Selection Center
 
         if flag_first:
             flag_first = False
 
-            Left_Up_Front_SEL[0] = Left_Up_Front[0]
-            Left_Up_Front_SEL[1] = Left_Up_Front[1]
-            Left_Up_Front_SEL[2] = Left_Up_Front[2]
+            Left_Front_Up_SEL[0] = Left_Front_Up[0]
+            Left_Front_Up_SEL[1] = Left_Front_Up[1]
+            Left_Front_Up_SEL[2] = Left_Front_Up[2]
 
-            Right_Down_Back_SEL[0] = Right_Down_Back[0]
-            Right_Down_Back_SEL[1] = Right_Down_Back[1]
-            Right_Down_Back_SEL[2] = Right_Down_Back[2]
+            Right_Back_Down_SEL[0] = Right_Back_Down[0]
+            Right_Back_Down_SEL[1] = Right_Back_Down[1]
+            Right_Back_Down_SEL[2] = Right_Back_Down[2]
 
         else:
             # X axis
-            if Left_Up_Front[0] < Left_Up_Front_SEL[0]:
-                Left_Up_Front_SEL[0] = Left_Up_Front[0]
+            if Left_Front_Up[0] < Left_Front_Up_SEL[0]:
+                Left_Front_Up_SEL[0] = Left_Front_Up[0]
             # Y axis
-            if Left_Up_Front[1] < Left_Up_Front_SEL[1]:
-                Left_Up_Front_SEL[1] = Left_Up_Front[1]
+            if Left_Front_Up[1] < Left_Front_Up_SEL[1]:
+                Left_Front_Up_SEL[1] = Left_Front_Up[1]
             # Z axis
-            if Left_Up_Front[2] > Left_Up_Front_SEL[2]:
-                Left_Up_Front_SEL[2] = Left_Up_Front[2]
+            if Left_Front_Up[2] > Left_Front_Up_SEL[2]:
+                Left_Front_Up_SEL[2] = Left_Front_Up[2]
 
             # X axis
-            if Right_Down_Back[0] > Right_Down_Back_SEL[0]:
-                Right_Down_Back_SEL[0] = Right_Down_Back[0]
+            if Right_Back_Down[0] > Right_Back_Down_SEL[0]:
+                Right_Back_Down_SEL[0] = Right_Back_Down[0]
             # Y axis
-            if Right_Down_Back[1] > Right_Down_Back_SEL[1]:
-                Right_Down_Back_SEL[1] = Right_Down_Back[1]
+            if Right_Back_Down[1] > Right_Back_Down_SEL[1]:
+                Right_Back_Down_SEL[1] = Right_Back_Down[1]
             # Z axis
-            if Right_Down_Back[2] < Right_Down_Back_SEL[2]:
-                Right_Down_Back_SEL[2] = Right_Down_Back[2]
+            if Right_Back_Down[2] < Right_Back_Down_SEL[2]:
+                Right_Back_Down_SEL[2] = Right_Back_Down[2]
 
-    center_sel_x = (Left_Up_Front_SEL[0] + Right_Down_Back_SEL[0]) / 2.0
-    center_sel_y = (Left_Up_Front_SEL[1] + Right_Down_Back_SEL[1]) / 2.0
-    center_sel_z = (Left_Up_Front_SEL[2] + Right_Down_Back_SEL[2]) / 2.0
+    center_sel_x = (Left_Front_Up_SEL[0] + Right_Back_Down_SEL[0]) / 2.0
+    center_sel_y = (Left_Front_Up_SEL[1] + Right_Back_Down_SEL[1]) / 2.0
+    center_sel_z = (Left_Front_Up_SEL[2] + Right_Back_Down_SEL[2]) / 2.0
 
     # Main Loop
 
-    for obj, bb_world in objs:
+    for obj, bb_world in objects:
+        matrix_world = obj.matrix_world.copy()
+        bb_world = [matrix_world * Vector(v[:]) for v in obj.bound_box]
 
-        loc_world = obj.location
-        bb_world = [Vector(v[:]) * obj.matrix_world for v in obj.bound_box]
+        if bb_quality and obj.type == 'MESH':
+            GBB = GlobalBB_HQ(obj)
+        else:
+            GBB = GlobalBB_LQ(bb_world)
 
-        Left_Up_Front = bb_world[1]
-        Right_Down_Back = bb_world[7]
+        Left_Front_Up = GBB[0]
+        Right_Back_Down = GBB[1]
 
-        center_x = (Left_Up_Front[0] + Right_Down_Back[0]) / 2.0
-        center_y = (Left_Up_Front[1] + Right_Down_Back[1]) / 2.0
-        center_z = (Left_Up_Front[2] + Right_Down_Back[2]) / 2.0
+        center_x = (Left_Front_Up[0] + Right_Back_Down[0]) / 2.0
+        center_y = (Left_Front_Up[1] + Right_Back_Down[1]) / 2.0
+        center_z = (Left_Front_Up[2] + Right_Back_Down[2]) / 2.0
 
-        positive_x = Right_Down_Back[0]
-        positive_y = Right_Down_Back[1]
-        positive_z = Left_Up_Front[2]
+        positive_x = Right_Back_Down[0]
+        positive_y = Right_Back_Down[1]
+        positive_z = Left_Front_Up[2]
 
-        negative_x = Left_Up_Front[0]
-        negative_y = Left_Up_Front[1]
-        negative_z = Right_Down_Back[2]
+        negative_x = Left_Front_Up[0]
+        negative_y = Left_Front_Up[1]
+        negative_z = Right_Back_Down[2]
 
         obj_loc = obj.location
 
@@ -230,40 +338,48 @@ def align_objects(align_x, align_y, align_z, align_mode, relative_to):
     return True
 
 
-from bpy.props import EnumProperty
+from bpy.props import EnumProperty, BoolProperty
 
 
-class AlignObjects(bpy.types.Operator):
-    '''Align Objects'''
+class AlignObjects(Operator):
+    """Align Objects"""
     bl_idname = "object.align"
-    bl_label = _("Align Objects")
+    bl_label = "Align Objects"
     bl_options = {'REGISTER', 'UNDO'}
 
-    align_mode = EnumProperty(items=(
-            ('OPT_1', "Negative Sides", ""),
-            ('OPT_2', "Centers", ""),
-            ('OPT_3', "Positive Sides", "")),
-        name="Align Mode:",
-        description="",
-        default='OPT_2')
-
-    relative_to = EnumProperty(items=(
-            ('OPT_1', "Scene Origin", ""),
-            ('OPT_2', "3D Cursor", ""),
-            ('OPT_3', "Selection", ""),
-            ('OPT_4', "Active", "")),
-        name="Relative To:",
-        description="",
-        default='OPT_4')
-
-    align_axis = EnumProperty(items=(
-            ('X', "X", ""),
-            ('Y', "Y", ""),
-            ('Z', "Z", ""),
-            ),
-                name="Align",
-                description="Align to axis",
-                options={'ENUM_FLAG'})
+    bb_quality = BoolProperty(
+            name="High Quality",
+            description=("Enables high quality calculation of the "
+                         "bounding box for perfect results on complex "
+                         "shape meshes with rotation/scale (Slow)"),
+            default=True,
+            )
+    align_mode = EnumProperty(
+            name="Align Mode:",
+            items=(('OPT_1', "Negative Sides", ""),
+                   ('OPT_2', "Centers", ""),
+                   ('OPT_3', "Positive Sides", ""),
+                   ),
+            default='OPT_2',
+            )
+    relative_to = EnumProperty(
+            name="Relative To:",
+            items=(('OPT_1', "Scene Origin", ""),
+                   ('OPT_2', "3D Cursor", ""),
+                   ('OPT_3', "Selection", ""),
+                   ('OPT_4', "Active", ""),
+                   ),
+            default='OPT_4',
+            )
+    align_axis = EnumProperty(
+            name="Align",
+            description="Align to axis",
+            items=(('X', "X", ""),
+                   ('Y', "Y", ""),
+                   ('Z', "Z", ""),
+                   ),
+            options={'ENUM_FLAG'},
+            )
 
     @classmethod
     def poll(cls, context):
@@ -271,7 +387,13 @@ class AlignObjects(bpy.types.Operator):
 
     def execute(self, context):
         align_axis = self.align_axis
-        ret = align_objects('X' in align_axis, 'Y' in align_axis, 'Z' in align_axis, self.align_mode, self.relative_to)
+        ret = align_objects(context,
+                            'X' in align_axis,
+                            'Y' in align_axis,
+                            'Z' in align_axis,
+                            self.align_mode,
+                            self.relative_to,
+                            self.bb_quality)
 
         if not ret:
             self.report({'WARNING'}, "No objects with bound-box selected")