-- creation and function scripts for the interpolator plugin
-- theo 12/02/00
-- NB Ctrl+D will color script making it easier to read




--------------------------------------------------------------------
-- these functions handle the interpolation in the controller scripts
---------------------------------------------------------------------

fn matmult matrix n = 
	(
	return matrix3 	(matrix.row1 * n)\
			(matrix.row2 * n)\
			(matrix.row3 * n)\
			(matrix.row4 * n)
	)

fn matblend mat1 mat2 coeff = 
	(
	a = matmult (orthogonalize mat1) coeff
	b = matmult (orthogonalize mat2) (1-coeff)
	return (a + b)
	)
	
fn eulerblend mat1 mat2 coeff axis =
	(
	a = (orthogonalize mat1) as eulerangles
	print a
	b = (orthogonalize mat2) as eulerangles
	print b
	case axis of 
		(
		1:(return eulerangles ((a.x * coeff) + (b.x * (1 - coeff))) 0 0 )
		2:(return eulerangles 0 ((a.y * coeff) + (b.y * (1 - coeff))) 0 )
		3:(return eulerangles 0 0 ((a.z * coeff) + (b.z * (1 - coeff))) )
		)
	)
	

---------------------------------------------------------------------
-- this is the definition of the interpolator object
---------------------------------------------------------------------

Plugin Geometry InterpBox\
	name:"InterpBox"\
	extends:box\
	replaceUI:true\
	classID:#(0x84d5489a, 0x4f48a365)
(

parameters UI rollout:Display
	(
	height		type:#WorldUnits	ui:height 	default:10 	-- the dummy's height
	width		type:#WorldUnits	ui:width 	default:10 	-- "      " width
	length		type:#WorldUnits	ui:length	default:10 	-- "      " length
	stubAxis	type:#integer		ui:axis 	default:3  	-- which way the attachment points
	stubdist	type:#WorldUnits	ui:dist		default:10	-- how far away the stub is
	Stub		type:#node									-- the node of the stub point
	Sib			type:#node									-- the node of the lower bone
	Par 		type:#node									-- the node of the upper bone
	boneDist	type:#integer		ui:boneDist default:0	-- placement of a shear bone along centerline of link
	
	on height set val do
		(
		delegate.height = val
		if classof selection[1] == InterpBox do selection[1].objectoffsetpos = [0,0,(-.5 * val)]
		)
	on width set val do
		(delegate.width = val)
	on length set val do
		(delegate.length = val)
	on StubAxis set val do
		(if stub != undefined and selection.count == 1 do Stub.pos =  (#([1,0,0],[0,1,0],[0,0,1])[val] * StubDist)* $.transform)
	on StubDist set val do
		(if stub != undefined and selection.count == 1 do Stub.pos = (#([1,0,0],[0,1,0],[0,0,1])[StubAxis] * StubDist)* $.transform)
	)
	
rollout Display "Display"
	(
	group "Interpolator Object"
		(
		spinner height "Height " align:#center fieldwidth:40
		spinner width	"Width " align:#center fieldwidth:40
		spinner length	"Length" align:#center fieldwidth:40
		)
	group "Attachment Stub"
		(
		spinner dist "Distance " type:#float range:[-20,20,10] align:#center fieldwidth:32 
		radiobuttons Axis "Axis" labels:#("X ","Y ","Z ")
		)	
	group "Placement"
		(
		Spinner	BoneDist "Link % " type:#integer range:[0,100,50] fieldwidth:32 align:#center
		)		
	
	on BoneDist changed val do	-- positions the object along the centerline of a link if the bone is a shear bone
		(
		if selection.count == 1 do
			(
			if selection[1].shear == true do
				(
				selection[1].pos = ([1,0,0] * (selection[1].bonedist/100.0) )* selection[1].sib.objecttransform
				)
			)
		)
		
	)

parameters Behave rollout:Behavior
	(
	tension 	type:#float		ui:tension 		animatable:true 	default:.5 		-- the interpolation coefficient. 1 = move with top bone, 0 = with bottom
	shear		type:#boolean	ui:shear 		animatable:false	default:false	-- is this a shear bone?
	shear_axis	type:#integer	ui:shearaxis 						default:1		-- which is the shear axis? usually will be X
	)
	
rollout Behavior "Behavior"
	(

	group "Interpolation"
		(
		Spinner	tension	"Tension" type:#float range:[0,2,.5] fieldwidth:32 align:#center
		)
	group "Shear"
		(
		checkbox shear "This is a shear bone" align:#center
		radiobuttons shearAxis "Axis" labels:#("X ","Y ","Z ") enabled:false
		)
		
	on behave open do
		(
		shear_axis.enabled = shear.checked
		)
		
	on shear changed state do -- changes from a regular interpolator to a shear bone
		(
		if state == true then
			(
			selection[1].pos = ([1,0,0] * (selection[1].bonedist/100.0) )* selection[1].sib.objecttransform
			selection[1].parent = selection[1].sib
			ShearAxis.enabled = true
			Display.bonedist.enabled = true
			)
		else
			(
			
			selection[1].pos = selection[1].sib.transform.row4
			selection[1].parent = selection[1].par
			shearAxis.enabled = false
			Display.bonedist.enabled = false
			)
		)	
	)
)

----------------------------------------------------------------------
-- this function generates and positions an interpolator for two bones
----------------------------------------------------------------------
-- Note: I think this may be moved into the body of the Interp definition
-- later. I'm not sure if that's a better way to handle it.

fn MakeInterp bone1 bone2 =
	(
	
	--- find top bone	
	case of 
		(
		(bone2.parent == bone1): (topB = bone1; botB = bone2)
		(bone1.parent == bone2): (topB = bone2; botB = bone1)
		default: return "bones are not parent and child"
		)
		
	---- make a name for new object	
	try -- for bipeds
		( 
		root = (parentBipOf topB).name
		newName = case (bipstrip topB) of
			(
			"L Clavicle": " L Bicep"
			"L Forearm": " L Wrist"
			"R Clavicle": " R Bicep"
			"R Forearm": " R Wrist"	
			"Neck": " NeckSmooth"
			)
		)	
		
	catch -- for non-bipeds
		(
		root = ""
		newName = " Interpolator"
		)
		
	if newName == undefined then newname = uniqueName " Interpolator"	
	
	--- make and initialize a new object
	newInterp = interpBox name:(uniqueName(root + newName) )
	
	
	--- setup the script controller
	newinterp.rotation.controller = rotation_script()

	--- initialize the object		
	newInterp.wirecolor = topB.wirecolor
	newinterp.delegate.width = 10
	newinterp.delegate.length = 10
	newinterp.delegate.height = 10
	newinterp.objectoffsetpos = [0,0,-5]
	newInterp.par = topB
	newInterp.sib = botB
	newinterp.stubDist = 20
	newInterp.stubAxis = 2
	
	-- create a stub point to hold physique
	stubbie = point name:(newName + "_end") showaxis:false
	stubbie.pos = [0,10,0] * newinterp.transform
	stubbie.parent = newInterp
	newInterp.stub = stubbie
	
	-- move it to the correct location & parent to upper term
	newInterp.pos = botb.transform.row4
	newinterp.parent = topb
	
	-- create the script for the interpolator
	RS = "" as stringstream
	format "-- automatic interpolator script v 1.0\n" to:RS
	format "-- Modify this at your own risk!"
	format "-- Theo 12/2/00\n\n" to:RS
	format "-- singlequoted name below must match name of the interp object\n" to:RS
	format "refObject = $'%'\n" (root + newName)to:RS 
	format "parentSpace = refObject.par.objectTransform\n\n" to:RS 
	format "siblingRotation = refObject.sib.transform * inverse parentSpace\n\n" to:RS 
	format "-- this is the line that does the actual work\n" to:RS
	format "blendedRotation = orthogonalize ( matblend (matrix3 1) siblingRotation refObject.tension)\n" to:RS
	format "-- this sees if it's a full or an oriented interpolation\n" to:RS
	format "if refObject.Shear == true then\n" to:RS
	format "\t(\n" to:RS
	format "\tprojectedvector = ([0,100,0] * inverse Blendedrotation) * scalematrix [0,1,1]\n" to:RS
	format "\toutput = eulerangles (asin (normalize projectedvector).z) 0 0\n" to:RS
	format "\t)\n" to:RS
	format "else\n" to:RS
	format "\t(\n" to:RS
	format "\toutput = blendedRotation\n" to:RS
	format "\t)\n" to:RS
	format "output\n" to:RS
	format "\n" to:RS
	RS = (RS as string)
	newInterp.rotation.controller.script = RS
	
)


--------------------------------------------------------------------------
-- this is the function to interactively pick the bones for an interpolator
--------------------------------------------------------------------------
fn MkI = 
	(
	messagebox "Pick the two bones which you wish to interpolate"
	Bonelist = pickObject count:2 "pick two bones to interpolate"
		(makeInterp boneList[1] bonelist[2])
	)