phaser
Version:
A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.
794 lines (715 loc) • 24.8 kB
JavaScript
var Path = require('../../../src/curves/path/Path');
var Vector2 = require('../../../src/math/Vector2');
var Rectangle = require('../../../src/geom/rectangle/Rectangle');
describe('Path', function ()
{
describe('constructor', function ()
{
it('should create a path with default values', function ()
{
var path = new Path();
expect(path.name).toBe('');
expect(path.defaultDivisions).toBe(12);
expect(path.curves).toEqual([]);
expect(path.cacheLengths).toEqual([]);
expect(path.autoClose).toBe(false);
expect(path.startPoint.x).toBe(0);
expect(path.startPoint.y).toBe(0);
});
it('should create a path with given x and y', function ()
{
var path = new Path(100, 200);
expect(path.startPoint.x).toBe(100);
expect(path.startPoint.y).toBe(200);
});
it('should create a path from a JSON object', function ()
{
var json = {
type: 'Path',
x: 50,
y: 75,
autoClose: true,
curves: [
{
type: 'LineCurve',
points: [ 50, 75, 150, 175 ]
}
]
};
var path = new Path(json);
expect(path.startPoint.x).toBe(50);
expect(path.startPoint.y).toBe(75);
expect(path.autoClose).toBe(true);
expect(path.curves.length).toBe(1);
});
it('should default x and y to 0 when undefined', function ()
{
var path = new Path(undefined, undefined);
expect(path.startPoint.x).toBe(0);
expect(path.startPoint.y).toBe(0);
});
});
describe('add', function ()
{
it('should append a curve and return the path', function ()
{
var path = new Path();
var mockCurve = { active: true, getPoint: function () { return new Vector2(); } };
var result = path.add(mockCurve);
expect(path.curves.length).toBe(1);
expect(path.curves[0]).toBe(mockCurve);
expect(result).toBe(path);
});
it('should append multiple curves in order', function ()
{
var path = new Path();
var curveA = { id: 'a' };
var curveB = { id: 'b' };
path.add(curveA);
path.add(curveB);
expect(path.curves[0]).toBe(curveA);
expect(path.curves[1]).toBe(curveB);
});
});
describe('lineTo', function ()
{
it('should add a LineCurve from the start point to x/y', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 50);
expect(path.curves.length).toBe(1);
});
it('should return the path', function ()
{
var path = new Path(0, 0);
var result = path.lineTo(100, 50);
expect(result).toBe(path);
});
it('should accept a Vector2 as first argument', function ()
{
var path = new Path(0, 0);
var v = new Vector2(200, 300);
path.lineTo(v);
expect(path.curves.length).toBe(1);
});
it('should accept a Vector2Like object as first argument', function ()
{
var path = new Path(0, 0);
path.lineTo({ x: 100, y: 200 });
expect(path.curves.length).toBe(1);
});
it('should chain multiple lineTo calls', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0).lineTo(100, 100).lineTo(0, 100);
expect(path.curves.length).toBe(3);
});
});
describe('moveTo', function ()
{
it('should add a MovePathTo curve', function ()
{
var path = new Path(0, 0);
path.moveTo(50, 50);
expect(path.curves.length).toBe(1);
});
it('should return the path', function ()
{
var path = new Path(0, 0);
var result = path.moveTo(50, 50);
expect(result).toBe(path);
});
it('should accept a Vector2 as first argument', function ()
{
var path = new Path(0, 0);
var v = new Vector2(50, 50);
path.moveTo(v);
expect(path.curves.length).toBe(1);
});
});
describe('getEndPoint', function ()
{
it('should return the start point when path has no curves', function ()
{
var path = new Path(10, 20);
var end = path.getEndPoint();
expect(end.x).toBe(10);
expect(end.y).toBe(20);
});
it('should return the end of the last curve', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
var end = path.getEndPoint();
expect(end.x).toBeCloseTo(100);
expect(end.y).toBeCloseTo(0);
});
it('should write into an existing out Vector2', function ()
{
var path = new Path(5, 10);
var out = new Vector2();
var result = path.getEndPoint(out);
expect(result).toBe(out);
expect(out.x).toBe(5);
expect(out.y).toBe(10);
});
});
describe('getStartPoint', function ()
{
it('should return the defined start point', function ()
{
var path = new Path(30, 40);
var start = path.getStartPoint();
expect(start.x).toBe(30);
expect(start.y).toBe(40);
});
it('should write into an existing out Vector2', function ()
{
var path = new Path(5, 10);
var out = new Vector2();
var result = path.getStartPoint(out);
expect(result).toBe(out);
expect(out.x).toBe(5);
expect(out.y).toBe(10);
});
});
describe('getLength', function ()
{
it('should return the total length of a single line segment', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
expect(path.getLength()).toBeCloseTo(100);
});
it('should return the total length of multiple line segments', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
path.lineTo(100, 100);
expect(path.getLength()).toBeCloseTo(200);
});
});
describe('getCurveLengths', function ()
{
it('should return an empty array for an empty path', function ()
{
var path = new Path();
var lengths = path.getCurveLengths();
expect(lengths).toEqual([]);
});
it('should return cumulative lengths for multiple curves', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
path.lineTo(100, 50);
var lengths = path.getCurveLengths();
expect(lengths.length).toBe(2);
expect(lengths[0]).toBeCloseTo(100);
expect(lengths[1]).toBeCloseTo(150);
});
it('should cache results and return the same array on subsequent calls', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
var first = path.getCurveLengths();
var second = path.getCurveLengths();
expect(first).toBe(second);
});
});
describe('getCurveAt', function ()
{
it('should return the first curve at t=0', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
path.lineTo(100, 100);
var curve = path.getCurveAt(0);
expect(curve).toBe(path.curves[0]);
});
it('should return the last curve at t=1', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
path.lineTo(100, 100);
var curve = path.getCurveAt(1);
expect(curve).toBe(path.curves[path.curves.length - 1]);
});
it('should return the correct curve at t=0.5 for equal-length segments', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
path.lineTo(200, 0);
var curve = path.getCurveAt(0.5);
expect(curve).not.toBeNull();
});
});
describe('getPoint', function ()
{
it('should return the start point at t=0', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
var pt = path.getPoint(0);
expect(pt.x).toBeCloseTo(0);
expect(pt.y).toBeCloseTo(0);
});
it('should return the end point at t=1', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
var pt = path.getPoint(1);
expect(pt.x).toBeCloseTo(100);
expect(pt.y).toBeCloseTo(0);
});
it('should return the midpoint at t=0.5 on a single line', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
var pt = path.getPoint(0.5);
expect(pt.x).toBeCloseTo(50);
expect(pt.y).toBeCloseTo(0);
});
it('should write into an existing out Vector2', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
var out = new Vector2();
var result = path.getPoint(0.5, out);
expect(result).toBe(out);
});
it('should return null for an empty path', function ()
{
var path = new Path(0, 0);
var pt = path.getPoint(0.5);
expect(pt).toBeNull();
});
});
describe('getPoints', function ()
{
it('should return an array of Vector2 points', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
var pts = path.getPoints(10);
expect(Array.isArray(pts)).toBe(true);
expect(pts.length).toBeGreaterThan(0);
});
it('should use defaultDivisions when no arguments are given', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
var pts = path.getPoints();
expect(pts.length).toBeGreaterThan(0);
});
it('should not contain consecutive duplicate points', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
path.lineTo(200, 0);
var pts = path.getPoints(10);
for (var i = 1; i < pts.length; i++)
{
var prev = pts[i - 1];
var curr = pts[i];
expect(prev.equals(curr)).toBe(false);
}
});
it('should append the first point when autoClose is true', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
path.lineTo(100, 100);
path.autoClose = true;
var pts = path.getPoints(4);
var first = pts[0];
var last = pts[pts.length - 1];
expect(last.x).toBeCloseTo(first.x);
expect(last.y).toBeCloseTo(first.y);
});
});
describe('getSpacedPoints', function ()
{
it('should return divisions+1 points by default', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
var pts = path.getSpacedPoints(10);
expect(pts.length).toBe(11);
});
it('should use 40 divisions when no argument is given', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
var pts = path.getSpacedPoints();
expect(pts.length).toBe(41);
});
it('should append a duplicate closing point when autoClose is true', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
path.autoClose = true;
var pts = path.getSpacedPoints(4);
expect(pts.length).toBe(6);
expect(pts[pts.length - 1]).toBe(pts[0]);
});
});
describe('getRandomPoint', function ()
{
it('should return a Vector2', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
var pt = path.getRandomPoint();
expect(pt).toBeInstanceOf(Vector2);
});
it('should write into an existing out Vector2', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
var out = new Vector2();
var result = path.getRandomPoint(out);
expect(result).toBe(out);
});
it('should return points within the line bounds over many iterations', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
for (var i = 0; i < 20; i++)
{
var pt = path.getRandomPoint();
expect(pt.x).toBeGreaterThanOrEqual(0);
expect(pt.x).toBeLessThanOrEqual(100);
}
});
});
describe('closePath', function ()
{
it('should add a line curve to close an open path', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
path.lineTo(100, 100);
var countBefore = path.curves.length;
path.closePath();
expect(path.curves.length).toBe(countBefore + 1);
});
it('should return the path', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
path.lineTo(100, 100);
var result = path.closePath();
expect(result).toBe(path);
});
it('should not add an extra curve if path is already closed', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
path.lineTo(100, 100);
path.lineTo(0, 0);
var countBefore = path.curves.length;
path.closePath();
expect(path.curves.length).toBe(countBefore);
});
});
describe('cubicBezierTo', function ()
{
it('should add a CubicBezierCurve using numeric arguments', function ()
{
var path = new Path(0, 0);
path.cubicBezierTo(100, 0, 25, -50, 75, -50);
expect(path.curves.length).toBe(1);
});
it('should add a CubicBezierCurve using Vector2 arguments', function ()
{
var path = new Path(0, 0);
var p1 = new Vector2(25, -50);
var p2 = new Vector2(75, -50);
var p3 = new Vector2(100, 0);
path.cubicBezierTo(p1, p2, p3);
expect(path.curves.length).toBe(1);
});
it('should return the path', function ()
{
var path = new Path(0, 0);
var result = path.cubicBezierTo(100, 0, 25, -50, 75, -50);
expect(result).toBe(path);
});
});
describe('quadraticBezierTo', function ()
{
it('should add a QuadraticBezierCurve using numeric arguments', function ()
{
var path = new Path(0, 0);
path.quadraticBezierTo(100, 0, 50, -50);
expect(path.curves.length).toBe(1);
});
it('should add a QuadraticBezierCurve using Vector2 arguments', function ()
{
var path = new Path(0, 0);
var p1 = new Vector2(50, -50);
var p2 = new Vector2(100, 0);
path.quadraticBezierTo(p1, p2);
expect(path.curves.length).toBe(1);
});
it('should return the path', function ()
{
var path = new Path(0, 0);
var result = path.quadraticBezierTo(100, 0, 50, -50);
expect(result).toBe(path);
});
});
describe('ellipseTo', function ()
{
it('should add an EllipseCurve', function ()
{
var path = new Path(0, 0);
path.ellipseTo(50, 30, 0, 360, false, 0);
expect(path.curves.length).toBe(1);
});
it('should return the path', function ()
{
var path = new Path(0, 0);
var result = path.ellipseTo(50, 30, 0, 360, false, 0);
expect(result).toBe(path);
});
});
describe('circleTo', function ()
{
it('should add an EllipseCurve (circle)', function ()
{
var path = new Path(0, 0);
path.circleTo(50);
expect(path.curves.length).toBe(1);
});
it('should return the path', function ()
{
var path = new Path(0, 0);
var result = path.circleTo(50);
expect(result).toBe(path);
});
});
describe('splineTo', function ()
{
it('should add a SplineCurve', function ()
{
var path = new Path(0, 0);
path.splineTo([ new Vector2(50, 50), new Vector2(100, 0) ]);
expect(path.curves.length).toBe(1);
});
it('should return the path', function ()
{
var path = new Path(0, 0);
var result = path.splineTo([ new Vector2(50, 50), new Vector2(100, 0) ]);
expect(result).toBe(path);
});
});
describe('getTangent', function ()
{
it('should return a Vector2 tangent for a horizontal line at t=0.5', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
var tangent = path.getTangent(0.5);
expect(tangent).not.toBeNull();
expect(tangent.x).toBeCloseTo(1);
expect(tangent.y).toBeCloseTo(0);
});
it('should write into an existing out Vector2', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
var out = new Vector2();
var result = path.getTangent(0.5, out);
expect(result).toBe(out);
});
it('should return null for an empty path', function ()
{
var path = new Path(0, 0);
var tangent = path.getTangent(0.5);
expect(tangent).toBeNull();
});
});
describe('getBounds', function ()
{
it('should return a Rectangle', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 50);
var bounds = path.getBounds();
expect(bounds).toBeInstanceOf(Rectangle);
});
it('should write into an existing Rectangle', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 50);
var out = new Rectangle();
var result = path.getBounds(out);
expect(result).toBe(out);
});
it('should return correct bounds for a horizontal line', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
var bounds = path.getBounds();
expect(bounds.x).toBeCloseTo(0);
expect(bounds.y).toBeCloseTo(0);
expect(bounds.width).toBeCloseTo(100);
expect(bounds.height).toBeCloseTo(0);
});
it('should return correct bounds for a diagonal line', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 50);
var bounds = path.getBounds();
expect(bounds.x).toBeCloseTo(0);
expect(bounds.y).toBeCloseTo(0);
expect(bounds.right).toBeCloseTo(100);
expect(bounds.bottom).toBeCloseTo(50);
});
});
describe('toJSON', function ()
{
it('should return an object with type Path', function ()
{
var path = new Path(10, 20);
var json = path.toJSON();
expect(json.type).toBe('Path');
});
it('should include the start point coordinates', function ()
{
var path = new Path(10, 20);
var json = path.toJSON();
expect(json.x).toBe(10);
expect(json.y).toBe(20);
});
it('should include the autoClose flag', function ()
{
var path = new Path();
path.autoClose = true;
var json = path.toJSON();
expect(json.autoClose).toBe(true);
});
it('should include serialized curves', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
var json = path.toJSON();
expect(Array.isArray(json.curves)).toBe(true);
expect(json.curves.length).toBe(1);
});
it('should produce JSON that can be round-tripped via fromJSON', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
path.lineTo(100, 100);
var json = path.toJSON();
var path2 = new Path(json);
expect(path2.curves.length).toBe(path.curves.length);
expect(path2.startPoint.x).toBe(path.startPoint.x);
expect(path2.startPoint.y).toBe(path.startPoint.y);
});
});
describe('fromJSON', function ()
{
it('should clear existing curves when loading JSON', function ()
{
var path = new Path(0, 0);
path.lineTo(50, 0);
var json = {
type: 'Path',
x: 0,
y: 0,
autoClose: false,
curves: []
};
path.fromJSON(json);
expect(path.curves.length).toBe(0);
});
it('should set autoClose from JSON', function ()
{
var path = new Path();
var json = {
type: 'Path',
x: 0,
y: 0,
autoClose: true,
curves: []
};
path.fromJSON(json);
expect(path.autoClose).toBe(true);
});
it('should reconstruct LineCurve from JSON', function ()
{
var json = {
type: 'Path',
x: 0,
y: 0,
autoClose: false,
curves: [ { type: 'LineCurve', points: [ 0, 0, 100, 0 ] } ]
};
var path = new Path(json);
expect(path.curves.length).toBe(1);
});
it('should return the path', function ()
{
var path = new Path();
var json = {
type: 'Path',
x: 0,
y: 0,
autoClose: false,
curves: []
};
var result = path.fromJSON(json);
expect(result).toBe(path);
});
});
describe('updateArcLengths', function ()
{
it('should clear the cache', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
path.getCurveLengths();
expect(path.cacheLengths.length).toBe(1);
path.updateArcLengths();
// After calling, getCurveLengths is called again internally which repopulates
expect(path.cacheLengths.length).toBe(1);
});
it('should force recalculation on next getCurveLengths call', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
var first = path.getCurveLengths();
path.updateArcLengths();
var second = path.getCurveLengths();
// Both should have same values
expect(second[0]).toBeCloseTo(first[0]);
});
});
describe('destroy', function ()
{
it('should empty the curves array', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
path.destroy();
expect(path.curves.length).toBe(0);
});
it('should empty the cacheLengths array', function ()
{
var path = new Path(0, 0);
path.lineTo(100, 0);
path.getCurveLengths();
path.destroy();
expect(path.cacheLengths.length).toBe(0);
});
it('should set startPoint to undefined', function ()
{
var path = new Path(0, 0);
path.destroy();
expect(path.startPoint).toBeUndefined();
});
});
});