#!BPY

"""
Name: 'Taper & Twist'
Blender: 248
Group: 'Object'
Tooltips: 'Taper or/and twist on selected vertices'
"""
__author__ = "flippyneck"
__url__ = ("http://wiki.blender.org/index.php/Extensions:Py/Scripts/Manual/Object/tapertwist")
__version__ = "1.0 01/27/2004"
__bpydoc__ = """\

this script should be fairly self-explanatory.

Select a single mesh object (it must be a mesh),

and follow the instructions on the GUI.

WARNING!  This script currently overwrites your mesh; 

copy it or save your file before you run the script.

If this script proves popular I will correct this in the next version. 


"""

# -------------------------------------------------------------------------- 
# taper & Twist release 1.0 by flippyneck 27th january 2004
# -------------------------------------------------------------------------- 
# ***** 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. 
# 
# ***** END GPL LICENCE BLOCK ***** 
# --------------------------------------------------------------------------

import Blender
from Blender import Draw, BGL, NMesh, Object
from math import sin, cos, sqrt, pi

#	------------------------------------
#	some handy definitions and functions.
#	thanks to theeth who's matrix functions (vecf.py) i 
#	either used as a basis for my own, or plundered
#	wholesale.

zero = 0.0000000001
mesh = []
meOr = []

def matrix4x4():
    ''' Returns an empty 4x4 matrix (all vals==0) '''
    mat=[]
    for n in range(0, 4):
        mat.append([0 for n in range(0, 4)])
    return mat

def idmatrix4x4():
    ''' ... '''
    mat=matrix4x4()
    for n in range(0, 4):
        mat[n][n]=1
    return mat

#nicked from theeth's vecf.py...
def makeRotMtx(axis, angle): 
   if axis=='X' or axis == 'x':    
      rs, rc = sin(angle), cos(angle) 
      mtx = [[1.0, 0.0, 0.0], 
            [0.0,  rc, rs], 
            [0.0, -rs, rc] ] 
   elif axis=='Y' or axis == 'y': 
      rs, rc = sin(angle), cos(angle) 
      mtx = [   [ rc, 0.0, -rs], 
            [0.0, 1.0, 0.0], 
            [ rs, 0.0,  rc] ] 
   else: 
      rs, rc = sin(angle), cos(angle) 
      mtx = [   [ rc,  rs, 0.0], 
            [-rs,  rc, 0.0], 
            [0.0, 0.0, 1.0] ] 
   return mtx 

def vecxmat(v, m):
	''' multiplies homogenous 3d vector by 4x4 matrix '''
	r=[0,0,0]
	r[0]=(v[0]*m[0][0])+(v[1]*m[1][0])+(v[2]*m[2][0])#+(v[3]*m[3][0])
	r[1]=(v[0]*m[0][1])+(v[1]*m[1][1])+(v[2]*m[2][1])#+(v[3]*m[3][1])
	r[2]=(v[0]*m[0][2])+(v[1]*m[1][2])+(v[2]*m[2][2])#+(v[3]*m[3][2])
	#r[3]=(v[0]*m[0][3])+(v[1]*m[1][3])+(v[2]*m[2][3])#+(v[3]*m[3][3])
	return r

def homovec(v):
	''' converts vector to homogenous vector '''
	hv=[]
	for c in v:
		hv.append(c)
	hv.append(1)
	return hv

def lscalematrix4x4(scale_factor):
	''' 
		if tuple given, returns local scale matrix using (sx, sy, sz) '''

	sm=idmatrix4x4()
	sm[0]=[scale_factor[0], 0, 0, 0]
	sm[1]=[0, scale_factor[1], 0, 0]
	sm[2]=[0, 0, scale_factor[2], 0]
	sm[3]=[0, 0, 0, 1]
	return sm

def interpolateLinear(min, max, val):
	''' returns 0.0 <= val <=1.0 '''
	return val/float((max-min))
		
def interpolateSin(min, max, val):
	n=interpolateLinear(min, max, val)
	return sin(n)

def interpolateCos(min, max, val):
	n=interpolateLinear(min, max, val)
	return cos(n) 

def interpolateSquared(min, max, val):
	n=interpolateLinear(min, max, val)
	return n**2

def interpolateRoot(min, max, val):
	n=interpolateLinear(min, max, val)
	return sqrt(n)

def axisExtremes(mesh, axis):
	''' takes a mesh object and returns a 3-tuple containing
		the two extremes of the bbox along that axis along
		with the distance betwen them '''

	if axis == 'x':
		i=0
	elif axis == 'y':
		i = 1
	elif axis == 'z':
		i=2

	min=max=0
	
	for v in mesh.verts:
		if v.co[i] < min: min=v.co[i]
		if v.co[i] > max: max=v.co[i]
		
	#	find length along axis

	length=max-min
	
	return (min, max, length)


#	taper and twist funcs ----------------------------------------

def taper(axis, startdef, enddef, interfunc):
	''' tapers the selected mesh along the given axis according
		to provided args '''

	global obj, mesh, meOr

#	obj=Blender.Object.GetSelected()[0]
#	mesh = obj.getData()


	ae = axisExtremes(meOr, axis)
	min = ae[0]
	max = ae[1]
	axis_length = ae[2]

	if axis == 'x':
		i=0
	elif axis == 'y':
		i = 1
	elif axis == 'z':
		i=2

	Vsel = getSelectedVerts(meOr)
	for v in Vsel:
		dist=v.co[i]-min
		if -zero < dist < zero: dist=zero
		
		sf=interfunc(0, axis_length, dist)
		sf=startdef+(sf*(enddef-startdef))
		sf=1-sf
		
		if axis == 'x':
			sf = (1,sf, sf)
		elif axis == 'y':
			sf = (sf, 1, sf)
		else:
			sf = (sf, sf, 1)

		sm=lscalematrix4x4(sf)
		hv=homovec(v.co)
		new=vecxmat(hv, sm)
	
		mesh.verts[v.index].co[0]=new[0]
		mesh.verts[v.index].co[1]=new[1]
		mesh.verts[v.index].co[2]=new[2]

	NMesh.PutRaw(mesh,mesh.name)
	#mesh.update()
	obj.makeDisplayList()

def twist(axis, startdef, enddef, interfunc):
	''' twists the selected mesh along the given axis according
		to provided args '''

	global obj, mesh, meOr

#	obj=Blender.Object.GetSelected()[0]
#	mesh = obj.getData()


	ae = axisExtremes(meOr, axis)
	min = ae[0]
	max = ae[1]
	axis_length = ae[2]

	if axis == 'x':
		i=0
	elif axis == 'y':
		i = 1
	elif axis == 'z':
		i=2

	Vsel = getSelectedVerts(meOr)
	for v in Vsel:
		dist=v.co[i]-min
		if -zero < dist < zero: dist=zero
		
		ang=interfunc(0, axis_length, dist)
		ang=startdef+(ang*(enddef-startdef))
		ang=1-ang
		
		rm=makeRotMtx(axis, ang*2*pi)
		hv=homovec(v.co)
		new=vecxmat(hv, rm)
	
		mesh.verts[v.index].co[0]=new[0]
		mesh.verts[v.index].co[1]=new[1]
		mesh.verts[v.index].co[2]=new[2]

	NMesh.PutRaw(mesh,mesh.name)
	#mesh.update()
	obj.makeDisplayList()

##########################
def getSelectedVerts(me):#
##########################

	theSelVerts = []
	for v in me.verts:
		if v.sel:
			theSelVerts.append(v)

	return theSelVerts


######################
def InfoObj():       #
                     #
######################
  global obj, mesh, meOr, notObj, calque

  obj = Object.GetSelected()[0]

  print "Obj =", obj
  if (obj != []):
    if (obj.getType() == 'Mesh'):
      calque = obj.Layer                   # Le "layer" de l'object selectionne
      if obj.getType() == 'Mesh':
          mename = obj.getData().name
          mesh = obj.getData()
          meOr = obj.getData()
          notObj = 0
          return (obj,mename,mesh)
      else:
          #print "Not a Mesh object"
          notObj = 1
          return (notObj,0,0)
  else:
      print "No object selected"
      notObj = 1
      return (notObj,0,0)

##############
def Apply(): #
             #
##############
  global obj, mesh, meOr, startdef, enddef, notObj, calque

  meOr = obj.getData()
  obj.makeDisplayList()
  startdef.val = 0.0
  enddef.val = 0.0

#	Gui Stuff ---------------------------------------------------------------------

startdef = Draw.Create(0.1)
enddef = Draw.Create(0.9)

taperx = Draw.Create(1)
tapery = Draw.Create(0)
taperz = Draw.Create(0)

intlin = Draw.Create(1)
intsin = Draw.Create(0)
intsquare = Draw.Create(0)
intcos=Draw.Create(0)
introot=Draw.Create(0)
BGet = Draw.Create(0)
TTapTwi = Draw.Create(0)
TTapTwiTEXT = "Taper"

def gui():
	global startdef, enddef, TTapTwi, TTapTwiTEXT
	global taperx, tapery, taperz
	global intlin, intsin, intsquare, intcos, introot
	
	BGL.glClearColor(0.75, 0.75, 0.75, 1.0)
	BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)

	BGL.glColor3f(0.0,0.0,0.0)
	BGL.glRasterPos2i(5,420)
	Draw.Text("Taper and twist:  v1.0 by flippyneck")

	BGL.glColor3f(1.0,0.25,0.0)
	BGL.glRasterPos2i(5,400)
	Draw.Text("WARNING!:")
	BGL.glRasterPos2i(5,385)
	Draw.Text("This script will overwrite your mesh")
	BGL.glRasterPos2i(5,370)
	Draw.Text("Copy it or save your file first!")

	BGL.glColor3f(0.0,0.0,0.0)
	BGL.glRasterPos2i(5,330)
	Draw.Text("(1) Select a single MESH object:")

	BGL.glRasterPos2i(5,300)
	Draw.Text("(2) Select LOCAL axis to deform along:")

	taperx = Draw.Toggle("X", 41, 5, 270, 15, 15, taperx.val, "Deform along local x axis")
	tapery = Draw.Toggle("Y", 42, 25, 270, 15, 15, tapery.val, "Deform along local y axis")
	taperz = Draw.Toggle("Z", 43, 45, 270, 15, 15, taperz.val, "Deform along local z axis")

	BGL.glRasterPos2i(5,240)
	Draw.Text("(3) Adjust extent of deform at start and end:")

	startdef = Draw.Slider("Start: ", 10, 5, 210, 150, 15, startdef.val, 0.0, 1.0, 0, 'deformation extent at start') 
	enddef = Draw.Slider("End: ", 10, 5, 190, 150, 15, enddef.val, 0.0, 1.0, 0, 'deformation extent at end')

	BGL.glRasterPos2i(5,150)
	Draw.Text("(4) Choose an interpolation type:")

	intlin=Draw.Toggle("Linear", 20, 5, 120, 50, 15, intlin.val, "Deform object linearly along chosen axis")
	intsin=Draw.Toggle("Sin", 21, 60, 120, 50, 15, intsin.val, "Deform object by sinoidal progression along chosen axis")
	intcos=Draw.Toggle("Cos", 22, 115, 120, 50, 15, intcos.val, "Deform object by cosinal progression along chosen axis")
	intsquare=Draw.Toggle("Square", 23, 5, 100, 50, 15, intsquare.val, "Deform object by squared progression along chosen axis")
	introot=Draw.Toggle("Root", 24, 60, 100, 50, 15, introot.val, "Deform object by square root progression along chosen axis")

	BGL.glRasterPos2i(5,60)
	Draw.Text("(5) Go for it:")
	BGet = Draw.Button("Get Sel", 31, 5, 20, 50, 30, "Get selected object")
	TTapTwi = Draw.Toggle(TTapTwiTEXT, 32, 65, 20, 50, 30, TTapTwi.val, "Taper or Twist the selected object")
	Draw.Button("Apply", 35, 125, 20, 50, 30, "Apply transformation")
	Draw.Button("Quit", 34, 185, 20, 50, 30, "Quit this script")

def event(evt, val): 
	global obj, mesh, meOr

	if evt == Draw.QKEY and not val:
		if mesh:
			NMesh.PutRaw(meOr,mesh.name)
			obj.makeDisplayList()
		Draw.Exit()

def bevent(evt):
	global obj, mesh, meOr, startdef, enddef, TTapTwi, TTapTwiTEXT
	if evt == 34:
		if mesh:
			NMesh.PutRaw(meOr,mesh.name)
			obj.makeDisplayList()
		Draw.Exit()

	if evt == 35:
		if mesh == []:
			InfoObj()
		Apply()
		Draw.Register(gui, event, bevent)

	elif evt != 34 and evt != 35:

		if evt == 20:
			intsin.val=intcos.val=intsquare.val=introot.val=0
			Draw.Register(gui, event, bevent)

		elif evt == 21:
			intlin.val=intcos.val=intsquare.val=introot.val=0
			Draw.Register(gui, event, bevent)

		elif evt == 22:
			intlin.val=intsin.val=intsquare.val=introot.val=0
			Draw.Register(gui, event, bevent)

		elif evt == 23:
			intlin.val=intsin.val=intcos.val=introot.val=0
			Draw.Register(gui, event, bevent)

		elif evt == 24:
			intlin.val=intsin.val=intcos.val=intsquare.val=0
			Draw.Register(gui, event, bevent)
		
		elif evt == 31:
			InfoObj()


		elif evt == 32:
			if TTapTwi.val == 0:
				TTapTwiTEXT = "Taper"
				Draw.Register(gui, event, bevent)

			elif TTapTwi.val == 1:
				TTapTwiTEXT = "Twist"
				Draw.Register(gui, event, bevent)

		elif evt == 41:
			tapery.val = taperz.val = 0
			Draw.Register(gui, event, bevent)

		elif evt == 42:
			taperx.val = taperz.val = 0
			Draw.Register(gui, event, bevent)

		elif evt == 43:
			taperx.val = tapery.val = 0
			Draw.Register(gui, event, bevent)


		if mesh == []:
			InfoObj()
		if taperx.val:
			axis_to_use = 'x'
		elif tapery.val:
			axis_to_use = 'y'
		else:
			axis_to_use = 'z'

		if intlin.val:
			interpolation_to_use = interpolateLinear
		elif intsin.val:
			interpolation_to_use = interpolateSin
		elif intcos.val:
			interpolation_to_use = interpolateCos
		elif intsquare.val:
			interpolation_to_use = interpolateSquared
		elif introot.val:
			interpolation_to_use = interpolateRoot
		if TTapTwi.val == 1:
			twist(axis_to_use, startdef.val, enddef.val, interpolation_to_use)
		elif TTapTwi.val == 0:
			taper(axis_to_use, startdef.val, enddef.val, interpolation_to_use)



	
Draw.Register(gui, event, bevent)
