1// CanvasPath methods, which all take an Path object as the first param 2 3function arc(skpath, x, y, radius, startAngle, endAngle, ccw) { 4 // As per https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-arc 5 // arc is essentially a simpler version of ellipse. 6 ellipse(skpath, x, y, radius, radius, 0, startAngle, endAngle, ccw); 7} 8 9function arcTo(skpath, x1, y1, x2, y2, radius) { 10 if (!allAreFinite([x1, y1, x2, y2, radius])) { 11 return; 12 } 13 if (radius < 0) { 14 throw 'radii cannot be negative'; 15 } 16 if (skpath.isEmpty()) { 17 skpath.moveTo(x1, y1); 18 } 19 skpath.arcToTangent(x1, y1, x2, y2, radius); 20} 21 22function bezierCurveTo(skpath, cp1x, cp1y, cp2x, cp2y, x, y) { 23 if (!allAreFinite([cp1x, cp1y, cp2x, cp2y, x, y])) { 24 return; 25 } 26 if (skpath.isEmpty()) { 27 skpath.moveTo(cp1x, cp1y); 28 } 29 skpath.cubicTo(cp1x, cp1y, cp2x, cp2y, x, y); 30} 31 32function closePath(skpath) { 33 if (skpath.isEmpty()) { 34 return; 35 } 36 // Check to see if we are not just a single point 37 var bounds = skpath.getBounds(); 38 if ((bounds[3] - bounds[1]) || (bounds[2] - bounds[0])) { 39 skpath.close(); 40 } 41} 42 43function _ellipseHelper(skpath, x, y, radiusX, radiusY, startAngle, endAngle) { 44 var sweepDegrees = radiansToDegrees(endAngle - startAngle); 45 var startDegrees = radiansToDegrees(startAngle); 46 47 var oval = CanvasKit.LTRBRect(x - radiusX, y - radiusY, x + radiusX, y + radiusY); 48 49 // draw in 2 180 degree segments because trying to draw all 360 degrees at once 50 // draws nothing. 51 if (almostEqual(Math.abs(sweepDegrees), 360)) { 52 var halfSweep = sweepDegrees/2; 53 skpath.arcToOval(oval, startDegrees, halfSweep, false); 54 skpath.arcToOval(oval, startDegrees + halfSweep, halfSweep, false); 55 return; 56 } 57 skpath.arcToOval(oval, startDegrees, sweepDegrees, false); 58} 59 60function ellipse(skpath, x, y, radiusX, radiusY, rotation, 61 startAngle, endAngle, ccw) { 62 if (!allAreFinite([x, y, radiusX, radiusY, rotation, startAngle, endAngle])) { 63 return; 64 } 65 if (radiusX < 0 || radiusY < 0) { 66 throw 'radii cannot be negative'; 67 } 68 69 // based off of CanonicalizeAngle in Chrome 70 var tao = 2 * Math.PI; 71 var newStartAngle = startAngle % tao; 72 if (newStartAngle < 0) { 73 newStartAngle += tao; 74 } 75 var delta = newStartAngle - startAngle; 76 startAngle = newStartAngle; 77 endAngle += delta; 78 79 // Based off of AdjustEndAngle in Chrome. 80 if (!ccw && (endAngle - startAngle) >= tao) { 81 // Draw complete ellipse 82 endAngle = startAngle + tao; 83 } else if (ccw && (startAngle - endAngle) >= tao) { 84 // Draw complete ellipse 85 endAngle = startAngle - tao; 86 } else if (!ccw && startAngle > endAngle) { 87 endAngle = startAngle + (tao - (startAngle - endAngle) % tao); 88 } else if (ccw && startAngle < endAngle) { 89 endAngle = startAngle - (tao - (endAngle - startAngle) % tao); 90 } 91 92 // Based off of Chrome's implementation in 93 // https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/path.cc 94 // of note, can't use addArc or addOval because they close the arc, which 95 // the spec says not to do (unless the user explicitly calls closePath). 96 // This throws off points being in/out of the arc. 97 if (!rotation) { 98 _ellipseHelper(skpath, x, y, radiusX, radiusY, startAngle, endAngle); 99 return; 100 } 101 var rotated = CanvasKit.Matrix.rotated(rotation, x, y); 102 var rotatedInvert = CanvasKit.Matrix.rotated(-rotation, x, y); 103 skpath.transform(rotatedInvert); 104 _ellipseHelper(skpath, x, y, radiusX, radiusY, startAngle, endAngle); 105 skpath.transform(rotated); 106} 107 108function lineTo(skpath, x, y) { 109 if (!allAreFinite([x, y])) { 110 return; 111 } 112 // A lineTo without a previous point has a moveTo inserted before it 113 if (skpath.isEmpty()) { 114 skpath.moveTo(x, y); 115 } 116 skpath.lineTo(x, y); 117} 118 119function moveTo(skpath, x, y) { 120 if (!allAreFinite([x, y])) { 121 return; 122 } 123 skpath.moveTo(x, y); 124} 125 126function quadraticCurveTo(skpath, cpx, cpy, x, y) { 127 if (!allAreFinite([cpx, cpy, x, y])) { 128 return; 129 } 130 if (skpath.isEmpty()) { 131 skpath.moveTo(cpx, cpy); 132 } 133 skpath.quadTo(cpx, cpy, x, y); 134} 135 136function rect(skpath, x, y, width, height) { 137 var rect = CanvasKit.XYWHRect(x, y, width, height); 138 if (!allAreFinite(rect)) { 139 return; 140 } 141 // https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-rect 142 skpath.addRect(rect); 143} 144 145function Path2D(path) { 146 this._path = null; 147 if (typeof path === 'string') { 148 this._path = CanvasKit.Path.MakeFromSVGString(path); 149 } else if (path && path._getPath) { 150 this._path = path._getPath().copy(); 151 } else { 152 this._path = new CanvasKit.Path(); 153 } 154 155 this._getPath = function() { 156 return this._path; 157 } 158 159 this.addPath = function(path2d, transform) { 160 if (!transform) { 161 transform = { 162 'a': 1, 'c': 0, 'e': 0, 163 'b': 0, 'd': 1, 'f': 0, 164 }; 165 } 166 this._path.addPath(path2d._getPath(), [transform.a, transform.c, transform.e, 167 transform.b, transform.d, transform.f]); 168 } 169 170 this.arc = function(x, y, radius, startAngle, endAngle, ccw) { 171 arc(this._path, x, y, radius, startAngle, endAngle, ccw); 172 } 173 174 this.arcTo = function(x1, y1, x2, y2, radius) { 175 arcTo(this._path, x1, y1, x2, y2, radius); 176 } 177 178 this.bezierCurveTo = function(cp1x, cp1y, cp2x, cp2y, x, y) { 179 bezierCurveTo(this._path, cp1x, cp1y, cp2x, cp2y, x, y); 180 } 181 182 this.closePath = function() { 183 closePath(this._path); 184 } 185 186 this.ellipse = function(x, y, radiusX, radiusY, rotation, 187 startAngle, endAngle, ccw) { 188 ellipse(this._path, x, y, radiusX, radiusY, rotation, 189 startAngle, endAngle, ccw); 190 } 191 192 this.lineTo = function(x, y) { 193 lineTo(this._path, x, y); 194 } 195 196 this.moveTo = function(x, y) { 197 moveTo(this._path, x, y); 198 } 199 200 this.quadraticCurveTo = function(cpx, cpy, x, y) { 201 quadraticCurveTo(this._path, cpx, cpy, x, y); 202 } 203 204 this.rect = function(x, y, width, height) { 205 rect(this._path, x, y, width, height); 206 } 207} 208