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