package Matrices import NoWurst import public Vectors import Maths import ErrorHandling import Wurstunit /** Matrix 2x2 00 01 10 11 Each column represents an axis in a rotation matrix. */ public tuple mat2(real m00, real m01, real m10, real m11) public constant ZERO22 = mat2(0, 0, 0, 0) public constant IDENTITY22 = mat2( 1, 0, 0, 1) tuple inverseresult22(bool success, mat2 matrix) /** Matrix 3x3 00 01 02 10 11 12 20 21 22 Each column represents an axis in a rotation matrix. */ public tuple mat3( real m00, real m01, real m02, real m10, real m11, real m12, real m20, real m21, real m22) public constant ZERO33 = mat3(0, 0, 0, 0, 0, 0, 0, 0, 0) public constant IDENTITY33 = mat3( 1, 0, 0, 0, 1, 0, 0, 0, 1) tuple inverseresult33(bool success, mat3 matrix) /** Matrix 4x4 00 01 02 03 10 11 12 13 20 21 22 23 30 31 32 33 */ public tuple mat4( real m00, real m01, real m02, real m03, real m10, real m11, real m12, real m13, real m20, real m21, real m22, real m23, real m30, real m31, real m32, real m33) public constant ZERO44 = mat4( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) public constant IDENTITY44 = mat4( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) tuple inverseresult44(bool success, mat4 matrix) constant EPSILON = 0.001 /* =========== 2X2 MATRIX =========== */ public function mat2.op_plus(mat2 m) returns mat2 return mat2(this.m00 + m.m00, this.m01 + m.m01, this.m10 + m.m10, this.m11 + m.m11) public function mat2.op_minus(mat2 m) returns mat2 return mat2(this.m00 - m.m00, this.m01 - m.m01, this.m10 - m.m10, this.m11 - m.m11) public function mat2.op_minus(real scalar) returns mat2 return mat2(this.m00 - scalar, this.m01 - scalar, this.m10 - scalar, this.m11 - scalar) public function mat2.op_plus(real scalar) returns mat2 return mat2(this.m00 + scalar, this.m01 + scalar, this.m10 + scalar, this.m11 + scalar) public function mat2.op_mult(real scalar) returns mat2 return mat2(this.m00 * scalar, this.m01 * scalar, this.m10 * scalar, this.m11 * scalar) public function real.op_mult(mat2 m) returns mat2 return m * this /** Matrix multiplication is not commutative that means matrix * vect is not the same as vec * matrix. */ public function mat2.op_mult(vec2 v) returns vec2 return vec2(this.m00 * v.x + this.m01 * v.y, this.m10 * v.x + this.m11 * v.y) /** Matrix multiplication is not commutative that means matrix * vect is not the same as vec * matrix. */ public function vec2.op_mult(mat2 m) returns vec2 return vec2(this.x * m.m00 + this.y * m.m10, this.x * m.m01 + this.y * m.m11) public function mat2.op_mult(mat2 m) returns mat2 return mat2(this.m00 * m.m00 + this.m01 * m.m10, this.m00 * m.m01 + this.m01 * m.m11, this.m10 * m.m00 + this.m11 * m.m10, this.m10 * m.m01 + this.m11 * m.m11) /** Returns matrix' column by index as a vector or rises an error if doesn't exist. */ public function mat2.col(int index) returns vec2 vec2 result = ZERO2 switch index case 0 result = vec2(this.m00, this.m10) case 1 result = vec2(this.m01, this.m11) default error("Invalid call mat2.col. Index out of range.") return result /** Returns matrix' row by index as a vector or rises an error if doesn't exist. */ public function mat2.row(int index) returns vec2 vec2 result = ZERO2 switch index case 0 result = vec2(this.m00, this.m01) case 1 result = vec2(this.m10, this.m11) default error("Invalid call mat2.row. Index out of range.") return result public function mat2.determinant() returns real return this.m00 * this.m11 - this.m01 * this.m10 /** Flips the matrix over its main diagonale. Result of transposing for rotation matrix is its inverse matrix. */ public function mat2.transpose() returns mat2 return mat2(this.m00, this.m10, this.m01, this.m11) /** Finds inverse matrix. Returns tuple inverseresult22(bool success, mat2 matrix) where 'success' is true if the inverse matrix exists and 'matrix' is the inverse matrix. If there's no inverse matrix 'success' is false and 'matrix' is zero matrix. */ public function mat2.inverse() returns inverseresult22 let d = this.determinant() var inverse = inverseresult22(d.abs() > EPSILON, ZERO22) if inverse.success let tmp = mat2( this.m11, -this.m01, -this.m10, this.m00) inverse.matrix = 1 / d * tmp return inverse public function mat2.toString() returns string return "Matrix 2x2\n{0} {1}\n{2} {3}".format( this.m00.toString(), this.m01.toString(), this.m10.toString(), this.m11.toString()) /** Creates rotation matrix from angle. */ public function angle.toRotation() returns mat2 return mat2(this.cos(), -this.sin(), this.sin(), this.cos()) /** Creates scaling 2x2 matrix from a vector. */ public function vec2.toScaling() returns mat2 return mat2(this.x, 0, 0, this.y) /* =========== 3X3 MATRIX =========== */ public function mat3.op_plus(mat3 m) returns mat3 return mat3(this.m00 + m.m00, this.m01 + m.m01, this.m02 + m.m02, this.m10 + m.m10, this.m11 + m.m11, this.m12 + m.m12, this.m20 + m.m20, this.m21 + m.m21, this.m22 + m.m22) public function mat3.op_plus(real scalar) returns mat3 return mat3(this.m00 + scalar, this.m01 + scalar, this.m02 + scalar, this.m10 + scalar, this.m11 + scalar, this.m12 + scalar, this.m20 + scalar, this.m21 + scalar, this.m22 + scalar) public function mat3.op_minus(mat3 m) returns mat3 return mat3(this.m00 - m.m00, this.m01 - m.m01, this.m02 - m.m02, this.m10 - m.m10, this.m11 - m.m11, this.m12 - m.m12, this.m20 - m.m20, this.m21 - m.m21, this.m22 - m.m22) public function mat3.op_minus(real scalar) returns mat3 return mat3(this.m00 - scalar, this.m01 - scalar, this.m02 - scalar, this.m10 - scalar, this.m11 - scalar, this.m12 - scalar, this.m20 - scalar, this.m21 - scalar, this.m22 - scalar) public function mat3.op_mult(real scalar) returns mat3 return mat3(this.m00 * scalar, this.m01 * scalar, this.m02 * scalar, this.m10 * scalar, this.m11 * scalar, this.m12 * scalar, this.m20 * scalar, this.m21 * scalar, this.m22 * scalar) public function real.op_mult(mat3 m) returns mat3 return m * this /** Matrix multiplication is not commutative that means matrix * vect is not the same as vec * matrix. */ public function mat3.op_mult(vec3 v) returns vec3 return vec3(this.m00 * v.x + this.m01 * v.y + this.m02 * v.z, this.m10 * v.x + this.m11 * v.y + this.m12 * v.z, this.m20 * v.x + this.m21 * v.y + this.m22 * v.z) public function vec3.op_mult(mat3 m) returns vec3 return vec3(this.x * m.m00 + this.y * m.m10 + this.z * m.m20, this.x * m.m01 + this.y * m.m11 + this.z * m.m21, this.x * m.m02 + this.y * m.m12 + this.z * m.m22) public function mat3.op_mult(mat3 m) returns mat3 return mat3(this.m00 * m.m00 + this.m01 * m.m10 + this.m02 * m.m20, this.m00 * m.m01 + this.m01 * m.m11 + this.m02 * m.m21, this.m00 * m.m02 + this.m01 * m.m12 + this.m02 * m.m22, this.m10 * m.m00 + this.m11 * m.m10 + this.m12 * m.m20, this.m10 * m.m01 + this.m11 * m.m11 + this.m12 * m.m21, this.m10 * m.m02 + this.m11 * m.m12 + this.m12 * m.m22, this.m20 * m.m00 + this.m21 * m.m10 + this.m22 * m.m20, this.m20 * m.m01 + this.m21 * m.m11 + this.m22 * m.m21, this.m20 * m.m02 + this.m21 * m.m12 + this.m22 * m.m22) public var tmp_mat = ZERO44 /** Due to parameter restriction you need to set `tmp_mat` as one matrix before multiplying. **/ public function mult(mat4 m) returns mat4 return mat4(tmp_mat.m00 * m.m00 + tmp_mat.m01 * m.m10 + tmp_mat.m02 * m.m20 + tmp_mat.m03 * m.m30, tmp_mat.m00 * m.m01 + tmp_mat.m01 * m.m11 + tmp_mat.m02 * m.m21 + tmp_mat.m03 * m.m31, tmp_mat.m00 * m.m02 + tmp_mat.m01 * m.m12 + tmp_mat.m02 * m.m22 + tmp_mat.m03 * m.m32, tmp_mat.m00 * m.m03 + tmp_mat.m01 * m.m13 + tmp_mat.m02 * m.m23 + tmp_mat.m03 * m.m33, tmp_mat.m10 * m.m00 + tmp_mat.m11 * m.m10 + tmp_mat.m12 * m.m20 + tmp_mat.m13 * m.m30, tmp_mat.m10 * m.m01 + tmp_mat.m11 * m.m11 + tmp_mat.m12 * m.m21 + tmp_mat.m13 * m.m31, tmp_mat.m10 * m.m02 + tmp_mat.m11 * m.m12 + tmp_mat.m12 * m.m22 + tmp_mat.m13 * m.m32, tmp_mat.m10 * m.m03 + tmp_mat.m11 * m.m13 + tmp_mat.m12 * m.m23 + tmp_mat.m13 * m.m33, tmp_mat.m20 * m.m00 + tmp_mat.m21 * m.m10 + tmp_mat.m22 * m.m20 + tmp_mat.m23 * m.m30, tmp_mat.m20 * m.m01 + tmp_mat.m21 * m.m11 + tmp_mat.m22 * m.m21 + tmp_mat.m23 * m.m31, tmp_mat.m20 * m.m02 + tmp_mat.m21 * m.m12 + tmp_mat.m22 * m.m22 + tmp_mat.m23 * m.m32, tmp_mat.m20 * m.m03 + tmp_mat.m21 * m.m13 + tmp_mat.m22 * m.m23 + tmp_mat.m23 * m.m33, tmp_mat.m30 * m.m00 + tmp_mat.m31 * m.m10 + tmp_mat.m32 * m.m20 + tmp_mat.m33 * m.m30, tmp_mat.m30 * m.m01 + tmp_mat.m31 * m.m11 + tmp_mat.m32 * m.m21 + tmp_mat.m33 * m.m31, tmp_mat.m30 * m.m02 + tmp_mat.m31 * m.m12 + tmp_mat.m32 * m.m22 + tmp_mat.m33 * m.m32, tmp_mat.m30 * m.m03 + tmp_mat.m31 * m.m13 + tmp_mat.m32 * m.m23 + tmp_mat.m33 * m.m33) /** Returns matrix' column by index as a vector or rises an error if doesn't exist. */ public function mat3.col(int index) returns vec3 vec3 result = ZERO3 switch index case 0 result = vec3(this.m00, this.m10, this.m20) case 1 result = vec3(this.m01, this.m11, this.m21) case 2 result = vec3(this.m02, this.m12, this.m22) default error("Invalid call mat3.col. Index out of range.") return result /** Returns matrix' row by index as a vector or rises an error if doesn't exist. */ public function mat3.row(int index) returns vec3 vec3 result = ZERO3 switch index case 0 result = vec3(this.m00, this.m01, this.m02) case 1 result = vec3(this.m10, this.m11, this.m12) case 2 result = vec3(this.m20, this.m21, this.m22) default error("Invalid call mat3.row. Index out of range.") return result /** The sum of the main diagonal terms. */ public function mat3.trace() returns real return this.m00 + this.m11 + this.m22 public function mat3.determinant() returns real return this.m00 * (this.m11 * this.m22 - this.m12 * this.m21) - this.m01 * (this.m10 * this.m22 - this.m12 * this.m20) + this.m02 * (this.m10 * this.m21 - this.m11 * this.m20) /** Flips the matrix over its main diagonale. Result of transposing for rotation matrix is its inverse matrix. */ public function mat3.transpose() returns mat3 return mat3(this.m00, this.m10, this.m20, this.m01, this.m11, this.m21, this.m02, this.m12, this.m22) /** Finds inverse matrix. Returns tuple inverseresult33(bool success, mat3 matrix) where 'success' is true if the inverse matrix exists and 'matrix' is the inverse matrix. If there's no inverse matrix 'success' is false and 'matrix' is zero matrix. */ public function mat3.inverse() returns inverseresult33 let d = this.determinant() var inverse = inverseresult33(d.abs() > EPSILON, ZERO33) if inverse.success let tmp = mat3( this.m11 * this.m22 - this.m12 * this.m21, this.m02 * this.m21 - this.m01 * this.m22, this.m01 * this.m12 - this.m02 * this.m11, this.m12 * this.m20 - this.m10 * this.m22, this.m00 * this.m22 - this.m20 * this.m02, this.m02 * this.m10 - this.m00 * this.m12, this.m10 * this.m21 - this.m11 * this.m20, this.m01 * this.m20 - this.m00 * this.m21, this.m00 * this.m11 - this.m01 * this.m10) inverse.matrix = 1 / d * tmp return inverse /** Extracts Euler-angles (Tait-Bryan) in radians from a rotation matrix. Order of the result is Z-Y-X (Yaw-Pitch-Roll). */ public function mat3.toEuler() returns vec3 real yaw real pitch real roll if this.m20 > 1 - EPSILON yaw = Atan2(-this.m12, -this.m02) pitch = -PI / 2 roll = 0 else if this.m20 < EPSILON - 1 yaw = Atan2(this.m12, this.m02) pitch = PI / 2 roll = 0 else yaw = Atan2(this.m10, this.m00) pitch = -Asin(this.m20) roll = Atan2(this.m21 , this.m22) return vec3(roll, pitch, yaw) public function mat3.toString() returns string return "Matrix 3x3\n{0} {1} {2}\n{3} {4} {5}\n{6} {7} {8}".format( this.m00.toString(), this.m01.toString(), this.m02.toString(), this.m10.toString(), this.m11.toString(), this.m12.toString(), this.m20.toString(), this.m21.toString(), this.m22.toString()) /** Creates 3x3 rotation matrix from axis and angle. */ public function vec3.toRotation(angle angl) returns mat3 let v = this.norm() let x = v.x let y = v.y let z = v.z let mcos = 1 - angl.cos() let sin = angl.sin() return mat3(1 + mcos * (x * x - 1), -z * sin + mcos * x * y, y * sin + mcos * x * z, z * sin + mcos * x * y, 1 + mcos * (y * y - 1), -x * sin + mcos * y * z, -y * sin + mcos * x * z, x * sin + mcos * y * z, 1 + mcos * (z * z - 1)) /** Creates 3x3 rotation matrix from axis and angle. */ public function angle.toRotation(vec3 axis) returns mat3 return axis.toRotation(this) /** Creates 3x3 rotation matrix around X-axis from angle. */ public function angle.toRotationX() returns mat3 return mat3(1, 0, 0, 0, this.cos(), -this.sin(), 0, this.sin(), this.cos()) /** Creates 3x3 rotation matrix around Y-axis from angle. */ public function angle.toRotationY() returns mat3 return mat3(this.cos(), 0, this.sin(), 0, 1, 0, -this.sin(), 0, this.cos()) /** Creates 3x3 rotation matrix around Z-axis from angle. */ public function angle.toRotationZ() returns mat3 return mat3(this.cos(), -this.sin(), 0, this.sin(), this.cos(), 0, 0, 0, 1) /** Creates 3x3 scaling matrix from a vector. */ public function vec3.toScaling() returns mat3 return mat3(this.x, 0, 0, 0, this.y, 0, 0, 0, this.z) /** Creates translation 3x3 matrix from a vector that represents an offset. Use it with vec3 that represents 2D point. */ public function vec2.toTranslation() returns mat3 return mat3(1, 0, this.x, 0, 1, this.y, 0, 0, 1) /** Multiplies this vector by the given matrix dividing by w, assuming the fourth (w) component of the vector is 1. This is * mostly used to project/unproject vectors via a perspective projection matrix. **/ public function vec3.project(mat4 matrix) returns vec3 let l_w = 1. / (this.x * matrix.m30 + this.y * matrix.m31 + this.z * matrix.m32 + matrix.m33) return vec3((this.x * matrix.m00 + this.y * matrix.m01 + this.z * matrix.m02 + matrix.m03) * l_w, (this.x * matrix.m10 + this.y * matrix.m11 + this.z * matrix.m12 + matrix.m13) * l_w, (this.x * matrix.m20 + this.y * matrix.m21 + this.z * matrix.m22 + matrix.m23) * l_w) /** Sets the matrix to a projection matrix with a near- and far plane, a field of view in degrees and an aspect ratio. Note that * the field of view specified is the angle in degrees for the height, the field of view for the width will be calculated * according to the aspect ratio. */ public function getProjection(real near, real far, real fovy, real aspectRatio) returns mat4 var result = IDENTITY44 let l_fd = (1.0 / ((fovy * (PI / 180)) / 2.0).tan()) let l_a1 = (far + near) / (near - far) let l_a2 = (2 * far * near) / (near - far) result.m00 = l_fd / aspectRatio result.m10 = 0 result.m20 = 0 result.m30 = 0 result.m01 = 0 result.m11 = l_fd result.m21 = 0 result.m31 = 0 result.m02 = 0 result.m12 = 0 result.m22 = l_a1 result.m32 = -1 result.m03 = 0 result.m13 = 0 result.m23 = l_a2 result.m33 = 0 return result /** Sets this matrix to a translation matrix, overwriting it first by an identity matrix and then setting the 4th column to the * translation vector. */ public function setToTranslation(real x, real y, real z) returns mat4 var result = IDENTITY44 result.m03 = x result.m13 = y result.m23 = z return result /** Sets this matrix to a look at matrix with the given position, target and up vector. */ public function setToLookAt(vec3 position, vec3 target, vec3 up) returns mat4 var result = setToLookAt(target - position, up) let trans = setToTranslation(-position.x, -position.y, -position.z) tmp_mat = result result = mult(trans) return result /** Sets the matrix to a look at matrix with a direction and an up vector. Multiply with a translation matrix to get a camera * model view matrix. */ public function setToLookAt(vec3 direction, vec3 up) returns mat4 let l_vez = direction.norm() var l_vex = direction.norm() l_vex = l_vex.cross(up).norm() let l_vey = l_vex.cross(l_vez).norm() var result = IDENTITY44 result.m00 = l_vex.x result.m01 = l_vex.y result.m02 = l_vex.z result.m10 = l_vey.x result.m11 = l_vey.y result.m12 = l_vey.z result.m20 = -l_vez.x result.m21 = -l_vez.y result.m22 = -l_vez.z return result public function mat4.determinant() returns real var f = this.m00 * ((this.m11 * this.m22 * this.m33 + this.m12 * this.m23 * this.m31 + this.m13 * this.m21 * this.m32) - this.m13 * this.m22 * this.m31 - this.m11 * this.m23 * this.m32 - this.m12 * this.m21 * this.m33) f -= this.m01 * ((this.m10 * this.m22 * this.m33 + this.m12 * this.m23 * this.m30 + this.m13 * this.m20 * this.m32) - this.m13 * this.m22 * this.m30 - this.m10 * this.m23 * this.m32 - this.m12 * this.m20 * this.m33) f += this.m02 * ((this.m10 * this.m21 * this.m33 + this.m11 * this.m23 * this.m30 + this.m13 * this.m20 * this.m31) - this.m13 * this.m21 * this.m30 - this.m10 * this.m23 * this.m31 - this.m11 * this.m20 * this.m33) f -= this.m03 * ((this.m10 * this.m21 * this.m32 + this.m11 * this.m22 * this.m30 + this.m12 * this.m20 * this.m31) - this.m12 * this.m21 * this.m30 - this.m10 * this.m22 * this.m31 - this.m11 * this.m20 * this.m32) return f function determinant3x3(real t00, real t01, real t02, real t10, real t11, real t12, real t20, real t21, real t22) returns real return t00 * (t11 * t22 - t12 * t21) + t01 * (t12 * t20 - t10 * t22) + t02 * (t10 * t21 - t11 * t20) /** Finds inverse matrix. Returns tuple inverseresult44(bool success, mat3 matrix) where 'success' is true if the inverse matrix exists and 'matrix' is the inverse matrix. If there's no inverse matrix 'success' is false and 'matrix' is zero matrix. */ public function mat4.inverse() returns inverseresult44 let d = this.determinant() var inverse = inverseresult44(d.abs() > EPSILON, ZERO44) if inverse.success let determinant_inv = 1 / d // first row let t00 = determinant3x3(this.m11, this.m12, this.m13, this.m21, this.m22, this.m23, this.m31, this.m32, this.m33) let t01 = -determinant3x3(this.m10, this.m12, this.m13, this.m20, this.m22, this.m23, this.m30, this.m32, this.m33) let t02 = determinant3x3(this.m10, this.m11, this.m13, this.m20, this.m21, this.m23, this.m30, this.m31, this.m33) let t03 = -determinant3x3(this.m10, this.m11, this.m12, this.m20, this.m21, this.m22, this.m30, this.m31, this.m32) // second row let t10 = -determinant3x3(this.m01, this.m02, this.m03, this.m21, this.m22, this.m23, this.m31, this.m32, this.m33) let t11 = determinant3x3(this.m00, this.m02, this.m03, this.m20, this.m22, this.m23, this.m30, this.m32, this.m33) let t12 = -determinant3x3(this.m00, this.m01, this.m03, this.m20, this.m21, this.m23, this.m30, this.m31, this.m33) let t13 = determinant3x3(this.m00, this.m01, this.m02, this.m20, this.m21, this.m22, this.m30, this.m31, this.m32) // third row let t20 = determinant3x3(this.m01, this.m02, this.m03, this.m11, this.m12, this.m13, this.m31, this.m32, this.m33) let t21 = -determinant3x3(this.m00, this.m02, this.m03, this.m10, this.m12, this.m13, this.m30, this.m32, this.m33) let t22 = determinant3x3(this.m00, this.m01, this.m03, this.m10, this.m11, this.m13, this.m30, this.m31, this.m33) let t23 = -determinant3x3(this.m00, this.m01, this.m02, this.m10, this.m11, this.m12, this.m30, this.m31, this.m32) // fourth row let t30 = -determinant3x3(this.m01, this.m02, this.m03, this.m11, this.m12, this.m13, this.m21, this.m22, this.m23) let t31 = determinant3x3(this.m00, this.m02, this.m03, this.m10, this.m12, this.m13, this.m20, this.m22, this.m23) let t32 = -determinant3x3(this.m00, this.m01, this.m03, this.m10, this.m11, this.m13, this.m20, this.m21, this.m23) let t33 = determinant3x3(this.m00, this.m01, this.m02, this.m10, this.m11, this.m12, this.m20, this.m21, this.m22) // transpose and divide by the determinant inverse.matrix.m00 = t00*determinant_inv inverse.matrix.m11 = t11*determinant_inv inverse.matrix.m22 = t22*determinant_inv inverse.matrix.m33 = t33*determinant_inv inverse.matrix.m01 = t10*determinant_inv inverse.matrix.m10 = t01*determinant_inv inverse.matrix.m20 = t02*determinant_inv inverse.matrix.m02 = t20*determinant_inv inverse.matrix.m12 = t21*determinant_inv inverse.matrix.m21 = t12*determinant_inv inverse.matrix.m03 = t30*determinant_inv inverse.matrix.m30 = t03*determinant_inv inverse.matrix.m13 = t31*determinant_inv inverse.matrix.m31 = t13*determinant_inv inverse.matrix.m32 = t23*determinant_inv inverse.matrix.m23 = t32*determinant_inv return inverse public function mat4.toString() returns string return "Matrix 4x4\n{0} {1} {2} {3}\n{4} {5} {6} {7}\n{8} {9} {10} {11}\n{12} {13} {14} {15}".format( this.m00.toString(), this.m01.toString(), this.m02.toString(), this.m03.toString(), this.m10.toString(), this.m11.toString(), this.m12.toString(), this.m13.toString(), this.m20.toString(), this.m21.toString(), this.m22.toString(), this.m23.toString(), this.m30.toString(), this.m31.toString(), this.m32.toString(), this.m33.toString()) /* =========== TESTING =========== */ public function mat2.assertEquals(mat2 expected) let delta = 0.002 let compare = mat2( (this.m00 - expected.m00).abs(), (this.m01 - expected.m01).abs(), (this.m10 - expected.m10).abs(), (this.m11 - expected.m11).abs()) let msg = "Expected {0} <{1}>, Actual <{2} with delta {3}>" if compare.m00 > delta testFail(msg.format("m00", expected.m00.toString(), this.m00.toString(), delta.toString())) if compare.m01 > delta testFail(msg.format("m01", expected.m01.toString(), this.m01.toString(), delta.toString())) if compare.m10 > delta testFail(msg.format("m10", expected.m10.toString(), this.m10.toString(), delta.toString())) if compare.m11 > delta testFail(msg.format("m11", expected.m11.toString(), this.m11.toString(), delta.toString())) public function mat3.assertEquals(mat3 expected) let delta = 0.002 let compare = mat3( (this.m00 - expected.m00).abs(), (this.m01 - expected.m01).abs(), (this.m02 - expected.m02).abs(), (this.m10 - expected.m10).abs(), (this.m11 - expected.m11).abs(), (this.m12 - expected.m12).abs(), (this.m20 - expected.m20).abs(), (this.m21 - expected.m21).abs(), (this.m22 - expected.m22).abs()) let msg = "Expected {0} <{1}>, Actual <{2} with delta {3}>" if compare.m00 > delta testFail(msg.format("m00", expected.m00.toString(), this.m00.toString(), delta.toString())) if compare.m01 > delta testFail(msg.format("m01", expected.m01.toString(), this.m01.toString(), delta.toString())) if compare.m02 > delta testFail(msg.format("m02", expected.m02.toString(), this.m02.toString(), delta.toString())) if compare.m10 > delta testFail(msg.format("m10", expected.m10.toString(), this.m10.toString(), delta.toString())) if compare.m11 > delta testFail(msg.format("m11", expected.m11.toString(), this.m11.toString(), delta.toString())) if compare.m12 > delta testFail(msg.format("m12", expected.m12.toString(), this.m12.toString(), delta.toString())) if compare.m20 > delta testFail(msg.format("m20", expected.m20.toString(), this.m20.toString(), delta.toString())) if compare.m21 > delta testFail(msg.format("m21", expected.m21.toString(), this.m21.toString(), delta.toString())) if compare.m22 > delta testFail(msg.format("m22", expected.m22.toString(), this.m22.toString(), delta.toString()))