// const MockPointer = require('../interaction/MockPointer'); const { Renderer, BatchRenderer } = require('@pixi/core'); const { Graphics } = require('../'); const { BLEND_MODES } = require('@pixi/constants'); const { Point } = require('@pixi/math'); const { skipHello } = require('@pixi/utils'); Renderer.registerPlugin('batch', BatchRenderer); skipHello(); function withGL(fn) { return !process.env.DISABLE_WEBGL ? (fn || true) : undefined; } describe('Graphics', function () { describe('constructor', function () { it('should set defaults', function () { const graphics = new Graphics(); expect(graphics.fill.color).to.be.equals(0xFFFFFF); expect(graphics.fill.alpha).to.be.equals(1); expect(graphics.line.width).to.be.equals(0); expect(graphics.line.color).to.be.equals(0); expect(graphics.tint).to.be.equals(0xFFFFFF); expect(graphics.blendMode).to.be.equals(BLEND_MODES.NORMAL); }); }); describe('lineTo', function () { it('should return correct bounds - north', function () { const graphics = new Graphics(); graphics.lineStyle(1); graphics.moveTo(0, 0); graphics.lineTo(0, 10); expect(graphics.width).to.be.below(1.00001); expect(graphics.width).to.be.above(0.99999); expect(graphics.height).to.be.equals(10); }); it('should return correct bounds - south', function () { const graphics = new Graphics(); graphics.moveTo(0, 0); graphics.lineStyle(1); graphics.lineTo(0, -10); expect(graphics.width).to.be.below(1.00001); expect(graphics.width).to.be.above(0.99999); expect(graphics.height).to.be.equals(10); }); it('should return correct bounds - east', function () { const graphics = new Graphics(); graphics.moveTo(0, 0); graphics.lineStyle(1); graphics.lineTo(10, 0); expect(graphics.height).to.be.equals(1); expect(graphics.width).to.be.equals(10); }); it('should return correct bounds - west', function () { const graphics = new Graphics(); graphics.moveTo(0, 0); graphics.lineStyle(1); graphics.lineTo(-10, 0); expect(graphics.height).to.be.above(0.9999); expect(graphics.height).to.be.below(1.0001); expect(graphics.width).to.be.equals(10); }); it('should return correct bounds when stacked with circle', function () { const graphics = new Graphics(); graphics.beginFill(0xFF0000); graphics.drawCircle(50, 50, 50); graphics.endFill(); expect(graphics.width).to.be.equals(100); expect(graphics.height).to.be.equals(100); graphics.lineStyle(20, 0); graphics.moveTo(25, 50); graphics.lineTo(75, 50); expect(graphics.width).to.be.equals(100); expect(graphics.height).to.be.equals(100); }); it('should return correct bounds when square', function () { const graphics = new Graphics(); graphics.lineStyle(20, 0, 0.5); graphics.moveTo(0, 0); graphics.lineTo(50, 0); graphics.lineTo(50, 50); graphics.lineTo(0, 50); graphics.lineTo(0, 0); expect(graphics.width).to.be.equals(70); expect(graphics.height).to.be.equals(70); }); it('should ignore duplicate calls', function () { const graphics = new Graphics(); graphics.moveTo(0, 0); graphics.lineTo(0, 0); graphics.lineTo(10, 0); graphics.lineTo(10, 0); expect(graphics.currentPath.points).to.deep.equal([0, 0, 10, 0]); }); describe('lineJoin', function () { describe('miter', function () { it('is miter by default (backwards compatible)', function () { // given const graphics = new Graphics(); // then expect(graphics.line.lineJoin).to.be.equal('miter'); }); it('clockwise miter', withGL(function () { // given const renderer = new Renderer({ width: 200, height: 200 }); const graphics = new Graphics(); graphics.lineStyle(2, 0, 1, 0.5); graphics.line.lineJoin = 'miter'; graphics.moveTo(0, 0); graphics.lineTo(50, 0); graphics.lineTo(50, 50); // when renderer.render(graphics); // then const points = graphics.geometry.points; // 6 control points, xy each expect(points.length / 2, 'number of control points is not right').to.be.equal(6); expect(points[0], 'x1').to.be.eql(0); expect(points[1], 'y1').to.be.eql(1); expect(points[2], 'x2').to.be.eql(0); expect(points[3], 'y2').to.be.eql(-1); expect(points[4], 'x3').to.be.eql(49); expect(points[5], 'y3').to.be.eql(1); expect(points[6], 'x4').to.be.eql(51); expect(points[7], 'y4').to.be.eql(-1); expect(points[8], 'x5').to.be.eql(49); expect(points[9], 'y5').to.be.eql(50); expect(points[10], 'x6').to.be.eql(51); expect(points[11], 'y6').to.be.eql(50); })); it('counterclockwise miter', withGL(function () { // given const renderer = new Renderer({ width: 200, height: 200 }); const graphics = new Graphics(); graphics.lineStyle(2, 0, 1, 0.5); graphics.line.lineJoin = 'miter'; graphics.moveTo(0, 0); graphics.lineTo(50, 0); graphics.lineTo(50, -50); // when renderer.render(graphics); // then const points = graphics.geometry.points; // 6 control points, xy each expect(points.length / 2, 'number of control points is not right').to.be.equal(6); expect(points[0], 'x1').to.be.eql(0); expect(points[1], 'y1').to.be.eql(1); expect(points[2], 'x2').to.be.eql(0); expect(points[3], 'y2').to.be.eql(-1); expect(points[4], 'x3').to.be.eql(51); expect(points[5], 'y3').to.be.eql(1); expect(points[6], 'x4').to.be.eql(49); expect(points[7], 'y4').to.be.eql(-1); expect(points[8], 'x5').to.be.eql(51); expect(points[9], 'y5').to.be.eql(-50); expect(points[10], 'x6').to.be.eql(49); expect(points[11], 'y6').to.be.eql(-50); })); it('flat line miter', withGL(function () { // given const renderer = new Renderer({ width: 200, height: 200 }); const graphics = new Graphics(); graphics.lineStyle(2, 0, 1, 0.5); graphics.line.lineJoin = 'miter'; graphics.moveTo(0, 0); graphics.lineTo(50, 0); graphics.lineTo(100, 0); // when renderer.render(graphics); // then const points = graphics.geometry.points; // 6 control points, xy each expect(points.length / 2, 'number of control points is not right').to.be.equal(6); expect(points[0], 'x1').to.be.eql(0); expect(points[1], 'y1').to.be.eql(1); expect(points[2], 'x2').to.be.eql(0); expect(points[3], 'y2').to.be.eql(-1); expect(points[4], 'x3').to.be.eql(50); expect(points[5], 'y3').to.be.eql(1); expect(points[6], 'x5').to.be.eql(50); expect(points[7], 'y5').to.be.eql(-1); expect(points[8], 'x7').to.be.eql(100); expect(points[9], 'y7').to.be.eql(1); expect(points[10], 'x8').to.be.eql(100); expect(points[11], 'y8').to.be.eql(-1); })); it('very sharp clockwise miter falling back to bevel', withGL(function () { // given const p1 = [0, 0]; const p2 = [50, 0]; const p3 = [0, 2]; // normalized perpendicular lines const perp1 = [0, 0.5]; const perp2 = [0.019984019174435787, 0.4996004793608947]; const anchor = [24.990003996803196, 0.5]; const renderer = new Renderer({ width: 200, height: 200 }); const graphics = new Graphics(); graphics.lineStyle(1, 0, 1, 0.5); graphics.line.lineJoin = 'miter'; graphics.moveTo(p1[0], p1[1]); graphics.lineTo(p2[0], p2[1]); // when graphics.lineTo(p3[0], p3[1]); renderer.render(graphics); // then const points = graphics.geometry.points; // 8 control points, xy each expect(points.length / 2, 'number of control points is not right').to.be.equal(8); expect(points[0], 'x1').to.be.eql(p1[0] + perp1[0]); expect(points[1], 'y1').to.be.eql(p1[1] + perp1[1]); expect(points[2], 'x2').to.be.eql(p1[0] - perp1[0]); expect(points[3], 'y2').to.be.eql(p1[1] - perp1[1]); expect(points[4], 'x3').to.be.eql(anchor[0]); expect(points[5], 'y3').to.be.eql(anchor[1]); expect(points[6], 'x4').to.be.eql(p2[0] - perp1[0]); expect(points[7], 'y4').to.be.eql(p2[1] - perp1[1]); expect(points[8], 'x5').to.be.eql(anchor[0]); expect(points[9], 'y5').to.be.eql(anchor[1]); expect(points[10], 'x6').to.be.eql(p2[0] + perp2[0]); expect(points[11], 'y6').to.be.eql(p2[1] + perp2[1]); expect(points[12], 'x7').to.be.eql(p3[0] - perp2[0]); expect(points[13], 'y7').to.be.eql(p3[1] - perp2[1]); expect(points[14], 'x8').to.be.eql(p3[0] + perp2[0]); expect(points[15], 'y8').to.be.eql(p3[1] + perp2[1]); })); it('very sharp counterclockwise miter falling back to bevel', withGL(function () { // given const p1 = [0, 0]; const p2 = [50, 0]; const p3 = [0, -2]; // normalized perpendicular vectors const perp1 = [0, 0.5]; const perp2 = [0.019984019174435787, -0.4996004793608947]; const anchor = [24.990003996803196, -0.5]; const renderer = new Renderer({ width: 200, height: 200 }); const graphics = new Graphics(); graphics.lineStyle(1, 0, 1, 0.5); graphics.line.lineJoin = 'miter'; graphics.moveTo(p1[0], p1[1]); graphics.lineTo(p2[0], p2[1]); // when graphics.lineTo(p3[0], p3[1]); renderer.render(graphics); // then const points = graphics.geometry.points; // 8 control points, xy each expect(points.length / 2, 'number of control points is not right').to.be.equal(8); expect(points[0], 'x1').to.be.eql(p1[0] + perp1[0]); expect(points[1], 'y1').to.be.eql(p1[1] + perp1[1]); expect(points[2], 'x2').to.be.eql(p1[0] - perp1[0]); expect(points[3], 'y2').to.be.eql(p1[1] - perp1[1]); expect(points[4], 'x3').to.be.eql(p2[0] + perp1[0]); expect(points[5], 'y3').to.be.eql(p2[1] + perp1[1]); expect(points[6], 'x4').to.be.eql(anchor[0]); expect(points[7], 'y4').to.be.eql(anchor[1]); expect(points[8], 'x5').to.be.eql(p2[0] + perp2[0]); expect(points[9], 'y5').to.be.eql(p2[1] + perp2[1]); expect(points[10], 'x6').to.be.eql(anchor[0]); expect(points[11], 'y6').to.be.eql(anchor[1]); expect(points[12], 'x7').to.be.eql(p3[0] + perp2[0]); expect(points[13], 'y7').to.be.eql(p3[1] + perp2[1]); expect(points[14], 'x8').to.be.eql(p3[0] - perp2[0]); expect(points[15], 'y8').to.be.eql(p3[1] - perp2[1]); })); it('miter join paralel lines', withGL(function () { // given const p1 = [0, 0]; const p2 = [50, 0]; const p3 = [100, 0]; // normalized perpendicular vectors const perp1 = [0, 1]; const perp2 = [0, -1]; const renderer = new Renderer({ width: 200, height: 200 }); const graphicsMiter = new Graphics(); graphicsMiter.lineStyle(2, 0, 1, 0.5); graphicsMiter.lineJoin = 'miter'; graphicsMiter.moveTo(p1[0], p1[1]); graphicsMiter.lineTo(p2[0], p2[1]); graphicsMiter.lineTo(p3[0], p3[1]); // when renderer.render(graphicsMiter); // then const points = graphicsMiter.vertexData; // 6 control points, xy each expect(points.length / 2, 'number of control points is not right').to.be.equal(6); expect(points[0], 'x1').to.be.eql(p1[0] + perp1[0]); expect(points[1], 'y1').to.be.eql(p1[1] + perp1[1]); expect(points[2], 'x2').to.be.eql(p1[0] - perp1[0]); expect(points[3], 'y2').to.be.eql(p1[1] - perp1[1]); expect(points[4], 'x3').to.be.eql(p2[0] + perp1[0]); expect(points[5], 'y3').to.be.eql(p2[1] + perp1[1]); expect(points[6], 'x4').to.be.eql(p2[0] + perp2[0]); expect(points[7], 'y4').to.be.eql(p2[1] + perp2[1]); expect(points[8], 'x5').to.be.eql(p3[0] - perp2[0]); expect(points[9], 'y5').to.be.eql(p3[1] - perp2[1]); expect(points[10], 'x6').to.be.eql(p3[0] + perp2[0]); expect(points[11], 'y6').to.be.eql(p3[1] + perp2[1]); })); }); describe('bevel', function () { it('clockwise bevel', withGL(function () { // given const renderer = new Renderer({ width: 200, height: 200 }); const graphics = new Graphics(); graphics.lineStyle(2, 0, 1, 0.5); graphics.line.lineJoin = 'bevel'; graphics.moveTo(0, 0); graphics.lineTo(50, 0); graphics.lineTo(50, 50); // when renderer.render(graphics); // then const points = graphics.geometry.points; // 8 control points, xy each expect(points.length / 2, 'number of control points is not right').to.be.equal(8); expect(points[0], 'x1').to.be.eql(0); expect(points[1], 'y1').to.be.eql(1); expect(points[2], 'x2').to.be.eql(0); expect(points[3], 'y2').to.be.eql(-1); expect(points[4], 'x3').to.be.eql(49); expect(points[5], 'y3').to.be.eql(1); expect(points[6], 'x4').to.be.eql(50); expect(points[7], 'y4').to.be.eql(-1); expect(points[8], 'x5').to.be.eql(49); expect(points[9], 'y5').to.be.eql(1); expect(points[10], 'x6').to.be.eql(51); expect(points[11], 'y6').to.be.eql(0); expect(points[12], 'x7').to.be.eql(49); expect(points[13], 'y7').to.be.eql(50); expect(points[14], 'x8').to.be.eql(51); expect(points[15], 'y8').to.be.eql(50); })); it('counterclockwise bevel', withGL(function () { // given const renderer = new Renderer({ width: 200, height: 200 }); const graphics = new Graphics(); graphics.lineStyle(2, 0, 1, 0.5); graphics.line.lineJoin = 'bevel'; graphics.moveTo(0, 0); graphics.lineTo(50, 0); graphics.lineTo(50, -50); // when renderer.render(graphics); // then const points = graphics.geometry.points; // 8 control points, xy each expect(points.length / 2, 'number of control points is not right').to.be.equal(8); expect(points[0], 'x1').to.be.eql(0); expect(points[1], 'y1').to.be.eql(1); expect(points[2], 'x2').to.be.eql(0); expect(points[3], 'y2').to.be.eql(-1); expect(points[4], 'x3').to.be.eql(50); expect(points[5], 'y3').to.be.eql(1); expect(points[6], 'x4').to.be.eql(49); expect(points[7], 'y4').to.be.eql(-1); expect(points[8], 'x5').to.be.eql(51); expect(points[9], 'y5').to.be.eql(0); expect(points[10], 'x6').to.be.eql(49); expect(points[11], 'y6').to.be.eql(-1); expect(points[12], 'x7').to.be.eql(51); expect(points[13], 'y7').to.be.eql(-50); expect(points[14], 'x8').to.be.eql(49); expect(points[15], 'y8').to.be.eql(-50); })); it('bevel join paralel lines', withGL(function () { // given const p1 = [0, 0]; const p2 = [50, 0]; const p3 = [100, 0]; // normalized perpendicular vectors const perp1 = [0, 1]; const perp2 = [0, -1]; const renderer = new Renderer({ width: 200, height: 200 }); const graphicsMiter = new Graphics(); graphicsMiter.lineStyle(2, 0, 1, 0.5); graphicsMiter.line.lineJoin = 'bevel'; graphicsMiter.moveTo(p1[0], p1[1]); graphicsMiter.lineTo(p2[0], p2[1]); graphicsMiter.lineTo(p3[0], p3[1]); // when renderer.render(graphicsMiter); // then const points = graphicsMiter.vertexData; // 6 control points, xy each expect(points.length / 2, 'number of control points is not right').to.be.equal(6); expect(points[0], 'x1').to.be.eql(p1[0] + perp1[0]); expect(points[1], 'y1').to.be.eql(p1[1] + perp1[1]); expect(points[2], 'x2').to.be.eql(p1[0] - perp1[0]); expect(points[3], 'y2').to.be.eql(p1[1] - perp1[1]); expect(points[4], 'x3').to.be.eql(p2[0] + perp1[0]); expect(points[5], 'y3').to.be.eql(p2[1] + perp1[1]); expect(points[6], 'x4').to.be.eql(p2[0] + perp2[0]); expect(points[7], 'y4').to.be.eql(p2[1] + perp2[1]); expect(points[8], 'x5').to.be.eql(p3[0] - perp2[0]); expect(points[9], 'y5').to.be.eql(p3[1] - perp2[1]); expect(points[10], 'x6').to.be.eql(p3[0] + perp2[0]); expect(points[11], 'y6').to.be.eql(p3[1] + perp2[1]); })); it('flat line bevel', withGL(function () { // given const renderer = new Renderer({ width: 200, height: 200 }); const graphics = new Graphics(); graphics.lineStyle(2, 0, 1, 0.5); graphics.line.lineJoin = 'bevel'; graphics.moveTo(0, 0); graphics.lineTo(50, 0); graphics.lineTo(100, 0); // when renderer.render(graphics); // then const points = graphics.geometry.points; // 6 control points, xy each expect(points.length / 2, 'number of control points is not right').to.be.equal(6); expect(points[0], 'x1').to.be.eql(0); expect(points[1], 'y1').to.be.eql(1); expect(points[2], 'x2').to.be.eql(0); expect(points[3], 'y2').to.be.eql(-1); expect(points[4], 'x3').to.be.eql(50); expect(points[5], 'y3').to.be.eql(1); expect(points[6], 'x4').to.be.eql(50); expect(points[7], 'y4').to.be.eql(-1); expect(points[8], 'x5').to.be.eql(100); expect(points[9], 'y5').to.be.eql(1); expect(points[10], 'x6').to.be.eql(100); expect(points[11], 'y6').to.be.eql(-1); })); }); describe('round', function () { it('round join clockwise', withGL(function () { // given const p1 = [0, 0]; const p2 = [50, 0]; const p3 = [50, 50]; // normalized perpendicular vectors const perp1 = [0, -1]; const perp2 = [1, 0]; const anchor = [p2[0] - perp1[0] - perp2[0], p2[1] - perp1[1] - perp2[1]]; // doubles cause every point is followed with center point // 1 + 1 + 15 * absAngleDiff * Math.sqrt(radius) / Math.PI const noOfCtlPts = 6 * 2; const r = 2.23606797749979; // sqrt(1^2 + 2^2) const angleIncrease = -0.12870022175865686; // anlge diff / 5 let angle = 2.677945044588987; // Math.atan2(1, -2) const renderer = new Renderer({ width: 200, height: 200 }); const graphics = new Graphics(); graphics.lineStyle(2, 0, 1, 0.5); graphics.line.lineJoin = 'round'; graphics.moveTo(p1[0], p1[1]); graphics.lineTo(p2[0], p2[1]); graphics.lineTo(p3[0], p3[1]); // when renderer.render(graphics); // then const points = graphics.geometry.points; // control points, xy each expect(points.length / 2, 'number of control points is not right').to.be.equal((4 + noOfCtlPts)); expect(points[0], 'x1').to.be.eql(p1[0] - perp1[0]); expect(points[1], 'y1').to.be.eql(p1[1] - perp1[1]); expect(points[2], 'x2').to.be.eql(p1[0] + perp1[0]); expect(points[3], 'y2').to.be.eql(p1[1] + perp1[1]); // center expect(points[4], 'center1 x').to.be.eql(anchor[0]); expect(points[5], 'center1 y').to.be.eql(anchor[1]); expect(points[8], 'center2 x').to.be.eql(anchor[0]); expect(points[9], 'center2 y').to.be.eql(anchor[1]); expect(points[12], 'center3 x').to.be.eql(anchor[0]); expect(points[13], 'center3 y').to.be.eql(anchor[1]); expect(points[16], 'center4 x').to.be.eql(anchor[0]); expect(points[17], 'center4 y').to.be.eql(anchor[1]); expect(points[20], 'center5 x').to.be.eql(anchor[0]); expect(points[21], 'center5 y').to.be.eql(anchor[1]); expect(points[24], 'center6 x').to.be.eql(anchor[0]); expect(points[25], 'center6 y').to.be.eql(anchor[1]); // peripheral pts expect(points[6], 'peripheral1 x').to.be.eql(p2[0] + perp1[0]); expect(points[7], 'peripheral1 y').to.be.eql(p2[1] + perp1[1]); angle += angleIncrease; expect(points[10], 'peripheral2 x').to.be.eql(anchor[0] + (Math.sin(angle) * r)); expect(points[11], 'peripheral2 y').to.be.eql(anchor[1] + (Math.cos(angle) * r)); angle += angleIncrease; expect(points[14], 'peripheral3 x').to.be.eql(anchor[0] + (Math.sin(angle) * r)); expect(points[15], 'peripheral3 y').to.be.eql(anchor[1] + (Math.cos(angle) * r)); angle += angleIncrease; expect(points[18], 'peripheral4 x').to.be.eql(anchor[0] + (Math.sin(angle) * r)); expect(points[19], 'peripheral4 y').to.be.eql(anchor[1] + (Math.cos(angle) * r)); angle += angleIncrease; expect(points[22], 'peripheral5 x').to.be.eql(anchor[0] + (Math.sin(angle) * r)); expect(points[23], 'peripheral5 y').to.be.eql(anchor[1] + (Math.cos(angle) * r)); expect(points[26], 'peripheral6 x').to.be.eql(p2[0] + perp2[0]); expect(points[27], 'peripheral6 y').to.be.eql(p2[1] + perp2[1]); expect(points[28], 'x[last-1]').to.be.eql(p3[0] - perp2[0]); expect(points[29], 'y[last-1]').to.be.eql(p3[1] - perp2[1]); expect(points[30], 'x[last]').to.be.eql(p3[0] + perp2[0]); expect(points[31], 'y[last]').to.be.eql(p3[1] + perp2[1]); })); it('round join counterclockwise', withGL(function () { // given const p1 = [0, 0]; const p2 = [50, 0]; const p3 = [50, -50]; // normalized perpendicular vectors const perp1 = [0, 1]; const perp2 = [1, 0]; const anchor = [p2[0] - perp1[0] - perp2[0], p2[1] - perp1[1] - perp2[1]]; // doubles cause every point is followed with center point // 1 + 1 + 15 * absAngleDiff * Math.sqrt(radius) / Math.PI const noOfCtlPts = 6 * 2; const r = 2.23606797749979; // sqrt(1^2 + 2^2) const angleIncrease = 0.12870022175865686; // anlge diff / 5 let angle = 0.4636476090008061; // Math.atan2(1, -2) const renderer = new Renderer({ width: 200, height: 200 }); const graphics = new Graphics(); graphics.lineStyle(2, 0, 1, 0.5); graphics.line.lineJoin = 'round'; graphics.moveTo(p1[0], p1[1]); graphics.lineTo(p2[0], p2[1]); graphics.lineTo(p3[0], p3[1]); // when renderer.render(graphics); // then const points = graphics.geometry.points; // control points, xy each expect(points.length / 2, 'number of control points is not right').to.be.equal((4 + noOfCtlPts)); expect(points[0], 'x1').to.be.eql(p1[0] + perp1[0]); expect(points[1], 'y1').to.be.eql(p1[1] + perp1[1]); expect(points[2], 'x2').to.be.eql(p1[0] - perp1[0]); expect(points[3], 'y2').to.be.eql(p1[1] - perp1[1]); // center expect(points[6], 'center1 x').to.be.eql(anchor[0]); expect(points[7], 'center1 y').to.be.eql(anchor[1]); expect(points[10], 'center2 x').to.be.eql(anchor[0]); expect(points[11], 'center2 y').to.be.eql(anchor[1]); expect(points[14], 'center3 x').to.be.eql(anchor[0]); expect(points[15], 'center3 y').to.be.eql(anchor[1]); expect(points[18], 'center4 x').to.be.eql(anchor[0]); expect(points[19], 'center4 y').to.be.eql(anchor[1]); expect(points[22], 'center5 x').to.be.eql(anchor[0]); expect(points[23], 'center5 y').to.be.eql(anchor[1]); expect(points[26], 'center6 x').to.be.eql(anchor[0]); expect(points[27], 'center6 y').to.be.eql(anchor[1]); // peripheral pts expect(points[4], 'peripheral1 x').to.be.eql(p2[0] + perp1[0]); expect(points[5], 'peripheral1 y').to.be.eql(p2[1] + perp1[1]); angle += angleIncrease; expect(points[8], 'peripheral2 x').to.be.eql(anchor[0] + (Math.sin(angle) * r)); expect(points[9], 'peripheral2 y').to.be.eql(anchor[1] + (Math.cos(angle) * r)); angle += angleIncrease; expect(points[12], 'peripheral3 x').to.be.eql(anchor[0] + (Math.sin(angle) * r)); expect(points[13], 'peripheral3 y').to.be.eql(anchor[1] + (Math.cos(angle) * r)); angle += angleIncrease; expect(points[16], 'peripheral4 x').to.be.eql(anchor[0] + (Math.sin(angle) * r)); expect(points[17], 'peripheral4 y').to.be.eql(anchor[1] + (Math.cos(angle) * r)); angle += angleIncrease; expect(points[20], 'peripheral5 x').to.be.eql(anchor[0] + (Math.sin(angle) * r)); expect(points[21], 'peripheral5 y').to.be.eql(anchor[1] + (Math.cos(angle) * r)); expect(points[24], 'peripheral6 x').to.be.eql(p2[0] + perp2[0]); expect(points[25], 'peripheral6 y').to.be.eql(p2[1] + perp2[1]); expect(points[28], 'x[last-1]').to.be.eql(p3[0] + perp2[0]); expect(points[29], 'y[last-1]').to.be.eql(p3[1] + perp2[1]); expect(points[30], 'x[last]').to.be.eql(p3[0] - perp2[0]); expect(points[31], 'y[last]').to.be.eql(p3[1] - perp2[1]); })); it('round join back and forth', withGL(function () { // given const p1 = [0, 0]; const p2 = [50, 0]; const p3 = [10, 0]; // normalized perpendicular vectors const perp1 = [0, -1]; const perp2 = [0, 1]; const anchor = [p2[0], p2[1]]; // doubles cause every point is followed with center point // 1 + 1 + 15 * absAngleDiff * Math.sqrt(radius) / Math.PI const noOfCtlPts = 16 * 2; const r = 1; const angleIncrease = -0.20943951023931953; let angle = 3.141592653589793; const renderer = new Renderer({ width: 200, height: 200 }); const graphics = new Graphics(); graphics.lineStyle(2, 0, 1, 0.5); graphics.line.lineJoin = 'round'; graphics.moveTo(p1[0], p1[1]); graphics.lineTo(p2[0], p2[1]); graphics.lineTo(p3[0], p3[1]); // when renderer.render(graphics); // then const points = graphics.geometry.points; const len = points.length; // control points, xy each expect(len / 2, 'number of control points is not right').to.be.equal((4 + noOfCtlPts)); expect(points[0], 'x1').to.be.eql(p1[0] - perp1[0]); expect(points[1], 'y1').to.be.eql(p1[1] - perp1[1]); expect(points[2], 'x2').to.be.eql(p1[0] + perp1[0]); expect(points[3], 'y2').to.be.eql(p1[1] + perp1[1]); // center for (let i = 4, j = 1; j <= 16; i += 4, j++) { expect(points[i], `center${j} x`).to.be.eql(p2[0]); expect(points[i + 1], `center${j} y`).to.be.eql(p2[1]); } // peripheral pts expect(points[6], 'peripheral1 x').to.be.eql(p2[0] + perp1[0]); expect(points[7], 'peripheral1 y').to.be.eql(p2[1] + perp1[1]); for (let i = 10, j = 2; j < 16; i += 4, j++) { angle += angleIncrease; expect(points[i], `peripheral${j} x`).to.be.eql(anchor[0] + (Math.sin(angle) * r)); expect(points[i + 1], `peripheral${j} y`).to.be.eql(anchor[1] + (Math.cos(angle) * r)); } expect(points[len - 6], 'peripheral16 x').to.be.eql(p2[0] + perp2[0]); expect(points[len - 5], 'peripheral16 y').to.be.eql(p2[1] + perp2[1]); expect(points[len - 4], 'x[last-1]').to.be.eql(p3[0] - perp2[0]); expect(points[len - 3], 'y[last-1]').to.be.eql(p3[1] - perp2[1]); expect(points[len - 2], 'x[last]').to.be.eql(p3[0] + perp2[0]); expect(points[len - 1], 'y[last]').to.be.eql(p3[1] + perp2[1]); })); it('round join back and forth other way around', withGL(function () { // given const p1 = [0, 0]; const p2 = [-50, 0]; const p3 = [10, 0]; // normalized perpendicular vectors const perp1 = [0, 1]; const perp2 = [0, -1]; const anchor = [p2[0], p2[1]]; // doubles cause every point is followed with center point // 1 + 1 + 15 * absAngleDiff * Math.sqrt(radius) / Math.PI const noOfCtlPts = 16 * 2; const r = 1; const angleIncrease = -0.20943951023931953; let angle = 0; const renderer = new Renderer({ width: 200, height: 200 }); const graphics = new Graphics(); graphics.lineStyle(2, 0, 1, 0.5); graphics.line.lineJoin = 'round'; graphics.moveTo(p1[0], p1[1]); graphics.lineTo(p2[0], p2[1]); graphics.lineTo(p3[0], p3[1]); // when renderer.render(graphics); // then const points = graphics.geometry.points; const len = points.length; // control points, xy each expect(len / 2, 'number of control points is not right').to.be.equal((4 + noOfCtlPts)); expect(points[0], 'x1').to.be.eql(p1[0] - perp1[0]); expect(points[1], 'y1').to.be.eql(p1[1] - perp1[1]); expect(points[2], 'x2').to.be.eql(p1[0] + perp1[0]); expect(points[3], 'y2').to.be.eql(p1[1] + perp1[1]); // center for (let i = 4, j = 1; j <= 16; i += 4, j++) { expect(points[i], `center${j} x`).to.be.eql(p2[0]); expect(points[i + 1], `center${j} y`).to.be.eql(p2[1]); } // peripheral pts expect(points[6], 'peripheral1 x').to.be.eql(p2[0] + perp1[0]); expect(points[7], 'peripheral1 y').to.be.eql(p2[1] + perp1[1]); for (let i = 10, j = 2; j < 16; i += 4, j++) { angle += angleIncrease; expect(points[i], `peripheral${j} x`).to.be.eql(anchor[0] + (Math.sin(angle) * r)); expect(points[i + 1], `peripheral${j} y`).to.be.eql(anchor[1] + (Math.cos(angle) * r)); } expect(points[len - 6], 'peripheral16 x').to.be.eql(p2[0] + perp2[0]); expect(points[len - 5], 'peripheral16 y').to.be.eql(p2[1] + perp2[1]); expect(points[len - 4], 'x[last-1]').to.be.eql(p3[0] - perp2[0]); expect(points[len - 3], 'y[last-1]').to.be.eql(p3[1] - perp2[1]); expect(points[len - 2], 'x[last]').to.be.eql(p3[0] + perp2[0]); expect(points[len - 1], 'y[last]').to.be.eql(p3[1] + perp2[1]); })); it('flat line round', withGL(function () { // given const renderer = new Renderer({ width: 200, height: 200 }); const graphics = new Graphics(); graphics.lineStyle(2, 0, 1, 0.5); graphics.line.lineJoin = 'round'; graphics.moveTo(0, 0); graphics.lineTo(50, 0); graphics.lineTo(100, 0); // when renderer.render(graphics); // then const points = graphics.geometry.points; // 6 control points, xy each expect(points.length / 2, 'number of control points is not right').to.be.equal(6); expect(points[0], 'x1').to.be.eql(0); expect(points[1], 'y1').to.be.eql(1); expect(points[2], 'x2').to.be.eql(0); expect(points[3], 'y2').to.be.eql(-1); expect(points[4], 'x3').to.be.eql(50); expect(points[5], 'y3').to.be.eql(1); expect(points[6], 'x4').to.be.eql(50); expect(points[7], 'y4').to.be.eql(-1); expect(points[8], 'x5').to.be.eql(100); expect(points[9], 'y5').to.be.eql(1); expect(points[10], 'x6').to.be.eql(100); expect(points[11], 'y6').to.be.eql(-1); })); }); }); }); describe('containsPoint', function () { it('should return true when point inside', function () { const point = new Point(1, 1); const graphics = new Graphics(); graphics.beginFill(0); graphics.drawRect(0, 0, 10, 10); expect(graphics.containsPoint(point)).to.be.true; }); it('should return false when point outside', function () { const point = new Point(20, 20); const graphics = new Graphics(); graphics.beginFill(0); graphics.drawRect(0, 0, 10, 10); expect(graphics.containsPoint(point)).to.be.false; }); it('should return false when no fill', function () { const point = new Point(1, 1); const graphics = new Graphics(); graphics.drawRect(0, 0, 10, 10); expect(graphics.containsPoint(point)).to.be.false; }); it('should return false with hole', function () { const point1 = new Point(1, 1); const point2 = new Point(5, 5); const graphics = new Graphics(); graphics.beginFill(0) .moveTo(0, 0) .lineTo(10, 0) .lineTo(10, 10) .lineTo(0, 10) .beginHole() .moveTo(2, 2) .lineTo(8, 2) .lineTo(8, 8) .lineTo(2, 8) .endHole(); expect(graphics.containsPoint(point1)).to.be.true; expect(graphics.containsPoint(point2)).to.be.false; }); }); describe('chaining', function () { it('should chain draw commands', function () { // complex drawing #1: draw triangle, rounder rect and an arc (issue #3433) const graphics = new Graphics().beginFill(0xFF3300) .lineStyle(4, 0xffd900, 1) .moveTo(50, 50) .lineTo(250, 50) .endFill() .drawRoundedRect(150, 450, 300, 100, 15) .beginHole() .endHole() .quadraticCurveTo(1, 1, 1, 1) .bezierCurveTo(1, 1, 1, 1) .arcTo(1, 1, 1, 1, 1) .arc(1, 1, 1, 1, 1, false) .drawRect(1, 1, 1, 1) .drawRoundedRect(1, 1, 1, 1, 0.1) .drawCircle(1, 1, 20) .drawEllipse(1, 1, 1, 1) .drawPolygon([1, 1, 1, 1, 1, 1]) .drawStar(1, 1, 1, 1, 1, 1) .clear(); expect(graphics).to.be.not.null; }); }); describe('arc', function () { it('should draw an arc', function () { const graphics = new Graphics(); expect(graphics.currentPath).to.be.null; expect(() => graphics.arc(100, 30, 20, 0, Math.PI)).to.not.throw(); expect(graphics.currentPath).to.be.not.null; }); it('should not throw with other shapes', function () { // complex drawing #1: draw triangle, rounder rect and an arc (issue #3433) const graphics = new Graphics(); // set a fill and line style graphics.beginFill(0xFF3300); graphics.lineStyle(4, 0xffd900, 1); // draw a shape graphics.moveTo(50, 50); graphics.lineTo(250, 50); graphics.lineTo(100, 100); graphics.lineTo(50, 50); graphics.endFill(); graphics.lineStyle(2, 0xFF00FF, 1); graphics.beginFill(0xFF00BB, 0.25); graphics.drawRoundedRect(150, 450, 300, 100, 15); graphics.endFill(); graphics.beginFill(); graphics.lineStyle(4, 0x00ff00, 1); expect(() => graphics.arc(300, 100, 20, 0, Math.PI)).to.not.throw(); }); it('should do nothing when startAngle and endAngle are equal', function () { const graphics = new Graphics(); expect(graphics.currentPath).to.be.null; graphics.arc(0, 0, 10, 0, 0); expect(graphics.currentPath).to.be.null; }); it('should do nothing if sweep equals zero', function () { const graphics = new Graphics(); expect(graphics.currentPath).to.be.null; graphics.arc(0, 0, 10, 10, 10); expect(graphics.currentPath).to.be.null; }); }); describe('_calculateBounds', function () { it('should only call updateLocalBounds once', function () { const graphics = new Graphics(); const spy = sinon.spy(graphics.geometry, 'calculateBounds'); graphics._calculateBounds(); expect(spy).to.have.been.calledOnce; graphics._calculateBounds(); expect(spy).to.have.been.calledOnce; }); }); describe('drawCircle', function () { it('should have no gaps in line border', withGL(function () { const renderer = new Renderer({ width: 200, height: 200 }); try { const graphics = new Graphics(); graphics.lineStyle(15, 0x8FC7E6); graphics.drawCircle(100, 100, 30); renderer.render(graphics); const points = graphics.geometry.graphicsData[0].points; const firstX = points[0]; const firstY = points[1]; const lastX = points[points.length - 2]; const lastY = points[points.length - 1]; expect(firstX).to.equals(lastX); expect(firstY).to.equals(lastY); } finally { renderer.destroy(); } })); }); describe('startPoly', function () { it('should fill two triangles', withGL(function () { const graphics = new Graphics(); graphics.beginFill(0xffffff, 1.0); graphics.moveTo(50, 50); graphics.lineTo(250, 50); graphics.lineTo(100, 100); graphics.lineTo(50, 50); graphics.moveTo(250, 50); graphics.lineTo(450, 50); graphics.lineTo(300, 100); graphics.lineTo(250, 50); graphics.endFill(); const data = graphics.geometry.graphicsData; expect(data.length).to.equals(2); expect(data[0].shape.points).to.eql([50, 50, 250, 50, 100, 100, 50, 50]); expect(data[1].shape.points).to.eql([250, 50, 450, 50, 300, 100, 250, 50]); })); it('should honor lineStyle break', withGL(function () { const graphics = new Graphics(); graphics.lineStyle(1.0, 0xffffff); graphics.moveTo(50, 50); graphics.lineTo(250, 50); graphics.lineStyle(2.0, 0xffffff); graphics.lineTo(100, 100); graphics.lineTo(50, 50); graphics.lineStyle(0.0); const data = graphics.geometry.graphicsData; expect(data.length).to.equals(2); expect(data[0].shape.points).to.eql([50, 50, 250, 50]); expect(data[1].shape.points).to.eql([250, 50, 100, 100, 50, 50]); })); }); });