phaser
Version:
A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.
434 lines (391 loc) • 14.4 kB
JavaScript
var Euler = require('../../src/math/Euler');
var Matrix4 = require('../../src/math/Matrix4');
describe('Euler', function ()
{
describe('constructor', function ()
{
it('should create an Euler with default values', function ()
{
var e = new Euler();
expect(e.x).toBe(0);
expect(e.y).toBe(0);
expect(e.z).toBe(0);
expect(e.order).toBe('XYZ');
});
it('should create an Euler with given values', function ()
{
var e = new Euler(1, 2, 3, 'YXZ');
expect(e.x).toBe(1);
expect(e.y).toBe(2);
expect(e.z).toBe(3);
expect(e.order).toBe('YXZ');
});
it('should default order to DefaultOrder constant', function ()
{
var e = new Euler(0.1, 0.2, 0.3);
expect(e.order).toBe(Euler.DefaultOrder);
});
it('should have onChangeCallback set to NOOP', function ()
{
var e = new Euler();
expect(typeof e.onChangeCallback).toBe('function');
});
});
describe('static properties', function ()
{
it('should have RotationOrders array with 6 entries', function ()
{
expect(Euler.RotationOrders).toEqual([ 'XYZ', 'YXZ', 'ZXY', 'ZYX', 'YZX', 'XZY' ]);
});
it('should have DefaultOrder of XYZ', function ()
{
expect(Euler.DefaultOrder).toBe('XYZ');
});
});
describe('property setters invoke onChangeCallback', function ()
{
it('should invoke onChangeCallback when x is set', function ()
{
var e = new Euler();
var called = false;
e.onChangeCallback = function () { called = true; };
e.x = 1;
expect(called).toBe(true);
});
it('should invoke onChangeCallback when y is set', function ()
{
var e = new Euler();
var called = false;
e.onChangeCallback = function () { called = true; };
e.y = 1;
expect(called).toBe(true);
});
it('should invoke onChangeCallback when z is set', function ()
{
var e = new Euler();
var called = false;
e.onChangeCallback = function () { called = true; };
e.z = 1;
expect(called).toBe(true);
});
it('should invoke onChangeCallback when order is set', function ()
{
var e = new Euler();
var called = false;
e.onChangeCallback = function () { called = true; };
e.order = 'YXZ';
expect(called).toBe(true);
});
it('should pass itself to onChangeCallback', function ()
{
var e = new Euler();
var received = null;
e.onChangeCallback = function (euler) { received = euler; };
e.x = 1;
expect(received).toBe(e);
});
});
describe('set', function ()
{
it('should set x, y, z and return this', function ()
{
var e = new Euler();
var result = e.set(1, 2, 3);
expect(e.x).toBe(1);
expect(e.y).toBe(2);
expect(e.z).toBe(3);
expect(result).toBe(e);
});
it('should keep existing order when order not provided', function ()
{
var e = new Euler(0, 0, 0, 'ZYX');
e.set(1, 2, 3);
expect(e.order).toBe('ZYX');
});
it('should set order when provided', function ()
{
var e = new Euler();
e.set(1, 2, 3, 'YZX');
expect(e.order).toBe('YZX');
});
it('should invoke onChangeCallback', function ()
{
var e = new Euler();
var callCount = 0;
e.onChangeCallback = function () { callCount++; };
e.set(1, 2, 3);
expect(callCount).toBe(1);
});
it('should work with negative values', function ()
{
var e = new Euler();
e.set(-1, -2, -3);
expect(e.x).toBe(-1);
expect(e.y).toBe(-2);
expect(e.z).toBe(-3);
});
it('should work with floating point values', function ()
{
var e = new Euler();
e.set(0.1, 0.2, 0.3);
expect(e.x).toBeCloseTo(0.1);
expect(e.y).toBeCloseTo(0.2);
expect(e.z).toBeCloseTo(0.3);
});
it('should work with zero values', function ()
{
var e = new Euler(1, 2, 3);
e.set(0, 0, 0);
expect(e.x).toBe(0);
expect(e.y).toBe(0);
expect(e.z).toBe(0);
});
});
describe('copy', function ()
{
it('should copy x, y, z, and order from another Euler', function ()
{
var source = new Euler(1, 2, 3, 'ZXY');
var target = new Euler();
target.copy(source);
expect(target.x).toBe(1);
expect(target.y).toBe(2);
expect(target.z).toBe(3);
expect(target.order).toBe('ZXY');
});
it('should return this', function ()
{
var source = new Euler(1, 2, 3);
var target = new Euler();
var result = target.copy(source);
expect(result).toBe(target);
});
it('should not affect the source Euler', function ()
{
var source = new Euler(1, 2, 3, 'YZX');
var target = new Euler();
target.copy(source);
expect(source.x).toBe(1);
expect(source.y).toBe(2);
expect(source.z).toBe(3);
expect(source.order).toBe('YZX');
});
it('should invoke onChangeCallback on target', function ()
{
var source = new Euler(1, 2, 3);
var target = new Euler();
var called = false;
target.onChangeCallback = function () { called = true; };
target.copy(source);
expect(called).toBe(true);
});
});
describe('setFromRotationMatrix', function ()
{
it('should return this', function ()
{
var e = new Euler();
var m = new Matrix4();
m.identity();
var result = e.setFromRotationMatrix(m);
expect(result).toBe(e);
});
it('should set angles to zero for identity matrix with XYZ order', function ()
{
var e = new Euler();
var m = new Matrix4();
m.identity();
e.setFromRotationMatrix(m, 'XYZ');
expect(e.x).toBeCloseTo(0);
expect(e.y).toBeCloseTo(0);
expect(e.z).toBeCloseTo(0);
});
it('should set angles to zero for identity matrix with YXZ order', function ()
{
var e = new Euler();
var m = new Matrix4();
m.identity();
e.setFromRotationMatrix(m, 'YXZ');
expect(e.x).toBeCloseTo(0);
expect(e.y).toBeCloseTo(0);
expect(e.z).toBeCloseTo(0);
});
it('should set angles to zero for identity matrix with ZXY order', function ()
{
var e = new Euler();
var m = new Matrix4();
m.identity();
e.setFromRotationMatrix(m, 'ZXY');
expect(e.x).toBeCloseTo(0);
expect(e.y).toBeCloseTo(0);
expect(e.z).toBeCloseTo(0);
});
it('should set angles to zero for identity matrix with ZYX order', function ()
{
var e = new Euler();
var m = new Matrix4();
m.identity();
e.setFromRotationMatrix(m, 'ZYX');
expect(e.x).toBeCloseTo(0);
expect(e.y).toBeCloseTo(0);
expect(e.z).toBeCloseTo(0);
});
it('should set angles to zero for identity matrix with YZX order', function ()
{
var e = new Euler();
var m = new Matrix4();
m.identity();
e.setFromRotationMatrix(m, 'YZX');
expect(e.x).toBeCloseTo(0);
expect(e.y).toBeCloseTo(0);
expect(e.z).toBeCloseTo(0);
});
it('should set angles to zero for identity matrix with XZY order', function ()
{
var e = new Euler();
var m = new Matrix4();
m.identity();
e.setFromRotationMatrix(m, 'XZY');
expect(e.x).toBeCloseTo(0);
expect(e.y).toBeCloseTo(0);
expect(e.z).toBeCloseTo(0);
});
it('should default to current order when order not provided', function ()
{
var e = new Euler(0, 0, 0, 'ZYX');
var m = new Matrix4();
m.identity();
e.setFromRotationMatrix(m);
expect(e.order).toBe('ZYX');
});
it('should not invoke onChangeCallback by default', function ()
{
var e = new Euler();
var called = false;
e.onChangeCallback = function () { called = true; };
var m = new Matrix4();
m.identity();
e.setFromRotationMatrix(m, 'XYZ', false);
expect(called).toBe(false);
});
it('should invoke onChangeCallback when update is true', function ()
{
var e = new Euler();
var called = false;
e.onChangeCallback = function () { called = true; };
var m = new Matrix4();
m.identity();
e.setFromRotationMatrix(m, 'XYZ', true);
expect(called).toBe(true);
});
it('should extract rotation X from a rotation matrix (XYZ order)', function ()
{
// Build a rotation matrix for a 90-degree rotation around X
var angle = Math.PI / 2;
var m = new Matrix4();
var val = m.val;
// column-major layout: identity then apply rotation
val[0] = 1; val[4] = 0; val[8] = 0;
val[1] = 0; val[5] = Math.cos(angle); val[9] = -Math.sin(angle);
val[2] = 0; val[6] = Math.sin(angle); val[10] = Math.cos(angle);
// m13 = val[8] = 0, so |m13| < epsilon -> XYZ branch uses atan2
var e = new Euler();
e.setFromRotationMatrix(m, 'XYZ');
expect(e.x).toBeCloseTo(angle);
expect(e.y).toBeCloseTo(0);
expect(e.z).toBeCloseTo(0);
});
it('should extract rotation Z from a rotation matrix (XYZ order)', function ()
{
var angle = Math.PI / 4;
var m = new Matrix4();
var val = m.val;
val[0] = Math.cos(angle); val[4] = -Math.sin(angle); val[8] = 0;
val[1] = Math.sin(angle); val[5] = Math.cos(angle); val[9] = 0;
val[2] = 0; val[6] = 0; val[10] = 1;
var e = new Euler();
e.setFromRotationMatrix(m, 'XYZ');
expect(e.x).toBeCloseTo(0);
expect(e.y).toBeCloseTo(0);
expect(e.z).toBeCloseTo(angle);
});
});
describe('setFromQuaternion', function ()
{
it('should return this', function ()
{
var e = new Euler();
var q = { x: 0, y: 0, z: 0, w: 1 };
var result = e.setFromQuaternion(q);
expect(result).toBe(e);
});
it('should produce zero angles from identity quaternion', function ()
{
var e = new Euler();
var q = { x: 0, y: 0, z: 0, w: 1 };
e.setFromQuaternion(q, 'XYZ');
expect(e.x).toBeCloseTo(0);
expect(e.y).toBeCloseTo(0);
expect(e.z).toBeCloseTo(0);
});
it('should default to current order when order not provided', function ()
{
var e = new Euler(0, 0, 0, 'ZYX');
var q = { x: 0, y: 0, z: 0, w: 1 };
e.setFromQuaternion(q);
expect(e.order).toBe('ZYX');
});
it('should not invoke onChangeCallback by default', function ()
{
var e = new Euler();
var called = false;
e.onChangeCallback = function () { called = true; };
var q = { x: 0, y: 0, z: 0, w: 1 };
e.setFromQuaternion(q, 'XYZ', false);
expect(called).toBe(false);
});
it('should invoke onChangeCallback when update is true', function ()
{
var e = new Euler();
var called = false;
e.onChangeCallback = function () { called = true; };
var q = { x: 0, y: 0, z: 0, w: 1 };
e.setFromQuaternion(q, 'XYZ', true);
expect(called).toBe(true);
});
it('should produce correct angles from a 90-degree X rotation quaternion', function ()
{
var half = Math.PI / 4; // sin/cos of 45deg for 90deg rotation
var e = new Euler();
var q = { x: Math.sin(half), y: 0, z: 0, w: Math.cos(half) };
e.setFromQuaternion(q, 'XYZ');
expect(e.x).toBeCloseTo(Math.PI / 2);
expect(e.y).toBeCloseTo(0);
expect(e.z).toBeCloseTo(0);
});
it('should produce correct angles from a 90-degree Y rotation quaternion', function ()
{
var half = Math.PI / 4;
var e = new Euler();
var q = { x: 0, y: Math.sin(half), z: 0, w: Math.cos(half) };
e.setFromQuaternion(q, 'XYZ');
expect(e.x).toBeCloseTo(0);
expect(e.y).toBeCloseTo(Math.PI / 2);
expect(e.z).toBeCloseTo(0);
});
it('should produce correct angles for all rotation orders with identity quaternion', function ()
{
var q = { x: 0, y: 0, z: 0, w: 1 };
var orders = Euler.RotationOrders;
for (var i = 0; i < orders.length; i++)
{
var e = new Euler();
e.setFromQuaternion(q, orders[i]);
expect(e.x).toBeCloseTo(0);
expect(e.y).toBeCloseTo(0);
expect(e.z).toBeCloseTo(0);
expect(e.order).toBe(orders[i]);
}
});
});
});