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