@vpmedia/phaser 1.0.1 → 1.0.3
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/README.md +20 -3
- package/dist/phaser.cjs +1 -1
- package/dist/phaser.cjs.LICENSE.txt +1 -1
- package/dist/phaser.cjs.map +1 -1
- package/dist/phaser.js +1 -1
- package/dist/phaser.js.LICENSE.txt +1 -1
- package/dist/phaser.js.map +1 -1
- package/package.json +23 -17
- package/src/index.js +142 -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 +133 -0
- package/src/phaser/core/array_set.js +107 -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 +388 -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 +33 -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 +1057 -0
- package/src/phaser/core/loader_parser.js +109 -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 +65 -0
- package/src/phaser/core/scene_manager.js +309 -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 +364 -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 +341 -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 +126 -0
- package/src/phaser/display/canvas/renderer.js +123 -0
- package/src/phaser/display/canvas/tinter.js +144 -0
- package/src/phaser/display/canvas/util.js +159 -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 +15 -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 +250 -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 +662 -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 +624 -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 +34 -0
- package/src/phaser/display/webgl/util.js +78 -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 +140 -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 +122 -0
- package/src/phaser/geom/util/ellipse.js +34 -0
- package/src/phaser/geom/util/line.js +135 -0
- package/src/phaser/geom/util/matrix.js +53 -0
- package/src/phaser/geom/util/point.js +296 -0
- package/src/phaser/geom/util/polygon.js +28 -0
- package/src/phaser/geom/util/rectangle.js +229 -0
- package/src/phaser/geom/util/rounded_rectangle.js +32 -0
- package/src/phaser/util/math.js +297 -0
- package/src/phaser/util/string.js +32 -0
|
@@ -0,0 +1,662 @@
|
|
|
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
|
+
import Node from './earcut_node';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
*
|
|
11
|
+
* @param {object} list TBD
|
|
12
|
+
* @returns {object} TBD
|
|
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 {object} a TBD
|
|
78
|
+
* @param {object} b TBD
|
|
79
|
+
* @returns {number} TBD
|
|
80
|
+
*/
|
|
81
|
+
export function compareX(a, b) {
|
|
82
|
+
return a.x - b.x;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
*
|
|
87
|
+
* @param {number} x TBD
|
|
88
|
+
* @param {number} y TBD
|
|
89
|
+
* @param {number} minX TBD
|
|
90
|
+
* @param {number} minY TBD
|
|
91
|
+
* @param {number} size TBD
|
|
92
|
+
* @returns {number} TBD
|
|
93
|
+
*/
|
|
94
|
+
export function zOrder(x, y, minX, minY, size) {
|
|
95
|
+
// coords are transformed into non-negative 15-bit integer range
|
|
96
|
+
x = 32767 * (x - minX) / size;
|
|
97
|
+
y = 32767 * (y - minY) / size;
|
|
98
|
+
x = (x | (x << 8)) & 0x00FF00FF;
|
|
99
|
+
x = (x | (x << 4)) & 0x0F0F0F0F;
|
|
100
|
+
x = (x | (x << 2)) & 0x33333333;
|
|
101
|
+
x = (x | (x << 1)) & 0x55555555;
|
|
102
|
+
y = (y | (y << 8)) & 0x00FF00FF;
|
|
103
|
+
y = (y | (y << 4)) & 0x0F0F0F0F;
|
|
104
|
+
y = (y | (y << 2)) & 0x33333333;
|
|
105
|
+
y = (y | (y << 1)) & 0x55555555;
|
|
106
|
+
return x | (y << 1);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
*
|
|
111
|
+
* @param {object} start TBD
|
|
112
|
+
* @param {number} minX TBD
|
|
113
|
+
* @param {number} minY TBD
|
|
114
|
+
* @param {number} size TBD
|
|
115
|
+
*/
|
|
116
|
+
export function indexCurve(start, minX, minY, size) {
|
|
117
|
+
let p = start;
|
|
118
|
+
do {
|
|
119
|
+
if (p.z === null) p.z = zOrder(p.x, p.y, minX, minY, size);
|
|
120
|
+
p.prevZ = p.prev;
|
|
121
|
+
p.nextZ = p.next;
|
|
122
|
+
p = p.next;
|
|
123
|
+
} while (p !== start);
|
|
124
|
+
p.prevZ.nextZ = null;
|
|
125
|
+
p.prevZ = null;
|
|
126
|
+
sortLinked(p);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
*
|
|
131
|
+
* @param {object} start TBD
|
|
132
|
+
* @returns {object} TBD
|
|
133
|
+
*/
|
|
134
|
+
export function getLeftmost(start) {
|
|
135
|
+
let p = start;
|
|
136
|
+
let leftmost = start;
|
|
137
|
+
do {
|
|
138
|
+
if (p.x < leftmost.x) leftmost = p;
|
|
139
|
+
p = p.next;
|
|
140
|
+
} while (p !== start);
|
|
141
|
+
return leftmost;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
*
|
|
146
|
+
* @param {number} ax TBD
|
|
147
|
+
* @param {number} ay TBD
|
|
148
|
+
* @param {number} bx TBD
|
|
149
|
+
* @param {number} by TBD
|
|
150
|
+
* @param {number} cx TBD
|
|
151
|
+
* @param {number} cy TBD
|
|
152
|
+
* @param {number} px TBD
|
|
153
|
+
* @param {number} py TBD
|
|
154
|
+
* @returns {object} TBD
|
|
155
|
+
*/
|
|
156
|
+
export function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
|
|
157
|
+
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;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
*
|
|
162
|
+
* @param {object} p TBD
|
|
163
|
+
* @param {object} q TBD
|
|
164
|
+
* @param {object} r TBD
|
|
165
|
+
* @returns {number} TBD
|
|
166
|
+
*/
|
|
167
|
+
export function area(p, q, r) {
|
|
168
|
+
return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
*
|
|
173
|
+
* @param {object} p1 TBD
|
|
174
|
+
* @param {object} p2 TBD
|
|
175
|
+
* @returns {boolean} TBD
|
|
176
|
+
*/
|
|
177
|
+
export function equals(p1, p2) {
|
|
178
|
+
return p1.x === p2.x && p1.y === p2.y;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
*
|
|
183
|
+
* @param {object} p1 TBD
|
|
184
|
+
* @param {object} q1 TBD
|
|
185
|
+
* @param {object} p2 TBD
|
|
186
|
+
* @param {object} q2 TBD
|
|
187
|
+
* @returns {boolean} TBD
|
|
188
|
+
*/
|
|
189
|
+
export function intersects(p1, q1, p2, q2) {
|
|
190
|
+
return area(p1, q1, p2) > 0 !== area(p1, q1, q2) > 0 && area(p2, q2, p1) > 0 !== area(p2, q2, q1) > 0;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
*
|
|
195
|
+
* @param {object} a TBD
|
|
196
|
+
* @param {object} b TBD
|
|
197
|
+
* @returns {boolean} TBD
|
|
198
|
+
*/
|
|
199
|
+
export function intersectsPolygon(a, b) {
|
|
200
|
+
let p = a;
|
|
201
|
+
do {
|
|
202
|
+
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;
|
|
203
|
+
p = p.next;
|
|
204
|
+
} while (p !== a);
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
*
|
|
210
|
+
* @param {object} a TBD
|
|
211
|
+
* @param {object} b TBD
|
|
212
|
+
* @returns {object} TBD
|
|
213
|
+
*/
|
|
214
|
+
export function locallyInside(a, b) {
|
|
215
|
+
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;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
*
|
|
220
|
+
* @param {object} a TBD
|
|
221
|
+
* @param {object} b TBD
|
|
222
|
+
* @returns {boolean} TBD
|
|
223
|
+
*/
|
|
224
|
+
export function middleInside(a, b) {
|
|
225
|
+
let p = a;
|
|
226
|
+
let inside = false;
|
|
227
|
+
const px = (a.x + b.x) / 2;
|
|
228
|
+
const py = (a.y + b.y) / 2;
|
|
229
|
+
do {
|
|
230
|
+
if (((p.y > py) !== (p.next.y > py)) && (px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x)) {
|
|
231
|
+
inside = !inside;
|
|
232
|
+
}
|
|
233
|
+
p = p.next;
|
|
234
|
+
} while (p !== a);
|
|
235
|
+
return inside;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
*
|
|
240
|
+
* @param {object} a TBD
|
|
241
|
+
* @param {object} b TBD
|
|
242
|
+
* @returns {boolean} TBD
|
|
243
|
+
*/
|
|
244
|
+
export function isValidDiagonal(a, b) {
|
|
245
|
+
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);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
*
|
|
250
|
+
* @param {object} a TBD
|
|
251
|
+
* @param {object} b TBD
|
|
252
|
+
* @returns {object} TBD
|
|
253
|
+
*/
|
|
254
|
+
export function splitPolygon(a, b) {
|
|
255
|
+
const a2 = new Node(a.i, a.x, a.y);
|
|
256
|
+
const b2 = new Node(b.i, b.x, b.y);
|
|
257
|
+
const an = a.next;
|
|
258
|
+
const bp = b.prev;
|
|
259
|
+
a.next = b;
|
|
260
|
+
b.prev = a;
|
|
261
|
+
a2.next = an;
|
|
262
|
+
an.prev = a2;
|
|
263
|
+
b2.next = a2;
|
|
264
|
+
a2.prev = b2;
|
|
265
|
+
bp.next = b2;
|
|
266
|
+
b2.prev = bp;
|
|
267
|
+
return b2;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
*
|
|
272
|
+
* @param {number} i TBD
|
|
273
|
+
* @param {number} x TBD
|
|
274
|
+
* @param {number} y TBD
|
|
275
|
+
* @param {object} last TBD
|
|
276
|
+
* @returns {object} TBD
|
|
277
|
+
*/
|
|
278
|
+
export function insertNode(i, x, y, last) {
|
|
279
|
+
const p = new Node(i, x, y);
|
|
280
|
+
if (!last) {
|
|
281
|
+
p.prev = p;
|
|
282
|
+
p.next = p;
|
|
283
|
+
} else {
|
|
284
|
+
p.next = last.next;
|
|
285
|
+
p.prev = last;
|
|
286
|
+
last.next.prev = p;
|
|
287
|
+
last.next = p;
|
|
288
|
+
}
|
|
289
|
+
return p;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
*
|
|
294
|
+
* @param {object} p TBD
|
|
295
|
+
*/
|
|
296
|
+
export function removeNode(p) {
|
|
297
|
+
p.next.prev = p.prev;
|
|
298
|
+
p.prev.next = p.next;
|
|
299
|
+
if (p.prevZ) {
|
|
300
|
+
p.prevZ.nextZ = p.nextZ;
|
|
301
|
+
}
|
|
302
|
+
if (p.nextZ) {
|
|
303
|
+
p.nextZ.prevZ = p.prevZ;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
*
|
|
309
|
+
* @param {object} ear TBD
|
|
310
|
+
* @returns {boolean} TBD
|
|
311
|
+
*/
|
|
312
|
+
export function isEar(ear) {
|
|
313
|
+
const a = ear.prev;
|
|
314
|
+
const b = ear;
|
|
315
|
+
const c = ear.next;
|
|
316
|
+
if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
|
|
317
|
+
// now make sure we don't have other points inside the potential ear
|
|
318
|
+
let p = ear.next.next;
|
|
319
|
+
while (p !== ear.prev) {
|
|
320
|
+
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;
|
|
321
|
+
p = p.next;
|
|
322
|
+
}
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
*
|
|
328
|
+
* @param {object} ear TBD
|
|
329
|
+
* @param {number} minX TBD
|
|
330
|
+
* @param {number} minY TBD
|
|
331
|
+
* @param {number} size TBD
|
|
332
|
+
* @returns {boolean} TBD
|
|
333
|
+
*/
|
|
334
|
+
export function isEarHashed(ear, minX, minY, size) {
|
|
335
|
+
const a = ear.prev;
|
|
336
|
+
const b = ear;
|
|
337
|
+
const c = ear.next;
|
|
338
|
+
if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
|
|
339
|
+
// triangle bbox; min & max are calculated like this for speed
|
|
340
|
+
const minTX = a.x < b.x ? (a.x < c.x ? a.x : c.x) : (b.x < c.x ? b.x : c.x);
|
|
341
|
+
const minTY = a.y < b.y ? (a.y < c.y ? a.y : c.y) : (b.y < c.y ? b.y : c.y);
|
|
342
|
+
const maxTX = a.x > b.x ? (a.x > c.x ? a.x : c.x) : (b.x > c.x ? b.x : c.x);
|
|
343
|
+
const maxTY = a.y > b.y ? (a.y > c.y ? a.y : c.y) : (b.y > c.y ? b.y : c.y);
|
|
344
|
+
// z-order range for the current triangle bbox;
|
|
345
|
+
const minZ = zOrder(minTX, minTY, minX, minY, size);
|
|
346
|
+
const maxZ = zOrder(maxTX, maxTY, minX, minY, size);
|
|
347
|
+
// first look for points inside the triangle in increasing z-order
|
|
348
|
+
let p = ear.nextZ;
|
|
349
|
+
while (p && p.z <= maxZ) {
|
|
350
|
+
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) {
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
p = p.nextZ;
|
|
354
|
+
}
|
|
355
|
+
// then look for points in decreasing z-order
|
|
356
|
+
p = ear.prevZ;
|
|
357
|
+
while (p && p.z >= minZ) {
|
|
358
|
+
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) {
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
p = p.prevZ;
|
|
362
|
+
}
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
*
|
|
368
|
+
* @param {object} data TBD
|
|
369
|
+
* @param {number} start TBD
|
|
370
|
+
* @param {number} end TBD
|
|
371
|
+
* @param {number} dim TBD
|
|
372
|
+
* @param {boolean} clockwise TBD
|
|
373
|
+
* @returns {object} TBD
|
|
374
|
+
*/
|
|
375
|
+
export function linkedList(data, start, end, dim, clockwise) {
|
|
376
|
+
let sum = 0;
|
|
377
|
+
let i;
|
|
378
|
+
let j;
|
|
379
|
+
let last;
|
|
380
|
+
// calculate original winding order of a polygon ring
|
|
381
|
+
for (i = start, j = end - dim; i < end; i += dim) {
|
|
382
|
+
sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
|
|
383
|
+
j = i;
|
|
384
|
+
}
|
|
385
|
+
// link points into circular doubly-linked list in the specified winding order
|
|
386
|
+
if (clockwise === (sum > 0)) {
|
|
387
|
+
for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last);
|
|
388
|
+
} else {
|
|
389
|
+
for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last);
|
|
390
|
+
}
|
|
391
|
+
return last;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
*
|
|
396
|
+
* @param {object} start TBD
|
|
397
|
+
* @param {object} end TBD
|
|
398
|
+
* @returns {object} TBD
|
|
399
|
+
*/
|
|
400
|
+
export function filterPoints(start, end) {
|
|
401
|
+
if (!start) return start;
|
|
402
|
+
if (!end) end = start;
|
|
403
|
+
let p = start;
|
|
404
|
+
let again;
|
|
405
|
+
do {
|
|
406
|
+
again = false;
|
|
407
|
+
if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
|
|
408
|
+
removeNode(p);
|
|
409
|
+
p = p.prev;
|
|
410
|
+
end = p;
|
|
411
|
+
if (p === p.next) return null;
|
|
412
|
+
again = true;
|
|
413
|
+
} else {
|
|
414
|
+
p = p.next;
|
|
415
|
+
}
|
|
416
|
+
} while (again || p !== end);
|
|
417
|
+
return end;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
*
|
|
422
|
+
* @param {object} hole TBD
|
|
423
|
+
* @param {object} outerNode TBD
|
|
424
|
+
* @returns {object} TBD
|
|
425
|
+
*/
|
|
426
|
+
export function findHoleBridge(hole, outerNode) {
|
|
427
|
+
let p = outerNode;
|
|
428
|
+
const hx = hole.x;
|
|
429
|
+
const hy = hole.y;
|
|
430
|
+
let qx = -Infinity;
|
|
431
|
+
let m;
|
|
432
|
+
// find a segment intersected by a ray from the hole's leftmost point to the left;
|
|
433
|
+
// segment's endpoint with lesser x will be potential connection point
|
|
434
|
+
do {
|
|
435
|
+
if (hy <= p.y && hy >= p.next.y) {
|
|
436
|
+
const x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);
|
|
437
|
+
if (x <= hx && x > qx) {
|
|
438
|
+
qx = x;
|
|
439
|
+
m = p.x < p.next.x ? p : p.next;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
p = p.next;
|
|
443
|
+
} while (p !== outerNode);
|
|
444
|
+
if (!m) return null;
|
|
445
|
+
if (hole.x === m.x) return m.prev; // hole touches outer segment; pick lower endpoint
|
|
446
|
+
// look for points inside the triangle of hole point, segment intersection and endpoint;
|
|
447
|
+
// if there are no points found, we have a valid connection;
|
|
448
|
+
// otherwise choose the point of the minimum angle with the ray as connection point
|
|
449
|
+
const stop = m;
|
|
450
|
+
let tanMin = Infinity;
|
|
451
|
+
let tan;
|
|
452
|
+
p = m.next;
|
|
453
|
+
while (p !== stop) {
|
|
454
|
+
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)) {
|
|
455
|
+
tan = Math.abs(hy - p.y) / (hx - p.x); // tangential
|
|
456
|
+
if ((tan < tanMin || (tan === tanMin && p.x > m.x)) && locallyInside(p, hole)) {
|
|
457
|
+
m = p;
|
|
458
|
+
tanMin = tan;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
p = p.next;
|
|
462
|
+
}
|
|
463
|
+
return m;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
*
|
|
468
|
+
* @param {object} hole TBD
|
|
469
|
+
* @param {object} outerNode TBD
|
|
470
|
+
*/
|
|
471
|
+
export function eliminateHole(hole, outerNode) {
|
|
472
|
+
outerNode = findHoleBridge(hole, outerNode);
|
|
473
|
+
if (outerNode) {
|
|
474
|
+
const b = splitPolygon(outerNode, hole);
|
|
475
|
+
filterPoints(b, b.next);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
*
|
|
481
|
+
* @param {object} data TBD
|
|
482
|
+
* @param {object} holeIndices TBD
|
|
483
|
+
* @param {object} outerNode TBD
|
|
484
|
+
* @param {object} dim TBD
|
|
485
|
+
* @returns {object} TBD
|
|
486
|
+
*/
|
|
487
|
+
export function eliminateHoles(data, holeIndices, outerNode, dim) {
|
|
488
|
+
const queue = [];
|
|
489
|
+
let i;
|
|
490
|
+
let len;
|
|
491
|
+
let start;
|
|
492
|
+
let end;
|
|
493
|
+
let list;
|
|
494
|
+
for (i = 0, len = holeIndices.length; i < len; i += 1) {
|
|
495
|
+
start = holeIndices[i] * dim;
|
|
496
|
+
end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
|
|
497
|
+
list = linkedList(data, start, end, dim, false);
|
|
498
|
+
if (list === list.next) list.steiner = true;
|
|
499
|
+
queue.push(getLeftmost(list));
|
|
500
|
+
}
|
|
501
|
+
queue.sort(compareX);
|
|
502
|
+
// process holes from left to right
|
|
503
|
+
for (i = 0; i < queue.length; i += 1) {
|
|
504
|
+
eliminateHole(queue[i], outerNode);
|
|
505
|
+
outerNode = filterPoints(outerNode, outerNode.next);
|
|
506
|
+
}
|
|
507
|
+
return outerNode;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
*
|
|
512
|
+
* @param {object} start TBD
|
|
513
|
+
* @param {object} triangles TBD
|
|
514
|
+
* @param {number} dim TBD
|
|
515
|
+
* @returns {object} TBD
|
|
516
|
+
*/
|
|
517
|
+
export function cureLocalIntersections(start, triangles, dim) {
|
|
518
|
+
let p = start;
|
|
519
|
+
do {
|
|
520
|
+
const a = p.prev;
|
|
521
|
+
const b = p.next.next;
|
|
522
|
+
// a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2])
|
|
523
|
+
if (intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) {
|
|
524
|
+
triangles.push(a.i / dim);
|
|
525
|
+
triangles.push(p.i / dim);
|
|
526
|
+
triangles.push(b.i / dim);
|
|
527
|
+
// remove two nodes involved
|
|
528
|
+
removeNode(p);
|
|
529
|
+
removeNode(p.next);
|
|
530
|
+
p = b;
|
|
531
|
+
start = p;
|
|
532
|
+
}
|
|
533
|
+
p = p.next;
|
|
534
|
+
} while (p !== start);
|
|
535
|
+
return p;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
*
|
|
540
|
+
* @param {object} start TBD
|
|
541
|
+
* @param {object} triangles TBD
|
|
542
|
+
* @param {number} dim TBD
|
|
543
|
+
* @param {number} minX TBD
|
|
544
|
+
* @param {number} minY TBD
|
|
545
|
+
* @param {number} size TBD
|
|
546
|
+
*/
|
|
547
|
+
export function splitEarcut(start, triangles, dim, minX, minY, size) {
|
|
548
|
+
// look for a valid diagonal that divides the polygon into two
|
|
549
|
+
let a = start;
|
|
550
|
+
do {
|
|
551
|
+
let b = a.next.next;
|
|
552
|
+
while (b !== a.prev) {
|
|
553
|
+
if (a.i !== b.i && isValidDiagonal(a, b)) {
|
|
554
|
+
// split the polygon in two by the diagonal
|
|
555
|
+
let c = splitPolygon(a, b);
|
|
556
|
+
// filter colinear points around the cuts
|
|
557
|
+
a = filterPoints(a, a.next);
|
|
558
|
+
c = filterPoints(c, c.next);
|
|
559
|
+
// run earcut on each half
|
|
560
|
+
earcutLinked(a, triangles, dim, minX, minY, size);
|
|
561
|
+
earcutLinked(c, triangles, dim, minX, minY, size);
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
b = b.next;
|
|
565
|
+
}
|
|
566
|
+
a = a.next;
|
|
567
|
+
} while (a !== start);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
*
|
|
572
|
+
* @param {object} ear TBD
|
|
573
|
+
* @param {object} triangles TBD
|
|
574
|
+
* @param {number} dim TBD
|
|
575
|
+
* @param {number} minX TBD
|
|
576
|
+
* @param {number} minY TBD
|
|
577
|
+
* @param {number} size TBD
|
|
578
|
+
* @param {object} pass TBD
|
|
579
|
+
*/
|
|
580
|
+
export function earcutLinked(ear, triangles, dim, minX, minY, size, pass) {
|
|
581
|
+
if (!ear) return;
|
|
582
|
+
// interlink polygon nodes in z-order
|
|
583
|
+
if (!pass && size) indexCurve(ear, minX, minY, size);
|
|
584
|
+
let stop = ear;
|
|
585
|
+
let prev;
|
|
586
|
+
let next;
|
|
587
|
+
// iterate through ears, slicing them one by one
|
|
588
|
+
while (ear.prev !== ear.next) {
|
|
589
|
+
prev = ear.prev;
|
|
590
|
+
next = ear.next;
|
|
591
|
+
if (size ? isEarHashed(ear, minX, minY, size) : isEar(ear)) {
|
|
592
|
+
// cut off the triangle
|
|
593
|
+
triangles.push(prev.i / dim);
|
|
594
|
+
triangles.push(ear.i / dim);
|
|
595
|
+
triangles.push(next.i / dim);
|
|
596
|
+
removeNode(ear);
|
|
597
|
+
// skipping the next vertice leads to less sliver triangles
|
|
598
|
+
ear = next.next;
|
|
599
|
+
stop = next.next;
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
ear = next;
|
|
603
|
+
// if we looped through the whole remaining polygon and can't find any more ears
|
|
604
|
+
if (ear === stop) {
|
|
605
|
+
// try filtering points and slicing again
|
|
606
|
+
if (!pass) {
|
|
607
|
+
earcutLinked(filterPoints(ear), triangles, dim, minX, minY, size, 1);
|
|
608
|
+
// if this didn't work, try curing all small self-intersections locally
|
|
609
|
+
} else if (pass === 1) {
|
|
610
|
+
ear = cureLocalIntersections(ear, triangles, dim);
|
|
611
|
+
earcutLinked(ear, triangles, dim, minX, minY, size, 2);
|
|
612
|
+
// as a last resort, try splitting the remaining polygon into two
|
|
613
|
+
} else if (pass === 2) {
|
|
614
|
+
splitEarcut(ear, triangles, dim, minX, minY, size);
|
|
615
|
+
}
|
|
616
|
+
break;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
*
|
|
623
|
+
* @param {object} data TBD
|
|
624
|
+
* @param {object} holeIndices TBD
|
|
625
|
+
* @param {number} dim TBD
|
|
626
|
+
* @returns {object} TBD
|
|
627
|
+
*/
|
|
628
|
+
export function triangulate(data, holeIndices, dim) {
|
|
629
|
+
dim = dim || 2;
|
|
630
|
+
const hasHoles = holeIndices && holeIndices.length;
|
|
631
|
+
const outerLen = hasHoles ? holeIndices[0] * dim : data.length;
|
|
632
|
+
let outerNode = linkedList(data, 0, outerLen, dim, true);
|
|
633
|
+
const triangles = [];
|
|
634
|
+
if (!outerNode) return triangles;
|
|
635
|
+
let minX;
|
|
636
|
+
let minY;
|
|
637
|
+
let maxX;
|
|
638
|
+
let maxY;
|
|
639
|
+
let x;
|
|
640
|
+
let y;
|
|
641
|
+
let size;
|
|
642
|
+
if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim);
|
|
643
|
+
// if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
|
|
644
|
+
if (data.length > 80 * dim) {
|
|
645
|
+
minX = data[0];
|
|
646
|
+
maxX = data[0];
|
|
647
|
+
minY = data[1];
|
|
648
|
+
maxY = data[1];
|
|
649
|
+
for (let i = dim; i < outerLen; i += dim) {
|
|
650
|
+
x = data[i];
|
|
651
|
+
y = data[i + 1];
|
|
652
|
+
if (x < minX) minX = x;
|
|
653
|
+
if (y < minY) minY = y;
|
|
654
|
+
if (x > maxX) maxX = x;
|
|
655
|
+
if (y > maxY) maxY = y;
|
|
656
|
+
}
|
|
657
|
+
// minX, minY and size are later used to transform coords into integers for z-order calculation
|
|
658
|
+
size = Math.max(maxX - minX, maxY - minY);
|
|
659
|
+
}
|
|
660
|
+
earcutLinked(outerNode, triangles, dim, minX, minY, size);
|
|
661
|
+
return triangles;
|
|
662
|
+
}
|
|
@@ -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
|
+
}
|