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