1 | import PathProxy from '../core/PathProxy.js';
|
2 | import * as line from './line.js';
|
3 | import * as cubic from './cubic.js';
|
4 | import * as quadratic from './quadratic.js';
|
5 | import * as arc from './arc.js';
|
6 | import * as curve from '../core/curve.js';
|
7 | import windingLine from './windingLine.js';
|
8 | var CMD = PathProxy.CMD;
|
9 | var PI2 = Math.PI * 2;
|
10 | var EPSILON = 1e-4;
|
11 | function isAroundEqual(a, b) {
|
12 | return Math.abs(a - b) < EPSILON;
|
13 | }
|
14 | var roots = [-1, -1, -1];
|
15 | var extrema = [-1, -1];
|
16 | function swapExtrema() {
|
17 | var tmp = extrema[0];
|
18 | extrema[0] = extrema[1];
|
19 | extrema[1] = tmp;
|
20 | }
|
21 | function windingCubic(x0, y0, x1, y1, x2, y2, x3, y3, x, y) {
|
22 | if ((y > y0 && y > y1 && y > y2 && y > y3)
|
23 | || (y < y0 && y < y1 && y < y2 && y < y3)) {
|
24 | return 0;
|
25 | }
|
26 | var nRoots = curve.cubicRootAt(y0, y1, y2, y3, y, roots);
|
27 | if (nRoots === 0) {
|
28 | return 0;
|
29 | }
|
30 | else {
|
31 | var w = 0;
|
32 | var nExtrema = -1;
|
33 | var y0_ = void 0;
|
34 | var y1_ = void 0;
|
35 | for (var i = 0; i < nRoots; i++) {
|
36 | var t = roots[i];
|
37 | var unit = (t === 0 || t === 1) ? 0.5 : 1;
|
38 | var x_ = curve.cubicAt(x0, x1, x2, x3, t);
|
39 | if (x_ < x) {
|
40 | continue;
|
41 | }
|
42 | if (nExtrema < 0) {
|
43 | nExtrema = curve.cubicExtrema(y0, y1, y2, y3, extrema);
|
44 | if (extrema[1] < extrema[0] && nExtrema > 1) {
|
45 | swapExtrema();
|
46 | }
|
47 | y0_ = curve.cubicAt(y0, y1, y2, y3, extrema[0]);
|
48 | if (nExtrema > 1) {
|
49 | y1_ = curve.cubicAt(y0, y1, y2, y3, extrema[1]);
|
50 | }
|
51 | }
|
52 | if (nExtrema === 2) {
|
53 | if (t < extrema[0]) {
|
54 | w += y0_ < y0 ? unit : -unit;
|
55 | }
|
56 | else if (t < extrema[1]) {
|
57 | w += y1_ < y0_ ? unit : -unit;
|
58 | }
|
59 | else {
|
60 | w += y3 < y1_ ? unit : -unit;
|
61 | }
|
62 | }
|
63 | else {
|
64 | if (t < extrema[0]) {
|
65 | w += y0_ < y0 ? unit : -unit;
|
66 | }
|
67 | else {
|
68 | w += y3 < y0_ ? unit : -unit;
|
69 | }
|
70 | }
|
71 | }
|
72 | return w;
|
73 | }
|
74 | }
|
75 | function windingQuadratic(x0, y0, x1, y1, x2, y2, x, y) {
|
76 | if ((y > y0 && y > y1 && y > y2)
|
77 | || (y < y0 && y < y1 && y < y2)) {
|
78 | return 0;
|
79 | }
|
80 | var nRoots = curve.quadraticRootAt(y0, y1, y2, y, roots);
|
81 | if (nRoots === 0) {
|
82 | return 0;
|
83 | }
|
84 | else {
|
85 | var t = curve.quadraticExtremum(y0, y1, y2);
|
86 | if (t >= 0 && t <= 1) {
|
87 | var w = 0;
|
88 | var y_ = curve.quadraticAt(y0, y1, y2, t);
|
89 | for (var i = 0; i < nRoots; i++) {
|
90 | var unit = (roots[i] === 0 || roots[i] === 1) ? 0.5 : 1;
|
91 | var x_ = curve.quadraticAt(x0, x1, x2, roots[i]);
|
92 | if (x_ < x) {
|
93 | continue;
|
94 | }
|
95 | if (roots[i] < t) {
|
96 | w += y_ < y0 ? unit : -unit;
|
97 | }
|
98 | else {
|
99 | w += y2 < y_ ? unit : -unit;
|
100 | }
|
101 | }
|
102 | return w;
|
103 | }
|
104 | else {
|
105 | var unit = (roots[0] === 0 || roots[0] === 1) ? 0.5 : 1;
|
106 | var x_ = curve.quadraticAt(x0, x1, x2, roots[0]);
|
107 | if (x_ < x) {
|
108 | return 0;
|
109 | }
|
110 | return y2 < y0 ? unit : -unit;
|
111 | }
|
112 | }
|
113 | }
|
114 | function windingArc(cx, cy, r, startAngle, endAngle, anticlockwise, x, y) {
|
115 | y -= cy;
|
116 | if (y > r || y < -r) {
|
117 | return 0;
|
118 | }
|
119 | var tmp = Math.sqrt(r * r - y * y);
|
120 | roots[0] = -tmp;
|
121 | roots[1] = tmp;
|
122 | var dTheta = Math.abs(startAngle - endAngle);
|
123 | if (dTheta < 1e-4) {
|
124 | return 0;
|
125 | }
|
126 | if (dTheta >= PI2 - 1e-4) {
|
127 | startAngle = 0;
|
128 | endAngle = PI2;
|
129 | var dir = anticlockwise ? 1 : -1;
|
130 | if (x >= roots[0] + cx && x <= roots[1] + cx) {
|
131 | return dir;
|
132 | }
|
133 | else {
|
134 | return 0;
|
135 | }
|
136 | }
|
137 | if (startAngle > endAngle) {
|
138 | var tmp_1 = startAngle;
|
139 | startAngle = endAngle;
|
140 | endAngle = tmp_1;
|
141 | }
|
142 | if (startAngle < 0) {
|
143 | startAngle += PI2;
|
144 | endAngle += PI2;
|
145 | }
|
146 | var w = 0;
|
147 | for (var i = 0; i < 2; i++) {
|
148 | var x_ = roots[i];
|
149 | if (x_ + cx > x) {
|
150 | var angle = Math.atan2(y, x_);
|
151 | var dir = anticlockwise ? 1 : -1;
|
152 | if (angle < 0) {
|
153 | angle = PI2 + angle;
|
154 | }
|
155 | if ((angle >= startAngle && angle <= endAngle)
|
156 | || (angle + PI2 >= startAngle && angle + PI2 <= endAngle)) {
|
157 | if (angle > Math.PI / 2 && angle < Math.PI * 1.5) {
|
158 | dir = -dir;
|
159 | }
|
160 | w += dir;
|
161 | }
|
162 | }
|
163 | }
|
164 | return w;
|
165 | }
|
166 | function containPath(path, lineWidth, isStroke, x, y) {
|
167 | var data = path.data;
|
168 | var len = path.len();
|
169 | var w = 0;
|
170 | var xi = 0;
|
171 | var yi = 0;
|
172 | var x0 = 0;
|
173 | var y0 = 0;
|
174 | var x1;
|
175 | var y1;
|
176 | for (var i = 0; i < len;) {
|
177 | var cmd = data[i++];
|
178 | var isFirst = i === 1;
|
179 | if (cmd === CMD.M && i > 1) {
|
180 | if (!isStroke) {
|
181 | w += windingLine(xi, yi, x0, y0, x, y);
|
182 | }
|
183 | }
|
184 | if (isFirst) {
|
185 | xi = data[i];
|
186 | yi = data[i + 1];
|
187 | x0 = xi;
|
188 | y0 = yi;
|
189 | }
|
190 | switch (cmd) {
|
191 | case CMD.M:
|
192 | x0 = data[i++];
|
193 | y0 = data[i++];
|
194 | xi = x0;
|
195 | yi = y0;
|
196 | break;
|
197 | case CMD.L:
|
198 | if (isStroke) {
|
199 | if (line.containStroke(xi, yi, data[i], data[i + 1], lineWidth, x, y)) {
|
200 | return true;
|
201 | }
|
202 | }
|
203 | else {
|
204 | w += windingLine(xi, yi, data[i], data[i + 1], x, y) || 0;
|
205 | }
|
206 | xi = data[i++];
|
207 | yi = data[i++];
|
208 | break;
|
209 | case CMD.C:
|
210 | if (isStroke) {
|
211 | if (cubic.containStroke(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], lineWidth, x, y)) {
|
212 | return true;
|
213 | }
|
214 | }
|
215 | else {
|
216 | w += windingCubic(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], x, y) || 0;
|
217 | }
|
218 | xi = data[i++];
|
219 | yi = data[i++];
|
220 | break;
|
221 | case CMD.Q:
|
222 | if (isStroke) {
|
223 | if (quadratic.containStroke(xi, yi, data[i++], data[i++], data[i], data[i + 1], lineWidth, x, y)) {
|
224 | return true;
|
225 | }
|
226 | }
|
227 | else {
|
228 | w += windingQuadratic(xi, yi, data[i++], data[i++], data[i], data[i + 1], x, y) || 0;
|
229 | }
|
230 | xi = data[i++];
|
231 | yi = data[i++];
|
232 | break;
|
233 | case CMD.A:
|
234 | var cx = data[i++];
|
235 | var cy = data[i++];
|
236 | var rx = data[i++];
|
237 | var ry = data[i++];
|
238 | var theta = data[i++];
|
239 | var dTheta = data[i++];
|
240 | i += 1;
|
241 | var anticlockwise = !!(1 - data[i++]);
|
242 | x1 = Math.cos(theta) * rx + cx;
|
243 | y1 = Math.sin(theta) * ry + cy;
|
244 | if (!isFirst) {
|
245 | w += windingLine(xi, yi, x1, y1, x, y);
|
246 | }
|
247 | else {
|
248 | x0 = x1;
|
249 | y0 = y1;
|
250 | }
|
251 | var _x = (x - cx) * ry / rx + cx;
|
252 | if (isStroke) {
|
253 | if (arc.containStroke(cx, cy, ry, theta, theta + dTheta, anticlockwise, lineWidth, _x, y)) {
|
254 | return true;
|
255 | }
|
256 | }
|
257 | else {
|
258 | w += windingArc(cx, cy, ry, theta, theta + dTheta, anticlockwise, _x, y);
|
259 | }
|
260 | xi = Math.cos(theta + dTheta) * rx + cx;
|
261 | yi = Math.sin(theta + dTheta) * ry + cy;
|
262 | break;
|
263 | case CMD.R:
|
264 | x0 = xi = data[i++];
|
265 | y0 = yi = data[i++];
|
266 | var width = data[i++];
|
267 | var height = data[i++];
|
268 | x1 = x0 + width;
|
269 | y1 = y0 + height;
|
270 | if (isStroke) {
|
271 | if (line.containStroke(x0, y0, x1, y0, lineWidth, x, y)
|
272 | || line.containStroke(x1, y0, x1, y1, lineWidth, x, y)
|
273 | || line.containStroke(x1, y1, x0, y1, lineWidth, x, y)
|
274 | || line.containStroke(x0, y1, x0, y0, lineWidth, x, y)) {
|
275 | return true;
|
276 | }
|
277 | }
|
278 | else {
|
279 | w += windingLine(x1, y0, x1, y1, x, y);
|
280 | w += windingLine(x0, y1, x0, y0, x, y);
|
281 | }
|
282 | break;
|
283 | case CMD.Z:
|
284 | if (isStroke) {
|
285 | if (line.containStroke(xi, yi, x0, y0, lineWidth, x, y)) {
|
286 | return true;
|
287 | }
|
288 | }
|
289 | else {
|
290 | w += windingLine(xi, yi, x0, y0, x, y);
|
291 | }
|
292 | xi = x0;
|
293 | yi = y0;
|
294 | break;
|
295 | }
|
296 | }
|
297 | if (!isStroke && !isAroundEqual(yi, y0)) {
|
298 | w += windingLine(xi, yi, x0, y0, x, y) || 0;
|
299 | }
|
300 | return w !== 0;
|
301 | }
|
302 | export function contain(pathProxy, x, y) {
|
303 | return containPath(pathProxy, 0, false, x, y);
|
304 | }
|
305 | export function containStroke(pathProxy, lineWidth, x, y) {
|
306 | return containPath(pathProxy, lineWidth, true, x, y);
|
307 | }
|