1PathKit - Geometry in the Browser 2============================= 3 4Skia has made its [SkPath](../api/SkPath_Reference) object and many related methods 5available to JS clients (e.g. Web Browsers) using WebAssembly and asm.js. 6 7Features 8-------- 9 10PathKit is still under rapid development, so the exact API is subject to change. 11 12The 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 206Example Code 207------------ 208The 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), 210which comes in the npm package. 211 212 213Download the library 214-------------------- 215 216See the the npm page for either the [WebAssembly](https://www.npmjs.com/package/pathkit-wasm) version 217or the [asm.js](https://www.npmjs.com/package/pathkit-asmjs) version 218for details on downloading and getting started. 219 220WebAssembly has faster load times and better overall performance but is 221currently supported by Chrome, Firefox, Edge, and Safari. 222The asm.js version should run anywhere JavaScript does. 223 224API 225---- 226 227The 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 234It 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 241Once 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 249leave the scope to avoid leaking the memory in the WASM heap. This includes any of the constructors, `copy()`, 250or 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 258Returns an `SkPath` with the same verbs and arguments as the SVG string, or `null` on a failure. 259 260Example: 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 270Returns an `SkPath` with the verbs and arguments from the list or `null` on a failure. 271 272This can be faster than calling `.moveTo()`, `.lineTo()`, etc many times. 273 274Example: 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 288Returns an empty `SkPath` object. 289 290Example: 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 302Returns a `SkPath` that is a copy of the passed in `SkPath`. 303 304Example: 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 318Returns a new `SkPath` that is the result of applying the given PathOp to the first and second 319path (order matters). 320 321Example: 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 334Fast evaluation of a cubic ease-in / ease-out curve. This is defined as a parametric cubic 335curve 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 341This 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 347Fast evaluation of a cubic ease-in / ease-out curve. This is defined as a parametric cubic 348curve 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 354This 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 362Adds 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 369Adds the given path to `this` after applying the transform and then returns `this` for 370chaining purposes. See [Path2D.addPath()](https://developer.mozilla.org/en-US/docs/Web/API/Path2D/addPath) 371for 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 379Adds the given path to `this` after applying the transform and then returns `this` for 380chaining purposes. See [Path2D.addPath()](https://developer.mozilla.org/en-US/docs/Web/API/Path2D/addPath) 381for more details. 382 383Example: 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 406Adds the given path to `this` after applying the transform and then returns `this` for 407chaining purposes. 408 409Example: 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 434Adds the described arc to `this` then returns `this` for 435chaining purposes. See [Path2D.arc()](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/arc) 436for more details. 437 438Example: 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 450Adds the described arc to `this` (appending a line, if needed) then returns `this` for 451chaining purposes. See [Path2D.arcTo()](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/arcTo) 452for more details. 453 454#### `close()` or `closePath()` #### 455Returns the pen to the start of the current sub-path, then returns `this` for 456chaining purposes. See [Path2D.closePath()](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/closePath) 457for more details. 458 459#### `computeTightBounds()` #### 460 461Returns 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) 463for 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 469Adds the described conic line to `this` (appending a line, if needed) then returns `this` for 470chaining purposes. See [SkPath reference](https://skia.org/user/api/SkPath_Reference#SkPath_conicTo) 471for more details. 472 473#### `copy()` #### 474 475Return 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 481Adds the described cubic line to `this` (appending a line, if needed) then returns `this` for 482chaining purposes. See [Path2D.bezierCurveTo](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/bezierCurveTo) 483for 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 489Applies a dashed path effect to `this` then returns `this` for chaining purposes. 490See the "Dash" effect above for a visual example. 491 492Example: 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 510Adds the described ellipse to `this` then returns `this` for chaining purposes. 511See [Path2D.ellipse](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/ellipse) 512for more details. 513 514#### `equals(otherPath)` #### 515**otherPath** - `SkPath`, the path to compare to. 516 517Returns a `Boolean` value based on if `this` path is equal 518to **otherPath**. 519 520#### `getBounds()` #### 521 522Returns 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) 524for more details. 525 526#### `getFillType()` #### 527 528Returns a `FillType` based on what this path is. This defaults to 529`PathKit.FillType.WINDING`, but may change with `op()` or `simplify()`. 530 531Clients will typically want `getFillTypeString()` because that value 532can be passed directly to an SVG or Canvas. 533 534#### `getFillTypeString()` #### 535 536Returns a `String` representing the fillType of `this` path. 537The values are either "nonzero" or "evenodd". 538 539Example: 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 549Moves the pen (without drawing) to the given coordinates then returns `this` for chaining purposes. 550See [Path2D.moveTo](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/moveTo) 551for more details. 552 553#### `lineTo(x, y)` #### 554**x, y** - `Number`, The coordinates of where the pen should be moved to. 555 556Draws a straight line to the given coordinates then returns `this` for chaining purposes. 557See [Path2D.lineTo](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineTo) 558for 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 564Combines otherPath into `this` path with the given operation and returns `this` 565for chaining purposes. 566 567Example: 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 581Draws a quadratic Bézier curve with the given coordinates then returns `this` for chaining purposes. 582See [Path2D.quadraticCurveTo](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/quadraticCurveTo) 583for 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 589Draws a rectangle on `this`, then returns `this` for chaining purposes. 590See [Path2D.rect](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/rect) 591for more details. 592 593#### `setFillType(fillType)` #### 594**fillType** - `FillType`, the new fillType. 595 596Set the fillType of the path. See [SkPath reference](https://skia.org/user/api/SkPath_Reference#SkPath_FillType) 597for more details. 598 599#### `simplify()` #### 600Set `this` path to a set of *non-overlapping* contours that describe the same area 601as 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 607Strokes `this` path out with the given options. This can be used for a variety of 608effects. See the "Stroke", "Grow", and "Shrink" effects above for visual examples. 609 610Example: 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 626Draws `this` path on the passed in 627[Canvas Context](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D). 628 629Example: 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 640Returns a 2D Array of verbs and args. See `PathKit.FromCmds()` for 641more details. 642 643#### `toPath2D()` #### 644 645Returns a [Path2D](https://developer.mozilla.org/en-US/docs/Web/API/Path2D) object 646that has the same operations as `this` path. 647 648Example: 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 657Returns a `String` representing an [SVGPath](https://www.w3schools.com/graphics/svg_path.asp) based on `this` path. 658 659Example: 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 672Applies the specified [transform](https://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations) 673to `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 679Applies the specified [transform](https://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations) 680to `this` and then returns `this` for chaining purposes. 681 682Example: 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 700Sets `this` path to be a subset of the original path, then returns `this` for chaining purposes. 701See the "Trim" effect above for a visual example. 702 703Example: 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) ### 712This object enables chaining multiple PathOps together. 713Create one with `let builder = new PathKit.SkOpBuilder();` 714When created, the internal state is "empty path". 715Don't forget to call `delete()` on both the builder and the result 716of `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 722Adds a path and the operand to the builder. 723 724#### `make()` or `resolve()` #### 725 726Creates and returns a new `SkPath` based on all the given paths 727and operands. 728 729Don'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. 734It basically takes a nine element 1D Array and turns it into a 7353x3 2D Affine Matrix. 736 737### SkRect (struct) ### 738 739`SkRect` translates between a C++ struct and a JS Object with 740the 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 749the 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`. 754See [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`. 756See [SkPaint reference](https://skia.org/user/api/SkPaint_Reference#Stroke_Cap) for more details. 757 758### PathOp (enum) ### 759The following enum values are exposed. They are essentially constant 760objects, 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 768These are used in `PathKit.MakeFromOp()` and `SkPath.op()`. 769 770### FillType (enum) ### 771The following enum values are exposed. They are essentially constant 772objects, 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 779These are used by `SkPath.getFillType()` and `SkPath.setFillType()`, but 780generally clients will want `SkPath.getFillTypeString()`. 781 782### StrokeJoin (enum) ### 783The following enum values are exposed. They are essentially constant 784objects, differentiated by thier `.value` property. 785 786 - `PathKit.StrokeJoin.MITER` 787 - `PathKit.StrokeJoin.ROUND` 788 - `PathKit.StrokeJoin.BEVEL` 789 790See [SkPaint reference](https://skia.org/user/api/SkPaint_Reference#SkPaint_Join) for more details. 791 792### StrokeCap (enum) ### 793The following enum values are exposed. They are essentially constant 794objects, differentiated by thier `.value` property. 795 796 - `PathKit.StrokeCap.BUTT` 797 - `PathKit.StrokeCap.ROUND` 798 - `PathKit.StrokeCap.SQUARE` 799 800See [SkPaint reference](https://skia.org/user/api/SkPaint_Reference#Stroke_Cap) for more details. 801 802### Constants ### 803The 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 812These 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 822Returns an `SkRect` object with the given params. 823 824#### `SkPath.dump()` #### 825 826Prints all the verbs and arguments to the console. 827Only available on Debug and Test builds. 828