############################################################################################################
#
# Copyright (c) 2012 Kofi Garbrah 
#
# Provided under The MIT License http://opensource.org/licenses/mit-license.php
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
# 
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Description:
# This blender add-on finds the lowest point on the selected object(s) and aligns that lowest point
# to the ground plane (y = 0 in most 3D software; z = 0 in blender) while maintaining relationships
# between selected objects.
# 
# 1) Imports and Global variables
# 2) Model
# 3) View (Panel)
# 4) Controller (button)
#
############################################################################################################

bl_info = {
    "name": "Bring 2 Floor",
    "author": "Kofi Garbrah",
    "version": (2,0),
    "blender": (2, 63, 0),
    "location": "View3D > Properties > Bring2Floor",
    "description": "Brings the selected item to the floor. Moves attached items as well.",
    "warning": "",
    "wiki_url": "http://www.4colorgrafix.net/bpy#B2F",
    "tracker_url": "http://www.4colorgrafix.net/bpy#B2F",
    "category": "System"}
    
#
# 1) Imports and Global variables
#

import bpy
from bpy.props import *

import math
from math import *
from mathutils import *
from mathutils import Vector
from mathutils import Matrix

import time 

############################################################################################################

#
# 2) Model
#

class Drop2FloorModel:

    #   
    # Set location to the ground. 
    #                     
    def drop( self, context):
        print( "\n\nSet mesh location to the ground" )

        maxY = float("-inf")
        minY = float("inf") 
        i = 0

        # Set orientation of figure
        bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)

        # find highest point and lowest point in the selected meshes
        for obj in context.selected_objects:
            if obj.type == "MESH":
               for v in obj.data.vertices:
                  vec = self.transpose_yz( v.co )
                  y = vec[1]
                  if maxY < y:
                     maxY = y
                  if minY > y:
                     minY = y

        # Drop figure to ground        
        for obj in context.selected_objects:
            if obj.type == "MESH":
               old_loc = self.transpose_yz( obj.location )
               obj.location = old_loc - self.transpose_yz( Vector( (0.0, minY, 0.0) ) )
        
        bpy.ops.object.transform_apply(location=True, rotation=False, scale=False)

    #
    # Transposes Y-axis with Z-axis. 
    #
    # In most 3d software, the Z-axis represents depth not height. For example 
    # look at ZBrush, Silo, Maya, Cinema4D, and OpenGL. Also look up the terms 
    # Z-Buffering and Z-Culling.
    #
    # Blender uses the Z-axis to represent height on the screen but the camera in 
    # Blender uses the Z-axis to represent depth for rendering. This routine
    # compensates for Blender's quirk.
    #   
    def transpose_yz( self, v ):
        return Vector( (v[0], v[2], v[1] ) )

############################################################################################################

#
# 3) View
#

d2f = Drop2FloorModel()

# Panel
class Drop2FloorView(bpy.types.Panel):

    """Creates a Panel in the VIEW 3D UI"""
    bl_idname = "Drop 2 Floor"
    bl_label = "Bring 2 Floor"
    bl_space_type = "VIEW_3D"
    bl_region_type = "UI"
    bl_context = "object"

    # Draws layout for Panel
    def draw(self, context):
        layout = self.layout

        if len(context.selected_objects) > 0:
           row = layout.row()
           row.alignment = "EXPAND"        
           row.operator("drop2floor.button", text="Bring Selected Figures To Floor")

############################################################################################################

#
# 4) Controller
#                

#   Button
class Drop2FloorController(bpy.types.Operator):
    bl_idname = "drop2floor.button"
    bl_label = "D2F Button"

    def execute(self, context):
        time_start = time.time()
        d2f.drop(context)
        print("Execution time: {:.4} sec".format(time.time() - time_start) )

        return{'FINISHED'}    
 
#    Registration

def register():
    bpy.utils.register_module(__name__)

def unregister():
    bpy.utils.unregister_module(__name__)

if __name__ == '__main__':
    register()
