blob: b60db85ce0945f1f8c343147244e87731ca109d9 [file] [log] [blame]
Kevin Lubick92c91712018-08-09 10:00:02 -04001describe('PathKit\'s Path2D API', function() {
Kevin Lubick97d6d982018-08-10 15:53:16 -04002 it('can do everything in the Path2D API w/o crashing', function(done) {
Kevin Lubicke71e9ef2018-11-05 07:51:40 -05003 LoadPathKit.then(catchException(done, () => {
Kevin Lubick92c91712018-08-09 10:00:02 -04004 // 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);
Kevin Lubick92c91712018-08-09 10:00:02 -040042
43 let canvas = document.createElement('canvas');
Kevin Lubick92c91712018-08-09 10:00:02 -040044 let canvasCtx = canvas.getContext('2d');
Kevin Lubicka0ba6122018-08-15 13:45:28 -040045 // 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);
Kevin Lubick92c91712018-08-09 10:00:02 -040048 canvasCtx.fillStyle = 'blue';
49 canvasCtx.stroke(path.toPath2D());
50
Kevin Lubickc6c48aa2018-08-17 15:00:43 -040051 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
Kevin Lubick92c91712018-08-09 10:00:02 -040063 path.delete();
64 secondPath.delete();
65
Kevin Lubicka0ba6122018-08-15 13:45:28 -040066 reportCanvas(canvas, 'path2D_api_example').then(() => {
Kevin Lubickc6c48aa2018-08-17 15:00:43 -040067 reportSVG(newSVG, 'path2D_api_example').then(() => {
68 done();
69 }).catch(reportError(done));
Kevin Lubicka0ba6122018-08-15 13:45:28 -040070 }).catch(reportError(done));
Kevin Lubicke71e9ef2018-11-05 07:51:40 -050071 }));
Kevin Lubick92c91712018-08-09 10:00:02 -040072 });
73
Kevin Lubick11194ab2018-08-17 13:52:56 -040074 it('can chain by returning the same object', function(done) {
Kevin Lubicke71e9ef2018-11-05 07:51:40 -050075 LoadPathKit.then(catchException(done, () => {
Kevin Lubick11194ab2018-08-17 13:52:56 -040076 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();
Kevin Lubicke71e9ef2018-11-05 07:51:40 -050095 }));
Kevin Lubick11194ab2018-08-17 13:52:56 -040096 });
97
98 it('does not leak path objects when chaining', function(done) {
Kevin Lubicke71e9ef2018-11-05 07:51:40 -050099 LoadPathKit.then(catchException(done, () => {
Kevin Lubick11194ab2018-08-17 13:52:56 -0400100 // 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();
Kevin Lubicke71e9ef2018-11-05 07:51:40 -0500115 }));
Kevin Lubick11194ab2018-08-17 13:52:56 -0400116 });
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) {
Kevin Lubicke71e9ef2018-11-05 07:51:40 -0500128 LoadPathKit.then(catchException(done, () => {
Kevin Lubick11194ab2018-08-17 13:52:56 -0400129 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
Kevin Lubickc6c48aa2018-08-17 15:00:43 -0400142 reportPath(basePath, 'add_path_3x', done);
Kevin Lubick11194ab2018-08-17 13:52:56 -0400143 basePath.delete();
144 otherPath.delete();
Kevin Lubicke71e9ef2018-11-05 07:51:40 -0500145 }));
Kevin Lubick11194ab2018-08-17 13:52:56 -0400146 });
147
Kevin Lubick97d6d982018-08-10 15:53:16 -0400148 it('approximates arcs (conics) with quads', function(done) {
Kevin Lubicke71e9ef2018-11-05 07:51:40 -0500149 LoadPathKit.then(catchException(done, () => {
Kevin Lubick97d6d982018-08-10 15:53:16 -0400150 let path = PathKit.NewPath();
Kevin Lubicka0ba6122018-08-15 13:45:28 -0400151 path.moveTo(50, 120);
152 path.arc(50, 120, 45, 0, 1.75 * Math.PI);
153 path.lineTo(50, 120);
Kevin Lubick97d6d982018-08-10 15:53:16 -0400154
155 let canvas = document.createElement('canvas');
Kevin Lubick97d6d982018-08-10 15:53:16 -0400156 let canvasCtx = canvas.getContext('2d');
Kevin Lubicka0ba6122018-08-15 13:45:28 -0400157 standardizedCanvasSize(canvasCtx);
158 // The and.callThrough is important to make it actually
159 // draw the quadratics
160 spyOn(canvasCtx, 'quadraticCurveTo').and.callThrough();
Kevin Lubick97d6d982018-08-10 15:53:16 -0400161
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();
Kevin Lubicka0ba6122018-08-15 13:45:28 -0400170 reportCanvas(canvas, 'conics_quads_approx').then(() => {
171 done();
172 }).catch(reportError(done));
Kevin Lubicke71e9ef2018-11-05 07:51:40 -0500173 }));
Kevin Lubick97d6d982018-08-10 15:53:16 -0400174 });
175
176});