blob: dd9c68257a4c483b8db6245b7a7a5565fc3ef5f2 [file] [log] [blame]
Kevin Lubicka40f8322018-12-17 16:01:36 -05001// CanvasPath methods, which all take an SkPath 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.arcTo(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.fBottom - bounds.fTop) || (bounds.fRight - bounds.fLeft)) {
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.arcTo(oval, startDegrees, halfSweep, false);
54 skpath.arcTo(oval, startDegrees + halfSweep, halfSweep, false);
55 return;
56 }
57 skpath.arcTo(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.SkMatrix.rotated(rotation, x, y);
102 var rotatedInvert = CanvasKit.SkMatrix.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 if (!allAreFinite([x, y, width, height])) {
138 return;
139 }
140 // https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-rect
141 skpath.addRect(x, y, x+width, y+height);
142}
143
144function Path2D(path) {
145 this._path = null;
146 if (typeof path === 'string') {
147 this._path = CanvasKit.MakePathFromSVGString(path);
148 } else if (path && path._getPath) {
149 this._path = path._getPath().copy();
150 } else {
151 this._path = new CanvasKit.SkPath();
152 }
153
154 this._getPath = function() {
155 return this._path;
156 }
157
158 this.addPath = function(path2d, transform) {
159 if (!transform) {
160 transform = {
161 'a': 1, 'c': 0, 'e': 0,
162 'b': 0, 'd': 1, 'f': 0,
163 };
164 }
165 this._path.addPath(path2d._getPath(), [transform.a, transform.c, transform.e,
166 transform.b, transform.d, transform.f]);
167 }
168
169 this.arc = function(x, y, radius, startAngle, endAngle, ccw) {
170 arc(this._path, x, y, radius, startAngle, endAngle, ccw);
171 }
172
173 this.arcTo = function(x1, y1, x2, y2, radius) {
174 arcTo(this._path, x1, y1, x2, y2, radius);
175 }
176
177 this.bezierCurveTo = function(cp1x, cp1y, cp2x, cp2y, x, y) {
178 bezierCurveTo(this._path, cp1x, cp1y, cp2x, cp2y, x, y);
179 }
180
181 this.closePath = function() {
182 closePath(this._path);
183 }
184
185 this.ellipse = function(x, y, radiusX, radiusY, rotation,
186 startAngle, endAngle, ccw) {
187 ellipse(this._path, x, y, radiusX, radiusY, rotation,
188 startAngle, endAngle, ccw);
189 }
190
191 this.lineTo = function(x, y) {
192 lineTo(this._path, x, y);
193 }
194
195 this.moveTo = function(x, y) {
196 moveTo(this._path, x, y);
197 }
198
199 this.quadraticCurveTo = function(cpx, cpy, x, y) {
200 quadraticCurveTo(this._path, cpx, cpy, x, y);
201 }
202
203 this.rect = function(x, y, width, height) {
204 rect(this._path, x, y, width, height);
205 }
206}