Transform.as

ActionScript source for It's the amoeba, 218 lines. Raw file

//----- Transform

function Transform()
{
  this.p = [0, 0, 0]
  this.q = [0, 0, 1, 0]
  this.qi = true  //  Identity quaternion flag (if true, no rotation calculation necessary)
  this.pi = true  //  Zero translation flag (p is zero)
}



//---- setPosition

Transform.prototype.setPosition = function( x, y, z )
{
  this.p = [x, y, z]
  this.pi = false
}



//---- addPosition

Transform.prototype.addPosition = function( x, y, z )
{
  this.p[0] += x
  this.p[1] += y
  this.p[2] += z
  this.pi = false
}



//---- setRotation

Transform.prototype.setRotation = function( x, y, z, r )
{
	//  Normalise & create quaternion

  var s = Math.sin(r/2);
  var d = s / Math.sqrt( x*x + y*y + z*z )

  this.q = [x*d, y*d, z*d, Math.cos(r/2) ]
  this.qi = false
}



//---- addRotation

//  This could be made more efficient by explicitly doing the calculation

Transform.prototype.addRotation = function( x, y, z, r )
{
	var a = this.copy()

	this.setRotation( x, y, z, r )
  a.setPosition( 0, 0, 0 )

  //  Combine with existing rotation

  this.combine( a )
}



//---- combine

//  does this times z
//  The new transformation is done on things first, before the original

//  TODO:  CombineEx( x, y, z, r ) to save us making a transform in addRotation

Transform.prototype.combine = function( z )
{
 //  Combine quaternions

	var a = this.q
	var b = z.q

  var dp = a[0]*b[0] + a[1]*b[1] + a[2]*b[2]  //  Dot (scalar) product
	var cp =
	[
	  a[1]*b[2]-a[2]*b[1],
	  a[2]*b[0]-a[0]*b[2],
	  a[0]*b[1]-a[1]*b[0]
  ]  											//  Cross (vector) product

  //  Combine transformations

  var tmp = [0,0,0]
  this.transformPoint( z.p, tmp )
	this.p = tmp

	this.q =
	[
	  b[0]*a[3] + a[0]*b[3] + cp[0],
	  b[1]*a[3] + a[1]*b[3] + cp[1],
	  b[2]*a[3] + a[2]*b[3] + cp[2],
		a[3]*b[3] - dp
	]

  this.pi = false
  this.qi = false
}



//---- copy

Transform.prototype.copy = function( )
{
  var d = new Transform()

	d.p[0] = this.p[0]
  d.p[1] = this.p[1]
  d.p[2] = this.p[2]

	d.q = [ this.q[0], this.q[1], this.q[2], this.q[3] ]

  d.pi = this.pi
  d.qi = this.qi

	return d
}



//---- transformPoint

Transform.prototype.transformPoint = function( s, d )
{
	//trace( "now "+this.q )
	var r1 = s[0]
	var r2 = s[1]
	var r3 = s[2]

	var cx = this.q[0]
	var cy = this.q[1]
	var cz = this.q[2]

	var cw = this.q[3]

//trace( this.q )
	//  Rotate by q then translate by p

	d[0] = this.p[0] + r1*(1-2*(cy*cy+cz*cz))    + r2*(2*cx*cy-2*cw*cz)      + r3*(2*cw*cy+2*cx*cz);
	d[1] = this.p[1] + r1*(2*cx*cy+2*cw*cz)      + r2*(1-2*(cx*cx + cz*cz))  + r3*((-2)*cw*cx+2*cy*cz);
	d[2] = this.p[2] + r1*((-2)*cw*cy + 2*cx*cz) + r2*(2*cw*cx+2*cy*cz)      + r3*(1-2*(cx*cx + cy*cy));

//  trace( "tP: ("+s[0]+", "+s[1]+", "+s[2]+") -> ("+d[0]+", "+d[1]+", "+d[2]+")" )
}



//---- transformNormal

//  rotates the given vector but does not translate it
//  For working out directions or normals in the space (from the point of view) of the object

Transform.prototype.transformNormal = function( s, d )
{
	var r1 = s[0]
	var r2 = s[1]
	var r3 = s[2]

	var cx = this.q[0]
	var cy = this.q[1]
	var cz = this.q[2]

	var cw = this.q[3]

	//  Rotate by q

	d[0] = r1*(1-2*(cy*cy+cz*cz))    + r2*(2*cx*cy-2*cw*cz)      + r3*(2*cw*cy+2*cx*cz);
	d[1] = r1*(2*cx*cy+2*cw*cz)      + r2*(1-2*(cx*cx + cz*cz))  + r3*((-2)*cw*cx+2*cy*cz);
	d[2] = r1*((-2)*cw*cy + 2*cx*cz) + r2*(2*cw*cx+2*cy*cz)      + r3*(1-2*(cx*cx + cy*cy));
}



//---- transformNormalInv

//  rotates the given vector but does not translate it
//  For working out directions or normals in the space (from the point of view) of the object
//  Does the inverse rotation

Transform.prototype.transformNormalInv = function( s, d )
{
	var r1 = s[0]
	var r2 = s[1]
	var r3 = s[2]

	var cx = -this.q[0]
	var cy = -this.q[1]
	var cz = -this.q[2]

	var cw = this.q[3]

	//  Rotate by q

	d[0] = r1*(1-2*(cy*cy+cz*cz))    + r2*(2*cx*cy-2*cw*cz)      + r3*(2*cw*cy+2*cx*cz);
	d[1] = r1*(2*cx*cy+2*cw*cz)      + r2*(1-2*(cx*cx + cz*cz))  + r3*((-2)*cw*cx+2*cy*cz);
	d[2] = r1*((-2)*cw*cy + 2*cx*cz) + r2*(2*cw*cx+2*cy*cz)      + r3*(1-2*(cx*cx + cy*cy));
}



//---- pointZAxis

Transform.prototype.pointZAxis = function( x, y, z )
{
  var d = 1.0 / Math.sqrt( x*x + y*y + z*z )
  this.setRotation( y*d, -x*d, 0, -Math.acos( z*d ) )
}