xref: /aosp_15_r20/external/skia/site/docs/user/modules/pathkit.md (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1---
2title: 'PathKit - Geometry in the Browser'
3linkTitle: 'PathKit - Geometry in the Browser'
4
5weight: 30
6---
7
8Skia has made its [SkPath](https://api.skia.org/classSkPath.html) object and
9many related methods available to JS clients (e.g. Web Browsers) using
10WebAssembly and asm.js.
11
12## Features
13
14PathKit is still under rapid development, so the exact API is subject to change.
15
16The primary features are:
17
18- API compatibility (e.g. drop-in replacement) with
19  [Path2D](https://developer.mozilla.org/en-US/docs/Web/API/Path2D)
20- Can output to SVG / Canvas / Path2D
21- Exposes a variety of path effects:
22
23<style>
24  canvas.patheffect {
25    border: 1px dashed #AAA;
26    width: 200px;
27    height: 200px;
28  }
29</style>
30
31<div id=effects>
32  <canvas class=patheffect id=canvas1 title="Plain: A drawn star with overlapping solid lines"></canvas>
33  <canvas class=patheffect id=canvas2 title="Dash: A drawn star with overlapping dashed lines"></canvas>
34  <canvas class=patheffect id=canvas3 title="Trim: A portion of a drawn star with overlapping solid lines"></canvas>
35  <canvas class=patheffect id=canvas4 title="Simplify: A drawn star with non-overlapping solid lines."></canvas>
36  <canvas class=patheffect id=canvas5 title="Stroke: A drawn star with non-overlapping solid lines stroked at various thicknesses and with square edges"></canvas>
37  <canvas class=patheffect id=canvas6 title="Grow: A drawn star's expanding outline"></canvas>
38  <canvas class=patheffect id=canvas7 title="Shrink: A solid drawn star shrunk down"></canvas>
39  <canvas class=patheffect id=canvasTransform title="Transform: A drawn star moved and rotated by an Affine Matrix"></canvas>
40</div>
41
42<script type="text/javascript">
43(function() {
44  // Tries to load the WASM version if supported, then falls back to asmjs
45  let s = document.createElement('script');
46  if (window.WebAssembly && typeof window.WebAssembly.compile === 'function') {
47    console.log('WebAssembly is supported! Using the wasm version of PathKit');
48    window.__pathkit_locate_file = 'https://unpkg.com/[email protected]/bin/';
49  } else {
50    console.log('WebAssembly is not supported (yet) on this browser. Using the asmjs version of PathKit');
51    window.__pathkit_locate_file = 'https://unpkg.com/[email protected]/bin/';
52  }
53  s.src = window.__pathkit_locate_file+'pathkit.js';
54  s.onload = () => {
55    // TODO(kjlubick) remove .ready() when we update the version served here.
56    try {
57      PathKitInit({
58        locateFile: (file) => window.__pathkit_locate_file+file,
59      }).ready().then((PathKit) => {
60        // Code goes here using PathKit
61        PathEffectsExample(PathKit);
62        MatrixTransformExample(PathKit);
63      });
64    }
65    catch(error) {
66      console.warn(error, 'falling back to image');
67      document.getElementById('effects').innerHTML = '<img width=800 src="./PathKit_effects.png"/>'
68    }
69  }
70
71  document.head.appendChild(s);
72
73  function setCanvasSize(ctx, width, height) {
74    ctx.canvas.width = width;
75    ctx.canvas.height = height;
76  }
77
78  function drawStar(path) {
79    let R = 115.2, C = 128.0;
80    path.moveTo(C + R + 22, C);
81    for (let i = 1; i < 8; i++) {
82      let a = 2.6927937 * i;
83      path.lineTo(C + R * Math.cos(a) + 22, C + R * Math.sin(a));
84    }
85    path.closePath();
86    return path;
87  }
88
89  function PathEffectsExample(PathKit) {
90    let effects = [
91      // no-op
92      (path) => path,
93      // dash
94      (path, counter) => path.dash(10, 3, counter/5),
95      // trim (takes optional 3rd param for returning the trimmed part
96      // or the complement)
97      (path, counter) => path.trim((counter/100) % 1, 0.8, false),
98      // simplify
99      (path) => path.simplify(),
100      // stroke
101      (path, counter) => path.stroke({
102        width: 10 * (Math.sin(counter/30) + 1),
103        join: PathKit.StrokeJoin.BEVEL,
104        cap: PathKit.StrokeCap.BUTT,
105        miter_limit: 1,
106      }),
107      // "offset effect", that is, making a border around the shape.
108      (path, counter) => {
109        let orig = path.copy();
110        path.stroke({
111          width: 10 + (counter / 4) % 50,
112          join: PathKit.StrokeJoin.ROUND,
113          cap: PathKit.StrokeCap.SQUARE,
114        })
115          .op(orig, PathKit.PathOp.DIFFERENCE);
116        orig.delete();
117      },
118      (path, counter) => {
119        let simplified = path.simplify().copy();
120        path.stroke({
121          width: 2 + (counter / 2) % 100,
122          join: PathKit.StrokeJoin.BEVEL,
123          cap: PathKit.StrokeCap.BUTT,
124        })
125          .op(simplified, PathKit.PathOp.REVERSE_DIFFERENCE);
126        simplified.delete();
127      }
128    ];
129
130    let names = ["(plain)", "Dash", "Trim", "Simplify", "Stroke", "Grow", "Shrink"];
131
132    let counter = 0;
133    function frame() {
134      counter++;
135      for (let i = 0; i < effects.length; i++) {
136        let path = PathKit.NewPath();
137        drawStar(path);
138
139        // The transforms apply directly to the path.
140        effects[i](path, counter);
141
142        let ctx = document.getElementById(`canvas${i+1}`);
143        if (!ctx) {
144          return;
145        } else {
146          ctx = ctx.getContext('2d');
147        }
148        setCanvasSize(ctx, 300, 300);
149        ctx.strokeStyle = '#3c597a';
150        ctx.fillStyle = '#3c597a';
151        if (i >=4 ) {
152          ctx.fill(path.toPath2D(), path.getFillTypeString());
153        } else {
154          ctx.stroke(path.toPath2D());
155        }
156
157        ctx.font = '42px monospace';
158
159        let x = 150-ctx.measureText(names[i]).width/2;
160        ctx.strokeText(names[i], x, 290);
161
162        path.delete();
163      }
164      window.requestAnimationFrame(frame);
165    }
166    window.requestAnimationFrame(frame);
167  }
168
169  function MatrixTransformExample(PathKit) {
170    // Creates an animated star that twists and moves.
171    let ctx = document.getElementById('canvasTransform').getContext('2d');
172    setCanvasSize(ctx, 300, 300);
173    ctx.strokeStyle = '#3c597a';
174
175    let path = drawStar(PathKit.NewPath());
176    // TODO(kjlubick): Perhaps expose some matrix helper functions to allow
177    // clients to build their own matrices like this?
178    // These matrices represent a 2 degree rotation and a 1% scale factor.
179    let scaleUp = [1.0094, -0.0352,  3.1041,
180                   0.0352,  1.0094, -6.4885,
181                   0     ,  0      , 1];
182
183    let scaleDown = [ 0.9895, 0.0346, -2.8473,
184                     -0.0346, 0.9895,  6.5276,
185                      0     , 0     ,  1];
186
187    let i = 0;
188    function frame(){
189      i++;
190      if (Math.round(i/100) % 2) {
191        path.transform(scaleDown);
192      } else {
193        path.transform(scaleUp);
194      }
195
196      ctx.clearRect(0, 0, 300, 300);
197      ctx.stroke(path.toPath2D());
198
199      ctx.font = '42px monospace';
200      let x = 150-ctx.measureText('Transform').width/2;
201      ctx.strokeText('Transform', x, 290);
202
203      window.requestAnimationFrame(frame);
204    }
205    window.requestAnimationFrame(frame);
206  }
207})();
208</script>
209
210## Example Code
211
212The best place to look for examples on how to use PathKit would be in the
213[example.html](https://github.com/google/skia/blob/main/modules/pathkit/npm-wasm/example.html#L45),
214which comes in the npm package.
215
216## Download the library
217
218See the the npm page for either the
219[WebAssembly](https://www.npmjs.com/package/pathkit-wasm) version or the
220[asm.js](https://www.npmjs.com/package/pathkit-asmjs) version for details on
221downloading and getting started.
222
223WebAssembly has faster load times and better overall performance but is
224currently supported by Chrome, Firefox, Edge, and Safari. The asm.js version
225should run anywhere JavaScript does.
226
227## API
228
229The primary feature of the library is the `SkPath` object. It can be created:
230
231- From the SVG string of a path `PathKit.FromSVGString(str)`
232- From a 2D array of verbs and arguments `PathKit.FromCmds(cmds)`
233- From `PathKit.NewPath()` (It will be blank)
234- As a copy of an existing `SkPath` with `path.copy()` or
235  `PathKit.NewPath(path)`
236
237It can be exported as:
238
239- An SVG string `path.toSVGString()`
240- A [Path2D](https://developer.mozilla.org/en-US/docs/Web/API/Path2D) object
241  `path.toPath2D()`
242- Directly to a canvas 2D context `path.toCanvas(ctx)`
243- A 2D array of verbs and arguments `path.toCmds()`
244
245Once an SkPath object has been made, it can be interacted with in the following
246ways:
247
248- expanded by any of the Path2D operations (`moveTo`, `lineTo`, `rect`, `arc`,
249  etc)
250- combined with other paths using `op` or `PathKit.MakeFromOp(p1, p2, op)`. For
251  example, `path1.op(path2, PathKit.PathOp.INTERSECT)` will set path1 to be the
252  area represented by where path1 and path2 overlap (intersect).
253  `PathKit.MakeFromOp(path1, path2, PathKit.PathOp.INTERSECT)` will do the same
254  but returned as a new `SkPath` object.
255- adjusted with some of the effects (`trim`, `dash`, `stroke`, etc)
256
257**Important**: Any objects (`SkPath`, `SkOpBuilder`, etc) that are created must
258be cleaned up with `path.delete()` when they leave the scope to avoid leaking
259the memory in the WASM heap. This includes any of the constructors, `copy()`, or
260any function prefixed with "make".
261
262### PathKit
263
264#### `FromSVGString(str)`
265
266**str** - `String` representing an
267[SVGPath](https://www.w3schools.com/graphics/svg_path.asp)
268
269Returns an `SkPath` with the same verbs and arguments as the SVG string, or
270`null` on a failure.
271
272Example:
273
274    let path = PathKit.FromSVGString('M150 0 L75 200 L225 200 Z');
275    // path represents a triangle
276    // don't forget to do path.delete() when it goes out of scope.
277
278#### `FromCmds(cmds)`
279
280**cmds** - `Array<Array<Number>>`, a 2D array of commands, where a command is a
281verb followed by its arguments.
282
283Returns an `SkPath` with the verbs and arguments from the list or `null` on a
284failure.
285
286This can be faster than calling `.moveTo()`, `.lineTo()`, etc many times.
287
288Example:
289
290    let cmds = [
291        [PathKit.MOVE_VERB, 0, 10],
292        [PathKit.LINE_VERB, 30, 40],
293        [PathKit.QUAD_VERB, 20, 50, 45, 60],
294    ];
295    let path = PathKit.FromCmds(cmds);
296    // path is the same as if a user had done
297    // let path = PathKit.NewPath().moveTo(0, 10).lineTo(30, 40).quadTo(20, 50, 45, 60);
298    // don't forget to do path.delete() when it goes out of scope.
299
300#### `NewPath()`
301
302Returns an empty `SkPath` object.
303
304Example:
305
306    let path = PathKit.NewPath();
307    path.moveTo(0, 10)
308        .lineTo(30, 40)
309        .quadTo(20, 50, 45, 60);
310    // don't forget to do path.delete() when it goes out of scope.
311    // Users can also do let path = new PathKit.SkPath();
312
313#### `NewPath(pathToCopy)`
314
315**pathToCopy** - SkPath, a path to make a copy of.
316
317Returns a `SkPath` that is a copy of the passed in `SkPath`.
318
319Example:
320
321    let otherPath = ...;
322    let clone = PathKit.NewPath(otherPath);
323    clone.simplify();
324    // don't forget to do clone.delete() when it goes out of scope.
325    // Users can also do let clone = new PathKit.SkPath(otherPath);
326    // or let clone = otherPath.copy();
327
328#### `MakeFromOp(pathOne, pathTwo, op)`
329
330**pathOne** - `SkPath`, a path. <br> **pathTwo** - `SkPath`, a path. <br>
331**op** - `PathOp`, an op to apply
332
333Returns a new `SkPath` that is the result of applying the given PathOp to the
334first and second path (order matters).
335
336Example:
337
338    let pathOne = PathKit.NewPath().moveTo(0, 20).lineTo(10, 10).lineTo(20, 20).close();
339    let pathTwo = PathKit.NewPath().moveTo(10, 20).lineTo(20, 10).lineTo(30, 20).close();
340    let mountains = PathKit.MakeFromOp(pathOne, pathTwo, PathKit.PathOp.UNION);
341    // don't forget to do mountains.delete() when it goes out of scope.
342    // Users can also do pathOne.op(pathTwo, PathKit.PathOp.UNION);
343    // to have the resulting path be stored to pathOne and avoid allocating another object.
344
345#### `cubicYFromX(cpx1, cpy1, cpx2, cpy2, X)`
346
347**cpx1, cpy1, cpx2, cpy2** - `Number`, coordinates for control points. <br>
348**X** - `Number`, The X coordinate for which to find the corresponding Y
349coordinate.
350
351Fast evaluation of a cubic ease-in / ease-out curve. This is defined as a
352parametric cubic curve inside the unit square. Makes the following assumptions:
353
354- pt[0] is implicitly { 0, 0 }
355- pt[3] is implicitly { 1, 1 }
356- pts[1, 2] are inside the unit square
357
358This returns the Y coordinate for the given X coordinate.
359
360#### `cubicPtFromT(cpx1, cpy1, cpx2, cpy2, T)`
361
362**cpx1, cpy1, cpx2, cpy2** - `Number`, coordinates for control points. <br>
363**T** - `Number`, The T param for which to find the corresponding (X, Y)
364coordinates.
365
366Fast evaluation of a cubic ease-in / ease-out curve. This is defined as a
367parametric cubic curve inside the unit square. Makes the following assumptions:
368
369- pt[0] is implicitly { 0, 0 }
370- pt[3] is implicitly { 1, 1 }
371- pts[1, 2] are inside the unit square
372
373This returns the (X, Y) coordinate for the given T value as a length 2 array.
374
375### SkPath (object)
376
377#### `addPath(otherPath)`
378
379**otherPath** - `SkPath`, a path to append to this path
380
381Adds the given path to `this` and then returns `this` for chaining purposes.
382
383#### `addPath(otherPath, transform)`
384
385**otherPath** - `SkPath`, a path to append to this path. <br> **transform** -
386[SVGMatrix](https://developer.mozilla.org/en-US/docs/Web/API/SVGMatrix), a
387transform to apply to otherPath before appending it.
388
389Adds the given path to `this` after applying the transform and then returns
390`this` for chaining purposes. See
391[Path2D.addPath()](https://developer.mozilla.org/en-US/docs/Web/API/Path2D/addPath)
392for more details.
393
394#### `addPath(otherPath, a, b, c, d, e, f)`
395
396**otherPath** - `SkPath`, a path to append to this path. <br> **a, b, c, d, e,
397f** - `Number`, the six components of an
398[SVGMatrix](https://developer.mozilla.org/en-US/docs/Web/API/SVGMatrix), which
399define the transform to apply to otherPath before appending it.
400
401Adds the given path to `this` after applying the transform and then returns
402`this` for chaining purposes. See
403[Path2D.addPath()](https://developer.mozilla.org/en-US/docs/Web/API/Path2D/addPath)
404for more details.
405
406Example:
407
408    let box = PathKit.NewPath().rect(0, 0, 100, 100);
409    let moreBoxes = PathKit.NewPath();
410    // add box un-transformed (i.e. at 0, 0)
411    moreBoxes.addPath(box)
412    // the params fill out a 2d matrix like:
413    //     a c e
414    //     b d f
415    //     0 0 1
416    // add box 300 points to the right
417             .addPath(box, 1, 0, 0, 1, 300, 0)
418    // add a box shrunk by 50% in both directions
419             .addPath(box, 0.5, 0, 0, 0.5, 0, 0);
420    // moreBoxes now has 3 paths appended to it
421
422#### `addPath(otherPath, scaleX, skewX, transX, skewY, scaleY, transY, pers0, pers1, pers2)`
423
424**otherPath** - `SkPath`, a path to append to this path. <br> **scaleX, skewX,
425transX, skewY, scaleY, transY, pers0, pers1, pers2** - `Number`, the nine
426components of an
427[Affine Matrix](https://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations),
428which define the transform to apply to otherPath before appending it.
429
430Adds the given path to `this` after applying the transform and then returns
431`this` for chaining purposes.
432
433Example:
434
435    let box = PathKit.NewPath().rect(0, 0, 100, 100);
436    let moreBoxes = PathKit.NewPath();
437    // add box un-transformed (i.e. at 0, 0)
438    moreBoxes.addPath(box)
439    // add box 300 points to the right
440             .addPath(box, 1, 0, 0,
441                           0, 1, 300,
442                           0, 0 ,1)
443    // add a box shrunk by 50% in both directions
444             .addPath(box, 0.5, 0,   0,
445                           0,   0.5, 0,
446                           0,   0,   1)
447    // moreBoxes now has 3 paths appended to it
448
449#### `arc(x, y, radius, startAngle, endAngle, ccw=false)`
450
451**x, y** - `Number`, The coordinates of the arc's center. <br> **radius** -
452`Number`, The radius of the arc. <br> **startAngle, endAngle** - `Number`, the
453start and end of the angle, measured clockwise from the positive x axis and in
454radians. <br> **ccw** - `Boolean`, optional argument specifying if the arc
455should be drawn counter-clockwise between **startAngle** and **endAngle**
456instead of clockwise, the default.
457
458Adds the described arc to `this` then returns `this` for chaining purposes. See
459[Path2D.arc()](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/arc)
460for more details.
461
462Example:
463
464    let path = PathKit.NewPath();
465    path.moveTo(20, 120);
466        .arc(20, 120, 18, 0, 1.75 * Math.PI);
467        .lineTo(20, 120);
468    // path looks like a pie with a 1/8th slice removed.
469
470#### `arcTo(x1, y1, x2, y2, radius)`
471
472**x1, y1, x2, y2** - `Number`, The coordinates defining the control points. <br>
473**radius** - `Number`, The radius of the arc.
474
475Adds the described arc to `this` (appending a line, if needed) then returns
476`this` for chaining purposes. See
477[Path2D.arcTo()](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/arcTo)
478for more details.
479
480#### `close()` or `closePath()`
481
482Returns the pen to the start of the current sub-path, then returns `this` for
483chaining purposes. See
484[Path2D.closePath()](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/closePath)
485for more details.
486
487#### `computeTightBounds()`
488
489Returns an `SkRect` that represents the minimum and maximum area of `this` path.
490See
491[SkPath reference](https://api.skia.org/classSkPath.html#a597c8fcc5e4750542e2688b057a14e9e)
492for more details.
493
494#### `conicTo(x1, y1, x2, y2, w)`
495
496**x1, y1, x2, y2** - `Number`, The coordinates defining the control point and
497the end point. <br> **w** - `Number`, The weight of the conic.
498
499Adds the described conic line to `this` (appending a line, if needed) then
500returns `this` for chaining purposes. See
501[SkPath reference](https://api.skia.org/classSkPath.html#a9edc41978765cfe9a0b16e9ecf4d276e)
502for more details.
503
504#### `copy()`
505
506Return a copy of `this` path.
507
508#### `cubicTo(cp1x, cp1y, cp2x, cp2y, x, y)` or `bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)`
509
510**cp1x, cp1y, cp2x, cp2y** - `Number`, The coordinates defining the control
511points. <br> **x,y** - `Number`, The coordinates defining the end point
512
513Adds the described cubic line to `this` (appending a line, if needed) then
514returns `this` for chaining purposes. See
515[Path2D.bezierCurveTo](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/bezierCurveTo)
516for more details.
517
518#### `dash(on, off, phase)`
519
520**on, off** - `Number`, The number of pixels the dash should be on (drawn) and
521off (blank). <br> **phase** - `Number`, The number of pixels the on/off should
522be offset (mod **on** + **off**)
523
524Applies a dashed path effect to `this` then returns `this` for chaining
525purposes. See the "Dash" effect above for a visual example.
526
527Example:
528
529    let box = PathKit.NewPath().rect(0, 0, 100, 100);
530    box.dash(20, 10, 3);
531    // box is now a dashed rectangle that will draw for 20 pixels, then
532    // stop for 10 pixels.  Since phase is 3, the first line won't start
533    // at (0, 0), but 3 pixels around the path (3, 0)
534
535#### `ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, ccw=false)`
536
537**x, y** - `Number`, The coordinates of the center of the ellipse. <br>
538**radiusX, radiusY** - `Number`, The radii in the X and Y directions. <br>
539**rotation** - `Number`, The rotation in radians of this ellipse. <br>
540**startAngle, endAngle** - `Number`, the starting and ending angles of which to
541draw, measured in radians from the positive x axis. <br> **ccw** - `Boolean`,
542optional argument specifying if the ellipse should be drawn counter-clockwise
543between **startAngle** and **endAngle** instead of clockwise, the default.
544
545Adds the described ellipse to `this` then returns `this` for chaining purposes.
546See
547[Path2D.ellipse](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/ellipse)
548for more details.
549
550#### `equals(otherPath)`
551
552**otherPath** - `SkPath`, the path to compare to.
553
554Returns a `Boolean` value based on if `this` path is equal to **otherPath**.
555
556#### `getBounds()`
557
558Returns an `SkRect` that represents the minimum and maximum area of `this` path.
559See
560[SkPath reference](https://api.skia.org/classSkPath.html#ac60188dc6075d6ebb56b5398fbba0c10)
561for more details.
562
563#### `getFillType()`
564
565Returns a `FillType` based on what this path is. This defaults to
566`PathKit.FillType.WINDING`, but may change with `op()` or `simplify()`.
567
568Clients will typically want `getFillTypeString()` because that value can be
569passed directly to an SVG or Canvas.
570
571#### `getFillTypeString()`
572
573Returns a `String` representing the fillType of `this` path. The values are
574either "nonzero" or "evenodd".
575
576Example:
577
578    let path = ...;
579    let ctx = document.getElementById('canvas1').getContext('2d');
580    ctx.strokeStyle = 'green';
581    ctx.fill(path.toPath2D(), path.getFillTypeString());
582
583#### `moveTo(x, y)`
584
585**x, y** - `Number`, The coordinates of where the pen should be moved to.
586
587Moves the pen (without drawing) to the given coordinates then returns `this` for
588chaining purposes. See
589[Path2D.moveTo](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/moveTo)
590for more details.
591
592#### `lineTo(x, y)`
593
594**x, y** - `Number`, The coordinates of where the pen should be moved to.
595
596Draws a straight line to the given coordinates then returns `this` for chaining
597purposes. See
598[Path2D.lineTo](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineTo)
599for more details.
600
601#### `op(otherPath, operation)`
602
603**otherPath** - `SkPath`, The other path to be combined with `this`. <br>
604**operation** - `PathOp`, The operation to apply to the two paths.
605
606Combines otherPath into `this` path with the given operation and returns `this`
607for chaining purposes.
608
609Example:
610
611    let pathOne = PathKit.NewPath().moveTo(0, 20).lineTo(10, 10).lineTo(20, 20).close();
612    let pathTwo = PathKit.NewPath().moveTo(10, 20).lineTo(20, 10).lineTo(30, 20).close();
613    // Combine the two triangles to look like two mountains
614    let mountains = pathOne.copy().op(pathOne, pathTwo, PathKit.PathOp.UNION);
615    // set pathOne to be the small triangle where pathOne and pathTwo overlap
616    pathOne.op(pathOne, pathTwo, PathKit.PathOp.INTERSECT);
617    // since copy() was called, don't forget to call delete() on mountains.
618
619#### `quadTo(cpx, cpy, x, y)` or `quadraticCurveTo(cpx, cpy, x, y)`
620
621**cpx, cpy** - `Number`, The coordinates for the control point. <br> **x, y** -
622`Number`, The coordinates for the end point.
623
624Draws a quadratic Bézier curve with the given coordinates then returns `this`
625for chaining purposes. See
626[Path2D.quadraticCurveTo](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/quadraticCurveTo)
627for more details.
628
629#### `rect(x, y, w, h)`
630
631**x, y** - `Number`, The coordinates of the upper-left corner of the rectangle.
632<br> **w, h** - `Number`, The width and height of the rectangle
633
634Draws a rectangle on `this`, then returns `this` for chaining purposes. See
635[Path2D.rect](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/rect)
636for more details.
637
638#### `setFillType(fillType)`
639
640**fillType** - `FillType`, the new fillType.
641
642Set the fillType of the path. See
643[SkPath reference](https://api.skia.org/SkPathTypes_8h.html#acc5b8721019c4a4b1beb8c759baab011)
644for more details.
645
646#### `simplify()`
647
648Set `this` path to a set of _non-overlapping_ contours that describe the same
649area as the original path. See the "Simplify" effect above for a visual example.
650
651#### `stroke(opts)`
652
653**opts** - `StrokeOpts`, contains the options for stroking.
654
655Strokes `this` path out with the given options. This can be used for a variety
656of effects. See the "Stroke", "Grow", and "Shrink" effects above for visual
657examples.
658
659Example:
660
661    let box = PathKit.NewPath().rect(0, 0, 100, 100);
662    // Stroke the path with width 10 and rounded corners
663    let rounded = box.copy().stroke({width: 10, join: PathKit.StrokeJoin.ROUND});
664    // Grow effect, that is, a 20 pixel expansion around the box.
665    let grow = box.copy().stroke({width: 20}).op(box, PathKit.PathOp.DIFFERENCE);
666    // Shrink effect, in which we subtract away from the original
667    let simplified = box.copy().simplify(); // sometimes required for complicated paths
668    let shrink = box.copy().stroke({width: 15, cap: PathKit.StrokeCap.BUTT})
669                           .op(simplified, PathKit.PathOp.REVERSE_DIFFERENCE);
670    // Don't forget to call delete() on each of the copies!
671
672#### `toCanvas(ctx)`
673
674**ctx** - `Canvas2DContext`, Canvas on which to draw the path.
675
676Draws `this` path on the passed in
677[Canvas Context](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D).
678
679Example:
680
681    let box = PathKit.NewPath().rect(0, 0, 100, 100);
682    let ctx = document.getElementById('canvas1').getContext('2d');
683    ctx.strokeStyle = 'green';
684    ctx.beginPath();
685    box.toCanvas(ctx);
686    ctx.stroke();  // could also ctx.fill()
687
688#### `toCmds()`
689
690Returns a 2D Array of verbs and args. See `PathKit.FromCmds()` for more details.
691
692#### `toPath2D()`
693
694Returns a [Path2D](https://developer.mozilla.org/en-US/docs/Web/API/Path2D)
695object that has the same operations as `this` path.
696
697Example:
698
699    let box = PathKit.NewPath().rect(0, 0, 100, 100);
700    let ctx = document.getElementById('canvas1').getContext('2d');
701    ctx.strokeStyle = 'green';
702    ctx.stroke(box.toPath2D());
703
704#### `toSVGString()`
705
706Returns a `String` representing an
707[SVGPath](https://www.w3schools.com/graphics/svg_path.asp) based on `this` path.
708
709Example:
710
711    let box = PathKit.NewPath().rect(0, 0, 100, 100);
712    let svg = document.getElementById('svg1');
713    let newPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
714    newPath.setAttribute('stroke', 'green');
715    newPath.setAttribute('fill', 'white');
716    newPath.setAttribute('d', box.toSVGString());
717    svg.appendChild(newPath);
718
719#### `transform(matr)`
720
721**matr** - `SkMatrix`, i.e. an `Array<Number>` of the nine numbers of an Affine
722Transform Matrix.
723
724Applies the specified
725[transform](https://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations)
726to `this` and then returns `this` for chaining purposes.
727
728#### `transform(scaleX, skewX, transX, skewY, scaleY, transY, pers0, pers1, pers2)`
729
730**scaleX, skewX, transX, skewY, scaleY, transY, pers0, pers1, pers2** -
731`Number`, the nine numbers of an Affine Transform Matrix.
732
733Applies the specified
734[transform](https://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations)
735to `this` and then returns `this` for chaining purposes.
736
737Example:
738
739    let path = PathKit.NewPath().rect(0, 0, 100, 100);
740    // scale up the path by 5x
741    path.transform([5, 0, 0,
742                    0, 5, 0,
743                    0, 0, 1]);
744    // move the path 75 px to the right.
745    path.transform(1, 0, 75,
746                   0, 1, 0,
747                   0, 0, 1);
748
749#### `trim(startT, stopT, isComplement=false)`
750
751**startT, stopT** - `Number`, values in [0, 1] that indicate the start and stop
752"percentages" of the path to draw <br> **isComplement** - `Boolean`, If the
753complement of the trimmed section should be drawn instead of the areas between
754**startT** and **stopT**.
755
756Sets `this` path to be a subset of the original path, then returns `this` for
757chaining purposes. See the "Trim" effect above for a visual example.
758
759Example:
760
761    let box = PathKit.NewPath().rect(0, 0, 100, 100);
762    box.trim(0.25, 1.0);
763    // box is now the 3 segments that look like a U
764    // (the top segment has been removed).
765
766### SkOpBuilder (object)
767
768This object enables chaining multiple PathOps together. Create one with
769`let builder = new PathKit.SkOpBuilder();` When created, the internal state is
770"empty path". Don't forget to call `delete()` on both the builder and the result
771of `resolve()`
772
773#### `add(path, operation)`
774
775**path** - `SkPath`, The path to be combined with the given rule. <br>
776**operation** - `PathOp`, The operation to apply to the two paths.
777
778Adds a path and the operand to the builder.
779
780#### `make()` or `resolve()`
781
782Creates and returns a new `SkPath` based on all the given paths and operands.
783
784Don't forget to call `.delete()` on the returned path when it goes out of scope.
785
786### SkMatrix (struct)
787
788`SkMatrix` translates between a C++ struct and a JS Array. It basically takes a
789nine element 1D Array and turns it into a 3x3 2D Affine Matrix.
790
791### SkRect (struct)
792
793`SkRect` translates between a C++ struct and a JS Object with the following keys
794(all values are `Number`:
795
796- **fLeft**: x coordinate of top-left corner
797- **fTop**: y coordinate of top-left corner
798- **fRight**: x coordinate of bottom-right corner
799- **fBottom**: y coordinate of bottom-rightcorner
800
801### StrokeOpts (struct)
802
803`StrokeOpts` translates between a C++ struct and a JS Object with the following
804keys:
805
806- **width**, `Number` the width of the lines of the path. Default 1.
807- **miter_limit**, `Number`, the miter limit. Defautl 4. See
808  [SkPaint reference](https://api.skia.org/classSkPaint.html#a2e767abfeb7795ed251a08b5ed85033f)
809  for more details.
810- **join**, `StrokeJoin`, the join to use. Default `PathKit.StrokeJoin.MITER`.
811  See
812  [SkPaint reference](https://api.skia.org/classSkPaint.html#ac582b0cbf59909c9056de34a6b977cca)
813  for more details.
814- **cap**, `StrokeCap`, the cap to use. Default `PathKit.StrokeCap.BUTT`. See
815  [SkPaint reference](https://api.skia.org/classSkPaint.html#a0f78de8559b795defba93171f6cb6333)
816  for more details.
817
818### PathOp (enum)
819
820The following enum values are exposed. They are essentially constant objects,
821differentiated by their `.value` property.
822
823- `PathKit.PathOp.DIFFERENCE`
824- `PathKit.PathOp.INTERSECT`
825- `PathKit.PathOp.REVERSE_DIFFERENCE`
826- `PathKit.PathOp.UNION`
827- `PathKit.PathOp.XOR`
828
829These are used in `PathKit.MakeFromOp()` and `SkPath.op()`.
830
831### FillType (enum)
832
833The following enum values are exposed. They are essentially constant objects,
834differentiated by their `.value` property.
835
836- `PathKit.FillType.WINDING` (also known as nonzero)
837- `PathKit.FillType.EVENODD`
838- `PathKit.FillType.INVERSE_WINDING`
839- `PathKit.FillType.INVERSE_EVENODD`
840
841These are used by `SkPath.getFillType()` and `SkPath.setFillType()`, but
842generally clients will want `SkPath.getFillTypeString()`.
843
844### StrokeJoin (enum)
845
846The following enum values are exposed. They are essentially constant objects,
847differentiated by their `.value` property.
848
849- `PathKit.StrokeJoin.MITER`
850- `PathKit.StrokeJoin.ROUND`
851- `PathKit.StrokeJoin.BEVEL`
852
853See
854[SkPaint reference](https://api.skia.org/classSkPaint.html#ac582b0cbf59909c9056de34a6b977cca)
855for more details.
856
857### StrokeCap (enum)
858
859The following enum values are exposed. They are essentially constant objects,
860differentiated by their `.value` property.
861
862- `PathKit.StrokeCap.BUTT`
863- `PathKit.StrokeCap.ROUND`
864- `PathKit.StrokeCap.SQUARE`
865
866See
867[SkPaint reference](https://api.skia.org/classSkPaint.html#a0f78de8559b795defba93171f6cb6333)
868for more details.
869
870### Constants
871
872The following constants are exposed:
873
874- `PathKit.MOVE_VERB` = 0
875- `PathKit.LINE_VERB` = 1
876- `PathKit.QUAD_VERB` = 2
877- `PathKit.CONIC_VERB` = 3
878- `PathKit.CUBIC_VERB` = 4
879- `PathKit.CLOSE_VERB` = 5
880
881These are only needed for `PathKit.FromCmds()`.
882
883### Functions for testing only
884
885#### `PathKit.LTRBRect(left, top, right, bottom)`
886
887**left** - `Number`, x coordinate of top-left corner of the `SkRect`. <br>
888**top** - `Number`, y coordinate of top-left corner of the `SkRect`. <br>
889**right** - `Number`, x coordinate of bottom-right corner of the `SkRect`. <br>
890**bottom** - `Number`, y coordinate of bottom-right corner of the `SkRect`.
891
892Returns an `SkRect` object with the given params.
893
894#### `SkPath.dump()`
895
896Prints all the verbs and arguments to the console. Only available on Debug and
897Test builds.
898