1*c8dee2aaSAndroid Build Coastguard Worker<!DOCTYPE html> 2*c8dee2aaSAndroid Build Coastguard Worker<title>PathKit (Skia's Geometry + asm.js)</title> 3*c8dee2aaSAndroid Build Coastguard Worker<meta charset="utf-8" /> 4*c8dee2aaSAndroid Build Coastguard Worker<meta http-equiv="X-UA-Compatible" content="IE=edge"> 5*c8dee2aaSAndroid Build Coastguard Worker<meta name="viewport" content="width=device-width, initial-scale=1.0"> 6*c8dee2aaSAndroid Build Coastguard Worker 7*c8dee2aaSAndroid Build Coastguard Worker<style> 8*c8dee2aaSAndroid Build Coastguard Worker svg, canvas { 9*c8dee2aaSAndroid Build Coastguard Worker border: 1px dashed #AAA; 10*c8dee2aaSAndroid Build Coastguard Worker } 11*c8dee2aaSAndroid Build Coastguard Worker 12*c8dee2aaSAndroid Build Coastguard Worker canvas { 13*c8dee2aaSAndroid Build Coastguard Worker width: 200px; 14*c8dee2aaSAndroid Build Coastguard Worker height: 200px; 15*c8dee2aaSAndroid Build Coastguard Worker } 16*c8dee2aaSAndroid Build Coastguard Worker 17*c8dee2aaSAndroid Build Coastguard Worker canvas.big { 18*c8dee2aaSAndroid Build Coastguard Worker width: 300px; 19*c8dee2aaSAndroid Build Coastguard Worker height: 300px; 20*c8dee2aaSAndroid Build Coastguard Worker } 21*c8dee2aaSAndroid Build Coastguard Worker 22*c8dee2aaSAndroid Build Coastguard Worker</style> 23*c8dee2aaSAndroid Build Coastguard Worker 24*c8dee2aaSAndroid Build Coastguard Worker<h2> Can output to an SVG Path, a Canvas, or a Path2D object </h2> 25*c8dee2aaSAndroid Build Coastguard Worker<svg id=svg1 xmlns='http://www.w3.org/2000/svg' width=200 height=200></svg> 26*c8dee2aaSAndroid Build Coastguard Worker<canvas id=canvas1></canvas> 27*c8dee2aaSAndroid Build Coastguard Worker<canvas id=canvas2></canvas> 28*c8dee2aaSAndroid Build Coastguard Worker 29*c8dee2aaSAndroid Build Coastguard Worker<h2> Interact with NewPath() just like a Path2D Object </h2> 30*c8dee2aaSAndroid Build Coastguard Worker<canvas class=big id=canvas3></canvas> 31*c8dee2aaSAndroid Build Coastguard Worker<canvas class=big id=canvas4></canvas> 32*c8dee2aaSAndroid Build Coastguard Worker 33*c8dee2aaSAndroid Build Coastguard Worker<h2> Has various Path Effects </h2> 34*c8dee2aaSAndroid Build Coastguard Worker<canvas class=big id=canvas5></canvas> 35*c8dee2aaSAndroid Build Coastguard Worker<canvas class=big id=canvas6></canvas> 36*c8dee2aaSAndroid Build Coastguard Worker<canvas class=big id=canvas7></canvas> 37*c8dee2aaSAndroid Build Coastguard Worker<canvas class=big id=canvas8></canvas> 38*c8dee2aaSAndroid Build Coastguard Worker<canvas class=big id=canvas9></canvas> 39*c8dee2aaSAndroid Build Coastguard Worker<canvas class=big id=canvas10></canvas> 40*c8dee2aaSAndroid Build Coastguard Worker<canvas class=big id=canvas11></canvas> 41*c8dee2aaSAndroid Build Coastguard Worker<canvas class=big id=canvasTransform></canvas> 42*c8dee2aaSAndroid Build Coastguard Worker 43*c8dee2aaSAndroid Build Coastguard Worker<h2> Supports fill-rules of nonzero and evenodd </h2> 44*c8dee2aaSAndroid Build Coastguard Worker<svg id=svg2 xmlns='http://www.w3.org/2000/svg' width=200 height=200></svg> 45*c8dee2aaSAndroid Build Coastguard Worker<svg id=svg3 xmlns='http://www.w3.org/2000/svg' width=200 height=200></svg> 46*c8dee2aaSAndroid Build Coastguard Worker 47*c8dee2aaSAndroid Build Coastguard Worker<h2> Solves Cubics for Y given X </h2> 48*c8dee2aaSAndroid Build Coastguard Worker<canvas class=big id=cubics></canvas> 49*c8dee2aaSAndroid Build Coastguard Worker 50*c8dee2aaSAndroid Build Coastguard Worker<script type="text/javascript" src="/build/asmjs/pathkit.js"></script> 51*c8dee2aaSAndroid Build Coastguard Worker 52*c8dee2aaSAndroid Build Coastguard Worker<script type="text/javascript" charset="utf-8"> 53*c8dee2aaSAndroid Build Coastguard Worker 54*c8dee2aaSAndroid Build Coastguard Worker PathKitInit({ 55*c8dee2aaSAndroid Build Coastguard Worker locateFile: (file) => '/build/asmjs/'+file, 56*c8dee2aaSAndroid Build Coastguard Worker }).then((PathKit) => { 57*c8dee2aaSAndroid Build Coastguard Worker window.PathKit = PathKit; 58*c8dee2aaSAndroid Build Coastguard Worker OutputsExample(PathKit); 59*c8dee2aaSAndroid Build Coastguard Worker Path2DExample(PathKit); 60*c8dee2aaSAndroid Build Coastguard Worker PathEffectsExample(PathKit); 61*c8dee2aaSAndroid Build Coastguard Worker MatrixTransformExample(PathKit); 62*c8dee2aaSAndroid Build Coastguard Worker FilledSVGExample(PathKit); 63*c8dee2aaSAndroid Build Coastguard Worker CubicSolverExample(PathKit); 64*c8dee2aaSAndroid Build Coastguard Worker }); 65*c8dee2aaSAndroid Build Coastguard Worker 66*c8dee2aaSAndroid Build Coastguard Worker function setCanvasSize(ctx, width, height) { 67*c8dee2aaSAndroid Build Coastguard Worker ctx.canvas.width = width; 68*c8dee2aaSAndroid Build Coastguard Worker ctx.canvas.height = height; 69*c8dee2aaSAndroid Build Coastguard Worker } 70*c8dee2aaSAndroid Build Coastguard Worker 71*c8dee2aaSAndroid Build Coastguard Worker function OutputsExample(PathKit) { 72*c8dee2aaSAndroid Build Coastguard Worker let firstPath = PathKit.FromSVGString('M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z'); 73*c8dee2aaSAndroid Build Coastguard Worker 74*c8dee2aaSAndroid Build Coastguard Worker let secondPath = PathKit.NewPath(); 75*c8dee2aaSAndroid Build Coastguard Worker // Acts somewhat like the Canvas API, except can be chained 76*c8dee2aaSAndroid Build Coastguard Worker secondPath.moveTo(1, 1) 77*c8dee2aaSAndroid Build Coastguard Worker .lineTo(20, 1) 78*c8dee2aaSAndroid Build Coastguard Worker .lineTo(10, 30) 79*c8dee2aaSAndroid Build Coastguard Worker .closePath(); 80*c8dee2aaSAndroid Build Coastguard Worker 81*c8dee2aaSAndroid Build Coastguard Worker // Join the two paths together (mutating firstPath in the process) 82*c8dee2aaSAndroid Build Coastguard Worker firstPath.op(secondPath, PathKit.PathOp.INTERSECT); 83*c8dee2aaSAndroid Build Coastguard Worker 84*c8dee2aaSAndroid Build Coastguard Worker let simpleStr = firstPath.toSVGString(); 85*c8dee2aaSAndroid Build Coastguard Worker 86*c8dee2aaSAndroid Build Coastguard Worker let newSVG = document.createElementNS('http://www.w3.org/2000/svg', 'path'); 87*c8dee2aaSAndroid Build Coastguard Worker newSVG.setAttribute('stroke', 'rgb(0,0,200)'); 88*c8dee2aaSAndroid Build Coastguard Worker newSVG.setAttribute('fill', 'white'); 89*c8dee2aaSAndroid Build Coastguard Worker newSVG.setAttribute('transform', 'scale(8,8)'); 90*c8dee2aaSAndroid Build Coastguard Worker newSVG.setAttribute('d', simpleStr); 91*c8dee2aaSAndroid Build Coastguard Worker document.getElementById('svg1').appendChild(newSVG); 92*c8dee2aaSAndroid Build Coastguard Worker 93*c8dee2aaSAndroid Build Coastguard Worker // Draw directly to Canvas 94*c8dee2aaSAndroid Build Coastguard Worker let ctx = document.getElementById('canvas1').getContext('2d'); 95*c8dee2aaSAndroid Build Coastguard Worker setCanvasSize(ctx, 200, 200); 96*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeStyle = 'green'; 97*c8dee2aaSAndroid Build Coastguard Worker ctx.fillStyle = 'white'; 98*c8dee2aaSAndroid Build Coastguard Worker ctx.scale(8, 8); 99*c8dee2aaSAndroid Build Coastguard Worker ctx.beginPath(); 100*c8dee2aaSAndroid Build Coastguard Worker firstPath.toCanvas(ctx); 101*c8dee2aaSAndroid Build Coastguard Worker ctx.stroke(); 102*c8dee2aaSAndroid Build Coastguard Worker 103*c8dee2aaSAndroid Build Coastguard Worker // create Path2D object and use it in a Canvas. 104*c8dee2aaSAndroid Build Coastguard Worker let path2D = firstPath.toPath2D(); 105*c8dee2aaSAndroid Build Coastguard Worker ctx = document.getElementById('canvas2').getContext('2d'); 106*c8dee2aaSAndroid Build Coastguard Worker setCanvasSize(ctx, 200, 200); 107*c8dee2aaSAndroid Build Coastguard Worker ctx.canvas.width = 200 108*c8dee2aaSAndroid Build Coastguard Worker ctx.scale(8, 8); 109*c8dee2aaSAndroid Build Coastguard Worker ctx.fillStyle = 'purple'; 110*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeStyle = 'orange'; 111*c8dee2aaSAndroid Build Coastguard Worker ctx.fill(path2D); 112*c8dee2aaSAndroid Build Coastguard Worker ctx.stroke(path2D); 113*c8dee2aaSAndroid Build Coastguard Worker 114*c8dee2aaSAndroid Build Coastguard Worker // clean up memory and call destructors in the c++ code (if any). 115*c8dee2aaSAndroid Build Coastguard Worker // See http://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/embind.html?highlight=memory#memory-management 116*c8dee2aaSAndroid Build Coastguard Worker firstPath.delete(); 117*c8dee2aaSAndroid Build Coastguard Worker secondPath.delete(); 118*c8dee2aaSAndroid Build Coastguard Worker } 119*c8dee2aaSAndroid Build Coastguard Worker 120*c8dee2aaSAndroid Build Coastguard Worker function Path2DExample(PathKit) { 121*c8dee2aaSAndroid Build Coastguard Worker let objs = [new Path2D(), PathKit.NewPath(), new Path2D(), PathKit.NewPath()]; 122*c8dee2aaSAndroid Build Coastguard Worker let canvases = [ 123*c8dee2aaSAndroid Build Coastguard Worker document.getElementById('canvas3').getContext('2d'), 124*c8dee2aaSAndroid Build Coastguard Worker document.getElementById('canvas4').getContext('2d') 125*c8dee2aaSAndroid Build Coastguard Worker ]; 126*c8dee2aaSAndroid Build Coastguard Worker 127*c8dee2aaSAndroid Build Coastguard Worker for (i = 0; i <= 1; i++) { 128*c8dee2aaSAndroid Build Coastguard Worker let path = objs[i]; 129*c8dee2aaSAndroid Build Coastguard Worker 130*c8dee2aaSAndroid Build Coastguard Worker path.moveTo(20, 5); 131*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(30, 20); 132*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(40, 10); 133*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(50, 20); 134*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(60, 0); 135*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(20, 5); 136*c8dee2aaSAndroid Build Coastguard Worker 137*c8dee2aaSAndroid Build Coastguard Worker path.moveTo(20, 80); 138*c8dee2aaSAndroid Build Coastguard Worker path.bezierCurveTo(90, 10, 160, 150, 190, 10); 139*c8dee2aaSAndroid Build Coastguard Worker 140*c8dee2aaSAndroid Build Coastguard Worker path.moveTo(36, 148); 141*c8dee2aaSAndroid Build Coastguard Worker path.quadraticCurveTo(66, 188, 120, 136); 142*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(36, 148); 143*c8dee2aaSAndroid Build Coastguard Worker 144*c8dee2aaSAndroid Build Coastguard Worker path.rect(5, 170, 20, 20); 145*c8dee2aaSAndroid Build Coastguard Worker 146*c8dee2aaSAndroid Build Coastguard Worker path.moveTo(150, 180); 147*c8dee2aaSAndroid Build Coastguard Worker path.arcTo(150, 100, 50, 200, 20); 148*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(160, 160); 149*c8dee2aaSAndroid Build Coastguard Worker 150*c8dee2aaSAndroid Build Coastguard Worker path.moveTo(20, 120); 151*c8dee2aaSAndroid Build Coastguard Worker path.arc(20, 120, 18, 0, 1.75 * Math.PI); 152*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(20, 120); 153*c8dee2aaSAndroid Build Coastguard Worker 154*c8dee2aaSAndroid Build Coastguard Worker let secondPath = objs[i+2]; 155*c8dee2aaSAndroid Build Coastguard Worker secondPath.ellipse(130, 25, 30, 10, -1*Math.PI/8, Math.PI/6, 1.5*Math.PI, false); 156*c8dee2aaSAndroid Build Coastguard Worker 157*c8dee2aaSAndroid Build Coastguard Worker path.addPath(secondPath); 158*c8dee2aaSAndroid Build Coastguard Worker 159*c8dee2aaSAndroid Build Coastguard Worker let m = document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGMatrix(); 160*c8dee2aaSAndroid Build Coastguard Worker m.a = 1; m.b = 0; 161*c8dee2aaSAndroid Build Coastguard Worker m.c = 0; m.d = 1; 162*c8dee2aaSAndroid Build Coastguard Worker m.e = 0; m.f = 20.5; 163*c8dee2aaSAndroid Build Coastguard Worker 164*c8dee2aaSAndroid Build Coastguard Worker path.addPath(secondPath, m); 165*c8dee2aaSAndroid Build Coastguard Worker // With PathKit, one can also just provide the 6 params as floats, to avoid 166*c8dee2aaSAndroid Build Coastguard Worker // the overhead of making an SVGMatrix 167*c8dee2aaSAndroid Build Coastguard Worker // path.addPath(secondPath, 1, 0, 0, 1, 0, 20.5); 168*c8dee2aaSAndroid Build Coastguard Worker 169*c8dee2aaSAndroid Build Coastguard Worker canvasCtx = canvases[i]; 170*c8dee2aaSAndroid Build Coastguard Worker canvasCtx.fillStyle = 'blue'; 171*c8dee2aaSAndroid Build Coastguard Worker setCanvasSize(canvasCtx, 300, 300); 172*c8dee2aaSAndroid Build Coastguard Worker canvasCtx.scale(1.5, 1.5); 173*c8dee2aaSAndroid Build Coastguard Worker if (path.toPath2D) { 174*c8dee2aaSAndroid Build Coastguard Worker canvasCtx.stroke(path.toPath2D()); 175*c8dee2aaSAndroid Build Coastguard Worker } else { 176*c8dee2aaSAndroid Build Coastguard Worker canvasCtx.stroke(path); 177*c8dee2aaSAndroid Build Coastguard Worker } 178*c8dee2aaSAndroid Build Coastguard Worker } 179*c8dee2aaSAndroid Build Coastguard Worker 180*c8dee2aaSAndroid Build Coastguard Worker 181*c8dee2aaSAndroid Build Coastguard Worker objs[1].delete(); 182*c8dee2aaSAndroid Build Coastguard Worker } 183*c8dee2aaSAndroid Build Coastguard Worker 184*c8dee2aaSAndroid Build Coastguard Worker // see https://fiddle.skia.org/c/@discrete_path 185*c8dee2aaSAndroid Build Coastguard Worker function drawStar(path) { 186*c8dee2aaSAndroid Build Coastguard Worker let R = 115.2, C = 128.0; 187*c8dee2aaSAndroid Build Coastguard Worker path.moveTo(C + R + 22, C); 188*c8dee2aaSAndroid Build Coastguard Worker for (let i = 1; i < 8; i++) { 189*c8dee2aaSAndroid Build Coastguard Worker let a = 2.6927937 * i; 190*c8dee2aaSAndroid Build Coastguard Worker path.lineTo(C + R * Math.cos(a) + 22, C + R * Math.sin(a)); 191*c8dee2aaSAndroid Build Coastguard Worker } 192*c8dee2aaSAndroid Build Coastguard Worker path.closePath(); 193*c8dee2aaSAndroid Build Coastguard Worker return path; 194*c8dee2aaSAndroid Build Coastguard Worker } 195*c8dee2aaSAndroid Build Coastguard Worker 196*c8dee2aaSAndroid Build Coastguard Worker function PathEffectsExample(PathKit) { 197*c8dee2aaSAndroid Build Coastguard Worker let effects = [ 198*c8dee2aaSAndroid Build Coastguard Worker // no-op 199*c8dee2aaSAndroid Build Coastguard Worker (path) => path, 200*c8dee2aaSAndroid Build Coastguard Worker // dash 201*c8dee2aaSAndroid Build Coastguard Worker (path, counter) => path.dash(10, 3, counter/5), 202*c8dee2aaSAndroid Build Coastguard Worker // trim (takes optional 3rd param for returning the trimmed part 203*c8dee2aaSAndroid Build Coastguard Worker // or the complement) 204*c8dee2aaSAndroid Build Coastguard Worker (path, counter) => path.trim((counter/100) % 1, 0.8, false), 205*c8dee2aaSAndroid Build Coastguard Worker // simplify 206*c8dee2aaSAndroid Build Coastguard Worker (path) => path.simplify(), 207*c8dee2aaSAndroid Build Coastguard Worker // stroke 208*c8dee2aaSAndroid Build Coastguard Worker (path, counter) => path.stroke({ 209*c8dee2aaSAndroid Build Coastguard Worker width: 10 * (Math.sin(counter/30) + 1), 210*c8dee2aaSAndroid Build Coastguard Worker join: PathKit.StrokeJoin.BEVEL, 211*c8dee2aaSAndroid Build Coastguard Worker cap: PathKit.StrokeCap.BUTT, 212*c8dee2aaSAndroid Build Coastguard Worker miter_limit: 1, 213*c8dee2aaSAndroid Build Coastguard Worker }), 214*c8dee2aaSAndroid Build Coastguard Worker // "offset effect", that is, making a border around the shape. 215*c8dee2aaSAndroid Build Coastguard Worker (path, counter) => { 216*c8dee2aaSAndroid Build Coastguard Worker let orig = path.copy(); 217*c8dee2aaSAndroid Build Coastguard Worker path.stroke({ 218*c8dee2aaSAndroid Build Coastguard Worker width: 10 + (counter / 4) % 50, 219*c8dee2aaSAndroid Build Coastguard Worker join: PathKit.StrokeJoin.ROUND, 220*c8dee2aaSAndroid Build Coastguard Worker cap: PathKit.StrokeCap.SQUARE, 221*c8dee2aaSAndroid Build Coastguard Worker }) 222*c8dee2aaSAndroid Build Coastguard Worker .op(orig, PathKit.PathOp.DIFFERENCE); 223*c8dee2aaSAndroid Build Coastguard Worker orig.delete(); 224*c8dee2aaSAndroid Build Coastguard Worker }, 225*c8dee2aaSAndroid Build Coastguard Worker (path, counter) => { 226*c8dee2aaSAndroid Build Coastguard Worker let simplified = path.simplify().copy(); 227*c8dee2aaSAndroid Build Coastguard Worker path.stroke({ 228*c8dee2aaSAndroid Build Coastguard Worker width: 2 + (counter / 2) % 100, 229*c8dee2aaSAndroid Build Coastguard Worker join: PathKit.StrokeJoin.BEVEL, 230*c8dee2aaSAndroid Build Coastguard Worker cap: PathKit.StrokeCap.BUTT, 231*c8dee2aaSAndroid Build Coastguard Worker }) 232*c8dee2aaSAndroid Build Coastguard Worker .op(simplified, PathKit.PathOp.REVERSE_DIFFERENCE); 233*c8dee2aaSAndroid Build Coastguard Worker simplified.delete(); 234*c8dee2aaSAndroid Build Coastguard Worker } 235*c8dee2aaSAndroid Build Coastguard Worker ]; 236*c8dee2aaSAndroid Build Coastguard Worker 237*c8dee2aaSAndroid Build Coastguard Worker let names = ["(plain)", "Dash", "Trim", "Simplify", "Stroke", "Grow", "Shrink"]; 238*c8dee2aaSAndroid Build Coastguard Worker 239*c8dee2aaSAndroid Build Coastguard Worker let counter = 0; 240*c8dee2aaSAndroid Build Coastguard Worker function frame() { 241*c8dee2aaSAndroid Build Coastguard Worker counter++; 242*c8dee2aaSAndroid Build Coastguard Worker for (let i = 0; i < effects.length; i++) { 243*c8dee2aaSAndroid Build Coastguard Worker let path = PathKit.NewPath(); 244*c8dee2aaSAndroid Build Coastguard Worker drawStar(path); 245*c8dee2aaSAndroid Build Coastguard Worker 246*c8dee2aaSAndroid Build Coastguard Worker // The transforms apply directly to the path. 247*c8dee2aaSAndroid Build Coastguard Worker effects[i](path, counter); 248*c8dee2aaSAndroid Build Coastguard Worker 249*c8dee2aaSAndroid Build Coastguard Worker let ctx = document.getElementById(`canvas${i+5}`).getContext('2d'); 250*c8dee2aaSAndroid Build Coastguard Worker setCanvasSize(ctx, 300, 300); 251*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeStyle = '#3c597a'; 252*c8dee2aaSAndroid Build Coastguard Worker ctx.fillStyle = '#3c597a'; 253*c8dee2aaSAndroid Build Coastguard Worker if (i >=4 ) { 254*c8dee2aaSAndroid Build Coastguard Worker ctx.fill(path.toPath2D(), path.getFillTypeString()); 255*c8dee2aaSAndroid Build Coastguard Worker } else { 256*c8dee2aaSAndroid Build Coastguard Worker ctx.stroke(path.toPath2D()); 257*c8dee2aaSAndroid Build Coastguard Worker } 258*c8dee2aaSAndroid Build Coastguard Worker 259*c8dee2aaSAndroid Build Coastguard Worker ctx.font = '42px monospace'; 260*c8dee2aaSAndroid Build Coastguard Worker 261*c8dee2aaSAndroid Build Coastguard Worker let x = 150-ctx.measureText(names[i]).width/2; 262*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeText(names[i], x, 290); 263*c8dee2aaSAndroid Build Coastguard Worker 264*c8dee2aaSAndroid Build Coastguard Worker path.delete(); 265*c8dee2aaSAndroid Build Coastguard Worker } 266*c8dee2aaSAndroid Build Coastguard Worker window.requestAnimationFrame(frame); 267*c8dee2aaSAndroid Build Coastguard Worker } 268*c8dee2aaSAndroid Build Coastguard Worker window.requestAnimationFrame(frame); 269*c8dee2aaSAndroid Build Coastguard Worker } 270*c8dee2aaSAndroid Build Coastguard Worker 271*c8dee2aaSAndroid Build Coastguard Worker function MatrixTransformExample(PathKit) { 272*c8dee2aaSAndroid Build Coastguard Worker // Creates an animated star that twists and moves. 273*c8dee2aaSAndroid Build Coastguard Worker let ctx = document.getElementById('canvasTransform').getContext('2d'); 274*c8dee2aaSAndroid Build Coastguard Worker setCanvasSize(ctx, 300, 300); 275*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeStyle = '#3c597a'; 276*c8dee2aaSAndroid Build Coastguard Worker 277*c8dee2aaSAndroid Build Coastguard Worker let path = drawStar(PathKit.NewPath()); 278*c8dee2aaSAndroid Build Coastguard Worker // TODO(kjlubick): Perhaps expose some matrix helper functions to allow 279*c8dee2aaSAndroid Build Coastguard Worker // clients to build their own matrices like this? 280*c8dee2aaSAndroid Build Coastguard Worker // These matrices represent a 2 degree rotation and a 1% scale factor. 281*c8dee2aaSAndroid Build Coastguard Worker let scaleUp = [1.0094, -0.0352, 3.1041, 282*c8dee2aaSAndroid Build Coastguard Worker 0.0352, 1.0094, -6.4885, 283*c8dee2aaSAndroid Build Coastguard Worker 0 , 0 , 1]; 284*c8dee2aaSAndroid Build Coastguard Worker 285*c8dee2aaSAndroid Build Coastguard Worker let scaleDown = [ 0.9895, 0.0346, -2.8473, 286*c8dee2aaSAndroid Build Coastguard Worker -0.0346, 0.9895, 6.5276, 287*c8dee2aaSAndroid Build Coastguard Worker 0 , 0 , 1]; 288*c8dee2aaSAndroid Build Coastguard Worker 289*c8dee2aaSAndroid Build Coastguard Worker let i = 0; 290*c8dee2aaSAndroid Build Coastguard Worker function frame(){ 291*c8dee2aaSAndroid Build Coastguard Worker i++; 292*c8dee2aaSAndroid Build Coastguard Worker if (Math.round(i/100) % 2) { 293*c8dee2aaSAndroid Build Coastguard Worker path.transform(scaleDown); 294*c8dee2aaSAndroid Build Coastguard Worker } else { 295*c8dee2aaSAndroid Build Coastguard Worker path.transform(scaleUp); 296*c8dee2aaSAndroid Build Coastguard Worker } 297*c8dee2aaSAndroid Build Coastguard Worker 298*c8dee2aaSAndroid Build Coastguard Worker ctx.clearRect(0, 0, 300, 300); 299*c8dee2aaSAndroid Build Coastguard Worker ctx.stroke(path.toPath2D()); 300*c8dee2aaSAndroid Build Coastguard Worker 301*c8dee2aaSAndroid Build Coastguard Worker ctx.font = '42px monospace'; 302*c8dee2aaSAndroid Build Coastguard Worker let x = 150-ctx.measureText('Transform').width/2; 303*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeText('Transform', x, 290); 304*c8dee2aaSAndroid Build Coastguard Worker 305*c8dee2aaSAndroid Build Coastguard Worker window.requestAnimationFrame(frame); 306*c8dee2aaSAndroid Build Coastguard Worker } 307*c8dee2aaSAndroid Build Coastguard Worker window.requestAnimationFrame(frame); 308*c8dee2aaSAndroid Build Coastguard Worker } 309*c8dee2aaSAndroid Build Coastguard Worker 310*c8dee2aaSAndroid Build Coastguard Worker function FilledSVGExample(PathKit) { 311*c8dee2aaSAndroid Build Coastguard Worker let innerRect = PathKit.NewPath(); 312*c8dee2aaSAndroid Build Coastguard Worker innerRect.rect(80, 100, 40, 40); 313*c8dee2aaSAndroid Build Coastguard Worker 314*c8dee2aaSAndroid Build Coastguard Worker let outerRect = PathKit.NewPath(); 315*c8dee2aaSAndroid Build Coastguard Worker outerRect.rect(50, 10, 100, 100) 316*c8dee2aaSAndroid Build Coastguard Worker .op(innerRect, PathKit.PathOp.XOR); 317*c8dee2aaSAndroid Build Coastguard Worker 318*c8dee2aaSAndroid Build Coastguard Worker let str = outerRect.toSVGString(); 319*c8dee2aaSAndroid Build Coastguard Worker 320*c8dee2aaSAndroid Build Coastguard Worker let diffSVG = document.createElementNS('http://www.w3.org/2000/svg', 'path'); 321*c8dee2aaSAndroid Build Coastguard Worker diffSVG.setAttribute('stroke', 'red'); 322*c8dee2aaSAndroid Build Coastguard Worker diffSVG.setAttribute('fill', 'black'); 323*c8dee2aaSAndroid Build Coastguard Worker // force fill-rule to nonzero to demonstrate difference 324*c8dee2aaSAndroid Build Coastguard Worker diffSVG.setAttribute('fill-rule', 'nonzero'); 325*c8dee2aaSAndroid Build Coastguard Worker diffSVG.setAttribute('d', str); 326*c8dee2aaSAndroid Build Coastguard Worker document.getElementById('svg2').appendChild(diffSVG); 327*c8dee2aaSAndroid Build Coastguard Worker 328*c8dee2aaSAndroid Build Coastguard Worker let unionSVG = document.createElementNS('http://www.w3.org/2000/svg', 'path'); 329*c8dee2aaSAndroid Build Coastguard Worker unionSVG.setAttribute('stroke', 'red'); 330*c8dee2aaSAndroid Build Coastguard Worker unionSVG.setAttribute('fill', 'black'); 331*c8dee2aaSAndroid Build Coastguard Worker // ask what the path thinks fill-rule should be ('evenodd') 332*c8dee2aaSAndroid Build Coastguard Worker // SVG and Canvas both use the same keys ('nonzero' and 'evenodd') and both 333*c8dee2aaSAndroid Build Coastguard Worker // default to 'nonzero', so one call supports both. 334*c8dee2aaSAndroid Build Coastguard Worker unionSVG.setAttribute('fill-rule', outerRect.getFillTypeString()); 335*c8dee2aaSAndroid Build Coastguard Worker unionSVG.setAttribute('d', str); 336*c8dee2aaSAndroid Build Coastguard Worker document.getElementById('svg3').appendChild(unionSVG); 337*c8dee2aaSAndroid Build Coastguard Worker 338*c8dee2aaSAndroid Build Coastguard Worker outerRect.delete(); 339*c8dee2aaSAndroid Build Coastguard Worker innerRect.delete(); 340*c8dee2aaSAndroid Build Coastguard Worker } 341*c8dee2aaSAndroid Build Coastguard Worker 342*c8dee2aaSAndroid Build Coastguard Worker function CubicSolverExample(PathKit) { 343*c8dee2aaSAndroid Build Coastguard Worker let ctx = document.getElementById('cubics').getContext('2d'); 344*c8dee2aaSAndroid Build Coastguard Worker setCanvasSize(ctx, 300, 300); 345*c8dee2aaSAndroid Build Coastguard Worker // Draw lines between cp0 (0, 0) and cp1 and then cp2 and cp3 (1, 1) 346*c8dee2aaSAndroid Build Coastguard Worker // scaled up to be on a 300x300 grid instead of unit square 347*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeStyle = 'black'; 348*c8dee2aaSAndroid Build Coastguard Worker ctx.beginPath(); 349*c8dee2aaSAndroid Build Coastguard Worker ctx.moveTo(0, 0); 350*c8dee2aaSAndroid Build Coastguard Worker ctx.lineTo(0.1 * 300, 0.5*300); 351*c8dee2aaSAndroid Build Coastguard Worker 352*c8dee2aaSAndroid Build Coastguard Worker ctx.moveTo(0.5 * 300, 0.1*300); 353*c8dee2aaSAndroid Build Coastguard Worker ctx.lineTo(300, 300); 354*c8dee2aaSAndroid Build Coastguard Worker ctx.stroke(); 355*c8dee2aaSAndroid Build Coastguard Worker 356*c8dee2aaSAndroid Build Coastguard Worker 357*c8dee2aaSAndroid Build Coastguard Worker ctx.strokeStyle = 'green'; 358*c8dee2aaSAndroid Build Coastguard Worker ctx.beginPath(); 359*c8dee2aaSAndroid Build Coastguard Worker 360*c8dee2aaSAndroid Build Coastguard Worker for (let x = 0; x < 300; x++) { 361*c8dee2aaSAndroid Build Coastguard Worker // scale X into unit square 362*c8dee2aaSAndroid Build Coastguard Worker let y = PathKit.cubicYFromX(0.1, 0.5, 0.5, 0.1, x/300); 363*c8dee2aaSAndroid Build Coastguard Worker ctx.arc(x, y*300, 2, 0, 2*Math.PI); 364*c8dee2aaSAndroid Build Coastguard Worker } 365*c8dee2aaSAndroid Build Coastguard Worker ctx.stroke(); 366*c8dee2aaSAndroid Build Coastguard Worker } 367*c8dee2aaSAndroid Build Coastguard Worker 368*c8dee2aaSAndroid Build Coastguard Worker</script> 369