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