| |
| |
| describe('PathKit\'s Path2D API', function() { |
| // Note, don't try to print the PathKit object - it can cause Karma/Jasmine to lock up. |
| var PathKit = null; |
| const LoadPathKit = new Promise(function(resolve, reject) { |
| if (PathKit) { |
| resolve(); |
| } else { |
| PathKitInit({ |
| locateFile: (file) => '/pathkit/'+file, |
| }).then((_PathKit) => { |
| PathKit = _PathKit; |
| resolve(); |
| }); |
| } |
| }); |
| |
| it('can do everything in the Path2D API w/o crashing', function(done) { |
| LoadPathKit.then(() => { |
| // This is taken from example.html |
| let path = PathKit.NewPath(); |
| |
| path.moveTo(20, 5); |
| path.lineTo(30, 20); |
| path.lineTo(40, 10); |
| path.lineTo(50, 20); |
| path.lineTo(60, 0); |
| path.lineTo(20, 5); |
| |
| path.moveTo(20, 80); |
| path.bezierCurveTo(90, 10, 160, 150, 190, 10); |
| |
| path.moveTo(36, 148); |
| path.quadraticCurveTo(66, 188, 120, 136); |
| path.lineTo(36, 148); |
| |
| path.rect(5, 170, 20, 20); |
| |
| path.moveTo(150, 180); |
| path.arcTo(150, 100, 50, 200, 20); |
| path.lineTo(160, 160); |
| |
| path.moveTo(20, 120); |
| path.arc(20, 120, 18, 0, 1.75 * Math.PI); |
| path.lineTo(20, 120); |
| |
| let secondPath = PathKit.NewPath(); |
| secondPath.ellipse(130, 25, 30, 10, -1*Math.PI/8, Math.PI/6, 1.5*Math.PI, false); |
| |
| path.addPath(secondPath); |
| |
| let m = document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGMatrix(); |
| m.a = 1; m.b = 0; |
| m.c = 0; m.d = 1; |
| m.e = 0; m.f = 20.5; |
| |
| path.addPath(secondPath, m); |
| |
| let canvas = document.createElement('canvas'); |
| let canvasCtx = canvas.getContext('2d'); |
| // Set canvas size and make it a bit bigger to zoom in on the lines |
| standardizedCanvasSize(canvasCtx); |
| canvasCtx.scale(3.0, 3.0); |
| canvasCtx.fillStyle = 'blue'; |
| canvasCtx.stroke(path.toPath2D()); |
| |
| let svgPath = document.createElementNS('http://www.w3.org/2000/svg', 'path'); |
| svgPath.setAttribute('stroke', 'black'); |
| svgPath.setAttribute('fill', 'rgba(255,255,255,0.0)'); |
| svgPath.setAttribute('transform', 'scale(3.0, 3.0)'); |
| svgPath.setAttribute('d', path.toSVGString()); |
| |
| let newSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); |
| newSVG.appendChild(svgPath); |
| newSVG.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); |
| newSVG.setAttribute('width', 600); |
| newSVG.setAttribute('height', 600); |
| |
| path.delete(); |
| secondPath.delete(); |
| |
| reportCanvas(canvas, 'path2D_api_example').then(() => { |
| reportSVG(newSVG, 'path2D_api_example').then(() => { |
| done(); |
| }).catch(reportError(done)); |
| }).catch(reportError(done)); |
| }); |
| }); |
| |
| it('can chain by returning the same object', function(done) { |
| LoadPathKit.then(() => { |
| let path = PathKit.NewPath(); |
| |
| let p1 = path.moveTo(20, 5) |
| .lineTo(30, 20) |
| .quadTo(66, 188, 120, 136) |
| .close(); |
| |
| // these should be the same object |
| expect(path === p1).toBe(true); |
| p1.delete(); |
| try { |
| // This should throw an exception because |
| // the underlying path was already deleted. |
| path.delete(); |
| expect('should not have gotten here').toBe(false); |
| } catch (e) { |
| // all is well |
| } |
| done(); |
| }); |
| }); |
| |
| it('does not leak path objects when chaining', function(done) { |
| LoadPathKit.then(() => { |
| // By default, we have 16 MB of memory assigned to our PathKit |
| // library. This can be configured by -S TOTAL_MEMORY=NN |
| // and defaults to 16MB (we likely don't need to touch this). |
| // If there's a leak in here, we should OOM pretty quick. |
| // Testing showed around 50k is enough to see one if we leak a path, |
| // so run 250k times just to be safe. |
| for(let i = 0; i < 250000; i++) { |
| let path = PathKit.NewPath() |
| .moveTo(20, 5) |
| .lineTo(30, 20) |
| .quadTo(66, 188, 120, 136) |
| .close(); |
| path.delete(); |
| } |
| done(); |
| }); |
| }); |
| |
| function drawTriangle() { |
| let path = PathKit.NewPath(); |
| path.moveTo(0, 0); |
| path.lineTo(10, 0); |
| path.lineTo(10, 10); |
| path.close(); |
| return path; |
| } |
| |
| it('has multiple overloads of addPath', function(done) { |
| LoadPathKit.then(() => { |
| let basePath = PathKit.NewPath(); |
| let otherPath = drawTriangle(); |
| // These add path call can be chained. |
| // add it unchanged |
| basePath.addPath(otherPath) |
| // providing the 6 params of an SVG matrix to make it appear 20.5 px down |
| .addPath(otherPath, 1, 0, 0, 1, 0, 20.5) |
| // provide the full 9 matrix params to make it appear 30 px to the right |
| // and be 3 times as big. |
| .addPath(otherPath, 3, 0, 30, |
| 0, 3, 0, |
| 0, 0, 1); |
| |
| reportPath(basePath, 'add_path_3x', done); |
| basePath.delete(); |
| otherPath.delete(); |
| }); |
| }); |
| |
| it('approximates arcs (conics) with quads', function(done) { |
| LoadPathKit.then(() => { |
| let path = PathKit.NewPath(); |
| path.moveTo(50, 120); |
| path.arc(50, 120, 45, 0, 1.75 * Math.PI); |
| path.lineTo(50, 120); |
| |
| let canvas = document.createElement('canvas'); |
| let canvasCtx = canvas.getContext('2d'); |
| standardizedCanvasSize(canvasCtx); |
| // The and.callThrough is important to make it actually |
| // draw the quadratics |
| spyOn(canvasCtx, 'quadraticCurveTo').and.callThrough(); |
| |
| canvasCtx.beginPath(); |
| path.toCanvas(canvasCtx); |
| canvasCtx.stroke(); |
| // No need to check the whole path, as that's more what the |
| // gold correctness tests are for (can account for changes we make |
| // to the approximation algorithms). |
| expect(canvasCtx.quadraticCurveTo).toHaveBeenCalled(); |
| path.delete(); |
| reportCanvas(canvas, 'conics_quads_approx').then(() => { |
| done(); |
| }).catch(reportError(done)); |
| }); |
| }); |
| |
| }); |