import { assert } from 'chai'; import { DOUBLE_EPSILON } from '../../../../lib/math/float'; import { M_2PI } from '../../../../lib/math/constants'; 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.Circle', () => { describe('constructor', () => { it('default constructor', () => { const circle = new Circle(); assert.strictEqual(circle.c.x, 0); assert.strictEqual(circle.c.y, 0); assert.strictEqual(circle.r, 0); }); it('center constructor', () => { const circle = new Circle(new Vector2D(1, 2)); assert.strictEqual(circle.c.x, 1); assert.strictEqual(circle.c.y, 2); assert.strictEqual(circle.r, 0); }); it('center, radius constructor', () => { const circle = new Circle(new Vector2D(1, 2), 3); assert.strictEqual(circle.c.x, 1); assert.strictEqual(circle.c.y, 2); assert.strictEqual(circle.r, 3); }); it('instanceof', () => { const circle = new Circle(); assert.isTrue(circle instanceof Shape); assert.isTrue(circle instanceof Circle); }); }); describe('clone', function () { it('clone', function () { const c1 = circle(vector2d(1, 2), 3); const c2 = c1.clone(); assert.isTrue(c2 instanceof Circle); assert.isTrue(c2.equals(c1)); c1.cx = 4; c1.cy = 5; c1.r = 6; assert.isTrue(c2.equals(circle(vector2d(1, 2), 3))); }); }); describe('getters/setters', () => { it('getters/setters', () => { const l = circle(vector2d(1, 2), 3); assert.strictEqual(l.cx, 1); assert.strictEqual(l.cy, 2); assert.strictEqual(l.r, 3); l.cx = -1; l.cy = -2; l.r = -3; assert.strictEqual(l.cx, -1); assert.strictEqual(l.cy, -2); assert.strictEqual(l.r, -3); }); }); describe('boundingRectangle', () => { test.each([ [circle(), rectangle()], [circle(vector2d(), 2), rectangle(vector2d(-2, -2), vector2d(4, 4))], [circle(vector2d(3, 3), 2), rectangle(vector2d(1, 1), vector2d(4, 4))], ])('boundingRectangle#%#', (circle: Circle, result: Rectangle) => { assert.isTrue(circle.boundingRectangle().equals(result)); }); }); describe('isNull', () => { test.each([ [circle(), true], [circle(vector2d(0, 0), 0), true], [circle(vector2d(0, 0), 1), false], [circle(vector2d(1, 1), 0), true], ])('isNull#%#', (circle: Circle, result: boolean) => { assert.isTrue(circle.isNull() === result); }); }); describe('fuzzyIsNull', () => { test.each([ [circle(), true, undefined], [circle(vector2d(0, 0), 0), true, undefined], [circle(vector2d(0, 0), 1), false, undefined], [circle(vector2d(1, 1), 0), true, undefined], [circle(vector2d(1, 1), 0.000001), true, undefined], [circle(vector2d(1, 1), 0.000001), false, DOUBLE_EPSILON], ])('fuzzyIsNull#%#', (circle: Circle, result: boolean, epsilon: number | undefined) => { assert.isTrue(circle.fuzzyIsNull(epsilon) === result); }); }); describe('equals', () => { test.each([ [circle(), circle(), true], [circle(vector2d(0, 0)), circle(), true], [circle(vector2d(0, 0), 0), circle(), true], [circle(undefined, undefined), circle(), true], [circle(vector2d(1, 1)), circle(), false], [circle(vector2d(1, 1)), circle(vector2d(1, 1)), true], [circle(vector2d(1, 1)), circle(vector2d(1, 2)), false], [circle(vector2d(1, 2)), circle(vector2d(1, 2)), true], [circle(vector2d(0, 0), 1), circle(), false], [circle(vector2d(0, 0), 1), circle(vector2d(0, 0), 1), true], [circle(vector2d(1, 2), 3), circle(vector2d(1, 2), 3), true], [circle(vector2d(2, 2), 3), circle(vector2d(1, 2), 3), false], ])('equals#%#', (circle1: Circle, circle2: Circle, result: boolean) => { assert.isTrue(circle1.equals(circle2) === result); }); }); describe('fuzzyEquals', () => { test.each([ [circle(), circle(), true, undefined], [circle(vector2d(0, 0)), circle(), true, undefined], [circle(vector2d(0, 0), 0), circle(), true, undefined], [circle(undefined, undefined), circle(), true, undefined], [circle(vector2d(1, 1)), circle(), false, undefined], [circle(vector2d(1, 1)), circle(vector2d(1, 1)), true, undefined], [circle(vector2d(1, 1)), circle(vector2d(1, 2)), false, undefined], [circle(vector2d(1, 2)), circle(vector2d(1, 2)), true, undefined], [circle(vector2d(0, 0), 1), circle(), false, undefined], [circle(vector2d(0, 0), 1), circle(vector2d(0, 0), 1), true, undefined], [circle(vector2d(1, 2), 3), circle(vector2d(1, 2), 3), true, undefined], [circle(vector2d(2, 2), 3), circle(vector2d(1, 2), 3), false, undefined], [circle(vector2d(1, 2), 3), circle(vector2d(1, 2), 3.000001), true, undefined], [circle(vector2d(1, 2), 3), circle(vector2d(1.000001, 2), 3), true, undefined], [circle(vector2d(1, 2), 3), circle(vector2d(1, 2), 3.000001), false, DOUBLE_EPSILON], [circle(vector2d(1, 2), 3), circle(vector2d(1.000001, 2), 3), false, DOUBLE_EPSILON], ])('fuzzyEquals#%#', (circle1: Circle, circle2: Circle, result: boolean, epsilon: number | undefined) => { assert.isTrue(circle1.fuzzyEquals(circle2, epsilon) === result); }); }); describe('translate', () => { test.each([ [circle(), vector2d(1, 1), circle(vector2d(1, 1))], [circle(vector2d(1, 2), 3), vector2d(), circle(vector2d(1, 2), 3)], [circle(vector2d(1, 2), 3), vector2d(1, 1), circle(vector2d(2, 3), 3)], ])('translate#%#', (circle: Circle, offset: Vector2D, result: Circle) => { assert.isTrue(circle.translate(offset.x, offset.y) === circle); assert.isTrue(circle.equals(result)); }); }); describe('translated', () => { test.each([ [circle(), vector2d(1, 1), circle(vector2d(1, 1))], [circle(vector2d(1, 2), 3), vector2d(), circle(vector2d(1, 2), 3)], [circle(vector2d(1, 2), 3), vector2d(1, 1), circle(vector2d(2, 3), 3)], ])('translated#%#', (circle: Circle, offset: Vector2D, result: Circle) => { assert.isTrue(circle.translated(offset.x, offset.y).equals(result)); }); }); describe('transform', () => { it('transform', () => { const c1 = circle(vector2d(2, 0), 3); const m = matrix().translate(2, 2).scale(2, 2).rotate(radians(90)); assert.isTrue(c1.transform(m) === c1); assert.isTrue(c1.fuzzyEquals(circle(vector2d(2, 6), 6))); }); }); describe('transformed', () => { it('transformed', () => { const c1 = circle(vector2d(2, 0), 3); const m = matrix().translate(2, 2).scale(2, 2).rotate(radians(90)); const c2 = c1.transformed(m); assert.isTrue(c2 instanceof Circle); assert.isTrue(c2.fuzzyEquals(circle(vector2d(2, 6), 6))); }); }); describe('map', () => { it('map', () => { const c = circle(vector2d(2.3, 3.6), 0.5); const bind2nd = (fn: (arg1: T1, arg2: T2) => R, boundArg: T2) => (arg: T1) => fn(arg, boundArg); assert.isTrue(c.map(p => p.map(Math.ceil)).equals(circle(c.c.map(Math.ceil), Math.ceil(c.r)))); assert.isTrue(c.map(p => p.map(bind2nd(Math.pow, 2))).equals(circle(c.c.map(bind2nd(Math.pow, 2)), Math.pow(c.r, 2)))); }); }); describe('containsPoint', () => { test.each([ [circle(vector2d(5, 5), 3), point(5, 5), true], [circle(vector2d(5, 5), 3), point(2, 5), false], [circle(vector2d(5, 5), 3), point(4, 6), true], [circle(vector2d(5, 5), 3), point(2.5, 2.5), false], [circle(vector2d(5, 5), 3), point(-1, -1), false], ])('containsPoint#%#', (circle: Circle, point: Point, result: boolean) => { assert.isTrue(circle.containsPoint(point) === result); }); }); describe('containsLine', () => { test.each([ [circle(vector2d(5, 5), 3), line(vector2d(4, 4), vector2d(6, 6)), true], [circle(vector2d(5, 5), 3), line(vector2d(2, 2), vector2d(4, 4)), false], [circle(vector2d(5, 5), 3), line(vector2d(5, 2), vector2d(5, 8)), false], [circle(vector2d(5, 5), 3), line(vector2d(5, 1), vector2d(5, 8)), false], ])('containsLine#%#', (circle: Circle, line: Line, result: boolean) => { assert.isTrue(circle.containsLine(line) === result); }); }); describe('containsCircle', () => { test.each([ [circle(vector2d(5, 5), 3), circle(vector2d(5, 5), 2), true], [circle(vector2d(5, 5), 3), circle(vector2d(3, 5), 1), false], [circle(vector2d(5, 5), 3), circle(vector2d(5, 5), 3), false], [circle(vector2d(5, 5), 3), circle(vector2d(3, 3), 1), false], [circle(vector2d(5, 5), 3), circle(vector2d(10, 5), 2), false], [circle(vector2d(5, 5), 3), circle(vector2d(5, 5), 5), false], [circle(vector2d(5, 5), 3), circle(vector2d(0, 0), 2), false], ])('containsCircle#%#', (circle1: Circle, circle2: Circle, result: boolean) => { assert.isTrue(circle1.containsCircle(circle2) === result); }); }); describe('containsRectangle', () => { test.each([ [circle(vector2d(6, 6), 4), rectangle(vector2d(2, 8), vector2d(4, 1)), false], [circle(vector2d(6, 6), 4), rectangle(vector2d(2, 9), vector2d(4, -1)), false], [circle(vector2d(6, 6), 4), rectangle(vector2d(6, 9), vector2d(-4, -1)), false], [circle(vector2d(6, 6), 4), rectangle(vector2d(6, 8), vector2d(-4, 1)), false], [circle(vector2d(6, 6), 4), rectangle(vector2d(3, 4), vector2d(6, 4)), true], [circle(vector2d(6, 6), 4), rectangle(vector2d(3, 8), vector2d(6, -4)), true], [circle(vector2d(6, 6), 4), rectangle(vector2d(9, 8), vector2d(-6, -4)), true], [circle(vector2d(6, 6), 4), rectangle(vector2d(9, 4), vector2d(-6, 4)), true], [circle(vector2d(6, 6), 4), rectangle(vector2d(2, 2), vector2d(8, 8)), false], [circle(vector2d(6, 6), 4), rectangle(vector2d(6, 4), vector2d(4, 1)), false], [circle(vector2d(6, 6)), rectangle(vector2d(6, 6)), false], [circle(vector2d(6, 6)), rectangle(vector2d(6, 6), vector2d(1, 1)), false], [circle(vector2d(6, 6), 1), rectangle(vector2d(6, 6)), true], [circle(vector2d(5, 5)), rectangle(vector2d(6, 6)), false], [circle(), rectangle(), false], ])('containsRectangle#%#', (circle: Circle, rectangle: Rectangle, result: boolean) => { assert.isTrue(circle.containsRectangle(rectangle) === result); }); }); describe('containsPolygon', () => { test.each([ [circle(), polygon(), false], [circle(vector2d(5, 5), 3), polygon(), false], [circle(vector2d(5, 5), 3), polygon([vector2d(3, 5), vector2d(6, 3), vector2d(5, 7)]), true], [circle(vector2d(5, 5), 3), polygon([vector2d(3, 5), vector2d(6, 3)]), true], [circle(vector2d(5, 5), 3), polygon([vector2d(3, 5)]), true], [circle(vector2d(5, 5), 3), polygon([vector2d(2, 5), vector2d(8, 5), vector2d(5, 5)]), false], [circle(vector2d(10, 3), 1), polygon([vector2d(3, 5), vector2d(6, 3), vector2d(5, 7)]), false], [circle(vector2d(5, 5), 3), polygon([vector2d(11, 7), vector2d(11, 9), vector2d(9, 9)]), false], [circle(vector2d(5, 5), 3), polygon([vector2d(11, 7), vector2d(11, 9), vector2d(9, 9)]), false], [circle(vector2d(5, 5), 3), polygon([vector2d(7, 4), vector2d(11, 5), vector2d(7, 6)]), false], [circle(vector2d(5, 5), 3), polygon([vector2d(1, 1), vector2d(9, 1), vector2d(9, 9), vector2d(1, 9)]), false], ])('containsPolygon#%#', (circle: Circle, polygon: Polygon, result: boolean) => { assert.isTrue(circle.containsPolygon(polygon) === result); }); }); describe('containsPolyline', () => { test.each([ [circle(), polyline(), false], [circle(vector2d(5, 5), 3), polyline(), false], [circle(vector2d(5, 5), 3), polyline([vector2d(3, 5), vector2d(6, 3), vector2d(5, 7)]), true], [circle(vector2d(5, 5), 3), polyline([vector2d(3, 5), vector2d(6, 3)]), true], [circle(vector2d(5, 5), 3), polyline([vector2d(3, 5)]), true], [circle(vector2d(5, 5), 3), polyline([vector2d(2, 5), vector2d(8, 5), vector2d(5, 5)]), false], [circle(vector2d(10, 3), 1), polyline([vector2d(3, 5), vector2d(6, 3), vector2d(5, 7)]), false], [circle(vector2d(5, 5), 3), polyline([vector2d(11, 7), vector2d(11, 9), vector2d(9, 9)]), false], [circle(vector2d(5, 5), 3), polyline([vector2d(11, 7), vector2d(11, 9), vector2d(9, 9)]), false], [circle(vector2d(5, 5), 3), polyline([vector2d(7, 4), vector2d(11, 5), vector2d(7, 6)]), false], [circle(vector2d(5, 5), 3), polyline([vector2d(1, 1), vector2d(9, 1), vector2d(9, 9), vector2d(1, 9)]), false], ])('containsPolyline#%#', (circle: Circle, polyline: Polyline, result: boolean) => { assert.isTrue(circle.containsPolyline(polyline) === result); }); }); describe('contains', () => { test.each([ [circle(vector2d(5, 5), 3), point(5, 5), true], [circle(vector2d(5, 5), 3), point(2, 5), false], [circle(vector2d(5, 5), 3), point(4, 6), true], [circle(vector2d(5, 5), 3), point(2.5, 2.5), false], [circle(vector2d(5, 5), 3), point(-1, -1), false], [circle(vector2d(5, 5), 3), line(vector2d(4, 4), vector2d(6, 6)), true], [circle(vector2d(5, 5), 3), line(vector2d(2, 2), vector2d(4, 4)), false], [circle(vector2d(5, 5), 3), line(vector2d(5, 2), vector2d(5, 8)), false], [circle(vector2d(5, 5), 3), line(vector2d(5, 1), vector2d(5, 8)), false], [circle(vector2d(5, 5), 3), circle(vector2d(5, 5), 2), true], [circle(vector2d(5, 5), 3), circle(vector2d(3, 5), 1), false], [circle(vector2d(5, 5), 3), circle(vector2d(5, 5), 3), false], [circle(vector2d(5, 5), 3), circle(vector2d(3, 3), 1), false], [circle(vector2d(5, 5), 3), circle(vector2d(10, 5), 2), false], [circle(vector2d(5, 5), 3), circle(vector2d(5, 5), 5), false], [circle(vector2d(5, 5), 3), circle(vector2d(0, 0), 2), false], [circle(vector2d(6, 6), 4), rectangle(vector2d(2, 8), vector2d(4, 1)), false], [circle(vector2d(6, 6), 4), rectangle(vector2d(2, 9), vector2d(4, -1)), false], [circle(vector2d(6, 6), 4), rectangle(vector2d(6, 9), vector2d(-4, -1)), false], [circle(vector2d(6, 6), 4), rectangle(vector2d(6, 8), vector2d(-4, 1)), false], [circle(vector2d(6, 6), 4), rectangle(vector2d(3, 4), vector2d(6, 4)), true], [circle(vector2d(6, 6), 4), rectangle(vector2d(3, 8), vector2d(6, -4)), true], [circle(vector2d(6, 6), 4), rectangle(vector2d(9, 8), vector2d(-6, -4)), true], [circle(vector2d(6, 6), 4), rectangle(vector2d(9, 4), vector2d(-6, 4)), true], [circle(vector2d(6, 6), 4), rectangle(vector2d(2, 2), vector2d(8, 8)), false], [circle(vector2d(6, 6), 4), rectangle(vector2d(6, 4), vector2d(4, 1)), false], [circle(vector2d(6, 6)), rectangle(vector2d(6, 6)), false], [circle(vector2d(6, 6)), rectangle(vector2d(6, 6), vector2d(1, 1)), false], [circle(vector2d(6, 6), 1), rectangle(vector2d(6, 6)), true], [circle(vector2d(5, 5)), rectangle(vector2d(6, 6)), false], [circle(), rectangle(), false], [circle(), polygon(), false], [circle(vector2d(5, 5), 3), polygon(), false], [circle(vector2d(5, 5), 3), polygon([vector2d(3, 5), vector2d(6, 3), vector2d(5, 7)]), true], [circle(vector2d(5, 5), 3), polygon([vector2d(3, 5), vector2d(6, 3)]), true], [circle(vector2d(5, 5), 3), polygon([vector2d(3, 5)]), true], [circle(vector2d(5, 5), 3), polygon([vector2d(2, 5), vector2d(8, 5), vector2d(5, 5)]), false], [circle(vector2d(10, 3), 1), polygon([vector2d(3, 5), vector2d(6, 3), vector2d(5, 7)]), false], [circle(vector2d(5, 5), 3), polygon([vector2d(11, 7), vector2d(11, 9), vector2d(9, 9)]), false], [circle(vector2d(5, 5), 3), polygon([vector2d(11, 7), vector2d(11, 9), vector2d(9, 9)]), false], [circle(vector2d(5, 5), 3), polygon([vector2d(7, 4), vector2d(11, 5), vector2d(7, 6)]), false], [circle(vector2d(5, 5), 3), polygon([vector2d(1, 1), vector2d(9, 1), vector2d(9, 9), vector2d(1, 9)]), false], ])('contains#%#', (shape1: Shape, shape2: Shape, result: boolean) => { assert.isTrue(shape2.contains(shape1) === result); }); }); describe('intersectsPoint', () => { test.each([ [circle(vector2d(5, 5), 3), point(5, 5), false, undefined], [circle(vector2d(5, 5), 3), point(2, 5), true, [vector2d(2, 5)]], [circle(vector2d(5, 5), 3), point(4, 6), false, undefined], [circle(vector2d(5, 5), 3), point(2.5, 2.5), false, undefined], ])('intersectsPoint#%#', (circle: Circle, point: Point, result: boolean, points: Vector2D[] | undefined) => { assert.isTrue(circle.intersectsPoint(point) === result); const intersection: any = {}; circle.intersectsPoint(point, function (this: any, points, thisShape, otherShape) { assert.isTrue(circle === 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}`); }); it('intersectsPoint#callbackReturnsTrue', () => { assert.isTrue(circle(vector2d(5, 5), 3).intersectsPoint(point(2, 5), function () { return true; })); assert.isFalse(circle(vector2d(5, 5), 3).intersectsPoint(point(5, 5), function () { return true; })); }); }); describe('intersectsLine', () => { test.each([ [circle(vector2d(5, 5), 3), line(vector2d(4, 4), vector2d(6, 6)), false, undefined], [circle(vector2d(5, 5), 3), line(vector2d(-1, -1), vector2d(2, 2)), false, undefined], [circle(vector2d(5, 5), 3), line(vector2d(3, 1), vector2d(1, 4)), false, undefined], [circle(vector2d(5, 5), 3), line(), false, undefined], [circle(vector2d(5, 5), 3), line(vector2d(2, 5), vector2d(2, 5)), true, [vector2d(2, 5)]], [circle(vector2d(5, 5), 3), line(vector2d(1, 5), vector2d(3, 5)), true, [vector2d(2, 5)]], [circle(vector2d(5, 5), 3), line(vector2d(2, 2), vector2d(2, 8)), true, [vector2d(2, 5)]], [circle(vector2d(5, 5), 3), line(vector2d(5, 2), vector2d(5, 8)), true, [vector2d(5, 2), vector2d(5, 8)]], [circle(vector2d(5, 5), 3), line(vector2d(5, 1), vector2d(5, 8)), true, [vector2d(5, 2), vector2d(5, 8)]], ])('intersectsLine#%#', (circle: Circle, line: Line, result: boolean, points: Vector2D[] | undefined) => { assert.isTrue(circle.intersectsLine(line) === result); const intersection: any = {}; circle.intersectsLine(line, function (this: any, points, thisShape, otherShape) { assert.isTrue(circle === 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}`); }); it('intersectsLine#callbackReturnsTrue', () => { assert.isTrue(circle(vector2d(5, 5), 3).intersectsLine(line(vector2d(2, 5), vector2d(2, 5)), function () { return true; })); assert.isFalse(circle(vector2d(5, 5), 3).intersectsLine(line(vector2d(4, 4), vector2d(6, 6)), function () { return true; })); }); }); describe('intersectsCircle', () => { test.each([ [circle(vector2d(5, 5), 3), circle(vector2d(5, 5), 2), false, undefined], [circle(vector2d(5, 5), 3), circle(vector2d(3, 5), 1), true, [vector2d(2, 5)]], [circle(vector2d(5, 5), 3), circle(vector2d(5, 5), 3), true, []], [circle(vector2d(5, 5), 3), circle(vector2d(8, 8), 3), true, [vector2d(5, 8), vector2d(8, 5)]], [circle(vector2d(5, 5), 3), circle(vector2d(10, 5), 1), false, undefined], [circle(vector2d(5, 5), 3), circle(vector2d(5, 5), 5), false, undefined], ])('intersectsCircle#%#', (circle1: Circle, circle2: Circle, result: boolean, points: Vector2D[] | undefined) => { assert.isTrue(circle1.intersectsCircle(circle2) === result); const intersection: any = {}; circle1.intersectsCircle(circle2, function (this: any, points, thisShape, otherShape) { assert.isTrue(circle1 === thisShape); assert.isTrue(circle2 === otherShape); this.points = this.points === undefined ? points : this.points.concat(points); return false; }, intersection); assert.isTrue(fuzzyDeepEqual(intersection.points, points), `${intersection.points} !== ${points}`); }); it('intersectsCircle#callbackReturnsTrue', () => { assert.isTrue(circle(vector2d(5, 5), 3).intersectsCircle(circle(vector2d(3, 5), 1), function () { return true; })); assert.isFalse(circle(vector2d(5, 5), 3).intersectsCircle(circle(vector2d(5, 5), 2), function () { return true; })); }); }); describe('intersects', () => { test.each([ [circle(vector2d(5, 5), 3), point(5, 5), false, undefined], [circle(vector2d(5, 5), 3), point(2, 5), true, [vector2d(2, 5)]], [circle(vector2d(5, 5), 3), point(4, 6), false, undefined], [circle(vector2d(5, 5), 3), point(2.5, 2.5), false, undefined], [circle(vector2d(5, 5), 3), line(vector2d(4, 4), vector2d(6, 6)), false, undefined], [circle(vector2d(5, 5), 3), line(vector2d(-1, -1), vector2d(2, 2)), false, undefined], [circle(vector2d(5, 5), 3), line(vector2d(3, 1), vector2d(1, 4)), false, undefined], [circle(vector2d(5, 5), 3), line(), false, undefined], [circle(vector2d(5, 5), 3), line(vector2d(2, 5), vector2d(2, 5)), true, [vector2d(2, 5)]], [circle(vector2d(5, 5), 3), line(vector2d(1, 5), vector2d(3, 5)), true, [vector2d(2, 5)]], [circle(vector2d(5, 5), 3), line(vector2d(2, 2), vector2d(2, 8)), true, [vector2d(2, 5)]], [circle(vector2d(5, 5), 3), line(vector2d(5, 2), vector2d(5, 8)), true, [vector2d(5, 2), vector2d(5, 8)]], [circle(vector2d(5, 5), 3), line(vector2d(5, 1), vector2d(5, 8)), true, [vector2d(5, 2), vector2d(5, 8)]], [circle(vector2d(5, 5), 3), circle(vector2d(5, 5), 2), false, undefined], [circle(vector2d(5, 5), 3), circle(vector2d(3, 5), 1), true, [vector2d(2, 5)]], [circle(vector2d(5, 5), 3), circle(vector2d(5, 5), 3), true, []], [circle(vector2d(5, 5), 3), circle(vector2d(8, 8), 3), true, [vector2d(5, 8), vector2d(8, 5)]], [circle(vector2d(5, 5), 3), circle(vector2d(10, 5), 1), false, undefined], [circle(vector2d(5, 5), 3), circle(vector2d(5, 5), 5), false, undefined], [circle(vector2d(3, 6), 1), rectangle(vector2d(3, 4), vector2d(6, 4)), true, [vector2d(3, 7), vector2d(3, 5)]], [circle(vector2d(6, 6), 1), rectangle(vector2d(3, 4), vector2d(6, 4)), false, undefined], [circle(vector2d(9, 4), 1), polygon([vector2d(2, 7), vector2d(10, 1), vector2d(10, 7)]), true, [vector2d(10, 4)]], [circle(vector2d(8, 5), 1), polygon([vector2d(2, 7), vector2d(10, 1), vector2d(10, 7)]), false, undefined], [circle(vector2d(9, 4), 1), polyline([vector2d(2, 7), vector2d(10, 1), vector2d(10, 7)]), true, [vector2d(10, 4)]], [circle(vector2d(8, 5), 1), polyline([vector2d(2, 7), vector2d(10, 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('intersectsSelf', () => { it('intersectsSelf', () => { assert.isFalse(circle().intersectsSelf()); assert.isFalse(circle(vector2d(1, 2), 3).intersectsSelf()); }); }); describe('render', () => { const begin = [ { apply: 'beginPath', args: [] } ]; const end = [ { set: 'strokeStyle', value: undefined, }, { set: 'fillStyle', value: undefined, } ]; test.each([ [ circle(), undefined, undefined, [ ...begin, { apply: 'arc', args: [0, 0, 0, 0, M_2PI, false] }, { apply: 'stroke', args: [] }, ...end ] ], [ circle(vector2d(1, 2), 3), undefined, undefined, [ ...begin, { apply: 'arc', args: [1, 2, 3, 0, M_2PI, false] }, { apply: 'stroke', args: [] }, ...end ] ], [ circle(vector2d(1, 2), 3), 'yellow', undefined, [ { set: 'strokeStyle', value: 'yellow', }, ...begin, { apply: 'arc', args: [1, 2, 3, 0, M_2PI, false] }, { apply: 'stroke', args: [] }, ...end ] ], [ circle(vector2d(1, 2), 3), undefined, 'yellow', [ { set: 'fillStyle', value: 'yellow', }, ...begin, { apply: 'arc', args: [1, 2, 3, 0, M_2PI, false] }, { apply: 'fill', args: [] }, ...end ] ], [ circle(vector2d(1, 2), 3), 'yellow', 'black', [ { set: 'strokeStyle', value: 'yellow', }, { set: 'fillStyle', value: 'black', }, ...begin, { apply: 'arc', args: [1, 2, 3, 0, M_2PI, false] }, { apply: 'fill', args: [] }, { apply: 'stroke', args: [] }, ...end ] ], ])('render#%#', (c: Circle, strokeStyle: string | undefined, fillStyle: string | undefined, result: any[]) => { const interceptions: any[] = []; const context = canvasRenderingContext2DSpy(interceptions); c.render(context, strokeStyle, fillStyle); assert.deepEqual(interceptions, result); }); }); describe('Circle.create', () => { it('default create', () => { const circle = Circle.create(); assert.strictEqual(circle.c.x, 0); assert.strictEqual(circle.c.y, 0); assert.strictEqual(circle.r, 0); }); it('center constructor', () => { const circle = Circle.create(new Vector2D(1, 2)); assert.strictEqual(circle.c.x, 1); assert.strictEqual(circle.c.y, 2); assert.strictEqual(circle.r, 0); }); it('center, radius constructor', () => { const circle = Circle.create(new Vector2D(1, 2), 3); assert.strictEqual(circle.c.x, 1); assert.strictEqual(circle.c.y, 2); assert.strictEqual(circle.r, 3); }); }); });