# toji/gl-matrix

Merge pull request #27 from sinisterchipmunk/vec2-and-mat2

`vec2 and mat2`
2 parents 7671a6d + 9b2699f commit 9c117cb92545951fbb65279ec170931ada2395b5 committed May 3, 2012
Showing with 932 additions and 3 deletions.
1. +516 −0 gl-matrix.js
2. +164 −0 spec/javascripts/mat2_spec.js
3. +19 −3 spec/javascripts/mat3_spec.js
4. +233 −0 spec/javascripts/vec2_spec.js
516 gl-matrix.js
 @@ -530,6 +530,23 @@ }; /** + * Transforms the vec2 according to the given 3x3 matrix. + * + * @param {mat3} matrix the 3x3 matrix to multiply against + * @param {vec2} vec the vector to multiply + * @param {vec2} [dest] an optional receiving vector. If not given, vec is used. + * + * @returns {vec2} The multiplication result + **/ + mat3.multiplyVec2 = function(matrix, vec, dest) { + if (!dest) dest = vec; + var x = vec[0], y = vec[1]; + dest[0] = x * matrix[0] + y * matrix[3] + matrix[6]; + dest[1] = x * matrix[1] + y * matrix[4] + matrix[7]; + return dest; + }; + + /** * Transforms the vec3 according to this rotation matrix. * * @param {mat3} matrix the 3x3 matrix to multiply against @@ -2247,6 +2264,501 @@ quat4.str = function (quat) { return '[' + quat[0] + ', ' + quat[1] + ', ' + quat[2] + ', ' + quat[3] + ']'; }; + + /** + * @class 2 Dimensional Vector + * @name vec2 + */ + var vec2 = {}; + + /** + * Creates a new vec2, initializing it from vec if vec + * is given. + * + * @param {vec2} [vec] the vector's initial contents + * @returns {vec2} a new 2D vector + */ + vec2.create = function(vec) { + var dest = new MatrixArray(2); + if (vec) { + dest[0] = vec[0]; + dest[1] = vec[1]; + } else { + dest[0] = 0; + dest[1] = 0; + } + return dest; + }; + + /** + * Adds the vec2's together. If dest is given, the result + * is stored there. Otherwise, the result is stored in vecB. + * + * @param {vec2} vecA the first operand + * @param {vec2} vecB the second operand + * @param {vec2} [dest] the optional receiving vector + * @returns {vec2} dest + */ + vec2.add = function(vecA, vecB, dest) { + if (!dest) dest = vecB; + dest[0] = vecA[0] + vecB[0]; + dest[1] = vecA[1] + vecB[1]; + return dest; + }; + + /** + * Subtracts vecB from vecA. If dest is given, the result + * is stored there. Otherwise, the result is stored in vecB. + * + * @param {vec2} vecA the first operand + * @param {vec2} vecB the second operand + * @param {vec2} [dest] the optional receiving vector + * @returns {vec2} dest + */ + vec2.subtract = function(vecA, vecB, dest) { + if (!dest) dest = vecB; + dest[0] = vecA[0] - vecB[0]; + dest[1] = vecA[1] - vecB[1]; + return dest; + }; + + /** + * Multiplies vecA with vecB. If dest is given, the result + * is stored there. Otherwise, the result is stored in vecB. + * + * @param {vec2} vecA the first operand + * @param {vec2} vecB the second operand + * @param {vec2} [dest] the optional receiving vector + * @returns {vec2} dest + */ + vec2.multiply = function(vecA, vecB, dest) { + if (!dest) dest = vecB; + dest[0] = vecA[0] * vecB[0]; + dest[1] = vecA[1] * vecB[1]; + return dest; + }; + + /** + * Divides vecA by vecB. If dest is given, the result + * is stored there. Otherwise, the result is stored in vecB. + * + * @param {vec2} vecA the first operand + * @param {vec2} vecB the second operand + * @param {vec2} [dest] the optional receiving vector + * @returns {vec2} dest + */ + vec2.divide = function(vecA, vecB, dest) { + if (!dest) dest = vecB; + dest[0] = vecA[0] / vecB[0]; + dest[1] = vecA[1] / vecB[1]; + return dest; + }; + + /** + * Scales vecA by some scalar number. If dest is given, the result + * is stored there. Otherwise, the result is stored in vecA. + * + * This is the same as multiplying each component of vecA + * by the given scalar. + * + * @param {vec2} vecA the vector to be scaled + * @param {Number} scalar the amount to scale the vector by + * @param {vec2} [dest] the optional receiving vector + * @returns {vec2} dest + */ + vec2.scale = function(vecA, scalar, dest) { + if (!dest) dest = vecA; + dest[0] = vecA[0] * scalar; + dest[1] = vecA[1] * scalar; + return dest; + }; + + /** + * Calculates the euclidian distance between two vec2 + * + * Params: + * @param {vec2} vecA First vector + * @param {vec2} vecB Second vector + * + * @returns {number} Distance between vecA and vecB + */ + vec2.dist = function (vecA, vecB) { + var x = vecB[0] - vecA[0], + y = vecB[1] - vecA[1]; + return Math.sqrt(x*x + y*y); + }; + + /** + * Copies the values of one vec2 to another + * + * @param {vec2} vec vec2 containing values to copy + * @param {vec2} dest vec2 receiving copied values + * + * @returns {vec2} dest + */ + vec2.set = function (vec, dest) { + dest[0] = vec[0]; + dest[1] = vec[1]; + return dest; + }; + + /** + * Negates the components of a vec2 + * + * @param {vec2} vec vec2 to negate + * @param {vec2} [dest] vec2 receiving operation result. If not specified result is written to vec + * + * @returns {vec2} dest if specified, vec otherwise + */ + vec2.negate = function (vec, dest) { + if (!dest) { dest = vec; } + dest[0] = -vec[0]; + dest[1] = -vec[1]; + return dest; + }; + + /** + * Normlize a vec2 + * + * @param {vec2} vec vec2 to normalize + * @param {vec2} [dest] vec2 receiving operation result. If not specified result is written to vec + * + * @returns {vec2} dest if specified, vec otherwise + */ + vec2.normalize = function (vec, dest) { + if (!dest) { dest = vec; } + var mag = vec[0] * vec[0] + vec[1] * vec[1]; + if (mag > 0) { + mag = Math.sqrt(mag); + dest[0] = vec[0] / mag; + dest[1] = vec[1] / mag; + } else { + dest[0] = dest[1] = 0; + } + return dest; + }; + + /** + * Computes the cross product of two vec2's. Note that the cross product must by definition + * produce a 3D vector. If a dest vector is given, it will contain the resultant 3D vector. + * Otherwise, a scalar number will be returned, representing the vector's Z coordinate, since + * its X and Y must always equal 0. + * + * Examples: + * var crossResult = vec3.create(); + * vec2.cross([1, 2], [3, 4], crossResult); + * //=> [0, 0, -2] + * + * vec2.cross([1, 2], [3, 4]); + * //=> -2 + * + * See http://stackoverflow.com/questions/243945/calculating-a-2d-vectors-cross-product + * for some interesting facts. + * + * @param {vec2} vecA left operand + * @param {vec2} vecB right operand + * @param {vec2} [dest] optional vec2 receiving result. If not specified a scalar is returned + * + */ + vec2.cross = function (vecA, vecB, dest) { + var z = vecA[0] * vecB[1] - vecA[1] * vecB[0]; + if (!dest) return z; + dest[0] = dest[1] = 0; + dest[2] = z; + return dest; + }; + + /** + * Caclulates the length of a vec2 + * + * @param {vec2} vec vec2 to calculate length of + * + * @returns {Number} Length of vec + */ + vec2.length = function (vec) { + var x = vec[0], y = vec[1]; + return Math.sqrt(x * x + y * y); + }; + + /** + * Caclulates the dot product of two vec2s + * + * @param {vec3} vecA First operand + * @param {vec3} vecB Second operand + * + * @returns {Number} Dot product of vecA and vecB + */ + vec2.dot = function (vecA, vecB) { + return vecA[0] * vecB[0] + vecA[1] * vecB[1]; + }; + + /** + * Generates a 2D unit vector pointing from one vector to another + * + * @param {vec2} vecA Origin vec2 + * @param {vec2} vecB vec2 to point to + * @param {vec2} [dest] vec2 receiving operation result. If not specified result is written to vecA + * + * @returns {vec2} dest if specified, vecA otherwise + */ + vec2.direction = function (vecA, vecB, dest) { + if (!dest) { dest = vecA; } + + var x = vecA[0] - vecB[0], + y = vecA[1] - vecB[1], + len = x * x + y * y; + + if (!len) { + dest[0] = 0; + dest[1] = 0; + dest[2] = 0; + return dest; + } + + len = 1 / Math.sqrt(len); + dest[0] = x * len; + dest[1] = y * len; + return dest; + }; + + /** + * Performs a linear interpolation between two vec2 + * + * @param {vec2} vecA First vector + * @param {vec2} vecB Second vector + * @param {Number} lerp Interpolation amount between the two inputs + * @param {vec2} [dest] vec2 receiving operation result. If not specified result is written to vecA + * + * @returns {vec2} dest if specified, vecA otherwise + */ + vec2.lerp = function (vecA, vecB, lerp, dest) { + if (!dest) { dest = vecA; } + dest[0] = vecA[0] + lerp * (vecB[0] - vecA[0]); + dest[1] = vecA[1] + lerp * (vecB[1] - vecA[1]); + return dest; + }; + + /** + * Returns a string representation of a vector + * + * @param {vec2} vec Vector to represent as a string + * + * @returns {String} String representation of vec + */ + vec2.str = function (vec) { + return '[' + vec[0] + ', ' + vec[1] + ']'; + }; + + /** + * @class 2x2 Matrix + * @name mat2 + */ + var mat2 = {}; + + /** + * Creates a new 2x2 matrix. If src is given, the new matrix + * is initialized to those values. + * + * @param {mat2} [src] the seed values for the new matrix, if any + * @returns {mat2} a new matrix + */ + mat2.create = function(src) { + var dest = new MatrixArray(4); + if (src) { + dest[0] = src[0]; + dest[1] = src[1]; + dest[2] = src[2]; + dest[3] = src[3]; + } + return dest; + }; + + /** + * Copies the values of one mat2 to another + * + * @param {mat2} mat mat2 containing values to copy + * @param {mat2} dest mat2 receiving copied values + * + * @returns {mat2} dest + */ + mat2.set = function (mat, dest) { + dest[0] = mat[0]; + dest[1] = mat[1]; + dest[2] = mat[2]; + dest[3] = mat[3]; + return dest; + }; + + /** + * Sets a mat2 to an identity matrix + * + * @param {mat2} [dest] mat2 to set. If omitted a new one will be created. + * + * @returns {mat2} dest + */ + mat2.identity = function (dest) { + if (!dest) { dest = mat2.create(); } + dest[0] = 1; + dest[1] = 0; + dest[2] = 0; + dest[3] = 1; + return dest; + }; + + /** + * Transposes a mat2 (flips the values over the diagonal) + * + * @param {mat2} mat mat2 to transpose + * @param {mat2} [dest] mat2 receiving transposed values. If not specified result is written to mat + * + * @param {mat2} dest if specified, mat otherwise + */ + mat2.transpose = function (mat, dest) { + // If we are transposing ourselves we can skip a few steps but have to cache some values + if (!dest || mat === dest) { + var a00 = mat[1]; + mat[1] = mat[2]; + mat[2] = a00; + return mat; + } + + dest[0] = mat[0]; + dest[1] = mat[2]; + dest[2] = mat[1]; + dest[3] = mat[3]; + return dest; + }; + + /** + * Calculates the determinant of a mat2 + * + * @param {mat2} mat mat2 to calculate determinant of + * + * @returns {Number} determinant of mat + */ + mat2.determinant = function (mat) { + return mat[0] * mat[3] - mat[2] * mat[1]; + }; + + /** + * Calculates the inverse matrix of a mat2 + * + * @param {mat2} mat mat2 to calculate inverse of + * @param {mat2} [dest] mat2 receiving inverse matrix. If not specified result is written to mat + * + * @param {mat2} dest is specified, mat otherwise, null if matrix cannot be inverted + */ + mat2.inverse = function (mat, dest) { + if (!dest) { dest = mat; } + var a0 = mat[0], a1 = mat[1], a2 = mat[2], a3 = mat[3]; + var det = a0 * a3 - a2 * a1; + if (!det) return null; + + det = 1.0 / det; + dest[0] = a3 * det; + dest[1] = -a1 * det; + dest[2] = -a2 * det; + dest[3] = a0 * det; + return dest; + }; + + /** + * Performs a matrix multiplication + * + * @param {mat2} matA First operand + * @param {mat2} matB Second operand + * @param {mat2} [dest] mat2 receiving operation result. If not specified result is written to matA + * + * @returns {mat2} dest if specified, matA otherwise + */ + mat2.multiply = function (matA, matB, dest) { + if (!dest) { dest = matA; } + var a11 = matA[0], + a12 = matA[1], + a21 = matA[2], + a22 = matA[3]; + dest[0] = a11 * matB[0] + a12 * matB[2]; + dest[1] = a11 * matB[1] + a12 * matB[3]; + dest[2] = a21 * matB[0] + a22 * matB[2]; + dest[3] = a21 * matB[1] + a22 * matB[3]; + return dest; + }; + + /** + * Rotates a 2x2 matrix by an angle + * + * @param {mat2} mat The matrix to rotate + * @param {Number} angle The angle in radians + * @param {mat2} [dest] Optional mat2 receiving the result. If omitted mat will be used. + * + * @returns {mat2} dest if specified, mat otherwise + */ + mat2.rotate = function (mat, angle, dest) { + if (!dest) { dest = mat; } + var a11 = mat[0], + a12 = mat[1], + a21 = mat[2], + a22 = mat[3], + s = Math.sin(angle), + c = Math.cos(angle); + dest[0] = a11 * c + a12 * s; + dest[1] = a11 * -s + a12 * c; + dest[2] = a21 * c + a22 * s; + dest[3] = a21 * -s + a22 * c; + return dest; + }; + + /** + * Multiplies the vec2 by the given 2x2 matrix + * + * @param {mat2} matrix the 2x2 matrix to multiply against + * @param {vec2} vec the vector to multiply + * @param {vec2} [dest] an optional receiving vector. If not given, vec is used. + * + * @returns {vec2} The multiplication result + **/ + mat2.multiplyVec2 = function(matrix, vec, dest) { + if (!dest) dest = vec; + var x = vec[0], y = vec[1]; + dest[0] = x * matrix[0] + y * matrix[1]; + dest[1] = x * matrix[2] + y * matrix[3]; + return dest; + }; + + /** + * Scales the mat2 by the dimensions in the given vec2 + * + * @param {mat2} matrix the 2x2 matrix to scale + * @param {vec2} vec the vector containing the dimensions to scale by + * @param {vec2} [dest] an optional receiving mat2. If not given, matrix is used. + * + * @returns {mat2} dest if specified, matrix otherwise + **/ + mat2.scale = function(matrix, vec, dest) { + if (!dest) { dest = matrix; } + var a11 = matrix[0], + a12 = matrix[1], + a21 = matrix[2], + a22 = matrix[3], + b11 = vec[0], + b22 = vec[1]; + dest[0] = a11 * b11; + dest[1] = a12 * b22; + dest[2] = a21 * b11; + dest[3] = a22 * b22; + return dest; + }; + + /** + * Returns a string representation of a mat2 + * + * @param {mat2} mat mat2 to represent as a string + * + * @param {String} String representation of mat + */ + mat2.str = function (mat) { + return '[' + mat[0] + ', ' + mat[1] + ', ' + mat[2] + ', ' + mat[3] + ']'; + }; /* * Exports @@ -2258,7 +2770,9 @@ root.setMatrixArrayType = setMatrixArrayType; root.determineMatrixArrayType = determineMatrixArrayType; root.glMath = glMath; + root.vec2 = vec2; root.vec3 = vec3; + root.mat2 = mat2; root.mat3 = mat3; root.mat4 = mat4; root.quat4 = quat4; @@ -2270,7 +2784,9 @@ setMatrixArrayType: setMatrixArrayType, determineMatrixArrayType: determineMatrixArrayType, glMath: glMath, + vec2: vec2, vec3: vec3, + mat2: mat2, mat3: mat3, mat4: mat4, quat4: quat4
164 spec/javascripts/mat2_spec.js
 @@ -0,0 +1,164 @@ +describe("mat2", function() { + var result, a, b, dest; + + beforeEach(function() { + a = [1, 2, 3, 4]; + b = [5, 6, 7, 8]; + dest = [0, 0, 0, 0]; + }); + + describe("create", function() { + describe("with src", function() { + beforeEach(function() { result = mat2.create(a); }); + it("should set result", function() { expect(result).toBeEqualish([1, 2, 3, 4]); }); + it("should not return a", function() { expect(result).not.toBe(a); }); + it("should not change a", function() { expect(a).toBeEqualish([1, 2, 3, 4]); }); + }); + + describe("with no src", function() { + it("should set to 0", function() { expect(mat2.create()).toBeEqualish([0, 0, 0, 0]); }); + }); + }); + + describe("set", function() { + it("should set values in dest to values from src", function() { + mat2.set(a, b); + expect(b).toBeEqualish(a); + }); + + it("should return b", function() { + expect(mat2.set(a, b)).toBe(b); + }); + }); + + describe("identity", function() { + describe("with dest", function() { + beforeEach(function() { result = mat2.identity(dest); }); + it("should set dest to identity", function() { expect(dest).toBeEqualish([1, 0, 0, 1]); }); + it("should return dest", function() { expect(result).toBe(dest); }); + }); + + describe("without dest", function() { + beforeEach(function() { result = mat2.identity(); }); + it("should set result to identity", function() { expect(result).toBeEqualish([1, 0, 0, 1]); }); + }); + }); + + describe("transpose", function() { + describe("with dest", function() { + beforeEach(function() { result = mat2.transpose(a, dest); }); + it("should set dest to transpose", function() { expect(dest).toBeEqualish([1, 3, 2, 4]); }); + it("should return dest", function() { expect(result).toBe(dest); }); + it("should not alter a", function() { expect(a).toBeEqualish([1, 2, 3, 4]); }); + }); + + describe("without dest", function() { + beforeEach(function() { result = mat2.transpose(a); }); + it("should set a to transpose", function() { expect(a).toBeEqualish([1, 3, 2, 4]); }); + it("should return a", function() { expect(result).toBe(a); }); + }); + }); + + describe("determinant", function() { + it("should produce the correct result", function() { + expect(mat2.determinant(a)).toBeEqualish(-2); + }); + }); + + describe("inverse", function() { + describe("when no inverse exists", function() { + it("should be null", function() { + expect(mat2.inverse([3, 4, 6, 8])).toBeNull(); + }); + }); + + describe("with dest", function() { + beforeEach(function() { result = mat2.inverse(a = [4, 7, 2, 6], dest); }); + it("should set dest", function() { expect(dest).toBeEqualish([0.6, -0.7, -0.2, 0.4]); }); + it("should return dest", function() { expect(result).toBe(dest); }); + it("should not modify a", function() { expect(a).toBeEqualish([4, 7, 2, 6]); }); + }); + + describe("without dest", function() { + beforeEach(function() { result = mat2.inverse(a = [4, 7, 2, 6]); }); + it("should set a", function() { expect(a).toBeEqualish([0.6, -0.7, -0.2, 0.4]); }); + it("should return a", function() { expect(result).toBe(a); }); + }); + }); + + describe("multiply", function() { + describe("with dest", function() { + beforeEach(function() { result = mat2.multiply(a, b, dest); }); + it("should set dest", function() { expect(dest).toBeEqualish([19, 22, 43, 50]); }); + it("should return dest", function() { expect(result).toBe(dest); }); + it("should not modify a", function() { expect(a).toBeEqualish([1, 2, 3, 4]); }); + it("should not modify b", function() { expect(b).toBeEqualish([5, 6, 7, 8]); }); + }); + + describe("without dest", function() { + beforeEach(function() { result = mat2.multiply(a, b); }); + it("should set a", function() { expect(a).toBeEqualish([19, 22, 43, 50]); }); + it("should not change b", function() { expect(b).toBeEqualish([5, 6, 7, 8]); }); + it("should return a", function() { expect(result).toBe(a); }); + }); + }); + + describe("multiplyVec2", function() { + beforeEach(function() { b = [5, 6]; dest = [0, 0]; }); + + describe("with dest", function() { + beforeEach(function() { result = mat2.multiplyVec2(a, b, dest); }); + it("should set dest", function() { expect(dest).toBeEqualish([17, 39]); }); + it("should return dest", function() { expect(result).toBe(dest); }); + it("should not modify a", function() { expect(a).toBeEqualish([1, 2, 3, 4]); }); + it("should not modify b", function() { expect(b).toBeEqualish([5, 6]); }); + }); + + describe("without dest", function() { + beforeEach(function() { result = mat2.multiplyVec2(a, b); }); + it("should not change a", function() { expect(a).toBeEqualish([1, 2, 3, 4]); }); + it("should change b", function() { expect(b).toBeEqualish([17, 39]); }); + it("should return b", function() { expect(result).toBe(b); }); + }); + }); + + describe("rotate", function() { + beforeEach(function() { b = Math.PI/2; }); + + describe("with dest", function() { + beforeEach(function() { result = mat2.rotate(a, b, dest); }); + it("should set dest", function() { expect(dest).toBeEqualish([2, -1, 4, -3]); }); + it("should return dest", function() { expect(result).toBe(dest); }); + it("should not modify a", function() { expect(a).toBeEqualish([1, 2, 3, 4]); }); + }); + + describe("without dest", function() { + beforeEach(function() { result = mat2.rotate(a, b); }); + it("should modify a", function() { expect(a).toBeEqualish([2, -1, 4, -3]); }); + it("should return a", function() { expect(result).toBe(a); }); + }); + }); + + describe("scale", function() { + beforeEach(function() { b = [2, 2] }); + + describe("with dest", function() { + beforeEach(function() { result = mat2.scale(a, b, dest); }); + it("should set dest", function() { expect(dest).toBeEqualish([2, 4, 6, 8]); }); + it("should return dest", function() { expect(result).toBe(dest); }); + it("should not modify a", function() { expect(a).toBeEqualish([1, 2, 3, 4]); }); + it("should not modify b", function() { expect(b).toBeEqualish([2, 2]); }); + }); + + describe("without dest", function() { + beforeEach(function() { result = mat2.scale(a, b); }); + it("should set a", function() { expect(a).toBeEqualish([2, 4, 6, 8]); }); + it("should return a", function() { expect(result).toBe(a); }); + it("should not modify b", function() { expect(b).toBeEqualish([2, 2]); }); + }); + }); + + describe("str", function() { + it("should produce pretty string", function() { expect(mat2.str(a)).toEqual("[1, 2, 3, 4]"); }); + }); +});
22 spec/javascripts/mat3_spec.js
 @@ -1,11 +1,29 @@ describe("mat3", function() { - var mat, vec, dest; + var mat, vec, dest, result; beforeEach(function() { + mat = mat3.create([-1, 0, 1, 0, -1, 0, 1, 0, -1]); vec = vec3.create([1, 2, 3]); dest = vec3.create(); }); + describe("multiplyVec2", function() { + beforeEach(function() { vec = [1, 2]; dest = vec2.create() }); + + describe("with dest", function() { + beforeEach(function() { result = mat3.multiplyVec2(mat, vec, dest); }); + it("should return dest", function() { expect(result).toBe(dest); }); + it("should not alter vec", function() { expect(vec).toBeEqualish([1, 2]); }); + it("should set dest", function() { expect(dest).toBeEqualish([0, -2]); }); + }); + + describe("without dest", function() { + beforeEach(function() { result = mat3.multiplyVec2(mat, vec); }); + it("should return vec", function() { expect(result).toBe(vec); }); + it("should set vec", function() { expect(vec).toBeEqualish([0, -2]); }); + }); + }); + describe("multiplyVec3", function() { describe("when set to identity", function() { beforeEach(function() { mat = mat3.identity(mat3.create()); }); @@ -16,8 +34,6 @@ describe("mat3", function() { }); describe("with an arbitrary mat3", function() { - beforeEach(function() { mat = mat3.create([-1, 0, 1, 0, -1, 0, 1, 0, -1]); }); - describe("given a dest vec3", function() { it("should not modify incoming vec3", function() { mat3.multiplyVec3(mat, vec, vec3.create());
233 spec/javascripts/vec2_spec.js
 @@ -0,0 +1,233 @@ +describe("vec2", function() { + var vec, dest; + var vecA, vecB, result; + beforeEach(function() { vecA = [1, 2]; vecB = [3, 4]; dest = [0, 0]; }); + + describe("when Float32Array is not supported", function() { + beforeEach(function() { setMatrixArrayType(Array); }); + + it("should initialize to 0", function() { + vec = vec2.create(); + expect(vec[0]).toEqual(0); + expect(vec[1]).toEqual(0); + }); + }); + + describe("str", function() { + it("should produce pretty string", function() { expect(vec2.str(vecA)).toEqual("[1, 2]"); }); + }); + + describe("lerp", function() { + describe("with dest", function() { + beforeEach(function() { result = vec2.lerp(vecA, vecB, 0.5, dest); }); + it("should return dest", function() { expect(result).toBe(dest); }); + it("should set dest to correct value", function() { expect(dest).toBeEqualish([2, 3]); }); + it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); }); + it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 4]); }); + }); + + describe("without dest", function() { + beforeEach(function() { result = vec2.lerp(vecA, vecB); }); + it("should return vecA", function() { expect(result).toBe(vecA); }); + it("should modify vecA", function() { expect(vecA).toBeEqualish([2, 3]); }); + it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 4]); }); + }); + }); + + describe("direction", function() { + describe("with dest", function() { + beforeEach(function() { result = vec2.direction(vecA, vecB, dest); }); + it("should return dest", function() { expect(result).toBe(dest); }); + it("should set dest to correct value", function() { expect(dest).toBeEqualish([ -0.707106, -0.707106 ]); }); + it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); }); + it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 4]); }); + }); + + describe("without dest", function() { + beforeEach(function() { result = vec2.direction(vecA, vecB); }); + it("should return vecA", function() { expect(result).toBe(vecA); }); + it("should modify vecA", function() { expect(vecA).toBeEqualish([-0.707106, -0.707106]); }); + it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 4]); }); + }); + }); + + describe("dot", function() { + it("should return dot product", function() { expect(vec2.dot(vecA, vecB)).toBeEqualish(11); }); + }); + + describe("length", function() { + it("should return the correct value", function() { expect(vec2.length(vecA)).toBeEqualish(2.236067); }); + }); + + describe("cross", function() { + describe("with dest given", function() { + beforeEach(function() { result = vec2.cross(vecA, vecB, dest); }); + it("should store the cross in dest", function() { expect(dest).toBeEqualish([0, 0, -2]); }); + it("should return dest", function() { expect(result).toBe(dest); }); + it("should not alter vecA", function() { expect(vecA).toBeEqualish([1, 2]); }); + it("should not alter vecB", function() { expect(vecB).toBeEqualish([3, 4]); }); + }); + + describe("with no dest given", function() { + beforeEach(function() { result = vec2.cross(vecA, vecB); }); + it("should return the scalar Z value", function() { expect(result).toBeEqualish(-2); }); + it("should not alter vecA", function() { expect(vecA).toBeEqualish([1, 2]); }); + it("should not alter vecB", function() { expect(vecB).toBeEqualish([3, 4]); }); + }); + }); + + describe("normalize", function() { + describe("with dest given", function() { + beforeEach(function() { result = vec2.normalize(vecA, dest); }); + it("should store normal in dest", function() { expect(dest).toBeEqualish([ 0.4472135954999579, 0.8944271909999159 ]); }); + it("should return dest", function() { expect(result).toBe(dest); }); + it("should not alter vecA", function() { expect(vecA).toBeEqualish([1, 2]); }); + }); + + describe("with dest not given", function() { + beforeEach(function() { result = vec2.normalize(vecA); }); + it("should store normal in vecA", function() { expect(vecA).toBeEqualish([ 0.4472135954999579, 0.8944271909999159 ]); }); + it("should return vecA", function() { expect(result).toBe(vecA); }); + }); + }); + + describe("negate", function() { + describe("with dest given", function() { + beforeEach(function() { result = vec2.negate(vecA, dest); }); + it("should store negation in dest", function() { expect(dest).toBeEqualish([-1, -2]); }); + it("should return dest", function() { expect(result).toBe(dest); }); + it("should not alter vecA", function() { expect(vecA).toBeEqualish([1, 2]); }); + }); + + describe("with dest not given", function() { + beforeEach(function() { result = vec2.negate(vecA); }); + it("should return vecA", function() { expect(result).toBe(vecA); }); + it("should alter vecA", function() { expect(vecA).toBeEqualish([-1, -2]); }); + }); + }); + + describe("set", function() { + beforeEach(function() { result = vec2.set(vecA, dest); }); + it("should assign values", function() { expect(dest).toBeEqualish([1, 2]); }); + it("should return dest", function() { expect(result).toBe(dest); }); + }); + + describe("dist", function() { + beforeEach(function() { result = vec2.dist(vecA, vecB); }); + + it("should return dest", function() { expect(result).toBeEqualish(2.828427); }); + it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); }); + it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 4]); }); + }); + + describe("scale", function() { + describe("with dest vec2", function() { + beforeEach(function() { result = vec2.scale(vecA, 0.5, dest); }); + + it("should place values into dest", function() { expect(dest).toBeEqualish([0.5, 1]); }); + it("should return dest", function() { expect(result).toBe(dest); }); + it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); }); + }); + + describe("without dest vec2", function() { + beforeEach(function() { result = vec2.scale(vecA, vecB); }); + + it("should place values into vecA", function() { expect(vecA).toBeEqualish([0.5, 1]); }); + }); + }); + + describe("add", function() { + describe("with dest vec2", function() { + beforeEach(function() { result = vec2.add(vecA, vecB, dest); }); + + it("should place values into dest", function() { expect(dest).toBeEqualish([4, 6]); }); + it("should return dest", function() { expect(result).toBe(dest); }); + it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); }); + it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 4]); }); + }); + + describe("without dest vec2", function() { + beforeEach(function() { result = vec2.add(vecA, vecB); }); + + it("should place values into vecB", function() { expect(vecB).toBeEqualish([4, 6]); }); + it("should return vecB", function() { expect(result).toBe(vecB); }); + it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); }); + }); + }); + + describe("subtract", function() { + describe("with dest vec2", function() { + beforeEach(function() { result = vec2.subtract(vecA, vecB, dest); }); + + it("should place values into dest", function() { expect(dest).toBeEqualish([-2, -2]); }); + it("should return dest", function() { expect(result).toBe(dest); }); + it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); }); + it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 4]); }); + }); + + describe("without dest vec2", function() { + beforeEach(function() { result = vec2.subtract(vecA, vecB); }); + + it("should place values into vecB", function() { expect(vecB).toBeEqualish([-2, -2]); }); + it("should return vecB", function() { expect(result).toBe(vecB); }); + it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); }); + }); + }); + + describe("multiply", function() { + describe("with dest vec2", function() { + beforeEach(function() { result = vec2.multiply(vecA, vecB, dest); }); + + it("should place values into dest", function() { expect(dest).toBeEqualish([3, 8]); }); + it("should return dest", function() { expect(result).toBe(dest); }); + it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); }); + it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 4]); }); + }); + + describe("without dest vec2", function() { + beforeEach(function() { result = vec2.multiply(vecA, vecB); }); + + it("should place values into vecB", function() { expect(vecB).toBeEqualish([3, 8]); }); + it("should return vecB", function() { expect(result).toBe(vecB); }); + it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); }); + }); + }); + + describe("divide", function() { + describe("with dest vec2", function() { + beforeEach(function() { result = vec2.divide(vecA, vecB, dest); }); + + it("should place values into dest", function() { expect(dest).toBeEqualish([0.333333, 0.5]); }); + it("should return dest", function() { expect(result).toBe(dest); }); + it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); }); + it("should not modify vecB", function() { expect(vecB).toBeEqualish([3, 4]); }); + }); + + describe("without dest vec2", function() { + beforeEach(function() { result = vec2.divide(vecA, vecB); }); + + it("should place values into vecB", function() { expect(vecB).toBeEqualish([0.333333, 0.5]); }); + it("should return vecB", function() { expect(result).toBe(vecB); }); + it("should not modify vecA", function() { expect(vecA).toBeEqualish([1, 2]); }); + }); + }); + + describe("create", function() { + describe("with vec", function() { + it("should clone vec's contents", function() { + expect(vec2.create([1, 2])).toBeEqualish([1, 2]); + }); + + it("should not return vec", function() { + vec = [1, 2]; + expect(vec2.create(vec)).not.toBe(vec); + }); + }); + + describe("without vec", function() { + it("should initialize components to 0", function() { + expect(vec2.create()).toBeEqualish([0, 0]); + }); + }); + }); +});