/* Copyright (c) 2025 Alex Diener 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. Alex Diener alex@ludobloom.com */ #include "calculator/calculator_shared.h" #include "gamemath/Matrix3x3f.h" #include "gamemath/Matrix3x3x.h" #include "gamemath/Matrix4x4f.h" #include "gamemath/Matrix4x4x.h" #include "gamemath/Vector2f.h" #include "gamemath/Vector2x.h" #include "gamemath/Vector3f.h" #include "gamemath/Vector3x.h" #include "gamemath/Vector4f.h" #include "gamemath/Vector4x.h" #include "gamemath/Quaternionf.h" #include "gamemath/Quaternionx.h" #include "gamemath/VectorConversions.h" #include #include #include static unsigned int namedMatrix3x3Count; static struct { Matrix3x3x matrix; const char * name; } * namedMatrices3x3; static unsigned int namedMatrix4x4Count; static struct { Matrix4x4x matrix; const char * name; } * namedMatrices4x4; static unsigned int namedQuaternionCount; static struct { Quaternionx quaternion; const char * name; } * namedQuaternions; static unsigned int namedVector2Count; static struct { Vector2x vector; const char * name; } * namedVector2s; static unsigned int namedVector3Count; static struct { Vector3x vector; const char * name; } * namedVector3s; static unsigned int namedVector4Count; static struct { Vector4x vector; const char * name; } * namedVector4s; static fixed16_16 readFixed16_16Argv(int * ioArgIndex, int argc, const char ** argv) { int argIndex = *ioArgIndex + 1; if (argIndex >= argc) { fprintf(stderr, "Reached end of argv when expecting more numbers\n"); exit(EXIT_FAILURE); } unsigned int hexValue; if (sscanf(argv[argIndex], "-0x%X", &hexValue) == 1) { *ioArgIndex += 1; return -((fixed16_16) hexValue); } if (sscanf(argv[argIndex], "0x%X", &hexValue) == 1) { *ioArgIndex += 1; return hexValue; } float floatValue; if (sscanf(argv[argIndex], "%f", &floatValue) == 1) { *ioArgIndex += 1; return ftox(floatValue); } fprintf(stderr, "Couldn't parse \"%s\" as a number\n", argv[argIndex]); exit(EXIT_FAILURE); } static fixed16_16 readFixed16_16FromFile(FILE * file) { unsigned int hexValue; if (fscanf(file, "-0x%X", &hexValue) == 1 || fscanf(file, " -0x%X", &hexValue) == 1) { return -((fixed16_16) hexValue); } if (fscanf(file, "0x%X", &hexValue) == 1 || fscanf(file, " 0x%X", &hexValue) == 1) { return hexValue; } float floatValue; if (fscanf(file, "%f", &floatValue) == 1 || fscanf(file, " %f", &floatValue) == 1) { return ftox(floatValue); } fprintf(stderr, "Couldn't parse next number from input file\n"); exit(EXIT_FAILURE); } static fixed16_16 readFixed16_16AngleArgv(int * ioArgIndex, int argc, const char ** argv) { int argIndex = *ioArgIndex + 1; if (argIndex >= argc) { fprintf(stderr, "Reached end of argv when expecting more numbers\n"); exit(EXIT_FAILURE); } float floatValue; if (sscanf(argv[argIndex], "deg:%f", &floatValue) == 1) { *ioArgIndex += 1; floatValue *= M_PI / 180.0f; return ftox(floatValue); } if (sscanf(argv[argIndex], "pi:%f", &floatValue) == 1) { *ioArgIndex += 1; floatValue *= M_PI; return ftox(floatValue); } return readFixed16_16Argv(ioArgIndex, argc, argv); } static Matrix3x3x readMatrix3x3xArgv(int * ioArgIndex, int argc, const char ** argv) { Matrix3x3x matrix; int argIndex = *ioArgIndex + 1; if (argIndex >= argc) { fprintf(stderr, "Reached end of argv when expecting a matrix\n"); exit(EXIT_FAILURE); } if (argv[argIndex][0] == '@') { for (unsigned int nameIndex = 0; nameIndex < namedMatrix3x3Count; nameIndex++) { if (!strcmp(namedMatrices3x3[nameIndex].name, argv[argIndex] + 1)) { *ioArgIndex += 1; return namedMatrices3x3[nameIndex].matrix; } } fprintf(stderr, "Matrix with name %s was not found\n", argv[argIndex]); exit(EXIT_FAILURE); } if (!strcmp(argv[argIndex], "identity")) { matrix = MATRIX3x3x_IDENTITY; *ioArgIndex += 1; } else if (!strcmp(argv[argIndex], "-")) { for (int numberIndex = 0; numberIndex < 9; numberIndex++) { matrix.m[numberIndex] = readFixed16_16FromFile(stdin); } *ioArgIndex += 1; } else { for (int numberIndex = 0; numberIndex < 9; numberIndex++) { matrix.m[numberIndex] = readFixed16_16Argv(ioArgIndex, argc, argv); } } Matrix3x3x_transpose(&matrix); return matrix; } static void printMatrix3x3x(Matrix3x3x matrix) { Matrix3x3x_transpose(&matrix); calculatorPrintf("{%x, %x, %x,\n %x, %x, %x,\n %x, %x, %x}\n", matrix.m[0], matrix.m[1], matrix.m[2], matrix.m[3], matrix.m[4], matrix.m[5], matrix.m[6], matrix.m[7], matrix.m[8]); } static Matrix4x4x readMatrix4x4xArgv(int * ioArgIndex, int argc, const char ** argv) { Matrix4x4x matrix; int argIndex = *ioArgIndex + 1; if (argIndex >= argc) { fprintf(stderr, "Reached end of argv when expecting a matrix\n"); exit(EXIT_FAILURE); } if (argv[argIndex][0] == '@') { for (unsigned int nameIndex = 0; nameIndex < namedMatrix4x4Count; nameIndex++) { if (!strcmp(namedMatrices4x4[nameIndex].name, argv[argIndex] + 1)) { *ioArgIndex += 1; return namedMatrices4x4[nameIndex].matrix; } } fprintf(stderr, "Matrix with name %s was not found\n", argv[argIndex]); exit(EXIT_FAILURE); } if (!strcmp(argv[argIndex], "identity")) { matrix = MATRIX4x4x_IDENTITY; *ioArgIndex += 1; } else if (!strcmp(argv[argIndex], "-")) { for (int numberIndex = 0; numberIndex < 16; numberIndex++) { matrix.m[numberIndex] = readFixed16_16FromFile(stdin); } *ioArgIndex += 1; } else { for (int numberIndex = 0; numberIndex < 16; numberIndex++) { matrix.m[numberIndex] = readFixed16_16Argv(ioArgIndex, argc, argv); } } Matrix4x4x_transpose(&matrix); return matrix; } static void printMatrix4x4x(Matrix4x4x matrix) { Matrix4x4x_transpose(&matrix); calculatorPrintf("{%x, %x, %x, %x,\n %x, %x, %x, %x,\n %x, %x, %x, %x,\n %x, %x, %x, %x}\n", matrix.m[0], matrix.m[1], matrix.m[2], matrix.m[3], matrix.m[4], matrix.m[5], matrix.m[6], matrix.m[7], matrix.m[8], matrix.m[9], matrix.m[10], matrix.m[11], matrix.m[12], matrix.m[13], matrix.m[14], matrix.m[15]); } static Vector3x readVector3xArgv(int * ioArgIndex, int argc, const char ** argv) { Vector3x vector; int argIndex = *ioArgIndex + 1; if (argIndex >= argc) { fprintf(stderr, "Reached end of argv when expecting a vector3\n"); exit(EXIT_FAILURE); } if (argv[argIndex][0] == '@') { for (unsigned int nameIndex = 0; nameIndex < namedVector3Count; nameIndex++) { if (!strcmp(namedVector3s[nameIndex].name, argv[argIndex] + 1)) { *ioArgIndex += 1; return namedVector3s[nameIndex].vector; } } fprintf(stderr, "Vector3 with name %s was not found\n", argv[argIndex]); exit(EXIT_FAILURE); } if (!strcmp(argv[argIndex], "barycenter")) { *ioArgIndex += 1; Vector3x barycenter = readVector3xArgv(ioArgIndex, argc, argv); Vector3x vertex0 = readVector3xArgv(ioArgIndex, argc, argv); Vector3x vertex1 = readVector3xArgv(ioArgIndex, argc, argv); Vector3x vertex2 = readVector3xArgv(ioArgIndex, argc, argv); vector = Vector3x_fromBarycenter(barycenter, vertex0, vertex1, vertex2); } else if (!strcmp(argv[argIndex], "-")) { vector.x = readFixed16_16FromFile(stdin); vector.y = readFixed16_16FromFile(stdin); vector.z = readFixed16_16FromFile(stdin); *ioArgIndex += 1; } else { vector.x = readFixed16_16Argv(ioArgIndex, argc, argv); vector.y = readFixed16_16Argv(ioArgIndex, argc, argv); vector.z = readFixed16_16Argv(ioArgIndex, argc, argv); } return vector; } static Quaternionx readQuaternionxArgv(int * ioArgIndex, int argc, const char ** argv) { Quaternionx quaternion; int argIndex = *ioArgIndex + 1; if (argIndex >= argc) { fprintf(stderr, "Reached end of argv when expecting a quaternion\n"); exit(EXIT_FAILURE); } if (argv[argIndex][0] == '@') { for (unsigned int nameIndex = 0; nameIndex < namedQuaternionCount; nameIndex++) { if (!strcmp(namedQuaternions[nameIndex].name, argv[argIndex] + 1)) { *ioArgIndex += 1; return namedQuaternions[nameIndex].quaternion; } } fprintf(stderr, "Quaternion with name %s was not found\n", argv[argIndex]); exit(EXIT_FAILURE); } if (!strcmp(argv[argIndex], "identity")) { quaternion = QUATERNIONx_IDENTITY; *ioArgIndex += 1; } else if (!strcmp(argv[argIndex], "axis_angle")) { ++*ioArgIndex; Vector3x axis = readVector3xArgv(ioArgIndex, argc, argv); fixed16_16 angle = readFixed16_16AngleArgv(ioArgIndex, argc, argv); quaternion = Quaternionx_fromAxisAngle(axis, angle); } else if (!strcmp(argv[argIndex], "forward")) { ++*ioArgIndex; Vector3x forward = readVector3xArgv(ioArgIndex, argc, argv); quaternion = Quaternionx_forwardDirect(forward); } else if (!strcmp(argv[argIndex], "forward_level")) { ++*ioArgIndex; Vector3x forward = readVector3xArgv(ioArgIndex, argc, argv); quaternion = Quaternionx_forwardLevel(forward); } else if (!strcmp(argv[argIndex], "-")) { quaternion.x = readFixed16_16FromFile(stdin); quaternion.y = readFixed16_16FromFile(stdin); quaternion.z = readFixed16_16FromFile(stdin); quaternion.w = readFixed16_16FromFile(stdin); *ioArgIndex += 1; } else { quaternion.x = readFixed16_16Argv(ioArgIndex, argc, argv); quaternion.y = readFixed16_16Argv(ioArgIndex, argc, argv); quaternion.z = readFixed16_16Argv(ioArgIndex, argc, argv); quaternion.w = readFixed16_16Argv(ioArgIndex, argc, argv); } return quaternion; } static void printQuaternionx(Quaternionx quaternion) { calculatorPrintf("{%x, %x, %x, %x}\n", quaternion.x, quaternion.y, quaternion.z, quaternion.w); } static void printVector3x(Vector3x vector) { calculatorPrintf("{%x, %x, %x}\n", vector.x, vector.y, vector.z); } static Vector2x readVector2xArgv(int * ioArgIndex, int argc, const char ** argv) { Vector2x vector; int argIndex = *ioArgIndex + 1; if (argIndex >= argc) { fprintf(stderr, "Reached end of argv when expecting a vector2\n"); exit(EXIT_FAILURE); } if (argv[argIndex][0] == '@') { for (unsigned int nameIndex = 0; nameIndex < namedVector2Count; nameIndex++) { if (!strcmp(namedVector2s[nameIndex].name, argv[argIndex] + 1)) { *ioArgIndex += 1; return namedVector2s[nameIndex].vector; } } fprintf(stderr, "Vector2 with name %s was not found\n", argv[argIndex]); exit(EXIT_FAILURE); } if (!strcmp(argv[argIndex], "barycenter")) { *ioArgIndex += 1; Vector3x barycenter = readVector3xArgv(ioArgIndex, argc, argv); Vector2x vertex0 = readVector2xArgv(ioArgIndex, argc, argv); Vector2x vertex1 = readVector2xArgv(ioArgIndex, argc, argv); Vector2x vertex2 = readVector2xArgv(ioArgIndex, argc, argv); vector = Vector2x_fromBarycenter(barycenter, vertex0, vertex1, vertex2); } else if (!strcmp(argv[argIndex], "angle")) { *ioArgIndex += 1; fixed16_16 angle = readFixed16_16AngleArgv(ioArgIndex, argc, argv); vector = VECTOR2x(xcos(angle), xsin(angle)); } else if (!strcmp(argv[argIndex], "-")) { vector.x = readFixed16_16FromFile(stdin); vector.y = readFixed16_16FromFile(stdin); *ioArgIndex += 1; } else { vector.x = readFixed16_16Argv(ioArgIndex, argc, argv); vector.y = readFixed16_16Argv(ioArgIndex, argc, argv); } return vector; } static void printVector2x(Vector2x vector) { calculatorPrintf("{%x, %x}\n", vector.x, vector.y); } static Vector4x readVector4xArgv(int * ioArgIndex, int argc, const char ** argv) { Vector4x vector; int argIndex = *ioArgIndex + 1; if (argIndex >= argc) { fprintf(stderr, "Reached end of argv when expecting a vector4\n"); exit(EXIT_FAILURE); } if (argv[argIndex][0] == '@') { for (unsigned int nameIndex = 0; nameIndex < namedVector4Count; nameIndex++) { if (!strcmp(namedVector4s[nameIndex].name, argv[argIndex] + 1)) { *ioArgIndex += 1; return namedVector4s[nameIndex].vector; } } fprintf(stderr, "Vector4 with name %s was not found\n", argv[argIndex]); exit(EXIT_FAILURE); } if (!strcmp(argv[argIndex], "barycenter")) { *ioArgIndex += 1; Vector3x barycenter = readVector3xArgv(ioArgIndex, argc, argv); Vector4x vertex0 = readVector4xArgv(ioArgIndex, argc, argv); Vector4x vertex1 = readVector4xArgv(ioArgIndex, argc, argv); Vector4x vertex2 = readVector4xArgv(ioArgIndex, argc, argv); vector = Vector4x_fromBarycenter(barycenter, vertex0, vertex1, vertex2); } else if (!strcmp(argv[argIndex], "-")) { vector.x = readFixed16_16FromFile(stdin); vector.y = readFixed16_16FromFile(stdin); vector.z = readFixed16_16FromFile(stdin); vector.w = readFixed16_16FromFile(stdin); *ioArgIndex += 1; } else { vector.x = readFixed16_16Argv(ioArgIndex, argc, argv); vector.y = readFixed16_16Argv(ioArgIndex, argc, argv); vector.z = readFixed16_16Argv(ioArgIndex, argc, argv); vector.w = readFixed16_16Argv(ioArgIndex, argc, argv); } return vector; } static unsigned int swizzleVector(fixed16_16 * vector, unsigned int dimensions, fixed16_16 * outComponents, const char * letters) { unsigned int length = strlen(letters); if (length < 2 || length > 4) { fprintf(stderr, "Invalid swizzle: \"%s\"\n", letters); exit(EXIT_FAILURE); } for (unsigned int letterIndex = 0; letterIndex < length; letterIndex++) { switch (letters[letterIndex]) { case 'x': case 'X': outComponents[letterIndex] = vector[0]; break; case 'y': case 'Y': outComponents[letterIndex] = vector[1]; break; case 'z': case 'Z': if (dimensions < 3) { fprintf(stderr, "Swizzle \"%s\" attempts to reference z component in a vector with only %u dimensions\n", letters, dimensions); exit(EXIT_FAILURE); } outComponents[letterIndex] = vector[2]; break; case 'w': case 'W': if (dimensions < 4) { fprintf(stderr, "Swizzle \"%s\" attempts to reference w component in a vector with only %u dimensions\n", letters, dimensions); exit(EXIT_FAILURE); } outComponents[letterIndex] = vector[3]; break; default: fprintf(stderr, "Swizzle \"%s\" contains invalid characters (only x, y, z, and w are allowed)\n", letters); exit(EXIT_FAILURE); } } return length; } static void printVector4x(Vector4x vector) { calculatorPrintf("{%x, %x, %x, %x}\n", vector.x, vector.y, vector.z, vector.w); } static void printAxisAngle(Vector3x axis, fixed16_16 angle) { calculatorPrintf("{%x, %x, %x}, %x\n", axis.x, axis.y, axis.z, angle); } static void printFixed16_16(fixed16_16 value) { calculatorPrintf("%x\n", value); } static void matrix3x3Loop(int argc, const char ** argv, int * ioArgIndex, Matrix3x3x matrix); static void matrix4x4Loop(int argc, const char ** argv, int * ioArgIndex, Matrix4x4x matrix); static void quaternionLoop(int argc, const char ** argv, int * ioArgIndex, Quaternionx quaternion); static void vector2Loop(int argc, const char ** argv, int * ioArgIndex, Vector2x vector); static void vector3Loop(int argc, const char ** argv, int * ioArgIndex, Vector3x vector); static void vector4Loop(int argc, const char ** argv, int * ioArgIndex, Vector4x vector); static void restartLoopWithVectorSwizzle(int argc, const char ** argv, int * ioArgIndex, fixed16_16 * vectorComponents, unsigned int dimensionCount) { fixed16_16 components[4]; int argIndex = *ioArgIndex + 1; unsigned int componentCount = swizzleVector(vectorComponents, dimensionCount, components, argv[argIndex]); switch (componentCount) { case 2: { Vector2x vector2 = {components[0], components[1]}; vector2Loop(argc, argv, &argIndex, vector2); break; } case 3: { Vector3x vector3 = {components[0], components[1], components[2]}; vector3Loop(argc, argv, &argIndex, vector3); break; } case 4: { Vector4x vector4 = {components[0], components[1], components[2], components[3]}; vector4Loop(argc, argv, &argIndex, vector4); break; } } *ioArgIndex = argIndex; } static void matrix3x3Loop(int argc, const char ** argv, int * ioArgIndex, Matrix3x3x matrix) { for (int argIndex = *ioArgIndex + 1; argIndex < argc; argIndex++) { if (!strcmp(argv[argIndex], "multiply")) { Matrix3x3x matrix2 = readMatrix3x3xArgv(&argIndex, argc, argv); Matrix3x3x_multiply(&matrix, matrix2); } else if (!strcmp(argv[argIndex], "left_multiply")) { Matrix3x3x matrix2 = readMatrix3x3xArgv(&argIndex, argc, argv); Matrix3x3x_leftMultiply(&matrix, matrix2); } else if (!strcmp(argv[argIndex], "multiply_quaternion")) { Quaternionx quaternion = readQuaternionxArgv(&argIndex, argc, argv); Matrix3x3x matrix2 = Matrix3x3x_fromQuaternionx(quaternion); Matrix3x3x_multiply(&matrix, matrix2); } else if (!strcmp(argv[argIndex], "scale")) { Vector3x scale = readVector3xArgv(&argIndex, argc, argv); Matrix3x3x_scale(&matrix, scale); } else if (!strcmp(argv[argIndex], "rotate")) { Vector3x axis = readVector3xArgv(&argIndex, argc, argv); fixed16_16 angle = readFixed16_16AngleArgv(&argIndex, argc, argv); Matrix3x3x_rotate(&matrix, axis, angle); } else if (!strcmp(argv[argIndex], "shear_x")) { fixed16_16 y = readFixed16_16Argv(&argIndex, argc, argv); fixed16_16 z = readFixed16_16Argv(&argIndex, argc, argv); Matrix3x3x_shearX(&matrix, y, z); } else if (!strcmp(argv[argIndex], "shear_y")) { fixed16_16 x = readFixed16_16Argv(&argIndex, argc, argv); fixed16_16 z = readFixed16_16Argv(&argIndex, argc, argv); Matrix3x3x_shearY(&matrix, x, z); } else if (!strcmp(argv[argIndex], "shear_z")) { fixed16_16 x = readFixed16_16Argv(&argIndex, argc, argv); fixed16_16 y = readFixed16_16Argv(&argIndex, argc, argv); Matrix3x3x_shearZ(&matrix, x, y); } else if (!strcmp(argv[argIndex], "transpose")) { Matrix3x3x_transpose(&matrix); } else if (!strcmp(argv[argIndex], "interpolate")) { Matrix3x3x matrix2 = readMatrix3x3xArgv(&argIndex, argc, argv); fixed16_16 value = readFixed16_16Argv(&argIndex, argc, argv); Matrix3x3x_interpolate(&matrix, matrix2, value); } else if (!strcmp(argv[argIndex], "normalize")) { Matrix3x3x_normalize(&matrix); } else if (!strcmp(argv[argIndex], "inverse") || !strcmp(argv[argIndex], "invert")) { Matrix3x3x_invert(&matrix); } else if (!strcmp(argv[argIndex], "determinant")) { fixed16_16 determinant = Matrix3x3x_determinant(matrix); printFixed16_16(determinant); exit(EXIT_SUCCESS); } else if (!strcmp(argv[argIndex], "to_quaternion")) { Quaternionx quaternion = Quaternionx_fromMatrix3x3x(matrix); *ioArgIndex = argIndex; quaternionLoop(argc, argv, ioArgIndex, quaternion); return; } else if (!strcmp(argv[argIndex], "to_matrix4x4")) { Matrix4x4x matrix4x4 = Matrix4x4x_fromMatrix3x3x(matrix); *ioArgIndex = argIndex; matrix4x4Loop(argc, argv, ioArgIndex, matrix4x4); return; } else if (!strcmp(argv[argIndex], "print")) { printMatrix3x3x(matrix); } else if (!strcmp(argv[argIndex], "assign")) { if (argc <= argIndex + 1) { fprintf(stderr, "No name specified after assign command\n"); printUsage(); exit(EXIT_FAILURE); } for (unsigned int namedMatrixIndex = 0; namedMatrixIndex < namedMatrix3x3Count; namedMatrixIndex++) { if (!strcmp(namedMatrices3x3[namedMatrixIndex].name, argv[argIndex + 1])) { namedMatrices3x3[namedMatrixIndex].matrix = matrix; *ioArgIndex = argIndex + 1; return; } } namedMatrices3x3 = realloc(namedMatrices3x3, (namedMatrix3x3Count + 1) * sizeof(*namedMatrices3x3)); namedMatrices3x3[namedMatrix3x3Count].name = argv[++argIndex]; namedMatrices3x3[namedMatrix3x3Count++].matrix = matrix; *ioArgIndex = argIndex; return; } else { fprintf(stderr, "Unrecognized matrix operation: %s\n", argv[argIndex]); break; } } printMatrix3x3x(matrix); exit(EXIT_SUCCESS); } static void matrix4x4Loop(int argc, const char ** argv, int * ioArgIndex, Matrix4x4x matrix) { for (int argIndex = *ioArgIndex + 1; argIndex < argc; argIndex++) { if (!strcmp(argv[argIndex], "multiply")) { Matrix4x4x matrix2 = readMatrix4x4xArgv(&argIndex, argc, argv); Matrix4x4x_multiply(&matrix, matrix2); } else if (!strcmp(argv[argIndex], "left_multiply")) { Matrix4x4x matrix2 = readMatrix4x4xArgv(&argIndex, argc, argv); Matrix4x4x_leftMultiply(&matrix, matrix2); } else if (!strcmp(argv[argIndex], "multiply_quaternion")) { Quaternionx quaternion = readQuaternionxArgv(&argIndex, argc, argv); Matrix4x4x matrix2 = Matrix4x4x_fromQuaternionx(quaternion); Matrix4x4x_multiply(&matrix, matrix2); } else if (!strcmp(argv[argIndex], "translate")) { Vector3x translation = readVector3xArgv(&argIndex, argc, argv); Matrix4x4x_translate(&matrix, translation); } else if (!strcmp(argv[argIndex], "scale")) { Vector3x scale = readVector3xArgv(&argIndex, argc, argv); Matrix4x4x_scale(&matrix, scale); } else if (!strcmp(argv[argIndex], "rotate")) { Vector3x axis = readVector3xArgv(&argIndex, argc, argv); fixed16_16 angle = readFixed16_16AngleArgv(&argIndex, argc, argv); Matrix4x4x_rotate(&matrix, axis, angle); } else if (!strcmp(argv[argIndex], "shear_x")) { fixed16_16 y = readFixed16_16Argv(&argIndex, argc, argv); fixed16_16 z = readFixed16_16Argv(&argIndex, argc, argv); Matrix4x4x_shearX(&matrix, y, z); } else if (!strcmp(argv[argIndex], "shear_y")) { fixed16_16 x = readFixed16_16Argv(&argIndex, argc, argv); fixed16_16 z = readFixed16_16Argv(&argIndex, argc, argv); Matrix4x4x_shearY(&matrix, x, z); } else if (!strcmp(argv[argIndex], "shear_z")) { fixed16_16 x = readFixed16_16Argv(&argIndex, argc, argv); fixed16_16 y = readFixed16_16Argv(&argIndex, argc, argv); Matrix4x4x_shearZ(&matrix, x, y); } else if (!strcmp(argv[argIndex], "perspective")) { fixed16_16 fovYDegrees = readFixed16_16Argv(&argIndex, argc, argv); fixed16_16 aspect = readFixed16_16Argv(&argIndex, argc, argv); fixed16_16 zNear = readFixed16_16Argv(&argIndex, argc, argv); fixed16_16 zFar = readFixed16_16Argv(&argIndex, argc, argv); Matrix4x4x_applyPerspective(&matrix, fovYDegrees, aspect, zNear, zFar); } else if (!strcmp(argv[argIndex], "ortho")) { fixed16_16 left = readFixed16_16Argv(&argIndex, argc, argv); fixed16_16 right = readFixed16_16Argv(&argIndex, argc, argv); fixed16_16 bottom = readFixed16_16Argv(&argIndex, argc, argv); fixed16_16 top = readFixed16_16Argv(&argIndex, argc, argv); fixed16_16 zNear = readFixed16_16Argv(&argIndex, argc, argv); fixed16_16 zFar = readFixed16_16Argv(&argIndex, argc, argv); Matrix4x4x_applyOrtho(&matrix, left, right, bottom, top, zNear, zFar); } else if (!strcmp(argv[argIndex], "transpose")) { Matrix4x4x_transpose(&matrix); } else if (!strcmp(argv[argIndex], "interpolate")) { Matrix4x4x matrix2 = readMatrix4x4xArgv(&argIndex, argc, argv); fixed16_16 value = readFixed16_16Argv(&argIndex, argc, argv); Matrix4x4x_interpolate(&matrix, matrix2, value); } else if (!strcmp(argv[argIndex], "normalize")) { Matrix4x4x_normalize(&matrix); } else if (!strcmp(argv[argIndex], "inverse") || !strcmp(argv[argIndex], "invert")) { Matrix4x4x_invert(&matrix); } else if (!strcmp(argv[argIndex], "determinant")) { fixed16_16 determinant = Matrix4x4x_determinant(matrix); printFixed16_16(determinant); exit(EXIT_SUCCESS); } else if (!strcmp(argv[argIndex], "to_matrix3x3")) { Matrix3x3x matrix3x3 = Matrix3x3x_fromMatrix4x4x(matrix); *ioArgIndex = argIndex; matrix3x3Loop(argc, argv, ioArgIndex, matrix3x3); return; } else if (!strcmp(argv[argIndex], "print")) { printMatrix4x4x(matrix); } else if (!strcmp(argv[argIndex], "assign")) { if (argc <= argIndex + 1) { fprintf(stderr, "No name specified after assign command\n"); printUsage(); exit(EXIT_FAILURE); } for (unsigned int namedMatrixIndex = 0; namedMatrixIndex < namedMatrix4x4Count; namedMatrixIndex++) { if (!strcmp(namedMatrices4x4[namedMatrixIndex].name, argv[argIndex + 1])) { namedMatrices4x4[namedMatrixIndex].matrix = matrix; *ioArgIndex = argIndex + 1; return; } } namedMatrices4x4 = realloc(namedMatrices4x4, (namedMatrix4x4Count + 1) * sizeof(*namedMatrices4x4)); namedMatrices4x4[namedMatrix4x4Count].name = argv[++argIndex]; namedMatrices4x4[namedMatrix4x4Count++].matrix = matrix; *ioArgIndex = argIndex; return; } else { fprintf(stderr, "Unrecognized matrix operation: %s\n", argv[argIndex]); break; } } printMatrix4x4x(matrix); exit(EXIT_SUCCESS); } static void quaternionLoop(int argc, const char ** argv, int * ioArgIndex, Quaternionx quaternion) { for (int argIndex = *ioArgIndex + 1; argIndex < argc; argIndex++) { if (!strcmp(argv[argIndex], "multiply")) { Quaternionx quaternion2 = readQuaternionxArgv(&argIndex, argc, argv); Quaternionx_multiply(&quaternion, quaternion2); } else if (!strcmp(argv[argIndex], "left_multiply")) { Quaternionx quaternion2 = readQuaternionxArgv(&argIndex, argc, argv); Quaternionx_leftMultiply(&quaternion, quaternion2); } else if (!strcmp(argv[argIndex], "rotate")) { Vector3x axis = readVector3xArgv(&argIndex, argc, argv); fixed16_16 angle = readFixed16_16AngleArgv(&argIndex, argc, argv); Quaternionx_rotate(&quaternion, axis, angle); } else if (!strcmp(argv[argIndex], "magnitude")) { fixed16_16 magnitude = Quaternionx_magnitude(quaternion); printFixed16_16(magnitude); exit(EXIT_SUCCESS); } else if (!strcmp(argv[argIndex], "normalize")) { Quaternionx_normalize(&quaternion); } else if (!strcmp(argv[argIndex], "slerp")) { Quaternionx quaternion2 = readQuaternionxArgv(&argIndex, argc, argv); fixed16_16 value = readFixed16_16Argv(&argIndex, argc, argv); quaternion = Quaternionx_slerp(quaternion, quaternion2, value); } else if (!strcmp(argv[argIndex], "squad")) { Quaternionx quaternion2 = readQuaternionxArgv(&argIndex, argc, argv); Quaternionx quaternion3 = readQuaternionxArgv(&argIndex, argc, argv); Quaternionx quaternion4 = readQuaternionxArgv(&argIndex, argc, argv); fixed16_16 value = readFixed16_16Argv(&argIndex, argc, argv); quaternion = Quaternionx_squad(quaternion, quaternion2, quaternion3, quaternion4, value); } else if (!strcmp(argv[argIndex], "inverse") || !strcmp(argv[argIndex], "invert")) { Quaternionx_invert(&quaternion); } else if (!strcmp(argv[argIndex], "level_horizon")) { quaternion = Quaternionx_levelHorizon(quaternion); } else if (!strcmp(argv[argIndex], "to_matrix3x3")) { Matrix3x3x matrix = Matrix3x3x_fromQuaternionx(quaternion); *ioArgIndex = argIndex; matrix3x3Loop(argc, argv, ioArgIndex, matrix); return; } else if (!strcmp(argv[argIndex], "to_matrix4x4") || !strcmp(argv[argIndex], "to_matrix")) { Matrix4x4x matrix = Matrix4x4x_fromQuaternionx(quaternion); *ioArgIndex = argIndex; matrix4x4Loop(argc, argv, ioArgIndex, matrix); return; } else if (!strcmp(argv[argIndex], "to_axis_angle")) { Vector3x axis; fixed16_16 angle; Quaternionx_toAxisAngle(quaternion, &axis, &angle); printAxisAngle(axis, angle); exit(EXIT_SUCCESS); } else if (!strcmp(argv[argIndex], "get_angle")) { Quaternionx quaternion2 = readQuaternionxArgv(&argIndex, argc, argv); fixed16_16 angle = Quaternionx_getAngle(quaternion, quaternion2); printFixed16_16(angle); exit(EXIT_SUCCESS); } else if (!strcmp(argv[argIndex], "print")) { printQuaternionx(quaternion); } else if (!strcmp(argv[argIndex], "assign")) { if (argc <= argIndex + 1) { fprintf(stderr, "No name specified after assign command\n"); printUsage(); exit(EXIT_FAILURE); } for (unsigned int namedQuaternionIndex = 0; namedQuaternionIndex < namedQuaternionCount; namedQuaternionIndex++) { if (!strcmp(namedQuaternions[namedQuaternionIndex].name, argv[argIndex + 1])) { namedQuaternions[namedQuaternionIndex].quaternion = quaternion; *ioArgIndex = argIndex + 1; return; } } namedQuaternions = realloc(namedQuaternions, (namedQuaternionCount + 1) * sizeof(*namedQuaternions)); namedQuaternions[namedQuaternionCount].name = argv[++argIndex]; namedQuaternions[namedQuaternionCount++].quaternion = quaternion; *ioArgIndex = argIndex; return; } else { fprintf(stderr, "Unrecognized quaternion operation: %s\n", argv[argIndex]); break; } } printQuaternionx(quaternion); exit(EXIT_SUCCESS); } static void vector2Loop(int argc, const char ** argv, int * ioArgIndex, Vector2x vector) { for (int argIndex = *ioArgIndex + 1; argIndex < argc; argIndex++) { if (!strcmp(argv[argIndex], "normalize")) { Vector2x_normalize(&vector); } else if (!strcmp(argv[argIndex], "inverse") || !strcmp(argv[argIndex], "invert")) { Vector2x_invert(&vector); } else if (!strcmp(argv[argIndex], "add")) { Vector2x vector2 = readVector2xArgv(&argIndex, argc, argv); vector = Vector2x_add(vector, vector2); } else if (!strcmp(argv[argIndex], "subtract")) { Vector2x vector2 = readVector2xArgv(&argIndex, argc, argv); vector = Vector2x_subtract(vector, vector2); } else if (!strcmp(argv[argIndex], "scale_to")) { fixed16_16 value = readFixed16_16Argv(&argIndex, argc, argv); Vector2x_scaleTo(&vector, value); } else if (!strcmp(argv[argIndex], "multiply_scalar")) { fixed16_16 multiplier = readFixed16_16Argv(&argIndex, argc, argv); vector = Vector2x_multiplyScalar(vector, multiplier); } else if (!strcmp(argv[argIndex], "multiply_vector")) { Vector2x vector2 = readVector2xArgv(&argIndex, argc, argv); vector = Vector2x_multiplyComponents(vector, vector2); } else if (!strcmp(argv[argIndex], "divide_scalar")) { fixed16_16 divisor = readFixed16_16Argv(&argIndex, argc, argv); vector = Vector2x_divideScalar(vector, divisor); } else if (!strcmp(argv[argIndex], "divide_vector")) { Vector2x vector2 = readVector2xArgv(&argIndex, argc, argv); vector = Vector2x_add(vector, vector2); } else if (!strcmp(argv[argIndex], "interpolate")) { Vector2x vector2 = readVector2xArgv(&argIndex, argc, argv); fixed16_16 value = readFixed16_16Argv(&argIndex, argc, argv); vector = Vector2x_interpolate(vector, vector2, value); } else if (!strcmp(argv[argIndex], "round")) { vector = Vector2x_round(vector); } else if (!strcmp(argv[argIndex], "reflect")) { Vector2x vector2 = readVector2xArgv(&argIndex, argc, argv); vector = Vector2x_reflect(vector, vector2); } else if (!strcmp(argv[argIndex], "project")) { Vector2x vector2 = readVector2xArgv(&argIndex, argc, argv); vector = Vector2x_project(vector, vector2); } else if (!strcmp(argv[argIndex], "line_position")) { Vector2x origin = readVector2xArgv(&argIndex, argc, argv); Vector2x normal = readVector2xArgv(&argIndex, argc, argv); vector = Vector2x_positionOnLine(vector, origin, normal); } else if (!strcmp(argv[argIndex], "transpose")) { vector = Vector2x_transpose(vector); } else if (!strcmp(argv[argIndex], "dot")) { Vector2x vector2 = readVector2xArgv(&argIndex, argc, argv); fixed16_16 dot = Vector2x_dot(vector, vector2); printFixed16_16(dot); exit(EXIT_SUCCESS); } else if (!strcmp(argv[argIndex], "cross")) { Vector2x vector2 = readVector2xArgv(&argIndex, argc, argv); fixed16_16 cross = Vector2x_cross(vector, vector2); printFixed16_16(cross); exit(EXIT_SUCCESS); } else if (!strcmp(argv[argIndex], "intersect")) { Vector2x direction = readVector2xArgv(&argIndex, argc, argv); Vector2x normal = readVector2xArgv(&argIndex, argc, argv); Vector2x intersection; bool success = Vector2x_intersectLine(vector, direction, normal, &intersection, NULL); if (!success) { fprintf(stderr, "Couldn't find intersection point on parallel lines ({0x%05X, 0x%05X} -> {0x%05X, 0x%05X}, {0x%05X, 0x%05X})\n", vector.x, vector.y, direction.x, direction.y, normal.x, normal.y); exit(EXIT_FAILURE); } vector = intersection; } else if (!strcmp(argv[argIndex], "intersect_segments")) { Vector2x point1 = readVector2xArgv(&argIndex, argc, argv); Vector2x point2 = readVector2xArgv(&argIndex, argc, argv); Vector2x point3 = readVector2xArgv(&argIndex, argc, argv); Vector2x intersection; bool success = Vector2x_intersectLineSegments(vector, point1, point2, point3, &intersection); if (!success) { fprintf(stderr, "Couldn't find intersection point on line segments {0x%05X, 0x%05X} -> {0x%05X, 0x%05X} and {0x%05X, 0x%05X} -> {0x%05X, 0x%05X}\n", vector.x, vector.y, point1.x, point1.y, point2.x, point2.y, point3.x, point3.y); exit(EXIT_FAILURE); } vector = intersection; } else if (!strcmp(argv[argIndex], "rotate")) { fixed16_16 radians = readFixed16_16AngleArgv(&argIndex, argc, argv); vector = Vector2x_rotate(vector, radians); } else if (!strcmp(argv[argIndex], "magnitude")) { fixed16_16 magnitude = Vector2x_magnitude(vector); printFixed16_16(magnitude); exit(EXIT_SUCCESS); } else if (!strcmp(argv[argIndex], "distance")) { Vector2x vector2 = readVector2xArgv(&argIndex, argc, argv); fixed16_16 distance = Vector2x_distance(vector, vector2); printFixed16_16(distance); exit(EXIT_SUCCESS); } else if (!strcmp(argv[argIndex], "to_barycenter")) { Vector2x vertex0 = readVector2xArgv(&argIndex, argc, argv); Vector2x vertex1 = readVector2xArgv(&argIndex, argc, argv); Vector2x vertex2 = readVector2xArgv(&argIndex, argc, argv); Vector3x barycenter = Vector2x_toBarycenter(vector, vertex0, vertex1, vertex2); printVector3x(barycenter); exit(EXIT_SUCCESS); } else if (!strcmp(argv[argIndex], "multiply_matrix3x3")) { Matrix3x3x matrix = readMatrix3x3xArgv(&argIndex, argc, argv); vector = Matrix3x3x_multiplyVector2x(matrix, vector); } else if (!strcmp(argv[argIndex], "multiply_matrix4x4") || !strcmp(argv[argIndex], "multiply_matrix")) { Matrix4x4x matrix = readMatrix4x4xArgv(&argIndex, argc, argv); vector = Matrix4x4x_multiplyVector2x(matrix, vector); } else if (!strcmp(argv[argIndex], "multiply_matrix4x4_rotation_only") || !strcmp(argv[argIndex], "multiply_matrix_rotation_only")) { Matrix4x4x matrix = readMatrix4x4xArgv(&argIndex, argc, argv); vector = Matrix4x4x_multiplyVector2x_rotationOnly(matrix, vector); } else if (!strcmp(argv[argIndex], "swizzle")) { restartLoopWithVectorSwizzle(argc, argv, &argIndex, (fixed16_16 *) &vector, 2); *ioArgIndex = argIndex; return; } else if (!strcmp(argv[argIndex], "print")) { printVector2x(vector); } else if (!strcmp(argv[argIndex], "assign")) { if (argc <= argIndex + 1) { fprintf(stderr, "No name specified after assign command\n"); printUsage(); exit(EXIT_FAILURE); } for (unsigned int namedVector2Index = 0; namedVector2Index < namedVector2Count; namedVector2Index++) { if (!strcmp(namedVector2s[namedVector2Index].name, argv[argIndex + 1])) { namedVector2s[namedVector2Index].vector = vector; *ioArgIndex = argIndex + 1; return; } } namedVector2s = realloc(namedVector2s, (namedVector2Count + 1) * sizeof(*namedVector2s)); namedVector2s[namedVector2Count].name = argv[++argIndex]; namedVector2s[namedVector2Count++].vector = vector; *ioArgIndex = argIndex; return; } else { fprintf(stderr, "Unrecognized vector2 operation: %s\n", argv[argIndex]); break; } } printVector2x(vector); exit(EXIT_SUCCESS); } static void vector3Loop(int argc, const char ** argv, int * ioArgIndex, Vector3x vector) { for (int argIndex = *ioArgIndex + 1; argIndex < argc; argIndex++) { if (!strcmp(argv[argIndex], "normalize")) { Vector3x_normalize(&vector); } else if (!strcmp(argv[argIndex], "inverse") || !strcmp(argv[argIndex], "invert")) { Vector3x_invert(&vector); } else if (!strcmp(argv[argIndex], "add")) { Vector3x vector2 = readVector3xArgv(&argIndex, argc, argv); vector = Vector3x_add(vector, vector2); } else if (!strcmp(argv[argIndex], "subtract")) { Vector3x vector2 = readVector3xArgv(&argIndex, argc, argv); vector = Vector3x_subtract(vector, vector2); } else if (!strcmp(argv[argIndex], "scale_to")) { fixed16_16 value = readFixed16_16Argv(&argIndex, argc, argv); Vector3x_scaleTo(&vector, value); } else if (!strcmp(argv[argIndex], "multiply_scalar")) { fixed16_16 multiplier = readFixed16_16Argv(&argIndex, argc, argv); vector = Vector3x_multiplyScalar(vector, multiplier); } else if (!strcmp(argv[argIndex], "multiply_vector")) { Vector3x vector2 = readVector3xArgv(&argIndex, argc, argv); vector = Vector3x_multiplyComponents(vector, vector2); } else if (!strcmp(argv[argIndex], "divide_scalar")) { fixed16_16 divisor = readFixed16_16Argv(&argIndex, argc, argv); vector = Vector3x_divideScalar(vector, divisor); } else if (!strcmp(argv[argIndex], "divide_vector")) { Vector3x vector2 = readVector3xArgv(&argIndex, argc, argv); vector = Vector3x_add(vector, vector2); } else if (!strcmp(argv[argIndex], "rotate")) { if (argc <= argIndex + 2) { fprintf(stderr, "Not enough arguments to rotate command (requires , )\n"); exit(EXIT_FAILURE); } argIndex++; unsigned int axisNumber = 0; if (!strcmp(argv[argIndex], "x")) { axisNumber = 0; } else if (!strcmp(argv[argIndex], "y")) { axisNumber = 1; } else if (!strcmp(argv[argIndex], "z")) { axisNumber = 2; } else { fprintf(stderr, "Unrecognized axis argument \"%s\" to rotate command; must be \"x\", \"y\", or \"z\")\n", argv[argIndex]); exit(EXIT_FAILURE); } argIndex++; if (!strcmp(argv[argIndex], "cw")) { switch (axisNumber) { case 0: vector = Vector3x_rotate90XCW(vector); break; case 1: vector = Vector3x_rotate90YCW(vector); break; case 2: vector = Vector3x_rotate90ZCW(vector); break; } } else if (!strcmp(argv[argIndex], "ccw")) { switch (axisNumber) { case 0: vector = Vector3x_rotate90XCCW(vector); break; case 1: vector = Vector3x_rotate90YCCW(vector); break; case 2: vector = Vector3x_rotate90ZCCW(vector); break; } } else if (!strcmp(argv[argIndex], "180")) { switch (axisNumber) { case 0: vector = Vector3x_rotate180X(vector); break; case 1: vector = Vector3x_rotate180Y(vector); break; case 2: vector = Vector3x_rotate180Z(vector); break; } } else { fprintf(stderr, "Unrecognized angle argument \"%s\" to rotate command; must be \"cw\", \"ccw\", or \"180\")\n", argv[argIndex]); exit(EXIT_FAILURE); } } else if (!strcmp(argv[argIndex], "interpolate")) { Vector3x vector2 = readVector3xArgv(&argIndex, argc, argv); fixed16_16 value = readFixed16_16Argv(&argIndex, argc, argv); vector = Vector3x_interpolate(vector, vector2, value); } else if (!strcmp(argv[argIndex], "round")) { vector = Vector3x_round(vector); } else if (!strcmp(argv[argIndex], "reflect")) { Vector3x vector2 = readVector3xArgv(&argIndex, argc, argv); vector = Vector3x_reflect(vector, vector2); } else if (!strcmp(argv[argIndex], "project")) { Vector3x vector2 = readVector3xArgv(&argIndex, argc, argv); vector = Vector3x_project(vector, vector2); } else if (!strcmp(argv[argIndex], "line_position")) { Vector3x origin = readVector3xArgv(&argIndex, argc, argv); Vector3x normal = readVector3xArgv(&argIndex, argc, argv); vector = Vector3x_positionOnLine(vector, origin, normal); } else if (!strcmp(argv[argIndex], "plane_position")) { Vector3x origin = readVector3xArgv(&argIndex, argc, argv); Vector3x normal = readVector3xArgv(&argIndex, argc, argv); vector = Vector3x_positionOnPlane(vector, origin, normal); } else if (!strcmp(argv[argIndex], "closest_line_to_line")) { Vector3x normal0 = readVector3xArgv(&argIndex, argc, argv); Vector3x origin1 = readVector3xArgv(&argIndex, argc, argv); Vector3x normal1 = readVector3xArgv(&argIndex, argc, argv); vector = Vector3x_closestLinePointToLine(vector, normal0, origin1, normal1); } else if (!strcmp(argv[argIndex], "perpendicular")) { vector = Vector3x_perpendicular(vector); } else if (!strcmp(argv[argIndex], "dot")) { Vector3x vector2 = readVector3xArgv(&argIndex, argc, argv); fixed16_16 dot = Vector3x_dot(vector, vector2); printFixed16_16(dot); exit(EXIT_SUCCESS); } else if (!strcmp(argv[argIndex], "cross")) { Vector3x vector2 = readVector3xArgv(&argIndex, argc, argv); vector = Vector3x_cross(vector, vector2); } else if (!strcmp(argv[argIndex], "intersect")) { Vector3x direction = readVector3xArgv(&argIndex, argc, argv); Vector3x normal = readVector3xArgv(&argIndex, argc, argv); Vector3x intersection; bool success = Vector3x_intersectPlane(vector, direction, normal, &intersection, NULL); if (!success) { fprintf(stderr, "Couldn't find intersection point on line parallel to plane ({0x%05X, 0x%05X, 0x%05X} -> {0x%05X, 0x%05X, 0x%05X}, {0x%05X, 0x%05X, 0x%05X})\n", vector.x, vector.y, vector.z, direction.x, direction.y, direction.z, normal.x, normal.y, normal.z); exit(EXIT_FAILURE); } vector = intersection; } else if (!strcmp(argv[argIndex], "magnitude")) { fixed16_16 magnitude = Vector3x_magnitude(vector); printFixed16_16(magnitude); exit(EXIT_SUCCESS); } else if (!strcmp(argv[argIndex], "distance")) { Vector3x vector2 = readVector3xArgv(&argIndex, argc, argv); fixed16_16 distance = Vector3x_distance(vector, vector2); printFixed16_16(distance); exit(EXIT_SUCCESS); } else if (!strcmp(argv[argIndex], "rotation_to")) { Vector3x vector2 = readVector3xArgv(&argIndex, argc, argv); if (argc <= argIndex + 1) { fprintf(stderr, "Not enough arguments to rotation_to command (requires \"print\" or \"quaternion\" after vector argument)\n"); exit(EXIT_FAILURE); } Vector3x axis; fixed16_16 angle; Vector3x_getAxisAngleForRotation(vector, vector2, &axis, &angle); argIndex++; if (!strcmp(argv[argIndex], "quaternion")) { *ioArgIndex = argIndex; quaternionLoop(argc, argv, ioArgIndex, Quaternionx_fromAxisAngle(axis, angle)); return; } if (!strcmp(argv[argIndex], "print")) { printAxisAngle(axis, angle); exit(EXIT_SUCCESS); } fprintf(stderr, "Unrecognized argument \"%s\" to rotation_to command; must be \"print\" or \"quaternion\")\n", argv[argIndex]); exit(EXIT_FAILURE); } else if (!strcmp(argv[argIndex], "to_barycenter")) { Vector3x vertex0 = readVector3xArgv(&argIndex, argc, argv); Vector3x vertex1 = readVector3xArgv(&argIndex, argc, argv); Vector3x vertex2 = readVector3xArgv(&argIndex, argc, argv); Vector3x barycenter = Vector3x_toBarycenter(vector, vertex0, vertex1, vertex2); printVector3x(barycenter); exit(EXIT_SUCCESS); } else if (!strcmp(argv[argIndex], "multiply_matrix3x3")) { Matrix3x3x matrix = readMatrix3x3xArgv(&argIndex, argc, argv); vector = Matrix3x3x_multiplyVector3x(matrix, vector); } else if (!strcmp(argv[argIndex], "multiply_matrix4x4") || !strcmp(argv[argIndex], "multiply_matrix")) { Matrix4x4x matrix = readMatrix4x4xArgv(&argIndex, argc, argv); vector = Matrix4x4x_multiplyVector3x(matrix, vector); } else if (!strcmp(argv[argIndex], "multiply_matrix4x4_rotation_only") || !strcmp(argv[argIndex], "multiply_matrix_rotation_only")) { Matrix4x4x matrix = readMatrix4x4xArgv(&argIndex, argc, argv); vector = Matrix4x4x_multiplyVector3x_rotationOnly(matrix, vector); } else if (!strcmp(argv[argIndex], "multiply_quaternion")) { Quaternionx quaternion = readQuaternionxArgv(&argIndex, argc, argv); vector = Quaternionx_multiplyVector3x(quaternion, vector); } else if (!strcmp(argv[argIndex], "swizzle")) { restartLoopWithVectorSwizzle(argc, argv, &argIndex, (fixed16_16 *) &vector, 3); *ioArgIndex = argIndex; return; } else if (!strcmp(argv[argIndex], "print")) { printVector3x(vector); } else if (!strcmp(argv[argIndex], "assign")) { if (argc <= argIndex + 1) { fprintf(stderr, "No name specified after assign command\n"); printUsage(); exit(EXIT_FAILURE); } for (unsigned int namedVector3Index = 0; namedVector3Index < namedVector3Count; namedVector3Index++) { if (!strcmp(namedVector3s[namedVector3Index].name, argv[argIndex + 1])) { namedVector3s[namedVector3Index].vector = vector; *ioArgIndex = argIndex + 1; return; } } namedVector3s = realloc(namedVector3s, (namedVector3Count + 1) * sizeof(*namedVector3s)); namedVector3s[namedVector3Count].name = argv[++argIndex]; namedVector3s[namedVector3Count++].vector = vector; *ioArgIndex = argIndex; return; } else { fprintf(stderr, "Unrecognized vector3 operation: %s\n", argv[argIndex]); break; } } printVector3x(vector); exit(EXIT_SUCCESS); } static void vector4Loop(int argc, const char ** argv, int * ioArgIndex, Vector4x vector) { for (int argIndex = *ioArgIndex + 1; argIndex < argc; argIndex++) { if (!strcmp(argv[argIndex], "normalize")) { Vector4x_normalize(&vector); } else if (!strcmp(argv[argIndex], "normalize_w")) { Vector4x_normalizeW(&vector); } else if (!strcmp(argv[argIndex], "inverse") || !strcmp(argv[argIndex], "invert")) { Vector4x_invert(&vector); } else if (!strcmp(argv[argIndex], "add")) { Vector4x vector2 = readVector4xArgv(&argIndex, argc, argv); vector = Vector4x_add(vector, vector2); } else if (!strcmp(argv[argIndex], "subtract")) { Vector4x vector2 = readVector4xArgv(&argIndex, argc, argv); vector = Vector4x_subtract(vector, vector2); } else if (!strcmp(argv[argIndex], "scale_to")) { fixed16_16 value = readFixed16_16Argv(&argIndex, argc, argv); Vector4x_scaleTo(&vector, value); } else if (!strcmp(argv[argIndex], "multiply_scalar")) { fixed16_16 multiplier = readFixed16_16Argv(&argIndex, argc, argv); vector = Vector4x_multiplyScalar(vector, multiplier); } else if (!strcmp(argv[argIndex], "multiply_vector")) { Vector4x vector2 = readVector4xArgv(&argIndex, argc, argv); vector = Vector4x_multiplyComponents(vector, vector2); } else if (!strcmp(argv[argIndex], "divide_scalar")) { fixed16_16 divisor = readFixed16_16Argv(&argIndex, argc, argv); vector = Vector4x_divideScalar(vector, divisor); } else if (!strcmp(argv[argIndex], "divide_vector")) { Vector4x vector2 = readVector4xArgv(&argIndex, argc, argv); vector = Vector4x_add(vector, vector2); } else if (!strcmp(argv[argIndex], "interpolate")) { Vector4x vector2 = readVector4xArgv(&argIndex, argc, argv); fixed16_16 value = readFixed16_16Argv(&argIndex, argc, argv); vector = Vector4x_interpolate(vector, vector2, value); } else if (!strcmp(argv[argIndex], "round")) { vector = Vector4x_round(vector); } else if (!strcmp(argv[argIndex], "reflect")) { Vector4x vector2 = readVector4xArgv(&argIndex, argc, argv); vector = Vector4x_reflect(vector, vector2); } else if (!strcmp(argv[argIndex], "project")) { Vector4x vector2 = readVector4xArgv(&argIndex, argc, argv); vector = Vector4x_project(vector, vector2); } else if (!strcmp(argv[argIndex], "line_position")) { Vector4x origin = readVector4xArgv(&argIndex, argc, argv); Vector4x normal = readVector4xArgv(&argIndex, argc, argv); vector = Vector4x_positionOnLine(vector, origin, normal); } else if (!strcmp(argv[argIndex], "plane_position")) { Vector4x origin = readVector4xArgv(&argIndex, argc, argv); Vector4x normal = readVector4xArgv(&argIndex, argc, argv); vector = Vector4x_positionOnPlane(vector, origin, normal); } else if (!strcmp(argv[argIndex], "dot")) { Vector4x vector2 = readVector4xArgv(&argIndex, argc, argv); fixed16_16 dot = Vector4x_dot(vector, vector2); printFixed16_16(dot); exit(EXIT_SUCCESS); } else if (!strcmp(argv[argIndex], "magnitude")) { fixed16_16 magnitude = Vector4x_magnitude(vector); printFixed16_16(magnitude); exit(EXIT_SUCCESS); } else if (!strcmp(argv[argIndex], "distance")) { Vector4x vector2 = readVector4xArgv(&argIndex, argc, argv); fixed16_16 distance = Vector4x_distance(vector, vector2); printFixed16_16(distance); exit(EXIT_SUCCESS); } else if (!strcmp(argv[argIndex], "multiply_matrix4x4") || !strcmp(argv[argIndex], "multiply_matrix")) { Matrix4x4x matrix = readMatrix4x4xArgv(&argIndex, argc, argv); vector = Matrix4x4x_multiplyVector4x(matrix, vector); } else if (!strcmp(argv[argIndex], "multiply_quaternion")) { Quaternionx quaternion = readQuaternionxArgv(&argIndex, argc, argv); vector = Quaternionx_multiplyVector4x(quaternion, vector); } else if (!strcmp(argv[argIndex], "swizzle")) { restartLoopWithVectorSwizzle(argc, argv, &argIndex, (fixed16_16 *) &vector, 4); *ioArgIndex = argIndex; return; } else if (!strcmp(argv[argIndex], "print")) { printVector4x(vector); } else if (!strcmp(argv[argIndex], "assign")) { if (argc <= argIndex + 1) { fprintf(stderr, "No name specified after assign command\n"); printUsage(); exit(EXIT_FAILURE); } for (unsigned int namedVector4Index = 0; namedVector4Index < namedVector4Count; namedVector4Index++) { if (!strcmp(namedVector4s[namedVector4Index].name, argv[argIndex + 1])) { namedVector4s[namedVector4Index].vector = vector; *ioArgIndex = argIndex + 1; return; } } namedVector4s = realloc(namedVector4s, (namedVector4Count + 1) * sizeof(*namedVector4s)); namedVector4s[namedVector4Count].name = argv[++argIndex]; namedVector4s[namedVector4Count++].vector = vector; *ioArgIndex = argIndex; return; } else { fprintf(stderr, "Unrecognized vector4 operation: %s\n", argv[argIndex]); break; } } printVector4x(vector); exit(EXIT_SUCCESS); } void startMatrix3x3xLoop(int argc, const char ** argv, int * ioArgIndex) { Matrix3x3x matrix = readMatrix3x3xArgv(ioArgIndex, argc, argv); matrix3x3Loop(argc, argv, ioArgIndex, matrix); } void startMatrix4x4xLoop(int argc, const char ** argv, int * ioArgIndex) { Matrix4x4x matrix = readMatrix4x4xArgv(ioArgIndex, argc, argv); matrix4x4Loop(argc, argv, ioArgIndex, matrix); } void startQuaternionxLoop(int argc, const char ** argv, int * ioArgIndex) { Quaternionx quaternion = readQuaternionxArgv(ioArgIndex, argc, argv); quaternionLoop(argc, argv, ioArgIndex, quaternion); } void startVector2xLoop(int argc, const char ** argv, int * ioArgIndex) { Vector2x vector = readVector2xArgv(ioArgIndex, argc, argv); vector2Loop(argc, argv, ioArgIndex, vector); } void startVector3xLoop(int argc, const char ** argv, int * ioArgIndex) { Vector3x vector = readVector3xArgv(ioArgIndex, argc, argv); vector3Loop(argc, argv, ioArgIndex, vector); } void startVector4xLoop(int argc, const char ** argv, int * ioArgIndex) { Vector4x vector = readVector4xArgv(ioArgIndex, argc, argv); vector4Loop(argc, argv, ioArgIndex, vector); }