@vpmedia/phaser 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/phaser.cjs.LICENSE.txt +1 -1
- package/dist/phaser.js.LICENSE.txt +1 -1
- package/package.json +2 -3
- package/src/index.js +99 -0
- package/src/phaser/core/animation.js +355 -0
- package/src/phaser/core/animation_manager.js +238 -0
- package/src/phaser/core/animation_parser.js +130 -0
- package/src/phaser/core/array_set.js +108 -0
- package/src/phaser/core/cache.js +558 -0
- package/src/phaser/core/const.js +106 -0
- package/src/phaser/core/device.js +67 -0
- package/src/phaser/core/device_util.js +386 -0
- package/src/phaser/core/dom.js +207 -0
- package/src/phaser/core/event_manager.js +243 -0
- package/src/phaser/core/factory.js +74 -0
- package/src/phaser/core/frame.js +75 -0
- package/src/phaser/core/frame_data.js +84 -0
- package/src/phaser/core/frame_util.js +31 -0
- package/src/phaser/core/game.js +412 -0
- package/src/phaser/core/input.js +401 -0
- package/src/phaser/core/input_button.js +102 -0
- package/src/phaser/core/input_handler.js +687 -0
- package/src/phaser/core/input_mouse.js +289 -0
- package/src/phaser/core/input_mspointer.js +197 -0
- package/src/phaser/core/input_pointer.js +427 -0
- package/src/phaser/core/input_touch.js +157 -0
- package/src/phaser/core/loader.js +946 -0
- package/src/phaser/core/loader_parser.js +105 -0
- package/src/phaser/core/raf.js +46 -0
- package/src/phaser/core/raf_fb.js +75 -0
- package/src/phaser/core/raf_to.js +34 -0
- package/src/phaser/core/scale_manager.js +806 -0
- package/src/phaser/core/scene.js +66 -0
- package/src/phaser/core/scene_manager.js +310 -0
- package/src/phaser/core/signal.js +175 -0
- package/src/phaser/core/signal_binding.js +69 -0
- package/src/phaser/core/sound.js +538 -0
- package/src/phaser/core/sound_manager.js +365 -0
- package/src/phaser/core/stage.js +108 -0
- package/src/phaser/core/time.js +203 -0
- package/src/phaser/core/timer.js +276 -0
- package/src/phaser/core/timer_event.js +21 -0
- package/src/phaser/core/tween.js +329 -0
- package/src/phaser/core/tween_data.js +258 -0
- package/src/phaser/core/tween_easing.js +316 -0
- package/src/phaser/core/tween_manager.js +185 -0
- package/src/phaser/core/world.js +18 -0
- package/src/phaser/display/bitmap_text.js +322 -0
- package/src/phaser/display/button.js +194 -0
- package/src/phaser/display/canvas/buffer.js +36 -0
- package/src/phaser/display/canvas/graphics.js +227 -0
- package/src/phaser/display/canvas/masker.js +39 -0
- package/src/phaser/display/canvas/pool.js +121 -0
- package/src/phaser/display/canvas/renderer.js +123 -0
- package/src/phaser/display/canvas/tinter.js +141 -0
- package/src/phaser/display/canvas/util.js +151 -0
- package/src/phaser/display/display_object.js +597 -0
- package/src/phaser/display/graphics.js +723 -0
- package/src/phaser/display/graphics_data.js +27 -0
- package/src/phaser/display/graphics_data_util.js +14 -0
- package/src/phaser/display/group.js +227 -0
- package/src/phaser/display/image.js +288 -0
- package/src/phaser/display/sprite_batch.js +15 -0
- package/src/phaser/display/sprite_util.js +248 -0
- package/src/phaser/display/text.js +1089 -0
- package/src/phaser/display/webgl/abstract_filter.js +25 -0
- package/src/phaser/display/webgl/base_texture.js +68 -0
- package/src/phaser/display/webgl/blend_manager.js +35 -0
- package/src/phaser/display/webgl/earcut.js +647 -0
- package/src/phaser/display/webgl/earcut_node.js +28 -0
- package/src/phaser/display/webgl/fast_sprite_batch.js +242 -0
- package/src/phaser/display/webgl/filter_manager.js +46 -0
- package/src/phaser/display/webgl/filter_texture.js +61 -0
- package/src/phaser/display/webgl/graphics.js +618 -0
- package/src/phaser/display/webgl/graphics_data.js +42 -0
- package/src/phaser/display/webgl/mask_manager.js +36 -0
- package/src/phaser/display/webgl/render_texture.js +81 -0
- package/src/phaser/display/webgl/renderer.js +234 -0
- package/src/phaser/display/webgl/shader/complex.js +74 -0
- package/src/phaser/display/webgl/shader/fast.js +97 -0
- package/src/phaser/display/webgl/shader/normal.js +225 -0
- package/src/phaser/display/webgl/shader/primitive.js +72 -0
- package/src/phaser/display/webgl/shader/strip.js +77 -0
- package/src/phaser/display/webgl/shader_manager.js +89 -0
- package/src/phaser/display/webgl/sprite_batch.js +320 -0
- package/src/phaser/display/webgl/stencil_manager.js +170 -0
- package/src/phaser/display/webgl/texture.js +117 -0
- package/src/phaser/display/webgl/texture_util.js +32 -0
- package/src/phaser/display/webgl/util.js +74 -0
- package/src/phaser/geom/circle.js +186 -0
- package/src/phaser/geom/ellipse.js +65 -0
- package/src/phaser/geom/line.js +190 -0
- package/src/phaser/geom/matrix.js +147 -0
- package/src/phaser/geom/point.js +164 -0
- package/src/phaser/geom/polygon.js +141 -0
- package/src/phaser/geom/rectangle.js +306 -0
- package/src/phaser/geom/rounded_rectangle.js +36 -0
- package/src/phaser/geom/util/circle.js +115 -0
- package/src/phaser/geom/util/ellipse.js +30 -0
- package/src/phaser/geom/util/line.js +130 -0
- package/src/phaser/geom/util/matrix.js +48 -0
- package/src/phaser/geom/util/point.js +276 -0
- package/src/phaser/geom/util/polygon.js +24 -0
- package/src/phaser/geom/util/rectangle.js +212 -0
- package/src/phaser/geom/util/rounded_rectangle.js +28 -0
- package/src/phaser/util/math.js +279 -0
- package/src/phaser/util/string.js +26 -0
|
@@ -0,0 +1,647 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Andras Csizmadia <andras@vpmedia.hu>
|
|
3
|
+
* @author Richard Davey <rich@photonstorm.com>
|
|
4
|
+
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
5
|
+
* @copyright Copyright (c) 2018-present Richard Davey, Photon Storm Ltd., Andras Csizmadia <andras@vpmedia.hu> (www.vpmedia.hu)
|
|
6
|
+
*/
|
|
7
|
+
/* eslint-disable no-continue */
|
|
8
|
+
import Node from './earcut_node';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
*
|
|
12
|
+
* @param list
|
|
13
|
+
*/
|
|
14
|
+
export function sortLinked(list) {
|
|
15
|
+
let i;
|
|
16
|
+
let p;
|
|
17
|
+
let q;
|
|
18
|
+
let e;
|
|
19
|
+
let tail;
|
|
20
|
+
let numMerges;
|
|
21
|
+
let pSize;
|
|
22
|
+
let qSize;
|
|
23
|
+
let inSize = 1;
|
|
24
|
+
do {
|
|
25
|
+
p = list;
|
|
26
|
+
list = null;
|
|
27
|
+
tail = null;
|
|
28
|
+
numMerges = 0;
|
|
29
|
+
while (p) {
|
|
30
|
+
numMerges += 1;
|
|
31
|
+
q = p;
|
|
32
|
+
pSize = 0;
|
|
33
|
+
for (i = 0; i < inSize; i += 1) {
|
|
34
|
+
pSize += 1;
|
|
35
|
+
q = q.nextZ;
|
|
36
|
+
if (!q) {
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
qSize = inSize;
|
|
41
|
+
while (pSize > 0 || (qSize > 0 && q)) {
|
|
42
|
+
if (pSize === 0) {
|
|
43
|
+
e = q;
|
|
44
|
+
q = q.nextZ;
|
|
45
|
+
qSize -= 1;
|
|
46
|
+
} else if (qSize === 0 || !q) {
|
|
47
|
+
e = p;
|
|
48
|
+
p = p.nextZ;
|
|
49
|
+
pSize -= 1;
|
|
50
|
+
} else if (p.z <= q.z) {
|
|
51
|
+
e = p;
|
|
52
|
+
p = p.nextZ;
|
|
53
|
+
pSize -= 1;
|
|
54
|
+
} else {
|
|
55
|
+
e = q;
|
|
56
|
+
q = q.nextZ;
|
|
57
|
+
qSize -= 1;
|
|
58
|
+
}
|
|
59
|
+
if (tail) {
|
|
60
|
+
tail.nextZ = e;
|
|
61
|
+
} else {
|
|
62
|
+
list = e;
|
|
63
|
+
}
|
|
64
|
+
e.prevZ = tail;
|
|
65
|
+
tail = e;
|
|
66
|
+
}
|
|
67
|
+
p = q;
|
|
68
|
+
}
|
|
69
|
+
tail.nextZ = null;
|
|
70
|
+
inSize *= 2;
|
|
71
|
+
} while (numMerges > 1);
|
|
72
|
+
return list;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
*
|
|
77
|
+
* @param a
|
|
78
|
+
* @param b
|
|
79
|
+
*/
|
|
80
|
+
export function compareX(a, b) {
|
|
81
|
+
return a.x - b.x;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
*
|
|
86
|
+
* @param x
|
|
87
|
+
* @param y
|
|
88
|
+
* @param minX
|
|
89
|
+
* @param minY
|
|
90
|
+
* @param size
|
|
91
|
+
*/
|
|
92
|
+
export function zOrder(x, y, minX, minY, size) {
|
|
93
|
+
// coords are transformed into non-negative 15-bit integer range
|
|
94
|
+
x = 32767 * (x - minX) / size;
|
|
95
|
+
y = 32767 * (y - minY) / size;
|
|
96
|
+
x = (x | (x << 8)) & 0x00FF00FF;
|
|
97
|
+
x = (x | (x << 4)) & 0x0F0F0F0F;
|
|
98
|
+
x = (x | (x << 2)) & 0x33333333;
|
|
99
|
+
x = (x | (x << 1)) & 0x55555555;
|
|
100
|
+
y = (y | (y << 8)) & 0x00FF00FF;
|
|
101
|
+
y = (y | (y << 4)) & 0x0F0F0F0F;
|
|
102
|
+
y = (y | (y << 2)) & 0x33333333;
|
|
103
|
+
y = (y | (y << 1)) & 0x55555555;
|
|
104
|
+
return x | (y << 1);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
*
|
|
109
|
+
* @param start
|
|
110
|
+
* @param minX
|
|
111
|
+
* @param minY
|
|
112
|
+
* @param size
|
|
113
|
+
*/
|
|
114
|
+
export function indexCurve(start, minX, minY, size) {
|
|
115
|
+
let p = start;
|
|
116
|
+
do {
|
|
117
|
+
if (p.z === null) p.z = zOrder(p.x, p.y, minX, minY, size);
|
|
118
|
+
p.prevZ = p.prev;
|
|
119
|
+
p.nextZ = p.next;
|
|
120
|
+
p = p.next;
|
|
121
|
+
} while (p !== start);
|
|
122
|
+
p.prevZ.nextZ = null;
|
|
123
|
+
p.prevZ = null;
|
|
124
|
+
sortLinked(p);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
*
|
|
129
|
+
* @param start
|
|
130
|
+
*/
|
|
131
|
+
export function getLeftmost(start) {
|
|
132
|
+
let p = start;
|
|
133
|
+
let leftmost = start;
|
|
134
|
+
do {
|
|
135
|
+
if (p.x < leftmost.x) leftmost = p;
|
|
136
|
+
p = p.next;
|
|
137
|
+
} while (p !== start);
|
|
138
|
+
return leftmost;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
*
|
|
143
|
+
* @param ax
|
|
144
|
+
* @param ay
|
|
145
|
+
* @param bx
|
|
146
|
+
* @param by
|
|
147
|
+
* @param cx
|
|
148
|
+
* @param cy
|
|
149
|
+
* @param px
|
|
150
|
+
* @param py
|
|
151
|
+
*/
|
|
152
|
+
export function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
|
|
153
|
+
return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 && (ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 && (bx - px) * (cy - py) - (cx - px) * (by - py) >= 0;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
*
|
|
158
|
+
* @param p
|
|
159
|
+
* @param q
|
|
160
|
+
* @param r
|
|
161
|
+
*/
|
|
162
|
+
export function area(p, q, r) {
|
|
163
|
+
return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
*
|
|
168
|
+
* @param p1
|
|
169
|
+
* @param p2
|
|
170
|
+
*/
|
|
171
|
+
export function equals(p1, p2) {
|
|
172
|
+
return p1.x === p2.x && p1.y === p2.y;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
*
|
|
177
|
+
* @param p1
|
|
178
|
+
* @param q1
|
|
179
|
+
* @param p2
|
|
180
|
+
* @param q2
|
|
181
|
+
*/
|
|
182
|
+
export function intersects(p1, q1, p2, q2) {
|
|
183
|
+
return area(p1, q1, p2) > 0 !== area(p1, q1, q2) > 0 && area(p2, q2, p1) > 0 !== area(p2, q2, q1) > 0;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
*
|
|
188
|
+
* @param a
|
|
189
|
+
* @param b
|
|
190
|
+
*/
|
|
191
|
+
export function intersectsPolygon(a, b) {
|
|
192
|
+
let p = a;
|
|
193
|
+
do {
|
|
194
|
+
if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects(p, p.next, a, b)) return true;
|
|
195
|
+
p = p.next;
|
|
196
|
+
} while (p !== a);
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
*
|
|
202
|
+
* @param a
|
|
203
|
+
* @param b
|
|
204
|
+
*/
|
|
205
|
+
export function locallyInside(a, b) {
|
|
206
|
+
return area(a.prev, a, a.next) < 0 ? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : area(a, b, a.prev) < 0 || area(a, a.next, b) < 0;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
*
|
|
211
|
+
* @param a
|
|
212
|
+
* @param b
|
|
213
|
+
*/
|
|
214
|
+
export function middleInside(a, b) {
|
|
215
|
+
let p = a;
|
|
216
|
+
let inside = false;
|
|
217
|
+
const px = (a.x + b.x) / 2;
|
|
218
|
+
const py = (a.y + b.y) / 2;
|
|
219
|
+
do {
|
|
220
|
+
if (((p.y > py) !== (p.next.y > py)) && (px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x)) {
|
|
221
|
+
inside = !inside;
|
|
222
|
+
}
|
|
223
|
+
p = p.next;
|
|
224
|
+
} while (p !== a);
|
|
225
|
+
return inside;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
*
|
|
230
|
+
* @param a
|
|
231
|
+
* @param b
|
|
232
|
+
*/
|
|
233
|
+
export function isValidDiagonal(a, b) {
|
|
234
|
+
return equals(a, b) || a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
*
|
|
239
|
+
* @param a
|
|
240
|
+
* @param b
|
|
241
|
+
*/
|
|
242
|
+
export function splitPolygon(a, b) {
|
|
243
|
+
const a2 = new Node(a.i, a.x, a.y);
|
|
244
|
+
const b2 = new Node(b.i, b.x, b.y);
|
|
245
|
+
const an = a.next;
|
|
246
|
+
const bp = b.prev;
|
|
247
|
+
a.next = b;
|
|
248
|
+
b.prev = a;
|
|
249
|
+
a2.next = an;
|
|
250
|
+
an.prev = a2;
|
|
251
|
+
b2.next = a2;
|
|
252
|
+
a2.prev = b2;
|
|
253
|
+
bp.next = b2;
|
|
254
|
+
b2.prev = bp;
|
|
255
|
+
return b2;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
*
|
|
260
|
+
* @param i
|
|
261
|
+
* @param x
|
|
262
|
+
* @param y
|
|
263
|
+
* @param last
|
|
264
|
+
*/
|
|
265
|
+
export function insertNode(i, x, y, last) {
|
|
266
|
+
const p = new Node(i, x, y);
|
|
267
|
+
if (!last) {
|
|
268
|
+
p.prev = p;
|
|
269
|
+
p.next = p;
|
|
270
|
+
} else {
|
|
271
|
+
p.next = last.next;
|
|
272
|
+
p.prev = last;
|
|
273
|
+
last.next.prev = p;
|
|
274
|
+
last.next = p;
|
|
275
|
+
}
|
|
276
|
+
return p;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
*
|
|
281
|
+
* @param p
|
|
282
|
+
*/
|
|
283
|
+
export function removeNode(p) {
|
|
284
|
+
p.next.prev = p.prev;
|
|
285
|
+
p.prev.next = p.next;
|
|
286
|
+
if (p.prevZ) {
|
|
287
|
+
p.prevZ.nextZ = p.nextZ;
|
|
288
|
+
}
|
|
289
|
+
if (p.nextZ) {
|
|
290
|
+
p.nextZ.prevZ = p.prevZ;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
*
|
|
296
|
+
* @param ear
|
|
297
|
+
*/
|
|
298
|
+
export function isEar(ear) {
|
|
299
|
+
const a = ear.prev;
|
|
300
|
+
const b = ear;
|
|
301
|
+
const c = ear.next;
|
|
302
|
+
if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
|
|
303
|
+
// now make sure we don't have other points inside the potential ear
|
|
304
|
+
let p = ear.next.next;
|
|
305
|
+
while (p !== ear.prev) {
|
|
306
|
+
if (pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
|
|
307
|
+
p = p.next;
|
|
308
|
+
}
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
*
|
|
314
|
+
* @param ear
|
|
315
|
+
* @param minX
|
|
316
|
+
* @param minY
|
|
317
|
+
* @param size
|
|
318
|
+
*/
|
|
319
|
+
export function isEarHashed(ear, minX, minY, size) {
|
|
320
|
+
const a = ear.prev;
|
|
321
|
+
const b = ear;
|
|
322
|
+
const c = ear.next;
|
|
323
|
+
if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
|
|
324
|
+
// triangle bbox; min & max are calculated like this for speed
|
|
325
|
+
/* eslint-disable no-nested-ternary */
|
|
326
|
+
const minTX = a.x < b.x ? (a.x < c.x ? a.x : c.x) : (b.x < c.x ? b.x : c.x);
|
|
327
|
+
/* eslint-disable no-nested-ternary */
|
|
328
|
+
const minTY = a.y < b.y ? (a.y < c.y ? a.y : c.y) : (b.y < c.y ? b.y : c.y);
|
|
329
|
+
/* eslint-disable no-nested-ternary */
|
|
330
|
+
const maxTX = a.x > b.x ? (a.x > c.x ? a.x : c.x) : (b.x > c.x ? b.x : c.x);
|
|
331
|
+
/* eslint-disable no-nested-ternary */
|
|
332
|
+
const maxTY = a.y > b.y ? (a.y > c.y ? a.y : c.y) : (b.y > c.y ? b.y : c.y);
|
|
333
|
+
// z-order range for the current triangle bbox;
|
|
334
|
+
const minZ = zOrder(minTX, minTY, minX, minY, size);
|
|
335
|
+
const maxZ = zOrder(maxTX, maxTY, minX, minY, size);
|
|
336
|
+
// first look for points inside the triangle in increasing z-order
|
|
337
|
+
let p = ear.nextZ;
|
|
338
|
+
while (p && p.z <= maxZ) {
|
|
339
|
+
if (p !== ear.prev && p !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) {
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
p = p.nextZ;
|
|
343
|
+
}
|
|
344
|
+
// then look for points in decreasing z-order
|
|
345
|
+
p = ear.prevZ;
|
|
346
|
+
while (p && p.z >= minZ) {
|
|
347
|
+
if (p !== ear.prev && p !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) && area(p.prev, p, p.next) >= 0) {
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
p = p.prevZ;
|
|
351
|
+
}
|
|
352
|
+
return true;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
*
|
|
357
|
+
* @param data
|
|
358
|
+
* @param start
|
|
359
|
+
* @param end
|
|
360
|
+
* @param dim
|
|
361
|
+
* @param clockwise
|
|
362
|
+
*/
|
|
363
|
+
export function linkedList(data, start, end, dim, clockwise) {
|
|
364
|
+
let sum = 0;
|
|
365
|
+
let i;
|
|
366
|
+
let j;
|
|
367
|
+
let last;
|
|
368
|
+
// calculate original winding order of a polygon ring
|
|
369
|
+
for (i = start, j = end - dim; i < end; i += dim) {
|
|
370
|
+
sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
|
|
371
|
+
j = i;
|
|
372
|
+
}
|
|
373
|
+
// link points into circular doubly-linked list in the specified winding order
|
|
374
|
+
if (clockwise === (sum > 0)) {
|
|
375
|
+
for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last);
|
|
376
|
+
} else {
|
|
377
|
+
for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last);
|
|
378
|
+
}
|
|
379
|
+
return last;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
*
|
|
384
|
+
* @param start
|
|
385
|
+
* @param end
|
|
386
|
+
*/
|
|
387
|
+
export function filterPoints(start, end) {
|
|
388
|
+
if (!start) return start;
|
|
389
|
+
if (!end) end = start;
|
|
390
|
+
let p = start;
|
|
391
|
+
let again;
|
|
392
|
+
do {
|
|
393
|
+
again = false;
|
|
394
|
+
if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
|
|
395
|
+
removeNode(p);
|
|
396
|
+
p = p.prev;
|
|
397
|
+
end = p;
|
|
398
|
+
if (p === p.next) return null;
|
|
399
|
+
again = true;
|
|
400
|
+
} else {
|
|
401
|
+
p = p.next;
|
|
402
|
+
}
|
|
403
|
+
} while (again || p !== end);
|
|
404
|
+
return end;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
*
|
|
409
|
+
* @param hole
|
|
410
|
+
* @param outerNode
|
|
411
|
+
*/
|
|
412
|
+
export function findHoleBridge(hole, outerNode) {
|
|
413
|
+
let p = outerNode;
|
|
414
|
+
const hx = hole.x;
|
|
415
|
+
const hy = hole.y;
|
|
416
|
+
let qx = -Infinity;
|
|
417
|
+
let m;
|
|
418
|
+
// find a segment intersected by a ray from the hole's leftmost point to the left;
|
|
419
|
+
// segment's endpoint with lesser x will be potential connection point
|
|
420
|
+
do {
|
|
421
|
+
if (hy <= p.y && hy >= p.next.y) {
|
|
422
|
+
const x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);
|
|
423
|
+
if (x <= hx && x > qx) {
|
|
424
|
+
qx = x;
|
|
425
|
+
m = p.x < p.next.x ? p : p.next;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
p = p.next;
|
|
429
|
+
} while (p !== outerNode);
|
|
430
|
+
if (!m) return null;
|
|
431
|
+
if (hole.x === m.x) return m.prev; // hole touches outer segment; pick lower endpoint
|
|
432
|
+
// look for points inside the triangle of hole point, segment intersection and endpoint;
|
|
433
|
+
// if there are no points found, we have a valid connection;
|
|
434
|
+
// otherwise choose the point of the minimum angle with the ray as connection point
|
|
435
|
+
const stop = m;
|
|
436
|
+
let tanMin = Infinity;
|
|
437
|
+
let tan;
|
|
438
|
+
p = m.next;
|
|
439
|
+
while (p !== stop) {
|
|
440
|
+
if (hx >= p.x && p.x >= m.x && pointInTriangle(hy < m.y ? hx : qx, hy, m.x, m.y, hy < m.y ? qx : hx, hy, p.x, p.y)) {
|
|
441
|
+
tan = Math.abs(hy - p.y) / (hx - p.x); // tangential
|
|
442
|
+
if ((tan < tanMin || (tan === tanMin && p.x > m.x)) && locallyInside(p, hole)) {
|
|
443
|
+
m = p;
|
|
444
|
+
tanMin = tan;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
p = p.next;
|
|
448
|
+
}
|
|
449
|
+
return m;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
*
|
|
454
|
+
* @param hole
|
|
455
|
+
* @param outerNode
|
|
456
|
+
*/
|
|
457
|
+
export function eliminateHole(hole, outerNode) {
|
|
458
|
+
outerNode = findHoleBridge(hole, outerNode);
|
|
459
|
+
if (outerNode) {
|
|
460
|
+
const b = splitPolygon(outerNode, hole);
|
|
461
|
+
filterPoints(b, b.next);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
*
|
|
467
|
+
* @param data
|
|
468
|
+
* @param holeIndices
|
|
469
|
+
* @param outerNode
|
|
470
|
+
* @param dim
|
|
471
|
+
*/
|
|
472
|
+
export function eliminateHoles(data, holeIndices, outerNode, dim) {
|
|
473
|
+
const queue = [];
|
|
474
|
+
let i;
|
|
475
|
+
let len;
|
|
476
|
+
let start;
|
|
477
|
+
let end;
|
|
478
|
+
let list;
|
|
479
|
+
for (i = 0, len = holeIndices.length; i < len; i += 1) {
|
|
480
|
+
start = holeIndices[i] * dim;
|
|
481
|
+
end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
|
|
482
|
+
list = linkedList(data, start, end, dim, false);
|
|
483
|
+
if (list === list.next) list.steiner = true;
|
|
484
|
+
queue.push(getLeftmost(list));
|
|
485
|
+
}
|
|
486
|
+
queue.sort(compareX);
|
|
487
|
+
// process holes from left to right
|
|
488
|
+
for (i = 0; i < queue.length; i += 1) {
|
|
489
|
+
eliminateHole(queue[i], outerNode);
|
|
490
|
+
outerNode = filterPoints(outerNode, outerNode.next);
|
|
491
|
+
}
|
|
492
|
+
return outerNode;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
*
|
|
497
|
+
* @param start
|
|
498
|
+
* @param triangles
|
|
499
|
+
* @param dim
|
|
500
|
+
*/
|
|
501
|
+
export function cureLocalIntersections(start, triangles, dim) {
|
|
502
|
+
let p = start;
|
|
503
|
+
do {
|
|
504
|
+
const a = p.prev;
|
|
505
|
+
const b = p.next.next;
|
|
506
|
+
// a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2])
|
|
507
|
+
if (intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) {
|
|
508
|
+
triangles.push(a.i / dim);
|
|
509
|
+
triangles.push(p.i / dim);
|
|
510
|
+
triangles.push(b.i / dim);
|
|
511
|
+
// remove two nodes involved
|
|
512
|
+
removeNode(p);
|
|
513
|
+
removeNode(p.next);
|
|
514
|
+
p = b;
|
|
515
|
+
start = p;
|
|
516
|
+
}
|
|
517
|
+
p = p.next;
|
|
518
|
+
} while (p !== start);
|
|
519
|
+
return p;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
*
|
|
524
|
+
* @param start
|
|
525
|
+
* @param triangles
|
|
526
|
+
* @param dim
|
|
527
|
+
* @param minX
|
|
528
|
+
* @param minY
|
|
529
|
+
* @param size
|
|
530
|
+
*/
|
|
531
|
+
export function splitEarcut(start, triangles, dim, minX, minY, size) {
|
|
532
|
+
// look for a valid diagonal that divides the polygon into two
|
|
533
|
+
let a = start;
|
|
534
|
+
do {
|
|
535
|
+
let b = a.next.next;
|
|
536
|
+
while (b !== a.prev) {
|
|
537
|
+
if (a.i !== b.i && isValidDiagonal(a, b)) {
|
|
538
|
+
// split the polygon in two by the diagonal
|
|
539
|
+
let c = splitPolygon(a, b);
|
|
540
|
+
// filter colinear points around the cuts
|
|
541
|
+
a = filterPoints(a, a.next);
|
|
542
|
+
c = filterPoints(c, c.next);
|
|
543
|
+
// run earcut on each half
|
|
544
|
+
/* eslint-disable no-use-before-define */
|
|
545
|
+
earcutLinked(a, triangles, dim, minX, minY, size);
|
|
546
|
+
/* eslint-disable no-use-before-define */
|
|
547
|
+
earcutLinked(c, triangles, dim, minX, minY, size);
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
b = b.next;
|
|
551
|
+
}
|
|
552
|
+
a = a.next;
|
|
553
|
+
} while (a !== start);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
*
|
|
558
|
+
* @param ear
|
|
559
|
+
* @param triangles
|
|
560
|
+
* @param dim
|
|
561
|
+
* @param minX
|
|
562
|
+
* @param minY
|
|
563
|
+
* @param size
|
|
564
|
+
* @param pass
|
|
565
|
+
*/
|
|
566
|
+
export function earcutLinked(ear, triangles, dim, minX, minY, size, pass) {
|
|
567
|
+
if (!ear) return;
|
|
568
|
+
// interlink polygon nodes in z-order
|
|
569
|
+
if (!pass && size) indexCurve(ear, minX, minY, size);
|
|
570
|
+
let stop = ear;
|
|
571
|
+
let prev;
|
|
572
|
+
let next;
|
|
573
|
+
// iterate through ears, slicing them one by one
|
|
574
|
+
while (ear.prev !== ear.next) {
|
|
575
|
+
prev = ear.prev;
|
|
576
|
+
next = ear.next;
|
|
577
|
+
if (size ? isEarHashed(ear, minX, minY, size) : isEar(ear)) {
|
|
578
|
+
// cut off the triangle
|
|
579
|
+
triangles.push(prev.i / dim);
|
|
580
|
+
triangles.push(ear.i / dim);
|
|
581
|
+
triangles.push(next.i / dim);
|
|
582
|
+
removeNode(ear);
|
|
583
|
+
// skipping the next vertice leads to less sliver triangles
|
|
584
|
+
ear = next.next;
|
|
585
|
+
stop = next.next;
|
|
586
|
+
continue;
|
|
587
|
+
}
|
|
588
|
+
ear = next;
|
|
589
|
+
// if we looped through the whole remaining polygon and can't find any more ears
|
|
590
|
+
if (ear === stop) {
|
|
591
|
+
// try filtering points and slicing again
|
|
592
|
+
if (!pass) {
|
|
593
|
+
earcutLinked(filterPoints(ear), triangles, dim, minX, minY, size, 1);
|
|
594
|
+
// if this didn't work, try curing all small self-intersections locally
|
|
595
|
+
} else if (pass === 1) {
|
|
596
|
+
ear = cureLocalIntersections(ear, triangles, dim);
|
|
597
|
+
earcutLinked(ear, triangles, dim, minX, minY, size, 2);
|
|
598
|
+
// as a last resort, try splitting the remaining polygon into two
|
|
599
|
+
} else if (pass === 2) {
|
|
600
|
+
splitEarcut(ear, triangles, dim, minX, minY, size);
|
|
601
|
+
}
|
|
602
|
+
break;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
*
|
|
609
|
+
* @param data
|
|
610
|
+
* @param holeIndices
|
|
611
|
+
* @param dim
|
|
612
|
+
*/
|
|
613
|
+
export function triangulate(data, holeIndices, dim) {
|
|
614
|
+
dim = dim || 2;
|
|
615
|
+
const hasHoles = holeIndices && holeIndices.length;
|
|
616
|
+
const outerLen = hasHoles ? holeIndices[0] * dim : data.length;
|
|
617
|
+
let outerNode = linkedList(data, 0, outerLen, dim, true);
|
|
618
|
+
const triangles = [];
|
|
619
|
+
if (!outerNode) return triangles;
|
|
620
|
+
let minX;
|
|
621
|
+
let minY;
|
|
622
|
+
let maxX;
|
|
623
|
+
let maxY;
|
|
624
|
+
let x;
|
|
625
|
+
let y;
|
|
626
|
+
let size;
|
|
627
|
+
if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim);
|
|
628
|
+
// if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
|
|
629
|
+
if (data.length > 80 * dim) {
|
|
630
|
+
minX = data[0];
|
|
631
|
+
maxX = data[0];
|
|
632
|
+
minY = data[1];
|
|
633
|
+
maxY = data[1];
|
|
634
|
+
for (let i = dim; i < outerLen; i += dim) {
|
|
635
|
+
x = data[i];
|
|
636
|
+
y = data[i + 1];
|
|
637
|
+
if (x < minX) minX = x;
|
|
638
|
+
if (y < minY) minY = y;
|
|
639
|
+
if (x > maxX) maxX = x;
|
|
640
|
+
if (y > maxY) maxY = y;
|
|
641
|
+
}
|
|
642
|
+
// minX, minY and size are later used to transform coords into integers for z-order calculation
|
|
643
|
+
size = Math.max(maxX - minX, maxY - minY);
|
|
644
|
+
}
|
|
645
|
+
earcutLinked(outerNode, triangles, dim, minX, minY, size);
|
|
646
|
+
return triangles;
|
|
647
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @author Andras Csizmadia <andras@vpmedia.hu>
|
|
3
|
+
* @author Richard Davey <rich@photonstorm.com>
|
|
4
|
+
* @author Mat Groves http://matgroves.com/ @Doormat23
|
|
5
|
+
* @copyright Copyright (c) 2018-present Richard Davey, Photon Storm Ltd., Andras Csizmadia <andras@vpmedia.hu> (www.vpmedia.hu)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export default class {
|
|
9
|
+
|
|
10
|
+
constructor(i, x, y) {
|
|
11
|
+
// vertice index in coordinates array
|
|
12
|
+
this.i = i;
|
|
13
|
+
// vertex coordinates
|
|
14
|
+
this.x = x;
|
|
15
|
+
this.y = y;
|
|
16
|
+
// previous and next vertice nodes in a polygon ring
|
|
17
|
+
this.prev = null;
|
|
18
|
+
this.next = null;
|
|
19
|
+
// z-order curve value
|
|
20
|
+
this.z = null;
|
|
21
|
+
// previous and next nodes in z-order
|
|
22
|
+
this.prevZ = null;
|
|
23
|
+
this.nextZ = null;
|
|
24
|
+
// indicates whether this is a steiner point
|
|
25
|
+
this.steiner = false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
}
|