#!BPY

"""
Name: 'Flotacion'
Blender: 248
Group: 'Animation'
Tooltip: 'Set a floating object.'
"""

__author__ = ["Theodore K Schundler"]
__url__ = ("http://wiki.blender.org/index.php/Scripts/Manual/Animation/Floctation")
__version__ = "0.10 - 2009"

__bpydoc__ = """\
This script creates a floating object, keeping the center of gravity from the "boat" 
Equal to the height of the "Sea" Mesh.

It can be best to modify the original scene, 

but if your prepared to set up the script properly it will work with any set up.

"""
################################################
#
# Flotacion 0.10 by Theodore K Schundler
#

 #  ***** 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 3 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, see <http://www.gnu.org/licenses/>.
 #
 #  The Original Code is Copyright (C) <date>-<date> by <name of copyright holder>	###
 #  All rights reserved.
 #
 #  Contact:      <name>@<domain.ext>	###
 #  Information:  http://<domain>.<ext>	###
 #
 #  The Original Code is: all of this file.
 #
 #  Contributor(s): none yet.
 #
 #  ***** END GPL LICENSE BLOCK *****
################################################
# Este script va a calcular la posicion de
# equilibrio de un cuerpo flotando en el mar
################################################

################################################
# Importing Blender libraries
################################################

import Blender
from Blender import *
from Blender import NMesh
from Blender.BGL import *
from Blender.Draw import *

import math
from math import *

# Parameters
T_Weight		= Create(4.0)
T_Density		= Create(1.0)
T_Boat			= Create("Boat2")
T_Water			= Create("Sea")
T_Volume		= Create(0.0)

T_Start			= Create(1)
T_End			= Create(250)

T_TolRot		= Create(0.7)
T_TolTras		= Create(5.0)
T_ItTras		= Create(0.2)
T_ItRot			= Create(0.05)
T_TolItMax		= Create(30)

pxyz 			= Blender.Mathutils.Vector(0.0,0.0,0.0)
exyz			= Blender.Mathutils.Vector(0.0,0.0,0.0)


# Events
EVENT_NOEVENT	= 1
EVENT_CALCULATE	= 2
EVENT_EXIT		= 3
EVENT_VOLUME	= 4
EVENT_ANIM		= 5

################################################
# Drawing the GUI
################################################
def draw():
	global T_Weight
	global T_Density
	global T_Boat
	global T_Water
	
	global T_Start
	global T_End
	
	global T_TolRot
	global T_TolTras
	global T_ItTras
	global T_ItRot
	global T_TolItMax
	
	global EVENT_NOEVENT,EVENT_CALCULATE,EVENT_EXIT,EVENT_VOLUME
	
	############# Titles
	glClear (GL_COLOR_BUFFER_BIT)
	glRasterPos2d(8, 400)
	Text("Flotacion Script")
	
	############# Parameters GUI Buttons
	glRasterPos2d(8, 373)
	Text("Parameters:")
		
	T_Water			= String("Water: ", EVENT_NOEVENT, 10, 135, 210, 18,
						T_Water.val,30,"Name of the water mesh");	
	T_Boat			= String("Boat: ", EVENT_NOEVENT, 10, 115, 210, 18,
						T_Boat.val,30,"Name of the floating mesh");
	T_Weight		= Number("Weight: ", EVENT_NOEVENT, 10, 95, 210, 18,
						T_Weight.val, 0.005, 1000, "Weight of the body");
	T_Density		= Number("Density: ", EVENT_NOEVENT, 10, 75, 210, 18,
						T_Density.val, 0.05, 5, "Density of the fluid");
	T_Start			= Number("Start: ", EVENT_NOEVENT, 10, 260, 100, 18,
						T_Start.val, 1, 1000, "Start frame");
	T_End			= Number("End: ", EVENT_NOEVENT, 110, 260, 100, 18,
						T_End.val, 1, 1000, "End frame");

	T_TolRot		= Number("TolRot: ", EVENT_NOEVENT, 10, 155, 100, 18,
						T_TolRot.val, 0.001, 10, "Rotation tolerance");
	T_TolTras		= Number("TolMove: ", EVENT_NOEVENT, 110, 155, 100, 18,
						T_TolTras.val, 0.001, 20, "Movement tolerance");
	T_ItTras		= Number("ItTras: ", EVENT_NOEVENT, 10, 175, 100, 18,
						T_ItTras.val, 0.001, 10, "Movement step iteration");
	T_ItRot			= Number("ItRot: ", EVENT_NOEVENT, 110, 175, 100, 18,
						T_ItRot.val, 0.001, 10, "Rotation step iteration");
	T_TolItMax		= Number("TolItMax: ", EVENT_NOEVENT, 10, 195, 100, 18,
						T_TolItMax.val, 1, 300, "Maximum number of iterations");



	############# Draw and Exit Buttons
	Button("Estatico",EVENT_CALCULATE , 10, 290, 80, 18)
	Button("Exit",EVENT_EXIT , 10, 10, 80, 18)
	Button("Volume",EVENT_VOLUME, 10, 320, 80, 18) 
	Button("Anim",EVENT_ANIM, 110, 290, 80, 18)
	
	
def event(evt, val):
	if (evt == QKEY and not val):
		Exit()
			
def bevent(evt):
	global T_Weight
	global T_Density
	global T_Boat
	global T_Water
	global EVENT_NOEVENT,EVENT_CALCULATE,EVENT_EXIT,EVENT_VOLUME,EVENT_ANIM
		
	######## Manages GUI events
	if (evt == EVENT_EXIT):
		Exit()
		
############################################
### Calculates the volume of the sunken part
	elif (evt == EVENT_VOLUME):
		boat = Blender.Object.Get(T_Boat.val)
		water = Blender.Object.Get(T_Water.val)
		
		from megabool_r14_lea import Intersection
		Intersection(boat, water)
		vol = Volumen()
		print vol
		BGL.glRasterPos2i(10, 230)
		Text("The volume of the sunken part is %f" % vol,"large")
		DestruirTemp()


#############################################
### Static calculation
	elif (evt == EVENT_CALCULATE):
		
		boat = Blender.Object.Get(T_Boat.val)
		water = Blender.Object.Get(T_Water.val)
		iter = TRUE
		j = 0
		
		while iter:
			j = j + 1
			# Intersects boat object with the sea object		
			from megabool_r14_lea import Intersection
			Intersection(boat, water)
			# Center of gravity
			pxyz = CentroDeMasas(T_Boat.val)
			# Volume of the sunken part
			vol = Volumen()
			# Center of buoyancy
			exyz = CentroDeEmpuje(vol)
			# Equilibrating the mesh
			iter = Equilibrio(T_Boat.val,pxyz,T_Weight.val,exyz,vol,T_Density.val)
			# Erasing temp data
			if j > T_TolItMax.val:
				iter = FALSE
			DestruirTemp()		
			Blender.Redraw()
			
##################################################
###  Animation			
	elif (evt == EVENT_ANIM):
		i = T_Start.val
		scn = Scene.GetCurrent()
		context = scn.getRenderingContext()
		boat = Blender.Object.Get(T_Boat.val)
		water = Blender.Object.Get(T_Water.val)
		curve = boat.getIpo()
		
		curve[Ipo.OB_LOCZ] = None
		curve.addCurve("LocZ")
		posz = curve.getCurve("LocZ")
		posz.setInterpolation("Linear")
		
		curve[Ipo.OB_ROTX] = None
		curve.addCurve("RotX")
		rotx = curve.getCurve("RotX")
		rotx.setInterpolation("Linear")
		
		curve[Ipo.OB_ROTY] = None
		curve.addCurve("RotY")
		roty = curve.getCurve("RotY")
		roty.setInterpolation("Linear")
				
		curve[Ipo.OB_ROTZ] = None
		curve.addCurve("RotZ")
		rotz = curve.getCurve("RotZ")
		rotz.setInterpolation("Linear")
		
		while i <= T_End.val:
			print "FRAME:", i
			context.currentFrame(i)
			iter = TRUE
			#glClear (GL_COLOR_BUFFER_BIT)
			Blender.Redraw()
			# provisional
			BGL.glRasterPos2i(300, 420 - 10 * i)
			Text("Frame %d" % i,"large")
			j = 0		
			
			while iter:
				j = j + 1
				# Interseccion del objeto con la superficie del agua		
				from megabool_r14_lea import Intersection
				Intersection(boat, water)
				# Calculo del centro de gravedad
				pxyz = CentroDeMasas(T_Boat.val)
				# Calculo del volumen sumergido
				vol = Volumen()
				print "Volumen =", vol
				# Calculo del centro de empuje
				exyz = CentroDeEmpuje(vol)
				# Acercamiento al equilibrio
				iter = Equilibrio(T_Boat.val,pxyz,T_Weight.val,exyz,vol,T_Density.val)
				# Destruccion de las mallas
				DestruirTemp()	
				# Criterios de parada
				if j > T_TolItMax.val:
					iter = FALSE
				if (evt == QKEY):
					Exit()
				
				
				Blender.Redraw()		
			
			posz.addBezier((i,boat.LocZ))
			rotx.addBezier((i,boat.RotX * 180 / (10 * pi)))
			roty.addBezier((i,boat.RotY * 180 / (10 * pi)))
			rotz.addBezier((i,boat.RotZ * 180 / (10 * pi)))	
						
			#Blender.Redraw()
			i = i + 1

		boat.setIpo(curve)
			
					
Register(draw, event, bevent)
		
########################################################
########################################################


##### Returns the gravity centre of the mesh
def CentroDeMasas(Boat):
	boat = Blender.Object.Get(Boat)
	pxyz = Blender.Mathutils.Vector(0.0,0.0,0.0)
	pxyz.x = boat.LocX
	pxyz.y = boat.LocY
	pxyz.z = boat.LocZ
	return (pxyz)
	
		
##### Calculates the gravity centre of the sunken part
def CentroDeEmpuje(vol):
	
	under = Blender.Object.GetSelected()[0]
	mesh = Mesh.Get(under.data.name)
	exyz = Blender.Mathutils.Vector(0.0,0.0,0.0)
	
	for face in mesh.faces:
		normal = face.no * face.area
		fo = face.cent
		exyz.x = exyz.x + normal.x * fo.x * fo.x / 2 	
		exyz.y = exyz.y + normal.y * fo.y * fo.y / 2 	
		exyz.z = exyz.z + normal.z * fo.z * fo.z / 2 	
		
	exyz = exyz / vol
	return (exyz)
		
##### Calculates the volume of the sunken mesh	
def Volumen():
	

	object = Blender.Object.GetSelected()[0]
	objname = object.name
	meshname = object.data.name
	mesh = Mesh.Get(meshname)
	volume = 0
	
	# Transforming faces into triangles
	mesh.hide = 0
	mesh.sel = 1
	mesh.quadToTriangle(0)
	
	for face in mesh.faces:
		v1, v2, v3 = face.v
		v1, v2, v3 = v1.co, v2.co, v3.co
		e1 = v1-v2
		e2 = v3-v2
		bc = Blender.Mathutils.CrossVecs(e1,e2)
		# Vol = (1/6)|A.(B x C)|
		volume = volume + Blender.Mathutils.DotVecs(v1, bc) / 6 
		
	if (volume < 0):
		volume = -volume
		
	return volume

##### Equilibrates the mesh into the fluid
def Equilibrio(boat, pxyz, peso, exyz, vol, densidad):
	
	object = Blender.Object.Get(boat)
	#constantes de translacion/rotacion
	tr = T_ItTras.val / vol
	r = T_ItRot.val	
	tolrot = T_TolRot.val
	toltras = T_TolTras.val
	empuje = vol * densidad
	seguimos = FALSE
	
	print "volumen:", vol

	# Movement
	fneta = empuje - peso
	print "fuerza neta:", fneta,fneta*tr
	if fneta > toltras or fneta < -toltras:
		object.setLocation(object.LocX, object.LocY, object.LocZ + fneta*tr)
		seguimos = TRUE
	else:
		object.setLocation(object.LocX, object.LocY, object.LocZ)
	
	# Rotation
	print "exyz:",exyz
	print "pxyz:",pxyz
	ejeflot = Blender.Mathutils.Vector(exyz[0]-pxyz[0], exyz[1]-pxyz[1], exyz[2]-pxyz[2])
	print "ejeflot:",ejeflot
	empuje = Blender.Mathutils.Vector(0.0,0.0,empuje)
	giro = Blender.Mathutils.CrossVecs(ejeflot, empuje)
	giro = giro / (3*vol)
	print "giro:",giro
		
	if giro.x > tolrot or giro.x < -tolrot:
		rotx = object.RotX + r * giro.x
		seguimos = TRUE
	else:
		rotx = object.RotX
		
	if giro.y > tolrot or giro.y < -tolrot:
		roty = object.RotY + r * giro.y
		seguimos = TRUE
	else:
		roty = object.RotY
		
	if giro.z > tolrot or giro.z < -tolrot:
		rotz = object.RotZ + r * giro.z
		seguimos = TRUE
	else:
		rotz = object.RotZ
		
	object.setEuler(rotx, roty, rotz)
	
	return seguimos
			
			
##### Destroys the temporal meshes			
def DestruirTemp():
	scn = Scene.GetCurrent()
	object = Blender.Object.GetSelected()[0]
	scn.objects.unlink(object)