poly-extrude 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- package/.eslintignore +12 -0
- package/.eslintrc.js +34 -0
- package/.vscode/settings.json +3 -0
- package/LICENSE +21 -0
- package/babel.config.js +12 -0
- package/dist/poly-extrude.js +1317 -0
- package/dist/poly-extrude.js.map +1 -0
- package/dist/poly-extrude.min.js +4 -0
- package/dist/poly-extrude.mjs +1305 -0
- package/index.js +3 -0
- package/package.json +42 -0
- package/pnpm-lock.yaml +3054 -0
- package/rollup.config.js +108 -0
- package/src/polygon.js +148 -0
- package/src/polyline.js +190 -0
- package/src/util.js +205 -0
- package/test/buildings.html +77 -0
- package/test/data/a.png +0 -0
- package/test/data/building-texture-dark.jpg +0 -0
- package/test/data/building.geojson +1118 -0
- package/test/data/buildings-ny.geojson +2845 -0
- package/test/data/buildings.geojson +1 -0
- package/test/data/free-line.geojson +1 -0
- package/test/data/line.geojson +1 -0
- package/test/data/polygon.geojson +1 -0
- package/test/data/simple-hole.geojson +1 -0
- package/test/data/simple-line.geojson +45 -0
- package/test/data/simple.geojson +1 -0
- package/test/data/street.geojson +1 -0
- package/test/data//345/244/252/346/271/226.geojson +8 -0
- package/test/data//350/210/237/345/261/261/345/270/202.geojson +1 -0
- package/test/data//350/213/217/345/267/236.geojson +1 -0
- package/test/data//351/204/261/351/230/263/346/271/226.geojson +1 -0
- package/test/line-draw.html +100 -0
- package/test/line-uv.html +69 -0
- package/test/line.html +56 -0
- package/test/multi-polygon.html +53 -0
- package/test/ny-building.html +67 -0
- package/test/simple.html +61 -0
- package/test/street.html +52 -0
- package/test/util.js +131 -0
- package/test/uv.html +77 -0
@@ -0,0 +1,1305 @@
|
|
1
|
+
/*!
|
2
|
+
* poly-extrude v0.0.1
|
3
|
+
*/
|
4
|
+
var earcut$2 = {exports: {}};
|
5
|
+
|
6
|
+
earcut$2.exports = earcut;
|
7
|
+
|
8
|
+
earcut$2.exports["default"] = earcut;
|
9
|
+
|
10
|
+
function earcut(data, holeIndices, dim) {
|
11
|
+
dim = dim || 2;
|
12
|
+
var hasHoles = holeIndices && holeIndices.length,
|
13
|
+
outerLen = hasHoles ? holeIndices[0] * dim : data.length,
|
14
|
+
outerNode = linkedList(data, 0, outerLen, dim, true),
|
15
|
+
triangles = [];
|
16
|
+
if (!outerNode || outerNode.next === outerNode.prev) return triangles;
|
17
|
+
var minX, minY, maxX, maxY, x, y, invSize;
|
18
|
+
if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim); // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
|
19
|
+
|
20
|
+
if (data.length > 80 * dim) {
|
21
|
+
minX = maxX = data[0];
|
22
|
+
minY = maxY = data[1];
|
23
|
+
|
24
|
+
for (var i = dim; i < outerLen; i += dim) {
|
25
|
+
x = data[i];
|
26
|
+
y = data[i + 1];
|
27
|
+
if (x < minX) minX = x;
|
28
|
+
if (y < minY) minY = y;
|
29
|
+
if (x > maxX) maxX = x;
|
30
|
+
if (y > maxY) maxY = y;
|
31
|
+
} // minX, minY and invSize are later used to transform coords into integers for z-order calculation
|
32
|
+
|
33
|
+
|
34
|
+
invSize = Math.max(maxX - minX, maxY - minY);
|
35
|
+
invSize = invSize !== 0 ? 1 / invSize : 0;
|
36
|
+
}
|
37
|
+
|
38
|
+
earcutLinked(outerNode, triangles, dim, minX, minY, invSize);
|
39
|
+
return triangles;
|
40
|
+
} // create a circular doubly linked list from polygon points in the specified winding order
|
41
|
+
|
42
|
+
|
43
|
+
function linkedList(data, start, end, dim, clockwise) {
|
44
|
+
var i, last;
|
45
|
+
|
46
|
+
if (clockwise === signedArea(data, start, end, dim) > 0) {
|
47
|
+
for (i = start; i < end; i += dim) {
|
48
|
+
last = insertNode(i, data[i], data[i + 1], last);
|
49
|
+
}
|
50
|
+
} else {
|
51
|
+
for (i = end - dim; i >= start; i -= dim) {
|
52
|
+
last = insertNode(i, data[i], data[i + 1], last);
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
if (last && equals(last, last.next)) {
|
57
|
+
removeNode(last);
|
58
|
+
last = last.next;
|
59
|
+
}
|
60
|
+
|
61
|
+
return last;
|
62
|
+
} // eliminate colinear or duplicate points
|
63
|
+
|
64
|
+
|
65
|
+
function filterPoints(start, end) {
|
66
|
+
if (!start) return start;
|
67
|
+
if (!end) end = start;
|
68
|
+
var p = start,
|
69
|
+
again;
|
70
|
+
|
71
|
+
do {
|
72
|
+
again = false;
|
73
|
+
|
74
|
+
if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
|
75
|
+
removeNode(p);
|
76
|
+
p = end = p.prev;
|
77
|
+
if (p === p.next) break;
|
78
|
+
again = true;
|
79
|
+
} else {
|
80
|
+
p = p.next;
|
81
|
+
}
|
82
|
+
} while (again || p !== end);
|
83
|
+
|
84
|
+
return end;
|
85
|
+
} // main ear slicing loop which triangulates a polygon (given as a linked list)
|
86
|
+
|
87
|
+
|
88
|
+
function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
|
89
|
+
if (!ear) return; // interlink polygon nodes in z-order
|
90
|
+
|
91
|
+
if (!pass && invSize) indexCurve(ear, minX, minY, invSize);
|
92
|
+
var stop = ear,
|
93
|
+
prev,
|
94
|
+
next; // iterate through ears, slicing them one by one
|
95
|
+
|
96
|
+
while (ear.prev !== ear.next) {
|
97
|
+
prev = ear.prev;
|
98
|
+
next = ear.next;
|
99
|
+
|
100
|
+
if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) {
|
101
|
+
// cut off the triangle
|
102
|
+
triangles.push(prev.i / dim);
|
103
|
+
triangles.push(ear.i / dim);
|
104
|
+
triangles.push(next.i / dim);
|
105
|
+
removeNode(ear); // skipping the next vertex leads to less sliver triangles
|
106
|
+
|
107
|
+
ear = next.next;
|
108
|
+
stop = next.next;
|
109
|
+
continue;
|
110
|
+
}
|
111
|
+
|
112
|
+
ear = next; // if we looped through the whole remaining polygon and can't find any more ears
|
113
|
+
|
114
|
+
if (ear === stop) {
|
115
|
+
// try filtering points and slicing again
|
116
|
+
if (!pass) {
|
117
|
+
earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1); // if this didn't work, try curing all small self-intersections locally
|
118
|
+
} else if (pass === 1) {
|
119
|
+
ear = cureLocalIntersections(filterPoints(ear), triangles, dim);
|
120
|
+
earcutLinked(ear, triangles, dim, minX, minY, invSize, 2); // as a last resort, try splitting the remaining polygon into two
|
121
|
+
} else if (pass === 2) {
|
122
|
+
splitEarcut(ear, triangles, dim, minX, minY, invSize);
|
123
|
+
}
|
124
|
+
|
125
|
+
break;
|
126
|
+
}
|
127
|
+
}
|
128
|
+
} // check whether a polygon node forms a valid ear with adjacent nodes
|
129
|
+
|
130
|
+
|
131
|
+
function isEar(ear) {
|
132
|
+
var a = ear.prev,
|
133
|
+
b = ear,
|
134
|
+
c = ear.next;
|
135
|
+
if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
|
136
|
+
// now make sure we don't have other points inside the potential ear
|
137
|
+
|
138
|
+
var p = ear.next.next;
|
139
|
+
|
140
|
+
while (p !== ear.prev) {
|
141
|
+
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;
|
142
|
+
p = p.next;
|
143
|
+
}
|
144
|
+
|
145
|
+
return true;
|
146
|
+
}
|
147
|
+
|
148
|
+
function isEarHashed(ear, minX, minY, invSize) {
|
149
|
+
var a = ear.prev,
|
150
|
+
b = ear,
|
151
|
+
c = ear.next;
|
152
|
+
if (area(a, b, c) >= 0) return false; // reflex, can't be an ear
|
153
|
+
// triangle bbox; min & max are calculated like this for speed
|
154
|
+
|
155
|
+
var minTX = a.x < b.x ? a.x < c.x ? a.x : c.x : b.x < c.x ? b.x : c.x,
|
156
|
+
minTY = a.y < b.y ? a.y < c.y ? a.y : c.y : b.y < c.y ? b.y : c.y,
|
157
|
+
maxTX = a.x > b.x ? a.x > c.x ? a.x : c.x : b.x > c.x ? b.x : c.x,
|
158
|
+
maxTY = a.y > b.y ? a.y > c.y ? a.y : c.y : b.y > c.y ? b.y : c.y; // z-order range for the current triangle bbox;
|
159
|
+
|
160
|
+
var minZ = zOrder(minTX, minTY, minX, minY, invSize),
|
161
|
+
maxZ = zOrder(maxTX, maxTY, minX, minY, invSize);
|
162
|
+
var p = ear.prevZ,
|
163
|
+
n = ear.nextZ; // look for points inside the triangle in both directions
|
164
|
+
|
165
|
+
while (p && p.z >= minZ && n && n.z <= maxZ) {
|
166
|
+
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) return false;
|
167
|
+
p = p.prevZ;
|
168
|
+
if (n !== ear.prev && n !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
|
169
|
+
n = n.nextZ;
|
170
|
+
} // look for remaining points in decreasing z-order
|
171
|
+
|
172
|
+
|
173
|
+
while (p && p.z >= minZ) {
|
174
|
+
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) return false;
|
175
|
+
p = p.prevZ;
|
176
|
+
} // look for remaining points in increasing z-order
|
177
|
+
|
178
|
+
|
179
|
+
while (n && n.z <= maxZ) {
|
180
|
+
if (n !== ear.prev && n !== ear.next && pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
|
181
|
+
n = n.nextZ;
|
182
|
+
}
|
183
|
+
|
184
|
+
return true;
|
185
|
+
} // go through all polygon nodes and cure small local self-intersections
|
186
|
+
|
187
|
+
|
188
|
+
function cureLocalIntersections(start, triangles, dim) {
|
189
|
+
var p = start;
|
190
|
+
|
191
|
+
do {
|
192
|
+
var a = p.prev,
|
193
|
+
b = p.next.next;
|
194
|
+
|
195
|
+
if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) {
|
196
|
+
triangles.push(a.i / dim);
|
197
|
+
triangles.push(p.i / dim);
|
198
|
+
triangles.push(b.i / dim); // remove two nodes involved
|
199
|
+
|
200
|
+
removeNode(p);
|
201
|
+
removeNode(p.next);
|
202
|
+
p = start = b;
|
203
|
+
}
|
204
|
+
|
205
|
+
p = p.next;
|
206
|
+
} while (p !== start);
|
207
|
+
|
208
|
+
return filterPoints(p);
|
209
|
+
} // try splitting polygon into two and triangulate them independently
|
210
|
+
|
211
|
+
|
212
|
+
function splitEarcut(start, triangles, dim, minX, minY, invSize) {
|
213
|
+
// look for a valid diagonal that divides the polygon into two
|
214
|
+
var a = start;
|
215
|
+
|
216
|
+
do {
|
217
|
+
var b = a.next.next;
|
218
|
+
|
219
|
+
while (b !== a.prev) {
|
220
|
+
if (a.i !== b.i && isValidDiagonal(a, b)) {
|
221
|
+
// split the polygon in two by the diagonal
|
222
|
+
var c = splitPolygon(a, b); // filter colinear points around the cuts
|
223
|
+
|
224
|
+
a = filterPoints(a, a.next);
|
225
|
+
c = filterPoints(c, c.next); // run earcut on each half
|
226
|
+
|
227
|
+
earcutLinked(a, triangles, dim, minX, minY, invSize);
|
228
|
+
earcutLinked(c, triangles, dim, minX, minY, invSize);
|
229
|
+
return;
|
230
|
+
}
|
231
|
+
|
232
|
+
b = b.next;
|
233
|
+
}
|
234
|
+
|
235
|
+
a = a.next;
|
236
|
+
} while (a !== start);
|
237
|
+
} // link every hole into the outer loop, producing a single-ring polygon without holes
|
238
|
+
|
239
|
+
|
240
|
+
function eliminateHoles(data, holeIndices, outerNode, dim) {
|
241
|
+
var queue = [],
|
242
|
+
i,
|
243
|
+
len,
|
244
|
+
start,
|
245
|
+
end,
|
246
|
+
list;
|
247
|
+
|
248
|
+
for (i = 0, len = holeIndices.length; i < len; i++) {
|
249
|
+
start = holeIndices[i] * dim;
|
250
|
+
end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
|
251
|
+
list = linkedList(data, start, end, dim, false);
|
252
|
+
if (list === list.next) list.steiner = true;
|
253
|
+
queue.push(getLeftmost(list));
|
254
|
+
}
|
255
|
+
|
256
|
+
queue.sort(compareX); // process holes from left to right
|
257
|
+
|
258
|
+
for (i = 0; i < queue.length; i++) {
|
259
|
+
outerNode = eliminateHole(queue[i], outerNode);
|
260
|
+
outerNode = filterPoints(outerNode, outerNode.next);
|
261
|
+
}
|
262
|
+
|
263
|
+
return outerNode;
|
264
|
+
}
|
265
|
+
|
266
|
+
function compareX(a, b) {
|
267
|
+
return a.x - b.x;
|
268
|
+
} // find a bridge between vertices that connects hole with an outer ring and and link it
|
269
|
+
|
270
|
+
|
271
|
+
function eliminateHole(hole, outerNode) {
|
272
|
+
var bridge = findHoleBridge(hole, outerNode);
|
273
|
+
|
274
|
+
if (!bridge) {
|
275
|
+
return outerNode;
|
276
|
+
}
|
277
|
+
|
278
|
+
var bridgeReverse = splitPolygon(bridge, hole); // filter collinear points around the cuts
|
279
|
+
|
280
|
+
var filteredBridge = filterPoints(bridge, bridge.next);
|
281
|
+
filterPoints(bridgeReverse, bridgeReverse.next); // Check if input node was removed by the filtering
|
282
|
+
|
283
|
+
return outerNode === bridge ? filteredBridge : outerNode;
|
284
|
+
} // David Eberly's algorithm for finding a bridge between hole and outer polygon
|
285
|
+
|
286
|
+
|
287
|
+
function findHoleBridge(hole, outerNode) {
|
288
|
+
var p = outerNode,
|
289
|
+
hx = hole.x,
|
290
|
+
hy = hole.y,
|
291
|
+
qx = -Infinity,
|
292
|
+
m; // find a segment intersected by a ray from the hole's leftmost point to the left;
|
293
|
+
// segment's endpoint with lesser x will be potential connection point
|
294
|
+
|
295
|
+
do {
|
296
|
+
if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {
|
297
|
+
var x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);
|
298
|
+
|
299
|
+
if (x <= hx && x > qx) {
|
300
|
+
qx = x;
|
301
|
+
|
302
|
+
if (x === hx) {
|
303
|
+
if (hy === p.y) return p;
|
304
|
+
if (hy === p.next.y) return p.next;
|
305
|
+
}
|
306
|
+
|
307
|
+
m = p.x < p.next.x ? p : p.next;
|
308
|
+
}
|
309
|
+
}
|
310
|
+
|
311
|
+
p = p.next;
|
312
|
+
} while (p !== outerNode);
|
313
|
+
|
314
|
+
if (!m) return null;
|
315
|
+
if (hx === qx) return m; // hole touches outer segment; pick leftmost endpoint
|
316
|
+
// look for points inside the triangle of hole point, segment intersection and endpoint;
|
317
|
+
// if there are no points found, we have a valid connection;
|
318
|
+
// otherwise choose the point of the minimum angle with the ray as connection point
|
319
|
+
|
320
|
+
var stop = m,
|
321
|
+
mx = m.x,
|
322
|
+
my = m.y,
|
323
|
+
tanMin = Infinity,
|
324
|
+
tan;
|
325
|
+
p = m;
|
326
|
+
|
327
|
+
do {
|
328
|
+
if (hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) {
|
329
|
+
tan = Math.abs(hy - p.y) / (hx - p.x); // tangential
|
330
|
+
|
331
|
+
if (locallyInside(p, hole) && (tan < tanMin || tan === tanMin && (p.x > m.x || p.x === m.x && sectorContainsSector(m, p)))) {
|
332
|
+
m = p;
|
333
|
+
tanMin = tan;
|
334
|
+
}
|
335
|
+
}
|
336
|
+
|
337
|
+
p = p.next;
|
338
|
+
} while (p !== stop);
|
339
|
+
|
340
|
+
return m;
|
341
|
+
} // whether sector in vertex m contains sector in vertex p in the same coordinates
|
342
|
+
|
343
|
+
|
344
|
+
function sectorContainsSector(m, p) {
|
345
|
+
return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0;
|
346
|
+
} // interlink polygon nodes in z-order
|
347
|
+
|
348
|
+
|
349
|
+
function indexCurve(start, minX, minY, invSize) {
|
350
|
+
var p = start;
|
351
|
+
|
352
|
+
do {
|
353
|
+
if (p.z === null) p.z = zOrder(p.x, p.y, minX, minY, invSize);
|
354
|
+
p.prevZ = p.prev;
|
355
|
+
p.nextZ = p.next;
|
356
|
+
p = p.next;
|
357
|
+
} while (p !== start);
|
358
|
+
|
359
|
+
p.prevZ.nextZ = null;
|
360
|
+
p.prevZ = null;
|
361
|
+
sortLinked(p);
|
362
|
+
} // Simon Tatham's linked list merge sort algorithm
|
363
|
+
// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
|
364
|
+
|
365
|
+
|
366
|
+
function sortLinked(list) {
|
367
|
+
var i,
|
368
|
+
p,
|
369
|
+
q,
|
370
|
+
e,
|
371
|
+
tail,
|
372
|
+
numMerges,
|
373
|
+
pSize,
|
374
|
+
qSize,
|
375
|
+
inSize = 1;
|
376
|
+
|
377
|
+
do {
|
378
|
+
p = list;
|
379
|
+
list = null;
|
380
|
+
tail = null;
|
381
|
+
numMerges = 0;
|
382
|
+
|
383
|
+
while (p) {
|
384
|
+
numMerges++;
|
385
|
+
q = p;
|
386
|
+
pSize = 0;
|
387
|
+
|
388
|
+
for (i = 0; i < inSize; i++) {
|
389
|
+
pSize++;
|
390
|
+
q = q.nextZ;
|
391
|
+
if (!q) break;
|
392
|
+
}
|
393
|
+
|
394
|
+
qSize = inSize;
|
395
|
+
|
396
|
+
while (pSize > 0 || qSize > 0 && q) {
|
397
|
+
if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) {
|
398
|
+
e = p;
|
399
|
+
p = p.nextZ;
|
400
|
+
pSize--;
|
401
|
+
} else {
|
402
|
+
e = q;
|
403
|
+
q = q.nextZ;
|
404
|
+
qSize--;
|
405
|
+
}
|
406
|
+
|
407
|
+
if (tail) tail.nextZ = e;else list = e;
|
408
|
+
e.prevZ = tail;
|
409
|
+
tail = e;
|
410
|
+
}
|
411
|
+
|
412
|
+
p = q;
|
413
|
+
}
|
414
|
+
|
415
|
+
tail.nextZ = null;
|
416
|
+
inSize *= 2;
|
417
|
+
} while (numMerges > 1);
|
418
|
+
|
419
|
+
return list;
|
420
|
+
} // z-order of a point given coords and inverse of the longer side of data bbox
|
421
|
+
|
422
|
+
|
423
|
+
function zOrder(x, y, minX, minY, invSize) {
|
424
|
+
// coords are transformed into non-negative 15-bit integer range
|
425
|
+
x = 32767 * (x - minX) * invSize;
|
426
|
+
y = 32767 * (y - minY) * invSize;
|
427
|
+
x = (x | x << 8) & 0x00FF00FF;
|
428
|
+
x = (x | x << 4) & 0x0F0F0F0F;
|
429
|
+
x = (x | x << 2) & 0x33333333;
|
430
|
+
x = (x | x << 1) & 0x55555555;
|
431
|
+
y = (y | y << 8) & 0x00FF00FF;
|
432
|
+
y = (y | y << 4) & 0x0F0F0F0F;
|
433
|
+
y = (y | y << 2) & 0x33333333;
|
434
|
+
y = (y | y << 1) & 0x55555555;
|
435
|
+
return x | y << 1;
|
436
|
+
} // find the leftmost node of a polygon ring
|
437
|
+
|
438
|
+
|
439
|
+
function getLeftmost(start) {
|
440
|
+
var p = start,
|
441
|
+
leftmost = start;
|
442
|
+
|
443
|
+
do {
|
444
|
+
if (p.x < leftmost.x || p.x === leftmost.x && p.y < leftmost.y) leftmost = p;
|
445
|
+
p = p.next;
|
446
|
+
} while (p !== start);
|
447
|
+
|
448
|
+
return leftmost;
|
449
|
+
} // check if a point lies within a convex triangle
|
450
|
+
|
451
|
+
|
452
|
+
function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
|
453
|
+
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;
|
454
|
+
} // check if a diagonal between two polygon nodes is valid (lies in polygon interior)
|
455
|
+
|
456
|
+
|
457
|
+
function isValidDiagonal(a, b) {
|
458
|
+
return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && ( // dones't intersect other edges
|
459
|
+
locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && ( // locally visible
|
460
|
+
area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors
|
461
|
+
equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0); // special zero-length case
|
462
|
+
} // signed area of a triangle
|
463
|
+
|
464
|
+
|
465
|
+
function area(p, q, r) {
|
466
|
+
return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
|
467
|
+
} // check if two points are equal
|
468
|
+
|
469
|
+
|
470
|
+
function equals(p1, p2) {
|
471
|
+
return p1.x === p2.x && p1.y === p2.y;
|
472
|
+
} // check if two segments intersect
|
473
|
+
|
474
|
+
|
475
|
+
function intersects(p1, q1, p2, q2) {
|
476
|
+
var o1 = sign(area(p1, q1, p2));
|
477
|
+
var o2 = sign(area(p1, q1, q2));
|
478
|
+
var o3 = sign(area(p2, q2, p1));
|
479
|
+
var o4 = sign(area(p2, q2, q1));
|
480
|
+
if (o1 !== o2 && o3 !== o4) return true; // general case
|
481
|
+
|
482
|
+
if (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1
|
483
|
+
|
484
|
+
if (o2 === 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1
|
485
|
+
|
486
|
+
if (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2
|
487
|
+
|
488
|
+
if (o4 === 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2
|
489
|
+
|
490
|
+
return false;
|
491
|
+
} // for collinear points p, q, r, check if point q lies on segment pr
|
492
|
+
|
493
|
+
|
494
|
+
function onSegment(p, q, r) {
|
495
|
+
return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y);
|
496
|
+
}
|
497
|
+
|
498
|
+
function sign(num) {
|
499
|
+
return num > 0 ? 1 : num < 0 ? -1 : 0;
|
500
|
+
} // check if a polygon diagonal intersects any polygon segments
|
501
|
+
|
502
|
+
|
503
|
+
function intersectsPolygon(a, b) {
|
504
|
+
var p = a;
|
505
|
+
|
506
|
+
do {
|
507
|
+
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;
|
508
|
+
p = p.next;
|
509
|
+
} while (p !== a);
|
510
|
+
|
511
|
+
return false;
|
512
|
+
} // check if a polygon diagonal is locally inside the polygon
|
513
|
+
|
514
|
+
|
515
|
+
function locallyInside(a, b) {
|
516
|
+
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;
|
517
|
+
} // check if the middle point of a polygon diagonal is inside the polygon
|
518
|
+
|
519
|
+
|
520
|
+
function middleInside(a, b) {
|
521
|
+
var p = a,
|
522
|
+
inside = false,
|
523
|
+
px = (a.x + b.x) / 2,
|
524
|
+
py = (a.y + b.y) / 2;
|
525
|
+
|
526
|
+
do {
|
527
|
+
if (p.y > py !== p.next.y > py && p.next.y !== p.y && px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x) inside = !inside;
|
528
|
+
p = p.next;
|
529
|
+
} while (p !== a);
|
530
|
+
|
531
|
+
return inside;
|
532
|
+
} // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two;
|
533
|
+
// if one belongs to the outer ring and another to a hole, it merges it into a single ring
|
534
|
+
|
535
|
+
|
536
|
+
function splitPolygon(a, b) {
|
537
|
+
var a2 = new Node(a.i, a.x, a.y),
|
538
|
+
b2 = new Node(b.i, b.x, b.y),
|
539
|
+
an = a.next,
|
540
|
+
bp = b.prev;
|
541
|
+
a.next = b;
|
542
|
+
b.prev = a;
|
543
|
+
a2.next = an;
|
544
|
+
an.prev = a2;
|
545
|
+
b2.next = a2;
|
546
|
+
a2.prev = b2;
|
547
|
+
bp.next = b2;
|
548
|
+
b2.prev = bp;
|
549
|
+
return b2;
|
550
|
+
} // create a node and optionally link it with previous one (in a circular doubly linked list)
|
551
|
+
|
552
|
+
|
553
|
+
function insertNode(i, x, y, last) {
|
554
|
+
var p = new Node(i, x, y);
|
555
|
+
|
556
|
+
if (!last) {
|
557
|
+
p.prev = p;
|
558
|
+
p.next = p;
|
559
|
+
} else {
|
560
|
+
p.next = last.next;
|
561
|
+
p.prev = last;
|
562
|
+
last.next.prev = p;
|
563
|
+
last.next = p;
|
564
|
+
}
|
565
|
+
|
566
|
+
return p;
|
567
|
+
}
|
568
|
+
|
569
|
+
function removeNode(p) {
|
570
|
+
p.next.prev = p.prev;
|
571
|
+
p.prev.next = p.next;
|
572
|
+
if (p.prevZ) p.prevZ.nextZ = p.nextZ;
|
573
|
+
if (p.nextZ) p.nextZ.prevZ = p.prevZ;
|
574
|
+
}
|
575
|
+
|
576
|
+
function Node(i, x, y) {
|
577
|
+
// vertex index in coordinates array
|
578
|
+
this.i = i; // vertex coordinates
|
579
|
+
|
580
|
+
this.x = x;
|
581
|
+
this.y = y; // previous and next vertex nodes in a polygon ring
|
582
|
+
|
583
|
+
this.prev = null;
|
584
|
+
this.next = null; // z-order curve value
|
585
|
+
|
586
|
+
this.z = null; // previous and next nodes in z-order
|
587
|
+
|
588
|
+
this.prevZ = null;
|
589
|
+
this.nextZ = null; // indicates whether this is a steiner point
|
590
|
+
|
591
|
+
this.steiner = false;
|
592
|
+
} // return a percentage difference between the polygon area and its triangulation area;
|
593
|
+
// used to verify correctness of triangulation
|
594
|
+
|
595
|
+
|
596
|
+
earcut.deviation = function (data, holeIndices, dim, triangles) {
|
597
|
+
var hasHoles = holeIndices && holeIndices.length;
|
598
|
+
var outerLen = hasHoles ? holeIndices[0] * dim : data.length;
|
599
|
+
var polygonArea = Math.abs(signedArea(data, 0, outerLen, dim));
|
600
|
+
|
601
|
+
if (hasHoles) {
|
602
|
+
for (var i = 0, len = holeIndices.length; i < len; i++) {
|
603
|
+
var start = holeIndices[i] * dim;
|
604
|
+
var end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
|
605
|
+
polygonArea -= Math.abs(signedArea(data, start, end, dim));
|
606
|
+
}
|
607
|
+
}
|
608
|
+
|
609
|
+
var trianglesArea = 0;
|
610
|
+
|
611
|
+
for (i = 0; i < triangles.length; i += 3) {
|
612
|
+
var a = triangles[i] * dim;
|
613
|
+
var b = triangles[i + 1] * dim;
|
614
|
+
var c = triangles[i + 2] * dim;
|
615
|
+
trianglesArea += Math.abs((data[a] - data[c]) * (data[b + 1] - data[a + 1]) - (data[a] - data[b]) * (data[c + 1] - data[a + 1]));
|
616
|
+
}
|
617
|
+
|
618
|
+
return polygonArea === 0 && trianglesArea === 0 ? 0 : Math.abs((trianglesArea - polygonArea) / polygonArea);
|
619
|
+
};
|
620
|
+
|
621
|
+
function signedArea(data, start, end, dim) {
|
622
|
+
var sum = 0;
|
623
|
+
|
624
|
+
for (var i = start, j = end - dim; i < end; i += dim) {
|
625
|
+
sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
|
626
|
+
j = i;
|
627
|
+
}
|
628
|
+
|
629
|
+
return sum;
|
630
|
+
} // turn a polygon in a multi-dimensional array form (e.g. as in GeoJSON) into a form Earcut accepts
|
631
|
+
|
632
|
+
|
633
|
+
earcut.flatten = function (data) {
|
634
|
+
var dim = data[0][0].length,
|
635
|
+
result = {
|
636
|
+
vertices: [],
|
637
|
+
holes: [],
|
638
|
+
dimensions: dim
|
639
|
+
},
|
640
|
+
holeIndex = 0;
|
641
|
+
|
642
|
+
for (var i = 0; i < data.length; i++) {
|
643
|
+
for (var j = 0; j < data[i].length; j++) {
|
644
|
+
for (var d = 0; d < dim; d++) {
|
645
|
+
result.vertices.push(data[i][j][d]);
|
646
|
+
}
|
647
|
+
}
|
648
|
+
|
649
|
+
if (i > 0) {
|
650
|
+
holeIndex += data[i - 1].length;
|
651
|
+
result.holes.push(holeIndex);
|
652
|
+
}
|
653
|
+
}
|
654
|
+
|
655
|
+
return result;
|
656
|
+
};
|
657
|
+
|
658
|
+
var earcut$1 = earcut$2.exports;
|
659
|
+
|
660
|
+
/**
|
661
|
+
* https://github.com/Turfjs/turf/blob/master/packages/turf-boolean-clockwise/index.ts
|
662
|
+
* @param {*} ring
|
663
|
+
* @returns
|
664
|
+
*/
|
665
|
+
function isClockwise(ring) {
|
666
|
+
var sum = 0;
|
667
|
+
var i = 1;
|
668
|
+
var prev;
|
669
|
+
var cur;
|
670
|
+
var len = ring.length;
|
671
|
+
|
672
|
+
while (i < len) {
|
673
|
+
prev = cur || ring[0];
|
674
|
+
cur = ring[i];
|
675
|
+
sum += (cur[0] - prev[0]) * (cur[1] + prev[1]);
|
676
|
+
i++;
|
677
|
+
}
|
678
|
+
|
679
|
+
return sum > 0;
|
680
|
+
}
|
681
|
+
|
682
|
+
function v3Sub(out, v1, v2) {
|
683
|
+
out[0] = v1[0] - v2[0];
|
684
|
+
out[1] = v1[1] - v2[1];
|
685
|
+
out[2] = v1[2] - v2[2];
|
686
|
+
return out;
|
687
|
+
}
|
688
|
+
|
689
|
+
function v3Normalize(out, v) {
|
690
|
+
var x = v[0];
|
691
|
+
var y = v[1];
|
692
|
+
var z = v[2];
|
693
|
+
var d = Math.sqrt(x * x + y * y + z * z) || 1;
|
694
|
+
out[0] = x / d;
|
695
|
+
out[1] = y / d;
|
696
|
+
out[2] = z / d;
|
697
|
+
return out;
|
698
|
+
}
|
699
|
+
|
700
|
+
function v3Cross(out, v1, v2) {
|
701
|
+
var ax = v1[0],
|
702
|
+
ay = v1[1],
|
703
|
+
az = v1[2],
|
704
|
+
bx = v2[0],
|
705
|
+
by = v2[1],
|
706
|
+
bz = v2[2];
|
707
|
+
out[0] = ay * bz - az * by;
|
708
|
+
out[1] = az * bx - ax * bz;
|
709
|
+
out[2] = ax * by - ay * bx;
|
710
|
+
return out;
|
711
|
+
}
|
712
|
+
|
713
|
+
function generateNormal(indices, position) {
|
714
|
+
function v3Set(p, a, b, c) {
|
715
|
+
p[0] = a;
|
716
|
+
p[1] = b;
|
717
|
+
p[2] = c;
|
718
|
+
}
|
719
|
+
|
720
|
+
var p1 = [];
|
721
|
+
var p2 = [];
|
722
|
+
var p3 = [];
|
723
|
+
var v21 = [];
|
724
|
+
var v32 = [];
|
725
|
+
var n = [];
|
726
|
+
var len = indices.length;
|
727
|
+
var normals = new Float32Array(position.length);
|
728
|
+
|
729
|
+
for (var f = 0; f < len; f += 3) {
|
730
|
+
// const i1 = indices[f++] * 3;
|
731
|
+
// const i2 = indices[f++] * 3;
|
732
|
+
// const i3 = indices[f++] * 3;
|
733
|
+
// const i1 = indices[f];
|
734
|
+
// const i2 = indices[f + 1];
|
735
|
+
// const i3 = indices[f + 2];
|
736
|
+
var a = indices[f],
|
737
|
+
b = indices[f + 1],
|
738
|
+
c = indices[f + 2];
|
739
|
+
var i1 = a * 3,
|
740
|
+
i2 = b * 3,
|
741
|
+
i3 = c * 3;
|
742
|
+
v3Set(p1, position[i1], position[i1 + 1], position[i1 + 2]);
|
743
|
+
v3Set(p2, position[i2], position[i2 + 1], position[i2 + 2]);
|
744
|
+
v3Set(p3, position[i3], position[i3 + 1], position[i3 + 2]); // pA.fromBufferAttribute(positionAttribute, vA);
|
745
|
+
// pB.fromBufferAttribute(positionAttribute, vB);
|
746
|
+
// pC.fromBufferAttribute(positionAttribute, vC);
|
747
|
+
// cb.subVectors(pC, pB);
|
748
|
+
// ab.subVectors(pA, pB);
|
749
|
+
// cb.cross(ab);
|
750
|
+
|
751
|
+
v3Sub(v32, p3, p2);
|
752
|
+
v3Sub(v21, p1, p2);
|
753
|
+
v3Cross(n, v32, v21); // Already be weighted by the triangle area
|
754
|
+
|
755
|
+
for (var i = 0; i < 3; i++) {
|
756
|
+
normals[i1 + i] += n[i];
|
757
|
+
normals[i2 + i] += n[i];
|
758
|
+
normals[i3 + i] += n[i];
|
759
|
+
}
|
760
|
+
}
|
761
|
+
|
762
|
+
for (var _i = 0; _i < normals.length; _i += 3) {
|
763
|
+
v3Set(n, normals[_i], normals[_i + 1], normals[_i + 2]);
|
764
|
+
v3Normalize(n, n);
|
765
|
+
normals[_i] = n[0] || 1;
|
766
|
+
normals[_i + 1] = n[1] || 1;
|
767
|
+
normals[_i + 2] = n[2] || 1;
|
768
|
+
}
|
769
|
+
|
770
|
+
return normals;
|
771
|
+
}
|
772
|
+
function merge(results) {
|
773
|
+
if (results.length === 1) {
|
774
|
+
var _result = {
|
775
|
+
position: results[0].position,
|
776
|
+
normal: results[0].normal,
|
777
|
+
uv: results[0].uv,
|
778
|
+
indices: results[0].indices,
|
779
|
+
results: results
|
780
|
+
};
|
781
|
+
return _result;
|
782
|
+
}
|
783
|
+
|
784
|
+
var plen = 0,
|
785
|
+
ilen = 0;
|
786
|
+
|
787
|
+
for (var i = 0, len = results.length; i < len; i++) {
|
788
|
+
var _results$i = results[i],
|
789
|
+
position = _results$i.position,
|
790
|
+
indices = _results$i.indices;
|
791
|
+
plen += position.length;
|
792
|
+
ilen += indices.length;
|
793
|
+
}
|
794
|
+
|
795
|
+
var result = {
|
796
|
+
position: new Float32Array(plen),
|
797
|
+
normal: new Float32Array(plen),
|
798
|
+
uv: new Float32Array(plen / 3 * 2),
|
799
|
+
indices: new Uint32Array(ilen),
|
800
|
+
results: results
|
801
|
+
};
|
802
|
+
var pOffset = 0,
|
803
|
+
pCount = 0,
|
804
|
+
iIdx = 0,
|
805
|
+
uvOffset = 0;
|
806
|
+
|
807
|
+
for (var _i2 = 0, _len = results.length; _i2 < _len; _i2++) {
|
808
|
+
var _results$_i = results[_i2],
|
809
|
+
_position = _results$_i.position,
|
810
|
+
_indices = _results$_i.indices,
|
811
|
+
normal = _results$_i.normal,
|
812
|
+
uv = _results$_i.uv;
|
813
|
+
result.position.set(_position, pOffset);
|
814
|
+
result.normal.set(normal, pOffset);
|
815
|
+
result.uv.set(uv, uvOffset);
|
816
|
+
|
817
|
+
for (var j = 0, len1 = _indices.length; j < len1; j++) {
|
818
|
+
var pIndex = _indices[j] + pCount;
|
819
|
+
result.indices[iIdx] = pIndex;
|
820
|
+
iIdx++;
|
821
|
+
}
|
822
|
+
|
823
|
+
uvOffset += uv.length;
|
824
|
+
pOffset += _position.length;
|
825
|
+
pCount += _position.length / 3;
|
826
|
+
}
|
827
|
+
|
828
|
+
return result;
|
829
|
+
}
|
830
|
+
function radToDeg(rad) {
|
831
|
+
return rad * 180 / Math.PI;
|
832
|
+
}
|
833
|
+
function degToRad(angle) {
|
834
|
+
return angle / 180 * Math.PI;
|
835
|
+
} // https://github.com/mrdoob/three.js/blob/16f13e3b07e31d0e9a00df7c3366bbe0e464588c/src/geometries/ExtrudeGeometry.js?_pjax=%23js-repo-pjax-container#L736
|
836
|
+
|
837
|
+
function generateSideWallUV(uvs, vertices, indexA, indexB, indexC, indexD) {
|
838
|
+
var a_x = vertices[indexA * 3];
|
839
|
+
var a_y = vertices[indexA * 3 + 1];
|
840
|
+
var a_z = vertices[indexA * 3 + 2];
|
841
|
+
var b_x = vertices[indexB * 3];
|
842
|
+
var b_y = vertices[indexB * 3 + 1];
|
843
|
+
var b_z = vertices[indexB * 3 + 2];
|
844
|
+
var c_x = vertices[indexC * 3];
|
845
|
+
var c_y = vertices[indexC * 3 + 1];
|
846
|
+
var c_z = vertices[indexC * 3 + 2];
|
847
|
+
var d_x = vertices[indexD * 3];
|
848
|
+
var d_y = vertices[indexD * 3 + 1];
|
849
|
+
var d_z = vertices[indexD * 3 + 2];
|
850
|
+
|
851
|
+
if (Math.abs(a_y - b_y) < Math.abs(a_x - b_x)) {
|
852
|
+
uvs.push(a_x, 1 - a_z);
|
853
|
+
uvs.push(b_x, 1 - b_z);
|
854
|
+
uvs.push(c_x, 1 - c_z);
|
855
|
+
uvs.push(d_x, 1 - d_z); // return [
|
856
|
+
// new Vector2(a_x, 1 - a_z),
|
857
|
+
// new Vector2(b_x, 1 - b_z),
|
858
|
+
// new Vector2(c_x, 1 - c_z),
|
859
|
+
// new Vector2(d_x, 1 - d_z)
|
860
|
+
// ];
|
861
|
+
} else {
|
862
|
+
uvs.push(a_y, 1 - a_z);
|
863
|
+
uvs.push(b_y, 1 - b_z);
|
864
|
+
uvs.push(c_y, 1 - c_z);
|
865
|
+
uvs.push(d_y, 1 - d_z); // return [
|
866
|
+
// new Vector2(a_y, 1 - a_z),
|
867
|
+
// new Vector2(b_y, 1 - b_z),
|
868
|
+
// new Vector2(c_y, 1 - c_z),
|
869
|
+
// new Vector2(d_y, 1 - d_z)
|
870
|
+
// ];
|
871
|
+
}
|
872
|
+
}
|
873
|
+
|
874
|
+
function extrudePolygons(polygons, options) {
|
875
|
+
options = Object.assign({}, {
|
876
|
+
depth: 2
|
877
|
+
}, options);
|
878
|
+
var results = polygons.map(function (polygon) {
|
879
|
+
if (!isClockwise(polygon[0])) {
|
880
|
+
polygon[0] = polygon[0].reverse();
|
881
|
+
}
|
882
|
+
|
883
|
+
polygon.slice(1, Infinity).forEach(function (coordinates, index) {
|
884
|
+
if (isClockwise(coordinates)) {
|
885
|
+
polygon[index + 1] = coordinates.reverse();
|
886
|
+
}
|
887
|
+
});
|
888
|
+
polygon.forEach(function (ring) {
|
889
|
+
var len = ring.length;
|
890
|
+
var _ring$ = ring[0],
|
891
|
+
x1 = _ring$[0],
|
892
|
+
y1 = _ring$[1],
|
893
|
+
_ring = ring[len - 1],
|
894
|
+
x2 = _ring[0],
|
895
|
+
y2 = _ring[1];
|
896
|
+
|
897
|
+
if (x1 === x2 && y1 === y2) {
|
898
|
+
ring.splice(len - 1, 1);
|
899
|
+
}
|
900
|
+
});
|
901
|
+
var result = flatVertices(polygon, options);
|
902
|
+
result.polygon = polygon;
|
903
|
+
var time = 'earcut';
|
904
|
+
console.time(time);
|
905
|
+
var triangles = earcut$1(result.flatVertices, result.holes, 2);
|
906
|
+
console.timeEnd(time);
|
907
|
+
generateTopAndBottom$1(result, triangles);
|
908
|
+
generateSides$1(result, options);
|
909
|
+
result.position = new Float32Array(result.points);
|
910
|
+
result.indices = new Uint32Array(result.index);
|
911
|
+
result.uv = new Float32Array(result.uvs);
|
912
|
+
result.normal = generateNormal(result.indices, result.position);
|
913
|
+
return result;
|
914
|
+
});
|
915
|
+
var result = merge(results);
|
916
|
+
result.polygons = polygons;
|
917
|
+
return result;
|
918
|
+
}
|
919
|
+
|
920
|
+
function generateTopAndBottom$1(result, triangles) {
|
921
|
+
var index = [];
|
922
|
+
var count = result.count;
|
923
|
+
|
924
|
+
for (var i = 0, len = triangles.length; i < len; i += 3) {
|
925
|
+
// top
|
926
|
+
var a = triangles[i],
|
927
|
+
b = triangles[i + 1],
|
928
|
+
c = triangles[i + 2];
|
929
|
+
index[i] = a;
|
930
|
+
index[i + 1] = b;
|
931
|
+
index[i + 2] = c; // bottom
|
932
|
+
|
933
|
+
var idx = len + i;
|
934
|
+
var a1 = count + a,
|
935
|
+
b1 = count + b,
|
936
|
+
c1 = count + c;
|
937
|
+
index[idx] = a1;
|
938
|
+
index[idx + 1] = b1;
|
939
|
+
index[idx + 2] = c1;
|
940
|
+
}
|
941
|
+
|
942
|
+
result.index = index;
|
943
|
+
}
|
944
|
+
|
945
|
+
function generateSides$1(result, options) {
|
946
|
+
var points = result.points,
|
947
|
+
index = result.index,
|
948
|
+
polygon = result.polygon,
|
949
|
+
uvs = result.uvs;
|
950
|
+
var z = options.depth;
|
951
|
+
|
952
|
+
for (var i = 0, len = polygon.length; i < len; i++) {
|
953
|
+
var ring = polygon[i];
|
954
|
+
|
955
|
+
for (var j = 0, len1 = ring.length; j < len1; j++) {
|
956
|
+
var v1 = ring[j];
|
957
|
+
var v2 = ring[j + 1];
|
958
|
+
|
959
|
+
if (j === len1 - 1) {
|
960
|
+
v2 = ring[0];
|
961
|
+
} // const p1 = [v1[0], v1[1], options.depth],
|
962
|
+
// p2 = [v2[0], v2[1], options.depth],
|
963
|
+
// p3 = [v1[0], v1[1], 0],
|
964
|
+
// p4 = [v2[0], v2[1], 0];
|
965
|
+
|
966
|
+
|
967
|
+
var idx = points.length / 3;
|
968
|
+
points.push(v1[0], v1[1], 0, v2[0], v2[1], 0, v1[0], v1[1], z, v2[0], v2[1], z);
|
969
|
+
var a = idx,
|
970
|
+
b = idx + 1,
|
971
|
+
c = idx + 2,
|
972
|
+
d = idx + 3; // points.push(p3, p4, p1, p2);
|
973
|
+
|
974
|
+
index.push(a, c, b);
|
975
|
+
index.push(c, d, b);
|
976
|
+
generateSideWallUV(uvs, points, a, b, c, d);
|
977
|
+
}
|
978
|
+
}
|
979
|
+
}
|
980
|
+
|
981
|
+
function calPolygonPointsCount(polygon) {
|
982
|
+
var count = 0;
|
983
|
+
var i = 0;
|
984
|
+
var len = polygon.length;
|
985
|
+
|
986
|
+
while (i < len) {
|
987
|
+
count += polygon[i].length;
|
988
|
+
i++;
|
989
|
+
}
|
990
|
+
|
991
|
+
return count;
|
992
|
+
}
|
993
|
+
|
994
|
+
function flatVertices(polygon, options) {
|
995
|
+
var count = calPolygonPointsCount(polygon);
|
996
|
+
var len = polygon.length;
|
997
|
+
var holes = [],
|
998
|
+
flatVertices = new Float32Array(count * 2),
|
999
|
+
points = [],
|
1000
|
+
uvs = [];
|
1001
|
+
var pOffset = count * 3,
|
1002
|
+
uOffset = count * 2;
|
1003
|
+
var z = options.depth;
|
1004
|
+
var idx0 = 0,
|
1005
|
+
idx1 = 0,
|
1006
|
+
idx2 = 0;
|
1007
|
+
|
1008
|
+
for (var i = 0; i < len; i++) {
|
1009
|
+
var ring = polygon[i];
|
1010
|
+
|
1011
|
+
if (i > 0) {
|
1012
|
+
holes.push(idx0 / 2);
|
1013
|
+
}
|
1014
|
+
|
1015
|
+
for (var j = 0, len1 = ring.length; j < len1; j++) {
|
1016
|
+
var c = ring[j];
|
1017
|
+
var x = c[0],
|
1018
|
+
y = c[1];
|
1019
|
+
flatVertices[idx0++] = x;
|
1020
|
+
flatVertices[idx0++] = y; // top vertices
|
1021
|
+
|
1022
|
+
points[idx1] = x;
|
1023
|
+
points[idx1 + 1] = y;
|
1024
|
+
points[idx1 + 2] = z; // bottom vertices
|
1025
|
+
|
1026
|
+
points[pOffset + idx1] = x;
|
1027
|
+
points[pOffset + idx1 + 1] = y;
|
1028
|
+
points[pOffset + idx1 + 2] = 0;
|
1029
|
+
uvs[idx2] = x;
|
1030
|
+
uvs[idx2 + 1] = y;
|
1031
|
+
uvs[uOffset + idx2] = x;
|
1032
|
+
uvs[uOffset + idx2 + 1] = y;
|
1033
|
+
idx1 += 3;
|
1034
|
+
idx2 += 2;
|
1035
|
+
}
|
1036
|
+
}
|
1037
|
+
|
1038
|
+
return {
|
1039
|
+
flatVertices: flatVertices,
|
1040
|
+
holes: holes,
|
1041
|
+
points: points,
|
1042
|
+
count: count,
|
1043
|
+
uvs: uvs
|
1044
|
+
};
|
1045
|
+
}
|
1046
|
+
|
1047
|
+
function extrudePolylines(lines, options) {
|
1048
|
+
options = Object.assign({}, {
|
1049
|
+
depth: 2,
|
1050
|
+
lineWidth: 1
|
1051
|
+
}, options);
|
1052
|
+
var results = lines.map(function (line) {
|
1053
|
+
var result = expandLine(line, options);
|
1054
|
+
result.line = line;
|
1055
|
+
generateTopAndBottom(result, options);
|
1056
|
+
generateSides(result, options);
|
1057
|
+
result.position = new Float32Array(result.points);
|
1058
|
+
result.indices = new Uint32Array(result.index);
|
1059
|
+
result.uv = new Float32Array(result.uvs);
|
1060
|
+
result.normal = generateNormal(result.indices, result.position);
|
1061
|
+
return result;
|
1062
|
+
});
|
1063
|
+
var result = merge(results);
|
1064
|
+
result.lines = lines;
|
1065
|
+
return result;
|
1066
|
+
}
|
1067
|
+
|
1068
|
+
function generateTopAndBottom(result, options) {
|
1069
|
+
var z = options.depth;
|
1070
|
+
var points = [],
|
1071
|
+
index = [],
|
1072
|
+
uvs = [];
|
1073
|
+
var leftPoints = result.leftPoints,
|
1074
|
+
rightPoints = result.rightPoints;
|
1075
|
+
|
1076
|
+
for (var i = 0, len = leftPoints.length; i < len; i++) {
|
1077
|
+
// top left
|
1078
|
+
var idx0 = i * 3;
|
1079
|
+
var _leftPoints$i = leftPoints[i],
|
1080
|
+
x1 = _leftPoints$i[0],
|
1081
|
+
y1 = _leftPoints$i[1];
|
1082
|
+
points[idx0] = x1;
|
1083
|
+
points[idx0 + 1] = y1;
|
1084
|
+
points[idx0 + 2] = z; // top right
|
1085
|
+
|
1086
|
+
var _rightPoints$i = rightPoints[i],
|
1087
|
+
x2 = _rightPoints$i[0],
|
1088
|
+
y2 = _rightPoints$i[1];
|
1089
|
+
var idx1 = len * 3 + idx0;
|
1090
|
+
points[idx1] = x2;
|
1091
|
+
points[idx1 + 1] = y2;
|
1092
|
+
points[idx1 + 2] = z; // bottom left
|
1093
|
+
|
1094
|
+
var idx2 = len * 2 * 3 + idx0;
|
1095
|
+
points[idx2] = x1;
|
1096
|
+
points[idx2 + 1] = y1;
|
1097
|
+
points[idx2 + 2] = 0; // bottom right
|
1098
|
+
|
1099
|
+
var idx3 = len * 2 * 3 + len * 3 + idx0;
|
1100
|
+
points[idx3] = x2;
|
1101
|
+
points[idx3 + 1] = y2;
|
1102
|
+
points[idx3 + 2] = 0;
|
1103
|
+
}
|
1104
|
+
|
1105
|
+
for (var _i = 0, _len = points.length; _i < _len; _i += 3) {
|
1106
|
+
var x = points[_i],
|
1107
|
+
y = points[_i + 1];
|
1108
|
+
uvs.push(x, y);
|
1109
|
+
}
|
1110
|
+
|
1111
|
+
for (var _i2 = 0, _len2 = leftPoints.length; _i2 < _len2 - 1; _i2++) {
|
1112
|
+
// top
|
1113
|
+
// left1 left2 right1,right2
|
1114
|
+
var a1 = _i2,
|
1115
|
+
b1 = _i2 + 1,
|
1116
|
+
c1 = a1 + _len2,
|
1117
|
+
d1 = b1 + _len2;
|
1118
|
+
index.push(a1, c1, b1);
|
1119
|
+
index.push(c1, d1, b1); // bottom
|
1120
|
+
// left1 left2 right1,right2
|
1121
|
+
|
1122
|
+
var len2 = _len2 * 2;
|
1123
|
+
var a2 = _i2 + len2,
|
1124
|
+
b2 = a2 + 1,
|
1125
|
+
c2 = a2 + _len2,
|
1126
|
+
d2 = b2 + _len2;
|
1127
|
+
index.push(a2, c2, b2);
|
1128
|
+
index.push(c2, d2, b2);
|
1129
|
+
}
|
1130
|
+
|
1131
|
+
result.index = index;
|
1132
|
+
result.points = points;
|
1133
|
+
result.uvs = uvs;
|
1134
|
+
}
|
1135
|
+
|
1136
|
+
function generateSides(result, options) {
|
1137
|
+
var points = result.points,
|
1138
|
+
index = result.index,
|
1139
|
+
leftPoints = result.leftPoints,
|
1140
|
+
rightPoints = result.rightPoints,
|
1141
|
+
uvs = result.uvs;
|
1142
|
+
var z = options.depth;
|
1143
|
+
var rings = [leftPoints, rightPoints];
|
1144
|
+
|
1145
|
+
function addOneSideIndex(v1, v2) {
|
1146
|
+
var idx = points.length / 3;
|
1147
|
+
points.push(v1[0], v1[1], 0, v2[0], v2[1], 0, v1[0], v1[1], z, v2[0], v2[1], z);
|
1148
|
+
var a = idx,
|
1149
|
+
b = idx + 1,
|
1150
|
+
c = idx + 2,
|
1151
|
+
d = idx + 3; // points.push(p3, p4, p1, p2);
|
1152
|
+
|
1153
|
+
index.push(a, c, b);
|
1154
|
+
index.push(c, d, b);
|
1155
|
+
generateSideWallUV(uvs, points, a, b, c, d);
|
1156
|
+
}
|
1157
|
+
|
1158
|
+
for (var i = 0, _len3 = rings.length; i < _len3; i++) {
|
1159
|
+
var ring = rings[i];
|
1160
|
+
|
1161
|
+
if (i > 0) {
|
1162
|
+
ring = ring.map(function (p) {
|
1163
|
+
return p;
|
1164
|
+
});
|
1165
|
+
ring = ring.reverse();
|
1166
|
+
}
|
1167
|
+
|
1168
|
+
for (var j = 0, len1 = ring.length - 1; j < len1; j++) {
|
1169
|
+
var v1 = ring[j];
|
1170
|
+
var v2 = ring[j + 1];
|
1171
|
+
addOneSideIndex(v1, v2);
|
1172
|
+
}
|
1173
|
+
}
|
1174
|
+
|
1175
|
+
var len = leftPoints.length;
|
1176
|
+
var vs = [rightPoints[0], leftPoints[0], leftPoints[len - 1], rightPoints[len - 1]];
|
1177
|
+
|
1178
|
+
for (var _i3 = 0; _i3 < vs.length; _i3 += 2) {
|
1179
|
+
var _v = vs[_i3],
|
1180
|
+
_v2 = vs[_i3 + 1];
|
1181
|
+
addOneSideIndex(_v, _v2);
|
1182
|
+
}
|
1183
|
+
}
|
1184
|
+
|
1185
|
+
var TEMPV1 = {
|
1186
|
+
x: 0,
|
1187
|
+
y: 0
|
1188
|
+
},
|
1189
|
+
TEMPV2 = {
|
1190
|
+
x: 0,
|
1191
|
+
y: 0
|
1192
|
+
};
|
1193
|
+
|
1194
|
+
function expandLine(line, options) {
|
1195
|
+
var preAngle = 0;
|
1196
|
+
var radius = options.lineWidth / 2;
|
1197
|
+
var points = [],
|
1198
|
+
leftPoints = [],
|
1199
|
+
rightPoints = [];
|
1200
|
+
var len = line.length;
|
1201
|
+
|
1202
|
+
for (var i = 0; i < len - 1; i++) {
|
1203
|
+
var _p = line[i],
|
1204
|
+
_p2 = line[i + 1];
|
1205
|
+
var dy = _p2[1] - _p[1],
|
1206
|
+
dx = _p2[0] - _p[0];
|
1207
|
+
var _rAngle = 0;
|
1208
|
+
var rad = Math.atan(dy / dx);
|
1209
|
+
var angle = radToDeg(rad);
|
1210
|
+
preAngle = angle;
|
1211
|
+
|
1212
|
+
if (i === 0) {
|
1213
|
+
_rAngle = angle;
|
1214
|
+
_rAngle -= 90;
|
1215
|
+
} else {
|
1216
|
+
var p0 = line[i - 1];
|
1217
|
+
TEMPV1.x = p0[0] - _p[0];
|
1218
|
+
TEMPV1.y = p0[1] - _p[1];
|
1219
|
+
TEMPV2.x = _p2[0] - _p[0];
|
1220
|
+
TEMPV2.y = _p2[1] - _p[1];
|
1221
|
+
var vAngle = getAngle(TEMPV1, TEMPV2);
|
1222
|
+
_rAngle = angle - vAngle / 2;
|
1223
|
+
}
|
1224
|
+
|
1225
|
+
var _rRad = degToRad(_rAngle);
|
1226
|
+
|
1227
|
+
var _calOffsetPoint = calOffsetPoint(_rRad, radius, _p),
|
1228
|
+
_op = _calOffsetPoint[0],
|
1229
|
+
_op2 = _calOffsetPoint[1];
|
1230
|
+
|
1231
|
+
points.push(_op, _op2);
|
1232
|
+
|
1233
|
+
if (leftOnLine(_op, _p, _p2)) {
|
1234
|
+
leftPoints.push(_op);
|
1235
|
+
rightPoints.push(_op2);
|
1236
|
+
} else {
|
1237
|
+
leftPoints.push(_op2);
|
1238
|
+
rightPoints.push(_op);
|
1239
|
+
}
|
1240
|
+
}
|
1241
|
+
|
1242
|
+
var rAngle = preAngle;
|
1243
|
+
rAngle -= 90;
|
1244
|
+
var rRad = degToRad(rAngle);
|
1245
|
+
var p1 = line[len - 2];
|
1246
|
+
var p2 = line[len - 1];
|
1247
|
+
|
1248
|
+
var _calOffsetPoint2 = calOffsetPoint(rRad, radius, p2),
|
1249
|
+
op1 = _calOffsetPoint2[0],
|
1250
|
+
op2 = _calOffsetPoint2[1];
|
1251
|
+
|
1252
|
+
points.push(op1, op2);
|
1253
|
+
|
1254
|
+
if (leftOnLine(op1, p1, p2)) {
|
1255
|
+
leftPoints.push(op1);
|
1256
|
+
rightPoints.push(op2);
|
1257
|
+
} else {
|
1258
|
+
leftPoints.push(op2);
|
1259
|
+
rightPoints.push(op1);
|
1260
|
+
}
|
1261
|
+
|
1262
|
+
return {
|
1263
|
+
offsetPoints: points,
|
1264
|
+
leftPoints: leftPoints,
|
1265
|
+
rightPoints: rightPoints
|
1266
|
+
};
|
1267
|
+
}
|
1268
|
+
|
1269
|
+
function calOffsetPoint(rad, radius, p) {
|
1270
|
+
var x = p[0],
|
1271
|
+
y = p[1];
|
1272
|
+
var x1 = Math.cos(rad) * radius,
|
1273
|
+
y1 = Math.sin(rad) * radius;
|
1274
|
+
var p1 = [x + x1, y + y1];
|
1275
|
+
var rad1 = rad += Math.PI;
|
1276
|
+
var x2 = Math.cos(rad1) * radius,
|
1277
|
+
y2 = Math.sin(rad1) * radius;
|
1278
|
+
var p2 = [x + x2, y + y2];
|
1279
|
+
return [p1, p2];
|
1280
|
+
}
|
1281
|
+
|
1282
|
+
var getAngle = function getAngle(_ref, _ref2) {
|
1283
|
+
var x1 = _ref.x,
|
1284
|
+
y1 = _ref.y;
|
1285
|
+
var x2 = _ref2.x,
|
1286
|
+
y2 = _ref2.y;
|
1287
|
+
var dot = x1 * x2 + y1 * y2;
|
1288
|
+
var det = x1 * y2 - y1 * x2;
|
1289
|
+
var angle = Math.atan2(det, dot) / Math.PI * 180;
|
1290
|
+
return (angle + 360) % 360;
|
1291
|
+
};
|
1292
|
+
|
1293
|
+
function leftOnLine(p, p1, p2) {
|
1294
|
+
var x1 = p1[0],
|
1295
|
+
y1 = p1[1];
|
1296
|
+
var x2 = p2[0],
|
1297
|
+
y2 = p2[1];
|
1298
|
+
var x = p[0],
|
1299
|
+
y = p[1];
|
1300
|
+
return (y1 - y2) * x + (x2 - x1) * y + x1 * y2 - x2 * y1 > 0;
|
1301
|
+
}
|
1302
|
+
|
1303
|
+
export { extrudePolygons, extrudePolylines };
|
1304
|
+
|
1305
|
+
typeof console !== 'undefined' && console.log('poly-extrude v0.0.1');
|