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