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