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