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.Polyline', () => { describe('constructor', () => { it('default constructor', () => { const p = new Polyline(); assert.strictEqual(p.points.length, 0); }); it('one point constructor', () => { const p = new Polyline([new Vector2D(1, 2)]); assert.strictEqual(p.points.length, 1); assert.strictEqual(p.points[0].x, 1); assert.strictEqual(p.points[0].y, 2); }); it('two points constructor', () => { const p = new Polyline([new Vector2D(1, 2), new Vector2D(3, 4)]); assert.strictEqual(p.points.length, 2); assert.strictEqual(p.points[0].x, 1); assert.strictEqual(p.points[0].y, 2); assert.strictEqual(p.points[1].x, 3); assert.strictEqual(p.points[1].y, 4); }); it('instanceof', () => { const p = new Polyline(); assert.isTrue(p instanceof Shape); assert.isTrue(p instanceof Polyline); }); }); describe('clone', function () { it('clone', function () { const p1 = polyline([vector2d(1, 2), vector2d(3, 4)]); const p2 = p1.clone(); p1.points[0].x = 5; p1.points[0].y = 6; p1.points.push(vector2d(7, 8)); assert.isTrue(p2 instanceof Polyline); assert.isTrue(p2.equals(polyline([vector2d(1, 2), vector2d(3, 4)]))); }); }); describe('getters/setters', () => { it('getters/setters', () => { const poly = polyline([vector2d(1, 2), vector2d(3, 4), vector2d(1, 4)]); assert.strictEqual(poly.size, 3); poly.size = 0; assert.strictEqual(poly.size, 0); assert.deepEqual(poly.points, []); }); }); describe('isEmpty', () => { test.each([ [polyline(), true], [polyline([]), true], [polyline([vector2d()]), false], ])('isEmpty#%#', (polyline: Polyline, result: boolean) => { assert.isTrue(polyline.isEmpty() === result); }); }); describe('first', () => { it('empty', () => { assert.isUndefined(polyline().first()); }); it('non empty', () => { assert.isTrue(polyline([vector2d(1, 2), vector2d(3, 4), vector2d(5, 6)]).first().equals(vector2d(1, 2))); }); }); describe('last', () => { it('empty', () => { assert.isUndefined(polyline().last()); }); it('non empty', () => { assert.isTrue(polyline([vector2d(1, 2), vector2d(3, 4), vector2d(5, 6)]).last().equals(vector2d(5, 6))); }); }); describe('isNull', () => { test.each([ [polyline(), true], [polyline([vector2d(1, 1)]), true], [polyline([vector2d(1, 1), vector2d(1, 1)]), true], [polyline([vector2d(1, 1), vector2d(2, 2)]), false], ])('isNull#%#', (polyline: Polyline, result: boolean) => { assert.isTrue(polyline.isNull() === result); }); }); describe('fuzzyIsNull', () => { test.each([ [polyline(), true, undefined], [polyline([vector2d(1, 1)]), true, undefined], [polyline([vector2d(1, 1), vector2d(1, 1)]), true, undefined], [polyline([vector2d(1, 1), vector2d(1.000001, 1.000001)]), true, undefined], [polyline([vector2d(1, 1), vector2d(1.000001, 1.000001)]), false, DOUBLE_EPSILON], [polyline([vector2d(1, 1), vector2d(2, 2)]), false, undefined], ])('isNull#%#', (polyline: Polyline, result: boolean, epsilon: number | undefined) => { assert.isTrue(polyline.fuzzyIsNull(epsilon) === result); }); }); describe('equals', () => { test.each([ [polyline(), polyline(), true], [polyline(), polyline([vector2d()]), false], [polyline([vector2d()]), polyline(), false], [polyline([vector2d()]), polyline([vector2d()]), true], [polyline([vector2d(1, 1)]), polyline([vector2d()]), false], ])('equals#%#', (poly1: Polyline, poly2: Polyline, result: boolean) => { assert.isTrue(poly1.equals(poly2) === result); }); }); describe('fuzzyEquals', () => { test.each([ [polyline(), polyline(), true, undefined], [polyline(), polyline([vector2d()]), false, undefined], [polyline([vector2d()]), polyline(), false, undefined], [polyline([vector2d()]), polyline([vector2d()]), true, undefined], [polyline([vector2d(1, 1)]), polyline([vector2d()]), false, undefined], [polyline([vector2d(1, 1)]), polyline([vector2d(1.000001, 1.000001)]), true, undefined], [polyline([vector2d(1, 1)]), polyline([vector2d(1.000001, 1.000001)]), false, DOUBLE_EPSILON], ])('equals#%#', (poly1: Polyline, poly2: Polyline, result: boolean, epsilon: number | undefined) => { assert.isTrue(poly1.fuzzyEquals(poly2, epsilon) === result); }); }); describe('boundingRectangle', () => { test.each([ [polyline(), rectangle()], [polyline([vector2d(5, 2), vector2d(5, 6), vector2d(2, 6)]), rectangle(vector2d(2, 2), vector2d(3, 4))], [polyline([vector2d(2, 6), vector2d(5, 2), vector2d(5, 6)]), rectangle(vector2d(2, 2), vector2d(3, 4))], [polyline([vector2d(5, 6), vector2d(2, 6), vector2d(5, 2)]), rectangle(vector2d(2, 2), vector2d(3, 4))], [polyline([vector2d(2, 6), vector2d(5, 6)]), rectangle(vector2d(2, 6), vector2d(3, 0))], [polyline([vector2d(5, 2), vector2d(5, 6)]), rectangle(vector2d(5, 2), vector2d(0, 4))], ])('boundingRectangle#%#', (polyline: Polyline, result: Rectangle) => { assert.isTrue(polyline.boundingRectangle().equals(result)); }); }); describe('translate', () => { test.each([ [polyline(), vector2d(1, 1), polyline()], [polyline([vector2d(2, 2), vector2d(2, 6), vector2d(5, 6)]), vector2d(), polyline([vector2d(2, 2), vector2d(2, 6), vector2d(5, 6)])], [polyline([vector2d(2, 2), vector2d(2, 6), vector2d(5, 6)]), vector2d(1, 1), polyline([vector2d(3, 3), vector2d(3, 7), vector2d(6, 7)])], ])('translate#%#', (polyline: Polyline, offset: Vector2D, result: Polyline) => { assert.isTrue(polyline.translate(offset.x, offset.y) === polyline); assert.isTrue(polyline.equals(result)); }); }); describe('translated', () => { test.each([ [polyline(), vector2d(1, 1), polyline()], [polyline([vector2d(2, 2), vector2d(2, 6), vector2d(5, 6)]), vector2d(), polyline([vector2d(2, 2), vector2d(2, 6), vector2d(5, 6)])], [polyline([vector2d(2, 2), vector2d(2, 6), vector2d(5, 6)]), vector2d(1, 1), polyline([vector2d(3, 3), vector2d(3, 7), vector2d(6, 7)])], ])('translated#%#', (polyline: Polyline, offset: Vector2D, result: Polyline) => { assert.isTrue(polyline.translated(offset.x, offset.y).equals(result)); }); }); describe('transform', () => { it('transform', () => { const p1 = polyline([vector2d(2, 2), vector2d(5, 2), vector2d(5, 0)]); const m = matrix().translate(2, 2).scale(2, 2).rotate(radians(90)); assert.isTrue(p1.transform(m) === p1); assert.isTrue(p1.fuzzyEquals(polyline([vector2d(-2, 6), vector2d(-2, 12), vector2d(2, 12)]))); }); }); describe('transformed', () => { it('transformed', () => { const p1 = polyline([vector2d(2, 2), vector2d(5, 2), vector2d(5, 0)]); const m = matrix().translate(2, 2).scale(2, 2).rotate(radians(90)); const p2 = p1.transformed(m); assert.isTrue(p2 instanceof Polyline); assert.isTrue(p2.fuzzyEquals(polyline([vector2d(-2, 6), vector2d(-2, 12), vector2d(2, 12)]))); }); }); describe('map', () => { it('map', () => { const pts = [vector2d(2.3, 3.6), vector2d(-1.1, 0.5), vector2d(4.2, -1.1)]; const poly = polyline(pts); const bind2nd = (fn: (arg1: T1, arg2: T2) => R, boundArg: T2) => (arg: T1) => fn(arg, boundArg); assert.isTrue(poly.map(p => p.map(Math.ceil)).equals(polyline(pts.map(p => p.map(Math.ceil))))); assert.isTrue(poly.map(p => p.map(bind2nd(Math.pow, 2))).equals(polyline(pts.map(p => p.map(bind2nd(Math.pow, 2)))))); }); }); describe('get', () => { it('get', () => { const poly = polyline([vector2d(1, 1), vector2d(2, 2), vector2d(1, 2)]); assert.isTrue(poly.get(0).equals(vector2d(1, 1))); assert.isTrue(poly.get(1).equals(vector2d(2, 2))); assert.isTrue(poly.get(2).equals(vector2d(1, 2))); assert.isUndefined(poly.get(3)); }); }); describe('set', () => { it('set', () => { const poly = polyline([vector2d(1, 1), vector2d(2, 2), vector2d(1, 2)]); poly.set(0, vector2d(-1, -1)); poly.set(2, vector2d(-1, -2)); assert.isTrue(poly.get(0).equals(vector2d(-1, -1))); assert.isTrue(poly.get(1).equals(vector2d(2, 2))); assert.isTrue(poly.get(2).equals(vector2d(-1, -2))); }); }); describe('forEach', () => { it('empty', () => { let count = 0; polyline().forEach(() => { count++; }); assert.strictEqual(count, 0); }); it('non empty', () => { const pts = [vector2d(1, 1), vector2d(2, 2), vector2d(1, 2)]; const poly = polyline(pts); let count = 0; poly.forEach((point, index, polyline) => { assert.isTrue(point.equals(pts[index])); assert.isTrue(poly === polyline); count++; }); assert.strictEqual(count, poly.size); }); }); describe('length', () => { test.each([ [polyline(), 0], [polyline([vector2d(1, 1)]), 0], [polyline([vector2d(1, 1), vector2d(4, 1)]), 3], [polyline([vector2d(1, 1), vector2d(4, 1), vector2d(4, 5)]), 7], [polyline([vector2d(1, 1), vector2d(4, 1), vector2d(4, 5), vector2d(1, 1)]), 12], ])('length#%#', (p: Polyline, result: number) => { assert.strictEqual(p.length(), result); }); }); describe('containsPoint', () => { test.each([ [polyline(), point(), false], [polyline([vector2d()]), point(), false], [polyline([vector2d(2, 1), vector2d(3, 3), vector2d(1, 3)]), point(2, 1), false], [polyline([vector2d(2, 1), vector2d(3, 3), vector2d(1, 3)]), point(1, 1), false], ])('containsPoint#%#', (polyline: Polyline, point: Point, result: boolean) => { assert.isTrue(polyline.containsPoint(point) === result); }); }); describe('containsLine', () => { test.each([ [polyline(), line(), false], [polyline([vector2d()]), line(), false], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), line(vector2d(5, 5), vector2d(7, 5)), false], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), line(vector2d(2, 7), vector2d(6, 1)), false], ])('containsLine#%#', (polyline: Polyline, line: Line, result: boolean) => { assert.isTrue(polyline.containsLine(line) === result); }); }); describe('containsCircle', () => { test.each([ [polyline(), circle(), false], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), circle(vector2d(5, 5), 1), false], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), circle(vector2d(4, 4)), false], ])('containsCircle#%#', (polyline: Polyline, circle: Circle, result: boolean) => { assert.isTrue(polyline.containsCircle(circle) === result); }); }); describe('containsRectangle', () => { test.each([ [polyline(), rectangle(), false], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), rectangle(vector2d(5, 4), vector2d(2, 2)), false], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), rectangle(vector2d(4, 4)), false], ])('containsRectangle#%#', (polyline: Polyline, rectangle: Rectangle, result: boolean) => { assert.isTrue(polyline.containsRectangle(rectangle) === result); }); }); describe('containsPolygon', () => { test.each([ [polyline(), polygon(), false], [polyline([vector2d()]), polygon(), false], [polyline(), polygon([vector2d()]), false], [polyline([vector2d()]), polygon([vector2d()]), false], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), polygon([vector2d(2, 7), vector2d(6, 1)]), false], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), polygon([vector2d(5, 6), vector2d(6, 4), vector2d(7, 6)]), false], ])('containsPolygon#%#', (polyline: Polyline, polygon: Polygon, result: boolean) => { assert.isTrue(polyline.containsPolygon(polygon) === result); }); }); describe('containsPolyline', () => { test.each([ [polyline(), polyline(), false], [polyline([vector2d()]), polyline(), false], [polyline(), polyline([vector2d()]), false], [polyline([vector2d()]), polyline([vector2d()]), false], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), polyline([vector2d(2, 7), vector2d(6, 1)]), false], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), polyline([vector2d(5, 6), vector2d(6, 4), vector2d(7, 6)]), false], ])('containsPolyline#%#', (polyline1: Polyline, polyline2: Polyline, result: boolean) => { assert.isTrue(polyline1.containsPolyline(polyline2) === result); }); }); describe('contains', () => { test.each([ [polyline(), point(), false], [polyline([vector2d()]), point(), false], [polyline([vector2d(2, 1), vector2d(3, 3), vector2d(1, 3)]), point(2, 1), false], [polyline([vector2d(2, 1), vector2d(3, 3), vector2d(1, 3)]), point(1, 1), false], [polyline(), line(), false], [polyline([vector2d()]), line(), false], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), line(vector2d(5, 5), vector2d(7, 5)), false], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), line(vector2d(2, 7), vector2d(6, 1)), false], [polyline(), circle(), false], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), circle(vector2d(5, 5), 1), false], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), circle(vector2d(4, 4)), false], [polyline(), rectangle(), false], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), rectangle(vector2d(5, 4), vector2d(2, 2)), false], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), rectangle(vector2d(4, 4)), false], [polyline(), polygon(), false], [polyline([vector2d()]), polygon(), false], [polyline(), polygon([vector2d()]), false], [polyline([vector2d()]), polygon([vector2d()]), false], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), polygon([vector2d(2, 7), vector2d(6, 1)]), false], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), polygon([vector2d(5, 6), vector2d(6, 4), vector2d(7, 6)]), false], [polyline(), polyline(), false], [polyline([vector2d()]), polyline(), false], [polyline(), polyline([vector2d()]), false], [polyline([vector2d()]), polyline([vector2d()]), false], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), polyline([vector2d(2, 7), vector2d(6, 1)]), false], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), polyline([vector2d(5, 6), vector2d(6, 4), vector2d(7, 6)]), false], ])('contains#%#', (shape1: Shape, shape2: Shape, result: boolean) => { assert.isTrue(shape2.contains(shape1) === result); }); }); describe('intersectsPoint', () => { test.each([ [polyline(), point(), false, undefined], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), point(4, 4), true, [vector2d(4, 4)]], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), point(6, 7), false, undefined], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), point(5, 5), false, undefined], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), point(6, 1), true, [vector2d(6, 1), vector2d(6, 1)]], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), point(2, 7), true, [vector2d(2, 7)]], ])('intersectsPoint#%#', (polyline: Polyline, point: Point, result: boolean, points: Vector2D[] | undefined) => { assert.isTrue(polyline.intersectsPoint(point) === result); const intersection: any = {}; polyline.intersectsPoint(point, function (this: any, points, thisShape, otherShape) { assert.isTrue(polyline === thisShape); assert.isTrue(point === otherShape); this.points = this.points === undefined ? points : this.points.concat(points); return false; }, intersection); assert.isTrue(fuzzyDeepEqual(intersection.points, points), `${intersection.points} !== ${points}`); }); }); describe('intersectsLine', () => { test.each([ [polyline(), line(), false, undefined], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), line(vector2d(3, 4), vector2d(9, 4)), true, [vector2d(4, 4), vector2d(8, 4)]], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), line(vector2d(6, 5), vector2d(6, 8)), false, undefined], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), line(vector2d(4, 1), vector2d(8, 1)), true, [vector2d(6, 1), vector2d(6, 1)]], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), line(vector2d(5, 6), vector2d(7, 6)), false, undefined], ])('intersectsLine#%#', (polyline: Polyline, line: Line, result: boolean, points: Vector2D[] | undefined) => { assert.isTrue(polyline.intersectsLine(line) === result); const intersection: any = {}; polyline.intersectsLine(line, function (this: any, points, thisShape, otherShape) { assert.isTrue(polyline === thisShape); assert.isTrue(line === otherShape); this.points = this.points === undefined ? points : this.points.concat(points); return false; }, intersection); assert.isTrue(fuzzyDeepEqual(intersection.points, points), `${intersection.points} !== ${points}`); }); }); describe('intersectsCircle', () => { test.each([ [polyline(), circle(), false, undefined], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), circle(vector2d(6, 6), 1), false, undefined], [polyline([vector2d(2, 7), vector2d(10, 1), vector2d(10, 7)]), circle(vector2d(9, 4), 1), true, [vector2d(10, 4)]], [polyline([vector2d(2, 7), vector2d(10, 1), vector2d(10, 7)]), circle(vector2d(8, 5), 1), false, undefined], [polyline([vector2d(2, 7), vector2d(10, 1), vector2d(10, 7)]), circle(vector2d(0, 7), 2), true, [vector2d(2, 7)]], ])('intersectsCircle#%#', (polyline: Polyline, circle: Circle, result: boolean, points: Vector2D[] | undefined) => { assert.isTrue(polyline.intersectsCircle(circle) === result); const intersection: any = {}; polyline.intersectsCircle(circle, function (this: any, points, thisShape, otherShape) { assert.isTrue(polyline === thisShape); assert.isTrue(circle === otherShape); this.points = this.points === undefined ? points : this.points.concat(points); return false; }, intersection); assert.isTrue(fuzzyDeepEqual(intersection.points, points), `${intersection.points} !== ${points}`); }); }); describe('intersectsRectangle', () => { test.each([ [polyline(), rectangle(), false, undefined], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), rectangle(vector2d(4, 4), vector2d(2, 2)), true, [vector2d(4, 4), vector2d(4, 4)]], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), rectangle(vector2d(5, 5), vector2d(2, 2)), false, undefined], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), rectangle(vector2d(5, 4), vector2d(2, 2)), false, undefined], ])('intersectsRectangle#%#', (polyline: Polyline, rectangle: Rectangle, result: boolean, points: Vector2D[] | undefined) => { assert.isTrue(polyline.intersectsRectangle(rectangle) === result); const intersection: any = {}; polyline.intersectsRectangle(rectangle, function (this: any, points, thisShape, otherShape) { assert.isTrue(polyline === thisShape); assert.isTrue(rectangle === otherShape); this.points = this.points === undefined ? points : this.points.concat(points); return false; }, intersection); assert.isTrue(fuzzyDeepEqual(intersection.points, points), `${intersection.points} !== ${points}`); }); }); describe('intersectsPolygon', () => { test.each([ [polyline(), polygon(), false, undefined], [polyline([vector2d()]), polygon([vector2d()]), false, undefined], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), polygon([vector2d(4, 4), vector2d(8, 4), vector2d(6, 7)]), true, [vector2d(4, 4), vector2d(4, 4), vector2d(8, 4), vector2d(8, 4)]], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), polygon([vector2d(8, 4), vector2d(6, 7), vector2d(4, 4)]), true, [vector2d(4, 4), vector2d(4, 4), vector2d(8, 4), vector2d(8, 4)]], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), polygon([vector2d(4, 4), vector2d(8, 4), vector2d(6, 7)]), true, [vector2d(4, 4), vector2d(4, 4), vector2d(8, 4), vector2d(8, 4)]], [polyline([vector2d(6, 1), vector2d(10, 7), vector2d(2, 7)]), polygon([vector2d(4, 4), vector2d(8, 4), vector2d(6, 7)]), true, [vector2d(8, 4), vector2d(8, 4), vector2d(6, 7), vector2d(6, 7)]], [polyline([vector2d(6, 1), vector2d(10, 7), vector2d(2, 7)]), polygon([vector2d(8, 4), vector2d(6, 7), vector2d(4, 4)]), true, [vector2d(8, 4), vector2d(8, 4), vector2d(6, 7), vector2d(6, 7)]], [polyline([vector2d(6, 1), vector2d(10, 7), vector2d(2, 7)]), polygon([vector2d(4, 4), vector2d(8, 4), vector2d(6, 7)]), true, [vector2d(8, 4), vector2d(8, 4), vector2d(6, 7), vector2d(6, 7)]], [polyline([vector2d(10, 7), vector2d(2, 7), vector2d(6, 1)]), polygon([vector2d(4, 4), vector2d(8, 4), vector2d(6, 7)]), true, [vector2d(6, 7), vector2d(6, 7), vector2d(4, 4), vector2d(4, 4)]], [polyline([vector2d(10, 7), vector2d(2, 7), vector2d(6, 1)]), polygon([vector2d(8, 4), vector2d(6, 7), vector2d(4, 4)]), true, [vector2d(6, 7), vector2d(6, 7), vector2d(4, 4), vector2d(4, 4)]], [polyline([vector2d(10, 7), vector2d(2, 7), vector2d(6, 1)]), polygon([vector2d(4, 4), vector2d(8, 4), vector2d(6, 7)]), true, [vector2d(6, 7), vector2d(6, 7), vector2d(4, 4), vector2d(4, 4)]], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), polygon([vector2d(6, 4), vector2d(7, 6), vector2d(5, 6)]), false, undefined], ])('intersectsPolygon#%#', (polyline: Polyline, polygon: Polygon, result: boolean, points: Vector2D[] | undefined) => { assert.isTrue(polyline.intersectsPolygon(polygon) === result); const intersection: any = {}; polyline.intersectsPolygon(polygon, function (this: any, points, thisShape, otherShape) { assert.isTrue(polyline === thisShape); assert.isTrue(polygon === otherShape); this.points = this.points === undefined ? points : this.points.concat(points); return false; }, intersection); assert.isTrue(fuzzyDeepEqual(intersection.points, points), `${intersection.points} !== ${points}`); }); }); describe('intersectsPolyline', () => { test.each([ [polyline(), polyline(), false, undefined], [polyline([vector2d()]), polyline([vector2d()]), false, undefined], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), polyline([vector2d(4, 4), vector2d(8, 4), vector2d(6, 7)]), true, [vector2d(4, 4), vector2d(8, 4), vector2d(8, 4)]], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), polyline([vector2d(8, 4), vector2d(6, 7), vector2d(4, 4)]), true, [vector2d(4, 4), vector2d(8, 4)]], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), polyline([vector2d(4, 4), vector2d(8, 4), vector2d(6, 7)]), true, [vector2d(4, 4), vector2d(8, 4), vector2d(8, 4)]], [polyline([vector2d(6, 1), vector2d(10, 7), vector2d(2, 7)]), polyline([vector2d(4, 4), vector2d(8, 4), vector2d(6, 7)]), true, [vector2d(8, 4), vector2d(8, 4), vector2d(6, 7)]], [polyline([vector2d(6, 1), vector2d(10, 7), vector2d(2, 7)]), polyline([vector2d(8, 4), vector2d(6, 7), vector2d(4, 4)]), true, [vector2d(8, 4), vector2d(6, 7), vector2d(6, 7)]], [polyline([vector2d(6, 1), vector2d(10, 7), vector2d(2, 7)]), polyline([vector2d(4, 4), vector2d(8, 4), vector2d(6, 7)]), true, [vector2d(8, 4), vector2d(8, 4), vector2d(6, 7)]], [polyline([vector2d(10, 7), vector2d(2, 7), vector2d(6, 1)]), polyline([vector2d(4, 4), vector2d(8, 4), vector2d(6, 7)]), true, [vector2d(6, 7), vector2d(4, 4)]], [polyline([vector2d(10, 7), vector2d(2, 7), vector2d(6, 1)]), polyline([vector2d(8, 4), vector2d(6, 7), vector2d(4, 4)]), true, [vector2d(6, 7), vector2d(6, 7), vector2d(4, 4)]], [polyline([vector2d(10, 7), vector2d(2, 7), vector2d(6, 1)]), polyline([vector2d(4, 4), vector2d(8, 4), vector2d(6, 7)]), true, [vector2d(6, 7), vector2d(4, 4)]], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), polyline([vector2d(6, 4), vector2d(7, 6), vector2d(5, 6)]), false, undefined], ])('intersectsPolyline#%#', (polyline1: Polyline, polyline2: Polyline, result: boolean, points: Vector2D[] | undefined) => { assert.isTrue(polyline1.intersectsPolyline(polyline2) === result); const intersection: any = {}; polyline1.intersectsPolyline(polyline2, function (this: any, points, thisShape, otherShape) { assert.isTrue(polyline1 === thisShape); assert.isTrue(polyline2 === otherShape); this.points = this.points === undefined ? points : this.points.concat(points); return false; }, intersection); assert.isTrue(fuzzyDeepEqual(intersection.points, points), `${intersection.points} !== ${points}`); }); }); describe('intersects', () => { test.each([ [polyline(), point(), false, undefined], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), point(4, 4), true, [vector2d(4, 4)]], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), point(6, 7), false, undefined], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), point(5, 5), false, undefined], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), point(6, 1), true, [vector2d(6, 1), vector2d(6, 1)]], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), point(2, 7), true, [vector2d(2, 7)]], [polyline(), line(), false, undefined], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), line(vector2d(3, 4), vector2d(9, 4)), true, [vector2d(4, 4), vector2d(8, 4)]], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), line(vector2d(6, 5), vector2d(6, 8)), false, undefined], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), line(vector2d(4, 1), vector2d(8, 1)), true, [vector2d(6, 1), vector2d(6, 1)]], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), line(vector2d(5, 6), vector2d(7, 6)), false, undefined], [polyline(), circle(), false, undefined], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), circle(vector2d(6, 6), 1), false, undefined], [polyline([vector2d(2, 7), vector2d(10, 1), vector2d(10, 7)]), circle(vector2d(9, 4), 1), true, [vector2d(10, 4)]], [polyline([vector2d(2, 7), vector2d(10, 1), vector2d(10, 7)]), circle(vector2d(8, 5), 1), false, undefined], [polyline([vector2d(2, 7), vector2d(10, 1), vector2d(10, 7)]), circle(vector2d(0, 7), 2), true, [vector2d(2, 7)]], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), rectangle(vector2d(4, 4), vector2d(2, 2)), true, [vector2d(4, 4), vector2d(4, 4)]], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), rectangle(vector2d(5, 5), vector2d(2, 2)), false, undefined], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), rectangle(vector2d(5, 4), vector2d(2, 2)), false, undefined], [polyline(), polygon(), false, undefined], [polyline([vector2d()]), polygon([vector2d()]), false, undefined], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), polygon([vector2d(4, 4), vector2d(8, 4), vector2d(6, 7)]), true, [vector2d(4, 4), vector2d(4, 4), vector2d(8, 4), vector2d(8, 4)]], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), polygon([vector2d(8, 4), vector2d(6, 7), vector2d(4, 4)]), true, [vector2d(4, 4), vector2d(4, 4), vector2d(8, 4), vector2d(8, 4)]], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), polygon([vector2d(4, 4), vector2d(8, 4), vector2d(6, 7)]), true, [vector2d(4, 4), vector2d(4, 4), vector2d(8, 4), vector2d(8, 4)]], [polyline([vector2d(6, 1), vector2d(10, 7), vector2d(2, 7)]), polygon([vector2d(4, 4), vector2d(8, 4), vector2d(6, 7)]), true, [vector2d(8, 4), vector2d(8, 4), vector2d(6, 7), vector2d(6, 7)]], [polyline([vector2d(6, 1), vector2d(10, 7), vector2d(2, 7)]), polygon([vector2d(8, 4), vector2d(6, 7), vector2d(4, 4)]), true, [vector2d(8, 4), vector2d(8, 4), vector2d(6, 7), vector2d(6, 7)]], [polyline([vector2d(6, 1), vector2d(10, 7), vector2d(2, 7)]), polygon([vector2d(4, 4), vector2d(8, 4), vector2d(6, 7)]), true, [vector2d(8, 4), vector2d(8, 4), vector2d(6, 7), vector2d(6, 7)]], [polyline([vector2d(10, 7), vector2d(2, 7), vector2d(6, 1)]), polygon([vector2d(4, 4), vector2d(8, 4), vector2d(6, 7)]), true, [vector2d(6, 7), vector2d(6, 7), vector2d(4, 4), vector2d(4, 4)]], [polyline([vector2d(10, 7), vector2d(2, 7), vector2d(6, 1)]), polygon([vector2d(8, 4), vector2d(6, 7), vector2d(4, 4)]), true, [vector2d(6, 7), vector2d(6, 7), vector2d(4, 4), vector2d(4, 4)]], [polyline([vector2d(10, 7), vector2d(2, 7), vector2d(6, 1)]), polygon([vector2d(4, 4), vector2d(8, 4), vector2d(6, 7)]), true, [vector2d(6, 7), vector2d(6, 7), vector2d(4, 4), vector2d(4, 4)]], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), polygon([vector2d(6, 4), vector2d(7, 6), vector2d(5, 6)]), false, undefined], [polyline(), polyline(), false, undefined], [polyline([vector2d()]), polyline([vector2d()]), false, undefined], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), polyline([vector2d(4, 4), vector2d(8, 4), vector2d(6, 7)]), true, [vector2d(4, 4), vector2d(8, 4), vector2d(8, 4)]], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), polyline([vector2d(8, 4), vector2d(6, 7), vector2d(4, 4)]), true, [vector2d(4, 4), vector2d(8, 4)]], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), polyline([vector2d(4, 4), vector2d(8, 4), vector2d(6, 7)]), true, [vector2d(4, 4), vector2d(8, 4), vector2d(8, 4)]], [polyline([vector2d(6, 1), vector2d(10, 7), vector2d(2, 7)]), polyline([vector2d(4, 4), vector2d(8, 4), vector2d(6, 7)]), true, [vector2d(8, 4), vector2d(8, 4), vector2d(6, 7)]], [polyline([vector2d(6, 1), vector2d(10, 7), vector2d(2, 7)]), polyline([vector2d(8, 4), vector2d(6, 7), vector2d(4, 4)]), true, [vector2d(8, 4), vector2d(6, 7), vector2d(6, 7)]], [polyline([vector2d(6, 1), vector2d(10, 7), vector2d(2, 7)]), polyline([vector2d(4, 4), vector2d(8, 4), vector2d(6, 7)]), true, [vector2d(8, 4), vector2d(8, 4), vector2d(6, 7)]], [polyline([vector2d(10, 7), vector2d(2, 7), vector2d(6, 1)]), polyline([vector2d(4, 4), vector2d(8, 4), vector2d(6, 7)]), true, [vector2d(6, 7), vector2d(4, 4)]], [polyline([vector2d(10, 7), vector2d(2, 7), vector2d(6, 1)]), polyline([vector2d(8, 4), vector2d(6, 7), vector2d(4, 4)]), true, [vector2d(6, 7), vector2d(6, 7), vector2d(4, 4)]], [polyline([vector2d(10, 7), vector2d(2, 7), vector2d(6, 1)]), polyline([vector2d(4, 4), vector2d(8, 4), vector2d(6, 7)]), true, [vector2d(6, 7), vector2d(4, 4)]], [polyline([vector2d(2, 7), vector2d(6, 1), vector2d(10, 7)]), polyline([vector2d(6, 4), vector2d(7, 6), vector2d(5, 6)]), 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('intersectsSelf', () => { test.each([ [polyline(), false, undefined], [polyline([vector2d(2, 2)]), false, undefined], [polyline([vector2d(2, 2), vector2d(8, 6)]), false, undefined], [polyline([vector2d(2, 2), vector2d(8, 6), vector2d(2, 6)]), false, undefined], [polyline([vector2d(2, 2), vector2d(8, 6), vector2d(8, 2), vector2d(2, 6)]), true, [vector2d(5, 4)]], [polyline([vector2d(2, 6), vector2d(2, 2), vector2d(8, 6), vector2d(8, 2)]), false, undefined], [polyline([vector2d(8, 2), vector2d(2, 6), vector2d(2, 2), vector2d(8, 6)]), true, [vector2d(5, 4)]], [polyline([vector2d(8, 6), vector2d(8, 2), vector2d(2, 6), vector2d(2, 2)]), false, undefined], [polyline([vector2d(2, 2), vector2d(4, 6), vector2d(6, 2), vector2d(8, 6), vector2d(10, 2), vector2d(11, 4), vector2d(1, 4)]), true, [vector2d(3, 4), vector2d(5, 4), vector2d(7, 4), vector2d(9, 4)]], ])('intersectsSelf#%#', (polyline: Polyline, result: boolean, points: Vector2D[] | undefined) => { assert.isTrue(polyline.intersectsSelf() === result); const intersection: any = {}; polyline.intersectsSelf(function (this: any, points, thisShape, otherShape) { assert.isTrue(polyline === thisShape); assert.isTrue(polyline === 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 begin = [ { apply: 'beginPath', args: [] } ]; const mid = [ { apply: 'stroke', args: [] } ]; const end = [ { set: 'strokeStyle', value: undefined, }, { set: 'fillStyle', value: undefined, } ]; test.each([ [ polyline(), undefined, undefined, [ ...end ] ], [ polyline([vector2d(1, 1)]), undefined, undefined, [ ...end ] ], [ polyline([vector2d(1, 1), vector2d(5, 3)]), undefined, undefined, [ ...begin, { apply: 'moveTo', args: [1, 1] }, { apply: 'lineTo', args: [5, 3] }, ...mid, ...end ] ], [ polyline([vector2d(1, 1), vector2d(5, 3), vector2d(4, -1)]), undefined, undefined, [ ...begin, { apply: 'moveTo', args: [1, 1] }, { apply: 'lineTo', args: [5, 3] }, { apply: 'lineTo', args: [4, -1] }, ...mid, ...end ] ], [ polyline([vector2d(1, 1), vector2d(5, 3), vector2d(4, -1)]), 'yellow', undefined, [ { set: 'strokeStyle', value: 'yellow' }, ...begin, { apply: 'moveTo', args: [1, 1] }, { apply: 'lineTo', args: [5, 3] }, { apply: 'lineTo', args: [4, -1] }, ...mid, ...end ] ], ])('render#%#', (p: Polyline, 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('Polyline.create', () => { it('default constructor', () => { const p = Polyline.create(); assert.strictEqual(p.points.length, 0); }); it('one point constructor', () => { const p = Polyline.create([new Vector2D(1, 2)]); assert.strictEqual(p.points.length, 1); assert.strictEqual(p.points[0].x, 1); assert.strictEqual(p.points[0].y, 2); }); it('two points constructor', () => { const p = Polyline.create([new Vector2D(1, 2), new Vector2D(3, 4)]); assert.strictEqual(p.points.length, 2); assert.strictEqual(p.points[0].x, 1); assert.strictEqual(p.points[0].y, 2); assert.strictEqual(p.points[1].x, 3); assert.strictEqual(p.points[1].y, 4); }); it('instanceof', () => { const p = Polyline.create(); assert.isTrue(p instanceof Shape); assert.isTrue(p instanceof Polyline); }); }); });