phaser
Version:
A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.
824 lines (736 loc) • 24.8 kB
JavaScript
var Vector4 = require('../../src/math/Vector4');
describe('Vector4', function ()
{
describe('constructor', function ()
{
it('should create a vector with default values of zero', function ()
{
var v = new Vector4();
expect(v.x).toBe(0);
expect(v.y).toBe(0);
expect(v.z).toBe(0);
expect(v.w).toBe(0);
});
it('should create a vector with given numeric values', function ()
{
var v = new Vector4(1, 2, 3, 4);
expect(v.x).toBe(1);
expect(v.y).toBe(2);
expect(v.z).toBe(3);
expect(v.w).toBe(4);
});
it('should create a vector from an object with x, y, z, w properties', function ()
{
var v = new Vector4({ x: 5, y: 6, z: 7, w: 8 });
expect(v.x).toBe(5);
expect(v.y).toBe(6);
expect(v.z).toBe(7);
expect(v.w).toBe(8);
});
it('should default missing object properties to zero', function ()
{
var v = new Vector4({ x: 1 });
expect(v.x).toBe(1);
expect(v.y).toBe(0);
expect(v.z).toBe(0);
expect(v.w).toBe(0);
});
it('should handle negative values', function ()
{
var v = new Vector4(-1, -2, -3, -4);
expect(v.x).toBe(-1);
expect(v.y).toBe(-2);
expect(v.z).toBe(-3);
expect(v.w).toBe(-4);
});
it('should handle floating point values', function ()
{
var v = new Vector4(1.5, 2.5, 3.5, 4.5);
expect(v.x).toBe(1.5);
expect(v.y).toBe(2.5);
expect(v.z).toBe(3.5);
expect(v.w).toBe(4.5);
});
});
describe('clone', function ()
{
it('should return a new Vector4 instance', function ()
{
var v = new Vector4(1, 2, 3, 4);
var clone = v.clone();
expect(clone).not.toBe(v);
});
it('should return a vector with the same component values', function ()
{
var v = new Vector4(1, 2, 3, 4);
var clone = v.clone();
expect(clone.x).toBe(1);
expect(clone.y).toBe(2);
expect(clone.z).toBe(3);
expect(clone.w).toBe(4);
});
it('should not mutate the original when the clone is modified', function ()
{
var v = new Vector4(1, 2, 3, 4);
var clone = v.clone();
clone.x = 99;
expect(v.x).toBe(1);
});
});
describe('copy', function ()
{
it('should copy all components from the source vector', function ()
{
var src = new Vector4(5, 6, 7, 8);
var v = new Vector4();
v.copy(src);
expect(v.x).toBe(5);
expect(v.y).toBe(6);
expect(v.z).toBe(7);
expect(v.w).toBe(8);
});
it('should return this vector for chaining', function ()
{
var src = new Vector4(1, 2, 3, 4);
var v = new Vector4();
var result = v.copy(src);
expect(result).toBe(v);
});
it('should default z and w to zero if source lacks them', function ()
{
var v = new Vector4(1, 2, 3, 4);
v.copy({ x: 10, y: 20 });
expect(v.x).toBe(10);
expect(v.y).toBe(20);
expect(v.z).toBe(0);
expect(v.w).toBe(0);
});
});
describe('equals', function ()
{
it('should return true when all components are equal', function ()
{
var a = new Vector4(1, 2, 3, 4);
var b = new Vector4(1, 2, 3, 4);
expect(a.equals(b)).toBe(true);
});
it('should return false when x differs', function ()
{
var a = new Vector4(1, 2, 3, 4);
var b = new Vector4(9, 2, 3, 4);
expect(a.equals(b)).toBe(false);
});
it('should return false when y differs', function ()
{
var a = new Vector4(1, 2, 3, 4);
var b = new Vector4(1, 9, 3, 4);
expect(a.equals(b)).toBe(false);
});
it('should return false when z differs', function ()
{
var a = new Vector4(1, 2, 3, 4);
var b = new Vector4(1, 2, 9, 4);
expect(a.equals(b)).toBe(false);
});
it('should return false when w differs', function ()
{
var a = new Vector4(1, 2, 3, 4);
var b = new Vector4(1, 2, 3, 9);
expect(a.equals(b)).toBe(false);
});
it('should return true for two zero vectors', function ()
{
var a = new Vector4();
var b = new Vector4();
expect(a.equals(b)).toBe(true);
});
});
describe('set', function ()
{
it('should set components from numeric arguments', function ()
{
var v = new Vector4();
v.set(1, 2, 3, 4);
expect(v.x).toBe(1);
expect(v.y).toBe(2);
expect(v.z).toBe(3);
expect(v.w).toBe(4);
});
it('should set components from an object', function ()
{
var v = new Vector4();
v.set({ x: 5, y: 6, z: 7, w: 8 });
expect(v.x).toBe(5);
expect(v.y).toBe(6);
expect(v.z).toBe(7);
expect(v.w).toBe(8);
});
it('should return this vector for chaining', function ()
{
var v = new Vector4();
var result = v.set(1, 2, 3, 4);
expect(result).toBe(v);
});
it('should default missing numeric args to zero', function ()
{
var v = new Vector4(9, 9, 9, 9);
v.set(1);
expect(v.x).toBe(1);
expect(v.y).toBe(0);
expect(v.z).toBe(0);
expect(v.w).toBe(0);
});
});
describe('add', function ()
{
it('should add components of another vector', function ()
{
var a = new Vector4(1, 2, 3, 4);
var b = new Vector4(10, 20, 30, 40);
a.add(b);
expect(a.x).toBe(11);
expect(a.y).toBe(22);
expect(a.z).toBe(33);
expect(a.w).toBe(44);
});
it('should return this vector for chaining', function ()
{
var a = new Vector4(1, 2, 3, 4);
var b = new Vector4(1, 1, 1, 1);
var result = a.add(b);
expect(result).toBe(a);
});
it('should handle adding zero vector', function ()
{
var a = new Vector4(1, 2, 3, 4);
var b = new Vector4(0, 0, 0, 0);
a.add(b);
expect(a.x).toBe(1);
expect(a.y).toBe(2);
expect(a.z).toBe(3);
expect(a.w).toBe(4);
});
it('should handle negative values', function ()
{
var a = new Vector4(5, 5, 5, 5);
var b = new Vector4(-3, -3, -3, -3);
a.add(b);
expect(a.x).toBe(2);
expect(a.y).toBe(2);
expect(a.z).toBe(2);
expect(a.w).toBe(2);
});
});
describe('subtract', function ()
{
it('should subtract components of another vector', function ()
{
var a = new Vector4(10, 20, 30, 40);
var b = new Vector4(1, 2, 3, 4);
a.subtract(b);
expect(a.x).toBe(9);
expect(a.y).toBe(18);
expect(a.z).toBe(27);
expect(a.w).toBe(36);
});
it('should return this vector for chaining', function ()
{
var a = new Vector4(1, 2, 3, 4);
var b = new Vector4(1, 1, 1, 1);
var result = a.subtract(b);
expect(result).toBe(a);
});
it('should return zero vector when subtracting itself', function ()
{
var a = new Vector4(5, 6, 7, 8);
var b = new Vector4(5, 6, 7, 8);
a.subtract(b);
expect(a.x).toBe(0);
expect(a.y).toBe(0);
expect(a.z).toBe(0);
expect(a.w).toBe(0);
});
});
describe('scale', function ()
{
it('should multiply all components by the scale factor', function ()
{
var v = new Vector4(1, 2, 3, 4);
v.scale(3);
expect(v.x).toBe(3);
expect(v.y).toBe(6);
expect(v.z).toBe(9);
expect(v.w).toBe(12);
});
it('should return this vector for chaining', function ()
{
var v = new Vector4(1, 2, 3, 4);
var result = v.scale(2);
expect(result).toBe(v);
});
it('should result in zero vector when scaled by zero', function ()
{
var v = new Vector4(1, 2, 3, 4);
v.scale(0);
expect(v.x).toBe(0);
expect(v.y).toBe(0);
expect(v.z).toBe(0);
expect(v.w).toBe(0);
});
it('should negate the vector when scaled by -1', function ()
{
var v = new Vector4(1, 2, 3, 4);
v.scale(-1);
expect(v.x).toBe(-1);
expect(v.y).toBe(-2);
expect(v.z).toBe(-3);
expect(v.w).toBe(-4);
});
it('should handle fractional scale', function ()
{
var v = new Vector4(2, 4, 6, 8);
v.scale(0.5);
expect(v.x).toBe(1);
expect(v.y).toBe(2);
expect(v.z).toBe(3);
expect(v.w).toBe(4);
});
});
describe('length', function ()
{
it('should return zero for the zero vector', function ()
{
var v = new Vector4(0, 0, 0, 0);
expect(v.length()).toBe(0);
});
it('should return correct length for a unit vector along x', function ()
{
var v = new Vector4(1, 0, 0, 0);
expect(v.length()).toBe(1);
});
it('should return correct length for known values', function ()
{
var v = new Vector4(1, 2, 3, 4);
expect(v.length()).toBeCloseTo(Math.sqrt(1 + 4 + 9 + 16), 10);
});
it('should return the same length for negated components', function ()
{
var v1 = new Vector4(3, 4, 0, 0);
var v2 = new Vector4(-3, -4, 0, 0);
expect(v1.length()).toBe(v2.length());
});
});
describe('lengthSq', function ()
{
it('should return zero for the zero vector', function ()
{
var v = new Vector4(0, 0, 0, 0);
expect(v.lengthSq()).toBe(0);
});
it('should return the squared length', function ()
{
var v = new Vector4(1, 2, 3, 4);
expect(v.lengthSq()).toBe(1 + 4 + 9 + 16);
});
it('should equal length squared', function ()
{
var v = new Vector4(2, 3, 6, 0);
var len = v.length();
expect(v.lengthSq()).toBeCloseTo(len * len, 10);
});
});
describe('normalize', function ()
{
it('should produce a vector of unit length', function ()
{
var v = new Vector4(1, 2, 3, 4);
v.normalize();
expect(v.length()).toBeCloseTo(1, 10);
});
it('should not change direction', function ()
{
var v = new Vector4(3, 0, 0, 0);
v.normalize();
expect(v.x).toBeCloseTo(1, 10);
expect(v.y).toBeCloseTo(0, 10);
expect(v.z).toBeCloseTo(0, 10);
expect(v.w).toBeCloseTo(0, 10);
});
it('should return this vector for chaining', function ()
{
var v = new Vector4(1, 2, 3, 4);
var result = v.normalize();
expect(result).toBe(v);
});
it('should not modify the zero vector', function ()
{
var v = new Vector4(0, 0, 0, 0);
v.normalize();
expect(v.x).toBe(0);
expect(v.y).toBe(0);
expect(v.z).toBe(0);
expect(v.w).toBe(0);
});
});
describe('dot', function ()
{
it('should return zero for perpendicular unit vectors', function ()
{
var a = new Vector4(1, 0, 0, 0);
var b = new Vector4(0, 1, 0, 0);
expect(a.dot(b)).toBe(0);
});
it('should return 1 for identical unit vectors', function ()
{
var a = new Vector4(1, 0, 0, 0);
expect(a.dot(a)).toBe(1);
});
it('should return correct value for known vectors', function ()
{
var a = new Vector4(1, 2, 3, 4);
var b = new Vector4(5, 6, 7, 8);
expect(a.dot(b)).toBe(5 + 12 + 21 + 32);
});
it('should return negative value for opposing vectors', function ()
{
var a = new Vector4(1, 0, 0, 0);
var b = new Vector4(-1, 0, 0, 0);
expect(a.dot(b)).toBe(-1);
});
});
describe('lerp', function ()
{
it('should return this vector unchanged when t is 0', function ()
{
var a = new Vector4(0, 0, 0, 0);
var b = new Vector4(10, 10, 10, 10);
a.lerp(b, 0);
expect(a.x).toBe(0);
expect(a.y).toBe(0);
expect(a.z).toBe(0);
expect(a.w).toBe(0);
});
it('should return the target vector when t is 1', function ()
{
var a = new Vector4(0, 0, 0, 0);
var b = new Vector4(10, 20, 30, 40);
a.lerp(b, 1);
expect(a.x).toBe(10);
expect(a.y).toBe(20);
expect(a.z).toBe(30);
expect(a.w).toBe(40);
});
it('should interpolate to midpoint when t is 0.5', function ()
{
var a = new Vector4(0, 0, 0, 0);
var b = new Vector4(10, 20, 30, 40);
a.lerp(b, 0.5);
expect(a.x).toBe(5);
expect(a.y).toBe(10);
expect(a.z).toBe(15);
expect(a.w).toBe(20);
});
it('should default t to 0 when not provided', function ()
{
var a = new Vector4(1, 2, 3, 4);
var b = new Vector4(10, 20, 30, 40);
a.lerp(b);
expect(a.x).toBe(1);
expect(a.y).toBe(2);
expect(a.z).toBe(3);
expect(a.w).toBe(4);
});
it('should return this vector for chaining', function ()
{
var a = new Vector4(0, 0, 0, 0);
var b = new Vector4(1, 1, 1, 1);
var result = a.lerp(b, 0.5);
expect(result).toBe(a);
});
});
describe('multiply', function ()
{
it('should multiply components together', function ()
{
var a = new Vector4(2, 3, 4, 5);
var b = new Vector4(6, 7, 8, 9);
a.multiply(b);
expect(a.x).toBe(12);
expect(a.y).toBe(21);
expect(a.z).toBe(32);
expect(a.w).toBe(45);
});
it('should return this vector for chaining', function ()
{
var a = new Vector4(1, 2, 3, 4);
var b = new Vector4(1, 1, 1, 1);
var result = a.multiply(b);
expect(result).toBe(a);
});
it('should default missing z/w to 1 so they are unchanged', function ()
{
var a = new Vector4(2, 3, 4, 5);
a.multiply({ x: 2, y: 2 });
expect(a.x).toBe(4);
expect(a.y).toBe(6);
expect(a.z).toBe(4);
expect(a.w).toBe(5);
});
});
describe('divide', function ()
{
it('should divide components', function ()
{
var a = new Vector4(10, 20, 30, 40);
var b = new Vector4(2, 4, 5, 8);
a.divide(b);
expect(a.x).toBe(5);
expect(a.y).toBe(5);
expect(a.z).toBe(6);
expect(a.w).toBe(5);
});
it('should return this vector for chaining', function ()
{
var a = new Vector4(4, 4, 4, 4);
var b = new Vector4(2, 2, 2, 2);
var result = a.divide(b);
expect(result).toBe(a);
});
it('should default missing z/w to 1 so they are unchanged', function ()
{
var a = new Vector4(10, 20, 30, 40);
a.divide({ x: 2, y: 4 });
expect(a.x).toBe(5);
expect(a.y).toBe(5);
expect(a.z).toBe(30);
expect(a.w).toBe(40);
});
});
describe('distance', function ()
{
it('should return zero when both vectors are identical', function ()
{
var a = new Vector4(1, 2, 3, 4);
var b = new Vector4(1, 2, 3, 4);
expect(a.distance(b)).toBe(0);
});
it('should return correct distance for known vectors', function ()
{
var a = new Vector4(0, 0, 0, 0);
var b = new Vector4(1, 0, 0, 0);
expect(a.distance(b)).toBe(1);
});
it('should return the same distance regardless of direction', function ()
{
var a = new Vector4(0, 0, 0, 0);
var b = new Vector4(3, 4, 0, 0);
expect(a.distance(b)).toBeCloseTo(5, 10);
});
it('should compute 4D distance correctly', function ()
{
var a = new Vector4(0, 0, 0, 0);
var b = new Vector4(1, 1, 1, 1);
expect(a.distance(b)).toBeCloseTo(2, 10);
});
});
describe('distanceSq', function ()
{
it('should return zero when both vectors are identical', function ()
{
var a = new Vector4(1, 2, 3, 4);
var b = new Vector4(1, 2, 3, 4);
expect(a.distanceSq(b)).toBe(0);
});
it('should return the squared distance', function ()
{
var a = new Vector4(0, 0, 0, 0);
var b = new Vector4(3, 4, 0, 0);
expect(a.distanceSq(b)).toBe(25);
});
it('should equal the square of distance()', function ()
{
var a = new Vector4(1, 2, 3, 4);
var b = new Vector4(5, 6, 7, 8);
var dist = a.distance(b);
expect(a.distanceSq(b)).toBeCloseTo(dist * dist, 10);
});
});
describe('negate', function ()
{
it('should negate all components', function ()
{
var v = new Vector4(1, 2, 3, 4);
v.negate();
expect(v.x).toBe(-1);
expect(v.y).toBe(-2);
expect(v.z).toBe(-3);
expect(v.w).toBe(-4);
});
it('should return this vector for chaining', function ()
{
var v = new Vector4(1, 2, 3, 4);
var result = v.negate();
expect(result).toBe(v);
});
it('should double-negate back to original', function ()
{
var v = new Vector4(1, 2, 3, 4);
v.negate().negate();
expect(v.x).toBe(1);
expect(v.y).toBe(2);
expect(v.z).toBe(3);
expect(v.w).toBe(4);
});
it('should leave the zero vector unchanged', function ()
{
var v = new Vector4(0, 0, 0, 0);
v.negate();
expect(v.x).toBe(-0);
expect(v.y).toBe(-0);
expect(v.z).toBe(-0);
expect(v.w).toBe(-0);
});
});
describe('transformMat4', function ()
{
it('should transform by the identity matrix leaving components unchanged', function ()
{
var v = new Vector4(1, 2, 3, 4);
// Column-major identity matrix
var mat = {
val: [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
]
};
v.transformMat4(mat);
expect(v.x).toBeCloseTo(1, 10);
expect(v.y).toBeCloseTo(2, 10);
expect(v.z).toBeCloseTo(3, 10);
expect(v.w).toBeCloseTo(4, 10);
});
it('should scale components by a scaling matrix', function ()
{
var v = new Vector4(1, 2, 3, 1);
// Scale by (2, 3, 4) — column-major
var mat = {
val: [
2, 0, 0, 0,
0, 3, 0, 0,
0, 0, 4, 0,
0, 0, 0, 1
]
};
v.transformMat4(mat);
expect(v.x).toBeCloseTo(2, 10);
expect(v.y).toBeCloseTo(6, 10);
expect(v.z).toBeCloseTo(12, 10);
expect(v.w).toBeCloseTo(1, 10);
});
it('should return this vector for chaining', function ()
{
var v = new Vector4(1, 0, 0, 1);
var mat = {
val: [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
]
};
var result = v.transformMat4(mat);
expect(result).toBe(v);
});
});
describe('transformQuat', function ()
{
it('should leave the vector unchanged when transformed by identity quaternion', function ()
{
var v = new Vector4(1, 0, 0, 0);
var q = { x: 0, y: 0, z: 0, w: 1 };
v.transformQuat(q);
expect(v.x).toBeCloseTo(1, 10);
expect(v.y).toBeCloseTo(0, 10);
expect(v.z).toBeCloseTo(0, 10);
});
it('should rotate 90 degrees around z-axis using a quaternion', function ()
{
var v = new Vector4(1, 0, 0, 0);
// 90° around z: q = (0, 0, sin(45°), cos(45°))
var s = Math.sin(Math.PI / 4);
var c = Math.cos(Math.PI / 4);
var q = { x: 0, y: 0, z: s, w: c };
v.transformQuat(q);
expect(v.x).toBeCloseTo(0, 5);
expect(v.y).toBeCloseTo(1, 5);
expect(v.z).toBeCloseTo(0, 5);
});
it('should return this vector for chaining', function ()
{
var v = new Vector4(1, 0, 0, 0);
var q = { x: 0, y: 0, z: 0, w: 1 };
var result = v.transformQuat(q);
expect(result).toBe(v);
});
});
describe('reset', function ()
{
it('should set all components to zero', function ()
{
var v = new Vector4(1, 2, 3, 4);
v.reset();
expect(v.x).toBe(0);
expect(v.y).toBe(0);
expect(v.z).toBe(0);
expect(v.w).toBe(0);
});
it('should return this vector for chaining', function ()
{
var v = new Vector4(1, 2, 3, 4);
var result = v.reset();
expect(result).toBe(v);
});
it('should be idempotent on a zero vector', function ()
{
var v = new Vector4(0, 0, 0, 0);
v.reset();
expect(v.x).toBe(0);
expect(v.y).toBe(0);
expect(v.z).toBe(0);
expect(v.w).toBe(0);
});
});
describe('aliases', function ()
{
it('should have sub as an alias for subtract', function ()
{
expect(Vector4.prototype.sub).toBe(Vector4.prototype.subtract);
});
it('should have mul as an alias for multiply', function ()
{
expect(Vector4.prototype.mul).toBe(Vector4.prototype.multiply);
});
it('should have div as an alias for divide', function ()
{
expect(Vector4.prototype.div).toBe(Vector4.prototype.divide);
});
it('should have dist as an alias for distance', function ()
{
expect(Vector4.prototype.dist).toBe(Vector4.prototype.distance);
});
it('should have distSq as an alias for distanceSq', function ()
{
expect(Vector4.prototype.distSq).toBe(Vector4.prototype.distanceSq);
});
it('should have len as an alias for length', function ()
{
expect(Vector4.prototype.len).toBe(Vector4.prototype.length);
});
it('should have lenSq as an alias for lengthSq', function ()
{
expect(Vector4.prototype.lenSq).toBe(Vector4.prototype.lengthSq);
});
});
});