| // CanvasPath methods, which all take an SkPath object as the first param |
| |
| function arc(skpath, x, y, radius, startAngle, endAngle, ccw) { |
| // As per https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-arc |
| // arc is essentially a simpler version of ellipse. |
| ellipse(skpath, x, y, radius, radius, 0, startAngle, endAngle, ccw); |
| } |
| |
| function arcTo(skpath, x1, y1, x2, y2, radius) { |
| if (!allAreFinite([x1, y1, x2, y2, radius])) { |
| return; |
| } |
| if (radius < 0) { |
| throw 'radii cannot be negative'; |
| } |
| if (skpath.isEmpty()) { |
| skpath.moveTo(x1, y1); |
| } |
| skpath.arcTo(x1, y1, x2, y2, radius); |
| } |
| |
| function bezierCurveTo(skpath, cp1x, cp1y, cp2x, cp2y, x, y) { |
| if (!allAreFinite([cp1x, cp1y, cp2x, cp2y, x, y])) { |
| return; |
| } |
| if (skpath.isEmpty()) { |
| skpath.moveTo(cp1x, cp1y); |
| } |
| skpath.cubicTo(cp1x, cp1y, cp2x, cp2y, x, y); |
| } |
| |
| function closePath(skpath) { |
| if (skpath.isEmpty()) { |
| return; |
| } |
| // Check to see if we are not just a single point |
| var bounds = skpath.getBounds(); |
| if ((bounds.fBottom - bounds.fTop) || (bounds.fRight - bounds.fLeft)) { |
| skpath.close(); |
| } |
| } |
| |
| function _ellipseHelper(skpath, x, y, radiusX, radiusY, startAngle, endAngle) { |
| var sweepDegrees = radiansToDegrees(endAngle - startAngle); |
| var startDegrees = radiansToDegrees(startAngle); |
| |
| var oval = CanvasKit.LTRBRect(x - radiusX, y - radiusY, x + radiusX, y + radiusY); |
| |
| // draw in 2 180 degree segments because trying to draw all 360 degrees at once |
| // draws nothing. |
| if (almostEqual(Math.abs(sweepDegrees), 360)) { |
| var halfSweep = sweepDegrees/2; |
| skpath.arcTo(oval, startDegrees, halfSweep, false); |
| skpath.arcTo(oval, startDegrees + halfSweep, halfSweep, false); |
| return; |
| } |
| skpath.arcTo(oval, startDegrees, sweepDegrees, false); |
| } |
| |
| function ellipse(skpath, x, y, radiusX, radiusY, rotation, |
| startAngle, endAngle, ccw) { |
| if (!allAreFinite([x, y, radiusX, radiusY, rotation, startAngle, endAngle])) { |
| return; |
| } |
| if (radiusX < 0 || radiusY < 0) { |
| throw 'radii cannot be negative'; |
| } |
| |
| // based off of CanonicalizeAngle in Chrome |
| var tao = 2 * Math.PI; |
| var newStartAngle = startAngle % tao; |
| if (newStartAngle < 0) { |
| newStartAngle += tao; |
| } |
| var delta = newStartAngle - startAngle; |
| startAngle = newStartAngle; |
| endAngle += delta; |
| |
| // Based off of AdjustEndAngle in Chrome. |
| if (!ccw && (endAngle - startAngle) >= tao) { |
| // Draw complete ellipse |
| endAngle = startAngle + tao; |
| } else if (ccw && (startAngle - endAngle) >= tao) { |
| // Draw complete ellipse |
| endAngle = startAngle - tao; |
| } else if (!ccw && startAngle > endAngle) { |
| endAngle = startAngle + (tao - (startAngle - endAngle) % tao); |
| } else if (ccw && startAngle < endAngle) { |
| endAngle = startAngle - (tao - (endAngle - startAngle) % tao); |
| } |
| |
| // Based off of Chrome's implementation in |
| // https://cs.chromium.org/chromium/src/third_party/blink/renderer/platform/graphics/path.cc |
| // of note, can't use addArc or addOval because they close the arc, which |
| // the spec says not to do (unless the user explicitly calls closePath). |
| // This throws off points being in/out of the arc. |
| if (!rotation) { |
| _ellipseHelper(skpath, x, y, radiusX, radiusY, startAngle, endAngle); |
| return; |
| } |
| var rotated = CanvasKit.SkMatrix.rotated(rotation, x, y); |
| var rotatedInvert = CanvasKit.SkMatrix.rotated(-rotation, x, y); |
| skpath.transform(rotatedInvert); |
| _ellipseHelper(skpath, x, y, radiusX, radiusY, startAngle, endAngle); |
| skpath.transform(rotated); |
| } |
| |
| function lineTo(skpath, x, y) { |
| if (!allAreFinite([x, y])) { |
| return; |
| } |
| // A lineTo without a previous point has a moveTo inserted before it |
| if (skpath.isEmpty()) { |
| skpath.moveTo(x, y); |
| } |
| skpath.lineTo(x, y); |
| } |
| |
| function moveTo(skpath, x, y) { |
| if (!allAreFinite([x, y])) { |
| return; |
| } |
| skpath.moveTo(x, y); |
| } |
| |
| function quadraticCurveTo(skpath, cpx, cpy, x, y) { |
| if (!allAreFinite([cpx, cpy, x, y])) { |
| return; |
| } |
| if (skpath.isEmpty()) { |
| skpath.moveTo(cpx, cpy); |
| } |
| skpath.quadTo(cpx, cpy, x, y); |
| } |
| |
| function rect(skpath, x, y, width, height) { |
| if (!allAreFinite([x, y, width, height])) { |
| return; |
| } |
| // https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-rect |
| skpath.addRect(x, y, x+width, y+height); |
| } |
| |
| function Path2D(path) { |
| this._path = null; |
| if (typeof path === 'string') { |
| this._path = CanvasKit.MakePathFromSVGString(path); |
| } else if (path && path._getPath) { |
| this._path = path._getPath().copy(); |
| } else { |
| this._path = new CanvasKit.SkPath(); |
| } |
| |
| this._getPath = function() { |
| return this._path; |
| } |
| |
| this.addPath = function(path2d, transform) { |
| if (!transform) { |
| transform = { |
| 'a': 1, 'c': 0, 'e': 0, |
| 'b': 0, 'd': 1, 'f': 0, |
| }; |
| } |
| this._path.addPath(path2d._getPath(), [transform.a, transform.c, transform.e, |
| transform.b, transform.d, transform.f]); |
| } |
| |
| this.arc = function(x, y, radius, startAngle, endAngle, ccw) { |
| arc(this._path, x, y, radius, startAngle, endAngle, ccw); |
| } |
| |
| this.arcTo = function(x1, y1, x2, y2, radius) { |
| arcTo(this._path, x1, y1, x2, y2, radius); |
| } |
| |
| this.bezierCurveTo = function(cp1x, cp1y, cp2x, cp2y, x, y) { |
| bezierCurveTo(this._path, cp1x, cp1y, cp2x, cp2y, x, y); |
| } |
| |
| this.closePath = function() { |
| closePath(this._path); |
| } |
| |
| this.ellipse = function(x, y, radiusX, radiusY, rotation, |
| startAngle, endAngle, ccw) { |
| ellipse(this._path, x, y, radiusX, radiusY, rotation, |
| startAngle, endAngle, ccw); |
| } |
| |
| this.lineTo = function(x, y) { |
| lineTo(this._path, x, y); |
| } |
| |
| this.moveTo = function(x, y) { |
| moveTo(this._path, x, y); |
| } |
| |
| this.quadraticCurveTo = function(cpx, cpy, x, y) { |
| quadraticCurveTo(this._path, cpx, cpy, x, y); |
| } |
| |
| this.rect = function(x, y, width, height) { |
| rect(this._path, x, y, width, height); |
| } |
| } |