import { assert } from 'chai'; import { DOUBLE_EPSILON } from '../../../../lib/math/float'; import { radians } from '../../../../lib/math/units'; import { Vector2D } from '../../../../lib/graphics/Vector2D'; import { Vector2DArray } from '../../../../lib/graphics/Vector2DArray'; import { Matrix } from '../../../../lib/graphics/Matrix'; import { Shape } from '../../../../lib/graphics/shapes/Shape'; import { Point } from '../../../../lib/graphics/shapes/Point'; import { Line } from '../../../../lib/graphics/shapes/Line'; import { Circle } from '../../../../lib/graphics/shapes/Circle'; import { Rectangle } from '../../../../lib/graphics/shapes/Rectangle'; import { Polygon } from '../../../../lib/graphics/shapes/Polygon'; import { Polyline } from '../../../../lib/graphics/shapes/Polyline'; import { canvasRenderingContext2DSpy } from '../mock/CanvasRenderingContext2D'; const matrix = Matrix.create; const vector2d = Vector2D.create; const fuzzyDeepEqual = Vector2DArray.fuzzyDeepEqual; const point = Point.create; const line = Line.create; const circle = Circle.create; const rectangle = Rectangle.create; const polygon = Polygon.create; const polyline = Polyline.create; describe('graphics.shapes.Point', () => { describe('constructor', () => { it('default constructor', () => { const p = new Point(); assert.strictEqual(p.x, 0); assert.strictEqual(p.y, 0); }); it('x constructor', () => { const p = new Point(1); assert.strictEqual(p.x, 1); assert.strictEqual(p.y, 0); }); it('x, y constructor', () => { const p = new Point(1, -1); assert.strictEqual(p.x, 1); assert.strictEqual(p.y, -1); }); it('instanceof', () => { const p = new Point(); assert.isTrue(p instanceof Shape); assert.isTrue(p instanceof Point); }); }); describe('clone', function () { it('clone', function () { const p1 = point(1, 2); const p2 = p1.clone(); assert.isTrue(p2 instanceof Point); assert.isTrue(p2.equals(p1)); p1.x = 3; p1.y = 4; assert.strictEqual(p2.x, 1); assert.strictEqual(p2.y, 2); }); }); describe('isNull', () => { test.each([ [point(), true], [point(0, -0.0), true], [point(-1, 0), false], [point(0.00001, -0.00001), false], ])('isNull#%#', (p: Point, res: boolean) => { assert.isTrue(p.isNull() === res); }); }); describe('fuzzyIsNull', () => { test.each([ [point(), true, undefined], [point(0, -0.0), true, undefined], [point(-1, 0), false, undefined], [point(0.00001, -0.00001), true, undefined], [point(0.00001, -0.00001), false, DOUBLE_EPSILON], [point(0.0001, -0.0001), false, undefined], ])('fuzzyIsNull#%#', (p: Point, res: boolean, epsilon: number | undefined) => { assert.isTrue(p.fuzzyIsNull(epsilon) === res); }); }); describe('equals', () => { test.each([ [point(), point(), true], [point(0), point(), true], [point(0, 0), point(), true], [point(1), point(1), true], [point(1, 2), point(1, 2), true], [point(-5, 0.2), point(-5, 0.2), true], [point(5, 0.2), point(-5, 0.2), false], [point(5, 0.2), point(5, 0.3), false], [point(1), point(2), false], [point(1), point(1, -1), false], ])('equals#%#', (p1: Point, p2: Point, res: boolean) => { assert.isTrue(p1.equals(p2) === res); }); }); describe('fuzzyEquals', () => { test.each([ [point(), point(), true, undefined], [point(0), point(), true, undefined], [point(0, 0), point(), true, undefined], [point(1), point(1), true, undefined], [point(1, 2), point(1, 2), true, undefined], [point(-5, 0.2), point(-5, 0.2), true, undefined], [point(5, 0.2), point(-5, 0.2), false, undefined], [point(5, 0.2), point(5, 0.3), false, undefined], [point(1), point(2), false, undefined], [point(1), point(1, -1), false, undefined], [point(1), point(1.000001), true, undefined], [point(1), point(-1.000001), false, undefined], [point(1, -1), point(1, -1.000001), true, undefined], [point(1), point(1.000001), false, DOUBLE_EPSILON] ])('fuzzyEquals#%#', (p1: Point, p2: Point, res: boolean, epsilon: number | undefined) => { assert.isTrue(p1.fuzzyEquals(p2, epsilon) === res); }); }); describe('translate', () => { test.each([ [point(), vector2d(1, 1), point(1, 1)], [point(1, 2), vector2d(), point(1, 2)], [point(1, 2), vector2d(1, 1), point(2, 3)], ])('translate#%#', (point: Point, offset: Vector2D, result: Point) => { assert.isTrue(point.translate(offset.x, offset.y) === point); assert.isTrue(point.equals(result)); }); }); describe('translated', () => { test.each([ [point(), vector2d(1, 1), point(1, 1)], [point(1, 2), vector2d(), point(1, 2)], [point(1, 2), vector2d(1, 1), point(2, 3)], ])('translated#%#', (point: Point, offset: Vector2D, result: Point) => { assert.isTrue(point.translated(offset.x, offset.y).equals(result)); }); }); describe('transform', () => { it('transform', () => { const p1 = point(4, 0); const m = matrix().translate(2, 2).scale(2, 2).rotate(radians(90)); assert.isTrue(p1.transform(m) === p1); assert.isTrue(p1.fuzzyEquals(point(2, 10))); }); }); describe('transformed', () => { it('transformed', () => { const p1 = point(4, 0); const m = matrix().translate(2, 2).scale(2, 2).rotate(radians(90)); const p2 = p1.transformed(m); assert.isTrue(p2 instanceof Point); assert.isTrue(p2.fuzzyEquals(point(2, 10))); }); }); describe('boundingRectangle', () => { test.each([ [point(), rectangle()], [point(1, 2), rectangle(vector2d(1, 2))], ])('boundingRectangle#%#', (point: Point, result: Rectangle) => { assert.isTrue(point.boundingRectangle().equals(result)); }); }); describe('containsPoint', () => { test.each([ [point(1, 1), point(1, 1), false], [point(1, 1), point(-1, 1), false], ])('containsPoint#%#', (p1: Point, p2: Point, res: boolean) => { assert.isTrue(p1.containsPoint(p2) === res); }); }); describe('containsLine', () => { test.each([ [point(1, 1), line(vector2d(0, 0), vector2d(5, 5)), false], [point(1, 1), line(vector2d(0, 0), vector2d(5, 5)), false], ])('containsLine#%#', (p1: Point, p2: Line, res: boolean) => { assert.isTrue(p1.containsLine(p2) === res); }); }); describe('containsCircle', () => { test.each([ [point(1, 1), circle(vector2d(1, 1), 0), false], [point(1, 1), circle(vector2d(1, 1), 1), false], [point(1, 1), circle(vector2d(1, 2), 0), false], ])('containsCircle#%#', (point: Point, circle: Circle, result: boolean) => { assert.isTrue(point.containsCircle(circle) === result); }); }); describe('containsRectangle', () => { test.each([ [point(1, 1), rectangle(vector2d(1, 1)), false], [point(1, 1), rectangle(vector2d(1, 1), vector2d(1, 1)), false], [point(1, 1), rectangle(vector2d(1, 2)), false], ])('containsRectangle#%#', (point: Point, rectangle: Rectangle, result: boolean) => { assert.isTrue(point.containsRectangle(rectangle) === result); }); }); describe('containsPolygon', () => { test.each([ [point(1, 1), polygon(), false], [point(1, 1), polygon([vector2d(1, 1)]), false], [point(1, 1), polygon([vector2d(1, 1), vector2d(1, 1)]), false], [point(1, 1), polygon([vector2d(1, 2)]), false], [point(1, 1), polygon([vector2d(1, 1), vector2d(1, 2)]), false], ])('containsPolygon#%#', (point: Point, polygon: Polygon, result: boolean) => { assert.isTrue(point.containsPolygon(polygon) === result); }); }); describe('containsPolyline', () => { test.each([ [point(1, 1), polyline(), false], [point(1, 1), polyline([vector2d(1, 1)]), false], [point(1, 1), polyline([vector2d(1, 1), vector2d(1, 1)]), false], [point(1, 1), polyline([vector2d(1, 2)]), false], [point(1, 1), polyline([vector2d(1, 1), vector2d(1, 2)]), false], ])('containsPolyline#%#', (point: Point, polyline: Polyline, result: boolean) => { assert.isTrue(point.containsPolyline(polyline) === result); }); }); describe('contains', () => { test.each([ [point(1, 1), point(1, 1), false], [point(1, 1), point(-1, 1), false], [point(1, 1), line(vector2d(0, 0), vector2d(5, 5)), false], [point(1, 1), line(vector2d(0, 0), vector2d(5, 5)), false], [point(1, 1), circle(vector2d(1, 1), 0), false], [point(1, 1), circle(vector2d(1, 1), 1), false], [point(1, 1), circle(vector2d(1, 2), 0), false], [point(1, 1), rectangle(vector2d(1, 1)), false], [point(1, 1), rectangle(vector2d(1, 1), vector2d(1, 1)), false], [point(1, 1), rectangle(vector2d(1, 2)), false], [point(1, 1), polygon(), false], [point(1, 1), polygon([vector2d(1, 1)]), false], [point(1, 1), polygon([vector2d(1, 1), vector2d(1, 1)]), false], [point(1, 1), polygon([vector2d(1, 2)]), false], [point(1, 1), polygon([vector2d(1, 1), vector2d(1, 2)]), false], ])('contains#%#', (shape1: Shape, shape2: Shape, res: boolean) => { assert.isTrue(shape2.contains(shape1) === res); }); }); describe('intersectsPoint', () => { test.each([ [point(1, 1), point(1, 1), true, [vector2d(1, 1)]], [point(1, 1), point(-1, 1), false, undefined], ])('intersectsPoint#%#', (p1: Point, p2: Point, result: boolean, points: Vector2D[] | undefined) => { assert.isTrue(p1.intersectsPoint(p2) === result); const intersection: any = {}; p1.intersectsPoint(p2, function (this: any, points, thisShape, otherShape) { assert.isTrue(p1 === thisShape); assert.isTrue(p2 === otherShape); this.points = this.points === undefined ? points : this.points.concat(points); return false; }, intersection); assert.isTrue(fuzzyDeepEqual(intersection.points, points), `${intersection.points} !== ${points}`); }); }); describe('intersectsSelf', () => { it('intersectsSelf', () => { assert.isFalse(point().intersectsSelf()); assert.isFalse(point(1, 2).intersectsSelf()); }); }); describe('intersects', () => { test.each([ [point(1, 1), point(1, 1), true, [vector2d(1, 1)]], [point(1, 1), point(-1, 1), false, undefined], [point(4, 4), line(vector2d(1, 1), vector2d(4, 4)), true, [vector2d(4, 4)]], [point(2, 1), line(vector2d(1, 1), vector2d(4, 4)), false, undefined], [point(5, 5), circle(vector2d(5, 5), 3), false, undefined], [point(2, 5), circle(vector2d(5, 5), 3), true, [vector2d(2, 5)]], [point(3, 6), rectangle(vector2d(3, 4), vector2d(6, 4)), true, [vector2d(3, 6)]], [point(6, 6), rectangle(vector2d(3, 4), vector2d(6, 4)), false, undefined], [point(), polygon(), false, undefined], [point(4, 4), polygon([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), true, [vector2d(4, 4)]], [point(4, 4), polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), true, [vector2d(4, 4)]], [point(6, 7), polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), false, undefined], ])('intersects#%#', (s1: Shape, s2: Shape, result: boolean, points: Vector2D[] | undefined) => { assert.isTrue(s2.intersects(s1) === result); const intersection: any = {}; s2.intersects(s1, function (this: any, points, thisShape, otherShape) { assert.isTrue(intersection === this); assert.isTrue(s1 === thisShape); assert.isTrue(s2 === otherShape); this.points = this.points === undefined ? points : this.points.concat(points); return false; }, intersection); assert.isTrue(fuzzyDeepEqual(intersection.points, points), `${intersection.points} !== ${points}`); }); }); describe('render', () => { const end = [ { set: 'strokeStyle', value: undefined, }, { set: 'fillStyle', value: undefined, } ]; test.each([ [ point(), undefined, undefined, [ { apply: 'fillRect', args: [0, 0, 1, 1] }, ...end ] ], [ point(1, 2), undefined, undefined, [ { apply: 'fillRect', args: [1, 2, 1, 1] }, ...end ] ], [ point(1, 2), 'yellow', 'black', [ { set: 'strokeStyle', value: 'yellow', }, { set: 'fillStyle', value: 'black', }, { apply: 'fillRect', args: [1, 2, 1, 1] }, ...end ] ], ])('render#%#', (p: Point, strokeStyle: string | undefined, fillStyle: string | undefined, result: any[]) => { const interceptions: any[] = []; const context = canvasRenderingContext2DSpy(interceptions); p.render(context, strokeStyle, fillStyle); assert.deepEqual(interceptions, result); }); }); describe('toVector2D', () => { it('null point', () => { const p = point(); const v = p.toVector2D(); assert.strictEqual(v.x, p.x); assert.strictEqual(v.y, p.y); assert.isTrue(v instanceof Vector2D); }); it('x, y point', () => { const p = point(-1, 1); const v = p.toVector2D(); assert.strictEqual(v.x, p.x); assert.strictEqual(v.y, p.y); assert.isTrue(v instanceof Vector2D); }); }); describe('map', () => { it('map', () => { const p = point(2.3, 3.6); const bind2nd = (fn: (arg1: T1, arg2: T2) => R, boundArg: T2) => (arg: T1) => fn(arg, boundArg); assert.isTrue(p.map(Math.ceil).equals(point(Math.ceil(p.x), Math.ceil(p.y)))); assert.isTrue(p.map(bind2nd(Math.pow, 2)).equals(point(Math.pow(p.x, 2), Math.pow(p.y, 2)))); }); }); describe('Point.create', () => { it('default constructor', () => { const p = Point.create(); assert.strictEqual(p.x, 0); assert.strictEqual(p.y, 0); }); it('x, y constructor', () => { const p = Point.create(1, -1); assert.strictEqual(p.x, 1); assert.strictEqual(p.y, -1); }); }); describe('Point.fromVector2D', () => { it('x, y vector', () => { const p = Point.fromVector2D(vector2d(-1, 1)); assert.strictEqual(p.x, -1); assert.strictEqual(p.y, 1); assert.isTrue(p instanceof Point); }); it('null vector', () => { const p = Point.fromVector2D(vector2d()); assert.strictEqual(p.x, 0); assert.strictEqual(p.y, 0); assert.isTrue(p instanceof Point); }); }); });