• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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