1describe('PathKit\'s Path2D API', function() { 2 it('can do everything in the Path2D API w/o crashing', function(done) { 3 LoadPathKit.then(catchException(done, () => { 4 // This is taken from example.html 5 let path = PathKit.NewPath(); 6 7 path.moveTo(20, 5); 8 path.lineTo(30, 20); 9 path.lineTo(40, 10); 10 path.lineTo(50, 20); 11 path.lineTo(60, 0); 12 path.lineTo(20, 5); 13 14 path.moveTo(20, 80); 15 path.bezierCurveTo(90, 10, 160, 150, 190, 10); 16 17 path.moveTo(36, 148); 18 path.quadraticCurveTo(66, 188, 120, 136); 19 path.lineTo(36, 148); 20 21 path.rect(5, 170, 20, 20); 22 23 path.moveTo(150, 180); 24 path.arcTo(150, 100, 50, 200, 20); 25 path.lineTo(160, 160); 26 27 path.moveTo(20, 120); 28 path.arc(20, 120, 18, 0, 1.75 * Math.PI); 29 path.lineTo(20, 120); 30 31 let secondPath = PathKit.NewPath(); 32 secondPath.ellipse(130, 25, 30, 10, -1*Math.PI/8, Math.PI/6, 1.5*Math.PI, false); 33 34 path.addPath(secondPath); 35 36 let m = document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGMatrix(); 37 m.a = 1; m.b = 0; 38 m.c = 0; m.d = 1; 39 m.e = 0; m.f = 20.5; 40 41 path.addPath(secondPath, m); 42 43 let canvas = document.createElement('canvas'); 44 let canvasCtx = canvas.getContext('2d'); 45 // Set canvas size and make it a bit bigger to zoom in on the lines 46 standardizedCanvasSize(canvasCtx); 47 canvasCtx.scale(3.0, 3.0); 48 canvasCtx.fillStyle = 'blue'; 49 canvasCtx.stroke(path.toPath2D()); 50 51 let svgPath = document.createElementNS('http://www.w3.org/2000/svg', 'path'); 52 svgPath.setAttribute('stroke', 'black'); 53 svgPath.setAttribute('fill', 'rgba(255,255,255,0.0)'); 54 svgPath.setAttribute('transform', 'scale(3.0, 3.0)'); 55 svgPath.setAttribute('d', path.toSVGString()); 56 57 let newSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); 58 newSVG.appendChild(svgPath); 59 newSVG.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); 60 newSVG.setAttribute('width', 600); 61 newSVG.setAttribute('height', 600); 62 63 path.delete(); 64 secondPath.delete(); 65 66 reportCanvas(canvas, 'path2D_api_example').then(() => { 67 reportSVG(newSVG, 'path2D_api_example').then(() => { 68 done(); 69 }).catch(reportError(done)); 70 }).catch(reportError(done)); 71 })); 72 }); 73 74 it('can chain by returning the same object', function(done) { 75 LoadPathKit.then(catchException(done, () => { 76 let path = PathKit.NewPath(); 77 78 let p1 = path.moveTo(20, 5) 79 .lineTo(30, 20) 80 .quadTo(66, 188, 120, 136) 81 .close(); 82 83 // these should be the same object 84 expect(path === p1).toBe(true); 85 p1.delete(); 86 try { 87 // This should throw an exception because 88 // the underlying path was already deleted. 89 path.delete(); 90 expect('should not have gotten here').toBe(false); 91 } catch (e) { 92 // all is well 93 } 94 done(); 95 })); 96 }); 97 98 it('does not leak path objects when chaining', function(done) { 99 LoadPathKit.then(catchException(done, () => { 100 // By default, we have 16 MB of memory assigned to our PathKit 101 // library. This can be configured by -S TOTAL_MEMORY=NN 102 // and defaults to 16MB (we likely don't need to touch this). 103 // If there's a leak in here, we should OOM pretty quick. 104 // Testing showed around 50k is enough to see one if we leak a path, 105 // so run 250k times just to be safe. 106 for(let i = 0; i < 250000; i++) { 107 let path = PathKit.NewPath() 108 .moveTo(20, 5) 109 .lineTo(30, 20) 110 .quadTo(66, 188, 120, 136) 111 .close(); 112 path.delete(); 113 } 114 done(); 115 })); 116 }); 117 118 function drawTriangle() { 119 let path = PathKit.NewPath(); 120 path.moveTo(0, 0); 121 path.lineTo(10, 0); 122 path.lineTo(10, 10); 123 path.close(); 124 return path; 125 } 126 127 it('has multiple overloads of addPath', function(done) { 128 LoadPathKit.then(catchException(done, () => { 129 let basePath = PathKit.NewPath(); 130 let otherPath = drawTriangle(); 131 // These add path call can be chained. 132 // add it unchanged 133 basePath.addPath(otherPath) 134 // providing the 6 params of an SVG matrix to make it appear 20.5 px down 135 .addPath(otherPath, 1, 0, 0, 1, 0, 20.5) 136 // provide the full 9 matrix params to make it appear 30 px to the right 137 // and be 3 times as big. 138 .addPath(otherPath, 3, 0, 30, 139 0, 3, 0, 140 0, 0, 1); 141 142 reportPath(basePath, 'add_path_3x', done); 143 basePath.delete(); 144 otherPath.delete(); 145 })); 146 }); 147 148 it('approximates arcs (conics) with quads', function(done) { 149 LoadPathKit.then(catchException(done, () => { 150 let path = PathKit.NewPath(); 151 path.moveTo(50, 120); 152 path.arc(50, 120, 45, 0, 1.75 * Math.PI); 153 path.lineTo(50, 120); 154 155 let canvas = document.createElement('canvas'); 156 let canvasCtx = canvas.getContext('2d'); 157 standardizedCanvasSize(canvasCtx); 158 // The and.callThrough is important to make it actually 159 // draw the quadratics 160 spyOn(canvasCtx, 'quadraticCurveTo').and.callThrough(); 161 162 canvasCtx.beginPath(); 163 path.toCanvas(canvasCtx); 164 canvasCtx.stroke(); 165 // No need to check the whole path, as that's more what the 166 // gold correctness tests are for (can account for changes we make 167 // to the approximation algorithms). 168 expect(canvasCtx.quadraticCurveTo).toHaveBeenCalled(); 169 path.delete(); 170 reportCanvas(canvas, 'conics_quads_approx').then(() => { 171 done(); 172 }).catch(reportError(done)); 173 })); 174 }); 175 176}); 177