import {Box} from "../geom/Box";
import {MathConsts} from "../geom/MathConsts";
import {Orientation3D} from "../geom/Orientation3D";
import {Quaternion} from "../geom/Quaternion";
import {Vector3D} from "../geom/Vector3D";
import {ArgumentError} from "../errors/ArgumentError";
export class Matrix3D
{
/**
* A Vector of 16 Numbers, where every four elements is a column of a 4x4 matrix.
*
*
An exception is thrown if the rawData property is set to a matrix that is not invertible. The Matrix3D
* object must be invertible. If a non-invertible matrix is needed, create a subexport class of the Matrix3D object.
*/
public rawData:Float32Array = new Float32Array(16);
private static tempMatrix:Matrix3D = new Matrix3D();
private static tempRawData:Float32Array = Matrix3D.tempMatrix.rawData;
private _position:Vector3D = new Vector3D();
private _positionDirty:boolean = true;
private _components:Array;
/**
* Creates a Matrix3D object.
*/
constructor(v:Float32Array = null)
{
if (v != null && v.length == 16) {
this.copyRawDataFrom(v);
} else {
this.identity();
}
}
/**
* Appends the matrix by multiplying another Matrix3D object by the current Matrix3D object.
*/
public append(lhs:Matrix3D):void
{
var lrd:Float32Array = lhs.rawData;
var m111:number = this.rawData[0];
var m112:number = this.rawData[1];
var m113:number = this.rawData[2];
var m114:number = this.rawData[3];
var m121:number = this.rawData[4];
var m122:number = this.rawData[5];
var m123:number = this.rawData[6];
var m124:number = this.rawData[7];
var m131:number = this.rawData[8];
var m132:number = this.rawData[9];
var m133:number = this.rawData[10];
var m134:number = this.rawData[11];
var m141:number = this.rawData[12];
var m142:number = this.rawData[13];
var m143:number = this.rawData[14];
var m144:number = this.rawData[15];
var m211:number = lrd[0];
var m212:number = lrd[1];
var m213:number = lrd[2];
var m214:number = lrd[3];
var m221:number = lrd[4];
var m222:number = lrd[5];
var m223:number = lrd[6];
var m224:number = lrd[7];
var m231:number = lrd[8];
var m232:number = lrd[9];
var m233:number = lrd[10];
var m234:number = lrd[11];
var m241:number = lrd[12];
var m242:number = lrd[13];
var m243:number = lrd[14];
var m244:number = lrd[15];
this.rawData[0] = m111*m211 + m112*m221 + m113*m231 + m114*m241;
this.rawData[1] = m111*m212 + m112*m222 + m113*m232 + m114*m242;
this.rawData[2] = m111*m213 + m112*m223 + m113*m233 + m114*m243;
this.rawData[3] = m111*m214 + m112*m224 + m113*m234 + m114*m244;
this.rawData[4] = m121*m211 + m122*m221 + m123*m231 + m124*m241;
this.rawData[5] = m121*m212 + m122*m222 + m123*m232 + m124*m242;
this.rawData[6] = m121*m213 + m122*m223 + m123*m233 + m124*m243;
this.rawData[7] = m121*m214 + m122*m224 + m123*m234 + m124*m244;
this.rawData[8] = m131*m211 + m132*m221 + m133*m231 + m134*m241;
this.rawData[9] = m131*m212 + m132*m222 + m133*m232 + m134*m242;
this.rawData[10] = m131*m213 + m132*m223 + m133*m233 + m134*m243;
this.rawData[11] = m131*m214 + m132*m224 + m133*m234 + m134*m244;
this.rawData[12] = m141*m211 + m142*m221 + m143*m231 + m144*m241;
this.rawData[13] = m141*m212 + m142*m222 + m143*m232 + m144*m242;
this.rawData[14] = m141*m213 + m142*m223 + m143*m233 + m144*m243;
this.rawData[15] = m141*m214 + m142*m224 + m143*m234 + m144*m244;
this._positionDirty = true;
}
/**
* Appends an incremental rotation to a Matrix3D object.
*/
public appendRotation(degrees:number, axis:Vector3D):void //, pivot:Vector3D = null ):void
{
this.append(Matrix3D.getAxisRotation(axis.x, axis.y, axis.z, degrees));
}
/**
* Appends an incremental skew change along the x, y, and z axes to a Matrix3D object.
*/
public appendSkew(xSkew:number, ySkew:number, zSkew:number):void
{
if(xSkew == 0 && ySkew == 0 && zSkew == 0) return;
var raw:Float32Array = Matrix3D.tempRawData;
raw[0] = 1;
raw[1] = 0;
raw[2] = 0;
raw[3] = 0;
raw[4] = xSkew;
raw[5] = 1;
raw[6] = 0;
raw[7] = 0;
raw[8] = ySkew;
raw[9] = zSkew;
raw[10] = 1;
raw[11] = 0;
raw[12] = 0;
raw[13] = 0;
raw[14] = 0;
raw[15] = 1;
this.append(Matrix3D.tempMatrix);
}
/**
* Appends an incremental scale change along the x, y, and z axes to a Matrix3D object.
*/
public appendScale(xScale:number, yScale:number, zScale:number):void
{
if(xScale == 1 && yScale == 1 && zScale == 1)
return;
var raw:Float32Array = Matrix3D.tempRawData;
raw[0] = xScale;
raw[1] = 0;
raw[2] = 0;
raw[3] = 0;
raw[4] = 0;
raw[5] = yScale;
raw[6] = 0;
raw[7] = 0;
raw[8] = 0;
raw[9] = 0;
raw[10] = zScale;
raw[11] = 0;
raw[12] = 0;
raw[13] = 0;
raw[14] = 0;
raw[15] = 1;
this.append(Matrix3D.tempMatrix);
}
/**
* Appends an incremental translation, a repositioning along the x, y, and z axes, to a Matrix3D object.
*/
public appendTranslation(x:number, y:number, z:number):void
{
this.rawData[12] += x;
this.rawData[13] += y;
this.rawData[14] += z;
this._positionDirty = true;
}
/**
* Returns a new Matrix3D object that is an exact copy of the current Matrix3D object.
*/
public clone():Matrix3D
{
return new Matrix3D(this.rawData);
}
/**
* Copies a Vector3D object into specific column of the calling Matrix3D object.
*/
public copyColumnFrom(column:number, vector3D:Vector3D):void
{
switch (column) {
case 0:
this.rawData[ 0 ] = vector3D.x;
this.rawData[ 1 ] = vector3D.y;
this.rawData[ 2 ] = vector3D.z;
this.rawData[ 3 ] = vector3D.w;
break;
case 1:
this.rawData[ 4 ] = vector3D.x;
this.rawData[ 5 ] = vector3D.y;
this.rawData[ 6 ] = vector3D.z;
this.rawData[ 7 ] = vector3D.w;
break;
case 2:
this.rawData[ 8 ] = vector3D.x;
this.rawData[ 9 ] = vector3D.y;
this.rawData[ 10 ] = vector3D.z;
this.rawData[ 11 ] = vector3D.w;
break;
case 3:
this.rawData[ 12 ] = vector3D.x;
this.rawData[ 13 ] = vector3D.y;
this.rawData[ 14 ] = vector3D.z;
this.rawData[ 15 ] = vector3D.w;
break;
default:
throw new ArgumentError("ArgumentError, Column " + column + " out of bounds [0, ..., 3]");
}
}
/**
* Copies specific column of the calling Matrix3D object into the Vector3D object.
*/
public copyColumnTo(column:number, vector3D:Vector3D):void
{
switch (column) {
case 0:
vector3D.x = this.rawData[ 0 ];
vector3D.y = this.rawData[ 1 ];
vector3D.z = this.rawData[ 2 ];
vector3D.w = this.rawData[ 3 ];
break;
case 1:
vector3D.x = this.rawData[ 4 ];
vector3D.y = this.rawData[ 5 ];
vector3D.z = this.rawData[ 6 ];
vector3D.w = this.rawData[ 7 ];
break;
case 2:
vector3D.x = this.rawData[ 8 ];
vector3D.y = this.rawData[ 9 ];
vector3D.z = this.rawData[ 10 ];
vector3D.w = this.rawData[ 11 ];
break;
case 3:
vector3D.x = this.rawData[ 12 ];
vector3D.y = this.rawData[ 13 ];
vector3D.z = this.rawData[ 14 ];
vector3D.w = this.rawData[ 15 ];
break;
default:
throw new ArgumentError("ArgumentError, Column " + column + " out of bounds [0, ..., 3]");
}
}
/**
* Copies all of the matrix data from the source Matrix3D object into the calling Matrix3D object.
*/
public copyFrom(source:Matrix3D):void
{
var sourceRaw:Float32Array = source.rawData;
var len:number = sourceRaw.length;
for (var c:number = 0; c < len; c++)
this.rawData[c] = sourceRaw[c];
}
/**
* Copies this Matrix3D object into a destination Matrix3D object.
*/
public copyTo(target:Matrix3D):void
{
var targetRaw:Float32Array = target.rawData;
var len:number = this.rawData.length;
for (var c:number = 0; c < len; c++)
targetRaw[c] = this.rawData[c];
}
public copyRawDataFrom(vector:Float32Array, index:number = 0, transpose:boolean = false):void
{
if (transpose)
this.transpose();
var len:number = vector.length - index;
for (var c:number = 0; c < len; c++)
this.rawData[c] = vector[c + index];
if (transpose)
this.transpose();
}
public copyRawDataTo(vector:Float32Array, index:number = 0, transpose:boolean = false):void
{
if (transpose)
this.transpose();
var len:number = this.rawData.length;
for (var c:number = 0; c < len; c++)
vector[c + index ] = this.rawData[c];
if (transpose)
this.transpose();
}
/**
* Copies a Vector3D object into specific row of the calling Matrix3D object.
*/
public copyRowFrom(row:number, vector3D:Vector3D):void
{
switch (row) {
case 0:
this.rawData[ 0 ] = vector3D.x;
this.rawData[ 4 ] = vector3D.y;
this.rawData[ 8 ] = vector3D.z;
this.rawData[ 12 ] = vector3D.w;
break;
case 1:
this.rawData[ 1 ] = vector3D.x;
this.rawData[ 5 ] = vector3D.y;
this.rawData[ 9 ] = vector3D.z;
this.rawData[ 13 ] = vector3D.w;
break;
case 2:
this.rawData[ 2 ] = vector3D.x;
this.rawData[ 6 ] = vector3D.y;
this.rawData[ 10 ] = vector3D.z;
this.rawData[ 14 ] = vector3D.w;
break;
case 3:
this.rawData[ 3 ] = vector3D.x;
this.rawData[ 7 ] = vector3D.y;
this.rawData[ 11 ] = vector3D.z;
this.rawData[ 15 ] = vector3D.w;
break;
default:
throw new ArgumentError("ArgumentError, Row " + row + " out of bounds [0, ..., 3]");
}
}
/**
* Copies specific row of the calling Matrix3D object into the Vector3D object.
*/
public copyRowTo(row:number, vector3D:Vector3D):void
{
switch (row) {
case 0:
vector3D.x = this.rawData[ 0 ];
vector3D.y = this.rawData[ 4 ];
vector3D.z = this.rawData[ 8 ];
vector3D.w = this.rawData[ 12 ];
break;
case 1:
vector3D.x = this.rawData[ 1 ];
vector3D.y = this.rawData[ 5 ];
vector3D.z = this.rawData[ 9 ];
vector3D.w = this.rawData[ 13 ];
break;
case 2:
vector3D.x = this.rawData[ 2 ];
vector3D.y = this.rawData[ 6 ];
vector3D.z = this.rawData[ 10 ];
vector3D.w = this.rawData[ 14 ];
break;
case 3:
vector3D.x = this.rawData[ 3 ];
vector3D.y = this.rawData[ 7 ];
vector3D.z = this.rawData[ 11 ];
vector3D.w = this.rawData[ 15 ]
break;
default:
throw new ArgumentError("ArgumentError, Row " + row + " out of bounds [0, ..., 3]");
}
}
/**
* Returns the transformation matrix's translation, rotation, and scale settings as a Vector of three Vector3D objects.
*/
public decompose(orientationStyle:string = "eulerAngles"):Vector3D[]
{
var q:Quaternion;
if (this._components == null)
this._components = [null, new Vector3D(), new Vector3D(), new Vector3D()];
var colX:Vector3D = new Vector3D(this.rawData[0], this.rawData[1], this.rawData[2]);
var colY:Vector3D = new Vector3D(this.rawData[4], this.rawData[5], this.rawData[6]);
var colZ:Vector3D = new Vector3D(this.rawData[8], this.rawData[9], this.rawData[10]);
var scale:Vector3D = this._components[3];
var skew:Vector3D = this._components[2];
//compute X scale factor and normalise colX
scale.x = colX.length;
colX.scaleBy(1/scale.x);
//compute XY shear factor and make colY orthogonal to colX
skew.x = colX.dotProduct(colY);
colY = Vector3D.combine(colY, colX, 1, -skew.x);
//compute Y scale factor and normalise colY
scale.y = colY.length;
colY.scaleBy(1/scale.y);
skew.x /= scale.y;
//compute XZ and YZ shears and make colZ orthogonal to colX and colY
skew.y = colX.dotProduct(colZ);
colZ = Vector3D.combine(colZ, colX, 1, -skew.y);
skew.z = colY.dotProduct(colZ);
colZ = Vector3D.combine(colZ, colY, 1, -skew.z);
//compute Z scale and normalise colZ
scale.z = colZ.length;
colZ.scaleBy(1/scale.z);
skew.y /= scale.z;
skew.z /= scale.z;
//at this point, the matrix (in cols) is orthonormal
//check for a coordinate system flip. If the determinant is -1, negate the z scaling factor
if (colX.dotProduct(colY.crossProduct(colZ)) < 0) {
scale.z = -scale.z;
colZ.x = -colZ.x;
colZ.y = -colZ.y;
colZ.z = -colZ.z;
}
var rot = this._components[1];
switch (orientationStyle) {
case Orientation3D.AXIS_ANGLE:
rot.w = Math.acos((colX.x + colY.y + colZ.z - 1)/2);
var len:number = Math.sqrt((colY.z - colZ.y)*(colY.z - colZ.y) + (colZ.x - colX.z)*(colZ.x - colX.z) + (colX.y - colY.x)*(colX.y - colY.x));
rot.x = (colY.z - colZ.y)/len;
rot.y = (colZ.x - colX.z)/len;
rot.z = (colX.y - colY.x)/len;
break;
case Orientation3D.QUATERNION:
var tr = colX.x + colY.y + colZ.z;
if (tr > 0) {
rot.w = Math.sqrt(1 + tr)/2;
rot.x = (colY.z - colZ.y)/(4*rot.w);
rot.y = (colZ.x - colX.z)/(4*rot.w);
rot.z = (colX.y - colY.x)/(4*rot.w);
} else if ((colX.x > colY.y) && (colX.x > colZ.z)) {
rot.x = Math.sqrt(1 + colX.x - colY.y - colZ.z)/2;
rot.w = (colY.z - colZ.y)/(4*rot.x);
rot.y = (colX.y + colY.x)/(4*rot.x);
rot.z = (colZ.x + colX.z)/(4*rot.x);
} else if (colY.y > colZ.z) {
rot.y = Math.sqrt(1 + colY.y - colX.x - colZ.z)/2;
rot.x = (colX.y + colY.x)/(4*rot.y);
rot.w = (colZ.x - colX.z)/(4*rot.y);
rot.z = (colY.z + colZ.y)/(4*rot.y);
} else {
rot.z = Math.sqrt(1 + colZ.z - colX.x - colY.y)/2;
rot.x = (colZ.x + colX.z)/(4*rot.z);
rot.y = (colY.z + colZ.y)/(4*rot.z);
rot.w = (colX.y - colY.x)/(4*rot.z);
}
break;
case Orientation3D.EULER_ANGLES:
rot.y = Math.asin(-colX.z);
//var cos:number = Math.cos(rot.y);
if (colX.z != 1 && colX.z != -1) {
rot.x = Math.atan2(colY.z, colZ.z);
rot.z = Math.atan2(colX.y, colX.x);
} else {
rot.z = 0;
rot.x = Math.atan2(colY.x, colY.y);
}
break;
}
this._components[0] = this.position;
return this._components;
}
/**
* Uses the transformation matrix without its translation elements to transform a Vector3D object from one space
* coordinate to another.
*/
public deltaTransformVector(v:Vector3D, t:Vector3D = null):Vector3D
{
var x:number = v.x;
var y:number = v.y;
var z:number = v.z;
if (!t)
t = new Vector3D();
t.x = x*this.rawData[0] + y*this.rawData[4] + z*this.rawData[8];
t.y = x*this.rawData[1] + y*this.rawData[5] + z*this.rawData[9];
t.z = x*this.rawData[2] + y*this.rawData[6] + z*this.rawData[10];
t.w = x*this.rawData[3] + y*this.rawData[7] + z*this.rawData[11];
return t;
}
/**
* Converts the current matrix to an identity or unit matrix.
*/
public identity():void
{
this.rawData[0] = 1;
this.rawData[1] = 0;
this.rawData[2] = 0;
this.rawData[3] = 0;
this.rawData[4] = 0;
this.rawData[5] = 1;
this.rawData[6] = 0;
this.rawData[7] = 0;
this.rawData[8] = 0;
this.rawData[9] = 0;
this.rawData[10] = 1;
this.rawData[11] = 0;
this.rawData[12] = 0;
this.rawData[13] = 0;
this.rawData[14] = 0;
this.rawData[15] = 1;
this._positionDirty = true;
}
/**
* [static] Interpolates the translation, rotation, and scale transformation of one matrix toward those of the target matrix.
*/
static interpolate(thisMat:Matrix3D, toMat:Matrix3D, percent:number):Matrix3D
{
var m:Matrix3D = new Matrix3D();
for (var i:number = 0; i < 16; ++i)
m.rawData[i] = thisMat.rawData[i] + (toMat.rawData[i] - thisMat.rawData[i])*percent;
return m;
}
/**
* Interpolates this matrix towards the translation, rotation, and scale transformations of the target matrix.
*/
public interpolateTo(toMat:Matrix3D, percent:number):void
{
for (var i:number = 0; i < 16; ++i)
this.rawData[i] = this.rawData[i] + (toMat.rawData[i] - this.rawData[i])*percent;
}
/**
* Inverts the current matrix.
*/
public invert():boolean
{
var d = this.determinant;
var invertable:boolean = Math.abs(d) > 0.00000000001;
if (invertable) {
d = 1/d;
var m11:number = this.rawData[0];
var m12:number = this.rawData[1];
var m13:number = this.rawData[2];
var m14:number = this.rawData[3];
var m21:number = this.rawData[4];
var m22:number = this.rawData[5];
var m23:number = this.rawData[6];
var m24:number = this.rawData[7];
var m31:number = this.rawData[8];
var m32:number = this.rawData[9];
var m33:number = this.rawData[10];
var m34:number = this.rawData[11];
var m41:number = this.rawData[12];
var m42:number = this.rawData[13];
var m43:number = this.rawData[14];
var m44:number = this.rawData[15];
this.rawData[0] = d*(m22*(m33*m44 - m43*m34) - m32*(m23*m44 - m43*m24) + m42*(m23*m34 - m33*m24));
this.rawData[1] = -d*(m12*(m33*m44 - m43*m34) - m32*(m13*m44 - m43*m14) + m42*(m13*m34 - m33*m14));
this.rawData[2] = d*(m12*(m23*m44 - m43*m24) - m22*(m13*m44 - m43*m14) + m42*(m13*m24 - m23*m14));
this.rawData[3] = -d*(m12*(m23*m34 - m33*m24) - m22*(m13*m34 - m33*m14) + m32*(m13*m24 - m23*m14));
this.rawData[4] = -d*(m21*(m33*m44 - m43*m34) - m31*(m23*m44 - m43*m24) + m41*(m23*m34 - m33*m24));
this.rawData[5] = d*(m11*(m33*m44 - m43*m34) - m31*(m13*m44 - m43*m14) + m41*(m13*m34 - m33*m14));
this.rawData[6] = -d*(m11*(m23*m44 - m43*m24) - m21*(m13*m44 - m43*m14) + m41*(m13*m24 - m23*m14));
this.rawData[7] = d*(m11*(m23*m34 - m33*m24) - m21*(m13*m34 - m33*m14) + m31*(m13*m24 - m23*m14));
this.rawData[8] = d*(m21*(m32*m44 - m42*m34) - m31*(m22*m44 - m42*m24) + m41*(m22*m34 - m32*m24));
this.rawData[9] = -d*(m11*(m32*m44 - m42*m34) - m31*(m12*m44 - m42*m14) + m41*(m12*m34 - m32*m14));
this.rawData[10] = d*(m11*(m22*m44 - m42*m24) - m21*(m12*m44 - m42*m14) + m41*(m12*m24 - m22*m14));
this.rawData[11] = -d*(m11*(m22*m34 - m32*m24) - m21*(m12*m34 - m32*m14) + m31*(m12*m24 - m22*m14));
this.rawData[12] = -d*(m21*(m32*m43 - m42*m33) - m31*(m22*m43 - m42*m23) + m41*(m22*m33 - m32*m23));
this.rawData[13] = d*(m11*(m32*m43 - m42*m33) - m31*(m12*m43 - m42*m13) + m41*(m12*m33 - m32*m13));
this.rawData[14] = -d*(m11*(m22*m43 - m42*m23) - m21*(m12*m43 - m42*m13) + m41*(m12*m23 - m22*m13));
this.rawData[15] = d*(m11*(m22*m33 - m32*m23) - m21*(m12*m33 - m32*m13) + m31*(m12*m23 - m22*m13));
}
this._positionDirty = true;
return invertable;
}
/* TODO implement pointAt
public pointAt( pos:Vector3D, at:Vector3D = null, up:Vector3D = null )
{
}
*/
/**
* Prepends a matrix by multiplying the current Matrix3D object by another Matrix3D object.
*/
public prepend(rhs:Matrix3D):void
{
var m111:number = rhs.rawData[0];
var m112:number = rhs.rawData[1];
var m113:number = rhs.rawData[2];
var m114:number = rhs.rawData[3];
var m121:number = rhs.rawData[4];
var m122:number = rhs.rawData[5];
var m123:number = rhs.rawData[6];
var m124:number = rhs.rawData[7];
var m131:number = rhs.rawData[8];
var m132:number = rhs.rawData[9];
var m133:number = rhs.rawData[10];
var m134:number = rhs.rawData[11];
var m141:number = rhs.rawData[12];
var m142:number = rhs.rawData[13];
var m143:number = rhs.rawData[14];
var m144:number = rhs.rawData[15];
var m211:number = this.rawData[0];
var m212:number = this.rawData[1];
var m213:number = this.rawData[2];
var m214:number = this.rawData[3];
var m221:number = this.rawData[4];
var m222:number = this.rawData[5];
var m223:number = this.rawData[6];
var m224:number = this.rawData[7];
var m231:number = this.rawData[8];
var m232:number = this.rawData[9];
var m233:number = this.rawData[10];
var m234:number = this.rawData[11];
var m241:number = this.rawData[12];
var m242:number = this.rawData[13];
var m243:number = this.rawData[14];
var m244:number = this.rawData[15];
this.rawData[0] = m111*m211 + m112*m221 + m113*m231 + m114*m241;
this.rawData[1] = m111*m212 + m112*m222 + m113*m232 + m114*m242;
this.rawData[2] = m111*m213 + m112*m223 + m113*m233 + m114*m243;
this.rawData[3] = m111*m214 + m112*m224 + m113*m234 + m114*m244;
this.rawData[4] = m121*m211 + m122*m221 + m123*m231 + m124*m241;
this.rawData[5] = m121*m212 + m122*m222 + m123*m232 + m124*m242;
this.rawData[6] = m121*m213 + m122*m223 + m123*m233 + m124*m243;
this.rawData[7] = m121*m214 + m122*m224 + m123*m234 + m124*m244;
this.rawData[8] = m131*m211 + m132*m221 + m133*m231 + m134*m241;
this.rawData[9] = m131*m212 + m132*m222 + m133*m232 + m134*m242;
this.rawData[10] = m131*m213 + m132*m223 + m133*m233 + m134*m243;
this.rawData[11] = m131*m214 + m132*m224 + m133*m234 + m134*m244;
this.rawData[12] = m141*m211 + m142*m221 + m143*m231 + m144*m241;
this.rawData[13] = m141*m212 + m142*m222 + m143*m232 + m144*m242;
this.rawData[14] = m141*m213 + m142*m223 + m143*m233 + m144*m243;
this.rawData[15] = m141*m214 + m142*m224 + m143*m234 + m144*m244;
this._positionDirty = true;
}
/**
* Prepends an incremental rotation to a Matrix3D object.
*/
public prependRotation(degrees:number, axis:Vector3D) //, pivot:Vector3D = null ):void
{
var m:Matrix3D = Matrix3D.getAxisRotation(axis.x, axis.y, axis.z, degrees);
/*
if ( pivot != null )
{
var p:Vector3D = pivot;
m.appendTranslation( p.x, p.y, p.z );
}
*/
this.prepend(m);
}
/**
* Prepends an incremental scale change along the x, y, and z axes to a Matrix3D object.
*/
public prependScale(xScale:number, yScale:number, zScale:number):void
{
if(xScale == 1 && yScale == 1 && zScale == 1)
return;
var raw:Float32Array = Matrix3D.tempRawData;
raw[0] = xScale;
raw[1] = 0;
raw[2] = 0;
raw[3] = 0;
raw[4] = 0;
raw[5] = yScale;
raw[6] = 0;
raw[7] = 0;
raw[8] = 0;
raw[9] = 0;
raw[10] = zScale;
raw[11] = 0;
raw[12] = 0;
raw[13] = 0;
raw[14] = 0;
raw[15] = 1;
this.prepend(Matrix3D.tempMatrix);
}
/**
* Prepends an incremental translation, a repositioning along the x, y, and z axes, to a Matrix3D object.
*/
public prependTranslation(x:number, y:number, z:number):void
{
var raw:Float32Array = Matrix3D.tempRawData;
raw[0] = 1;
raw[1] = 0;
raw[2] = 0;
raw[3] = 0;
raw[4] = 0;
raw[5] = 1;
raw[6] = 0;
raw[7] = 0;
raw[8] = 0;
raw[9] = 0;
raw[10] = 1;
raw[11] = 0;
raw[12] = x;
raw[13] = y;
raw[14] = z;
raw[15] = 1;
this.prepend(Matrix3D.tempMatrix);
}
// TODO orientationStyle
/**
* Sets the transformation matrix's translation, rotation, and scale settings.
*/
public recompose(components:Vector3D[]):boolean
{
var pos:Vector3D = components[0] || this.position;
this.identity();
var scale:Vector3D = components[3];
if(scale && (scale.x != 1 || scale.y != 1 || scale.z != 1))
this.appendScale(scale.x, scale.y, scale.z);
var skew:Vector3D = components[2];
if(skew && (skew.x !=0 || skew.y != 0 || skew.z != 0))
this.appendSkew(skew.x, skew.y, skew.z);
var sin:number;
var cos:number;
var raw:Float32Array = Matrix3D.tempRawData;
raw[12] = 0;
raw[13] = 0;
raw[14] = 0;
raw[15] = 0;
var rotation:Vector3D = components[1];
if (rotation) {
var angle:number = -rotation.x;
if(angle != 0){
sin = Math.sin(angle);
cos = Math.cos(angle);
raw[0] = 1;
raw[1] = 0;
raw[2] = 0;
raw[3] = 0;
raw[4] = 0;
raw[5] = cos;
raw[6] = -sin;
raw[7] = 0;
raw[8] = 0;
raw[9] = sin;
raw[10] = cos;
raw[11] = 0;
this.append(Matrix3D.tempMatrix);
}
angle = -rotation.y;
if(angle != 0){
sin = Math.sin(angle);
cos = Math.cos(angle);
raw[0] = cos;
raw[1] = 0;
raw[2] = sin;
raw[3] = 0;
raw[4] = 0;
raw[5] = 1;
raw[6] = 0;
raw[7] = 0;
raw[8] = -sin;
raw[9] = 0;
raw[10] = cos;
raw[11] = 0;
this.append(Matrix3D.tempMatrix);
}
angle = -rotation.z;
if(angle != 0){
sin = Math.sin(angle);
cos = Math.cos(angle);
raw[0] = cos;
raw[1] = -sin;
raw[2] = 0;
raw[3] = 0;
raw[4] = sin;
raw[5] = cos;
raw[6] = 0;
raw[7] = 0;
raw[8] = 0;
raw[9] = 0;
raw[10] = 1;
raw[11] = 0;
this.append(Matrix3D.tempMatrix);
}
}
this.rawData[12] = pos.x;
this.rawData[13] = pos.y;
this.rawData[14] = pos.z;
if (components[0])
this._positionDirty = true;
this.rawData[15] = 1;
return true;
}
public transformVector(v:Vector3D, t:Vector3D = null):Vector3D
{
if (v == null)
return t || new Vector3D();
var x:number = v.x;
var y:number = v.y;
var z:number = v.z;
if (!t)
t = new Vector3D();
t.x = x*this.rawData[0] + y*this.rawData[4] + z*this.rawData[8] + this.rawData[12];
t.y = x*this.rawData[1] + y*this.rawData[5] + z*this.rawData[9] + this.rawData[13];
t.z = x*this.rawData[2] + y*this.rawData[6] + z*this.rawData[10] + this.rawData[14];
t.w = x*this.rawData[3] + y*this.rawData[7] + z*this.rawData[11] + this.rawData[15];
return t;
}
public transformBox(b:Box, t:Box = null):Box
{
if (b == null)
return t || new Box();
var minX:number, minY:number, minZ:number;
var maxX:number, maxY:number, maxZ:number;
maxX = b.width + (minX = b.x);
maxY = b.height + (minY = b.y);
maxZ = b.depth + (minZ = b.z);
if (!t)
t = new Box();
//TODO: take account of shear
t.width = maxX*this.rawData[0] + maxY*this.rawData[4] + maxZ*this.rawData[8] + this.rawData[12] - (t.x = minX*this.rawData[0] + minY*this.rawData[4] + minZ*this.rawData[8] + this.rawData[12]);
t.height = maxX*this.rawData[1] + maxY*this.rawData[5] + maxZ*this.rawData[9] + this.rawData[13] - (t.y = minX*this.rawData[1] + minY*this.rawData[5] + minZ*this.rawData[9] + this.rawData[13]);
t.depth = maxX*this.rawData[2] + maxY*this.rawData[6] + maxZ*this.rawData[10] + this.rawData[14] - (t.z = minX*this.rawData[2] + minY*this.rawData[6] + minZ*this.rawData[10] + this.rawData[14]);
return t;
}
/**
* Uses the transformation matrix to transform a Vector of Numbers from one coordinate space to another.
*/
public transformVectors(vin:number[], vout:number[]):void
{
var i:number = 0;
var x:number = 0, y:number = 0, z:number = 0;
while (i + 3 <= vin.length) {
x = vin[i];
y = vin[i + 1];
z = vin[i + 2];
vout[i] = x*this.rawData[0] + y*this.rawData[4] + z*this.rawData[8] + this.rawData[12];
vout[i + 1] = x*this.rawData[1] + y*this.rawData[5] + z*this.rawData[9] + this.rawData[13];
vout[i + 2] = x*this.rawData[2] + y*this.rawData[6] + z*this.rawData[10] + this.rawData[14];
i += 3;
}
}
/**
* Converts the current Matrix3D object to a matrix where the rows and columns are swapped.
*/
public transpose():void
{
var raw:Float32Array = Matrix3D.tempRawData;
this.copyRawDataTo(raw);
this.rawData[1] = raw[4];
this.rawData[2] = raw[8];
this.rawData[3] = raw[12];
this.rawData[4] = raw[1];
this.rawData[6] = raw[9];
this.rawData[7] = raw[13];
this.rawData[8] = raw[2];
this.rawData[9] = raw[6];
this.rawData[11] = raw[14];
this.rawData[12] = raw[3];
this.rawData[13] = raw[7];
this.rawData[14] = raw[11];
this._positionDirty = true;
}
static getAxisRotation(x:number, y:number, z:number, degrees:number):Matrix3D
{
// internal export class use by rotations which have been tested
var m:Matrix3D = new Matrix3D();
var rad = degrees*MathConsts.DEGREES_TO_RADIANS;
var c:number = Math.cos(rad);
var s:number = Math.sin(rad);
var t:number = 1 - c;
var tmp1:number, tmp2:number;
m.rawData[0] = c + x*x*t;
m.rawData[5] = c + y*y*t;
m.rawData[10] = c + z*z*t;
tmp1 = x*y*t;
tmp2 = z*s;
m.rawData[1] = tmp1 + tmp2;
m.rawData[4] = tmp1 - tmp2;
tmp1 = x*z*t;
tmp2 = y*s;
m.rawData[8] = tmp1 + tmp2;
m.rawData[2] = tmp1 - tmp2;
tmp1 = y*z*t;
tmp2 = x*s;
m.rawData[9] = tmp1 - tmp2;
m.rawData[6] = tmp1 + tmp2;
return m;
}
/**
* [read-only] A Number that determines whether a matrix is invertible.
*/
public get determinant():number
{
return ((this.rawData[0]*this.rawData[5] - this.rawData[4]*this.rawData[1])*(this.rawData[10]*this.rawData[15] - this.rawData[14]*this.rawData[11]) - (this.rawData[0]*this.rawData[9] - this.rawData[8]*this.rawData[1])*(this.rawData[6]*this.rawData[15] - this.rawData[14]*this.rawData[7]) + (this.rawData[0]*this.rawData[13] - this.rawData[12]*this.rawData[1])*(this.rawData[6]*this.rawData[11] - this.rawData[10]*this.rawData[7]) + (this.rawData[4]*this.rawData[9] - this.rawData[8]*this.rawData[5])*(this.rawData[2]*this.rawData[15] - this.rawData[14]*this.rawData[3]) - (this.rawData[4]*this.rawData[13] - this.rawData[12]*this.rawData[5])*(this.rawData[2]*this.rawData[11] - this.rawData[10]*this.rawData[3]) + (this.rawData[8]*this.rawData[13] - this.rawData[12]*this.rawData[9])*(this.rawData[2]*this.rawData[7] - this.rawData[6]*this.rawData[3]));
}
/**
* A Vector3D object that holds the position, the 3D coordinate (x,y,z) of a display object within the
* transformation's frame of reference.
*/
public get position():Vector3D
{
if (this._positionDirty) {
this._positionDirty = false;
this._position.x = this.rawData[12];
this._position.y = this.rawData[13];
this._position.z = this.rawData[14];
}
return this._position;
}
public invalidatePosition():void
{
this._positionDirty = true;
}
public toFixed(decimalPlace:number):string
{
var magnitude:number = Math.pow(10, decimalPlace);
return "matrix3d(" + Math.round(this.rawData[0]*magnitude)/magnitude + "," + Math.round(this.rawData[1]*magnitude)/magnitude + "," + Math.round(this.rawData[2]*magnitude)/magnitude + "," + Math.round(this.rawData[3]*magnitude)/magnitude + "," + Math.round(this.rawData[4]*magnitude)/magnitude + "," + Math.round(this.rawData[5]*magnitude)/magnitude + "," + Math.round(this.rawData[6]*magnitude)/magnitude + "," + Math.round(this.rawData[7]*magnitude)/magnitude + "," + Math.round(this.rawData[8]*magnitude)/magnitude + "," + Math.round(this.rawData[9]*magnitude)/magnitude + "," + Math.round(this.rawData[10]*magnitude)/magnitude + "," + Math.round(this.rawData[11]*magnitude)/magnitude + "," + Math.round(this.rawData[12]*magnitude)/magnitude + "," + Math.round(this.rawData[13]*magnitude)/magnitude + "," + Math.round(this.rawData[14]*magnitude)/magnitude + "," + Math.round(this.rawData[15]*magnitude)/magnitude + ")";
}
public toString():string
{
return "matrix3d(" + Math.round(this.rawData[0]*1000)/1000 + "," + Math.round(this.rawData[1]*1000)/1000 + "," + Math.round(this.rawData[2]*1000)/1000 + "," + Math.round(this.rawData[3]*1000)/1000 + "," + Math.round(this.rawData[4]*1000)/1000 + "," + Math.round(this.rawData[5]*1000)/1000 + "," + Math.round(this.rawData[6]*1000)/1000 + "," + Math.round(this.rawData[7]*1000)/1000 + "," + Math.round(this.rawData[8]*1000)/1000 + "," + Math.round(this.rawData[9]*1000)/1000 + "," + Math.round(this.rawData[10]*1000)/1000 + "," + Math.round(this.rawData[11]*1000)/1000 + "," + Math.round(this.rawData[12]*1000)/1000 + "," + Math.round(this.rawData[13]*1000)/1000 + "," + Math.round(this.rawData[14]*1000)/1000 + "," + Math.round(this.rawData[15]*1000)/1000 + ")";
}
}