blob: c495178df453ea265166d013da0be9bd728851f3 [file] [log] [blame]
/*
VectorMath.dart
Copyright (C) 2012 John McCutchan <john@johnmccutchan.com>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
part of vector_math_generator;
class MatrixGenerator extends BaseGenerator {
int rows;
int cols;
MatrixGenerator() : super() {
}
String matrixTypeName(int rows_, int cols_) {
return 'mat${cols}';
}
String get rowVecType => 'vec$cols';
String get colVecType => 'vec$rows';
String get matType => matrixTypeName(rows, cols);
String get matTypeTransposed => matrixTypeName(cols, rows);
List<String> get matrixComponents {
List<String> r = new List<String>();
for (int i = 0; i < cols; i++) {
r.add('col$i');
}
return r;
}
String Access(int row, int col, [String pre = 'col']) {
//assert(row < rows && row >= 0);
//assert(col < cols && col >= 0);
String rowName = '';
if (row == 0) {
rowName = 'x';
} else if (row == 1) {
rowName = 'y';
} else if (row == 2) {
rowName = 'z';
} else if (row == 3) {
rowName = 'w';
} else {
assert(row < 4);
}
return '$pre$col.$rowName';
}
String AccessV(int row) {
String rowName = '';
if (row == 0) {
rowName = 'x';
} else if (row == 1) {
rowName = 'y';
} else if (row == 2) {
rowName = 'z';
} else if (row == 3) {
rowName = 'w';
} else {
assert(row < 4 && row >= 0);
}
return rowName;
}
void generatePrologue() {
iPrint('part of vector_math;');
iPrint('\/\/\/ ${matType} is a column major matrix where each column is represented by [$colVecType]. This matrix has $cols columns and $rows rows.');
iPrint('class ${matType} {');
iPush();
for (int i = 0; i < cols; i++) {
iPrint('$colVecType col$i;');
}
}
void generateConstructors() {
int numArguments = cols * rows;
List<String> arguments = new List<String>(numArguments);
for (int i = 0; i < numArguments; i++) {
arguments[i] = 'arg$i';
}
List<String> columnArguments = new List<String>(cols);
for (int i = 0; i < cols; i++) {
columnArguments[i] = 'arg$i';
}
iPrint('\/\/\/ Constructs a new ${matType}. Supports GLSL like syntax so many possible inputs. Defaults to identity matrix.');
iPrint('${matType}([${joinStrings(arguments, 'dynamic ')}]) {');
iPush();
iPrint('//Initialize the matrix as the identity matrix');
for (int i = 0; i < cols; i++) {
iPrint('col$i = new $colVecType.zero();');
}
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
if (i == j) {
iPrint('${Access(j, i)} = 1.0;');
}
}
}
iPrint('if (${joinStrings(arguments, '', ' is num', ' && ')}) {');
iPush();
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
iPrint('${Access(j, i)} = arg${(i*rows)+j};');
}
}
iPrint('return;');
iPop();
iPrint('}');
iPrint('if (arg0 is num && ${joinStrings(arguments.getRange(1, numArguments-1), '', ' == null', ' && ')}) {');
iPush();
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
if (i == j) {
iPrint('${Access(j, i)} = arg0;');
}
}
}
iPrint('return;');
iPop();
iPrint('}');
iPrint('if (${joinStrings(columnArguments, '', ' is vec${cols}', ' && ')}) {');
iPush();
for (int i = 0; i < cols; i++) {
iPrint('col$i = arg$i;');
}
iPrint('return;');
iPop();
iPrint('}');
iPrint('if (arg0 is ${matType}) {');
iPush();
for (int i = 0; i < cols; i++) {
iPrint('col$i = arg0.col$i;');
}
iPrint('return;');
iPop();
iPrint('}');
for (int i = cols; i >= 2; i--) {
for (int j = rows; j >= 2; j--) {
if (i == cols && j == rows) {
continue;
}
if (i != j) {
continue;
}
iPrint('if (arg0 is mat${i}) {');
iPush();
for (int k = 0; k < i; k++) {
for (int l = 0; l < j; l++) {
iPrint('${Access(l, k)} = arg0.${Access(l, k)};');
}
}
iPrint('return;');
iPop();
iPrint('}');
}
}
int diagonals = rows < cols ? rows : cols;
for (int i = 1; i < diagonals; i++) {
iPrint('if (arg0 is vec${i+1} && ${joinStrings(arguments.getRange(1, numArguments-1), '', ' == null', ' && ')}) {');
iPush();
for (int j = 0; j < i+1; j++) {
iPrint('${Access(j, j)} = arg0.${AccessV(j)};');
}
iPop();
iPrint('}');
}
iPrint('throw new ArgumentError(\'Invalid arguments\');');
iPop();
iPrint('}');
// Outer product constructor
iPrint('\/\/\/ Constructs a new [${matType}] from computing the outer product of [u] and [v].');
iPrint('${matType}.outer(vec${cols} u, vec${rows} v) {');
iPush();
for (int i = 0; i < cols; i++) {
iPrint('col$i = new ${colVecType}.zero();');
}
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
iPrint('${Access(j, i)} = u.${AccessV(i)} * v.${AccessV(j)};');
}
}
iPop();
iPrint('}');
iPrint('\/\/\/ Constructs a new [${matType}] filled with zeros.');
iPrint('${matType}.zero() {');
iPush();
for (int i = 0; i < cols; i++) {
iPrint('col$i = new $colVecType.zero();');
}
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
iPrint('${Access(j, i)} = 0.0;');
}
}
iPop();
iPrint('}');
iPrint('\/\/\/ Constructs a new identity [${matType}].');
iPrint('${matType}.identity() {');
iPush();
for (int i = 0; i < cols; i++) {
iPrint('col$i = new ${colVecType}.zero();');
}
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
if (i == j) {
iPrint('${Access(j, i)} = 1.0;');
}
}
}
iPop();
iPrint('}');
iPrint('\/\/\/ Constructs a new [${matType}] which is a copy of [other].');
iPrint('${matType}.copy($matType other) {');
iPush();
for (int i = 0; i < cols; i++) {
iPrint('col$i = new ${colVecType}.zero();');
}
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
iPrint('${Access(j, i)} = other.${Access(j, i)};');
}
}
iPop();
iPrint('}');
if (rows == 2 && cols == 2) {
iPrint('\/\/\/ Constructs a new [${matType}] representing a rotation by [radians].');
iPrint('${matType}.rotation(num radians_) {');
iPush();
for (int i = 0; i < cols; i++) {
iPrint('col$i = new $colVecType.zero();');
}
iPrint('setRotation(radians_);');
iPop();
iPrint('}');
}
if ((rows == 3 && cols == 3) || (rows == 4 && cols == 4)) {
iPrint('\/\/\/\/ Constructs a new [${matType}] representation a rotation of [radians] around the X axis');
iPrint('${matType}.rotationX(num radians_) {');
iPush();
for (int i = 0; i < cols; i++) {
iPrint('col$i = new $colVecType.zero();');
}
if (rows == 4 && cols == 4) {
iPrint('col3.w = 1.0;');
}
iPrint('setRotationX(radians_);');
iPop();
iPrint('}');
iPrint('\/\/\/\/ Constructs a new [${matType}] representation a rotation of [radians] around the Y axis');
iPrint('${matType}.rotationY(num radians_) {');
iPush();
for (int i = 0; i < cols; i++) {
iPrint('col$i = new $colVecType.zero();');
}
if (rows == 4 && cols == 4) {
iPrint('col3.w = 1.0;');
}
iPrint('setRotationY(radians_);');
iPop();
iPrint('}');
iPrint('\/\/\/\/ Constructs a new [${matType}] representation a rotation of [radians] around the Z axis');
iPrint('${matType}.rotationZ(num radians_) {');
iPush();
for (int i = 0; i < cols; i++) {
iPrint('col$i = new $colVecType.zero();');
}
if (rows == 4 && cols == 4) {
iPrint('col3.w = 1.0;');
}
iPrint('setRotationZ(radians_);');
iPop();
iPrint('}');
}
if (rows == 4 && cols == 4) {
iPrint('\/\/\/ Constructs a new [${matType}] translation matrix from [translation]');
iPrint('${matType}.translation(vec3 translation) {');
iPush();
for (int i = 0; i < cols; i++) {
iPrint('col$i = new $colVecType.zero();');
}
iPrint('col0.x = 1.0;');
iPrint('col1.y = 1.0;');
iPrint('col2.z = 1.0;');
iPrint('col3.w = 1.0;');
iPrint('col3.xyz = translation;');
iPop();
iPrint('}');
iPrint('\/\/\/ Constructs a new [${matType}] translation from [x], [y], and [z]');
iPrint('${matType}.translationRaw(num x, num y, num z) {');
iPush();
for (int i = 0; i < cols; i++) {
iPrint('col$i = new $colVecType.zero();');
}
iPrint('col0.x = 1.0;');
iPrint('col1.y = 1.0;');
iPrint('col2.z = 1.0;');
iPrint('col3.w = 1.0;');
iPrint('col3.x = x;');
iPrint('col3.y = y;');
iPrint('col3.z = z;');
iPop();
iPrint('}');
iPrint('\/\/\/\/ Constructs a new [${matType}] scale of [x], [y], and [z]');
iPrint('${matType}.scaleVec(vec3 scale_) {');
iPush();
for (int i = 0; i < cols; i++) {
iPrint('col$i = new $colVecType.zero();');
}
iPrint('col0.x = scale_.x;');
iPrint('col1.y = scale_.y;');
iPrint('col2.z = scale_.z;');
iPrint('col3.w = 1.0;');
iPop();
iPrint('}');
iPrint('\/\/\/\/ Constructs a new [${matType}] representening a scale of [x], [y], and [z]');
iPrint('${matType}.scaleRaw(num x, num y, num z) {');
iPush();
for (int i = 0; i < cols; i++) {
iPrint('col$i = new $colVecType.zero();');
}
iPrint('col0.x = x;');
iPrint('col1.y = y;');
iPrint('col2.z = z;');
iPrint('col3.w = 1.0;');
iPop();
iPrint('}');
}
iPrint('${matType}.raw(${joinStrings(arguments, 'num ')}) {');
iPush();
for (int i = 0; i < cols; i++) {
iPrint('col$i = new $colVecType.zero();');
}
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
iPrint('${Access(j, i)} = arg${(i*rows)+j};');
}
}
iPop();
iPrint('}');
}
void generateRowColProperties() {
iPrint('\/\/\/ Returns the number of rows in the matrix.');
iPrint('int get rows => $rows;');
iPrint('\/\/\/ Returns the number of columns in the matrix.');
iPrint('int get cols => $cols;');
iPrint('\/\/\/ Returns the number of columns in the matrix.');
iPrint('int get length => $cols;');
}
void generateRowGetterSetters() {
for (int i = 0; i < rows; i++) {
iPrint('\/\/\/ Returns row $i');
iPrint('$rowVecType get row$i => getRow($i);');
}
for (int i = 0; i < rows; i++) {
iPrint('\/\/\/ Sets row $i to [arg]');
iPrint('set row$i($rowVecType arg) => setRow($i, arg);');
}
}
void generateIndexOperator() {
iPrint('\/\/\/ Gets the [column] of the matrix');
iPrint('$colVecType operator[](int column) {');
iPush();
iPrint('assert(column >= 0 && column < $cols);');
iPrint('switch (column) {');
iPush();
for (int i = 0; i < cols; i++) {
iPrint('case $i: return col$i;');
}
iPop();
iPrint('}');
iPrint('throw new ArgumentError(column);');
iPop();
iPrint('}');
}
void generateAssignIndexOperator() {
iPrint('\/\/\/ Assigns the [column] of the matrix [arg]');
iPrint('void operator[]=(int column, $colVecType arg) {');
iPush();
iPrint('assert(column >= 0 && column < $cols);');
iPrint('switch (column) {');
iPush();
for (int i = 0; i < cols; i++) {
iPrint('case $i: col$i = arg; break;');
}
iPop();
iPrint('}');
iPrint('throw new ArgumentError(column);');
iPop();
iPrint('}');
}
void generateRowHelpers() {
iPrint('\/\/\/ Assigns the [column] of the matrix [arg]');
iPrint('void setRow(int row, $rowVecType arg) {');
iPush();
iPrint('assert(row >= 0 && row < $rows);');
for (int i = 0; i < cols; i++) {
iPrint('col$i[row] = arg.${AccessV(i)};');
}
iPop();
iPrint('}');
iPrint('\/\/\/ Gets the [row] of the matrix');
iPrint('$rowVecType getRow(int row) {');
iPush();
iPrint('assert(row >= 0 && row < $rows);');
iPrint('${rowVecType} r = new ${rowVecType}.zero();');
for (int i = 0; i < cols; i++) {
iPrint('r.${AccessV(i)} = col$i[row];');
}
iPrint('return r;');
iPop();
iPrint('}');
}
void generateColumnHelpers() {
iPrint('\/\/\/ Assigns the [column] of the matrix [arg]');
iPrint('void setColumn(int column, $colVecType arg) {');
iPush();
iPrint('assert(column >= 0 && column < $cols);');
iPrint('this[column] = arg;');
iPop();
iPrint('}');
iPrint('\/\/\/ Gets the [column] of the matrix');
iPrint('$colVecType getColumn(int column) {');
iPush();
iPrint('assert(column >= 0 && column < $cols);');
iPrint('return new ${colVecType}.copy(this[column]);');
iPop();
iPrint('}');
}
void generateToString() {
iPrint('\/\/\/ Returns a printable string');
iPrint('String toString() {');
iPush();
iPrint('String s = \'\';');
for (int i = 0; i < rows; i++) {
iPrint('s = \'\$s[$i] \${getRow($i)}\\n\';');
}
iPrint('return s;');
iPop();
iPrint('}');
}
void generateEpilogue() {
iPop();
iPrint('}');
}
String generateInlineDot(String rowPrefix, int row, String col, int len) {
String r = '';
for (int i = 0; i < len; i++) {
if (i != 0) {
r = '$r +';
}
r = '$r (${rowPrefix}.${Access(row, i)} * ${col}.${AccessV(i)})';
}
return r;
}
void generateMatrixVectorMultiply() {
iPrint('$colVecType r = new $colVecType.zero();');
for (int i = 0; i < rows; i++) {
iPrint('r.${AccessV(i)} = ${generateInlineDot('this', i, 'arg', cols)};');
}
iPrint('return r;');
}
void generateMatrixVectorMultiply3() {
iPrint('vec3 r = new vec3.zero();');
for (int i = 0; i < 3; i++) {
iPrint('r.${AccessV(i)} = ${generateInlineDot('this', i, 'arg', 3)} + ${Access(i, 3)};');
}
iPrint('return r;');
}
String generateInlineDotM(String rowPrefix, String colPrefix, int srow, int scol, int len) {
String r = '';
for (int i = 0; i < len; i++) {
if (i != 0) {
r = '$r +';
}
r = '$r (${rowPrefix}.${Access(srow, i)} * ${colPrefix}.${Access(i, scol)})';
}
return r;
}
void generateMatrixMatrixMultiply() {
iPrint('dynamic r = null;');
if (rows == 2 && cols == 2) {
iPrint('if (arg.cols == 2) {');
iPush();
iPrint('r = new mat2.zero();');
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 2; j++) {
iPrint('r.${Access(i, j)} = ${generateInlineDotM('this', 'arg', i, j, cols)};');
}
}
iPrint('return r;');
iPop();
iPrint('}');
}
if (rows == 3 && cols == 3) {
if (rows >= 3) {
iPrint('if (arg.cols == 3) {');
iPush();
iPrint('r = new mat3.zero();');
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 3; j++) {
iPrint('r.${Access(i, j)} = ${generateInlineDotM('this', 'arg', i, j, cols)};');
}
}
iPrint('return r;');
iPop();
iPrint('}');
}
}
if (rows == 4 && cols == 4) {
if (rows >= 4) {
iPrint('if (arg.cols == 4) {');
iPush();
iPrint('r = new mat4.zero();');
for (int i = 0; i < rows; i++) {
for (int j = 0; j < 4; j++) {
iPrint('r.${Access(i, j)} = ${generateInlineDotM('this', 'arg', i, j, cols)};');
}
}
iPrint('return r;');
iPop();
iPrint('}');
}
}
iPrint('return r;');
}
void generateMatrixScale() {
iPrint('${matType} r = new ${matType}.zero();');
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
iPrint('r.${Access(j, i)} = ${Access(j, i)} * arg;');
}
}
iPrint('return r;');
}
void generateMult() {
iPrint('\/\/\/ Returns a new vector or matrix by multiplying [this] with [arg].');
iPrint('dynamic operator*(dynamic arg) {');
iPush();
iPrint('if (arg is num) {');
iPush();
generateMatrixScale();
iPop();
iPrint('}');
iPrint('if (arg is $rowVecType) {');
iPush();
generateMatrixVectorMultiply();
iPop();
iPrint('}');
if (matType == 'mat4') {
iPrint('if (arg is vec3) {');
iPush();
generateMatrixVectorMultiply3();
iPop();
iPrint('}');
}
iPrint('if ($cols == arg.rows) {');
iPush();
generateMatrixMatrixMultiply();
iPop();
iPrint('}');
iPrint('throw new ArgumentError(arg);');
iPop();
iPrint('}');
}
void generateConstructionSetters() {
iPrint('\/\/\/ Zeros [this].');
iPrint('${matType} setZero() {');
iPush();
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
iPrint('${Access(j, i)} = 0.0;');
}
}
iPrint('return this;');
iPop();
iPrint('}');
iPrint('\/\/\/ Makes [this] into the identity matrix.');
iPrint('${matType} setIdentity() {');
iPush();
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
if (i == j) {
iPrint('${Access(j, i)} = 1.0;');
} else {
iPrint('${Access(j, i)} = 0.0;');
}
}
}
iPrint('return this;');
iPop();
iPrint('}');
}
void generateOp(String op) {
iPrint('\/\/\/ Returns new matrix after component wise [this] $op [arg]');
iPrint('${matType} operator$op(${matType} arg) {');
iPush();
iPrint('${matType} r = new ${matType}.zero();');
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
iPrint('r.${Access(j, i)} = ${Access(j, i)} $op arg.${Access(j, i)};');
}
}
iPrint('return r;');
iPop();
iPrint('}');
}
String generateInlineDotArgs(String aX, String aY, String aZ, String aW, String bX, String bY, String bZ, String bW) {
return '$aX * $bX + $aY * $bY + $aZ * $bZ + $aW * $bW';
}
void generateInlineTranslate() {
if (rows != 4 || cols != 4) {
return;
}
iPrint('\/\/\/ Translate this matrix by a [vec3], [vec4], or x,y,z');
iPrint('${matType} translate(dynamic x, [num y = 0.0, num z = 0.0]) {');
iPush();
iPrint('double tx;');
iPrint('double ty;');
iPrint('double tz;');
iPrint('double tw = x is vec4 ? x.w : 1.0;');
iPrint('if (x is vec3 || x is vec4) {');
iPush();
iPrint('tx = x.x;');
iPrint('ty = x.y;');
iPrint('tz = x.z;');
iPop();
iPrint('} else {');
iPush();
iPrint('tx = x;');
iPrint('ty = y;');
iPrint('tz = z;');
iPop();
iPrint('}');
iPrint('var t1 = ${generateInlineDotArgs(Access(0, 0), Access(0, 1), Access(0, 2), Access(0, 3), 'tx', 'ty', 'tz', 'tw')};');
iPrint('var t2 = ${generateInlineDotArgs(Access(1, 0), Access(1, 1), Access(1, 2), Access(1, 3), 'tx', 'ty', 'tz', 'tw')};');
iPrint('var t3 = ${generateInlineDotArgs(Access(2, 0), Access(2, 1), Access(2, 2), Access(2, 3), 'tx', 'ty', 'tz', 'tw')};');
iPrint('var t4 = ${generateInlineDotArgs(Access(3, 0), Access(3, 1), Access(3, 2), Access(3, 3), 'tx', 'ty', 'tz', 'tw')};');
iPrint('${Access(0, 3)} = t1;');
iPrint('${Access(1, 3)} = t2;');
iPrint('${Access(2, 3)} = t3;');
iPrint('${Access(3, 3)} = t4;');
iPrint('return this;');
iPop();
iPrint('}');
}
void generateInlineRotate() {
if (rows != 4 || cols != 4) {
return;
}
iPrint('\/\/\/ Rotate this [angle] radians around [axis]');
iPrint('${matType} rotate(vec3 axis, num angle_) {');
iPush();
// http://en.wikipedia.org/wiki/Rotation_matrix#Axis_and_angle
iPrint('var len = axis.length;');
iPrint('double angle = angle_.toDouble();');
iPrint('var x = axis.x/len;');
iPrint('var y = axis.y/len;');
iPrint('var z = axis.z/len;');
iPrint('var c = cos(angle);');
iPrint('var s = sin(angle);');
iPrint('var C = 1.0 - c;');
// row 1
iPrint('var m11 = x * x * C + c;');
iPrint('var m12 = x * y * C - z * s;');
iPrint('var m13 = x * z * C + y * s;');
// row 2
iPrint('var m21 = y * x * C + z * s;');
iPrint('var m22 = y * y * C + c;');
iPrint('var m23 = y * z * C - x * s;');
// row 3
iPrint('var m31 = z * x * C - y * s;');
iPrint('var m32 = z * y * C + x * s;');
iPrint('var m33 = z * z * C + c;');
iPrint('var t1 = ${generateInlineDotArgs(Access(0, 0), Access(0, 1), Access(0, 2), Access(0, 3), 'm11', 'm21', 'm31', '0.0')};');
iPrint('var t2 = ${generateInlineDotArgs(Access(1, 0), Access(1, 1), Access(1, 2), Access(1, 3), 'm11', 'm21', 'm31', '0.0')};');
iPrint('var t3 = ${generateInlineDotArgs(Access(2, 0), Access(2, 1), Access(2, 2), Access(2, 3), 'm11', 'm21', 'm31', '0.0')};');
iPrint('var t4 = ${generateInlineDotArgs(Access(3, 0), Access(3, 1), Access(3, 2), Access(3, 3), 'm11', 'm21', 'm31', '0.0')};');
iPrint('var t5 = ${generateInlineDotArgs(Access(0, 0), Access(0, 1), Access(0, 2), Access(0, 3), 'm12', 'm22', 'm32', '0.0')};');
iPrint('var t6 = ${generateInlineDotArgs(Access(1, 0), Access(1, 1), Access(1, 2), Access(1, 3), 'm12', 'm22', 'm32', '0.0')};');
iPrint('var t7 = ${generateInlineDotArgs(Access(2, 0), Access(2, 1), Access(2, 2), Access(2, 3), 'm12', 'm22', 'm32', '0.0')};');
iPrint('var t8 = ${generateInlineDotArgs(Access(3, 0), Access(3, 1), Access(3, 2), Access(3, 3), 'm12', 'm22', 'm32', '0.0')};');
iPrint('var t9 = ${generateInlineDotArgs(Access(0, 0), Access(0, 1), Access(0, 2), Access(0, 3), 'm13', 'm23', 'm33', '0.0')};');
iPrint('var t10 = ${generateInlineDotArgs(Access(1, 0), Access(1, 1), Access(1, 2), Access(1, 3), 'm13', 'm23', 'm33', '0.0')};');
iPrint('var t11 = ${generateInlineDotArgs(Access(2, 0), Access(2, 1), Access(2, 2), Access(2, 3), 'm13', 'm23', 'm33', '0.0')};');
iPrint('var t12 = ${generateInlineDotArgs(Access(3, 0), Access(3, 1), Access(3, 2), Access(3, 3), 'm13', 'm23', 'm33', '0.0')};');
iPrint('${Access(0, 0)} = t1;');
iPrint('${Access(1, 0)} = t2;');
iPrint('${Access(2, 0)} = t3;');
iPrint('${Access(3, 0)} = t4;');
iPrint('${Access(0, 1)} = t5;');
iPrint('${Access(1, 1)} = t6;');
iPrint('${Access(2, 1)} = t7;');
iPrint('${Access(3, 1)} = t8;');
iPrint('${Access(0, 2)} = t9;');
iPrint('${Access(1, 2)} = t10;');
iPrint('${Access(2, 2)} = t11;');
iPrint('${Access(3, 2)} = t12;');
iPrint('return this;');
iPop();
iPrint('}');
iPrint('\/\/\/ Rotate this [angle] radians around X');
iPrint('${matType} rotateX(num angle_) {');
iPush();
iPrint('double angle = angle_.toDouble();');
iPrint('double cosAngle = cos(angle);');
iPrint('double sinAngle = sin(angle);');
iPrint('var t1 = ${generateInlineDotArgs(Access(0, 0), Access(0, 1), Access(0, 2), Access(0, 3), '0.0', 'cosAngle', 'sinAngle', '0.0')};');
iPrint('var t2 = ${generateInlineDotArgs(Access(1, 0), Access(1, 1), Access(1, 2), Access(1, 3), '0.0', 'cosAngle', 'sinAngle', '0.0')};');
iPrint('var t3 = ${generateInlineDotArgs(Access(2, 0), Access(2, 1), Access(2, 2), Access(2, 3), '0.0', 'cosAngle', 'sinAngle', '0.0')};');
iPrint('var t4 = ${generateInlineDotArgs(Access(3, 0), Access(3, 1), Access(3, 2), Access(3, 3), '0.0', 'cosAngle', 'sinAngle', '0.0')};');
iPrint('var t5 = ${generateInlineDotArgs(Access(0, 0), Access(0, 1), Access(0, 2), Access(0, 3), '0.0', '-sinAngle', 'cosAngle', '0.0')};');
iPrint('var t6 = ${generateInlineDotArgs(Access(1, 0), Access(1, 1), Access(1, 2), Access(1, 3), '0.0', '-sinAngle', 'cosAngle', '0.0')};');
iPrint('var t7 = ${generateInlineDotArgs(Access(2, 0), Access(2, 1), Access(2, 2), Access(2, 3), '0.0', '-sinAngle', 'cosAngle', '0.0')};');
iPrint('var t8 = ${generateInlineDotArgs(Access(3, 0), Access(3, 1), Access(3, 2), Access(3, 3), '0.0', '-sinAngle', 'cosAngle', '0.0')};');
iPrint('${Access(0, 1)} = t1;');
iPrint('${Access(1, 1)} = t2;');
iPrint('${Access(2, 1)} = t3;');
iPrint('${Access(3, 1)} = t4;');
iPrint('${Access(0, 2)} = t5;');
iPrint('${Access(1, 2)} = t6;');
iPrint('${Access(2, 2)} = t7;');
iPrint('${Access(3, 2)} = t8;');
iPrint('return this;');
iPop();
iPrint('}');
iPrint('\/\/\/ Rotate this matrix [angle] radians around Y');
iPrint('${matType} rotateY(double angle) {');
iPush();
iPrint('double cosAngle = cos(angle);');
iPrint('double sinAngle = sin(angle);');
iPrint('var t1 = ${generateInlineDotArgs(Access(0, 0), Access(0, 1), Access(0, 2), Access(0, 3), 'cosAngle', '0.0', 'sinAngle', '0.0')};');
iPrint('var t2 = ${generateInlineDotArgs(Access(1, 0), Access(1, 1), Access(1, 2), Access(1, 3), 'cosAngle', '0.0', 'sinAngle', '0.0')};');
iPrint('var t3 = ${generateInlineDotArgs(Access(2, 0), Access(2, 1), Access(2, 2), Access(2, 3), 'cosAngle', '0.0', 'sinAngle', '0.0')};');
iPrint('var t4 = ${generateInlineDotArgs(Access(3, 0), Access(3, 1), Access(3, 2), Access(3, 3), 'cosAngle', '0.0', 'sinAngle', '0.0')};');
iPrint('var t5 = ${generateInlineDotArgs(Access(0, 0), Access(0, 1), Access(0, 2), Access(0, 3), '-sinAngle', '0.0', 'cosAngle', '0.0')};');
iPrint('var t6 = ${generateInlineDotArgs(Access(1, 0), Access(1, 1), Access(1, 2), Access(1, 3), '-sinAngle', '0.0', 'cosAngle', '0.0')};');
iPrint('var t7 = ${generateInlineDotArgs(Access(2, 0), Access(2, 1), Access(2, 2), Access(2, 3), '-sinAngle', '0.0', 'cosAngle', '0.0')};');
iPrint('var t8 = ${generateInlineDotArgs(Access(3, 0), Access(3, 1), Access(3, 2), Access(3, 3), '-sinAngle', '0.0', 'cosAngle', '0.0')};');
iPrint('${Access(0, 0)} = t1;');
iPrint('${Access(1, 0)} = t2;');
iPrint('${Access(2, 0)} = t3;');
iPrint('${Access(3, 0)} = t4;');
iPrint('${Access(0, 2)} = t5;');
iPrint('${Access(1, 2)} = t6;');
iPrint('${Access(2, 2)} = t7;');
iPrint('${Access(3, 2)} = t8;');
iPrint('return this;');
iPop();
iPrint('}');
iPrint('\/\/\/ Rotate this matrix [angle] radians around Z');
iPrint('${matType} rotateZ(double angle) {');
iPush();
iPrint('double cosAngle = cos(angle);');
iPrint('double sinAngle = sin(angle);');
iPrint('var t1 = ${generateInlineDotArgs(Access(0, 0), Access(0, 1), Access(0, 2), Access(0, 3), 'cosAngle', 'sinAngle', '0.0', '0.0')};');
iPrint('var t2 = ${generateInlineDotArgs(Access(1, 0), Access(1, 1), Access(1, 2), Access(1, 3), 'cosAngle', 'sinAngle', '0.0', '0.0')};');
iPrint('var t3 = ${generateInlineDotArgs(Access(2, 0), Access(2, 1), Access(2, 2), Access(2, 3), 'cosAngle', 'sinAngle', '0.0', '0.0')};');
iPrint('var t4 = ${generateInlineDotArgs(Access(3, 0), Access(3, 1), Access(3, 2), Access(3, 3), 'cosAngle', 'sinAngle', '0.0', '0.0')};');
iPrint('var t5 = ${generateInlineDotArgs(Access(0, 0), Access(0, 1), Access(0, 2), Access(0, 3), '-sinAngle', 'cosAngle', '0.0', '0.0')};');
iPrint('var t6 = ${generateInlineDotArgs(Access(1, 0), Access(1, 1), Access(1, 2), Access(1, 3), '-sinAngle', 'cosAngle', '0.0', '0.0')};');
iPrint('var t7 = ${generateInlineDotArgs(Access(2, 0), Access(2, 1), Access(2, 2), Access(2, 3), '-sinAngle', 'cosAngle', '0.0', '0.0')};');
iPrint('var t8 = ${generateInlineDotArgs(Access(3, 0), Access(3, 1), Access(3, 2), Access(3, 3), '-sinAngle', 'cosAngle', '0.0', '0.0')};');
iPrint('${Access(0, 0)} = t1;');
iPrint('${Access(1, 0)} = t2;');
iPrint('${Access(2, 0)} = t3;');
iPrint('${Access(3, 0)} = t4;');
iPrint('${Access(0, 1)} = t5;');
iPrint('${Access(1, 1)} = t6;');
iPrint('${Access(2, 1)} = t7;');
iPrint('${Access(3, 1)} = t8;');
iPrint('return this;');
iPop();
iPrint('}');
}
void generateInlineScale() {
if (rows != 4 || cols != 4) {
return;
}
iPrint('\/\/\/ Scale this matrix by a [vec3], [vec4], or x,y,z');
iPrint('${matType} scale(dynamic x, [num y = null, num z = null]) {');
iPush();
iPrint('double sx;');
iPrint('double sy;');
iPrint('double sz;');
iPrint('double sw = x is vec4 ? x.w : 1.0;');
iPrint('if (x is vec3 || x is vec4) {');
iPush();
iPrint('sx = x.x;');
iPrint('sy = x.y;');
iPrint('sz = x.z;');
iPop();
iPrint('} else {');
iPush();
iPrint('sx = x;');
iPrint('sy = y == null ? x : y.toDouble();');
iPrint('sz = z == null ? x : z.toDouble();');
iPop();
iPrint('}');
for (int i = 0; i < 4; i++) {
String scalar;
if (i == 0) {
scalar = 'sx';
} else if (i == 1) {
scalar = 'sy';
} else if (i == 2) {
scalar = 'sz';
} else if (i == 3) {
scalar = 'sw';
}
for (int j = 0; j < 4; j++) {
iPrint('${Access(i, j)} *= $scalar;');
}
}
iPrint('return this;');
iPop();
iPrint('}');
}
void generateNegate() {
iPrint('\/\/\/ Returns new matrix -this');
iPrint('${matType} operator-() {');
iPush();
iPrint('${matType} r = new ${matType}.zero();');
for (int i = 0; i < cols; i++) {
iPrint('r[$i] = -this[$i];');
}
iPrint('return r;');
iPop();
iPrint('}');
}
void generateTranspose() {
iPrint('\/\/\/ Returns the tranpose of this.');
iPrint('${matTypeTransposed} transposed() {');
iPush();
iPrint('${matTypeTransposed} r = new ${matTypeTransposed}.zero();');
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
iPrint('r.${Access(j, i)} = ${Access(i, j)};');
}
}
iPrint('return r;');
iPop();
iPrint('}');
iPrint('${matTypeTransposed} transpose() {');
iPush();
iPrint('double temp;');
for (int n = 0; n < rows; n++) {
for (int m = n+1; m < rows; m++) {
iPrint('temp = ${Access(n,m)};');
iPrint('${Access(n,m)} = ${Access(m,n)};');
iPrint('${Access(m,n)} = temp;');
}
}
iPrint('return this;');
iPop();
iPrint('}');
}
void generateAbsolute() {
iPrint('\/\/\/ Returns the component wise absolute value of this.');
iPrint('${matType} absolute() {');
iPush();
iPrint('${matType} r = new ${matType}.zero();');
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
iPrint('r.${Access(j, i)} = ${Access(j, i)}.abs();');
}
}
iPrint('return r;');
iPop();
iPrint('}');
}
void generateDeterminant() {
if (rows == 2 && cols == 2) {
iPrint('\/\/\/ Returns the determinant of this matrix.');
iPrint('double determinant() {');
iPush();
iPrint('return (col0.x * col1.y) - (col0.y*col1.x);');
iPop();
iPrint('}');
}
if (cols == 3 && rows == 3) {
iPrint('\/\/\/ Returns the determinant of this matrix.');
iPrint('double determinant() {');
iPush();
iPrint('double x = col0.x*((col1.y*col2.z)-(col1.z*col2.y));');
iPrint('double y = col0.y*((col1.x*col2.z)-(col1.z*col2.x));');
iPrint('double z = col0.z*((col1.x*col2.y)-(col1.y*col2.x));');
iPrint('return x - y + z;');
iPop();
iPrint('}');
}
if (rows == 4 && cols == 4) {
iPrint('\/\/\/ Returns the determinant of this matrix.');
iPrint('double determinant() {');
iPush();
iPrint('double det2_01_01 = col0.x * col1.y - col0.y * col1.x;');
iPrint('double det2_01_02 = col0.x * col1.z - col0.z * col1.x;');
iPrint('double det2_01_03 = col0.x * col1.w - col0.w * col1.x;');
iPrint('double det2_01_12 = col0.y * col1.z - col0.z * col1.y;');
iPrint('double det2_01_13 = col0.y * col1.w - col0.w * col1.y;');
iPrint('double det2_01_23 = col0.z * col1.w - col0.w * col1.z;');
iPrint('double det3_201_012 = col2.x * det2_01_12 - col2.y * det2_01_02 + col2.z * det2_01_01;');
iPrint('double det3_201_013 = col2.x * det2_01_13 - col2.y * det2_01_03 + col2.w * det2_01_01;');
iPrint('double det3_201_023 = col2.x * det2_01_23 - col2.z * det2_01_03 + col2.w * det2_01_02;');
iPrint('double det3_201_123 = col2.y * det2_01_23 - col2.z * det2_01_13 + col2.w * det2_01_12;');
iPrint('return ( - det3_201_123 * col3.x + det3_201_023 * col3.y - det3_201_013 * col3.z + det3_201_012 * col3.w);');
iPop();
iPrint('}');
}
}
void generateTrace() {
if (rows == cols) {
iPrint('\/\/\/ Returns the trace of the matrix. The trace of a matrix is the sum of the diagonal entries');
iPrint('double trace() {');
iPush();
iPrint('double t = 0.0;');
for (int i = 0; i < cols; i++) {
iPrint('t += ${Access(i, i)};');
}
iPrint('return t;');
iPop();
iPrint('}');
}
}
void generateInfinityNorm() {
iPrint('\/\/\/ Returns infinity norm of the matrix. Used for numerical analysis.');
iPrint('double infinityNorm() {');
iPush();
iPrint('double norm = 0.0;');
for (int i = 0; i < cols; i++) {
iPrint('{');
iPush();
iPrint('double row_norm = 0.0;');
for (int j = 0; j < rows; j++) {
iPrint('row_norm += this[$i][$j].abs();');
}
iPrint('norm = row_norm > norm ? row_norm : norm;');
iPop();
iPrint('}');
}
iPrint('return norm;');
iPop();
iPrint('}');
}
void generateError() {
iPrint('\/\/\/ Returns relative error between [this] and [correct]');
iPrint('double relativeError($matType correct) {');
iPush();
iPrint('$matType diff = correct - this;');
iPrint('double correct_norm = correct.infinityNorm();');
iPrint('double diff_norm = diff.infinityNorm();');
iPrint('return diff_norm/correct_norm;');
iPop();
iPrint('}');
iPrint('\/\/\/ Returns absolute error between [this] and [correct]');
iPrint('double absoluteError($matType correct) {');
iPush();
iPrint('double this_norm = infinityNorm();');
iPrint('double correct_norm = correct.infinityNorm();');
iPrint('double diff_norm = (this_norm - correct_norm).abs();');
iPrint('return diff_norm;');
iPop();
iPrint('}');
}
void generateTranslate() {
if (rows == 4 && cols == 4) {
iPrint('\/\/\/ Returns the translation vector from this homogeneous transformation matrix.');
iPrint('vec3 getTranslation() {');
iPush();
iPrint('return new vec3.raw(col3.x, col3.y, col3.z);');
iPop();
iPrint('}');
iPrint('\/\/\/ Sets the translation vector in this homogeneous transformation matrix.');
iPrint('void setTranslation(vec3 T) {');
iPush();
iPrint('col3.xyz = T;');
iPop();
iPrint('}');
}
}
void generateRotation() {
if (rows == 4 && cols == 4) {
iPrint('\/\/\/ Returns the rotation matrix from this homogeneous transformation matrix.');
iPrint('mat3 getRotation() {');
iPush();
iPrint('mat3 r = new mat3.zero();');
iPrint('r.col0 = new vec3.raw(this.col0.x,this.col0.y,this.col0.z);');
iPrint('r.col1 = new vec3.raw(this.col1.x,this.col1.y,this.col1.z);');
iPrint('r.col2 = new vec3.raw(this.col2.x,this.col2.y,this.col2.z);');
iPrint('return r;');
iPop();
iPrint('}');
iPrint('\/\/\/ Sets the rotation matrix in this homogeneous transformation matrix.');
iPrint('void setRotation(mat3 rotation) {');
iPush();
iPrint('this.col0.xyz = rotation.col0;');
iPrint('this.col1.xyz = rotation.col1;');
iPrint('this.col2.xyz = rotation.col2;');
iPop();
iPrint('}');
iPrint('\/\/\/ Transposes just the upper 3x3 rotation matrix.');
iPrint('mat4 transposeRotation() {');
iPush();
iPrint('double temp;');
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == j) {
continue;
}
iPrint('temp = this.${Access(j, i)};');
iPrint('this.${Access(j, i)} = this.${Access(i, j)};');
iPrint('this.${Access(i, j)} = temp;');
}
}
iPrint('return this;');
iPop();
iPrint('}');
}
}
void generateInvert() {
if (rows != cols) {
// Only square matrices have inverses
return;
}
if (rows == 2) {
iPrint('\/\/\/ Invert the matrix. Returns the determinant.');
iPrint('double invert() {');
iPush();
iPrint('double det = determinant();');
iPrint('if (det == 0.0) {');
iPush();
iPrint('return 0.0;');
iPop();
iPrint('}');
iPrint('double invDet = 1.0 / det;');
iPrint('double temp = col0.x;');
iPrint('col0.x = col1.y * invDet;');
iPrint('col0.y = - col0.y * invDet;');
iPrint('col1.x = - col1.x * invDet;');
iPrint('col1.y = temp * invDet;');
iPrint('return det;');
iPop();
iPrint('}');
} else if (rows == 3) {
iPrint('/\/\/\ Invert the matrix. Returns the determinant.');
iPrint('double invert() {');
iPush();
iPrint('double det = determinant();');
iPrint('if (det == 0.0) {');
iPush();
iPrint('return 0.0;');
iPop();
iPrint('}');
iPrint('double invDet = 1.0 / det;');
iPrint('vec3 i = new vec3.zero();');
iPrint('vec3 j = new vec3.zero();');
iPrint('vec3 k = new vec3.zero();');
iPrint('i.x = invDet * (col1.y * col2.z - col1.z * col2.y);');
iPrint('i.y = invDet * (col0.z * col2.y - col0.y * col2.z);');
iPrint('i.z = invDet * (col0.y * col1.z - col0.z * col1.y);');
iPrint('j.x = invDet * (col1.z * col2.x - col1.x * col2.z);');
iPrint('j.y = invDet * (col0.x * col2.z - col0.z * col2.x);');
iPrint('j.z = invDet * (col0.z * col1.x - col0.x * col1.z);');
iPrint('k.x = invDet * (col1.x * col2.y - col1.y * col2.x);');
iPrint('k.y = invDet * (col0.y * col2.x - col0.x * col2.y);');
iPrint('k.z = invDet * (col0.x * col1.y - col0.y * col1.x);');
iPrint('col0 = i;');
iPrint('col1 = j;');
iPrint('col2 = k;');
iPrint('return det;');
iPop();
iPrint('}');
} else if (rows == 4) {
iPrint('double invert() {');
iPush();
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
iPrint('double a$i$j = ${Access(j, i)};');
}
}
iPrint('var b00 = a00 * a11 - a01 * a10;');
iPrint('var b01 = a00 * a12 - a02 * a10;');
iPrint('var b02 = a00 * a13 - a03 * a10;');
iPrint('var b03 = a01 * a12 - a02 * a11;');
iPrint('var b04 = a01 * a13 - a03 * a11;');
iPrint('var b05 = a02 * a13 - a03 * a12;');
iPrint('var b06 = a20 * a31 - a21 * a30;');
iPrint('var b07 = a20 * a32 - a22 * a30;');
iPrint('var b08 = a20 * a33 - a23 * a30;');
iPrint('var b09 = a21 * a32 - a22 * a31;');
iPrint('var b10 = a21 * a33 - a23 * a31;');
iPrint('var b11 = a22 * a33 - a23 * a32;');
iPrint('var det = (b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06);');
iPrint('if (det == 0.0) { return det; }');
iPrint('var invDet = 1.0 / det;');
iPrint('${Access(0, 0)} = (a11 * b11 - a12 * b10 + a13 * b09) * invDet;');
iPrint('${Access(1, 0)} = (-a01 * b11 + a02 * b10 - a03 * b09) * invDet;');
iPrint('${Access(2, 0)} = (a31 * b05 - a32 * b04 + a33 * b03) * invDet;');
iPrint('${Access(3, 0)} = (-a21 * b05 + a22 * b04 - a23 * b03) * invDet;');
iPrint('${Access(0, 1)} = (-a10 * b11 + a12 * b08 - a13 * b07) * invDet;');
iPrint('${Access(1, 1)} = (a00 * b11 - a02 * b08 + a03 * b07) * invDet;');
iPrint('${Access(2, 1)} = (-a30 * b05 + a32 * b02 - a33 * b01) * invDet;');
iPrint('${Access(3, 1)} = (a20 * b05 - a22 * b02 + a23 * b01) * invDet;');
iPrint('${Access(0, 2)} = (a10 * b10 - a11 * b08 + a13 * b06) * invDet;');
iPrint('${Access(1, 2)} = (-a00 * b10 + a01 * b08 - a03 * b06) * invDet;');
iPrint('${Access(2, 2)} = (a30 * b04 - a31 * b02 + a33 * b00) * invDet;');
iPrint('${Access(3, 2)} = (-a20 * b04 + a21 * b02 - a23 * b00) * invDet;');
iPrint('${Access(0, 3)} = (-a10 * b09 + a11 * b07 - a12 * b06) * invDet;');
iPrint('${Access(1, 3)} = (a00 * b09 - a01 * b07 + a02 * b06) * invDet;');
iPrint('${Access(2, 3)} = (-a30 * b03 + a31 * b01 - a32 * b00) * invDet;');
iPrint('${Access(3, 3)} = (a20 * b03 - a21 * b01 + a22 * b00) * invDet;');
iPrint('return det;');
iPop();
iPrint('}');
iPrint('double invertRotation() {');
iPush();
iPrint('double det = determinant();');
iPrint('if (det == 0.0) {');
iPush();
iPrint('return 0.0;');
iPop();
iPrint('}');
iPrint('double invDet = 1.0 / det;');
iPrint('vec4 i = new vec4.zero();');
iPrint('vec4 j = new vec4.zero();');
iPrint('vec4 k = new vec4.zero();');
iPrint('i.x = invDet * (col1.y * col2.z - col1.z * col2.y);');
iPrint('i.y = invDet * (col0.z * col2.y - col0.y * col2.z);');
iPrint('i.z = invDet * (col0.y * col1.z - col0.z * col1.y);');
iPrint('j.x = invDet * (col1.z * col2.x - col1.x * col2.z);');
iPrint('j.y = invDet * (col0.x * col2.z - col0.z * col2.x);');
iPrint('j.z = invDet * (col0.z * col1.x - col0.x * col1.z);');
iPrint('k.x = invDet * (col1.x * col2.y - col1.y * col2.x);');
iPrint('k.y = invDet * (col0.y * col2.x - col0.x * col2.y);');
iPrint('k.z = invDet * (col0.x * col1.y - col0.y * col1.x);');
iPrint('col0 = i;');
iPrint('col1 = j;');
iPrint('col2 = k;');
iPrint('return det;');
iPop();
iPrint('}');
}
}
void generateSetRotation() {
if (rows == 2 && cols == 2) {
iPrint('\/\/\/ Turns the matrix into a rotation of [radians]');
iPrint('void setRotation(num radians) {');
iPush();
iPrint('double radians_ = radians.toDouble();');
iPrint('double c = Math.cos(radians_);');
iPrint('double s = Math.sin(radians_);');
iPrint('col0.x = c;');
iPrint('col0.y = s;');
iPrint('col1.x = -s;');
iPrint('col1.y = c;');
iPop();
iPrint('}');
}
if (rows == 3 && cols == 3) {
iPrint('\/\/\/ Turns the matrix into a rotation of [radians] around X');
iPrint('void setRotationX(num radians) {');
iPush();
iPrint('double radians_ = radians.toDouble();');
iPrint('double c = Math.cos(radians_);');
iPrint('double s = Math.sin(radians_);');
iPrint('col0.x = 1.0;');
iPrint('col0.y = 0.0;');
iPrint('col0.z = 0.0;');
iPrint('col1.x = 0.0;');
iPrint('col1.y = c;');
iPrint('col1.z = s;');
iPrint('col2.x = 0.0;');
iPrint('col2.y = -s;');
iPrint('col2.z = c;');
iPop();
iPrint('}');
iPrint('\/\/\/ Turns the matrix into a rotation of [radians] around Y');
iPrint('void setRotationY(num radians) {');
iPush();
iPrint('double radians_ = radians.toDouble();');
iPrint('double c = Math.cos(radians_);');
iPrint('double s = Math.sin(radians_);');
iPrint('col0.x = c;');
iPrint('col0.y = 0.0;');
iPrint('col0.z = s;');
iPrint('col1.x = 0.0;');
iPrint('col1.y = 1.0;');
iPrint('col1.z = 0.0;');
iPrint('col2.x = -s;');
iPrint('col2.y = 0.0;');
iPrint('col2.z = c;');
iPop();
iPrint('}');
iPrint('\/\/\/ Turns the matrix into a rotation of [radians] around Z');
iPrint('void setRotationZ(num radians) {');
iPush();
iPrint('double radians_ = radians.toDouble();');
iPrint('double c = Math.cos(radians_);');
iPrint('double s = Math.sin(radians_);');
iPrint('col0.x = c;');
iPrint('col0.y = s;');
iPrint('col0.z = 0.0;');
iPrint('col1.x = -s;');
iPrint('col1.y = c;');
iPrint('col1.z = 0.0;');
iPrint('col2.x = 0.0;');
iPrint('col2.y = 0.0;');
iPrint('col2.z = 1.0;');
iPop();
iPrint('}');
}
if (rows == 4 && cols == 4) {
iPrint('\/\/\/ Sets the upper 3x3 to a rotation of [radians] around X');
iPrint('void setRotationX(num radians) {');
iPush();
iPrint('double radians_ = radians.toDouble();');
iPrint('double c = Math.cos(radians_);');
iPrint('double s = Math.sin(radians_);');
iPrint('col0.x = 1.0;');
iPrint('col0.y = 0.0;');
iPrint('col0.z = 0.0;');
iPrint('col1.x = 0.0;');
iPrint('col1.y = c;');
iPrint('col1.z = s;');
iPrint('col2.x = 0.0;');
iPrint('col2.y = -s;');
iPrint('col2.z = c;');
iPrint('col0.w = 0.0;');
iPrint('col1.w = 0.0;');
iPrint('col2.w = 0.0;');
iPop();
iPrint('}');
iPrint('\/\/\/ Sets the upper 3x3 to a rotation of [radians] around Y');
iPrint('void setRotationY(num radians) {');
iPush();
iPrint('double radians_ = radians.toDouble();');
iPrint('double c = Math.cos(radians_);');
iPrint('double s = Math.sin(radians_);');
iPrint('col0.x = c;');
iPrint('col0.y = 0.0;');
iPrint('col0.z = s;');
iPrint('col1.x = 0.0;');
iPrint('col1.y = 1.0;');
iPrint('col1.z = 0.0;');
iPrint('col2.x = -s;');
iPrint('col2.y = 0.0;');
iPrint('col2.z = c;');
iPrint('col0.w = 0.0;');
iPrint('col1.w = 0.0;');
iPrint('col2.w = 0.0;');
iPop();
iPrint('}');
iPrint('\/\/\/ Sets the upper 3x3 to a rotation of [radians] around Z');
iPrint('void setRotationZ(num radians) {');
iPush();
iPrint('double radians_ = radians.toDouble();');
iPrint('double c = Math.cos(radians_);');
iPrint('double s = Math.sin(radians_);');
iPrint('col0.x = c;');
iPrint('col0.y = s;');
iPrint('col0.z = 0.0;');
iPrint('col1.x = -s;');
iPrint('col1.y = c;');
iPrint('col1.z = 0.0;');
iPrint('col2.x = 0.0;');
iPrint('col2.y = 0.0;');
iPrint('col2.z = 1.0;');
iPrint('col0.w = 0.0;');
iPrint('col1.w = 0.0;');
iPrint('col2.w = 0.0;');
iPop();
iPrint('}');
}
}
String generateInlineDet2(String a, String b, String c, String d) {
return '($a * $d - $b * $c)';
}
String generateInlineDet3(String a1, String a2, String a3, String b1, String b2, String b3, String c1, String c2, String c3) {
return '($a1 * ${generateInlineDet2(b2, b3, c2, c3)} - $b1 * ${generateInlineDet2(a2, a3, c2, c3)} + $c1 * ${generateInlineDet2(a2, a3, b2, b3)})';
}
void generateAdjugate() {
if (rows != cols) {
return;
}
iPrint('\/\/\/ Converts into Adjugate matrix and scales by [scale]');
if (rows == 2) {
iPrint('$matType scaleAdjoint(num scale) {');
iPush();
iPrint('double scale_ = scale.toDouble();');
iPrint('double temp = ${Access(0, 0)};');
iPrint('${Access(0, 0)} = ${Access(1,1)} * scale_;');
iPrint('${Access(0, 1)} = - ${Access(0,1)} * scale_;');
iPrint('${Access(1, 0)} = - ${Access(1, 0)} * scale_;');
iPrint('${Access(1, 1)} = temp * scale_;');
iPrint('return this;');
iPop();
iPrint('}');
}
if (cols == 3) {
iPrint('$matType scaleAdjoint(num scale) {');
iPush();
iPrint('double scale_ = scale.toDouble();');
iPrint('double m00 = ${Access(0, 0)};');
iPrint('double m01 = ${Access(0, 1)};');
iPrint('double m02 = ${Access(0, 2)};');
iPrint('double m10 = ${Access(1, 0)};');
iPrint('double m11 = ${Access(1, 1)};');
iPrint('double m12 = ${Access(1, 2)};');
iPrint('double m20 = ${Access(2, 0)};');
iPrint('double m21 = ${Access(2, 1)};');
iPrint('double m22 = ${Access(2, 2)};');
iPrint('${Access(0, 0)} = (m11 * m22 - m12 * m21) * scale_;');
iPrint('${Access(1, 0)} = (m12 * m20 - m10 * m22) * scale_;');
iPrint('${Access(2, 0)} = (m10 * m21 - m11 * m20) * scale_;');
iPrint('${Access(0, 1)} = (m02 * m21 - m01 * m22) * scale_;');
iPrint('${Access(1, 1)} = (m00 * m22 - m02 * m20) * scale_;');
iPrint('${Access(2, 1)} = (m01 * m20 - m00 * m21) * scale_;');
iPrint('${Access(0, 2)} = (m01 * m12 - m02 * m11) * scale_;');
iPrint('${Access(1, 2)} = (m02 * m10 - m00 * m12) * scale_;');
iPrint('${Access(2, 2)} = (m00 * m11 - m01 * m10) * scale_;');
iPrint('return this;');
iPop();
iPrint('}');
}
if (cols == 4) {
iPrint('$matType scaleAdjoint(num scale) {');
iPush();
iPrint('double scale_ = scale.toDouble();');
iPrint('\/\/ Adapted from code by Richard Carling.');
iPrint('double a1 = ${Access(0,0)};');
iPrint('double b1 = ${Access(0,1)};');
iPrint('double c1 = ${Access(0,2)};');
iPrint('double d1 = ${Access(0,3)};');
iPrint('double a2 = ${Access(1,0)};');
iPrint('double b2 = ${Access(1,1)};');
iPrint('double c2 = ${Access(1,2)};');
iPrint('double d2 = ${Access(1,3)};');
iPrint('double a3 = ${Access(2,0)};');
iPrint('double b3 = ${Access(2,1)};');
iPrint('double c3 = ${Access(2,2)};');
iPrint('double d3 = ${Access(2,3)};');
iPrint('double a4 = ${Access(3,0)};');
iPrint('double b4 = ${Access(3,1)};');
iPrint('double c4 = ${Access(3,2)};');
iPrint('double d4 = ${Access(3,3)};');
iPrint('${Access(0,0)} = ${generateInlineDet3( 'b2', 'b3', 'b4', 'c2', 'c3', 'c4', 'd2', 'd3', 'd4')} * scale_;');
iPrint('${Access(1,0)} = - ${generateInlineDet3( 'a2', 'a3', 'a4', 'c2', 'c3', 'c4', 'd2', 'd3', 'd4')} * scale_;');
iPrint('${Access(2,0)} = ${generateInlineDet3( 'a2', 'a3', 'a4', 'b2', 'b3', 'b4', 'd2', 'd3', 'd4')} * scale_;');
iPrint('${Access(3,0)} = - ${generateInlineDet3( 'a2', 'a3', 'a4', 'b2', 'b3', 'b4', 'c2', 'c3', 'c4')} * scale_;');
iPrint('${Access(0,1)} = - ${generateInlineDet3( 'b1', 'b3', 'b4', 'c1', 'c3', 'c4', 'd1', 'd3', 'd4')} * scale_;');
iPrint('${Access(1,1)} = ${generateInlineDet3( 'a1', 'a3', 'a4', 'c1', 'c3', 'c4', 'd1', 'd3', 'd4')} * scale_;');
iPrint('${Access(2,1)} = - ${generateInlineDet3( 'a1', 'a3', 'a4', 'b1', 'b3', 'b4', 'd1', 'd3', 'd4')} * scale_;');
iPrint('${Access(3,1)} = ${generateInlineDet3( 'a1', 'a3', 'a4', 'b1', 'b3', 'b4', 'c1', 'c3', 'c4')} * scale_;');
iPrint('${Access(0,2)} = ${generateInlineDet3( 'b1', 'b2', 'b4', 'c1', 'c2', 'c4', 'd1', 'd2', 'd4')} * scale_;');
iPrint('${Access(1,2)} = - ${generateInlineDet3( 'a1', 'a2', 'a4', 'c1', 'c2', 'c4', 'd1', 'd2', 'd4')} * scale_;');
iPrint('${Access(2,2)} = ${generateInlineDet3( 'a1', 'a2', 'a4', 'b1', 'b2', 'b4', 'd1', 'd2', 'd4')} * scale_;');
iPrint('${Access(3,2)} = - ${generateInlineDet3( 'a1', 'a2', 'a4', 'b1', 'b2', 'b4', 'c1', 'c2', 'c4')} * scale_;');
iPrint('${Access(0,3)} = - ${generateInlineDet3( 'b1', 'b2', 'b3', 'c1', 'c2', 'c3', 'd1', 'd2', 'd3')} * scale_;');
iPrint('${Access(1,3)} = ${generateInlineDet3( 'a1', 'a2', 'a3', 'c1', 'c2', 'c3', 'd1', 'd2', 'd3')} * scale_;');
iPrint('${Access(2,3)} = - ${generateInlineDet3( 'a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'd1', 'd2', 'd3')} * scale_;');
iPrint('${Access(3,3)} = ${generateInlineDet3( 'a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3')} * scale_;');
iPrint('return this;');
iPop();
iPrint('}');
}
}
void generateCopy() {
iPrint('$matType clone() {');
iPush();
iPrint('return new $matType.copy(this);');
iPop();
iPrint('}');
iPrint('$matType copyInto($matType arg) {');
iPush();
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
iPrint('arg.${Access(j,i)} = ${Access(j,i)};');
}
}
iPrint('return arg;');
iPop();
iPrint('}');
iPrint('$matType copyFrom($matType arg) {');
iPush();
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
iPrint('${Access(j,i)} = arg.${Access(j,i)};');
}
}
iPrint('return this;');
iPop();
iPrint('}');
}
void generateSelfOp(String name, String op) {
iPrint('$matType $name($matType o) {');
iPush();
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
iPrint('${Access(j,i)} = ${Access(j,i)} $op o.${Access(j,i)};');
}
}
iPrint('return this;');
iPop();
iPrint('}');
}
void generateSelfScalarOp(String name, String op) {
iPrint('$matType $name(num o) {');
iPush();
iPrint('double o_ = o.toDouble();');
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
iPrint('${Access(j,i)} = ${Access(j,i)} $op o_;');
}
}
iPrint('return this;');
iPop();
iPrint('}');
}
void generateSelfNegate() {
iPrint('$matType negate_() {');
iPush();
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
iPrint('${Access(j,i)} = -${Access(j,i)};');
}
}
iPrint('return this;');
iPop();
iPrint('}');
}
generateSelfMultiplyMatrix() {
if (rows != cols) {
// Only generate this for square matrices
return;
}
iPrint('$matType multiply($matType arg) {');
iPush();
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
iPrint('final double m$i$j = ${Access(i, j)};');
}
}
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
iPrint('final double n$i$j = arg.${Access(i, j)};');
}
}
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
String r = '';
for (int k = 0; k < rows; k++) {
if (k != 0) {
r = '$r +';
}
r = '$r (m$i$k * n$k$j)';
}
iPrint('${Access(i, j)} = $r;');
}
}
iPrint('return this;');
iPop();
iPrint('}');
}
generateSelfTransposeMultiplyMatrix() {
if (rows != cols) {
// Only generate this for square matrices
return;
}
iPrint('$matType transposeMultiply($matType arg) {');
iPush();
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
iPrint('double m$i$j = ${Access(j, i)};');
}
}
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
String r = '';
for (int k = 0; k < rows; k++) {
if (k != 0) {
r = '$r +';
}
r = '$r (m$i$k * arg.${Access(k, j)})';
}
iPrint('${Access(i, j)} = $r;');
}
}
iPrint('return this;');
iPop();
iPrint('}');
}
generateSelfMultiplyTransposeMatrix() {
if (rows != cols) {
// Only generate this for square matrices
return;
}
iPrint('$matType multiplyTranspose($matType arg) {');
iPush();
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
iPrint('double m$i$j = ${Access(i, j)};');
}
}
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
String r = '';
for (int k = 0; k < rows; k++) {
if (k != 0) {
r = '$r +';
}
r = '$r (m$i$k * arg.${Access(j, k)})';
}
iPrint('${Access(i, j)} = $r;');
}
}
iPrint('return this;');
iPop();
iPrint('}');
}
/*
String generateInlineDot(String rowPrefix, int row, String col, int len) {
String r = '';
for (int i = 0; i < len; i++) {
if (i != 0) {
r = '$r +';
}
r = '$r (${rowPrefix}.${Access(row, i)} * ${col}.${AccessV(i)})';
}
return r;
}
*/
void generateTransforms() {
if (rows != cols) {
return;
}
if (rows == 2) {
iPrint('vec2 transform(vec2 arg) {');
iPush();
iPrint('double x_ = ${generateInlineDot('this', 0, 'arg', 2)};');
iPrint('double y_ = ${generateInlineDot('this', 1, 'arg', 2)};');
iPrint('arg.x = x_;');
iPrint('arg.y = y_;');
iPrint('return arg;');
iPop();
iPrint('}');
iPrint('vec2 transformed(vec2 arg, [vec2 out=null]) {');
iPush();
iPrint('if (out == null) {');
iPush();
iPrint('out = new vec2.copy(arg);');
iPop();
iPrint('} else {');
iPush();
iPrint('out.copyFrom(arg);');
iPop();
iPrint('}');
iPrint('return transform(out);');
iPop();
iPrint('}');
}
if (rows == 3) {
iPrint('vec3 transform(vec3 arg) {');
iPush();
iPrint('double x_ = ${generateInlineDot('this', 0, 'arg', 3)};');
iPrint('double y_ = ${generateInlineDot('this', 1, 'arg', 3)};');
iPrint('double z_ = ${generateInlineDot('this', 2, 'arg', 3)};');
iPrint('arg.x = x_;');
iPrint('arg.y = y_;');
iPrint('arg.z = z_;');
iPrint('return arg;');
iPop();
iPrint('}');
iPrint('vec3 transformed(vec3 arg, [vec3 out=null]) {');
iPush();
iPrint('if (out == null) {');
iPush();
iPrint('out = new vec3.copy(arg);');
iPop();
iPrint('} else {');
iPush();
iPrint('out.copyFrom(arg);');
iPop();
iPrint('}');
iPrint('return transform(out);');
iPop();
iPrint('}');
}
if (rows == 4) {
iPrint('vec3 rotate3(vec3 arg) {');
iPush();
iPrint('double x_ = ${generateInlineDot('this', 0, 'arg', 3)};');
iPrint('double y_ = ${generateInlineDot('this', 1, 'arg', 3)};');
iPrint('double z_ = ${generateInlineDot('this', 2, 'arg', 3)};');
iPrint('arg.x = x_;');
iPrint('arg.y = y_;');
iPrint('arg.z = z_;');
iPrint('return arg;');
iPop();
iPrint('}');
iPrint('vec3 rotated3(vec3 arg, [vec3 out=null]) {');
iPush();
iPrint('if (out == null) {');
iPush();
iPrint('out = new vec3.copy(arg);');
iPop();
iPrint('} else {');
iPush();
iPrint('out.copyFrom(arg);');
iPop();
iPrint('}');
iPrint('return rotate3(out);');
iPop();
iPrint('}');
iPrint('vec3 transform3(vec3 arg) {');
iPush();
iPrint('double x_ = ${generateInlineDot('this', 0, 'arg', 3)} + ${Access(0, 3)};');
iPrint('double y_ = ${generateInlineDot('this', 1, 'arg', 3)} + ${Access(1, 3)};');
iPrint('double z_ = ${generateInlineDot('this', 2, 'arg', 3)} + ${Access(2, 3)};');
iPrint('arg.x = x_;');
iPrint('arg.y = y_;');
iPrint('arg.z = z_;');
iPrint('return arg;');
iPop();
iPrint('}');
iPrint('vec3 transformed3(vec3 arg, [vec3 out=null]) {');
iPush();
iPrint('if (out == null) {');
iPush();
iPrint('out = new vec3.copy(arg);');
iPop();
iPrint('} else {');
iPush();
iPrint('out.copyFrom(arg);');
iPop();
iPrint('}');
iPrint('return transform3(out);');
iPop();
iPrint('}');
iPrint('vec4 transform(vec4 arg) {');
iPush();
iPrint('double x_ = ${generateInlineDot('this', 0, 'arg', 4)};');
iPrint('double y_ = ${generateInlineDot('this', 1, 'arg', 4)};');
iPrint('double z_ = ${generateInlineDot('this', 2, 'arg', 4)};');
iPrint('double w_ = ${generateInlineDot('this', 3, 'arg', 4)};');
iPrint('arg.x = x_;');
iPrint('arg.y = y_;');
iPrint('arg.z = z_;');
iPrint('arg.w = w_;');
iPrint('return arg;');
iPop();
iPrint('}');
iPrint('vec4 transformed(vec4 arg, [vec4 out=null]) {');
iPush();
iPrint('if (out == null) {');
iPush();
iPrint('out = new vec4.copy(arg);');
iPop();
iPrint('} else {');
iPush();
iPrint('out.copyFrom(arg);');
iPop();
iPrint('}');
iPrint('return transform(out);');
iPop();
iPrint('}');
}
}
void generateAbsoluteRotate() {
if (cols < 3 || rows < 3) {
return;
}
iPrint('\/\/\/ Rotates [arg] by the absolute rotation of [this]');
iPrint('\/\/\/ Returns [arg].');
iPrint('\/\/\/ Primarily used by AABB transformation code.');
iPrint('vec3 absoluteRotate(vec3 arg) {');
iPush();
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
iPrint('double m$i$j = ${Access(i, j)}.abs();');
}
}
iPrint('double x = arg.x;');
iPrint('double y = arg.y;');
iPrint('double z = arg.z;');
iPrint('arg.x = ${generateInlineDotArgs('x', 'y', 'z', '0.0', 'm00', 'm01', 'm02', '0.0')};');
iPrint('arg.y = ${generateInlineDotArgs('x', 'y', 'z', '0.0', 'm10', 'm11', 'm12', '0.0')};');
iPrint('arg.z = ${generateInlineDotArgs('x', 'y', 'z', '0.0', 'm20', 'm21', 'm22', '0.0')};');
iPrint('return arg;');
iPop();
iPrint('}');
}
void generateBuffer() {
iPrint('\/\/\/ Copies [this] into [array] starting at [offset].');
iPrint('void copyIntoArray(List<num> array, [int offset=0]) {');
iPush();
iPrint('int i = offset;');
for (int j = 0; j < cols; j++) {
for (int i = 0; i < rows; i++) {
iPrint('array[i] = ${Access(i,j)};');
iPrint('i++;');
}
}
iPop();
iPrint('}');
iPrint('\/\/\/ Copies elements from [array] into [this] starting at [offset].');
iPrint('void copyFromArray(List<num> array, [int offset=0]) {');
iPush();
iPrint('int i = offset;');
for (int j = 0; j < cols; j++) {
for (int i = 0; i < rows; i++) {
iPrint('${Access(i,j)} = array[i].toDouble();');
iPrint('i++;');
}
}
iPop();
iPrint('}');
}
void generateRightUpForward() {
if (rows != cols) {
return;
}
if (rows == 3 || rows == 4) {
iPrint('vec3 get right {');
iPush();
iPrint('vec3 f = new vec3.zero();');
iPrint('f.x = ${Access(0, 0)};');
iPrint('f.y = ${Access(1, 0)};');
iPrint('f.z = ${Access(2, 0)};');
iPrint('return f;');
iPop();
iPrint('}');
iPrint('vec3 get up {');
iPush();
iPrint('vec3 f = new vec3.zero();');
iPrint('f.x = ${Access(0, 1)};');
iPrint('f.y = ${Access(1, 1)};');
iPrint('f.z = ${Access(2, 1)};');
iPrint('return f;');
iPop();
iPrint('}');
iPrint('vec3 get forward {');
iPush();
iPrint('vec3 f = new vec3.zero();');
iPrint('f.x = ${Access(0, 2)};');
iPrint('f.y = ${Access(1, 2)};');
iPrint('f.z = ${Access(2, 2)};');
iPrint('return f;');
iPop();
iPrint('}');
}
}
void generate() {
writeLicense();
generatePrologue();
generateConstructors();
generateToString();
generateRowColProperties();
generateIndexOperator();
generateAssignIndexOperator();
generateRowGetterSetters();
generateRowHelpers();
generateColumnHelpers();
generateMult();
generateOp('+');
generateOp('-');
generateInlineTranslate();
generateInlineRotate();
generateInlineScale();
generateNegate();
generateConstructionSetters();
generateTranspose();
generateAbsolute();
generateDeterminant();
generateTrace();
generateInfinityNorm();
generateError();
generateTranslate();
generateRotation();
generateInvert();
generateSetRotation();
generateAdjugate();
generateAbsoluteRotate();
generateCopy();
generateSelfOp('add', '+');
generateSelfOp('sub', '-');
generateSelfNegate();
generateSelfMultiplyMatrix();
generateSelfTransposeMultiplyMatrix();
generateSelfMultiplyTransposeMatrix();
generateTransforms();
generateBuffer();
generateRightUpForward();
generateEpilogue();
}
}