1cb93a386Sopenharmony_ci<!DOCTYPE html>
2cb93a386Sopenharmony_ci<title>PathKit (Skia's Geometry + asm.js)</title>
3cb93a386Sopenharmony_ci<meta charset="utf-8" />
4cb93a386Sopenharmony_ci<meta http-equiv="X-UA-Compatible" content="IE=edge">
5cb93a386Sopenharmony_ci<meta name="viewport" content="width=device-width, initial-scale=1.0">
6cb93a386Sopenharmony_ci
7cb93a386Sopenharmony_ci<style>
8cb93a386Sopenharmony_ci  svg, canvas {
9cb93a386Sopenharmony_ci    border: 1px dashed #AAA;
10cb93a386Sopenharmony_ci  }
11cb93a386Sopenharmony_ci
12cb93a386Sopenharmony_ci  canvas {
13cb93a386Sopenharmony_ci    width: 200px;
14cb93a386Sopenharmony_ci    height: 200px;
15cb93a386Sopenharmony_ci  }
16cb93a386Sopenharmony_ci
17cb93a386Sopenharmony_ci  canvas.big {
18cb93a386Sopenharmony_ci    width: 300px;
19cb93a386Sopenharmony_ci    height: 300px;
20cb93a386Sopenharmony_ci  }
21cb93a386Sopenharmony_ci
22cb93a386Sopenharmony_ci</style>
23cb93a386Sopenharmony_ci
24cb93a386Sopenharmony_ci<h2> Can output to an SVG Path, a Canvas, or a Path2D object </h2>
25cb93a386Sopenharmony_ci<svg id=svg1 xmlns='http://www.w3.org/2000/svg' width=200 height=200></svg>
26cb93a386Sopenharmony_ci<canvas id=canvas1></canvas>
27cb93a386Sopenharmony_ci<canvas id=canvas2></canvas>
28cb93a386Sopenharmony_ci
29cb93a386Sopenharmony_ci<h2> Interact with NewPath() just like a Path2D Object </h2>
30cb93a386Sopenharmony_ci<canvas class=big id=canvas3></canvas>
31cb93a386Sopenharmony_ci<canvas class=big id=canvas4></canvas>
32cb93a386Sopenharmony_ci
33cb93a386Sopenharmony_ci<h2> Has various Path Effects </h2>
34cb93a386Sopenharmony_ci<canvas class=big id=canvas5></canvas>
35cb93a386Sopenharmony_ci<canvas class=big id=canvas6></canvas>
36cb93a386Sopenharmony_ci<canvas class=big id=canvas7></canvas>
37cb93a386Sopenharmony_ci<canvas class=big id=canvas8></canvas>
38cb93a386Sopenharmony_ci<canvas class=big id=canvas9></canvas>
39cb93a386Sopenharmony_ci<canvas class=big id=canvas10></canvas>
40cb93a386Sopenharmony_ci<canvas class=big id=canvas11></canvas>
41cb93a386Sopenharmony_ci<canvas class=big id=canvasTransform></canvas>
42cb93a386Sopenharmony_ci
43cb93a386Sopenharmony_ci<h2> Supports fill-rules of nonzero and evenodd </h2>
44cb93a386Sopenharmony_ci<svg id=svg2 xmlns='http://www.w3.org/2000/svg' width=200 height=200></svg>
45cb93a386Sopenharmony_ci<svg id=svg3 xmlns='http://www.w3.org/2000/svg' width=200 height=200></svg>
46cb93a386Sopenharmony_ci
47cb93a386Sopenharmony_ci<h2> Solves Cubics for Y given X </h2>
48cb93a386Sopenharmony_ci<canvas class=big id=cubics></canvas>
49cb93a386Sopenharmony_ci
50cb93a386Sopenharmony_ci<script type="text/javascript" src="/node_modules/pathkit-asmjs/bin/pathkit.js"></script>
51cb93a386Sopenharmony_ci
52cb93a386Sopenharmony_ci<script type="text/javascript" charset="utf-8">
53cb93a386Sopenharmony_ci
54cb93a386Sopenharmony_ci  PathKitInit({
55cb93a386Sopenharmony_ci    locateFile: (file) => '/node_modules/pathkit-asmjs/bin/'+file,
56cb93a386Sopenharmony_ci  }).then((PathKit) => {
57cb93a386Sopenharmony_ci    window.PathKit = PathKit;
58cb93a386Sopenharmony_ci    OutputsExample(PathKit);
59cb93a386Sopenharmony_ci    Path2DExample(PathKit);
60cb93a386Sopenharmony_ci    PathEffectsExample(PathKit);
61cb93a386Sopenharmony_ci    MatrixTransformExample(PathKit);
62cb93a386Sopenharmony_ci    FilledSVGExample(PathKit);
63cb93a386Sopenharmony_ci    CubicSolverExample(PathKit);
64cb93a386Sopenharmony_ci  });
65cb93a386Sopenharmony_ci
66cb93a386Sopenharmony_ci  function setCanvasSize(ctx, width, height) {
67cb93a386Sopenharmony_ci    ctx.canvas.width = width;
68cb93a386Sopenharmony_ci    ctx.canvas.height = height;
69cb93a386Sopenharmony_ci  }
70cb93a386Sopenharmony_ci
71cb93a386Sopenharmony_ci  function OutputsExample(PathKit) {
72cb93a386Sopenharmony_ci    let firstPath = PathKit.FromSVGString('M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z');
73cb93a386Sopenharmony_ci
74cb93a386Sopenharmony_ci    let secondPath = PathKit.NewPath();
75cb93a386Sopenharmony_ci    // Acts somewhat like the Canvas API, except can be chained
76cb93a386Sopenharmony_ci    secondPath.moveTo(1, 1)
77cb93a386Sopenharmony_ci              .lineTo(20, 1)
78cb93a386Sopenharmony_ci              .lineTo(10, 30)
79cb93a386Sopenharmony_ci              .closePath();
80cb93a386Sopenharmony_ci
81cb93a386Sopenharmony_ci    // Join the two paths together (mutating firstPath in the process)
82cb93a386Sopenharmony_ci    firstPath.op(secondPath, PathKit.PathOp.INTERSECT);
83cb93a386Sopenharmony_ci
84cb93a386Sopenharmony_ci    let simpleStr = firstPath.toSVGString();
85cb93a386Sopenharmony_ci
86cb93a386Sopenharmony_ci    let newSVG = document.createElementNS('http://www.w3.org/2000/svg', 'path');
87cb93a386Sopenharmony_ci    newSVG.setAttribute('stroke', 'rgb(0,0,200)');
88cb93a386Sopenharmony_ci    newSVG.setAttribute('fill', 'white');
89cb93a386Sopenharmony_ci    newSVG.setAttribute('transform', 'scale(8,8)');
90cb93a386Sopenharmony_ci    newSVG.setAttribute('d', simpleStr);
91cb93a386Sopenharmony_ci    document.getElementById('svg1').appendChild(newSVG);
92cb93a386Sopenharmony_ci
93cb93a386Sopenharmony_ci    // Draw directly to Canvas
94cb93a386Sopenharmony_ci    let ctx = document.getElementById('canvas1').getContext('2d');
95cb93a386Sopenharmony_ci    setCanvasSize(ctx, 200, 200);
96cb93a386Sopenharmony_ci    ctx.strokeStyle = 'green';
97cb93a386Sopenharmony_ci    ctx.fillStyle = 'white';
98cb93a386Sopenharmony_ci    ctx.scale(8, 8);
99cb93a386Sopenharmony_ci    ctx.beginPath();
100cb93a386Sopenharmony_ci    firstPath.toCanvas(ctx);
101cb93a386Sopenharmony_ci    ctx.stroke();
102cb93a386Sopenharmony_ci
103cb93a386Sopenharmony_ci    // create Path2D object and use it in a Canvas.
104cb93a386Sopenharmony_ci    let path2D = firstPath.toPath2D();
105cb93a386Sopenharmony_ci    ctx = document.getElementById('canvas2').getContext('2d');
106cb93a386Sopenharmony_ci    setCanvasSize(ctx, 200, 200);
107cb93a386Sopenharmony_ci    ctx.canvas.width = 200
108cb93a386Sopenharmony_ci    ctx.scale(8, 8);
109cb93a386Sopenharmony_ci    ctx.fillStyle = 'purple';
110cb93a386Sopenharmony_ci    ctx.strokeStyle = 'orange';
111cb93a386Sopenharmony_ci    ctx.fill(path2D);
112cb93a386Sopenharmony_ci    ctx.stroke(path2D);
113cb93a386Sopenharmony_ci
114cb93a386Sopenharmony_ci    // clean up memory and call destructors in the c++ code (if any).
115cb93a386Sopenharmony_ci    // See http://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/embind.html?highlight=memory#memory-management
116cb93a386Sopenharmony_ci    firstPath.delete();
117cb93a386Sopenharmony_ci    secondPath.delete();
118cb93a386Sopenharmony_ci  }
119cb93a386Sopenharmony_ci
120cb93a386Sopenharmony_ci  function Path2DExample(PathKit) {
121cb93a386Sopenharmony_ci    let objs = [new Path2D(), PathKit.NewPath(), new Path2D(), PathKit.NewPath()];
122cb93a386Sopenharmony_ci    let canvases = [
123cb93a386Sopenharmony_ci      document.getElementById('canvas3').getContext('2d'),
124cb93a386Sopenharmony_ci      document.getElementById('canvas4').getContext('2d')
125cb93a386Sopenharmony_ci    ];
126cb93a386Sopenharmony_ci
127cb93a386Sopenharmony_ci    for (i = 0; i <= 1; i++) {
128cb93a386Sopenharmony_ci      let path = objs[i];
129cb93a386Sopenharmony_ci
130cb93a386Sopenharmony_ci      path.moveTo(20, 5);
131cb93a386Sopenharmony_ci      path.lineTo(30, 20);
132cb93a386Sopenharmony_ci      path.lineTo(40, 10);
133cb93a386Sopenharmony_ci      path.lineTo(50, 20);
134cb93a386Sopenharmony_ci      path.lineTo(60, 0);
135cb93a386Sopenharmony_ci      path.lineTo(20, 5);
136cb93a386Sopenharmony_ci
137cb93a386Sopenharmony_ci      path.moveTo(20, 80);
138cb93a386Sopenharmony_ci      path.bezierCurveTo(90, 10, 160, 150, 190, 10);
139cb93a386Sopenharmony_ci
140cb93a386Sopenharmony_ci      path.moveTo(36, 148);
141cb93a386Sopenharmony_ci      path.quadraticCurveTo(66, 188, 120, 136);
142cb93a386Sopenharmony_ci      path.lineTo(36, 148);
143cb93a386Sopenharmony_ci
144cb93a386Sopenharmony_ci      path.rect(5, 170, 20, 20);
145cb93a386Sopenharmony_ci
146cb93a386Sopenharmony_ci      path.moveTo(150, 180);
147cb93a386Sopenharmony_ci      path.arcTo(150, 100, 50, 200, 20);
148cb93a386Sopenharmony_ci      path.lineTo(160, 160);
149cb93a386Sopenharmony_ci
150cb93a386Sopenharmony_ci      path.moveTo(20, 120);
151cb93a386Sopenharmony_ci      path.arc(20, 120, 18, 0, 1.75 * Math.PI);
152cb93a386Sopenharmony_ci      path.lineTo(20, 120);
153cb93a386Sopenharmony_ci
154cb93a386Sopenharmony_ci      let secondPath = objs[i+2];
155cb93a386Sopenharmony_ci      secondPath.ellipse(130, 25, 30, 10, -1*Math.PI/8, Math.PI/6, 1.5*Math.PI, false);
156cb93a386Sopenharmony_ci
157cb93a386Sopenharmony_ci      path.addPath(secondPath);
158cb93a386Sopenharmony_ci
159cb93a386Sopenharmony_ci      let m = document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGMatrix();
160cb93a386Sopenharmony_ci      m.a = 1; m.b = 0;
161cb93a386Sopenharmony_ci      m.c = 0; m.d = 1;
162cb93a386Sopenharmony_ci      m.e = 0; m.f = 20.5;
163cb93a386Sopenharmony_ci
164cb93a386Sopenharmony_ci      path.addPath(secondPath, m);
165cb93a386Sopenharmony_ci      // With PathKit, one can also just provide the 6 params as floats, to avoid
166cb93a386Sopenharmony_ci      // the overhead of making an SVGMatrix
167cb93a386Sopenharmony_ci      // path.addPath(secondPath, 1, 0, 0, 1, 0, 20.5);
168cb93a386Sopenharmony_ci
169cb93a386Sopenharmony_ci      canvasCtx = canvases[i];
170cb93a386Sopenharmony_ci      canvasCtx.fillStyle = 'blue';
171cb93a386Sopenharmony_ci      setCanvasSize(canvasCtx, 300, 300);
172cb93a386Sopenharmony_ci      canvasCtx.scale(1.5, 1.5);
173cb93a386Sopenharmony_ci      if (path.toPath2D) {
174cb93a386Sopenharmony_ci        canvasCtx.stroke(path.toPath2D());
175cb93a386Sopenharmony_ci      } else {
176cb93a386Sopenharmony_ci        canvasCtx.stroke(path);
177cb93a386Sopenharmony_ci      }
178cb93a386Sopenharmony_ci    }
179cb93a386Sopenharmony_ci
180cb93a386Sopenharmony_ci
181cb93a386Sopenharmony_ci    objs[1].delete();
182cb93a386Sopenharmony_ci  }
183cb93a386Sopenharmony_ci
184cb93a386Sopenharmony_ci  // see https://fiddle.skia.org/c/@discrete_path
185cb93a386Sopenharmony_ci  function drawStar(path) {
186cb93a386Sopenharmony_ci    let R = 115.2, C = 128.0;
187cb93a386Sopenharmony_ci    path.moveTo(C + R + 22, C);
188cb93a386Sopenharmony_ci    for (let i = 1; i < 8; i++) {
189cb93a386Sopenharmony_ci      let a = 2.6927937 * i;
190cb93a386Sopenharmony_ci      path.lineTo(C + R * Math.cos(a) + 22, C + R * Math.sin(a));
191cb93a386Sopenharmony_ci    }
192cb93a386Sopenharmony_ci    path.closePath();
193cb93a386Sopenharmony_ci    return path;
194cb93a386Sopenharmony_ci  }
195cb93a386Sopenharmony_ci
196cb93a386Sopenharmony_ci  function PathEffectsExample(PathKit) {
197cb93a386Sopenharmony_ci    let effects = [
198cb93a386Sopenharmony_ci      // no-op
199cb93a386Sopenharmony_ci      (path) => path,
200cb93a386Sopenharmony_ci      // dash
201cb93a386Sopenharmony_ci      (path, counter) => path.dash(10, 3, counter/5),
202cb93a386Sopenharmony_ci      // trim (takes optional 3rd param for returning the trimmed part
203cb93a386Sopenharmony_ci      // or the complement)
204cb93a386Sopenharmony_ci      (path, counter) => path.trim((counter/100) % 1, 0.8, false),
205cb93a386Sopenharmony_ci      // simplify
206cb93a386Sopenharmony_ci      (path) => path.simplify(),
207cb93a386Sopenharmony_ci      // stroke
208cb93a386Sopenharmony_ci      (path, counter) => path.stroke({
209cb93a386Sopenharmony_ci        width: 10 * (Math.sin(counter/30) + 1),
210cb93a386Sopenharmony_ci        join: PathKit.StrokeJoin.BEVEL,
211cb93a386Sopenharmony_ci        cap: PathKit.StrokeCap.BUTT,
212cb93a386Sopenharmony_ci        miter_limit: 1,
213cb93a386Sopenharmony_ci      }),
214cb93a386Sopenharmony_ci      // "offset effect", that is, making a border around the shape.
215cb93a386Sopenharmony_ci      (path, counter) => {
216cb93a386Sopenharmony_ci        let orig = path.copy();
217cb93a386Sopenharmony_ci        path.stroke({
218cb93a386Sopenharmony_ci          width: 10 + (counter / 4) % 50,
219cb93a386Sopenharmony_ci          join: PathKit.StrokeJoin.ROUND,
220cb93a386Sopenharmony_ci          cap: PathKit.StrokeCap.SQUARE,
221cb93a386Sopenharmony_ci        })
222cb93a386Sopenharmony_ci          .op(orig, PathKit.PathOp.DIFFERENCE);
223cb93a386Sopenharmony_ci        orig.delete();
224cb93a386Sopenharmony_ci      },
225cb93a386Sopenharmony_ci      (path, counter) => {
226cb93a386Sopenharmony_ci        let simplified = path.simplify().copy();
227cb93a386Sopenharmony_ci        path.stroke({
228cb93a386Sopenharmony_ci          width: 2 + (counter / 2) % 100,
229cb93a386Sopenharmony_ci          join: PathKit.StrokeJoin.BEVEL,
230cb93a386Sopenharmony_ci          cap: PathKit.StrokeCap.BUTT,
231cb93a386Sopenharmony_ci        })
232cb93a386Sopenharmony_ci          .op(simplified, PathKit.PathOp.REVERSE_DIFFERENCE);
233cb93a386Sopenharmony_ci        simplified.delete();
234cb93a386Sopenharmony_ci      }
235cb93a386Sopenharmony_ci    ];
236cb93a386Sopenharmony_ci
237cb93a386Sopenharmony_ci    let names = ["(plain)", "Dash", "Trim", "Simplify", "Stroke", "Grow", "Shrink"];
238cb93a386Sopenharmony_ci
239cb93a386Sopenharmony_ci    let counter = 0;
240cb93a386Sopenharmony_ci    function frame() {
241cb93a386Sopenharmony_ci      counter++;
242cb93a386Sopenharmony_ci      for (let i = 0; i < effects.length; i++) {
243cb93a386Sopenharmony_ci        let path = PathKit.NewPath();
244cb93a386Sopenharmony_ci        drawStar(path);
245cb93a386Sopenharmony_ci
246cb93a386Sopenharmony_ci        // The transforms apply directly to the path.
247cb93a386Sopenharmony_ci        effects[i](path, counter);
248cb93a386Sopenharmony_ci
249cb93a386Sopenharmony_ci        let ctx = document.getElementById(`canvas${i+5}`).getContext('2d');
250cb93a386Sopenharmony_ci        setCanvasSize(ctx, 300, 300);
251cb93a386Sopenharmony_ci        ctx.strokeStyle = '#3c597a';
252cb93a386Sopenharmony_ci        ctx.fillStyle = '#3c597a';
253cb93a386Sopenharmony_ci        if (i >=4 ) {
254cb93a386Sopenharmony_ci          ctx.fill(path.toPath2D(), path.getFillTypeString());
255cb93a386Sopenharmony_ci        } else {
256cb93a386Sopenharmony_ci          ctx.stroke(path.toPath2D());
257cb93a386Sopenharmony_ci        }
258cb93a386Sopenharmony_ci
259cb93a386Sopenharmony_ci        ctx.font = '42px monospace';
260cb93a386Sopenharmony_ci
261cb93a386Sopenharmony_ci        let x = 150-ctx.measureText(names[i]).width/2;
262cb93a386Sopenharmony_ci        ctx.strokeText(names[i], x, 290);
263cb93a386Sopenharmony_ci
264cb93a386Sopenharmony_ci        path.delete();
265cb93a386Sopenharmony_ci      }
266cb93a386Sopenharmony_ci      window.requestAnimationFrame(frame);
267cb93a386Sopenharmony_ci    }
268cb93a386Sopenharmony_ci    window.requestAnimationFrame(frame);
269cb93a386Sopenharmony_ci  }
270cb93a386Sopenharmony_ci
271cb93a386Sopenharmony_ci  function MatrixTransformExample(PathKit) {
272cb93a386Sopenharmony_ci    // Creates an animated star that twists and moves.
273cb93a386Sopenharmony_ci    let ctx = document.getElementById('canvasTransform').getContext('2d');
274cb93a386Sopenharmony_ci    setCanvasSize(ctx, 300, 300);
275cb93a386Sopenharmony_ci    ctx.strokeStyle = '#3c597a';
276cb93a386Sopenharmony_ci
277cb93a386Sopenharmony_ci    let path = drawStar(PathKit.NewPath());
278cb93a386Sopenharmony_ci    // TODO(kjlubick): Perhaps expose some matrix helper functions to allow
279cb93a386Sopenharmony_ci    // clients to build their own matrices like this?
280cb93a386Sopenharmony_ci    // These matrices represent a 2 degree rotation and a 1% scale factor.
281cb93a386Sopenharmony_ci    let scaleUp = [1.0094, -0.0352,  3.1041,
282cb93a386Sopenharmony_ci                   0.0352,  1.0094, -6.4885,
283cb93a386Sopenharmony_ci                   0     ,  0      , 1];
284cb93a386Sopenharmony_ci
285cb93a386Sopenharmony_ci    let scaleDown = [ 0.9895, 0.0346, -2.8473,
286cb93a386Sopenharmony_ci                     -0.0346, 0.9895,  6.5276,
287cb93a386Sopenharmony_ci                      0     , 0     ,  1];
288cb93a386Sopenharmony_ci
289cb93a386Sopenharmony_ci    let i = 0;
290cb93a386Sopenharmony_ci    function frame(){
291cb93a386Sopenharmony_ci      i++;
292cb93a386Sopenharmony_ci      if (Math.round(i/100) % 2) {
293cb93a386Sopenharmony_ci        path.transform(scaleDown);
294cb93a386Sopenharmony_ci      } else {
295cb93a386Sopenharmony_ci        path.transform(scaleUp);
296cb93a386Sopenharmony_ci      }
297cb93a386Sopenharmony_ci
298cb93a386Sopenharmony_ci      ctx.clearRect(0, 0, 300, 300);
299cb93a386Sopenharmony_ci      ctx.stroke(path.toPath2D());
300cb93a386Sopenharmony_ci
301cb93a386Sopenharmony_ci      ctx.font = '42px monospace';
302cb93a386Sopenharmony_ci      let x = 150-ctx.measureText('Transform').width/2;
303cb93a386Sopenharmony_ci      ctx.strokeText('Transform', x, 290);
304cb93a386Sopenharmony_ci
305cb93a386Sopenharmony_ci      window.requestAnimationFrame(frame);
306cb93a386Sopenharmony_ci    }
307cb93a386Sopenharmony_ci    window.requestAnimationFrame(frame);
308cb93a386Sopenharmony_ci  }
309cb93a386Sopenharmony_ci
310cb93a386Sopenharmony_ci  function FilledSVGExample(PathKit) {
311cb93a386Sopenharmony_ci    let innerRect = PathKit.NewPath();
312cb93a386Sopenharmony_ci    innerRect.rect(80, 100, 40, 40);
313cb93a386Sopenharmony_ci
314cb93a386Sopenharmony_ci    let outerRect = PathKit.NewPath();
315cb93a386Sopenharmony_ci    outerRect.rect(50, 10, 100, 100)
316cb93a386Sopenharmony_ci             .op(innerRect, PathKit.PathOp.XOR);
317cb93a386Sopenharmony_ci
318cb93a386Sopenharmony_ci    let str = outerRect.toSVGString();
319cb93a386Sopenharmony_ci
320cb93a386Sopenharmony_ci    let diffSVG = document.createElementNS('http://www.w3.org/2000/svg', 'path');
321cb93a386Sopenharmony_ci    diffSVG.setAttribute('stroke', 'red');
322cb93a386Sopenharmony_ci    diffSVG.setAttribute('fill', 'black');
323cb93a386Sopenharmony_ci    // force fill-rule to nonzero to demonstrate difference
324cb93a386Sopenharmony_ci    diffSVG.setAttribute('fill-rule', 'nonzero');
325cb93a386Sopenharmony_ci    diffSVG.setAttribute('d', str);
326cb93a386Sopenharmony_ci    document.getElementById('svg2').appendChild(diffSVG);
327cb93a386Sopenharmony_ci
328cb93a386Sopenharmony_ci    let unionSVG = document.createElementNS('http://www.w3.org/2000/svg', 'path');
329cb93a386Sopenharmony_ci    unionSVG.setAttribute('stroke', 'red');
330cb93a386Sopenharmony_ci    unionSVG.setAttribute('fill', 'black');
331cb93a386Sopenharmony_ci    // ask what the path thinks fill-rule should be ('evenodd')
332cb93a386Sopenharmony_ci    // SVG and Canvas both use the same keys ('nonzero' and 'evenodd') and both
333cb93a386Sopenharmony_ci    // default to 'nonzero', so one call supports both.
334cb93a386Sopenharmony_ci    unionSVG.setAttribute('fill-rule', outerRect.getFillTypeString());
335cb93a386Sopenharmony_ci    unionSVG.setAttribute('d', str);
336cb93a386Sopenharmony_ci    document.getElementById('svg3').appendChild(unionSVG);
337cb93a386Sopenharmony_ci
338cb93a386Sopenharmony_ci    outerRect.delete();
339cb93a386Sopenharmony_ci    innerRect.delete();
340cb93a386Sopenharmony_ci  }
341cb93a386Sopenharmony_ci
342cb93a386Sopenharmony_ci  function CubicSolverExample(PathKit) {
343cb93a386Sopenharmony_ci    let ctx = document.getElementById('cubics').getContext('2d');
344cb93a386Sopenharmony_ci    setCanvasSize(ctx, 300, 300);
345cb93a386Sopenharmony_ci    // Draw lines between cp0 (0, 0) and cp1 and then cp2 and cp3 (1, 1)
346cb93a386Sopenharmony_ci    // scaled up to be on a 300x300 grid instead of unit square
347cb93a386Sopenharmony_ci    ctx.strokeStyle = 'black';
348cb93a386Sopenharmony_ci    ctx.beginPath();
349cb93a386Sopenharmony_ci    ctx.moveTo(0, 0);
350cb93a386Sopenharmony_ci    ctx.lineTo(0.1 * 300, 0.5*300);
351cb93a386Sopenharmony_ci
352cb93a386Sopenharmony_ci    ctx.moveTo(0.5 * 300, 0.1*300);
353cb93a386Sopenharmony_ci    ctx.lineTo(300, 300);
354cb93a386Sopenharmony_ci    ctx.stroke();
355cb93a386Sopenharmony_ci
356cb93a386Sopenharmony_ci
357cb93a386Sopenharmony_ci    ctx.strokeStyle = 'green';
358cb93a386Sopenharmony_ci    ctx.beginPath();
359cb93a386Sopenharmony_ci
360cb93a386Sopenharmony_ci    for (let x = 0; x < 300; x++) {
361cb93a386Sopenharmony_ci      // scale X into unit square
362cb93a386Sopenharmony_ci      let y = PathKit.cubicYFromX(0.1, 0.5, 0.5, 0.1, x/300);
363cb93a386Sopenharmony_ci      ctx.arc(x, y*300, 2, 0, 2*Math.PI);
364cb93a386Sopenharmony_ci    }
365cb93a386Sopenharmony_ci    ctx.stroke();
366cb93a386Sopenharmony_ci  }
367cb93a386Sopenharmony_ci
368cb93a386Sopenharmony_ci</script>
369