blob: dd5cfa1dd7b642997f7e8b7da82a109ef3f942a2 [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.
*/
class VectorGenerator extends BaseGenerator {
List<String> vectorComponents;
int get vectorDimension() {
return vectorComponents != null ? vectorComponents.length : 0;
}
String vectorType;
int vectorLen;
List<String> allTypes;
List<int> allTypesLength;
List<List<String>> componentAliases;
String generatedName;
String floatArrayType;
VectorGenerator() : super() {
}
void generatePrologue() {
iPrint('class $generatedName {');
iPush();
vectorComponents.forEach((comp) {
iPrint('$vectorType $comp;');
});
}
void generateAliases(bool getters) {
for (List<String> ca in componentAliases) {
for (int i = 0; i < ca.length; i++) {
if (getters) {
iPrint('$vectorType get ${ca[i]}() => ${vectorComponents[i]};');
} else {
iPrint('set ${ca[i]}($vectorType arg) => ${vectorComponents[i]} = arg;');
}
}
}
}
void generateDefaultConstructor() {
iPrint('\/\/\/ Constructs a new [$generatedName]. Follows GLSL constructor syntax so many combinations are possible');
iPrint('$generatedName([${joinStrings(vectorComponents, 'Dynamic ', '_')}]) {');
iPush();
iPrint('${joinStrings(vectorComponents, joiner: ' = ')} = 0.0;');
if (generatedName == 'vec3') {
iPrint('if (${vectorComponents[0]}_ is vec2 && ${vectorComponents[1]}_ is num) {');
iPush();
iPrint('this.xy = ${vectorComponents[0]}_.xy;');
iPrint('this.z = ${vectorComponents[1]}_;');
iPop();
iPrint('}');
iPrint('if (${vectorComponents[0]}_ is num && ${vectorComponents[1]}_ is vec2) {');
iPush();
iPrint('this.x = ${vectorComponents[0]}_;');
iPrint('this.yz = ${vectorComponents[1]}_.xy;');
iPop();
iPrint('}');
iPrint('if (${vectorComponents[0]}_ is vec2 && ${vectorComponents[1]}_ == null) {');
iPush();
iPrint('this.xy = ${vectorComponents[0]}_.xy;');
iPrint('this.z = 0;');
iPop();
iPrint('}');
} else if (generatedName == 'vec4') {
iPrint('if (${vectorComponents[0]}_ is vec3 && ${vectorComponents[1]}_ is num) {');
iPush();
iPrint('this.xyz = ${vectorComponents[0]}_.xyz;');
iPrint('this.w = ${vectorComponents[1]}_;');
iPop();
iPrint('}');
iPrint('if (${vectorComponents[0]}_ is num && ${vectorComponents[1]}_ is vec3) {');
iPush();
iPrint('this.x = ${vectorComponents[0]}_;');
iPrint('this.yzw = ${vectorComponents[1]}_.xyz;');
iPop();
iPrint('}');
iPrint('if (${vectorComponents[0]}_ is vec3 && ${vectorComponents[1]}_ == null) {');
iPush();
iPrint('this.xyz = ${vectorComponents[0]}_.xyz;');
iPrint('this.z = 0;');
iPop();
iPrint('}');
iPrint('if (${vectorComponents[0]}_ is vec2 && ${vectorComponents[1]}_ is vec2) {');
iPush();
iPrint('this.xy = ${vectorComponents[0]}_.xy;');
iPrint('this.zw = ${vectorComponents[1]}_.xy;');
iPop();
iPrint('}');
}
iPrint('if (${vectorComponents[0]}_ is $generatedName) {');
iPush();
iPrint('${joinStrings(vectorComponents, joiner: '')} = ${vectorComponents[0]}_.${joinStrings(vectorComponents, joiner: '')};');
iPrint('return;');
iPop();
iPrint('}');
iPrint('if (${joinStrings(vectorComponents, '', '_ is num', ' && ')}) {');
iPush();
for (String e in vectorComponents) {
iPrint('$e = ${e}_;');
}
iPrint('return;');
iPop();
iPrint('}');
iPrint('if (${vectorComponents[0]}_ is num) {');
iPush();
iPrint('${joinStrings(vectorComponents, joiner: ' = ')} = ${vectorComponents[0]}_;');
iPrint('return;');
iPop();
iPrint('}');
iPop();
iPrint('}');
iPrint('\/\/\/ Constructs a new [$generatedName] filled with 0.');
iPrint('$generatedName.zero() {');
iPush();
for (String e in vectorComponents) {
iPrint('$e = 0.0;');
}
iPop();
iPrint('}');
iPrint('\/\/\/ Constructs a new [$generatedName] that is a copy of [other].');
iPrint('$generatedName.copy($generatedName other) {');
iPush();
for (String e in vectorComponents) {
iPrint('$e = other.$e;');
}
iPop();
iPrint('}');
iPrint('\/\/\/ Constructs a new [$generatedName] that is initialized with passed in values.');
iPrint('$generatedName.raw(${joinStrings(vectorComponents, 'num ', '_')}) {');
iPush();
for (String e in vectorComponents) {
iPrint('$e = ${e}_;');
}
iPop();
iPrint('}');
iPrint('\/\/\/ Constructs a new [$generatedName] that is initialized with values from [array] starting at [offset].');
iPrint('$generatedName.array(${floatArrayType} array, [int offset=0]) {');
iPush();
iPrint('int i = offset;');
for (String e in vectorComponents) {
iPrint('$e = array[i];');
iPrint('i++;');
}
iPop();
iPrint('}');
}
void generateToString() {
iPrint('''/// Returns a printable string''');
String code = 'String toString() => \'';
bool first = true;
vectorComponents.forEach((comp) {
var extra = first ? '\$$comp' : ',\$$comp';
code = '$code$extra';
first = false;
});
code = '$code\';';
iPrint(code);
}
void generateSplat() {
iPrint('\/\/\/ Splats a scalar into all lanes of the vector.');
iPrint('$generatedName splat(num arg) {');
iPush();
for (String c in vectorComponents) {
iPrint('$c = arg;');
}
iPrint('return this;');
iPop();
iPrint('}');
}
void generateOperator(String op) {
iPrint('\/\/\/ Returns a new $generatedName from this $op [other]');
String code = '$generatedName operator$op($generatedName other) => new $generatedName(';
bool first = true;
vectorComponents.forEach((comp) {
var extra = first ? '$comp $op other.$comp' :', $comp $op other.$comp';
code = '$code$extra';
first = false;
});
code = '$code);';
iPrint(code);
}
void generateScaleOperator(String op) {
iPrint('\/\/\/ Returns a new $generatedName ${op == '*' ? 'scaled' : 'divided'} by [other]');
iPrint('$generatedName operator$op(Dynamic other) {');
iPush();
iPrint('if (other is num) {');
{
iPush();
String code = 'return new $generatedName(';
bool first = true;
vectorComponents.forEach((comp) {
var extra =first ? '$comp $op other' :', $comp $op other';
code = '$code$extra';
first = false;
});
code = '$code);';
iPrint(code);
iPop();
iPrint('}');
}
iPrint('if (other is $generatedName) {');
{
iPush();
bool first = true;
String code = 'return new $generatedName(';
vectorComponents.forEach((comp) {
var extra = first ? '$comp $op other.$comp' :', $comp $op other.$comp';
code = '$code$extra';
first = false;
});
code = '$code);';
iPrint(code);
iPop();
iPrint('}');
}
iPop();
iPrint('}');
}
void generateNegateOperator() {
iPrint('\/\/\/ Returns a new $generatedName from -this');
String op = '-';
String code = '$generatedName operator negate() => new $generatedName(';
bool first = true;
vectorComponents.forEach((comp) {
var extra = first ? '$op$comp' :', $op$comp';
code = '$code$extra';
first = false;
});
code = '$code);';
iPrint(code);
}
void generateIndexOperator() {
iPrint('\/\/\/ Returns a component from ${generatedName}. This is indexed as an array with [i]');
iPrint('$vectorType operator[](int i) {');
iPush();
iPrint('assert(i >= 0 && i < $vectorDimension);');
iPrint('switch (i) {');
iPush();
int i = 0;
vectorComponents.forEach((comp) {
iPrint('case $i: return $comp;');
i++;
});
iPop();
iPrint('};');
iPrint('return 0.0;');
iPop();
iPrint('}');
}
void generateAssignIndexOperator() {
iPrint('\/\/\/ Assigns a component in $generatedName the value in [v]. This is indexed as an array with [i]');
iPrint('void operator[]=(int i, $vectorType v) {');
iPush();
iPrint('assert(i >= 0 && i < $vectorDimension);');
iPrint('switch (i) {');
iPush();
int i = 0;
vectorComponents.forEach((comp) {
iPrint('case $i: $comp = v; break;');
i++;
});
iPop();
iPrint('};');
iPop();
iPrint('}');
}
/*
void generateSetter(List<int> seq, String type) {
List<String> comps = PrintablePermutation(seq, vectorComponents);
var propertyName = "";
comps.forEach((e) => propertyName += e);
iPrint('set $propertyName($type arg) \{');
iPush();
int i = 0;
comps.forEach((e) {
iPrint('$e = arg.${vectorComponents[i]};');
i++;
});
iPop();
iPrint('\}');
}
void generateSetters() {
List<int> dimensions = new List<int>(vectorDimension);
for (int i = 0; i < vectorDimension; i++) {
dimensions[i] = i;
}
var components = dimensions.getRange(0, vectorDimension);
var permutation = new List.from(components);
generateSetter(permutation);
var N = permutation.length;
while (ListNextPermutation(permutation, 0, vectorDimension)) {
generateSetter(permutation);
}
}
void generateSetters() {
List<int> dimensions = new List<int>(vectorDimension);
for (int i = 0; i < vectorDimension; i++) {
dimensions[i] = i;
}
for (int d = 1; d < vectorDimension; d++) {
var components = dimensions.getRange(0, d+1);
var permutation = new List.from(components);
generateSetter(permutation, allTypes[d-1]);
var N = permutation.length;
while (ListNextPermutation(permutation, 0, d+1)) {
generateSetter(permutation, allTypes[d-1]);
}
}
}
*/
void generateSettersForType(String type, int len, String pre, int i, int j) {
if (i == len) {
iPrint('set $pre($type arg) {');
iPush();
int z = 0;
for (String c in pre.splitChars()) {
iPrint('$c = arg.${vectorComponents[z]};');
z++;
}
iPop();
iPrint('}');
/*
String code = 'set $pre($type arg) => new $type(';
bool first = true;
pre.splitChars().forEach((c) {
code += first ? '$c' : ', $c';
first = false;
});
code += ');';
iPrint(code);
return;
*/
return;
}
if (j == len) {
return;
}
for (int a = 0; a < vectorDimension; a++) {
if (pre.charCodes().indexOf(vectorComponents[a].charCodeAt(0)) != -1) {
continue;
}
String property_name = '$pre${vectorComponents[a]}';
generateSettersForType(type, len, property_name, i+1, 0);
}
}
void generateSetters() {
for (int i = 0; i < allTypes.length; i++) {
var type = allTypes[i];
var len = allTypesLength[i];
generateSettersForType(type, len, '', 0, 0);
if (type == generatedName) {
break;
}
}
}
void generateGettersForType(String type, int len, String pre, int i, int j) {
if (i == len) {
String code = '$type get $pre() => new $type(';
bool first = true;
pre.splitChars().forEach((c) {
var extra = first ? '$c' : ', $c';
code = '$code$extra';
first = false;
});
code = '$code);';
iPrint(code);
return;
}
if (j == len) {
return;
}
for (int a = 0; a < vectorDimension; a++) {
String property_name = '$pre${vectorComponents[a]}';
generateGettersForType(type, len, property_name, i+1, 0);
}
}
void generateGetters() {
for (int i = 0; i < allTypes.length; i++) {
var type = allTypes[i];
var len = allTypesLength[i];
generateGettersForType(type, len, '', 0, 0);
}
}
void generateLength() {
iPrint('\/\/\/ Returns length of this');
iPrint('num get length() {');
iPush();
iPrint('num sum = 0.0;');
vectorComponents.forEach((comp) {
iPrint('sum += ($comp * $comp);');
});
iPrint('return Math.sqrt(sum);');
iPop();
iPrint('}');
}
void generateLength2() {
iPrint('\/\/\/ Returns squared length of this');
iPrint('num get length2() {');
iPush();
iPrint('num sum = 0.0;');
vectorComponents.forEach((comp) {
iPrint('sum += ($comp * $comp);');
});
iPrint('return sum;');
iPop();
iPrint('}');
}
void generateNormalize() {
iPrint('\/\/\/ Normalizes this');
iPrint('void normalize() {');
iPush();
iPrint('num l = length;');
iPrint('if (l == 0.0) {');
iPush();
iPrint('return;');
iPop();
iPrint('}');
vectorComponents.forEach((comp) {
iPrint('$comp /= l;');
});
iPop();
iPrint('}');
}
void generateDot() {
iPrint('\/\/\/ Returns the dot product of [this] and [other]');
iPrint('num dot($generatedName other) {');
iPush();
iPrint('num sum = 0.0;');
vectorComponents.forEach((comp) {
iPrint('sum += ($comp * other.$comp);');
});
iPrint('return sum;');
iPop();
iPrint('}');
}
void generateCross() {
iPrint('\/\/\/ Returns the cross product of [this] and [other]');
if (generatedName == 'vec3') {
iPrint('$generatedName cross($generatedName other) {');
iPush();
iPrint('return new ${generatedName}.raw(y * other.z - z * other.y, z * other.x - x * other.z, x * other.y - y * other.x);');
} else if (generatedName == 'vec2') {
iPrint('num cross($generatedName other) {');
iPush();
iPrint('return x * other.y - y * other.x;');
} else {
assert(false);
}
iPop();
iPrint('}');
}
void generateError() {
iPrint('\/\/\/ Returns the relative error between [this] and [correct]');
iPrint('num relativeError($generatedName correct) {');
iPush();
iPrint('num this_norm = length;');
iPrint('num correct_norm = correct.length;');
iPrint('num diff_norm = (this_norm - correct_norm).abs();');
iPrint('return diff_norm/correct_norm;');
iPop();
iPrint('}');
iPrint('\/\/\/ Returns the absolute error between [this] and [correct]');
iPrint('num absoluteError($generatedName correct) {');
iPush();
iPrint('num this_norm = length;');
iPrint('num correct_norm = correct.length;');
iPrint('num diff_norm = (this_norm - correct_norm).abs();');
iPrint('return diff_norm;');
iPop();
iPrint('}');
}
void generateSelfScalarOp(String methodName, String op) {
iPrint('$generatedName self$methodName(num arg) {');
iPush();
for (String c in vectorComponents) {
iPrint('$c = $c $op arg;');
}
iPrint('return this;');
iPop();
iPrint('}');
}
void generateSelfOp(String methodName, String op) {
iPrint('$generatedName self$methodName($generatedName arg) {');
iPush();
for (String c in vectorComponents) {
iPrint('$c = $c $op arg.$c;');
}
iPrint('return this;');
iPop();
iPrint('}');
}
void generateSelfNegate() {
iPrint('$generatedName selfNegate() {');
iPush();
for (String c in vectorComponents) {
iPrint('$c = -$c;');
}
iPrint('return this;');
iPop();
iPrint('}');
}
void generateSelfAbsolute() {
iPrint('$generatedName selfAbsolute() {');
iPush();
for (String c in vectorComponents) {
iPrint('$c = $c.abs();');
}
iPrint('return this;');
iPop();
iPrint('}');
}
void generateEpilogue() {
iPop();
iPrint('}');
}
void generateCopy() {
iPrint('$generatedName copy() {');
iPush();
iPrint('$generatedName c = new ${generatedName}.copy(this);');
iPrint('return c;');
iPop();
iPrint('}');
iPrint('$generatedName copyInto($generatedName arg) {');
iPush();
for (String c in vectorComponents) {
iPrint('arg.$c = $c;');
}
iPrint('return arg;');
iPop();
iPrint('}');
iPrint('$generatedName copyFrom($generatedName arg) {');
iPush();
for (String c in vectorComponents) {
iPrint('$c = arg.$c;');
}
iPrint('return this;');
iPop();
iPrint('}');
}
void generateSet() {
iPrint('$generatedName set($generatedName arg) {');
iPush();
for (String c in vectorComponents) {
iPrint('$c = arg.$c;');
}
iPrint('return this;');
iPop();
iPrint('}');
}
void generateBuffer() {
iPrint('\/\/\/ Copies [this] into [array] starting at [offset].');
iPrint('void copyIntoArray(${floatArrayType} array, [int offset=0]) {');
iPush();
iPrint('int i = offset;');
for (String c in vectorComponents) {
iPrint('array[i] = $c;');
iPrint('i++;');
}
iPop();
iPrint('}');
iPrint('\/\/\/ Returns a copy of [this] as a [${floatArrayType}].');
iPrint('${floatArrayType} copyAsArray() {');
iPush();
iPrint('${floatArrayType} array = new ${floatArrayType}($vectorLen);');
iPrint('int i = 0;');
for (String c in vectorComponents) {
iPrint('array[i] = $c;');
iPrint('i++;');
}
iPrint('return array;');
iPop();
iPrint('}');
iPrint('\/\/\/ Copies elements from [array] into [this] starting at [offset].');
iPrint('void copyFromArray(${floatArrayType} array, [int offset=0]) {');
iPush();
iPrint('int i = offset;');
for (String c in vectorComponents) {
iPrint('$c = array[i];');
iPrint('i++;');
}
iPop();
iPrint('}');
}
void generateIsInfinite() {
iPrint('\/\/\/ Returns true if any component is infinite.');
iPrint('bool isInfinite() {');
iPush();
iPrint('bool is_infinite = false;');
for (String c in vectorComponents) {
iPrint('is_infinite = is_infinite || $c.isInfinite();');
}
iPrint('return is_infinite;');
iPop();
iPrint('}');
}
void generateIsNaN() {
iPrint('\/\/\/ Returns true if any component is NaN.');
iPrint('bool isNaN() {');
iPush();
iPrint('bool is_nan = false;');
for (String c in vectorComponents) {
iPrint('is_nan = is_nan || $c.isNaN();');
}
iPrint('return is_nan;');
iPop();
iPrint('}');
}
void generate() {
writeLicense();
generatePrologue();
generateDefaultConstructor();
generateSplat();
generateToString();
generateNegateOperator();
generateOperator('-');
generateOperator('+');
generateScaleOperator('/');
generateScaleOperator('*');
generateIndexOperator();
generateAssignIndexOperator();
generateLength();
generateLength2();
generateNormalize();
generateDot();
if (generatedName == 'vec3' || generatedName == 'vec2') {
generateCross();
}
generateError();
generateSetters();
generateIsInfinite();
generateIsNaN();
generateAliases(false);
{
var backup = vectorComponents;
for (List<String> ca in componentAliases) {
vectorComponents = ca;
generateSetters();
}
vectorComponents = backup;
}
generateGetters();
generateAliases(true);
{
var backup = vectorComponents;
for (List<String> ca in componentAliases) {
vectorComponents = ca;
generateGetters();
}
vectorComponents = backup;
}
generateSelfOp('Add', '+');
generateSelfOp('Sub', '-');
generateSelfOp('Mul', '*');
generateSelfOp('Div', '/');
generateSelfScalarOp('Scale', '*');
generateSelfNegate();
generateSelfAbsolute();
generateCopy();
generateSet();
generateBuffer();
generateEpilogue();
}
}