@jgphilpott/polytree 0.0.6 → 0.0.8
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 +31 -1
- package/package.json +18 -11
- package/polytree.bundle.browser.js +3453 -0
- package/polytree.bundle.browser.min.js +1 -0
- package/polytree.bundle.js +3653 -0
- package/polytree.bundle.min.js +1 -0
- package/polytree.js +0 -2938
|
@@ -0,0 +1,3653 @@
|
|
|
1
|
+
// Generated by CoffeeScript 2.7.0
|
|
2
|
+
var Box3, BufferAttribute, BufferGeometry, DoubleSide, Matrix3, Mesh, Ray, Raycaster, Triangle, Vector2, Vector3;
|
|
3
|
+
|
|
4
|
+
({Vector2, Vector3, Mesh, Triangle, Box3, Matrix3, DoubleSide, Ray, Raycaster, BufferGeometry, BufferAttribute} = require("three"));
|
|
5
|
+
// Generated by CoffeeScript 2.7.0
|
|
6
|
+
var BACK, COPLANAR, CSG_Rules, EPSILON, FRONT, Plane, Polygon, Polytree, RAY_EPSILON, SPANNING, Vertex, _asyncUnionArrayID, _asyncUnionID, _matrix3, _normal1, _polygonID, _wP, _wP_EPS_ARR, _wP_EPS_ARR_COUNT, _wV1, _wV2, _wV3, calcWindingNumber_buffer, disposePolytree, edge1, edge2, h, handleIntersectingPolytrees, handleObjectForOp, handleObjectForOp_async, isUniqueTriangle, isValidTriangle, nbuf2, nbuf3, pointRounding, polyInside_WindingNumber_buffer, prepareTriangleBuffer, q, rayIntersectsTriangle, raycastIntersectAscSort, returnXYZ, s, splitPolygonArr, splitPolygonByPlane, tempBox3, tempRay, tempRayDirection, tempRaycaster, tempVector1, tempVector2, tmpm3, triangleVertex0, triangleVertex1, ttvv0, wNPI;
|
|
7
|
+
|
|
8
|
+
tempVector1 = new Vector3();
|
|
9
|
+
|
|
10
|
+
tempVector2 = new Vector3();
|
|
11
|
+
|
|
12
|
+
tempBox3 = new Box3();
|
|
13
|
+
|
|
14
|
+
triangleVertex0 = new Vector3();
|
|
15
|
+
|
|
16
|
+
triangleVertex1 = new Vector3();
|
|
17
|
+
|
|
18
|
+
tempRaycaster = new Raycaster();
|
|
19
|
+
|
|
20
|
+
tempRay = new Ray();
|
|
21
|
+
|
|
22
|
+
tempRayDirection = new Vector3(0, 0, 1);
|
|
23
|
+
|
|
24
|
+
EPSILON = 1e-5;
|
|
25
|
+
|
|
26
|
+
COPLANAR = 0;
|
|
27
|
+
|
|
28
|
+
FRONT = 1;
|
|
29
|
+
|
|
30
|
+
BACK = 2;
|
|
31
|
+
|
|
32
|
+
SPANNING = 3;
|
|
33
|
+
|
|
34
|
+
_polygonID = 0;
|
|
35
|
+
|
|
36
|
+
Polytree = class Polytree {
|
|
37
|
+
constructor(box, parent) {
|
|
38
|
+
this.polygons = [];
|
|
39
|
+
this.replacedPolygons = [];
|
|
40
|
+
this.mesh;
|
|
41
|
+
this.originalMatrixWorld;
|
|
42
|
+
this.box = box;
|
|
43
|
+
this.subTrees = [];
|
|
44
|
+
this.parent = parent;
|
|
45
|
+
this.level = 0;
|
|
46
|
+
this.polygonArrays = void 0;
|
|
47
|
+
// @isPolytree = true
|
|
48
|
+
this.addPolygonsArrayToRoot(this.polygons);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
clone() {
|
|
52
|
+
return (new this.constructor()).copy(this);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
copy(source) {
|
|
56
|
+
var i, k, ref, subTree;
|
|
57
|
+
this.deletePolygonsArrayFromRoot(this.polygons);
|
|
58
|
+
this.polygons = source.polygons.map(function(p) {
|
|
59
|
+
return p.clone();
|
|
60
|
+
});
|
|
61
|
+
this.addPolygonsArrayToRoot(this.polygons);
|
|
62
|
+
this.replacedPolygons = source.replacedPolygons.map(function(p) {
|
|
63
|
+
return p.clone();
|
|
64
|
+
});
|
|
65
|
+
if (source.mesh) {
|
|
66
|
+
this.mesh = source.mesh;
|
|
67
|
+
}
|
|
68
|
+
if (source.originalMatrixWorld) {
|
|
69
|
+
this.originalMatrixWorld = source.originalMatrixWorld.clone();
|
|
70
|
+
}
|
|
71
|
+
this.box = source.box.clone();
|
|
72
|
+
this.level = source.level;
|
|
73
|
+
for (i = k = 0, ref = source.subTrees.length; (0 <= ref ? k < ref : k > ref); i = 0 <= ref ? ++k : --k) {
|
|
74
|
+
subTree = new this.constructor(void 0, this).copy(source.subTrees[i]);
|
|
75
|
+
this.subTrees.push(subTree);
|
|
76
|
+
}
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
addPolygonsArrayToRoot(array) {
|
|
81
|
+
if (this.parent) {
|
|
82
|
+
return this.parent.addPolygonsArrayToRoot(array);
|
|
83
|
+
} else {
|
|
84
|
+
if (this.polygonArrays === void 0) {
|
|
85
|
+
this.polygonArrays = [];
|
|
86
|
+
}
|
|
87
|
+
return this.polygonArrays.push(array);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
deletePolygonsArrayFromRoot(array) {
|
|
92
|
+
var index;
|
|
93
|
+
if (this.parent) {
|
|
94
|
+
return this.parent.deletePolygonsArrayFromRoot(array);
|
|
95
|
+
} else {
|
|
96
|
+
index = this.polygonArrays.indexOf(array);
|
|
97
|
+
if (index > -1) {
|
|
98
|
+
return this.polygonArrays.splice(index, 1);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
isEmpty() {
|
|
104
|
+
return this.polygons.length === 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
addPolygon(polygon, trianglesSet) {
|
|
108
|
+
var triangle;
|
|
109
|
+
if (!this.bounds) {
|
|
110
|
+
this.bounds = new Box3();
|
|
111
|
+
}
|
|
112
|
+
triangle = polygon.triangle;
|
|
113
|
+
if (trianglesSet && !isUniqueTriangle(triangle, trianglesSet)) {
|
|
114
|
+
return this;
|
|
115
|
+
}
|
|
116
|
+
this.bounds.min.x = Math.min(this.bounds.min.x, triangle.a.x, triangle.b.x, triangle.c.x);
|
|
117
|
+
this.bounds.min.y = Math.min(this.bounds.min.y, triangle.a.y, triangle.b.y, triangle.c.y);
|
|
118
|
+
this.bounds.min.z = Math.min(this.bounds.min.z, triangle.a.z, triangle.b.z, triangle.c.z);
|
|
119
|
+
this.bounds.max.x = Math.max(this.bounds.max.x, triangle.a.x, triangle.b.x, triangle.c.x);
|
|
120
|
+
this.bounds.max.y = Math.max(this.bounds.max.y, triangle.a.y, triangle.b.y, triangle.c.y);
|
|
121
|
+
this.bounds.max.z = Math.max(this.bounds.max.z, triangle.a.z, triangle.b.z, triangle.c.z);
|
|
122
|
+
this.polygons.push(polygon);
|
|
123
|
+
return this;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
calcBox() {
|
|
127
|
+
if (!this.bounds) {
|
|
128
|
+
this.bounds = new Box3();
|
|
129
|
+
}
|
|
130
|
+
this.box = this.bounds.clone();
|
|
131
|
+
// offset small ammount to account for regular grid
|
|
132
|
+
this.box.min.x -= 0.01;
|
|
133
|
+
this.box.min.y -= 0.01;
|
|
134
|
+
this.box.min.z -= 0.01;
|
|
135
|
+
return this;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
newPolytree(box, parent) {
|
|
139
|
+
return new this.constructor(box, parent);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
split(level) {
|
|
143
|
+
var box, found, halfsize, i, k, l, len, m, n, o, polygon, ref, ref1, subTrees, vectorPosition, x, y, z;
|
|
144
|
+
if (!this.box) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
subTrees = [];
|
|
148
|
+
halfsize = tempVector2.copy(this.box.max).sub(this.box.min).multiplyScalar(0.5);
|
|
149
|
+
for (x = k = 0; k <= 1; x = ++k) {
|
|
150
|
+
for (y = l = 0; l <= 1; y = ++l) {
|
|
151
|
+
for (z = m = 0; m <= 1; z = ++m) {
|
|
152
|
+
box = new Box3();
|
|
153
|
+
vectorPosition = tempVector1.set(x, y, z);
|
|
154
|
+
box.min.copy(this.box.min).add(vectorPosition.multiply(halfsize));
|
|
155
|
+
box.max.copy(box.min).add(halfsize);
|
|
156
|
+
box.expandByScalar(EPSILON);
|
|
157
|
+
subTrees.push(this.newPolytree(box, this));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
polygon = void 0;
|
|
162
|
+
while (polygon = this.polygons.pop()) {
|
|
163
|
+
found = false;
|
|
164
|
+
for (i = n = 0, ref = subTrees.length; (0 <= ref ? n < ref : n > ref); i = 0 <= ref ? ++n : --n) {
|
|
165
|
+
if (subTrees[i].box.containsPoint(polygon.getMidpoint())) {
|
|
166
|
+
subTrees[i].polygons.push(polygon);
|
|
167
|
+
found = true;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (!found) {
|
|
171
|
+
console.error("ERROR: unable to find subtree for:", polygon.triangle);
|
|
172
|
+
throw new Error(`Unable to find subtree for triangle at level ${level}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
for (i = o = 0, ref1 = subTrees.length; (0 <= ref1 ? o < ref1 : o > ref1); i = 0 <= ref1 ? ++o : --o) {
|
|
176
|
+
subTrees[i].level = level + 1;
|
|
177
|
+
len = subTrees[i].polygons.length;
|
|
178
|
+
// if (len !== 0) {
|
|
179
|
+
if (len > Polytree.polygonsPerTree && level < Polytree.maxLevel) {
|
|
180
|
+
subTrees[i].split(level + 1);
|
|
181
|
+
}
|
|
182
|
+
this.subTrees.push(subTrees[i]);
|
|
183
|
+
}
|
|
184
|
+
// }
|
|
185
|
+
return this;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
buildTree() {
|
|
189
|
+
this.calcBox();
|
|
190
|
+
this.split(0);
|
|
191
|
+
this.processTree();
|
|
192
|
+
return this;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
processTree() {
|
|
196
|
+
var i, k, l, ref, ref1, results1;
|
|
197
|
+
if (!this.isEmpty()) {
|
|
198
|
+
tempBox3.copy(this.box);
|
|
199
|
+
for (i = k = 0, ref = this.polygons.length; (0 <= ref ? k < ref : k > ref); i = 0 <= ref ? ++k : --k) {
|
|
200
|
+
this.box.expandByPoint(this.polygons[i].triangle.a);
|
|
201
|
+
this.box.expandByPoint(this.polygons[i].triangle.b);
|
|
202
|
+
this.box.expandByPoint(this.polygons[i].triangle.c);
|
|
203
|
+
}
|
|
204
|
+
this.expandParentBox();
|
|
205
|
+
}
|
|
206
|
+
results1 = [];
|
|
207
|
+
for (i = l = 0, ref1 = this.subTrees.length; (0 <= ref1 ? l < ref1 : l > ref1); i = 0 <= ref1 ? ++l : --l) {
|
|
208
|
+
results1.push(this.subTrees[i].processTree());
|
|
209
|
+
}
|
|
210
|
+
return results1;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
expandParentBox() {
|
|
214
|
+
if (this.parent) {
|
|
215
|
+
this.parent.box.expandByPoint(this.box.min);
|
|
216
|
+
this.parent.box.expandByPoint(this.box.max);
|
|
217
|
+
return this.parent.expandParentBox();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
getPolygonsIntersectingPolygon(targetPolygon, polygons = []) {
|
|
222
|
+
var allPolygons, i, k, l, m, polygon, ref, ref1, ref2;
|
|
223
|
+
if (this.box.intersectsTriangle(targetPolygon.triangle)) {
|
|
224
|
+
if (this.polygons.length > 0) {
|
|
225
|
+
allPolygons = this.polygons.slice();
|
|
226
|
+
if (this.replacedPolygons.length > 0) {
|
|
227
|
+
for (i = k = 0, ref = this.replacedPolygons.length; (0 <= ref ? k < ref : k > ref); i = 0 <= ref ? ++k : --k) {
|
|
228
|
+
allPolygons.push(this.replacedPolygons[i]);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
for (i = l = 0, ref1 = allPolygons.length; (0 <= ref1 ? l < ref1 : l > ref1); i = 0 <= ref1 ? ++l : --l) {
|
|
232
|
+
polygon = allPolygons[i];
|
|
233
|
+
if (!(polygon.originalValid && polygon.valid && polygon.intersects)) {
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
if (triangleIntersectsTriangle(targetPolygon.triangle, polygon.triangle)) {
|
|
237
|
+
polygons.push(polygon);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
for (i = m = 0, ref2 = this.subTrees.length; (0 <= ref2 ? m < ref2 : m > ref2); i = 0 <= ref2 ? ++m : --m) {
|
|
243
|
+
this.subTrees[i].getPolygonsIntersectingPolygon(targetPolygon, polygons);
|
|
244
|
+
}
|
|
245
|
+
return polygons;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
getRayPolygons(ray, polygons = []) {
|
|
249
|
+
var i, k, l, ref, ref1;
|
|
250
|
+
if (this.polygons.length > 0) {
|
|
251
|
+
for (i = k = 0, ref = this.polygons.length; (0 <= ref ? k < ref : k > ref); i = 0 <= ref ? ++k : --k) {
|
|
252
|
+
if (this.polygons[i].valid && this.polygons[i].originalValid) {
|
|
253
|
+
if (polygons.indexOf(this.polygons[i]) === -1) {
|
|
254
|
+
polygons.push(this.polygons[i]);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (this.replacedPolygons.length > 0) {
|
|
260
|
+
polygons.push(...this.replacedPolygons);
|
|
261
|
+
}
|
|
262
|
+
for (i = l = 0, ref1 = this.subTrees.length; (0 <= ref1 ? l < ref1 : l > ref1); i = 0 <= ref1 ? ++l : --l) {
|
|
263
|
+
if (ray.intersectsBox(this.subTrees[i].box)) {
|
|
264
|
+
this.subTrees[i].getRayPolygons(ray, polygons);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return polygons;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
rayIntersect(ray, matrixWorld, intersects = []) {
|
|
271
|
+
var distance, i, k, newdistance, polygons, ref, result;
|
|
272
|
+
if (ray.direction.length() === 0) {
|
|
273
|
+
return [];
|
|
274
|
+
}
|
|
275
|
+
distance = 1e100;
|
|
276
|
+
polygons = this.getRayPolygons(ray);
|
|
277
|
+
for (i = k = 0, ref = polygons.length; (0 <= ref ? k < ref : k > ref); i = 0 <= ref ? ++k : --k) {
|
|
278
|
+
result = void 0;
|
|
279
|
+
if (Polytree.rayIntersectTriangleType === "regular") {
|
|
280
|
+
result = ray.intersectTriangle(polygons[i].triangle.a, polygons[i].triangle.b, polygons[i].triangle.c, false, tempVector1);
|
|
281
|
+
if (result) {
|
|
282
|
+
tempVector1.applyMatrix4(matrixWorld);
|
|
283
|
+
distance = tempVector1.distanceTo(ray.origin);
|
|
284
|
+
if (distance < 0 || distance > 2e308) {
|
|
285
|
+
console.warn("[rayIntersect] Failed ray distance check", ray);
|
|
286
|
+
} else {
|
|
287
|
+
intersects.push({
|
|
288
|
+
distance: distance,
|
|
289
|
+
polygon: polygons[i],
|
|
290
|
+
position: tempVector1.clone()
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
} else {
|
|
295
|
+
result = rayIntersectsTriangle(ray, polygons[i].triangle, tempVector1);
|
|
296
|
+
if (result) {
|
|
297
|
+
newdistance = result.clone().sub(ray.origin).length();
|
|
298
|
+
if (distance > newdistance) {
|
|
299
|
+
distance = newdistance;
|
|
300
|
+
}
|
|
301
|
+
if (distance < 1e100) {
|
|
302
|
+
intersects.push({
|
|
303
|
+
distance: distance,
|
|
304
|
+
polygon: polygons[i],
|
|
305
|
+
position: result.clone().add(ray.origin)
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
intersects.length && intersects.sort(raycastIntersectAscSort);
|
|
312
|
+
return intersects;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
getIntersectingPolygons(polygons = []) {
|
|
316
|
+
this.polygonArrays.forEach(function(polygonsArray) {
|
|
317
|
+
var i, k, ref, results1;
|
|
318
|
+
if (polygonsArray.length) {
|
|
319
|
+
results1 = [];
|
|
320
|
+
for (i = k = 0, ref = polygonsArray.length; (0 <= ref ? k < ref : k > ref); i = 0 <= ref ? ++k : --k) {
|
|
321
|
+
if (polygonsArray[i].valid && polygonsArray[i].intersects) {
|
|
322
|
+
results1.push(polygons.push(polygonsArray[i]));
|
|
323
|
+
} else {
|
|
324
|
+
results1.push(void 0);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return results1;
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
// if (@polygons.length)
|
|
331
|
+
// for i in [0...@polygons.length]
|
|
332
|
+
// if @polygons[i].valid and @polygons[i].intersects
|
|
333
|
+
// polygons.push(@polygons[i])
|
|
334
|
+
// for i in [0...@subTrees.length]
|
|
335
|
+
// @subTrees[i].getIntersectingPolygons(polygons)
|
|
336
|
+
return polygons;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
getPolygons(polygons = []) {
|
|
340
|
+
this.polygonArrays.forEach(function(polygonsArray) {
|
|
341
|
+
var i, k, ref, results1;
|
|
342
|
+
if (polygonsArray.length) {
|
|
343
|
+
results1 = [];
|
|
344
|
+
for (i = k = 0, ref = polygonsArray.length; (0 <= ref ? k < ref : k > ref); i = 0 <= ref ? ++k : --k) {
|
|
345
|
+
if (polygonsArray[i].valid) {
|
|
346
|
+
if (polygons.indexOf(polygonsArray[i]) === -1) {
|
|
347
|
+
results1.push(polygons.push(polygonsArray[i]));
|
|
348
|
+
} else {
|
|
349
|
+
results1.push(void 0);
|
|
350
|
+
}
|
|
351
|
+
} else {
|
|
352
|
+
results1.push(void 0);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return results1;
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
// if @polygons.length > 0
|
|
359
|
+
// for i in [0...@polygons.length]
|
|
360
|
+
// if @polygons[i].valid
|
|
361
|
+
// if polygons.indexOf(@polygons[i]) is -1
|
|
362
|
+
// polygons.push(@polygons[i])
|
|
363
|
+
// for i in [0...@subTrees.length]
|
|
364
|
+
// @subTrees[i].getPolygons(polygons)
|
|
365
|
+
return polygons;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
invert() {
|
|
369
|
+
return this.polygonArrays.forEach(function(polygonsArray) {
|
|
370
|
+
if (polygonsArray.length) {
|
|
371
|
+
return polygonsArray.forEach(function(p) {
|
|
372
|
+
return p.flip();
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// if @polygons.length > 0
|
|
379
|
+
// @polygons.forEach (p) -> p.flip()
|
|
380
|
+
// for i in [0...@subTrees.length]
|
|
381
|
+
// @subTrees[i].invert()
|
|
382
|
+
getMesh() {
|
|
383
|
+
if (this.parent) {
|
|
384
|
+
return this.parent.getMesh();
|
|
385
|
+
} else {
|
|
386
|
+
return this.mesh;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
replacePolygon(polygon, newPolygons) {
|
|
391
|
+
var i, k, polygonIndex, ref, results1;
|
|
392
|
+
if (!Array.isArray(newPolygons)) {
|
|
393
|
+
newPolygons = [newPolygons];
|
|
394
|
+
}
|
|
395
|
+
// if @polygons.length > 0
|
|
396
|
+
// polygonIndex = @polygons.indexOf(polygon)
|
|
397
|
+
// if polygonIndex > -1
|
|
398
|
+
// if polygon.originalValid is true
|
|
399
|
+
// @replacedPolygons.push(polygon)
|
|
400
|
+
// else
|
|
401
|
+
// polygon.setInvalid()
|
|
402
|
+
// @polygons.splice(polygonIndex, 1, ...newPolygons)
|
|
403
|
+
// for i in [0...@subTrees.length]
|
|
404
|
+
// @subTrees[i].replacePolygon(polygon, newPolygons)
|
|
405
|
+
if (this.polygons.length > 0) {
|
|
406
|
+
polygonIndex = this.polygons.indexOf(polygon);
|
|
407
|
+
if (polygonIndex > -1) {
|
|
408
|
+
if (polygon.originalValid === true) {
|
|
409
|
+
this.replacedPolygons.push(polygon);
|
|
410
|
+
} else {
|
|
411
|
+
polygon.setInvalid();
|
|
412
|
+
}
|
|
413
|
+
this.polygons.splice(polygonIndex, 1, ...newPolygons);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
results1 = [];
|
|
417
|
+
for (i = k = 0, ref = this.subTrees.length; (0 <= ref ? k < ref : k > ref); i = 0 <= ref ? ++k : --k) {
|
|
418
|
+
results1.push(this.subTrees[i].replacePolygon(polygon, newPolygons));
|
|
419
|
+
}
|
|
420
|
+
return results1;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
deletePolygonsByStateRules(rulesArr, firstRun = true) {
|
|
424
|
+
return this.polygonArrays.forEach(function(polygonsArray) {
|
|
425
|
+
var polygonArr;
|
|
426
|
+
if (polygonsArray.length) {
|
|
427
|
+
polygonArr = polygonsArray.filter(function(polygon) {
|
|
428
|
+
return (polygon.valid === true) && (polygon.intersects === true);
|
|
429
|
+
});
|
|
430
|
+
return polygonArr.forEach(function(polygon) {
|
|
431
|
+
var found, i, j, k, l, mainStatesObj, polygonIndex, ref, ref1, state, states, statesObj;
|
|
432
|
+
found = false;
|
|
433
|
+
for (j = k = 0, ref = rulesArr.length; (0 <= ref ? k < ref : k > ref); j = 0 <= ref ? ++k : --k) {
|
|
434
|
+
if (rulesArr[j].array) {
|
|
435
|
+
states = rulesArr[j].rule;
|
|
436
|
+
if ((states.includes(polygon.state)) && (((polygon.previousState !== "undecided") && (states.includes(polygon.previousState))) || (polygon.previousState === "undecided"))) {
|
|
437
|
+
found = true;
|
|
438
|
+
statesObj = {};
|
|
439
|
+
mainStatesObj = {};
|
|
440
|
+
states.forEach(function(state) {
|
|
441
|
+
return statesObj[state] = false;
|
|
442
|
+
});
|
|
443
|
+
states.forEach(function(state) {
|
|
444
|
+
return mainStatesObj[state] = false;
|
|
445
|
+
});
|
|
446
|
+
statesObj[polygon.state] = true;
|
|
447
|
+
for (i = l = 0, ref1 = polygon.previousStates.length; (0 <= ref1 ? l < ref1 : l > ref1); i = 0 <= ref1 ? ++l : --l) {
|
|
448
|
+
if (!states.includes(polygon.previousStates[i])) {
|
|
449
|
+
found = false;
|
|
450
|
+
break;
|
|
451
|
+
} else {
|
|
452
|
+
statesObj[polygon.previousStates[i]] = true;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
if (found) {
|
|
456
|
+
for (state in statesObj) {
|
|
457
|
+
if (statesObj[state] === false) {
|
|
458
|
+
found = false;
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
if (found) {
|
|
463
|
+
break;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
} else {
|
|
468
|
+
if (polygon.checkAllStates(rulesArr[j].rule)) {
|
|
469
|
+
found = true;
|
|
470
|
+
break;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
if (found) {
|
|
475
|
+
polygonIndex = polygonsArray.indexOf(polygon);
|
|
476
|
+
if (polygonIndex > -1) {
|
|
477
|
+
polygon.setInvalid();
|
|
478
|
+
polygonsArray.splice(polygonIndex, 1);
|
|
479
|
+
}
|
|
480
|
+
if (firstRun) {
|
|
481
|
+
return polygon.delete();
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// if @polygons.length > 0
|
|
490
|
+
// polygonArr = @polygons.filter (polygon) -> (polygon.valid == true) and (polygon.intersects == true)
|
|
491
|
+
// polygonArr.forEach (polygon) ->
|
|
492
|
+
// found = false
|
|
493
|
+
// for j in [0...rulesArr.length]
|
|
494
|
+
// if rulesArr[j].array
|
|
495
|
+
// states = rulesArr[j].rule
|
|
496
|
+
// if (states.includes(polygon.state)) and (((polygon.previousState isnt "undecided") and (states.includes(polygon.previousState))) or (polygon.previousState is "undecided"))
|
|
497
|
+
// found = true
|
|
498
|
+
// statesObj = {}
|
|
499
|
+
// mainStatesObj = {}
|
|
500
|
+
// states.forEach (state) -> statesObj[state] = false
|
|
501
|
+
// states.forEach (state) -> mainStatesObj[state] = false
|
|
502
|
+
// statesObj[polygon.state] = true
|
|
503
|
+
// for i in [0...polygon.previousStates.length]
|
|
504
|
+
// unless states.includes(polygon.previousStates[i])
|
|
505
|
+
// found = false
|
|
506
|
+
// break
|
|
507
|
+
// else
|
|
508
|
+
// statesObj[polygon.previousStates[i]] = true
|
|
509
|
+
// if found
|
|
510
|
+
// for state of statesObj
|
|
511
|
+
// if statesObj[state] is false
|
|
512
|
+
// found = false
|
|
513
|
+
// break
|
|
514
|
+
// if found
|
|
515
|
+
// break
|
|
516
|
+
// else
|
|
517
|
+
// if polygon.checkAllStates(rulesArr[j].rule)
|
|
518
|
+
// found = true
|
|
519
|
+
// break
|
|
520
|
+
// if found
|
|
521
|
+
// polygonIndex = @polygons.indexOf(polygon)
|
|
522
|
+
// if polygonIndex > -1
|
|
523
|
+
// polygon.setInvalid()
|
|
524
|
+
// @polygons.splice(polygonIndex, 1)
|
|
525
|
+
// if firstRun
|
|
526
|
+
// polygon.delete()
|
|
527
|
+
// for i in [0...@subTrees.length]
|
|
528
|
+
// @subTrees[i].deletePolygonsByStateRules(rulesArr, false)
|
|
529
|
+
deletePolygonsByIntersection(intersects, firstRun = true) {
|
|
530
|
+
if (intersects === void 0) {
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
return this.polygonArrays.forEach(function(polygonsArray) {
|
|
534
|
+
var polygonArr;
|
|
535
|
+
if (polygonsArray.length) {
|
|
536
|
+
polygonArr = polygonsArray.slice();
|
|
537
|
+
return polygonArr.forEach(function(polygon) {
|
|
538
|
+
var polygonIndex;
|
|
539
|
+
if (polygon.valid) {
|
|
540
|
+
if (polygon.intersects === intersects) {
|
|
541
|
+
polygonIndex = polygonsArray.indexOf(polygon);
|
|
542
|
+
if (polygonIndex > -1) {
|
|
543
|
+
polygon.setInvalid();
|
|
544
|
+
polygonsArray.splice(polygonIndex, 1);
|
|
545
|
+
}
|
|
546
|
+
if (firstRun) {
|
|
547
|
+
return polygon.delete();
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// if @polygons.length > 0
|
|
557
|
+
// polygonArr = @polygons.slice()
|
|
558
|
+
// polygonArr.forEach (polygon) ->
|
|
559
|
+
// if polygon.valid
|
|
560
|
+
// if polygon.intersects == intersects
|
|
561
|
+
// polygonIndex = @polygons.indexOf(polygon)
|
|
562
|
+
// if polygonIndex > -1
|
|
563
|
+
// polygon.setInvalid()
|
|
564
|
+
// @polygons.splice(polygonIndex, 1)
|
|
565
|
+
// if firstRun
|
|
566
|
+
// polygon.delete()
|
|
567
|
+
// for i in [0...@subTrees.length]
|
|
568
|
+
// @subTrees[i].deletePolygonsByIntersection(intersects, false)
|
|
569
|
+
isPolygonIntersecting(polygon) {
|
|
570
|
+
if (!this.box.intersectsTriangle(polygon.triangle)) {
|
|
571
|
+
return false;
|
|
572
|
+
}
|
|
573
|
+
return true;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
markIntesectingPolygons(targetPolytree) {
|
|
577
|
+
return this.polygonArrays.forEach(function(polygonsArray) {
|
|
578
|
+
if (polygonsArray.length) {
|
|
579
|
+
return polygonsArray.forEach(function(polygon) {
|
|
580
|
+
return polygon.intersects = targetPolytree.isPolygonIntersecting(polygon);
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// if @polygons.length > 0
|
|
587
|
+
// @polygons.forEach (polygon) ->
|
|
588
|
+
// polygon.intersects = targetPolytree.isPolygonIntersecting(polygon)
|
|
589
|
+
// for i in [0...@subTrees.length]
|
|
590
|
+
// @subTrees[i].markIntesectingPolygons(targetPolytree)
|
|
591
|
+
resetPolygons(resetOriginal = true) {
|
|
592
|
+
return this.polygonArrays.forEach(function(polygonsArray) {
|
|
593
|
+
if (polygonsArray.length) {
|
|
594
|
+
return polygonsArray.forEach(function(polygon) {
|
|
595
|
+
return polygon.reset(resetOriginal);
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// if @polygons.length > 0
|
|
602
|
+
// @polygons.forEach (polygon) ->
|
|
603
|
+
// polygon.reset(resetOriginal)
|
|
604
|
+
// for i in [0...@subTrees.length]
|
|
605
|
+
// @subTrees[i].resetPolygons(resetOriginal)
|
|
606
|
+
handleIntersectingPolygons(targetPolytree, targetPolytreeBuffer) {
|
|
607
|
+
var currentPolygon, i, inside, intersects, j, k, l, m, n, o, point, polygon, polygonStack, ref, ref1, ref2, ref3, ref4, results1, splitResults, target, targetPolygons;
|
|
608
|
+
// if @polygons.length > 0
|
|
609
|
+
// polygonStack = @polygons.filter (polygon) -> (polygon.valid == true) and (polygon.intersects == true) and (polygon.state == "undecided")
|
|
610
|
+
// currentPolygon = polygonStack.pop()
|
|
611
|
+
// while currentPolygon
|
|
612
|
+
// if currentPolygon.state isnt "undecided"
|
|
613
|
+
// continue
|
|
614
|
+
// unless currentPolygon.valid
|
|
615
|
+
// continue
|
|
616
|
+
// targetPolygons = targetPolytree.getPolygonsIntersectingPolygon(currentPolygon)
|
|
617
|
+
// if targetPolygons.length > 0
|
|
618
|
+
// for j in [0...targetPolygons.length]
|
|
619
|
+
// target = targetPolygons[j]
|
|
620
|
+
// splitResults = splitPolygonByPlane(currentPolygon, target.plane)
|
|
621
|
+
// if splitResults.length > 1
|
|
622
|
+
// for i in [0...splitResults.length]
|
|
623
|
+
// polygon = splitResults[i].polygon
|
|
624
|
+
// polygon.intersects = currentPolygon.intersects
|
|
625
|
+
// polygon.newPolygon = true
|
|
626
|
+
// polygonStack.push(polygon)
|
|
627
|
+
// @replacePolygon(currentPolygon, splitResults.map((result) -> result.polygon))
|
|
628
|
+
// break
|
|
629
|
+
// else
|
|
630
|
+
// if currentPolygon.id isnt splitResults[0].polygon.id
|
|
631
|
+
// splitResults[0].polygon.intersects = currentPolygon.intersects
|
|
632
|
+
// splitResults[0].polygon.newPolygon = true
|
|
633
|
+
// polygonStack.push(splitResults[0].polygon)
|
|
634
|
+
// @replacePolygon(currentPolygon, splitResults[0].polygon)
|
|
635
|
+
// break
|
|
636
|
+
// else
|
|
637
|
+
// if (splitResults[0].type == "coplanar-front") or (splitResults[0].type == "coplanar-back")
|
|
638
|
+
// currentPolygon.setState(splitResults[0].type)
|
|
639
|
+
// currentPolygon.coplanar = true
|
|
640
|
+
// currentPolygon = polygonStack.pop()
|
|
641
|
+
// polygonStack = @polygons.filter (polygon) -> (polygon.valid == true) and (polygon.intersects == true)
|
|
642
|
+
// currentPolygon = polygonStack.pop()
|
|
643
|
+
// inside = false
|
|
644
|
+
// while currentPolygon
|
|
645
|
+
// unless currentPolygon.valid
|
|
646
|
+
// continue
|
|
647
|
+
// inside = false
|
|
648
|
+
// if targetPolytree.box.containsPoint(currentPolygon.getMidpoint())
|
|
649
|
+
// if Polytree.useWindingNumber is true
|
|
650
|
+
// inside = polyInside_WindingNumber_buffer(targetPolytreeBuffer, currentPolygon.getMidpoint(), currentPolygon.coplanar)
|
|
651
|
+
// else
|
|
652
|
+
// point = pointRounding(tempVector2.copy(currentPolygon.getMidpoint()))
|
|
653
|
+
// if Polytree.usePolytreeRay isnt true and targetPolytree.mesh
|
|
654
|
+
// tempRayDirection.copy(currentPolygon.plane.normal)
|
|
655
|
+
// tempRaycaster.set(point, tempRayDirection)
|
|
656
|
+
// intersects = tempRaycaster.intersectObject(targetPolytree.mesh)
|
|
657
|
+
// if intersects.length
|
|
658
|
+
// if tempRayDirection.dot(intersects[0].face.normal) > 0
|
|
659
|
+
// inside = true
|
|
660
|
+
// unless inside or not currentPolygon.coplanar
|
|
661
|
+
// for j in [0..._wP_EPS_ARR_COUNT]
|
|
662
|
+
// tempRaycaster.ray.origin.copy(point).add(_wP_EPS_ARR[j])
|
|
663
|
+
// intersects = tempRaycaster.intersectObject(targetPolytree.mesh)
|
|
664
|
+
// if intersects.length
|
|
665
|
+
// if tempRayDirection.dot(intersects[0].face.normal) > 0
|
|
666
|
+
// inside = true
|
|
667
|
+
// break
|
|
668
|
+
// else
|
|
669
|
+
// tempRay.origin.copy(point)
|
|
670
|
+
// tempRayDirection.copy(currentPolygon.plane.normal)
|
|
671
|
+
// tempRay.direction.copy(currentPolygon.plane.normal)
|
|
672
|
+
// intersects = targetPolytree.rayIntersect(tempRay, targetPolytree.originalMatrixWorld)
|
|
673
|
+
// if intersects.length
|
|
674
|
+
// if tempRayDirection.dot(intersects[0].polygon.plane.normal) > 0
|
|
675
|
+
// inside = true
|
|
676
|
+
// unless inside or not currentPolygon.coplanar
|
|
677
|
+
// for j in [0..._wP_EPS_ARR_COUNT]
|
|
678
|
+
// tempRay.origin.copy(point).add(_wP_EPS_ARR[j])
|
|
679
|
+
// tempRayDirection.copy(currentPolygon.plane.normal)
|
|
680
|
+
// tempRay.direction.copy(currentPolygon.plane.normal)
|
|
681
|
+
// intersects = targetPolytree.rayIntersect(tempRay, targetPolytree.originalMatrixWorld)
|
|
682
|
+
// if intersects.length
|
|
683
|
+
// if tempRayDirection.dot(intersects[0].polygon.plane.normal) > 0
|
|
684
|
+
// inside = true
|
|
685
|
+
// break
|
|
686
|
+
// if inside is true
|
|
687
|
+
// currentPolygon.setState("inside")
|
|
688
|
+
// else
|
|
689
|
+
// currentPolygon.setState("outside")
|
|
690
|
+
// currentPolygon = polygonStack.pop()
|
|
691
|
+
// for i in [0...@subTrees.length]
|
|
692
|
+
// @subTrees[i].handleIntersectingPolygons(targetPolytree, targetPolytreeBuffer)
|
|
693
|
+
if (this.polygons.length > 0) {
|
|
694
|
+
polygonStack = this.polygons.filter(function(polygon) {
|
|
695
|
+
return (polygon.valid === true) && (polygon.intersects === true) && (polygon.state === "undecided");
|
|
696
|
+
});
|
|
697
|
+
currentPolygon = polygonStack.pop();
|
|
698
|
+
while (currentPolygon) {
|
|
699
|
+
if (currentPolygon.state !== "undecided") {
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
if (!currentPolygon.valid) {
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
705
|
+
targetPolygons = targetPolytree.getPolygonsIntersectingPolygon(currentPolygon);
|
|
706
|
+
if (targetPolygons.length > 0) {
|
|
707
|
+
for (j = k = 0, ref = targetPolygons.length; (0 <= ref ? k < ref : k > ref); j = 0 <= ref ? ++k : --k) {
|
|
708
|
+
target = targetPolygons[j];
|
|
709
|
+
splitResults = splitPolygonByPlane(currentPolygon, target.plane);
|
|
710
|
+
if (splitResults.length > 1) {
|
|
711
|
+
for (i = l = 0, ref1 = splitResults.length; (0 <= ref1 ? l < ref1 : l > ref1); i = 0 <= ref1 ? ++l : --l) {
|
|
712
|
+
polygon = splitResults[i].polygon;
|
|
713
|
+
polygon.intersects = currentPolygon.intersects;
|
|
714
|
+
polygon.newPolygon = true;
|
|
715
|
+
polygonStack.push(polygon);
|
|
716
|
+
}
|
|
717
|
+
this.replacePolygon(currentPolygon, splitResults.map(function(result) {
|
|
718
|
+
return result.polygon;
|
|
719
|
+
}));
|
|
720
|
+
break;
|
|
721
|
+
} else {
|
|
722
|
+
if (currentPolygon.id !== splitResults[0].polygon.id) {
|
|
723
|
+
splitResults[0].polygon.intersects = currentPolygon.intersects;
|
|
724
|
+
splitResults[0].polygon.newPolygon = true;
|
|
725
|
+
polygonStack.push(splitResults[0].polygon);
|
|
726
|
+
this.replacePolygon(currentPolygon, splitResults[0].polygon);
|
|
727
|
+
break;
|
|
728
|
+
} else {
|
|
729
|
+
if ((splitResults[0].type === "coplanar-front") || (splitResults[0].type === "coplanar-back")) {
|
|
730
|
+
currentPolygon.setState(splitResults[0].type);
|
|
731
|
+
currentPolygon.coplanar = true;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
currentPolygon = polygonStack.pop();
|
|
738
|
+
}
|
|
739
|
+
polygonStack = this.polygons.filter(function(polygon) {
|
|
740
|
+
return (polygon.valid === true) && (polygon.intersects === true);
|
|
741
|
+
});
|
|
742
|
+
currentPolygon = polygonStack.pop();
|
|
743
|
+
inside = false;
|
|
744
|
+
while (currentPolygon) {
|
|
745
|
+
if (!currentPolygon.valid) {
|
|
746
|
+
continue;
|
|
747
|
+
}
|
|
748
|
+
inside = false;
|
|
749
|
+
if (targetPolytree.box.containsPoint(currentPolygon.getMidpoint())) {
|
|
750
|
+
if (Polytree.useWindingNumber === true) {
|
|
751
|
+
inside = polyInside_WindingNumber_buffer(targetPolytreeBuffer, currentPolygon.getMidpoint(), currentPolygon.coplanar);
|
|
752
|
+
} else {
|
|
753
|
+
point = pointRounding(tempVector2.copy(currentPolygon.getMidpoint()));
|
|
754
|
+
if (Polytree.usePolytreeRay !== true && targetPolytree.mesh) {
|
|
755
|
+
tempRayDirection.copy(currentPolygon.plane.normal);
|
|
756
|
+
tempRaycaster.set(point, tempRayDirection);
|
|
757
|
+
intersects = tempRaycaster.intersectObject(targetPolytree.mesh);
|
|
758
|
+
if (intersects.length) {
|
|
759
|
+
if (tempRayDirection.dot(intersects[0].face.normal) > 0) {
|
|
760
|
+
inside = true;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
if (!(inside || !currentPolygon.coplanar)) {
|
|
764
|
+
for (j = m = 0, ref2 = _wP_EPS_ARR_COUNT; (0 <= ref2 ? m < ref2 : m > ref2); j = 0 <= ref2 ? ++m : --m) {
|
|
765
|
+
tempRaycaster.ray.origin.copy(point).add(_wP_EPS_ARR[j]);
|
|
766
|
+
intersects = tempRaycaster.intersectObject(targetPolytree.mesh);
|
|
767
|
+
if (intersects.length) {
|
|
768
|
+
if (tempRayDirection.dot(intersects[0].face.normal) > 0) {
|
|
769
|
+
inside = true;
|
|
770
|
+
break;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
} else {
|
|
776
|
+
tempRay.origin.copy(point);
|
|
777
|
+
tempRayDirection.copy(currentPolygon.plane.normal);
|
|
778
|
+
tempRay.direction.copy(currentPolygon.plane.normal);
|
|
779
|
+
intersects = targetPolytree.rayIntersect(tempRay, targetPolytree.originalMatrixWorld);
|
|
780
|
+
if (intersects.length) {
|
|
781
|
+
if (tempRayDirection.dot(intersects[0].polygon.plane.normal) > 0) {
|
|
782
|
+
inside = true;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
if (!(inside || !currentPolygon.coplanar)) {
|
|
786
|
+
for (j = n = 0, ref3 = _wP_EPS_ARR_COUNT; (0 <= ref3 ? n < ref3 : n > ref3); j = 0 <= ref3 ? ++n : --n) {
|
|
787
|
+
tempRay.origin.copy(point).add(_wP_EPS_ARR[j]);
|
|
788
|
+
tempRayDirection.copy(currentPolygon.plane.normal);
|
|
789
|
+
tempRay.direction.copy(currentPolygon.plane.normal);
|
|
790
|
+
intersects = targetPolytree.rayIntersect(tempRay, targetPolytree.originalMatrixWorld);
|
|
791
|
+
if (intersects.length) {
|
|
792
|
+
if (tempRayDirection.dot(intersects[0].polygon.plane.normal) > 0) {
|
|
793
|
+
inside = true;
|
|
794
|
+
break;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
if (inside === true) {
|
|
803
|
+
currentPolygon.setState("inside");
|
|
804
|
+
} else {
|
|
805
|
+
currentPolygon.setState("outside");
|
|
806
|
+
}
|
|
807
|
+
currentPolygon = polygonStack.pop();
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
results1 = [];
|
|
811
|
+
for (i = o = 0, ref4 = this.subTrees.length; (0 <= ref4 ? o < ref4 : o > ref4); i = 0 <= ref4 ? ++o : --o) {
|
|
812
|
+
results1.push(this.subTrees[i].handleIntersectingPolygons(targetPolytree, targetPolytreeBuffer));
|
|
813
|
+
}
|
|
814
|
+
return results1;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
delete(deletePolygons = true) {
|
|
818
|
+
var i, k, ref;
|
|
819
|
+
if (this.polygons.length > 0 && deletePolygons) {
|
|
820
|
+
this.polygons.forEach(function(p) {
|
|
821
|
+
return p.delete();
|
|
822
|
+
});
|
|
823
|
+
this.polygons.length = 0;
|
|
824
|
+
}
|
|
825
|
+
if (this.replacedPolygons.length > 0 && deletePolygons) {
|
|
826
|
+
this.replacedPolygons.forEach(function(p) {
|
|
827
|
+
return p.delete();
|
|
828
|
+
});
|
|
829
|
+
this.replacedPolygons.length = 0;
|
|
830
|
+
}
|
|
831
|
+
if (this.polygonArrays) {
|
|
832
|
+
this.polygonArrays.length = 0;
|
|
833
|
+
}
|
|
834
|
+
if (this.subTrees.length) {
|
|
835
|
+
for (i = k = 0, ref = this.subTrees.length; (0 <= ref ? k < ref : k > ref); i = 0 <= ref ? ++k : --k) {
|
|
836
|
+
this.subTrees[i].delete(deletePolygons);
|
|
837
|
+
}
|
|
838
|
+
this.subTrees.length = 0;
|
|
839
|
+
}
|
|
840
|
+
this.mesh = void 0;
|
|
841
|
+
this.originalMatrixWorld = void 0;
|
|
842
|
+
this.box = void 0;
|
|
843
|
+
this.parent = void 0;
|
|
844
|
+
return this.level = void 0;
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
dispose(deletePolygons = true) {
|
|
848
|
+
return this.delete(deletePolygons);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
getPolygonCloneCallback(cbFunc, trianglesSet) {
|
|
852
|
+
return this.polygonArrays.forEach(function(polygonsArray) {
|
|
853
|
+
var i, k, ref, results1;
|
|
854
|
+
if (polygonsArray.length) {
|
|
855
|
+
results1 = [];
|
|
856
|
+
for (i = k = 0, ref = polygonsArray.length; (0 <= ref ? k < ref : k > ref); i = 0 <= ref ? ++k : --k) {
|
|
857
|
+
if (polygonsArray[i].valid) {
|
|
858
|
+
results1.push(cbFunc(polygonsArray[i].clone(), trianglesSet));
|
|
859
|
+
} else {
|
|
860
|
+
results1.push(void 0);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
return results1;
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
// if @polygons.length > 0
|
|
869
|
+
// for i in [0...@polygons.length]
|
|
870
|
+
// if @polygons[i].valid
|
|
871
|
+
// cbFunc(@polygons[i].clone(), trianglesSet)
|
|
872
|
+
// for i in [0...@subTrees.length]
|
|
873
|
+
// @subTrees[i].getPolygonCloneCallback(cbFunc, trianglesSet)
|
|
874
|
+
deleteReplacedPolygons() {
|
|
875
|
+
var i, k, ref, results1;
|
|
876
|
+
if (this.replacedPolygons.length > 0) {
|
|
877
|
+
this.replacedPolygons.forEach(function(p) {
|
|
878
|
+
return p.delete();
|
|
879
|
+
});
|
|
880
|
+
this.replacedPolygons.length = 0;
|
|
881
|
+
}
|
|
882
|
+
results1 = [];
|
|
883
|
+
for (i = k = 0, ref = this.subTrees.length; (0 <= ref ? k < ref : k > ref); i = 0 <= ref ? ++k : --k) {
|
|
884
|
+
results1.push(this.subTrees[i].deleteReplacedPolygons());
|
|
885
|
+
}
|
|
886
|
+
return results1;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
markPolygonsAsOriginal() {
|
|
890
|
+
return this.polygonArrays.forEach(function(polygonsArray) {
|
|
891
|
+
if (polygonsArray.length) {
|
|
892
|
+
return polygonsArray.forEach(function(p) {
|
|
893
|
+
return p.originalValid = true;
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// if @polygons.length > 0
|
|
900
|
+
// @polygons.forEach (p) -> p.originalValid = true
|
|
901
|
+
// for i in [0...@subTrees.length]
|
|
902
|
+
// @subTrees[i].markPolygonsAsOriginal()
|
|
903
|
+
applyMatrix(matrix, normalMatrix, firstRun = true) {
|
|
904
|
+
var i, k, l, ref, ref1;
|
|
905
|
+
if (matrix.isMesh) {
|
|
906
|
+
matrix.updateMatrix();
|
|
907
|
+
matrix = matrix.matrix;
|
|
908
|
+
}
|
|
909
|
+
this.box.makeEmpty();
|
|
910
|
+
normalMatrix = normalMatrix || tmpm3.getNormalMatrix(matrix);
|
|
911
|
+
if (this.polygons.length > 0) {
|
|
912
|
+
for (i = k = 0, ref = this.polygons.length; (0 <= ref ? k < ref : k > ref); i = 0 <= ref ? ++k : --k) {
|
|
913
|
+
if (this.polygons[i].valid) {
|
|
914
|
+
this.polygons[i].applyMatrix(matrix, normalMatrix);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
for (i = l = 0, ref1 = this.subTrees.length; (0 <= ref1 ? l < ref1 : l > ref1); i = 0 <= ref1 ? ++l : --l) {
|
|
919
|
+
this.subTrees[i].applyMatrix(matrix, normalMatrix, false);
|
|
920
|
+
}
|
|
921
|
+
if (firstRun) {
|
|
922
|
+
return this.processTree();
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
setPolygonIndex(index) {
|
|
927
|
+
if (index === void 0) {
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
return this.polygonArrays.forEach(function(polygonsArray) {
|
|
931
|
+
if (polygonsArray.length) {
|
|
932
|
+
return polygonsArray.forEach(function(p) {
|
|
933
|
+
return p.shared = index;
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
});
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
};
|
|
940
|
+
|
|
941
|
+
// if @polygons.length > 0
|
|
942
|
+
// @polygons.forEach (p) -> p.shared = index
|
|
943
|
+
// for i in [0...@subTrees.length]
|
|
944
|
+
// @subTrees[i].setPolygonIndex(index)
|
|
945
|
+
Polytree.prototype.isPolytree = true;
|
|
946
|
+
|
|
947
|
+
raycastIntersectAscSort = function(a, b) {
|
|
948
|
+
return a.distance - b.distance;
|
|
949
|
+
};
|
|
950
|
+
|
|
951
|
+
pointRounding = function(point, num = 15) {
|
|
952
|
+
point.x = +point.x.toFixed(num);
|
|
953
|
+
point.y = +point.y.toFixed(num);
|
|
954
|
+
point.z = +point.z.toFixed(num);
|
|
955
|
+
return point;
|
|
956
|
+
};
|
|
957
|
+
|
|
958
|
+
splitPolygonByPlane = function(polygon, plane, result = []) {
|
|
959
|
+
var backVertices, currentType, currentVertex, distanceToPlane, frontVertices, i, intersectionParameter, k, l, m, n, newPolygons, nextIndex, nextType, nextVertex, polygonIndex, polygonType, ref, ref1, ref2, ref3, returnPolygon, type, types, vertexParameter;
|
|
960
|
+
returnPolygon = {
|
|
961
|
+
polygon: polygon,
|
|
962
|
+
type: "undecided"
|
|
963
|
+
};
|
|
964
|
+
polygonType = 0;
|
|
965
|
+
types = [];
|
|
966
|
+
for (i = k = 0, ref = polygon.vertices.length; (0 <= ref ? k < ref : k > ref); i = 0 <= ref ? ++k : --k) {
|
|
967
|
+
distanceToPlane = plane.normal.dot(polygon.vertices[i].pos) - plane.w;
|
|
968
|
+
type = distanceToPlane < -EPSILON ? BACK : distanceToPlane > EPSILON ? FRONT : COPLANAR;
|
|
969
|
+
polygonType |= type;
|
|
970
|
+
types.push(type);
|
|
971
|
+
}
|
|
972
|
+
switch (polygonType) {
|
|
973
|
+
case COPLANAR:
|
|
974
|
+
returnPolygon.type = plane.normal.dot(polygon.plane.normal) > 0 ? "coplanar-front" : "coplanar-back";
|
|
975
|
+
result.push(returnPolygon);
|
|
976
|
+
break;
|
|
977
|
+
case FRONT:
|
|
978
|
+
returnPolygon.type = "front";
|
|
979
|
+
result.push(returnPolygon);
|
|
980
|
+
break;
|
|
981
|
+
case BACK:
|
|
982
|
+
returnPolygon.type = "back";
|
|
983
|
+
result.push(returnPolygon);
|
|
984
|
+
break;
|
|
985
|
+
case SPANNING:
|
|
986
|
+
frontVertices = [];
|
|
987
|
+
backVertices = [];
|
|
988
|
+
for (i = l = 0, ref1 = polygon.vertices.length; (0 <= ref1 ? l < ref1 : l > ref1); i = 0 <= ref1 ? ++l : --l) {
|
|
989
|
+
nextIndex = (i + 1) % polygon.vertices.length;
|
|
990
|
+
currentType = types[i];
|
|
991
|
+
nextType = types[nextIndex];
|
|
992
|
+
currentVertex = polygon.vertices[i];
|
|
993
|
+
nextVertex = polygon.vertices[nextIndex];
|
|
994
|
+
if (currentType !== BACK) {
|
|
995
|
+
frontVertices.push(currentVertex);
|
|
996
|
+
}
|
|
997
|
+
if (currentType !== FRONT) {
|
|
998
|
+
backVertices.push(currentType !== BACK ? currentVertex.clone() : currentVertex);
|
|
999
|
+
}
|
|
1000
|
+
if ((currentType | nextType) === SPANNING) {
|
|
1001
|
+
intersectionParameter = (plane.w - plane.normal.dot(currentVertex.pos)) / plane.normal.dot(triangleVertex0.copy(nextVertex.pos).sub(currentVertex.pos));
|
|
1002
|
+
vertexParameter = currentVertex.interpolate(nextVertex, intersectionParameter);
|
|
1003
|
+
frontVertices.push(vertexParameter);
|
|
1004
|
+
backVertices.push(vertexParameter.clone());
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
if (frontVertices.length >= 3) {
|
|
1008
|
+
if (frontVertices.length > 3) {
|
|
1009
|
+
newPolygons = splitPolygonArr(frontVertices);
|
|
1010
|
+
for (polygonIndex = m = 0, ref2 = newPolygons.length; (0 <= ref2 ? m < ref2 : m > ref2); polygonIndex = 0 <= ref2 ? ++m : --m) {
|
|
1011
|
+
result.push({
|
|
1012
|
+
polygon: new Polygon(newPolygons[polygonIndex], polygon.shared),
|
|
1013
|
+
type: "front"
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
} else {
|
|
1017
|
+
result.push({
|
|
1018
|
+
polygon: new Polygon(frontVertices, polygon.shared),
|
|
1019
|
+
type: "front"
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
if (backVertices.length >= 3) {
|
|
1024
|
+
if (backVertices.length > 3) {
|
|
1025
|
+
newPolygons = splitPolygonArr(backVertices);
|
|
1026
|
+
for (polygonIndex = n = 0, ref3 = newPolygons.length; (0 <= ref3 ? n < ref3 : n > ref3); polygonIndex = 0 <= ref3 ? ++n : --n) {
|
|
1027
|
+
result.push({
|
|
1028
|
+
polygon: new Polygon(newPolygons[polygonIndex], polygon.shared),
|
|
1029
|
+
type: "back"
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
} else {
|
|
1033
|
+
result.push({
|
|
1034
|
+
polygon: new Polygon(backVertices, polygon.shared),
|
|
1035
|
+
type: "back"
|
|
1036
|
+
});
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
if (result.length === 0) {
|
|
1041
|
+
result.push(returnPolygon);
|
|
1042
|
+
}
|
|
1043
|
+
return result;
|
|
1044
|
+
};
|
|
1045
|
+
|
|
1046
|
+
splitPolygonArr = function(arr) {
|
|
1047
|
+
var j, k, ref, result, resultArr;
|
|
1048
|
+
resultArr = [];
|
|
1049
|
+
if (arr.length > 4) {
|
|
1050
|
+
console.warn("[splitPolygonArr] arr.length > 4", arr.length);
|
|
1051
|
+
for (j = k = 3, ref = arr.length; (3 <= ref ? k <= ref : k >= ref); j = 3 <= ref ? ++k : --k) {
|
|
1052
|
+
result = [];
|
|
1053
|
+
result.push(arr[0].clone());
|
|
1054
|
+
result.push(arr[j - 2].clone());
|
|
1055
|
+
result.push(arr[j - 1].clone());
|
|
1056
|
+
resultArr.push(result);
|
|
1057
|
+
}
|
|
1058
|
+
} else {
|
|
1059
|
+
if (arr[0].pos.distanceTo(arr[2].pos) <= arr[1].pos.distanceTo(arr[3].pos)) {
|
|
1060
|
+
resultArr.push([arr[0].clone(), arr[1].clone(), arr[2].clone()], [arr[0].clone(), arr[2].clone(), arr[3].clone()]);
|
|
1061
|
+
} else {
|
|
1062
|
+
resultArr.push([arr[0].clone(), arr[1].clone(), arr[3].clone()], [arr[1].clone(), arr[2].clone(), arr[3].clone()]);
|
|
1063
|
+
}
|
|
1064
|
+
return resultArr;
|
|
1065
|
+
}
|
|
1066
|
+
return resultArr;
|
|
1067
|
+
};
|
|
1068
|
+
|
|
1069
|
+
CSG_Rules = {
|
|
1070
|
+
union: {
|
|
1071
|
+
a: [
|
|
1072
|
+
{
|
|
1073
|
+
array: true,
|
|
1074
|
+
rule: ["inside",
|
|
1075
|
+
"coplanar-back"]
|
|
1076
|
+
},
|
|
1077
|
+
{
|
|
1078
|
+
array: false,
|
|
1079
|
+
rule: "inside"
|
|
1080
|
+
}
|
|
1081
|
+
],
|
|
1082
|
+
b: [
|
|
1083
|
+
{
|
|
1084
|
+
array: true,
|
|
1085
|
+
rule: ["inside",
|
|
1086
|
+
"coplanar-back"]
|
|
1087
|
+
},
|
|
1088
|
+
{
|
|
1089
|
+
array: true,
|
|
1090
|
+
rule: ["inside",
|
|
1091
|
+
"coplanar-front"]
|
|
1092
|
+
},
|
|
1093
|
+
{
|
|
1094
|
+
array: false,
|
|
1095
|
+
rule: "inside"
|
|
1096
|
+
}
|
|
1097
|
+
]
|
|
1098
|
+
},
|
|
1099
|
+
subtract: {
|
|
1100
|
+
a: [
|
|
1101
|
+
{
|
|
1102
|
+
array: true,
|
|
1103
|
+
rule: ["inside",
|
|
1104
|
+
"coplanar-back"]
|
|
1105
|
+
},
|
|
1106
|
+
{
|
|
1107
|
+
array: true,
|
|
1108
|
+
rule: ["inside",
|
|
1109
|
+
"coplanar-front"]
|
|
1110
|
+
},
|
|
1111
|
+
{
|
|
1112
|
+
array: false,
|
|
1113
|
+
rule: "inside"
|
|
1114
|
+
}
|
|
1115
|
+
],
|
|
1116
|
+
b: [
|
|
1117
|
+
{
|
|
1118
|
+
array: true,
|
|
1119
|
+
rule: ["outside",
|
|
1120
|
+
"coplanar-back"]
|
|
1121
|
+
},
|
|
1122
|
+
{
|
|
1123
|
+
array: true,
|
|
1124
|
+
rule: ["outside",
|
|
1125
|
+
"coplanar-front"]
|
|
1126
|
+
},
|
|
1127
|
+
{
|
|
1128
|
+
array: true,
|
|
1129
|
+
rule: ["inside",
|
|
1130
|
+
"coplanar-front"]
|
|
1131
|
+
},
|
|
1132
|
+
{
|
|
1133
|
+
array: false,
|
|
1134
|
+
rule: "outside"
|
|
1135
|
+
}
|
|
1136
|
+
]
|
|
1137
|
+
},
|
|
1138
|
+
intersect: {
|
|
1139
|
+
a: [
|
|
1140
|
+
{
|
|
1141
|
+
array: true,
|
|
1142
|
+
rule: ["inside",
|
|
1143
|
+
"coplanar-back"]
|
|
1144
|
+
},
|
|
1145
|
+
{
|
|
1146
|
+
array: true,
|
|
1147
|
+
rule: ["outside",
|
|
1148
|
+
"coplanar-front"]
|
|
1149
|
+
},
|
|
1150
|
+
{
|
|
1151
|
+
array: true,
|
|
1152
|
+
rule: ["outside",
|
|
1153
|
+
"coplanar-back"]
|
|
1154
|
+
},
|
|
1155
|
+
{
|
|
1156
|
+
array: false,
|
|
1157
|
+
rule: "outside"
|
|
1158
|
+
}
|
|
1159
|
+
],
|
|
1160
|
+
b: [
|
|
1161
|
+
{
|
|
1162
|
+
array: true,
|
|
1163
|
+
rule: ["inside",
|
|
1164
|
+
"coplanar-front"]
|
|
1165
|
+
},
|
|
1166
|
+
{
|
|
1167
|
+
array: true,
|
|
1168
|
+
rule: ["inside",
|
|
1169
|
+
"coplanar-back"]
|
|
1170
|
+
},
|
|
1171
|
+
{
|
|
1172
|
+
array: true,
|
|
1173
|
+
rule: ["outside",
|
|
1174
|
+
"coplanar-front"]
|
|
1175
|
+
},
|
|
1176
|
+
{
|
|
1177
|
+
array: true,
|
|
1178
|
+
rule: ["outside",
|
|
1179
|
+
"coplanar-back"]
|
|
1180
|
+
},
|
|
1181
|
+
{
|
|
1182
|
+
array: false,
|
|
1183
|
+
rule: "outside"
|
|
1184
|
+
}
|
|
1185
|
+
]
|
|
1186
|
+
}
|
|
1187
|
+
};
|
|
1188
|
+
|
|
1189
|
+
// class Polytree { };
|
|
1190
|
+
/*
|
|
1191
|
+
Union:
|
|
1192
|
+
- Combine all polygons from A and B, except:
|
|
1193
|
+
- Polygons in A that are inside B or coplanar-back with B
|
|
1194
|
+
- Polygons in B that are inside A or coplanar-back/front with A
|
|
1195
|
+
*/
|
|
1196
|
+
/*
|
|
1197
|
+
Subtract:
|
|
1198
|
+
- Keep polygons from A that are outside B or coplanar-front with B
|
|
1199
|
+
- Keep polygons from B that are inside A and coplanar-front with A
|
|
1200
|
+
*/
|
|
1201
|
+
/*
|
|
1202
|
+
Intersect:
|
|
1203
|
+
1. Delete all polygons in A that are:
|
|
1204
|
+
a. inside and coplanar-back
|
|
1205
|
+
b. outside and coplanar-front
|
|
1206
|
+
c. outside and coplanar-back
|
|
1207
|
+
d. outside
|
|
1208
|
+
2. Delete all polygons in B that are:
|
|
1209
|
+
a. inside and coplanar-front
|
|
1210
|
+
b. inside and coplanar-back
|
|
1211
|
+
c. outside and coplanar-front
|
|
1212
|
+
d. outside and coplanar-back
|
|
1213
|
+
e. outside
|
|
1214
|
+
*/
|
|
1215
|
+
Polytree.union = function(polytreeA, polytreeB, buildTargetPolytree = true) {
|
|
1216
|
+
var currentMeshSideA, currentMeshSideB, polytree, trianglesSet;
|
|
1217
|
+
polytree = new Polytree();
|
|
1218
|
+
trianglesSet = new Set();
|
|
1219
|
+
if (polytreeA.box.intersectsBox(polytreeB.box)) {
|
|
1220
|
+
currentMeshSideA = void 0;
|
|
1221
|
+
currentMeshSideB = void 0;
|
|
1222
|
+
if (polytreeA.mesh) {
|
|
1223
|
+
currentMeshSideA = polytreeA.mesh.material.side;
|
|
1224
|
+
polytreeA.mesh.material.side = DoubleSide;
|
|
1225
|
+
}
|
|
1226
|
+
if (polytreeB.mesh) {
|
|
1227
|
+
currentMeshSideB = polytreeB.mesh.material.side;
|
|
1228
|
+
polytreeB.mesh.material.side = DoubleSide;
|
|
1229
|
+
}
|
|
1230
|
+
polytreeA.resetPolygons(false);
|
|
1231
|
+
polytreeB.resetPolygons(false);
|
|
1232
|
+
polytreeA.markIntesectingPolygons(polytreeB);
|
|
1233
|
+
polytreeB.markIntesectingPolygons(polytreeA);
|
|
1234
|
+
handleIntersectingPolytrees(polytreeA, polytreeB);
|
|
1235
|
+
polytreeA.deleteReplacedPolygons();
|
|
1236
|
+
polytreeB.deleteReplacedPolygons();
|
|
1237
|
+
polytreeA.deletePolygonsByStateRules(CSG_Rules.union.a);
|
|
1238
|
+
polytreeB.deletePolygonsByStateRules(CSG_Rules.union.b);
|
|
1239
|
+
polytreeA.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
1240
|
+
polytreeB.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
1241
|
+
if (polytreeA.mesh && polytreeA.mesh.material.side !== currentMeshSideA) {
|
|
1242
|
+
polytreeA.mesh.material.side = currentMeshSideA;
|
|
1243
|
+
}
|
|
1244
|
+
if (polytreeB.mesh && polytreeB.mesh.material.side !== currentMeshSideB) {
|
|
1245
|
+
polytreeB.mesh.material.side = currentMeshSideB;
|
|
1246
|
+
}
|
|
1247
|
+
} else {
|
|
1248
|
+
polytreeA.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
1249
|
+
polytreeB.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
1250
|
+
}
|
|
1251
|
+
trianglesSet.clear();
|
|
1252
|
+
trianglesSet = void 0;
|
|
1253
|
+
polytree.markPolygonsAsOriginal();
|
|
1254
|
+
buildTargetPolytree && polytree.buildTree();
|
|
1255
|
+
return polytree;
|
|
1256
|
+
};
|
|
1257
|
+
|
|
1258
|
+
Polytree.subtract = function(polytreeA, polytreeB, buildTargetPolytree = true) {
|
|
1259
|
+
var currentMeshSideA, currentMeshSideB, polytree, trianglesSet;
|
|
1260
|
+
polytree = new Polytree();
|
|
1261
|
+
trianglesSet = new Set();
|
|
1262
|
+
if (polytreeA.box.intersectsBox(polytreeB.box)) {
|
|
1263
|
+
currentMeshSideA = void 0;
|
|
1264
|
+
currentMeshSideB = void 0;
|
|
1265
|
+
if (polytreeA.mesh) {
|
|
1266
|
+
currentMeshSideA = polytreeA.mesh.material.side;
|
|
1267
|
+
polytreeA.mesh.material.side = DoubleSide;
|
|
1268
|
+
}
|
|
1269
|
+
if (polytreeB.mesh) {
|
|
1270
|
+
currentMeshSideB = polytreeB.mesh.material.side;
|
|
1271
|
+
polytreeB.mesh.material.side = DoubleSide;
|
|
1272
|
+
}
|
|
1273
|
+
polytreeA.resetPolygons(false);
|
|
1274
|
+
polytreeB.resetPolygons(false);
|
|
1275
|
+
polytreeA.markIntesectingPolygons(polytreeB);
|
|
1276
|
+
polytreeB.markIntesectingPolygons(polytreeA);
|
|
1277
|
+
handleIntersectingPolytrees(polytreeA, polytreeB);
|
|
1278
|
+
polytreeA.deleteReplacedPolygons();
|
|
1279
|
+
polytreeB.deleteReplacedPolygons();
|
|
1280
|
+
polytreeA.deletePolygonsByStateRules(CSG_Rules.subtract.a);
|
|
1281
|
+
polytreeB.deletePolygonsByStateRules(CSG_Rules.subtract.b);
|
|
1282
|
+
polytreeB.deletePolygonsByIntersection(false);
|
|
1283
|
+
polytreeB.invert();
|
|
1284
|
+
polytreeA.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
1285
|
+
polytreeB.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
1286
|
+
if (polytreeA.mesh && polytreeA.mesh.material.side !== currentMeshSideA) {
|
|
1287
|
+
polytreeA.mesh.material.side = currentMeshSideA;
|
|
1288
|
+
}
|
|
1289
|
+
if (polytreeB.mesh && polytreeB.mesh.material.side !== currentMeshSideB) {
|
|
1290
|
+
polytreeB.mesh.material.side = currentMeshSideB;
|
|
1291
|
+
}
|
|
1292
|
+
} else {
|
|
1293
|
+
polytreeA.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
1294
|
+
}
|
|
1295
|
+
trianglesSet.clear();
|
|
1296
|
+
trianglesSet = void 0;
|
|
1297
|
+
polytree.markPolygonsAsOriginal();
|
|
1298
|
+
buildTargetPolytree && polytree.buildTree();
|
|
1299
|
+
// polytree.invert()
|
|
1300
|
+
return polytree;
|
|
1301
|
+
};
|
|
1302
|
+
|
|
1303
|
+
Polytree.intersect = function(polytreeA, polytreeB, buildTargetPolytree = true) {
|
|
1304
|
+
var currentMeshSideA, currentMeshSideB, polytree, trianglesSet;
|
|
1305
|
+
polytree = new Polytree();
|
|
1306
|
+
trianglesSet = new Set();
|
|
1307
|
+
if (polytreeA.box.intersectsBox(polytreeB.box)) {
|
|
1308
|
+
currentMeshSideA = void 0;
|
|
1309
|
+
currentMeshSideB = void 0;
|
|
1310
|
+
if (polytreeA.mesh) {
|
|
1311
|
+
currentMeshSideA = polytreeA.mesh.material.side;
|
|
1312
|
+
polytreeA.mesh.material.side = DoubleSide;
|
|
1313
|
+
}
|
|
1314
|
+
if (polytreeB.mesh) {
|
|
1315
|
+
currentMeshSideB = polytreeB.mesh.material.side;
|
|
1316
|
+
polytreeB.mesh.material.side = DoubleSide;
|
|
1317
|
+
}
|
|
1318
|
+
polytreeA.resetPolygons(false);
|
|
1319
|
+
polytreeB.resetPolygons(false);
|
|
1320
|
+
polytreeA.markIntesectingPolygons(polytreeB);
|
|
1321
|
+
polytreeB.markIntesectingPolygons(polytreeA);
|
|
1322
|
+
handleIntersectingPolytrees(polytreeA, polytreeB);
|
|
1323
|
+
polytreeA.deleteReplacedPolygons();
|
|
1324
|
+
polytreeB.deleteReplacedPolygons();
|
|
1325
|
+
polytreeA.deletePolygonsByStateRules(CSG_Rules.intersect.a);
|
|
1326
|
+
polytreeB.deletePolygonsByStateRules(CSG_Rules.intersect.b);
|
|
1327
|
+
polytreeA.deletePolygonsByIntersection(false);
|
|
1328
|
+
polytreeB.deletePolygonsByIntersection(false);
|
|
1329
|
+
polytreeA.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
1330
|
+
polytreeB.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
1331
|
+
if (polytreeA.mesh && polytreeA.mesh.material.side !== currentMeshSideA) {
|
|
1332
|
+
polytreeA.mesh.material.side = currentMeshSideA;
|
|
1333
|
+
}
|
|
1334
|
+
if (polytreeB.mesh && polytreeB.mesh.material.side !== currentMeshSideB) {
|
|
1335
|
+
polytreeB.mesh.material.side = currentMeshSideB;
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
trianglesSet.clear();
|
|
1339
|
+
trianglesSet = void 0;
|
|
1340
|
+
polytree.markPolygonsAsOriginal();
|
|
1341
|
+
buildTargetPolytree && polytree.buildTree();
|
|
1342
|
+
return polytree;
|
|
1343
|
+
};
|
|
1344
|
+
|
|
1345
|
+
CSG_Rules = {
|
|
1346
|
+
union: {
|
|
1347
|
+
a: [
|
|
1348
|
+
{
|
|
1349
|
+
array: true,
|
|
1350
|
+
rule: ["inside",
|
|
1351
|
+
"coplanar-back"]
|
|
1352
|
+
},
|
|
1353
|
+
{
|
|
1354
|
+
array: false,
|
|
1355
|
+
rule: "inside"
|
|
1356
|
+
}
|
|
1357
|
+
],
|
|
1358
|
+
b: [
|
|
1359
|
+
{
|
|
1360
|
+
array: true,
|
|
1361
|
+
rule: ["inside",
|
|
1362
|
+
"coplanar-back"]
|
|
1363
|
+
},
|
|
1364
|
+
{
|
|
1365
|
+
array: true,
|
|
1366
|
+
rule: ["inside",
|
|
1367
|
+
"coplanar-front"]
|
|
1368
|
+
},
|
|
1369
|
+
{
|
|
1370
|
+
array: false,
|
|
1371
|
+
rule: "inside"
|
|
1372
|
+
}
|
|
1373
|
+
]
|
|
1374
|
+
},
|
|
1375
|
+
subtract: {
|
|
1376
|
+
a: [
|
|
1377
|
+
{
|
|
1378
|
+
array: true,
|
|
1379
|
+
rule: ["inside",
|
|
1380
|
+
"coplanar-back"]
|
|
1381
|
+
},
|
|
1382
|
+
{
|
|
1383
|
+
array: true,
|
|
1384
|
+
rule: ["inside",
|
|
1385
|
+
"coplanar-front"]
|
|
1386
|
+
},
|
|
1387
|
+
{
|
|
1388
|
+
array: false,
|
|
1389
|
+
rule: "inside"
|
|
1390
|
+
}
|
|
1391
|
+
],
|
|
1392
|
+
b: [
|
|
1393
|
+
{
|
|
1394
|
+
array: true,
|
|
1395
|
+
rule: ["outside",
|
|
1396
|
+
"coplanar-back"]
|
|
1397
|
+
},
|
|
1398
|
+
{
|
|
1399
|
+
array: true,
|
|
1400
|
+
rule: ["outside",
|
|
1401
|
+
"coplanar-front"]
|
|
1402
|
+
},
|
|
1403
|
+
{
|
|
1404
|
+
array: true,
|
|
1405
|
+
rule: ["inside",
|
|
1406
|
+
"coplanar-front"]
|
|
1407
|
+
},
|
|
1408
|
+
{
|
|
1409
|
+
array: false,
|
|
1410
|
+
rule: "outside"
|
|
1411
|
+
}
|
|
1412
|
+
]
|
|
1413
|
+
},
|
|
1414
|
+
intersect: {
|
|
1415
|
+
a: [
|
|
1416
|
+
{
|
|
1417
|
+
array: true,
|
|
1418
|
+
rule: ["inside",
|
|
1419
|
+
"coplanar-back"]
|
|
1420
|
+
},
|
|
1421
|
+
{
|
|
1422
|
+
array: true,
|
|
1423
|
+
rule: ["outside",
|
|
1424
|
+
"coplanar-front"]
|
|
1425
|
+
},
|
|
1426
|
+
{
|
|
1427
|
+
array: true,
|
|
1428
|
+
rule: ["outside",
|
|
1429
|
+
"coplanar-back"]
|
|
1430
|
+
},
|
|
1431
|
+
{
|
|
1432
|
+
array: false,
|
|
1433
|
+
rule: "outside"
|
|
1434
|
+
}
|
|
1435
|
+
],
|
|
1436
|
+
b: [
|
|
1437
|
+
{
|
|
1438
|
+
array: true,
|
|
1439
|
+
rule: ["inside",
|
|
1440
|
+
"coplanar-front"]
|
|
1441
|
+
},
|
|
1442
|
+
{
|
|
1443
|
+
array: true,
|
|
1444
|
+
rule: ["inside",
|
|
1445
|
+
"coplanar-back"]
|
|
1446
|
+
},
|
|
1447
|
+
{
|
|
1448
|
+
array: true,
|
|
1449
|
+
rule: ["outside",
|
|
1450
|
+
"coplanar-front"]
|
|
1451
|
+
},
|
|
1452
|
+
{
|
|
1453
|
+
array: true,
|
|
1454
|
+
rule: ["outside",
|
|
1455
|
+
"coplanar-back"]
|
|
1456
|
+
},
|
|
1457
|
+
{
|
|
1458
|
+
array: false,
|
|
1459
|
+
rule: "outside"
|
|
1460
|
+
}
|
|
1461
|
+
]
|
|
1462
|
+
}
|
|
1463
|
+
};
|
|
1464
|
+
|
|
1465
|
+
// class Polytree { };
|
|
1466
|
+
/*
|
|
1467
|
+
Union:
|
|
1468
|
+
- Combine all polygons from A and B, except:
|
|
1469
|
+
- Polygons in A that are inside B or coplanar-back with B
|
|
1470
|
+
- Polygons in B that are inside A or coplanar-back/front with A
|
|
1471
|
+
*/
|
|
1472
|
+
/*
|
|
1473
|
+
Subtract:
|
|
1474
|
+
- Keep polygons from A that are outside B or coplanar-front with B
|
|
1475
|
+
- Keep polygons from B that are inside A and coplanar-front with A
|
|
1476
|
+
*/
|
|
1477
|
+
/*
|
|
1478
|
+
Intersect:
|
|
1479
|
+
1. Delete all polygons in A that are:
|
|
1480
|
+
a. inside and coplanar-back
|
|
1481
|
+
b. outside and coplanar-front
|
|
1482
|
+
c. outside and coplanar-back
|
|
1483
|
+
d. outside
|
|
1484
|
+
2. Delete all polygons in B that are:
|
|
1485
|
+
a. inside and coplanar-front
|
|
1486
|
+
b. inside and coplanar-back
|
|
1487
|
+
c. outside and coplanar-front
|
|
1488
|
+
d. outside and coplanar-back
|
|
1489
|
+
e. outside
|
|
1490
|
+
*/
|
|
1491
|
+
Polytree.union = function(polytreeA, polytreeB, buildTargetPolytree = true) {
|
|
1492
|
+
var currentMeshSideA, currentMeshSideB, polytree, trianglesSet;
|
|
1493
|
+
polytree = new Polytree();
|
|
1494
|
+
trianglesSet = new Set();
|
|
1495
|
+
if (polytreeA.box.intersectsBox(polytreeB.box)) {
|
|
1496
|
+
currentMeshSideA = void 0;
|
|
1497
|
+
currentMeshSideB = void 0;
|
|
1498
|
+
if (polytreeA.mesh) {
|
|
1499
|
+
currentMeshSideA = polytreeA.mesh.material.side;
|
|
1500
|
+
polytreeA.mesh.material.side = DoubleSide;
|
|
1501
|
+
}
|
|
1502
|
+
if (polytreeB.mesh) {
|
|
1503
|
+
currentMeshSideB = polytreeB.mesh.material.side;
|
|
1504
|
+
polytreeB.mesh.material.side = DoubleSide;
|
|
1505
|
+
}
|
|
1506
|
+
polytreeA.resetPolygons(false);
|
|
1507
|
+
polytreeB.resetPolygons(false);
|
|
1508
|
+
polytreeA.markIntesectingPolygons(polytreeB);
|
|
1509
|
+
polytreeB.markIntesectingPolygons(polytreeA);
|
|
1510
|
+
handleIntersectingPolytrees(polytreeA, polytreeB);
|
|
1511
|
+
polytreeA.deleteReplacedPolygons();
|
|
1512
|
+
polytreeB.deleteReplacedPolygons();
|
|
1513
|
+
polytreeA.deletePolygonsByStateRules(CSG_Rules.union.a);
|
|
1514
|
+
polytreeB.deletePolygonsByStateRules(CSG_Rules.union.b);
|
|
1515
|
+
polytreeA.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
1516
|
+
polytreeB.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
1517
|
+
if (polytreeA.mesh && polytreeA.mesh.material.side !== currentMeshSideA) {
|
|
1518
|
+
polytreeA.mesh.material.side = currentMeshSideA;
|
|
1519
|
+
}
|
|
1520
|
+
if (polytreeB.mesh && polytreeB.mesh.material.side !== currentMeshSideB) {
|
|
1521
|
+
polytreeB.mesh.material.side = currentMeshSideB;
|
|
1522
|
+
}
|
|
1523
|
+
} else {
|
|
1524
|
+
polytreeA.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
1525
|
+
polytreeB.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
1526
|
+
}
|
|
1527
|
+
trianglesSet.clear();
|
|
1528
|
+
trianglesSet = void 0;
|
|
1529
|
+
polytree.markPolygonsAsOriginal();
|
|
1530
|
+
buildTargetPolytree && polytree.buildTree();
|
|
1531
|
+
return polytree;
|
|
1532
|
+
};
|
|
1533
|
+
|
|
1534
|
+
Polytree.subtract = function(polytreeA, polytreeB, buildTargetPolytree = true) {
|
|
1535
|
+
var currentMeshSideA, currentMeshSideB, polytree, trianglesSet;
|
|
1536
|
+
polytree = new Polytree();
|
|
1537
|
+
trianglesSet = new Set();
|
|
1538
|
+
if (polytreeA.box.intersectsBox(polytreeB.box)) {
|
|
1539
|
+
currentMeshSideA = void 0;
|
|
1540
|
+
currentMeshSideB = void 0;
|
|
1541
|
+
if (polytreeA.mesh) {
|
|
1542
|
+
currentMeshSideA = polytreeA.mesh.material.side;
|
|
1543
|
+
polytreeA.mesh.material.side = DoubleSide;
|
|
1544
|
+
}
|
|
1545
|
+
if (polytreeB.mesh) {
|
|
1546
|
+
currentMeshSideB = polytreeB.mesh.material.side;
|
|
1547
|
+
polytreeB.mesh.material.side = DoubleSide;
|
|
1548
|
+
}
|
|
1549
|
+
polytreeA.resetPolygons(false);
|
|
1550
|
+
polytreeB.resetPolygons(false);
|
|
1551
|
+
polytreeA.markIntesectingPolygons(polytreeB);
|
|
1552
|
+
polytreeB.markIntesectingPolygons(polytreeA);
|
|
1553
|
+
handleIntersectingPolytrees(polytreeA, polytreeB);
|
|
1554
|
+
polytreeA.deleteReplacedPolygons();
|
|
1555
|
+
polytreeB.deleteReplacedPolygons();
|
|
1556
|
+
polytreeA.deletePolygonsByStateRules(CSG_Rules.subtract.a);
|
|
1557
|
+
polytreeB.deletePolygonsByStateRules(CSG_Rules.subtract.b);
|
|
1558
|
+
polytreeB.deletePolygonsByIntersection(false);
|
|
1559
|
+
polytreeB.invert();
|
|
1560
|
+
polytreeA.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
1561
|
+
polytreeB.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
1562
|
+
if (polytreeA.mesh && polytreeA.mesh.material.side !== currentMeshSideA) {
|
|
1563
|
+
polytreeA.mesh.material.side = currentMeshSideA;
|
|
1564
|
+
}
|
|
1565
|
+
if (polytreeB.mesh && polytreeB.mesh.material.side !== currentMeshSideB) {
|
|
1566
|
+
polytreeB.mesh.material.side = currentMeshSideB;
|
|
1567
|
+
}
|
|
1568
|
+
} else {
|
|
1569
|
+
polytreeA.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
1570
|
+
}
|
|
1571
|
+
trianglesSet.clear();
|
|
1572
|
+
trianglesSet = void 0;
|
|
1573
|
+
polytree.markPolygonsAsOriginal();
|
|
1574
|
+
buildTargetPolytree && polytree.buildTree();
|
|
1575
|
+
// polytree.invert()
|
|
1576
|
+
return polytree;
|
|
1577
|
+
};
|
|
1578
|
+
|
|
1579
|
+
/*
|
|
1580
|
+
Intersect:
|
|
1581
|
+
1. Delete all polygons in A that are:
|
|
1582
|
+
a. inside and coplanar-back
|
|
1583
|
+
b. outside and coplanar-front
|
|
1584
|
+
c. outside and coplanar-back
|
|
1585
|
+
d. outside
|
|
1586
|
+
2. Delete all polygons in B that are:
|
|
1587
|
+
a. inside and coplanar-front
|
|
1588
|
+
b. inside and coplanar-back
|
|
1589
|
+
c. outside and coplanar-front
|
|
1590
|
+
d. outside and coplanar-back
|
|
1591
|
+
e. outside
|
|
1592
|
+
*/
|
|
1593
|
+
Polytree.intersect = function(polytreeA, polytreeB, buildTargetPolytree = true) {
|
|
1594
|
+
var currentMeshSideA, currentMeshSideB, polytree, trianglesSet;
|
|
1595
|
+
polytree = new Polytree();
|
|
1596
|
+
trianglesSet = new Set();
|
|
1597
|
+
if (polytreeA.box.intersectsBox(polytreeB.box)) {
|
|
1598
|
+
currentMeshSideA = void 0;
|
|
1599
|
+
currentMeshSideB = void 0;
|
|
1600
|
+
if (polytreeA.mesh) {
|
|
1601
|
+
currentMeshSideA = polytreeA.mesh.material.side;
|
|
1602
|
+
polytreeA.mesh.material.side = DoubleSide;
|
|
1603
|
+
}
|
|
1604
|
+
if (polytreeB.mesh) {
|
|
1605
|
+
currentMeshSideB = polytreeB.mesh.material.side;
|
|
1606
|
+
polytreeB.mesh.material.side = DoubleSide;
|
|
1607
|
+
}
|
|
1608
|
+
polytreeA.resetPolygons(false);
|
|
1609
|
+
polytreeB.resetPolygons(false);
|
|
1610
|
+
polytreeA.markIntesectingPolygons(polytreeB);
|
|
1611
|
+
polytreeB.markIntesectingPolygons(polytreeA);
|
|
1612
|
+
handleIntersectingPolytrees(polytreeA, polytreeB);
|
|
1613
|
+
polytreeA.deleteReplacedPolygons();
|
|
1614
|
+
polytreeB.deleteReplacedPolygons();
|
|
1615
|
+
polytreeA.deletePolygonsByStateRules(CSG_Rules.intersect.a);
|
|
1616
|
+
polytreeB.deletePolygonsByStateRules(CSG_Rules.intersect.b);
|
|
1617
|
+
polytreeA.deletePolygonsByIntersection(false);
|
|
1618
|
+
polytreeB.deletePolygonsByIntersection(false);
|
|
1619
|
+
polytreeA.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
1620
|
+
polytreeB.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
1621
|
+
if (polytreeA.mesh) {
|
|
1622
|
+
if (polytreeA.mesh.material.side !== currentMeshSideA) {
|
|
1623
|
+
polytreeA.mesh.material.side = currentMeshSideA;
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
if (polytreeB.mesh) {
|
|
1627
|
+
if (polytreeB.mesh.material.side !== currentMeshSideB) {
|
|
1628
|
+
polytreeB.mesh.material.side = currentMeshSideB;
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
trianglesSet.clear();
|
|
1633
|
+
trianglesSet = void 0;
|
|
1634
|
+
polytree.markPolygonsAsOriginal();
|
|
1635
|
+
buildTargetPolytree && polytree.buildTree();
|
|
1636
|
+
return polytree;
|
|
1637
|
+
};
|
|
1638
|
+
|
|
1639
|
+
Polytree.meshUnion = function(mesh1, mesh2, targetMaterial) {
|
|
1640
|
+
var polytreeA, polytreeB, resultMesh, resultPolytree;
|
|
1641
|
+
polytreeA = void 0;
|
|
1642
|
+
polytreeB = void 0;
|
|
1643
|
+
if (targetMaterial && Array.isArray(targetMaterial)) {
|
|
1644
|
+
polytreeA = Polytree.fromMesh(mesh1, 0);
|
|
1645
|
+
polytreeB = Polytree.fromMesh(mesh2, 1);
|
|
1646
|
+
} else {
|
|
1647
|
+
polytreeA = Polytree.fromMesh(mesh1);
|
|
1648
|
+
polytreeB = Polytree.fromMesh(mesh2);
|
|
1649
|
+
targetMaterial = targetMaterial !== void 0 ? targetMaterial : (Array.isArray(mesh1.material) ? mesh1.material[0] : mesh1.material).clone();
|
|
1650
|
+
}
|
|
1651
|
+
resultPolytree = Polytree.union(polytreeA, polytreeB, false);
|
|
1652
|
+
resultMesh = Polytree.toMesh(resultPolytree, targetMaterial);
|
|
1653
|
+
disposePolytree(polytreeA, polytreeB, resultPolytree);
|
|
1654
|
+
return resultMesh;
|
|
1655
|
+
};
|
|
1656
|
+
|
|
1657
|
+
Polytree.meshSubtract = function(mesh1, mesh2, targetMaterial) {
|
|
1658
|
+
var polytreeA, polytreeB, resultMesh, resultPolytree;
|
|
1659
|
+
polytreeA = void 0;
|
|
1660
|
+
polytreeB = void 0;
|
|
1661
|
+
if (targetMaterial && Array.isArray(targetMaterial)) {
|
|
1662
|
+
polytreeA = Polytree.fromMesh(mesh1, 0);
|
|
1663
|
+
polytreeB = Polytree.fromMesh(mesh2, 1);
|
|
1664
|
+
} else {
|
|
1665
|
+
polytreeA = Polytree.fromMesh(mesh1);
|
|
1666
|
+
polytreeB = Polytree.fromMesh(mesh2);
|
|
1667
|
+
targetMaterial = targetMaterial !== void 0 ? targetMaterial : (Array.isArray(mesh1.material) ? mesh1.material[0] : mesh1.material).clone();
|
|
1668
|
+
}
|
|
1669
|
+
resultPolytree = Polytree.subtract(polytreeA, polytreeB, false);
|
|
1670
|
+
resultMesh = Polytree.toMesh(resultPolytree, targetMaterial);
|
|
1671
|
+
disposePolytree(polytreeA, polytreeB, resultPolytree);
|
|
1672
|
+
return resultMesh;
|
|
1673
|
+
};
|
|
1674
|
+
|
|
1675
|
+
Polytree.meshIntersect = function(mesh1, mesh2, targetMaterial) {
|
|
1676
|
+
var polytreeA, polytreeB, resultMesh, resultPolytree;
|
|
1677
|
+
polytreeA = void 0;
|
|
1678
|
+
polytreeB = void 0;
|
|
1679
|
+
if (targetMaterial && Array.isArray(targetMaterial)) {
|
|
1680
|
+
polytreeA = Polytree.fromMesh(mesh1, 0);
|
|
1681
|
+
polytreeB = Polytree.fromMesh(mesh2, 1);
|
|
1682
|
+
} else {
|
|
1683
|
+
polytreeA = Polytree.fromMesh(mesh1);
|
|
1684
|
+
polytreeB = Polytree.fromMesh(mesh2);
|
|
1685
|
+
targetMaterial = targetMaterial !== void 0 ? targetMaterial : (Array.isArray(mesh1.material) ? mesh1.material[0] : mesh1.material).clone();
|
|
1686
|
+
}
|
|
1687
|
+
resultPolytree = Polytree.intersect(polytreeA, polytreeB, false);
|
|
1688
|
+
resultMesh = Polytree.toMesh(resultPolytree, targetMaterial);
|
|
1689
|
+
disposePolytree(polytreeA, polytreeB, resultPolytree);
|
|
1690
|
+
return resultMesh;
|
|
1691
|
+
};
|
|
1692
|
+
|
|
1693
|
+
_asyncUnionID = 0;
|
|
1694
|
+
|
|
1695
|
+
_asyncUnionArrayID = 0;
|
|
1696
|
+
|
|
1697
|
+
Polytree.disposePolytree = true;
|
|
1698
|
+
|
|
1699
|
+
Polytree.async = {
|
|
1700
|
+
batchSize: 100,
|
|
1701
|
+
union: function(polytreeA, polytreeB, buildTargetPolytree = true) {
|
|
1702
|
+
return new Promise(function(resolve, reject) {
|
|
1703
|
+
var e, result;
|
|
1704
|
+
try {
|
|
1705
|
+
// const id = _asyncUnionID++
|
|
1706
|
+
// console.log("Promise Union ##{id} started")
|
|
1707
|
+
result = Polytree.union(polytreeA, polytreeB, buildTargetPolytree);
|
|
1708
|
+
resolve(result);
|
|
1709
|
+
return disposePolytree(polytreeA, polytreeB);
|
|
1710
|
+
} catch (error) {
|
|
1711
|
+
e = error;
|
|
1712
|
+
return reject(e);
|
|
1713
|
+
}
|
|
1714
|
+
});
|
|
1715
|
+
},
|
|
1716
|
+
subtract: function(polytreeA, polytreeB, buildTargetPolytree = true) {
|
|
1717
|
+
return new Promise(function(resolve, reject) {
|
|
1718
|
+
var e, result;
|
|
1719
|
+
try {
|
|
1720
|
+
result = Polytree.subtract(polytreeA, polytreeB, buildTargetPolytree);
|
|
1721
|
+
resolve(result);
|
|
1722
|
+
return disposePolytree(polytreeA, polytreeB);
|
|
1723
|
+
} catch (error) {
|
|
1724
|
+
e = error;
|
|
1725
|
+
return reject(e);
|
|
1726
|
+
}
|
|
1727
|
+
});
|
|
1728
|
+
},
|
|
1729
|
+
intersect: function(polytreeA, polytreeB, buildTargetPolytree = true) {
|
|
1730
|
+
return new Promise(function(resolve, reject) {
|
|
1731
|
+
var e, result;
|
|
1732
|
+
try {
|
|
1733
|
+
result = Polytree.intersect(polytreeA, polytreeB, buildTargetPolytree);
|
|
1734
|
+
resolve(result);
|
|
1735
|
+
return disposePolytree(polytreeA, polytreeB);
|
|
1736
|
+
} catch (error) {
|
|
1737
|
+
e = error;
|
|
1738
|
+
return reject(e);
|
|
1739
|
+
}
|
|
1740
|
+
});
|
|
1741
|
+
},
|
|
1742
|
+
unionArray: function(objArr, materialIndexMax = 2e308) {
|
|
1743
|
+
return new Promise(function(resolve, reject) {
|
|
1744
|
+
var batch, batches, currentIndex, e, hasLeftOver, i, k, l, leftOverPolytree, mainPolytree, mainPolytreeUsed, materialIndex, polytreesArray, promise, promises, ref, ref1, result, tempPolytree, usingBatches;
|
|
1745
|
+
try {
|
|
1746
|
+
usingBatches = Polytree.async.batchSize > 4 && Polytree.async.batchSize < objArr.length;
|
|
1747
|
+
// const id = _asyncUnionArrayID++
|
|
1748
|
+
// console.log("Promise Union Array ##{id}", usingBatches)
|
|
1749
|
+
mainPolytree = void 0;
|
|
1750
|
+
mainPolytreeUsed = false;
|
|
1751
|
+
promises = [];
|
|
1752
|
+
if (usingBatches) {
|
|
1753
|
+
batches = [];
|
|
1754
|
+
currentIndex = 0;
|
|
1755
|
+
while (currentIndex < objArr.length) {
|
|
1756
|
+
batches.push(objArr.slice(currentIndex, currentIndex + Polytree.async.batchSize));
|
|
1757
|
+
currentIndex += Polytree.async.batchSize;
|
|
1758
|
+
}
|
|
1759
|
+
batch = batches.shift();
|
|
1760
|
+
while (batch) {
|
|
1761
|
+
promise = Polytree.async.unionArray(batch, 0);
|
|
1762
|
+
promises.push(promise);
|
|
1763
|
+
batch = batches.shift();
|
|
1764
|
+
}
|
|
1765
|
+
usingBatches = true;
|
|
1766
|
+
mainPolytreeUsed = true;
|
|
1767
|
+
objArr.length = 0;
|
|
1768
|
+
} else {
|
|
1769
|
+
polytreesArray = [];
|
|
1770
|
+
for (i = k = 0, ref = objArr.length; (0 <= ref ? k < ref : k > ref); i = 0 <= ref ? ++k : --k) {
|
|
1771
|
+
materialIndex = i > materialIndexMax ? materialIndexMax : i;
|
|
1772
|
+
tempPolytree = void 0;
|
|
1773
|
+
if (objArr[i].isMesh) {
|
|
1774
|
+
tempPolytree = Polytree.fromMesh(objArr[i], materialIndexMax > -1 ? materialIndex : void 0);
|
|
1775
|
+
} else {
|
|
1776
|
+
tempPolytree = objArr[i];
|
|
1777
|
+
if (materialIndexMax > -1) {
|
|
1778
|
+
tempPolytree.setPolygonIndex(materialIndex);
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
tempPolytree.polytreeIndex = i;
|
|
1782
|
+
polytreesArray.push(tempPolytree);
|
|
1783
|
+
}
|
|
1784
|
+
mainPolytree = polytreesArray.shift();
|
|
1785
|
+
result = void 0;
|
|
1786
|
+
hasLeftOver = false;
|
|
1787
|
+
leftOverPolytree = void 0;
|
|
1788
|
+
for (i = l = 0, ref1 = polytreesArray.length; l < ref1; i = l += 2) {
|
|
1789
|
+
if (i + 1 >= polytreesArray.length) {
|
|
1790
|
+
leftOverPolytree = polytreesArray[i];
|
|
1791
|
+
hasLeftOver = true;
|
|
1792
|
+
break;
|
|
1793
|
+
}
|
|
1794
|
+
promise = Polytree.async.union(polytreesArray[i], polytreesArray[i + 1]);
|
|
1795
|
+
promises.push(promise);
|
|
1796
|
+
}
|
|
1797
|
+
if (leftOverPolytree) {
|
|
1798
|
+
promise = Polytree.async.union(mainPolytree, leftOverPolytree);
|
|
1799
|
+
promises.push(promise);
|
|
1800
|
+
mainPolytreeUsed = true;
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
return Promise.allSettled(promises).then(function(results) {
|
|
1804
|
+
var polytrees;
|
|
1805
|
+
polytrees = [];
|
|
1806
|
+
results.forEach(function(r) {
|
|
1807
|
+
if (r.status === "fulfilled") {
|
|
1808
|
+
return polytrees.push(r.value);
|
|
1809
|
+
}
|
|
1810
|
+
});
|
|
1811
|
+
if (!mainPolytreeUsed) {
|
|
1812
|
+
polytrees.unshift(mainPolytree);
|
|
1813
|
+
}
|
|
1814
|
+
if (polytrees.length > 0) {
|
|
1815
|
+
if (polytrees.length === 1) {
|
|
1816
|
+
return resolve(polytrees[0]);
|
|
1817
|
+
} else if (polytrees.length > 3) {
|
|
1818
|
+
return Polytree.async.unionArray(polytrees, usingBatches ? 0 : -1).then(function(result) {
|
|
1819
|
+
return resolve(result);
|
|
1820
|
+
}).catch(function(e) {
|
|
1821
|
+
return reject(e);
|
|
1822
|
+
});
|
|
1823
|
+
} else {
|
|
1824
|
+
return Polytree.async.union(polytrees[0], polytrees[1]).then(function(result) {
|
|
1825
|
+
if (polytrees.length === 3) {
|
|
1826
|
+
return Polytree.async.union(result, polytrees[2]).then(function(result) {
|
|
1827
|
+
return resolve(result);
|
|
1828
|
+
}).catch(function(e) {
|
|
1829
|
+
return reject(e);
|
|
1830
|
+
});
|
|
1831
|
+
} else {
|
|
1832
|
+
return resolve(result);
|
|
1833
|
+
}
|
|
1834
|
+
}).catch(function(e) {
|
|
1835
|
+
return reject(e);
|
|
1836
|
+
});
|
|
1837
|
+
}
|
|
1838
|
+
} else {
|
|
1839
|
+
return reject('Unable to find any result polytree');
|
|
1840
|
+
}
|
|
1841
|
+
});
|
|
1842
|
+
} catch (error) {
|
|
1843
|
+
e = error;
|
|
1844
|
+
return reject(e);
|
|
1845
|
+
}
|
|
1846
|
+
});
|
|
1847
|
+
},
|
|
1848
|
+
subtractArray: function(objArr, materialIndexMax = 2e308) {
|
|
1849
|
+
return new Promise(function(resolve, reject) {
|
|
1850
|
+
var batch, batches, currentIndex, e, hasLeftOver, i, k, l, leftOverPolytree, mainPolytree, mainPolytreeUsed, materialIndex, polytreesArray, promise, promises, ref, ref1, result, tempPolytree, usingBatches;
|
|
1851
|
+
try {
|
|
1852
|
+
usingBatches = Polytree.async.batchSize > 4 && Polytree.async.batchSize < objArr.length;
|
|
1853
|
+
mainPolytree = void 0;
|
|
1854
|
+
mainPolytreeUsed = false;
|
|
1855
|
+
promises = [];
|
|
1856
|
+
if (usingBatches) {
|
|
1857
|
+
batches = [];
|
|
1858
|
+
currentIndex = 0;
|
|
1859
|
+
while (currentIndex < objArr.length) {
|
|
1860
|
+
batches.push(objArr.slice(currentIndex, currentIndex + Polytree.async.batchSize));
|
|
1861
|
+
currentIndex += Polytree.async.batchSize;
|
|
1862
|
+
}
|
|
1863
|
+
batch = batches.shift();
|
|
1864
|
+
while (batch) {
|
|
1865
|
+
promise = Polytree.async.subtractArray(batch, 0);
|
|
1866
|
+
promises.push(promise);
|
|
1867
|
+
batch = batches.shift();
|
|
1868
|
+
}
|
|
1869
|
+
usingBatches = true;
|
|
1870
|
+
mainPolytreeUsed = true;
|
|
1871
|
+
objArr.length = 0;
|
|
1872
|
+
} else {
|
|
1873
|
+
polytreesArray = [];
|
|
1874
|
+
for (i = k = 0, ref = objArr.length; (0 <= ref ? k < ref : k > ref); i = 0 <= ref ? ++k : --k) {
|
|
1875
|
+
materialIndex = i > materialIndexMax ? materialIndexMax : i;
|
|
1876
|
+
tempPolytree = void 0;
|
|
1877
|
+
if (objArr[i].isMesh) {
|
|
1878
|
+
tempPolytree = Polytree.fromMesh(objArr[i], materialIndexMax > -1 ? materialIndex : void 0);
|
|
1879
|
+
} else {
|
|
1880
|
+
tempPolytree = objArr[i];
|
|
1881
|
+
if (materialIndexMax > -1) {
|
|
1882
|
+
tempPolytree.setPolygonIndex(materialIndex);
|
|
1883
|
+
}
|
|
1884
|
+
}
|
|
1885
|
+
tempPolytree.polytreeIndex = i;
|
|
1886
|
+
polytreesArray.push(tempPolytree);
|
|
1887
|
+
}
|
|
1888
|
+
mainPolytree = polytreesArray.shift();
|
|
1889
|
+
result = void 0;
|
|
1890
|
+
hasLeftOver = false;
|
|
1891
|
+
leftOverPolytree = void 0;
|
|
1892
|
+
for (i = l = 0, ref1 = polytreesArray.length; l < ref1; i = l += 2) {
|
|
1893
|
+
if (i + 1 >= polytreesArray.length) {
|
|
1894
|
+
leftOverPolytree = polytreesArray[i];
|
|
1895
|
+
hasLeftOver = true;
|
|
1896
|
+
break;
|
|
1897
|
+
}
|
|
1898
|
+
promise = Polytree.async.subtract(polytreesArray[i], polytreesArray[i + 1]);
|
|
1899
|
+
promises.push(promise);
|
|
1900
|
+
}
|
|
1901
|
+
if (leftOverPolytree) {
|
|
1902
|
+
promise = Polytree.async.subtract(mainPolytree, leftOverPolytree);
|
|
1903
|
+
promises.push(promise);
|
|
1904
|
+
mainPolytreeUsed = true;
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
return Promise.allSettled(promises).then(function(results) {
|
|
1908
|
+
var polytrees;
|
|
1909
|
+
polytrees = [];
|
|
1910
|
+
results.forEach(function(r) {
|
|
1911
|
+
if (r.status === "fulfilled") {
|
|
1912
|
+
return polytrees.push(r.value);
|
|
1913
|
+
}
|
|
1914
|
+
});
|
|
1915
|
+
if (!mainPolytreeUsed) {
|
|
1916
|
+
polytrees.unshift(mainPolytree);
|
|
1917
|
+
}
|
|
1918
|
+
if (polytrees.length > 0) {
|
|
1919
|
+
if (polytrees.length === 1) {
|
|
1920
|
+
return resolve(polytrees[0]);
|
|
1921
|
+
} else if (polytrees.length > 3) {
|
|
1922
|
+
return Polytree.async.subtractArray(polytrees, usingBatches ? 0 : -1).then(function(result) {
|
|
1923
|
+
return resolve(result);
|
|
1924
|
+
}).catch(function(e) {
|
|
1925
|
+
return reject(e);
|
|
1926
|
+
});
|
|
1927
|
+
} else {
|
|
1928
|
+
return Polytree.async.subtract(polytrees[0], polytrees[1]).then(function(result) {
|
|
1929
|
+
if (polytrees.length === 3) {
|
|
1930
|
+
return Polytree.async.subtract(result, polytrees[2]).then(function(result) {
|
|
1931
|
+
return resolve(result);
|
|
1932
|
+
}).catch(function(e) {
|
|
1933
|
+
return reject(e);
|
|
1934
|
+
});
|
|
1935
|
+
} else {
|
|
1936
|
+
return resolve(result);
|
|
1937
|
+
}
|
|
1938
|
+
}).catch(function(e) {
|
|
1939
|
+
return reject(e);
|
|
1940
|
+
});
|
|
1941
|
+
}
|
|
1942
|
+
} else {
|
|
1943
|
+
return reject('Unable to find any result polytree');
|
|
1944
|
+
}
|
|
1945
|
+
});
|
|
1946
|
+
} catch (error) {
|
|
1947
|
+
e = error;
|
|
1948
|
+
return reject(e);
|
|
1949
|
+
}
|
|
1950
|
+
});
|
|
1951
|
+
},
|
|
1952
|
+
intersectArray: function(objArr, materialIndexMax = 2e308) {
|
|
1953
|
+
return new Promise(function(resolve, reject) {
|
|
1954
|
+
var batch, batches, currentIndex, e, hasLeftOver, i, k, l, leftOverPolytree, mainPolytree, mainPolytreeUsed, materialIndex, polytreesArray, promise, promises, ref, ref1, result, tempPolytree, usingBatches;
|
|
1955
|
+
try {
|
|
1956
|
+
usingBatches = Polytree.async.batchSize > 4 && Polytree.async.batchSize < objArr.length;
|
|
1957
|
+
mainPolytree = void 0;
|
|
1958
|
+
mainPolytreeUsed = false;
|
|
1959
|
+
promises = [];
|
|
1960
|
+
if (usingBatches) {
|
|
1961
|
+
batches = [];
|
|
1962
|
+
currentIndex = 0;
|
|
1963
|
+
while (currentIndex < objArr.length) {
|
|
1964
|
+
batches.push(objArr.slice(currentIndex, currentIndex + Polytree.async.batchSize));
|
|
1965
|
+
currentIndex += Polytree.async.batchSize;
|
|
1966
|
+
}
|
|
1967
|
+
batch = batches.shift();
|
|
1968
|
+
while (batch) {
|
|
1969
|
+
promise = Polytree.async.intersectArray(batch, 0);
|
|
1970
|
+
promises.push(promise);
|
|
1971
|
+
batch = batches.shift();
|
|
1972
|
+
}
|
|
1973
|
+
usingBatches = true;
|
|
1974
|
+
mainPolytreeUsed = true;
|
|
1975
|
+
objArr.length = 0;
|
|
1976
|
+
} else {
|
|
1977
|
+
polytreesArray = [];
|
|
1978
|
+
for (i = k = 0, ref = objArr.length; (0 <= ref ? k < ref : k > ref); i = 0 <= ref ? ++k : --k) {
|
|
1979
|
+
materialIndex = i > materialIndexMax ? materialIndexMax : i;
|
|
1980
|
+
tempPolytree = void 0;
|
|
1981
|
+
if (objArr[i].isMesh) {
|
|
1982
|
+
tempPolytree = Polytree.fromMesh(objArr[i], materialIndexMax > -1 ? materialIndex : void 0);
|
|
1983
|
+
} else {
|
|
1984
|
+
tempPolytree = objArr[i];
|
|
1985
|
+
if (materialIndexMax > -1) {
|
|
1986
|
+
tempPolytree.setPolygonIndex(materialIndex);
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
tempPolytree.polytreeIndex = i;
|
|
1990
|
+
polytreesArray.push(tempPolytree);
|
|
1991
|
+
}
|
|
1992
|
+
mainPolytree = polytreesArray.shift();
|
|
1993
|
+
result = void 0;
|
|
1994
|
+
hasLeftOver = false;
|
|
1995
|
+
leftOverPolytree = void 0;
|
|
1996
|
+
for (i = l = 0, ref1 = polytreesArray.length; l < ref1; i = l += 2) {
|
|
1997
|
+
if (i + 1 >= polytreesArray.length) {
|
|
1998
|
+
leftOverPolytree = polytreesArray[i];
|
|
1999
|
+
hasLeftOver = true;
|
|
2000
|
+
break;
|
|
2001
|
+
}
|
|
2002
|
+
promise = Polytree.async.intersect(polytreesArray[i], polytreesArray[i + 1]);
|
|
2003
|
+
promises.push(promise);
|
|
2004
|
+
}
|
|
2005
|
+
if (leftOverPolytree) {
|
|
2006
|
+
promise = Polytree.async.intersect(mainPolytree, leftOverPolytree);
|
|
2007
|
+
promises.push(promise);
|
|
2008
|
+
mainPolytreeUsed = true;
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
return Promise.allSettled(promises).then(function(results) {
|
|
2012
|
+
var polytrees;
|
|
2013
|
+
polytrees = [];
|
|
2014
|
+
results.forEach(function(r) {
|
|
2015
|
+
if (r.status === "fulfilled") {
|
|
2016
|
+
return polytrees.push(r.value);
|
|
2017
|
+
}
|
|
2018
|
+
});
|
|
2019
|
+
if (!mainPolytreeUsed) {
|
|
2020
|
+
polytrees.unshift(mainPolytree);
|
|
2021
|
+
}
|
|
2022
|
+
if (polytrees.length > 0) {
|
|
2023
|
+
if (polytrees.length === 1) {
|
|
2024
|
+
return resolve(polytrees[0]);
|
|
2025
|
+
} else if (polytrees.length > 3) {
|
|
2026
|
+
return Polytree.async.intersectArray(polytrees, usingBatches ? 0 : -1).then(function(result) {
|
|
2027
|
+
return resolve(result);
|
|
2028
|
+
}).catch(function(e) {
|
|
2029
|
+
return reject(e);
|
|
2030
|
+
});
|
|
2031
|
+
} else {
|
|
2032
|
+
return Polytree.async.intersect(polytrees[0], polytrees[1]).then(function(result) {
|
|
2033
|
+
if (polytrees.length === 3) {
|
|
2034
|
+
return Polytree.async.intersect(result, polytrees[2]).then(function(result) {
|
|
2035
|
+
return resolve(result);
|
|
2036
|
+
}).catch(function(e) {
|
|
2037
|
+
return reject(e);
|
|
2038
|
+
});
|
|
2039
|
+
} else {
|
|
2040
|
+
return resolve(result);
|
|
2041
|
+
}
|
|
2042
|
+
}).catch(function(e) {
|
|
2043
|
+
return reject(e);
|
|
2044
|
+
});
|
|
2045
|
+
}
|
|
2046
|
+
} else {
|
|
2047
|
+
return reject('Unable to find any result polytree');
|
|
2048
|
+
}
|
|
2049
|
+
});
|
|
2050
|
+
} catch (error) {
|
|
2051
|
+
e = error;
|
|
2052
|
+
return reject(e);
|
|
2053
|
+
}
|
|
2054
|
+
});
|
|
2055
|
+
},
|
|
2056
|
+
operation: function(obj, returnPolytrees = false, buildTargetPolytree = true, options = {
|
|
2057
|
+
objCounter: 0
|
|
2058
|
+
}, firstRun = true) {
|
|
2059
|
+
return new Promise(function(resolve, reject) {
|
|
2060
|
+
var e, material, polytreeA, polytreeB, promise, promises, resultPolytree;
|
|
2061
|
+
try {
|
|
2062
|
+
polytreeA = void 0;
|
|
2063
|
+
polytreeB = void 0;
|
|
2064
|
+
resultPolytree = void 0;
|
|
2065
|
+
material = void 0;
|
|
2066
|
+
if (obj.material) {
|
|
2067
|
+
material = obj.material;
|
|
2068
|
+
}
|
|
2069
|
+
promises = [];
|
|
2070
|
+
if (obj.objA) {
|
|
2071
|
+
promise = handleObjectForOp_async(obj.objA, returnPolytrees, buildTargetPolytree, options, 0);
|
|
2072
|
+
promises.push(promise);
|
|
2073
|
+
}
|
|
2074
|
+
if (obj.objB) {
|
|
2075
|
+
promise = handleObjectForOp_async(obj.objB, returnPolytrees, buildTargetPolytree, options, 1);
|
|
2076
|
+
promises.push(promise);
|
|
2077
|
+
}
|
|
2078
|
+
return Promise.allSettled(promises).then(function(results) {
|
|
2079
|
+
var polytrees, resultPromise;
|
|
2080
|
+
polytrees = [];
|
|
2081
|
+
results.forEach(function(r) {
|
|
2082
|
+
if (r.status === "fulfilled") {
|
|
2083
|
+
if (r.value.objIndex === 0) {
|
|
2084
|
+
return polytreeA = r.value;
|
|
2085
|
+
} else if (r.value.objIndex === 1) {
|
|
2086
|
+
return polytreeB = r.value;
|
|
2087
|
+
}
|
|
2088
|
+
}
|
|
2089
|
+
});
|
|
2090
|
+
if (returnPolytrees === true) {
|
|
2091
|
+
obj.objA = polytreeA.original;
|
|
2092
|
+
polytreeA = polytreeA.result;
|
|
2093
|
+
obj.objB = polytreeB.original;
|
|
2094
|
+
polytreeB = polytreeB.result;
|
|
2095
|
+
}
|
|
2096
|
+
resultPromise = void 0;
|
|
2097
|
+
switch (obj.op) {
|
|
2098
|
+
case 'union':
|
|
2099
|
+
resultPromise = Polytree.async.union(polytreeA, polytreeB, buildTargetPolytree);
|
|
2100
|
+
break;
|
|
2101
|
+
case 'subtract':
|
|
2102
|
+
resultPromise = Polytree.async.subtract(polytreeA, polytreeB, buildTargetPolytree);
|
|
2103
|
+
break;
|
|
2104
|
+
case 'intersect':
|
|
2105
|
+
resultPromise = Polytree.async.intersect(polytreeA, polytreeB, buildTargetPolytree);
|
|
2106
|
+
}
|
|
2107
|
+
return resultPromise.then(function(resultPolytree) {
|
|
2108
|
+
var mesh;
|
|
2109
|
+
if (firstRun && material) {
|
|
2110
|
+
mesh = Polytree.toMesh(resultPolytree, material);
|
|
2111
|
+
if (!returnPolytrees) {
|
|
2112
|
+
disposePolytree(resultPolytree);
|
|
2113
|
+
}
|
|
2114
|
+
resolve(returnPolytrees ? {
|
|
2115
|
+
result: mesh,
|
|
2116
|
+
operationTree: obj
|
|
2117
|
+
} : mesh);
|
|
2118
|
+
} else if (firstRun && returnPolytrees) {
|
|
2119
|
+
resolve({
|
|
2120
|
+
result: resultPolytree,
|
|
2121
|
+
operationTree: obj
|
|
2122
|
+
});
|
|
2123
|
+
} else {
|
|
2124
|
+
resolve(resultPolytree);
|
|
2125
|
+
}
|
|
2126
|
+
if (!returnPolytrees) {
|
|
2127
|
+
return disposePolytree(polytreeA, polytreeB);
|
|
2128
|
+
}
|
|
2129
|
+
}).catch(function(e) {
|
|
2130
|
+
return reject(e);
|
|
2131
|
+
});
|
|
2132
|
+
});
|
|
2133
|
+
} catch (error) {
|
|
2134
|
+
e = error;
|
|
2135
|
+
return reject(e);
|
|
2136
|
+
}
|
|
2137
|
+
});
|
|
2138
|
+
}
|
|
2139
|
+
};
|
|
2140
|
+
|
|
2141
|
+
Polytree.unionArray = function(objArr, materialIndexMax = 2e308) {
|
|
2142
|
+
var i, k, materialIndex, polytreeA, polytreeB, polytreesArray, ref, resultPolytree, tempPolytree;
|
|
2143
|
+
polytreesArray = [];
|
|
2144
|
+
for (i = k = 0, ref = objArr.length; (0 <= ref ? k < ref : k > ref); i = 0 <= ref ? ++k : --k) {
|
|
2145
|
+
materialIndex = i > materialIndexMax ? materialIndexMax : i;
|
|
2146
|
+
tempPolytree = void 0;
|
|
2147
|
+
if (objArr[i].isMesh) {
|
|
2148
|
+
tempPolytree = Polytree.fromMesh(objArr[i], materialIndex);
|
|
2149
|
+
} else {
|
|
2150
|
+
tempPolytree = objArr[i];
|
|
2151
|
+
tempPolytree.setPolygonIndex(materialIndex);
|
|
2152
|
+
}
|
|
2153
|
+
tempPolytree.polytreeIndex = i;
|
|
2154
|
+
polytreesArray.push(tempPolytree);
|
|
2155
|
+
}
|
|
2156
|
+
polytreeA = polytreesArray.shift();
|
|
2157
|
+
polytreeB = polytreesArray.shift();
|
|
2158
|
+
while (polytreeA && polytreeB) {
|
|
2159
|
+
resultPolytree = Polytree.union(polytreeA, polytreeB);
|
|
2160
|
+
disposePolytree(polytreeA, polytreeB);
|
|
2161
|
+
polytreeA = resultPolytree;
|
|
2162
|
+
polytreeB = polytreesArray.shift();
|
|
2163
|
+
}
|
|
2164
|
+
return polytreeA;
|
|
2165
|
+
};
|
|
2166
|
+
|
|
2167
|
+
Polytree.subtractArray = function(objArr, materialIndexMax = 2e308) {
|
|
2168
|
+
var i, k, materialIndex, polytreeA, polytreeB, polytreesArray, ref, resultPolytree, tempPolytree;
|
|
2169
|
+
polytreesArray = [];
|
|
2170
|
+
for (i = k = 0, ref = objArr.length; (0 <= ref ? k < ref : k > ref); i = 0 <= ref ? ++k : --k) {
|
|
2171
|
+
materialIndex = i > materialIndexMax ? materialIndexMax : i;
|
|
2172
|
+
tempPolytree = void 0;
|
|
2173
|
+
if (objArr[i].isMesh) {
|
|
2174
|
+
tempPolytree = Polytree.fromMesh(objArr[i], materialIndex);
|
|
2175
|
+
} else {
|
|
2176
|
+
tempPolytree = objArr[i];
|
|
2177
|
+
tempPolytree.setPolygonIndex(materialIndex);
|
|
2178
|
+
}
|
|
2179
|
+
tempPolytree.polytreeIndex = i;
|
|
2180
|
+
polytreesArray.push(tempPolytree);
|
|
2181
|
+
}
|
|
2182
|
+
polytreeA = polytreesArray.shift();
|
|
2183
|
+
polytreeB = polytreesArray.shift();
|
|
2184
|
+
while (polytreeA && polytreeB) {
|
|
2185
|
+
resultPolytree = Polytree.subtract(polytreeA, polytreeB);
|
|
2186
|
+
disposePolytree(polytreeA, polytreeB);
|
|
2187
|
+
polytreeA = resultPolytree;
|
|
2188
|
+
polytreeB = polytreesArray.shift();
|
|
2189
|
+
}
|
|
2190
|
+
return polytreeA;
|
|
2191
|
+
};
|
|
2192
|
+
|
|
2193
|
+
Polytree.intersectArray = function(objArr, materialIndexMax = 2e308) {
|
|
2194
|
+
var i, k, materialIndex, polytreeA, polytreeB, polytreesArray, ref, resultPolytree, tempPolytree;
|
|
2195
|
+
polytreesArray = [];
|
|
2196
|
+
for (i = k = 0, ref = objArr.length; (0 <= ref ? k < ref : k > ref); i = 0 <= ref ? ++k : --k) {
|
|
2197
|
+
materialIndex = i > materialIndexMax ? materialIndexMax : i;
|
|
2198
|
+
tempPolytree = void 0;
|
|
2199
|
+
if (objArr[i].isMesh) {
|
|
2200
|
+
tempPolytree = Polytree.fromMesh(objArr[i], materialIndex);
|
|
2201
|
+
} else {
|
|
2202
|
+
tempPolytree = objArr[i];
|
|
2203
|
+
tempPolytree.setPolygonIndex(materialIndex);
|
|
2204
|
+
}
|
|
2205
|
+
tempPolytree.polytreeIndex = i;
|
|
2206
|
+
polytreesArray.push(tempPolytree);
|
|
2207
|
+
}
|
|
2208
|
+
polytreeA = polytreesArray.shift();
|
|
2209
|
+
polytreeB = polytreesArray.shift();
|
|
2210
|
+
while (polytreeA && polytreeB) {
|
|
2211
|
+
resultPolytree = Polytree.intersect(polytreeA, polytreeB);
|
|
2212
|
+
disposePolytree(polytreeA, polytreeB);
|
|
2213
|
+
polytreeA = resultPolytree;
|
|
2214
|
+
polytreeB = polytreesArray.shift();
|
|
2215
|
+
}
|
|
2216
|
+
return polytreeA;
|
|
2217
|
+
};
|
|
2218
|
+
|
|
2219
|
+
Polytree.operation = function(obj, returnPolytrees = false, buildTargetPolytree = true, options = {
|
|
2220
|
+
objCounter: 0
|
|
2221
|
+
}, firstRun = true) {
|
|
2222
|
+
var material, mesh, polytreeA, polytreeB, resultPolytree;
|
|
2223
|
+
polytreeA = void 0;
|
|
2224
|
+
polytreeB = void 0;
|
|
2225
|
+
resultPolytree = void 0;
|
|
2226
|
+
material = void 0;
|
|
2227
|
+
if (obj.material) {
|
|
2228
|
+
material = obj.material;
|
|
2229
|
+
}
|
|
2230
|
+
if (obj.objA) {
|
|
2231
|
+
polytreeA = handleObjectForOp(obj.objA, returnPolytrees, buildTargetPolytree, options);
|
|
2232
|
+
if (returnPolytrees === true) {
|
|
2233
|
+
obj.objA = polytreeA.original;
|
|
2234
|
+
polytreeA = polytreeA.result;
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
if (obj.objB) {
|
|
2238
|
+
polytreeB = handleObjectForOp(obj.objB, returnPolytrees, buildTargetPolytree, options);
|
|
2239
|
+
if (returnPolytrees === true) {
|
|
2240
|
+
obj.objB = polytreeB.original;
|
|
2241
|
+
polytreeB = polytreeB.result;
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
switch (obj.op) {
|
|
2245
|
+
case 'union':
|
|
2246
|
+
resultPolytree = Polytree.union(polytreeA, polytreeB, buildTargetPolytree);
|
|
2247
|
+
break;
|
|
2248
|
+
case 'subtract':
|
|
2249
|
+
resultPolytree = Polytree.subtract(polytreeA, polytreeB, buildTargetPolytree);
|
|
2250
|
+
break;
|
|
2251
|
+
case 'intersect':
|
|
2252
|
+
resultPolytree = Polytree.intersect(polytreeA, polytreeB, buildTargetPolytree);
|
|
2253
|
+
}
|
|
2254
|
+
if (!returnPolytrees) {
|
|
2255
|
+
disposePolytree(polytreeA, polytreeB);
|
|
2256
|
+
}
|
|
2257
|
+
if (firstRun && material) {
|
|
2258
|
+
mesh = Polytree.toMesh(resultPolytree, material);
|
|
2259
|
+
disposePolytree(resultPolytree);
|
|
2260
|
+
if (returnPolytrees) {
|
|
2261
|
+
return {
|
|
2262
|
+
result: mesh,
|
|
2263
|
+
operationTree: obj
|
|
2264
|
+
};
|
|
2265
|
+
} else {
|
|
2266
|
+
return mesh;
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
if (firstRun && returnPolytrees) {
|
|
2270
|
+
return {
|
|
2271
|
+
result: resultPolytree,
|
|
2272
|
+
operationTree: obj
|
|
2273
|
+
};
|
|
2274
|
+
}
|
|
2275
|
+
return resultPolytree;
|
|
2276
|
+
};
|
|
2277
|
+
|
|
2278
|
+
handleObjectForOp = function(obj, returnPolytrees, buildTargetPolytree, options) {
|
|
2279
|
+
var returnObj;
|
|
2280
|
+
returnObj = void 0;
|
|
2281
|
+
if (obj.isMesh) {
|
|
2282
|
+
returnObj = Polytree.fromMesh(obj, options.objCounter++);
|
|
2283
|
+
if (returnPolytrees) {
|
|
2284
|
+
returnObj = {
|
|
2285
|
+
result: returnObj,
|
|
2286
|
+
original: returnObj.clone()
|
|
2287
|
+
};
|
|
2288
|
+
}
|
|
2289
|
+
} else if (obj.isPolytree) {
|
|
2290
|
+
returnObj = obj;
|
|
2291
|
+
if (returnPolytrees) {
|
|
2292
|
+
returnObj = {
|
|
2293
|
+
result: obj,
|
|
2294
|
+
original: obj.clone()
|
|
2295
|
+
};
|
|
2296
|
+
}
|
|
2297
|
+
} else if (obj.op) {
|
|
2298
|
+
returnObj = Polytree.operation(obj, returnPolytrees, buildTargetPolytree, options, false);
|
|
2299
|
+
if (returnPolytrees) {
|
|
2300
|
+
returnObj = {
|
|
2301
|
+
result: returnObj,
|
|
2302
|
+
original: obj
|
|
2303
|
+
};
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
return returnObj;
|
|
2307
|
+
};
|
|
2308
|
+
|
|
2309
|
+
handleObjectForOp_async = function(obj, returnPolytrees, buildTargetPolytree, options, objIndex) {
|
|
2310
|
+
return new Promise(function(resolve, reject) {
|
|
2311
|
+
var e, returnObj;
|
|
2312
|
+
try {
|
|
2313
|
+
returnObj = void 0;
|
|
2314
|
+
if (obj.isMesh) {
|
|
2315
|
+
returnObj = Polytree.fromMesh(obj, options.objCounter++);
|
|
2316
|
+
if (returnPolytrees) {
|
|
2317
|
+
returnObj = {
|
|
2318
|
+
result: returnObj,
|
|
2319
|
+
original: returnObj.clone()
|
|
2320
|
+
};
|
|
2321
|
+
}
|
|
2322
|
+
returnObj.objIndex = objIndex;
|
|
2323
|
+
return resolve(returnObj);
|
|
2324
|
+
} else if (obj.isPolytree) {
|
|
2325
|
+
returnObj = obj;
|
|
2326
|
+
if (returnPolytrees) {
|
|
2327
|
+
returnObj = {
|
|
2328
|
+
result: obj,
|
|
2329
|
+
original: obj.clone()
|
|
2330
|
+
};
|
|
2331
|
+
}
|
|
2332
|
+
returnObj.objIndex = objIndex;
|
|
2333
|
+
return resolve(returnObj);
|
|
2334
|
+
} else if (obj.op) {
|
|
2335
|
+
return Polytree.async.operation(obj, returnPolytrees, buildTargetPolytree, options, false).then(function(returnObj) {
|
|
2336
|
+
if (returnPolytrees) {
|
|
2337
|
+
returnObj = {
|
|
2338
|
+
result: returnObj,
|
|
2339
|
+
original: obj
|
|
2340
|
+
};
|
|
2341
|
+
}
|
|
2342
|
+
returnObj.objIndex = objIndex;
|
|
2343
|
+
return resolve(returnObj);
|
|
2344
|
+
});
|
|
2345
|
+
}
|
|
2346
|
+
} catch (error) {
|
|
2347
|
+
e = error;
|
|
2348
|
+
return reject(e);
|
|
2349
|
+
}
|
|
2350
|
+
});
|
|
2351
|
+
};
|
|
2352
|
+
|
|
2353
|
+
isUniqueTriangle = function(triangle, set, map) {
|
|
2354
|
+
var hash1;
|
|
2355
|
+
hash1 = `{${triangle.a.x},${triangle.a.y},${triangle.a.z}}-{${triangle.b.x},${triangle.b.y},${triangle.b.z}}-{${triangle.c.x},${triangle.c.y},${triangle.c.z}}`;
|
|
2356
|
+
if (set.has(hash1) === true) {
|
|
2357
|
+
return false;
|
|
2358
|
+
} else {
|
|
2359
|
+
set.add(hash1);
|
|
2360
|
+
if (map) {
|
|
2361
|
+
map.set(triangle, triangle);
|
|
2362
|
+
}
|
|
2363
|
+
return true;
|
|
2364
|
+
}
|
|
2365
|
+
};
|
|
2366
|
+
|
|
2367
|
+
nbuf3 = function(ct) {
|
|
2368
|
+
return {
|
|
2369
|
+
top: 0,
|
|
2370
|
+
array: new Float32Array(ct),
|
|
2371
|
+
write: function(v) {
|
|
2372
|
+
this.array[this.top++] = v.x;
|
|
2373
|
+
this.array[this.top++] = v.y;
|
|
2374
|
+
return this.array[this.top++] = v.z;
|
|
2375
|
+
}
|
|
2376
|
+
};
|
|
2377
|
+
};
|
|
2378
|
+
|
|
2379
|
+
nbuf2 = function(ct) {
|
|
2380
|
+
return {
|
|
2381
|
+
top: 0,
|
|
2382
|
+
array: new Float32Array(ct),
|
|
2383
|
+
write: function(v) {
|
|
2384
|
+
this.array[this.top++] = v.x;
|
|
2385
|
+
return this.array[this.top++] = v.y;
|
|
2386
|
+
}
|
|
2387
|
+
};
|
|
2388
|
+
};
|
|
2389
|
+
|
|
2390
|
+
_normal1 = new Vector3();
|
|
2391
|
+
|
|
2392
|
+
tmpm3 = new Matrix3();
|
|
2393
|
+
|
|
2394
|
+
ttvv0 = new Vector3();
|
|
2395
|
+
|
|
2396
|
+
Polytree.toGeometry = function(polytree) {
|
|
2397
|
+
var colors, defaultGroup, geometry, groupBase, groups, i, index, k, l, len1, m, normals, polygon, polygons, positions, ref, ref1, triangleCount, uvs, vertices, verticesLen;
|
|
2398
|
+
polygons = polytree.getPolygons();
|
|
2399
|
+
triangleCount = polygons.length;
|
|
2400
|
+
// let validPolygons = [];
|
|
2401
|
+
// let trianglesSet = new Set();
|
|
2402
|
+
// let duplicateCount = 0;
|
|
2403
|
+
|
|
2404
|
+
// let triangleCount = 0;
|
|
2405
|
+
// polygons.forEach(polygon => {
|
|
2406
|
+
// if (isUniqueTriangle(polygon.triangle, trianglesSet)) {
|
|
2407
|
+
// triangleCount += (polygon.vertices.length - 2);
|
|
2408
|
+
// validPolygons.push(polygon);
|
|
2409
|
+
// }
|
|
2410
|
+
// });
|
|
2411
|
+
|
|
2412
|
+
// trianglesSet.clear();
|
|
2413
|
+
// trianglesSet = undefined;
|
|
2414
|
+
positions = nbuf3(triangleCount * 3 * 3);
|
|
2415
|
+
normals = nbuf3(triangleCount * 3 * 3);
|
|
2416
|
+
uvs = void 0;
|
|
2417
|
+
colors = void 0;
|
|
2418
|
+
groups = [];
|
|
2419
|
+
defaultGroup = [];
|
|
2420
|
+
for (k = 0, len1 = polygons.length; k < len1; k++) {
|
|
2421
|
+
polygon = polygons[k];
|
|
2422
|
+
vertices = polygon.vertices;
|
|
2423
|
+
verticesLen = vertices.length;
|
|
2424
|
+
if (polygon.shared !== void 0) {
|
|
2425
|
+
if (!groups[polygon.shared]) {
|
|
2426
|
+
groups[polygon.shared] = [];
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
if (verticesLen > 0) {
|
|
2430
|
+
if (vertices[0].uv !== void 0) {
|
|
2431
|
+
uvs || (uvs = nbuf2(triangleCount * 2 * 3));
|
|
2432
|
+
}
|
|
2433
|
+
if (vertices[0].color !== void 0) {
|
|
2434
|
+
colors || (colors = nbuf3(triangleCount * 3 * 3));
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2437
|
+
for (i = l = 3, ref = verticesLen; (3 <= ref ? l <= ref : l >= ref); i = 3 <= ref ? ++l : --l) {
|
|
2438
|
+
(polygon.shared === void 0 ? defaultGroup : groups[polygon.shared]).push(positions.top / 3, (positions.top / 3) + 1, (positions.top / 3) + 2);
|
|
2439
|
+
positions.write(vertices[0].pos);
|
|
2440
|
+
positions.write(vertices[i - 2].pos);
|
|
2441
|
+
positions.write(vertices[i - 1].pos);
|
|
2442
|
+
normals.write(vertices[0].normal);
|
|
2443
|
+
normals.write(vertices[i - 2].normal);
|
|
2444
|
+
normals.write(vertices[i - 1].normal);
|
|
2445
|
+
if (uvs != null) {
|
|
2446
|
+
uvs.write(vertices[0].uv);
|
|
2447
|
+
uvs.write(vertices[i - 2].uv);
|
|
2448
|
+
uvs.write(vertices[i - 1].uv);
|
|
2449
|
+
}
|
|
2450
|
+
if (colors != null) {
|
|
2451
|
+
colors.write(vertices[0].color);
|
|
2452
|
+
colors.write(vertices[i - 2].color);
|
|
2453
|
+
colors.write(vertices[i - 1].color);
|
|
2454
|
+
}
|
|
2455
|
+
}
|
|
2456
|
+
}
|
|
2457
|
+
geometry = new BufferGeometry();
|
|
2458
|
+
geometry.setAttribute('position', new BufferAttribute(positions.array, 3));
|
|
2459
|
+
geometry.setAttribute('normal', new BufferAttribute(normals.array, 3));
|
|
2460
|
+
uvs && geometry.setAttribute('uv', new BufferAttribute(uvs.array, 2));
|
|
2461
|
+
colors && geometry.setAttribute('color', new BufferAttribute(colors.array, 3));
|
|
2462
|
+
if (groups.length > 0) {
|
|
2463
|
+
index = [];
|
|
2464
|
+
groupBase = 0;
|
|
2465
|
+
for (i = m = 0, ref1 = groups.length; (0 <= ref1 ? m < ref1 : m > ref1); i = 0 <= ref1 ? ++m : --m) {
|
|
2466
|
+
groups[i] = groups[i] || [];
|
|
2467
|
+
geometry.addGroup(groupBase, groups[i].length, i);
|
|
2468
|
+
groupBase += groups[i].length;
|
|
2469
|
+
index = index.concat(groups[i]);
|
|
2470
|
+
}
|
|
2471
|
+
if (defaultGroup.length) {
|
|
2472
|
+
geometry.addGroup(groupBase, defaultGroup.length, groups.length);
|
|
2473
|
+
index = index.concat(defaultGroup);
|
|
2474
|
+
}
|
|
2475
|
+
geometry.setIndex(index);
|
|
2476
|
+
}
|
|
2477
|
+
return geometry;
|
|
2478
|
+
};
|
|
2479
|
+
|
|
2480
|
+
Polytree.toMesh = function(polytree, toMaterial) {
|
|
2481
|
+
var geometry;
|
|
2482
|
+
geometry = Polytree.toGeometry(polytree);
|
|
2483
|
+
return new Mesh(geometry, toMaterial);
|
|
2484
|
+
};
|
|
2485
|
+
|
|
2486
|
+
Polytree.fromMesh = function(obj, objectIndex, polytree = new Polytree(), buildTargetPolytree = true) {
|
|
2487
|
+
var color, colorattr, geometry, group, groups, i, index, j, k, l, len1, m, n, normal, normalattr, polygon, polys, pos, posattr, positionIndex, ref, ref1, uvCoords, uvIndex, uvattr, vertexIndex, vertices;
|
|
2488
|
+
if (obj.isPolytree) {
|
|
2489
|
+
return obj;
|
|
2490
|
+
}
|
|
2491
|
+
if (Polytree.rayIntersectTriangleType === "regular") {
|
|
2492
|
+
polytree.originalMatrixWorld = obj.matrixWorld.clone();
|
|
2493
|
+
}
|
|
2494
|
+
obj.updateWorldMatrix(true, true);
|
|
2495
|
+
geometry = obj.geometry;
|
|
2496
|
+
tmpm3.getNormalMatrix(obj.matrix);
|
|
2497
|
+
posattr = geometry.attributes.position;
|
|
2498
|
+
normalattr = geometry.attributes.normal;
|
|
2499
|
+
uvattr = geometry.attributes.uv;
|
|
2500
|
+
colorattr = geometry.attributes.color;
|
|
2501
|
+
groups = geometry.groups;
|
|
2502
|
+
index = geometry.index ? geometry.index.array : Array((posattr.array.length / posattr.itemSize) | 0).fill().map(function(_, i) {
|
|
2503
|
+
return i;
|
|
2504
|
+
});
|
|
2505
|
+
polys = [];
|
|
2506
|
+
for (i = k = 0, ref = index.length; k < ref; i = k += 3) {
|
|
2507
|
+
vertices = [];
|
|
2508
|
+
for (j = l = 0; l < 3; j = ++l) {
|
|
2509
|
+
vertexIndex = index[i + j];
|
|
2510
|
+
positionIndex = vertexIndex * 3;
|
|
2511
|
+
uvIndex = vertexIndex * 2;
|
|
2512
|
+
pos = new Vector3(posattr.array[positionIndex], posattr.array[positionIndex + 1], posattr.array[positionIndex + 2]);
|
|
2513
|
+
normal = new Vector3(normalattr.array[positionIndex], normalattr.array[positionIndex + 1], normalattr.array[positionIndex + 2]);
|
|
2514
|
+
pos.applyMatrix4(obj.matrix);
|
|
2515
|
+
normal.applyMatrix3(tmpm3);
|
|
2516
|
+
uvCoords = uvattr ? {
|
|
2517
|
+
x: uvattr.array[uvIndex],
|
|
2518
|
+
y: uvattr.array[uvIndex + 1]
|
|
2519
|
+
} : void 0;
|
|
2520
|
+
color = colorattr ? {
|
|
2521
|
+
x: colorattr.array[uvIndex],
|
|
2522
|
+
y: colorattr.array[uvIndex + 1],
|
|
2523
|
+
z: colorattr.array[uvIndex + 2]
|
|
2524
|
+
} : void 0;
|
|
2525
|
+
vertices.push(new Vertex(pos, normal, uvCoords, color));
|
|
2526
|
+
}
|
|
2527
|
+
if ((objectIndex === void 0) && groups && groups.length > 0) {
|
|
2528
|
+
polygon = void 0;
|
|
2529
|
+
for (m = 0, len1 = groups.length; m < len1; m++) {
|
|
2530
|
+
group = groups[m];
|
|
2531
|
+
if ((index[i] >= group.start) && (index[i] < (group.start + group.count))) {
|
|
2532
|
+
polygon = new Polygon(vertices, group.materialIndex);
|
|
2533
|
+
polygon.originalValid = true;
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2536
|
+
if (polygon) {
|
|
2537
|
+
polys.push(polygon);
|
|
2538
|
+
}
|
|
2539
|
+
} else {
|
|
2540
|
+
polygon = new Polygon(vertices, objectIndex);
|
|
2541
|
+
polygon.originalValid = true;
|
|
2542
|
+
polys.push(polygon);
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
for (i = n = 0, ref1 = polys.length; (0 <= ref1 ? n < ref1 : n > ref1); i = 0 <= ref1 ? ++n : --n) {
|
|
2546
|
+
if (isValidTriangle(polys[i].triangle)) {
|
|
2547
|
+
polytree.addPolygon(polys[i]);
|
|
2548
|
+
} else {
|
|
2549
|
+
polys[i].delete();
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
buildTargetPolytree && polytree.buildTree();
|
|
2553
|
+
if (Polytree.usePolytreeRay !== true) {
|
|
2554
|
+
polytree.mesh = obj;
|
|
2555
|
+
}
|
|
2556
|
+
return polytree;
|
|
2557
|
+
};
|
|
2558
|
+
|
|
2559
|
+
isValidTriangle = function(triangle) {
|
|
2560
|
+
if (triangle.a.equals(triangle.b)) {
|
|
2561
|
+
return false;
|
|
2562
|
+
}
|
|
2563
|
+
if (triangle.a.equals(triangle.c)) {
|
|
2564
|
+
return false;
|
|
2565
|
+
}
|
|
2566
|
+
if (triangle.b.equals(triangle.c)) {
|
|
2567
|
+
return false;
|
|
2568
|
+
}
|
|
2569
|
+
return true;
|
|
2570
|
+
};
|
|
2571
|
+
|
|
2572
|
+
// class Vertex
|
|
2573
|
+
Vertex = class Vertex {
|
|
2574
|
+
constructor(pos, normal, uv, color) {
|
|
2575
|
+
this.pos = new Vector3().copy(pos);
|
|
2576
|
+
this.normal = new Vector3().copy(normal);
|
|
2577
|
+
uv && (this.uv = new Vector2().copy(uv));
|
|
2578
|
+
color && (this.color = new Vector3().copy(color));
|
|
2579
|
+
}
|
|
2580
|
+
|
|
2581
|
+
clone() {
|
|
2582
|
+
return new Vertex(this.pos.clone(), this.normal.clone(), this.uv && this.uv.clone(), this.color && this.color.clone());
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2585
|
+
flip() {
|
|
2586
|
+
return this.normal.negate();
|
|
2587
|
+
}
|
|
2588
|
+
|
|
2589
|
+
delete() {
|
|
2590
|
+
this.pos = void 0;
|
|
2591
|
+
this.normal = void 0;
|
|
2592
|
+
this.uv && (this.uv = void 0);
|
|
2593
|
+
return this.color && (this.color = void 0);
|
|
2594
|
+
}
|
|
2595
|
+
|
|
2596
|
+
interpolate(other, t) {
|
|
2597
|
+
return new Vertex(this.pos.clone().lerp(other.pos, t), this.normal.clone().lerp(other.normal, t), this.uv && other.uv && this.uv.clone().lerp(other.uv, t), this.color && other.color && this.color.clone().lerp(other.color, t));
|
|
2598
|
+
}
|
|
2599
|
+
|
|
2600
|
+
};
|
|
2601
|
+
|
|
2602
|
+
// class Plane
|
|
2603
|
+
Plane = class Plane {
|
|
2604
|
+
constructor(normal, w) {
|
|
2605
|
+
this.normal = normal;
|
|
2606
|
+
this.w = w;
|
|
2607
|
+
}
|
|
2608
|
+
|
|
2609
|
+
clone() {
|
|
2610
|
+
return new Plane(this.normal.clone(), this.w);
|
|
2611
|
+
}
|
|
2612
|
+
|
|
2613
|
+
flip() {
|
|
2614
|
+
this.normal.negate();
|
|
2615
|
+
return this.w = -this.w;
|
|
2616
|
+
}
|
|
2617
|
+
|
|
2618
|
+
delete() {
|
|
2619
|
+
this.normal = void 0;
|
|
2620
|
+
return this.w = void 0;
|
|
2621
|
+
}
|
|
2622
|
+
|
|
2623
|
+
equals(p) {
|
|
2624
|
+
return this.normal.equals(p.normal) && this.w === p.w;
|
|
2625
|
+
}
|
|
2626
|
+
|
|
2627
|
+
};
|
|
2628
|
+
|
|
2629
|
+
Plane.fromPoints = function(a, b, c) {
|
|
2630
|
+
var planeNormal;
|
|
2631
|
+
planeNormal = triangleVertex0.copy(b).sub(a).cross(triangleVertex1.copy(c).sub(a)).normalize().clone();
|
|
2632
|
+
return new Plane(planeNormal, planeNormal.dot(a));
|
|
2633
|
+
};
|
|
2634
|
+
|
|
2635
|
+
// class Polygon
|
|
2636
|
+
Polygon = class Polygon {
|
|
2637
|
+
constructor(vertices, shared) {
|
|
2638
|
+
this.id = _polygonID++;
|
|
2639
|
+
this.vertices = vertices.map(function(v) {
|
|
2640
|
+
return v.clone();
|
|
2641
|
+
});
|
|
2642
|
+
this.shared = shared;
|
|
2643
|
+
this.plane = Plane.fromPoints(this.vertices[0].pos, this.vertices[1].pos, this.vertices[2].pos);
|
|
2644
|
+
this.triangle = new Triangle(this.vertices[0].pos, this.vertices[1].pos, this.vertices[2].pos);
|
|
2645
|
+
this.intersects = false;
|
|
2646
|
+
this.state = "undecided";
|
|
2647
|
+
this.previousState = "undecided";
|
|
2648
|
+
this.previousStates = [];
|
|
2649
|
+
this.valid = true;
|
|
2650
|
+
this.coplanar = false;
|
|
2651
|
+
this.originalValid = false;
|
|
2652
|
+
this.newPolygon = false;
|
|
2653
|
+
}
|
|
2654
|
+
|
|
2655
|
+
getMidpoint() {
|
|
2656
|
+
if (this.triangle.midPoint) {
|
|
2657
|
+
return this.triangle.midPoint;
|
|
2658
|
+
} else {
|
|
2659
|
+
return this.triangle.midPoint = this.triangle.getMidpoint(new Vector3());
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
|
|
2663
|
+
applyMatrix(matrix, normalMatrix) {
|
|
2664
|
+
normalMatrix = normalMatrix || tmpm3.getNormalMatrix(matrix);
|
|
2665
|
+
this.vertices.forEach(function(v) {
|
|
2666
|
+
v.pos.applyMatrix4(matrix);
|
|
2667
|
+
return v.normal.applyMatrix3(normalMatrix);
|
|
2668
|
+
});
|
|
2669
|
+
this.plane.delete();
|
|
2670
|
+
this.plane = Plane.fromPoints(this.vertices[0].pos, this.vertices[1].pos, this.vertices[2].pos);
|
|
2671
|
+
this.triangle.set(this.vertices[0].pos, this.vertices[1].pos, this.vertices[2].pos);
|
|
2672
|
+
if (this.triangle.midPoint) {
|
|
2673
|
+
return this.triangle.getMidpoint(this.triangle.midPoint);
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
|
|
2677
|
+
reset(resetOriginal = true) {
|
|
2678
|
+
this.intersects = false;
|
|
2679
|
+
this.state = "undecided";
|
|
2680
|
+
this.previousState = "undecided";
|
|
2681
|
+
this.previousStates.length = 0;
|
|
2682
|
+
this.valid = true;
|
|
2683
|
+
this.coplanar = false;
|
|
2684
|
+
resetOriginal && (this.originalValid = false);
|
|
2685
|
+
return this.newPolygon = false;
|
|
2686
|
+
}
|
|
2687
|
+
|
|
2688
|
+
setState(state, keepState) {
|
|
2689
|
+
if (this.state === keepState) {
|
|
2690
|
+
return;
|
|
2691
|
+
}
|
|
2692
|
+
this.previousState = this.state;
|
|
2693
|
+
this.state !== "undecided" && this.previousStates.push(this.state);
|
|
2694
|
+
return this.state = state;
|
|
2695
|
+
}
|
|
2696
|
+
|
|
2697
|
+
checkAllStates(state) {
|
|
2698
|
+
var k, len1, ref, s;
|
|
2699
|
+
if ((this.state !== state) || ((this.previousState !== state) && (this.previousState !== "undecided"))) {
|
|
2700
|
+
return false;
|
|
2701
|
+
}
|
|
2702
|
+
ref = this.previousStates;
|
|
2703
|
+
for (k = 0, len1 = ref.length; k < len1; k++) {
|
|
2704
|
+
s = ref[k];
|
|
2705
|
+
if (s !== state) {
|
|
2706
|
+
return false;
|
|
2707
|
+
}
|
|
2708
|
+
}
|
|
2709
|
+
return true;
|
|
2710
|
+
}
|
|
2711
|
+
|
|
2712
|
+
setInvalid() {
|
|
2713
|
+
return this.valid = false;
|
|
2714
|
+
}
|
|
2715
|
+
|
|
2716
|
+
setValid() {
|
|
2717
|
+
return this.valid = true;
|
|
2718
|
+
}
|
|
2719
|
+
|
|
2720
|
+
clone() {
|
|
2721
|
+
var polygon;
|
|
2722
|
+
polygon = new Polygon(this.vertices.map(function(v) {
|
|
2723
|
+
return v.clone();
|
|
2724
|
+
}), this.shared);
|
|
2725
|
+
polygon.intersects = this.intersects;
|
|
2726
|
+
polygon.valid = this.valid;
|
|
2727
|
+
polygon.coplanar = this.coplanar;
|
|
2728
|
+
polygon.state = this.state;
|
|
2729
|
+
polygon.originalValid = this.originalValid;
|
|
2730
|
+
polygon.newPolygon = this.newPolygon;
|
|
2731
|
+
polygon.previousState = this.previousState;
|
|
2732
|
+
polygon.previousStates = this.previousStates.slice();
|
|
2733
|
+
if (this.triangle.midPoint) {
|
|
2734
|
+
polygon.triangle.midPoint = this.triangle.midPoint.clone();
|
|
2735
|
+
}
|
|
2736
|
+
return polygon;
|
|
2737
|
+
}
|
|
2738
|
+
|
|
2739
|
+
flip() {
|
|
2740
|
+
var tmp;
|
|
2741
|
+
this.vertices.reverse().forEach(function(v) {
|
|
2742
|
+
return v.flip();
|
|
2743
|
+
});
|
|
2744
|
+
tmp = this.triangle.a;
|
|
2745
|
+
this.triangle.a = this.triangle.c;
|
|
2746
|
+
this.triangle.c = tmp;
|
|
2747
|
+
return this.plane.flip();
|
|
2748
|
+
}
|
|
2749
|
+
|
|
2750
|
+
delete() {
|
|
2751
|
+
this.vertices.forEach(function(v) {
|
|
2752
|
+
return v.delete();
|
|
2753
|
+
});
|
|
2754
|
+
this.vertices.length = 0;
|
|
2755
|
+
if (this.plane) {
|
|
2756
|
+
this.plane.delete();
|
|
2757
|
+
this.plane = void 0;
|
|
2758
|
+
}
|
|
2759
|
+
this.triangle = void 0;
|
|
2760
|
+
this.shared = void 0;
|
|
2761
|
+
return this.setInvalid();
|
|
2762
|
+
}
|
|
2763
|
+
|
|
2764
|
+
};
|
|
2765
|
+
|
|
2766
|
+
disposePolytree = function(...polytrees) {
|
|
2767
|
+
if (Polytree.disposePolytree) {
|
|
2768
|
+
return polytrees.forEach(function(polytree) {
|
|
2769
|
+
return polytree.delete();
|
|
2770
|
+
});
|
|
2771
|
+
}
|
|
2772
|
+
};
|
|
2773
|
+
|
|
2774
|
+
// Winding Number algorithm adapted from https://github.com/grame-cncm/faust/blob/master-dev/tools/physicalModeling/mesh2faust/vega/libraries/windingNumber/windingNumber.cpp
|
|
2775
|
+
_wV1 = new Vector3();
|
|
2776
|
+
|
|
2777
|
+
_wV2 = new Vector3();
|
|
2778
|
+
|
|
2779
|
+
_wV3 = new Vector3();
|
|
2780
|
+
|
|
2781
|
+
_wP = new Vector3();
|
|
2782
|
+
|
|
2783
|
+
_wP_EPS_ARR = [new Vector3(EPSILON, 0, 0), new Vector3(0, EPSILON, 0), new Vector3(0, 0, EPSILON), new Vector3(-EPSILON, 0, 0), new Vector3(0, -EPSILON, 0), new Vector3(0, 0, -EPSILON)];
|
|
2784
|
+
|
|
2785
|
+
_wP_EPS_ARR_COUNT = _wP_EPS_ARR.length;
|
|
2786
|
+
|
|
2787
|
+
_matrix3 = new Matrix3();
|
|
2788
|
+
|
|
2789
|
+
wNPI = 4 * Math.PI;
|
|
2790
|
+
|
|
2791
|
+
returnXYZ = function(arr, index) {
|
|
2792
|
+
return {
|
|
2793
|
+
x: arr[index],
|
|
2794
|
+
y: arr[index + 1],
|
|
2795
|
+
z: arr[index + 2]
|
|
2796
|
+
};
|
|
2797
|
+
};
|
|
2798
|
+
|
|
2799
|
+
calcWindingNumber_buffer = function(trianglesArr, point) {
|
|
2800
|
+
var i, k, lenA, lenB, lenC, omega, ref, wN;
|
|
2801
|
+
wN = 0;
|
|
2802
|
+
for (i = k = 0, ref = trianglesArr.length; k < ref; i = k += 9) {
|
|
2803
|
+
_wV1.subVectors(returnXYZ(trianglesArr, i), point);
|
|
2804
|
+
_wV2.subVectors(returnXYZ(trianglesArr, i + 3), point);
|
|
2805
|
+
_wV3.subVectors(returnXYZ(trianglesArr, i + 6), point);
|
|
2806
|
+
lenA = _wV1.length();
|
|
2807
|
+
lenB = _wV2.length();
|
|
2808
|
+
lenC = _wV3.length();
|
|
2809
|
+
_matrix3.set(_wV1.x, _wV1.y, _wV1.z, _wV2.x, _wV2.y, _wV2.z, _wV3.x, _wV3.y, _wV3.z);
|
|
2810
|
+
omega = 2 * Math.atan2(_matrix3.determinant(), lenA * lenB * lenC + _wV1.dot(_wV2) * lenC + _wV2.dot(_wV3) * lenA + _wV3.dot(_wV1) * lenB);
|
|
2811
|
+
wN += omega;
|
|
2812
|
+
}
|
|
2813
|
+
wN = Math.round(wN / wNPI);
|
|
2814
|
+
return wN;
|
|
2815
|
+
};
|
|
2816
|
+
|
|
2817
|
+
polyInside_WindingNumber_buffer = function(trianglesArr, point, coplanar) {
|
|
2818
|
+
var j, k, ref, result, wN;
|
|
2819
|
+
result = false;
|
|
2820
|
+
_wP.copy(point);
|
|
2821
|
+
wN = calcWindingNumber_buffer(trianglesArr, _wP);
|
|
2822
|
+
if (wN === 0) {
|
|
2823
|
+
if (coplanar) {
|
|
2824
|
+
for (j = k = 0, ref = _wP_EPS_ARR_COUNT; (0 <= ref ? k < ref : k > ref); j = 0 <= ref ? ++k : --k) {
|
|
2825
|
+
_wP.copy(point).add(_wP_EPS_ARR[j]);
|
|
2826
|
+
wN = calcWindingNumber_buffer(trianglesArr, _wP);
|
|
2827
|
+
if (wN !== 0) {
|
|
2828
|
+
result = true;
|
|
2829
|
+
break;
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
}
|
|
2833
|
+
} else {
|
|
2834
|
+
result = true;
|
|
2835
|
+
}
|
|
2836
|
+
return result;
|
|
2837
|
+
};
|
|
2838
|
+
|
|
2839
|
+
// -----
|
|
2840
|
+
handleIntersectingPolytrees = function(polytreeA, polytreeB, bothPolytrees = true) {
|
|
2841
|
+
var polytreeA_buffer, polytreeB_buffer;
|
|
2842
|
+
polytreeA_buffer = void 0;
|
|
2843
|
+
polytreeB_buffer = void 0;
|
|
2844
|
+
if (Polytree.useWindingNumber === true) {
|
|
2845
|
+
if (bothPolytrees) {
|
|
2846
|
+
polytreeA_buffer = prepareTriangleBuffer(polytreeA.getPolygons());
|
|
2847
|
+
}
|
|
2848
|
+
polytreeB_buffer = prepareTriangleBuffer(polytreeB.getPolygons());
|
|
2849
|
+
}
|
|
2850
|
+
polytreeA.handleIntersectingPolygons(polytreeB, polytreeB_buffer);
|
|
2851
|
+
if (bothPolytrees) {
|
|
2852
|
+
polytreeB.handleIntersectingPolygons(polytreeA, polytreeA_buffer);
|
|
2853
|
+
}
|
|
2854
|
+
if (polytreeA_buffer !== void 0) {
|
|
2855
|
+
polytreeA_buffer = void 0;
|
|
2856
|
+
return polytreeB_buffer = void 0;
|
|
2857
|
+
}
|
|
2858
|
+
};
|
|
2859
|
+
|
|
2860
|
+
prepareTriangleBuffer = function(polygons) {
|
|
2861
|
+
var array, bufferIndex, i, k, numOfTriangles, ref, triangle;
|
|
2862
|
+
numOfTriangles = polygons.length;
|
|
2863
|
+
array = new Float32Array(numOfTriangles * 3 * 3);
|
|
2864
|
+
bufferIndex = 0;
|
|
2865
|
+
for (i = k = 0, ref = numOfTriangles; (0 <= ref ? k < ref : k > ref); i = 0 <= ref ? ++k : --k) {
|
|
2866
|
+
triangle = polygons[i].triangle;
|
|
2867
|
+
array[bufferIndex++] = triangle.a.x;
|
|
2868
|
+
array[bufferIndex++] = triangle.a.y;
|
|
2869
|
+
array[bufferIndex++] = triangle.a.z;
|
|
2870
|
+
array[bufferIndex++] = triangle.b.x;
|
|
2871
|
+
array[bufferIndex++] = triangle.b.y;
|
|
2872
|
+
array[bufferIndex++] = triangle.b.z;
|
|
2873
|
+
array[bufferIndex++] = triangle.c.x;
|
|
2874
|
+
array[bufferIndex++] = triangle.c.y;
|
|
2875
|
+
array[bufferIndex++] = triangle.c.z;
|
|
2876
|
+
}
|
|
2877
|
+
return array;
|
|
2878
|
+
};
|
|
2879
|
+
|
|
2880
|
+
// https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm
|
|
2881
|
+
edge1 = new Vector3();
|
|
2882
|
+
|
|
2883
|
+
edge2 = new Vector3();
|
|
2884
|
+
|
|
2885
|
+
h = new Vector3();
|
|
2886
|
+
|
|
2887
|
+
s = new Vector3();
|
|
2888
|
+
|
|
2889
|
+
q = new Vector3();
|
|
2890
|
+
|
|
2891
|
+
RAY_EPSILON = 0.0000001;
|
|
2892
|
+
|
|
2893
|
+
rayIntersectsTriangle = function(ray, triangle, target = new Vector3()) {
|
|
2894
|
+
var a, f, t, u, v;
|
|
2895
|
+
edge1.subVectors(triangle.b, triangle.a);
|
|
2896
|
+
edge2.subVectors(triangle.c, triangle.a);
|
|
2897
|
+
h.crossVectors(ray.direction, edge2);
|
|
2898
|
+
a = edge1.dot(h);
|
|
2899
|
+
if (a > -RAY_EPSILON && a < RAY_EPSILON) {
|
|
2900
|
+
return null; // Ray is parallel to the triangle
|
|
2901
|
+
}
|
|
2902
|
+
f = 1 / a;
|
|
2903
|
+
s.subVectors(ray.origin, triangle.a);
|
|
2904
|
+
u = f * s.dot(h);
|
|
2905
|
+
if (u < 0 || u > 1) {
|
|
2906
|
+
return null;
|
|
2907
|
+
}
|
|
2908
|
+
q.crossVectors(s, edge1);
|
|
2909
|
+
v = f * ray.direction.dot(q);
|
|
2910
|
+
if (v < 0 || u + v > 1) {
|
|
2911
|
+
return null;
|
|
2912
|
+
}
|
|
2913
|
+
t = f * edge2.dot(q);
|
|
2914
|
+
if (t > RAY_EPSILON) {
|
|
2915
|
+
return target.copy(ray.direction).multiplyScalar(t).add(ray.origin);
|
|
2916
|
+
}
|
|
2917
|
+
return null;
|
|
2918
|
+
};
|
|
2919
|
+
|
|
2920
|
+
Polytree.rayIntersectsTriangle = rayIntersectsTriangle;
|
|
2921
|
+
|
|
2922
|
+
Polytree.usePolytreeRay = true;
|
|
2923
|
+
|
|
2924
|
+
Polytree.useWindingNumber = false;
|
|
2925
|
+
|
|
2926
|
+
Polytree.rayIntersectTriangleType = "MollerTrumbore"; // "regular" (three.js' ray.intersectTriangle; "MollerTrumbore" (Moller Trumbore algorithm);
|
|
2927
|
+
|
|
2928
|
+
Polytree.maxLevel = 16;
|
|
2929
|
+
|
|
2930
|
+
Polytree.polygonsPerTree = 100;
|
|
2931
|
+
// Generated by CoffeeScript 2.7.0
|
|
2932
|
+
|
|
2933
|
+
/* Determines the intersection segment (if any) between two triangles in 3D space.
|
|
2934
|
+
If an intersection segment exists, its endpoints are written to `additions.source` and `additions.target`.
|
|
2935
|
+
|
|
2936
|
+
@param {Vector3} vertex1TriangleA, vertex2TriangleA, vertex3TriangleA - Vertices of triangle A
|
|
2937
|
+
@param {Vector3} vertex1TriangleB, vertex2TriangleB, vertex3TriangleB - Vertices of triangle B
|
|
2938
|
+
|
|
2939
|
+
@param {Object} additions - Data object used for storing extra intersection info:
|
|
2940
|
+
|
|
2941
|
+
- coplanar {Boolean} whether the triangles lie in the same plane.
|
|
2942
|
+
- source {Vector3} intersection segment start (if applicable).
|
|
2943
|
+
- target {Vector3} intersection segment end (if applicable).
|
|
2944
|
+
- normal1 {Vector3} normal of Triangle A (used in coplanar case).
|
|
2945
|
+
- normal2 {Vector3} normal of Triangle B (used in coplanar case).
|
|
2946
|
+
|
|
2947
|
+
@return {Boolean} True if intersection segment exists, false otherwise. */
|
|
2948
|
+
/* Checks for edge intersection between two triangles in 2D.
|
|
2949
|
+
|
|
2950
|
+
@param {Vector2} vertex1TriangleA, vertex2TriangleA, vertex3TriangleA - Vertices of triangle A
|
|
2951
|
+
@param {Vector2} vertex1TriangleB, vertex2TriangleB, vertex3TriangleB - Vertices of triangle B
|
|
2952
|
+
|
|
2953
|
+
@return {Boolean} True if an edge intersection is found, false otherwise. */
|
|
2954
|
+
/* Checks for vertex intersection between two triangles in 2D.
|
|
2955
|
+
|
|
2956
|
+
@param {Vector2} vertex1TriangleA, vertex2TriangleA, vertex3TriangleA - Vertices of triangle A
|
|
2957
|
+
@param {Vector2} vertex1TriangleB, vertex2TriangleB, vertex3TriangleB - Vertices of triangle B
|
|
2958
|
+
|
|
2959
|
+
@return {Boolean} True if a vertex intersection is found, false otherwise. */
|
|
2960
|
+
/* Resolves intersection between two coplanar triangles in 3D space.
|
|
2961
|
+
Since the triangles lie in the same plane, the problem is reduced from 3D to 2D.
|
|
2962
|
+
By projecting both triangles onto the axis-aligned plane (XY, YZ, or XZ) that maximizes the projected area.
|
|
2963
|
+
This minimizes numerical errors when working in 2D. The function then delegates the overlap test to `trianglesOverlap2D`.
|
|
2964
|
+
|
|
2965
|
+
@param {Vector2} vertex1TriangleA, vertex2TriangleA, vertex3TriangleA - Vertices of triangle A
|
|
2966
|
+
@param {Vector2} vertex1TriangleB, vertex2TriangleB, vertex3TriangleB - Vertices of triangle B
|
|
2967
|
+
|
|
2968
|
+
@param {Vector3} normalTriangleA - Normal vector of Triangle A.
|
|
2969
|
+
@param {Vector3} normalTriangleB - Normal vector of Triangle B.
|
|
2970
|
+
|
|
2971
|
+
@returns {Boolean} - True if the coplanar triangles overlap in 2D, false otherwise. */
|
|
2972
|
+
/* Determines the intersection between two 3D triangles given their vertices and the signed distances of Triangle B’s vertices to the plane of Triangle A.
|
|
2973
|
+
Goal: Always pass to `constructIntersection` the triangles arranged so the first vertex of each lies alone on one side of the other triangle’s plane (or is on the plane), and the remaining two share the opposite side.
|
|
2974
|
+
This function chooses one of several vertex orderings based on the sign pattern of (distanceVertex1B, distanceVertex2B, distanceVertex3B).
|
|
2975
|
+
If all three distances for Triangle B are zero → triangles are coplanar and handled by `resolveCoplanarTriangleIntersection`.
|
|
2976
|
+
|
|
2977
|
+
@param {Vector2} vertex1TriangleA, vertex2TriangleA, vertex3TriangleA - Vertices of triangle A
|
|
2978
|
+
@param {Vector2} vertex1TriangleB, vertex2TriangleB, vertex3TriangleB - Vertices of triangle B
|
|
2979
|
+
|
|
2980
|
+
@param {Number} distanceVertex1B - Signed distance of vertex1TriangleB to Triangle A’s plane.
|
|
2981
|
+
@param {Number} distanceVertex2B - Signed distance of vertex2TriangleB to Triangle A’s plane.
|
|
2982
|
+
@param {Number} distanceVertex3B - Signed distance of vertex3TriangleB to Triangle A’s plane.
|
|
2983
|
+
|
|
2984
|
+
@param {Object} additions - Data object used for storing extra intersection info:
|
|
2985
|
+
|
|
2986
|
+
- coplanar {Boolean} whether the triangles lie in the same plane.
|
|
2987
|
+
- source {Vector3} intersection segment start (if applicable).
|
|
2988
|
+
- target {Vector3} intersection segment end (if applicable).
|
|
2989
|
+
- normal1 {Vector3} normal of Triangle A (used in coplanar case).
|
|
2990
|
+
- normal2 {Vector3} normal of Triangle B (used in coplanar case).
|
|
2991
|
+
|
|
2992
|
+
@returns {Boolean} - True if an intersection is found, false otherwise. */
|
|
2993
|
+
/* Determines if two counter-clockwise (CCW) triangles in 2D overlap.
|
|
2994
|
+
The function checks the relative orientation of triangle B's vertices with respect to triangle A.
|
|
2995
|
+
Then recursively tests for edge or vertex intersection depending on the configuration.
|
|
2996
|
+
|
|
2997
|
+
@param {Vector2} vertex1TriangleA, vertex2TriangleA, vertex3TriangleA - Vertices of triangle A (CCW order)
|
|
2998
|
+
@param {Vector2} vertex1TriangleB, vertex2TriangleB, vertex3TriangleB - Vertices of triangle B (CCW order)
|
|
2999
|
+
|
|
3000
|
+
@return {Boolean} True if triangles overlap, false otherwise. */
|
|
3001
|
+
/* Checks whether two triangles in 3D space intersect.
|
|
3002
|
+
|
|
3003
|
+
@param {Object} triangleA - First triangle, with properties {a, b, c} (Vector3 vertices).
|
|
3004
|
+
@param {Object} triangleB - Second triangle, with properties {a, b, c} (Vector3 vertices).
|
|
3005
|
+
|
|
3006
|
+
@param {Object} additions - Optional data object used for storing extra intersection info:
|
|
3007
|
+
|
|
3008
|
+
- coplanar {Boolean} whether the triangles lie in the same plane.
|
|
3009
|
+
- source {Vector3} intersection segment start (if applicable).
|
|
3010
|
+
- target {Vector3} intersection segment end (if applicable).
|
|
3011
|
+
|
|
3012
|
+
@returns {Boolean} - True if the triangles intersect, false otherwise. */
|
|
3013
|
+
/* Computes the orientation (signed area) of a 2D triangle defined by three vertices.
|
|
3014
|
+
The result indicates whether the points are arranged clockwise (CW), counter-clockwise (CCW), or collinear.
|
|
3015
|
+
|
|
3016
|
+
Note: This returns the raw signed area (twice the triangle area), without applying an epsilon threshold.
|
|
3017
|
+
Callers should compare against a small EPS (e.g., EPS2D) when classifying near-collinear inputs.
|
|
3018
|
+
|
|
3019
|
+
Formula: orientation(a, b, c) = (a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x)
|
|
3020
|
+
|
|
3021
|
+
- If result > 0 → counter-clockwise (CCW).
|
|
3022
|
+
- If result < 0 → clockwise (CW).
|
|
3023
|
+
- If result = 0 → points are collinear.
|
|
3024
|
+
|
|
3025
|
+
@param {Vector2} a - First vertex.
|
|
3026
|
+
@param {Vector2} b - Second vertex.
|
|
3027
|
+
@param {Vector2} c - Third vertex.
|
|
3028
|
+
|
|
3029
|
+
@returns {Number} - Positive if CCW, negative if CW, zero if collinear. */
|
|
3030
|
+
/* Determines whether two triangles in 2D overlap.
|
|
3031
|
+
|
|
3032
|
+
Behavior and notes:
|
|
3033
|
+
|
|
3034
|
+
- Orientation: Triangles may be CW or CCW; inputs are normalized to CCW before testing.
|
|
3035
|
+
- Inclusivity: Overlap is inclusive of shared edges and shared vertices.
|
|
3036
|
+
- Degenerate handling:
|
|
3037
|
+
|
|
3038
|
+
- If one triangle degenerates to a point, returns whether that point lies in (or on) the other triangle.
|
|
3039
|
+
- If both degenerate to points, returns true only if the points coincide within EPS2D.
|
|
3040
|
+
- Degenerate line triangles are handled by the main CCW intersection routine and by point/segment checks where applicable.
|
|
3041
|
+
|
|
3042
|
+
@param {Vector2} vertex1TriangleA, vertex2TriangleA, vertex3TriangleA - Vertices of triangle A
|
|
3043
|
+
@param {Vector2} vertex1TriangleB, vertex2TriangleB, vertex3TriangleB - Vertices of triangle B
|
|
3044
|
+
|
|
3045
|
+
@returns {Boolean} - True if the triangles overlap in 2D, false otherwise. */
|
|
3046
|
+
var EPS2D, constructIntersection, intersectionTestEdge2D, intersectionTestVertex2D, pointInTriangleInclusive2D, pointOnSegmentInclusive2D, pointsEqual2D, resolveCoplanarTriangleIntersection, resolveTriangleIntersection, tempVector1, tempVector2, tempVector3, triangleIntersectionCCW2D, triangleIntersectsTriangle, triangleOrientation2D, trianglesOverlap2D;
|
|
3047
|
+
|
|
3048
|
+
EPS2D = 1e-10;
|
|
3049
|
+
|
|
3050
|
+
tempVector1 = new Vector3();
|
|
3051
|
+
|
|
3052
|
+
tempVector2 = new Vector3();
|
|
3053
|
+
|
|
3054
|
+
tempVector3 = new Vector3();
|
|
3055
|
+
|
|
3056
|
+
// Epsilon-aware 2D point equality.
|
|
3057
|
+
pointsEqual2D = function(p, q, eps = EPS2D) {
|
|
3058
|
+
return Math.abs(p.x - q.x) <= eps && Math.abs(p.y - q.y) <= eps;
|
|
3059
|
+
};
|
|
3060
|
+
|
|
3061
|
+
// Inclusive point-on-segment check for collinear points.
|
|
3062
|
+
pointOnSegmentInclusive2D = function(p, a, b, eps = EPS2D) {
|
|
3063
|
+
var maxX, maxY, minX, minY;
|
|
3064
|
+
if (pointsEqual2D(a, b, eps)) {
|
|
3065
|
+
return pointsEqual2D(p, a, eps) || pointsEqual2D(p, b, eps);
|
|
3066
|
+
}
|
|
3067
|
+
if (Math.abs(triangleOrientation2D(p, a, b)) > eps) {
|
|
3068
|
+
return false;
|
|
3069
|
+
}
|
|
3070
|
+
minX = Math.min(a.x, b.x) - eps;
|
|
3071
|
+
maxX = Math.max(a.x, b.x) + eps;
|
|
3072
|
+
minY = Math.min(a.y, b.y) - eps;
|
|
3073
|
+
maxY = Math.max(a.y, b.y) + eps;
|
|
3074
|
+
return p.x >= minX && p.x <= maxX && p.y >= minY && p.y <= maxY;
|
|
3075
|
+
};
|
|
3076
|
+
|
|
3077
|
+
// Inclusive point-in-triangle test using orientations.
|
|
3078
|
+
// - Works for CW or CCW input (auto-detects orientation).
|
|
3079
|
+
// - Inclusive of edges/vertices.
|
|
3080
|
+
// - Handles degenerate triangles (point or segment) robustly.
|
|
3081
|
+
pointInTriangleInclusive2D = function(p, a, b, c, eps = EPS2D) {
|
|
3082
|
+
var o, s1, s2, s3;
|
|
3083
|
+
o = triangleOrientation2D(a, b, c);
|
|
3084
|
+
if (o > eps) {
|
|
3085
|
+
s1 = triangleOrientation2D(p, a, b);
|
|
3086
|
+
s2 = triangleOrientation2D(p, b, c);
|
|
3087
|
+
s3 = triangleOrientation2D(p, c, a);
|
|
3088
|
+
return (s1 >= -eps) && (s2 >= -eps) && (s3 >= -eps);
|
|
3089
|
+
} else if (o < -eps) {
|
|
3090
|
+
s1 = triangleOrientation2D(p, a, b);
|
|
3091
|
+
s2 = triangleOrientation2D(p, b, c);
|
|
3092
|
+
s3 = triangleOrientation2D(p, c, a);
|
|
3093
|
+
return (s1 <= eps) && (s2 <= eps) && (s3 <= eps);
|
|
3094
|
+
} else {
|
|
3095
|
+
// Degenerate triangle: either a point (all equal) or a segment (collinear).
|
|
3096
|
+
if (pointsEqual2D(a, b, eps) && pointsEqual2D(b, c, eps)) {
|
|
3097
|
+
return pointsEqual2D(p, a, eps);
|
|
3098
|
+
}
|
|
3099
|
+
// Reduce to the non-degenerate segment and test inclusively.
|
|
3100
|
+
if (pointsEqual2D(a, b, eps)) {
|
|
3101
|
+
return pointOnSegmentInclusive2D(p, b, c, eps);
|
|
3102
|
+
}
|
|
3103
|
+
if (pointsEqual2D(b, c, eps)) {
|
|
3104
|
+
return pointOnSegmentInclusive2D(p, a, b, eps);
|
|
3105
|
+
}
|
|
3106
|
+
if (pointsEqual2D(c, a, eps)) {
|
|
3107
|
+
return pointOnSegmentInclusive2D(p, a, b, eps);
|
|
3108
|
+
}
|
|
3109
|
+
// All three distinct but collinear: test against hull segments.
|
|
3110
|
+
return pointOnSegmentInclusive2D(p, a, b, eps) || pointOnSegmentInclusive2D(p, b, c, eps) || pointOnSegmentInclusive2D(p, c, a, eps);
|
|
3111
|
+
}
|
|
3112
|
+
};
|
|
3113
|
+
|
|
3114
|
+
triangleIntersectsTriangle = function(triangleA, triangleB, additions = {
|
|
3115
|
+
coplanar: false,
|
|
3116
|
+
source: new Vector3(),
|
|
3117
|
+
target: new Vector3()
|
|
3118
|
+
}) {
|
|
3119
|
+
var distanceVertex1A, distanceVertex1B, distanceVertex2A, distanceVertex2B, distanceVertex3A, distanceVertex3B, normal1, normal2, vertex1TriangleA, vertex1TriangleB, vertex2TriangleA, vertex2TriangleB, vertex3TriangleA, vertex3TriangleB;
|
|
3120
|
+
// Extract vertices of triangle A.
|
|
3121
|
+
vertex1TriangleA = triangleA.a;
|
|
3122
|
+
vertex2TriangleA = triangleA.b;
|
|
3123
|
+
vertex3TriangleA = triangleA.c;
|
|
3124
|
+
// Extract vertices of triangle B.
|
|
3125
|
+
vertex1TriangleB = triangleB.a;
|
|
3126
|
+
vertex2TriangleB = triangleB.b;
|
|
3127
|
+
vertex3TriangleB = triangleB.c;
|
|
3128
|
+
// Step 1: Compute signed distances of Triangle A’s vertices relative to the plane defined by Triangle B.
|
|
3129
|
+
tempVector1.copy(vertex1TriangleB).sub(vertex3TriangleB);
|
|
3130
|
+
tempVector2.copy(vertex2TriangleB).sub(vertex3TriangleB);
|
|
3131
|
+
normal2 = (new Vector3()).copy(tempVector1).cross(tempVector2);
|
|
3132
|
+
tempVector1.copy(vertex1TriangleA).sub(vertex3TriangleB);
|
|
3133
|
+
distanceVertex1A = tempVector1.dot(normal2);
|
|
3134
|
+
tempVector1.copy(vertex2TriangleA).sub(vertex3TriangleB);
|
|
3135
|
+
distanceVertex2A = tempVector1.dot(normal2);
|
|
3136
|
+
tempVector1.copy(vertex3TriangleA).sub(vertex3TriangleB);
|
|
3137
|
+
distanceVertex3A = tempVector1.dot(normal2);
|
|
3138
|
+
if (((distanceVertex1A * distanceVertex2A) > 0) && ((distanceVertex1A * distanceVertex3A) > 0)) {
|
|
3139
|
+
return false; // All vertices of Triangle A are on the same side of Triangle B’s plane.
|
|
3140
|
+
}
|
|
3141
|
+
|
|
3142
|
+
// Step 2: Compute signed distances of Triangle B’s vertices relative to the plane defined by Triangle A.
|
|
3143
|
+
tempVector1.copy(vertex2TriangleA).sub(vertex1TriangleA);
|
|
3144
|
+
tempVector2.copy(vertex3TriangleA).sub(vertex1TriangleA);
|
|
3145
|
+
normal1 = (new Vector3()).copy(tempVector1).cross(tempVector2);
|
|
3146
|
+
tempVector1.copy(vertex1TriangleB).sub(vertex3TriangleA);
|
|
3147
|
+
distanceVertex1B = tempVector1.dot(normal1);
|
|
3148
|
+
tempVector1.copy(vertex2TriangleB).sub(vertex3TriangleA);
|
|
3149
|
+
distanceVertex2B = tempVector1.dot(normal1);
|
|
3150
|
+
tempVector1.copy(vertex3TriangleB).sub(vertex3TriangleA);
|
|
3151
|
+
distanceVertex3B = tempVector1.dot(normal1);
|
|
3152
|
+
if (((distanceVertex1B * distanceVertex2B) > 0) && ((distanceVertex1B * distanceVertex3B) > 0)) {
|
|
3153
|
+
return false; // All vertices of Triangle B are on the same side of Triangle A’s plane.
|
|
3154
|
+
}
|
|
3155
|
+
|
|
3156
|
+
// Step 3: At this point, neither triangle is fully on one side of the other’s plane. This means the triangles potentially intersect.
|
|
3157
|
+
// Next, we use the vertex signed distances to decide which configuration applies and call `resolveTriangleIntersection` (or `resolveCoplanarTriangleIntersection` if the triangles are coplanar).
|
|
3158
|
+
additions.normal1 = normal1;
|
|
3159
|
+
additions.normal2 = normal2;
|
|
3160
|
+
// Decide how to proceed by looking at the signs of distanceVertex1A, distanceVertex2A and distanceVertex3A.
|
|
3161
|
+
// These numbers tell us whether each vertex of Triangle A sits above the flat surface of Triangle B (positive), below it (negative), or exactly on it (zero).
|
|
3162
|
+
// We then pass the vertices to the resolver in a stable order: the single “different-side” vertex first, followed by the two vertices that are on the same side.
|
|
3163
|
+
if (distanceVertex1A > 0) {
|
|
3164
|
+
// If distanceVertex1A is positive (the first vertex of Triangle A is above Triangle B's surface):
|
|
3165
|
+
if (distanceVertex2A > 0) {
|
|
3166
|
+
// Then distanceVertex2A is also positive, while distanceVertex3A is zero or negative.
|
|
3167
|
+
// In plain terms: the third vertex of Triangle A lies on the other side of Triangle B’s surface (or exactly on it).
|
|
3168
|
+
return resolveTriangleIntersection(vertex3TriangleA, vertex1TriangleA, vertex2TriangleA, vertex1TriangleB, vertex3TriangleB, vertex2TriangleB, distanceVertex1B, distanceVertex3B, distanceVertex2B, additions);
|
|
3169
|
+
} else if (distanceVertex3A > 0) {
|
|
3170
|
+
// Then distanceVertex3A is positive, while distanceVertex2A is zero or negative.
|
|
3171
|
+
// That means the second vertex of Triangle A is the one on the other side (or exactly on the surface).
|
|
3172
|
+
return resolveTriangleIntersection(vertex2TriangleA, vertex3TriangleA, vertex1TriangleA, vertex1TriangleB, vertex3TriangleB, vertex2TriangleB, distanceVertex1B, distanceVertex3B, distanceVertex2B, additions);
|
|
3173
|
+
} else {
|
|
3174
|
+
// Here only the first vertex is above the surface; the second and third are on or below it.
|
|
3175
|
+
return resolveTriangleIntersection(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, distanceVertex1B, distanceVertex2B, distanceVertex3B, additions);
|
|
3176
|
+
}
|
|
3177
|
+
} else if (distanceVertex1A < 0) {
|
|
3178
|
+
// If distanceVertex1A is negative (the first vertex of Triangle A is below Triangle B's surface):
|
|
3179
|
+
if (distanceVertex2A < 0) {
|
|
3180
|
+
// Then distanceVertex2A is also negative, while distanceVertex3A is zero or positive.
|
|
3181
|
+
// In other words: the third vertex of Triangle A is on the other side (or exactly on the surface).
|
|
3182
|
+
return resolveTriangleIntersection(vertex3TriangleA, vertex1TriangleA, vertex2TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, distanceVertex1B, distanceVertex2B, distanceVertex3B, additions);
|
|
3183
|
+
} else if (distanceVertex3A < 0) {
|
|
3184
|
+
// Then distanceVertex3A is negative, while distanceVertex2A is zero or positive.
|
|
3185
|
+
// So the second vertex is the one on the other side (or exactly on the surface).
|
|
3186
|
+
return resolveTriangleIntersection(vertex2TriangleA, vertex3TriangleA, vertex1TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, distanceVertex1B, distanceVertex2B, distanceVertex3B, additions);
|
|
3187
|
+
} else {
|
|
3188
|
+
// Only the first vertex is below the surface; the second and third are on or above it.
|
|
3189
|
+
// Note: We also swap the order of Triangle B’s vertices here to keep a consistent “one different, two the same” pattern for the resolver.
|
|
3190
|
+
return resolveTriangleIntersection(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex3TriangleB, vertex2TriangleB, distanceVertex1B, distanceVertex3B, distanceVertex2B, additions);
|
|
3191
|
+
}
|
|
3192
|
+
} else {
|
|
3193
|
+
// Then the first vertex of Triangle A lies exactly on Triangle B’s surface (distanceVertex1A is zero).
|
|
3194
|
+
// We look at distanceVertex2A and distanceVertex3A to decide which side the other vertices are on.
|
|
3195
|
+
if (distanceVertex2A < 0) {
|
|
3196
|
+
if (distanceVertex3A >= 0) {
|
|
3197
|
+
// The second vertex is below the surface, while the third is on or above it.
|
|
3198
|
+
return resolveTriangleIntersection(vertex2TriangleA, vertex3TriangleA, vertex1TriangleA, vertex1TriangleB, vertex3TriangleB, vertex2TriangleB, distanceVertex1B, distanceVertex3B, distanceVertex2B, additions);
|
|
3199
|
+
} else {
|
|
3200
|
+
// Both the second and third vertices are below the surface.
|
|
3201
|
+
return resolveTriangleIntersection(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, distanceVertex1B, distanceVertex2B, distanceVertex3B, additions);
|
|
3202
|
+
}
|
|
3203
|
+
} else if (distanceVertex2A > 0) {
|
|
3204
|
+
if (distanceVertex3A > 0) {
|
|
3205
|
+
// Both the second and third vertices are above the surface.
|
|
3206
|
+
return resolveTriangleIntersection(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex3TriangleB, vertex2TriangleB, distanceVertex1B, distanceVertex3B, distanceVertex2B, additions);
|
|
3207
|
+
} else {
|
|
3208
|
+
// The second vertex is above the surface, while the third is on or below it.
|
|
3209
|
+
return resolveTriangleIntersection(vertex2TriangleA, vertex3TriangleA, vertex1TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, distanceVertex1B, distanceVertex2B, distanceVertex3B, additions);
|
|
3210
|
+
}
|
|
3211
|
+
} else {
|
|
3212
|
+
if (distanceVertex3A > 0) {
|
|
3213
|
+
// The second vertex is exactly on the surface, and the third is above it.
|
|
3214
|
+
return resolveTriangleIntersection(vertex3TriangleA, vertex1TriangleA, vertex2TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, distanceVertex1B, distanceVertex2B, distanceVertex3B, additions);
|
|
3215
|
+
} else if (distanceVertex3A < 0) {
|
|
3216
|
+
// The second vertex is exactly on the surface, and the third is below it.
|
|
3217
|
+
return resolveTriangleIntersection(vertex3TriangleA, vertex1TriangleA, vertex2TriangleA, vertex1TriangleB, vertex3TriangleB, vertex2TriangleB, distanceVertex1B, distanceVertex3B, distanceVertex2B, additions);
|
|
3218
|
+
} else {
|
|
3219
|
+
additions.coplanar = true; // All three vertices of Triangle A lie in Triangle B's plane.
|
|
3220
|
+
return resolveCoplanarTriangleIntersection(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, normal1, normal2);
|
|
3221
|
+
}
|
|
3222
|
+
}
|
|
3223
|
+
}
|
|
3224
|
+
};
|
|
3225
|
+
|
|
3226
|
+
resolveTriangleIntersection = function(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, distanceVertex1B, distanceVertex2B, distanceVertex3B, additions) {
|
|
3227
|
+
// Early exit → If all B's distances are strictly positive (totally outside/above) or negative (totally outside/below), there's no intersection.
|
|
3228
|
+
if ((distanceVertex1B > 0 && distanceVertex2B > 0 && distanceVertex3B > 0) || (distanceVertex1B < 0 && distanceVertex2B < 0 && distanceVertex3B < 0)) {
|
|
3229
|
+
return false;
|
|
3230
|
+
}
|
|
3231
|
+
if (distanceVertex1B > 0) { // First vertex of Triangle B is above (positive side of) Triangle A's plane.
|
|
3232
|
+
if (distanceVertex2B > 0) { // First two vertices of B are positive, third is zero or negative.
|
|
3233
|
+
return constructIntersection(vertex1TriangleA, vertex3TriangleA, vertex2TriangleA, vertex3TriangleB, vertex1TriangleB, vertex2TriangleB, additions); // Reorder B as (C, A, B) so differing vertex is last.
|
|
3234
|
+
} else if (distanceVertex3B > 0) { // First and third vertices of B are positive, second is zero or negative.
|
|
3235
|
+
return constructIntersection(vertex1TriangleA, vertex3TriangleA, vertex2TriangleA, vertex2TriangleB, vertex3TriangleB, vertex1TriangleB, additions); // Reorder B as (B, C, A).
|
|
3236
|
+
// Only first vertex of B is positive.
|
|
3237
|
+
} else {
|
|
3238
|
+
return constructIntersection(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, additions); // Pass original B ordering.
|
|
3239
|
+
}
|
|
3240
|
+
} else if (distanceVertex1B < 0) { // First vertex of Triangle B is below (negative side of) Triangle A's plane.
|
|
3241
|
+
if (distanceVertex2B < 0) { // First two vertices of B are negative, third is zero or positive.
|
|
3242
|
+
return constructIntersection(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex3TriangleB, vertex1TriangleB, vertex2TriangleB, additions); // Reorder B as (C, A, B) so differing vertex is last.
|
|
3243
|
+
} else if (distanceVertex3B < 0) { // First and third vertices of B are negative, second is zero or positive.
|
|
3244
|
+
return constructIntersection(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex2TriangleB, vertex3TriangleB, vertex1TriangleB, additions); // Reorder B as (B, C, A).
|
|
3245
|
+
// Only first vertex of B is negative.
|
|
3246
|
+
} else {
|
|
3247
|
+
return constructIntersection(vertex1TriangleA, vertex3TriangleA, vertex2TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, additions); // Pass A, reordered B.
|
|
3248
|
+
// First vertex of Triangle B is exactly on the plane (distance zero).
|
|
3249
|
+
}
|
|
3250
|
+
} else {
|
|
3251
|
+
if (distanceVertex2B < 0) { // Second vertex is negative, third will decide.
|
|
3252
|
+
if (distanceVertex3B >= 0) { // Second negative, third zero or positive.
|
|
3253
|
+
return constructIntersection(vertex1TriangleA, vertex3TriangleA, vertex2TriangleA, vertex2TriangleB, vertex3TriangleB, vertex1TriangleB, additions); // Mixed across the plane.
|
|
3254
|
+
// Second & third negative.
|
|
3255
|
+
} else {
|
|
3256
|
+
return constructIntersection(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, additions); // Only first on the plane.
|
|
3257
|
+
}
|
|
3258
|
+
} else if (distanceVertex2B > 0) { // Second vertex is positive.
|
|
3259
|
+
if (distanceVertex3B > 0) { // Both second and third are positive.
|
|
3260
|
+
return constructIntersection(vertex1TriangleA, vertex3TriangleA, vertex2TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, additions); // Both above the plane.
|
|
3261
|
+
// Second positive, third zero or negative.
|
|
3262
|
+
} else {
|
|
3263
|
+
return constructIntersection(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex2TriangleB, vertex3TriangleB, vertex1TriangleB, additions); // Mixed, split across plane.
|
|
3264
|
+
// Second vertex is exactly on the plane (zero).
|
|
3265
|
+
}
|
|
3266
|
+
} else {
|
|
3267
|
+
if (distanceVertex3B > 0) { // Only third is positive.
|
|
3268
|
+
return constructIntersection(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex3TriangleB, vertex1TriangleB, vertex2TriangleB, additions); // Third above, first/second on plane.
|
|
3269
|
+
} else if (distanceVertex3B < 0) { // Only third is negative.
|
|
3270
|
+
return constructIntersection(vertex1TriangleA, vertex3TriangleA, vertex2TriangleA, vertex3TriangleB, vertex1TriangleB, vertex2TriangleB, additions); // Third below plane.
|
|
3271
|
+
// All three B vertices are exactly on the plane (coplanar).
|
|
3272
|
+
} else {
|
|
3273
|
+
additions.coplanar = true; // Mark coplanar.
|
|
3274
|
+
return resolveCoplanarTriangleIntersection(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, additions.normal1, additions.normal2);
|
|
3275
|
+
}
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3278
|
+
};
|
|
3279
|
+
|
|
3280
|
+
resolveCoplanarTriangleIntersection = function(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, normalTriangleA, normalTriangleB) {
|
|
3281
|
+
var normalAbsX, normalAbsY, normalAbsZ, vertex1TriangleA2D, vertex1TriangleB2D, vertex2TriangleA2D, vertex2TriangleB2D, vertex3TriangleA2D, vertex3TriangleB2D;
|
|
3282
|
+
// Prepare 2D projected vertices.
|
|
3283
|
+
vertex1TriangleA2D = new Vector2();
|
|
3284
|
+
vertex2TriangleA2D = new Vector2();
|
|
3285
|
+
vertex3TriangleA2D = new Vector2();
|
|
3286
|
+
vertex1TriangleB2D = new Vector2();
|
|
3287
|
+
vertex2TriangleB2D = new Vector2();
|
|
3288
|
+
vertex3TriangleB2D = new Vector2();
|
|
3289
|
+
// Absolute values of the triangle's normal components.
|
|
3290
|
+
// Used to determine the dominant axis, which we drop during 2D projection.
|
|
3291
|
+
normalAbsX = Math.abs(normalTriangleA.x);
|
|
3292
|
+
normalAbsY = Math.abs(normalTriangleA.y);
|
|
3293
|
+
normalAbsZ = Math.abs(normalTriangleA.z);
|
|
3294
|
+
// Project triangles into 2D by dropping the dominant axis of the normal.
|
|
3295
|
+
if ((normalAbsX > normalAbsZ) && (normalAbsX >= normalAbsY)) { // Project onto YZ plane.
|
|
3296
|
+
vertex1TriangleA2D.set(vertex1TriangleA.y, vertex1TriangleA.z);
|
|
3297
|
+
vertex2TriangleA2D.set(vertex2TriangleA.y, vertex2TriangleA.z);
|
|
3298
|
+
vertex3TriangleA2D.set(vertex3TriangleA.y, vertex3TriangleA.z);
|
|
3299
|
+
vertex1TriangleB2D.set(vertex1TriangleB.y, vertex1TriangleB.z);
|
|
3300
|
+
vertex2TriangleB2D.set(vertex2TriangleB.y, vertex2TriangleB.z);
|
|
3301
|
+
vertex3TriangleB2D.set(vertex3TriangleB.y, vertex3TriangleB.z);
|
|
3302
|
+
} else if ((normalAbsY > normalAbsZ) && (normalAbsY >= normalAbsX)) { // Project onto XZ plane.
|
|
3303
|
+
vertex1TriangleA2D.set(vertex1TriangleA.x, vertex1TriangleA.z);
|
|
3304
|
+
vertex2TriangleA2D.set(vertex2TriangleA.x, vertex2TriangleA.z);
|
|
3305
|
+
vertex3TriangleA2D.set(vertex3TriangleA.x, vertex3TriangleA.z);
|
|
3306
|
+
vertex1TriangleB2D.set(vertex1TriangleB.x, vertex1TriangleB.z);
|
|
3307
|
+
vertex2TriangleB2D.set(vertex2TriangleB.x, vertex2TriangleB.z);
|
|
3308
|
+
vertex3TriangleB2D.set(vertex3TriangleB.x, vertex3TriangleB.z); // Project onto XY plane
|
|
3309
|
+
} else {
|
|
3310
|
+
vertex1TriangleA2D.set(vertex1TriangleA.x, vertex1TriangleA.y);
|
|
3311
|
+
vertex2TriangleA2D.set(vertex2TriangleA.x, vertex2TriangleA.y);
|
|
3312
|
+
vertex3TriangleA2D.set(vertex3TriangleA.x, vertex3TriangleA.y);
|
|
3313
|
+
vertex1TriangleB2D.set(vertex1TriangleB.x, vertex1TriangleB.y);
|
|
3314
|
+
vertex2TriangleB2D.set(vertex2TriangleB.x, vertex2TriangleB.y);
|
|
3315
|
+
vertex3TriangleB2D.set(vertex3TriangleB.x, vertex3TriangleB.y);
|
|
3316
|
+
}
|
|
3317
|
+
return trianglesOverlap2D(vertex1TriangleA2D, vertex2TriangleA2D, vertex3TriangleA2D, vertex1TriangleB2D, vertex2TriangleB2D, vertex3TriangleB2D);
|
|
3318
|
+
};
|
|
3319
|
+
|
|
3320
|
+
trianglesOverlap2D = function(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB) {
|
|
3321
|
+
var aE, aMax, aMin, aS, bE, bMax, bMin, bS, extractSegment, isLineDegenerate, maxAx, maxAy, maxBx, maxBy, minAx, minAy, minBx, minBy, spanAx, spanAy;
|
|
3322
|
+
// Early handle point-degenerate cases explicitly and efficiently.
|
|
3323
|
+
if (pointsEqual2D(vertex1TriangleB, vertex2TriangleB, EPS2D) && pointsEqual2D(vertex2TriangleB, vertex3TriangleB, EPS2D)) {
|
|
3324
|
+
return pointInTriangleInclusive2D(vertex1TriangleB, vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, EPS2D);
|
|
3325
|
+
}
|
|
3326
|
+
if (pointsEqual2D(vertex1TriangleA, vertex2TriangleA, EPS2D) && pointsEqual2D(vertex2TriangleA, vertex3TriangleA, EPS2D)) {
|
|
3327
|
+
return pointInTriangleInclusive2D(vertex1TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, EPS2D);
|
|
3328
|
+
}
|
|
3329
|
+
// If both are line-degenerate (not points): reduce to segment overlap test.
|
|
3330
|
+
isLineDegenerate = function(v1, v2, v3) {
|
|
3331
|
+
return (pointsEqual2D(v1, v2, EPS2D) && !pointsEqual2D(v2, v3, EPS2D)) || (pointsEqual2D(v2, v3, EPS2D) && !pointsEqual2D(v1, v2, EPS2D)) || (pointsEqual2D(v3, v1, EPS2D) && !pointsEqual2D(v1, v2, EPS2D));
|
|
3332
|
+
};
|
|
3333
|
+
extractSegment = function(v1, v2, v3) {
|
|
3334
|
+
// Return the two distinct endpoints in stable order.
|
|
3335
|
+
if (pointsEqual2D(v1, v2, EPS2D)) {
|
|
3336
|
+
return [v2, v3];
|
|
3337
|
+
} else if (pointsEqual2D(v2, v3, EPS2D)) {
|
|
3338
|
+
return [v1, v2];
|
|
3339
|
+
} else {
|
|
3340
|
+
return [
|
|
3341
|
+
v1,
|
|
3342
|
+
v2 // fallback (should not reach if collinear case handled earlier)
|
|
3343
|
+
];
|
|
3344
|
+
}
|
|
3345
|
+
};
|
|
3346
|
+
if (isLineDegenerate(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA) && isLineDegenerate(vertex1TriangleB, vertex2TriangleB, vertex3TriangleB)) {
|
|
3347
|
+
[aS, aE] = extractSegment(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA);
|
|
3348
|
+
[bS, bE] = extractSegment(vertex1TriangleB, vertex2TriangleB, vertex3TriangleB);
|
|
3349
|
+
// Fast reject by axis-aligned bounding boxes.
|
|
3350
|
+
minAx = Math.min(aS.x, aE.x);
|
|
3351
|
+
maxAx = Math.max(aS.x, aE.x);
|
|
3352
|
+
minAy = Math.min(aS.y, aE.y);
|
|
3353
|
+
maxAy = Math.max(aS.y, aE.y);
|
|
3354
|
+
minBx = Math.min(bS.x, bE.x);
|
|
3355
|
+
maxBx = Math.max(bS.x, bE.x);
|
|
3356
|
+
minBy = Math.min(bS.y, bE.y);
|
|
3357
|
+
maxBy = Math.max(bS.y, bE.y);
|
|
3358
|
+
if (maxAx < minBx - EPS2D || maxBx < minAx - EPS2D || maxAy < minBy - EPS2D || maxBy < minAy - EPS2D) {
|
|
3359
|
+
return false;
|
|
3360
|
+
}
|
|
3361
|
+
// Collinear check: orientation of any mixed triple should be ~0; since we know each triangle is a line, test one.
|
|
3362
|
+
if (Math.abs(triangleOrientation2D(aS, aE, bS)) > EPS2D) {
|
|
3363
|
+
return false;
|
|
3364
|
+
}
|
|
3365
|
+
// 1D overlap test along dominant axis (choose axis with larger span to reduce precision issues).
|
|
3366
|
+
spanAx = Math.abs(aE.x - aS.x);
|
|
3367
|
+
spanAy = Math.abs(aE.y - aS.y);
|
|
3368
|
+
if (spanAx >= spanAy) {
|
|
3369
|
+
// Project onto X.
|
|
3370
|
+
aMin = Math.min(aS.x, aE.x) - EPS2D;
|
|
3371
|
+
aMax = Math.max(aS.x, aE.x) + EPS2D;
|
|
3372
|
+
bMin = Math.min(bS.x, bE.x) - EPS2D;
|
|
3373
|
+
bMax = Math.max(bS.x, bE.x) + EPS2D;
|
|
3374
|
+
return !(aMax < bMin || bMax < aMin);
|
|
3375
|
+
} else {
|
|
3376
|
+
aMin = Math.min(aS.y, aE.y) - EPS2D;
|
|
3377
|
+
aMax = Math.max(aS.y, aE.y) + EPS2D;
|
|
3378
|
+
bMin = Math.min(bS.y, bE.y) - EPS2D;
|
|
3379
|
+
bMax = Math.max(bS.y, bE.y) + EPS2D;
|
|
3380
|
+
return !(aMax < bMin || bMax < aMin);
|
|
3381
|
+
}
|
|
3382
|
+
}
|
|
3383
|
+
if (triangleOrientation2D(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA) < 0) { // If triangle A is CW.
|
|
3384
|
+
if (triangleOrientation2D(vertex1TriangleB, vertex2TriangleB, vertex3TriangleB) < 0) { // If both A and B are CW → reorder both.
|
|
3385
|
+
return triangleIntersectionCCW2D(vertex1TriangleA, vertex3TriangleA, vertex2TriangleA, vertex1TriangleB, vertex3TriangleB, vertex2TriangleB); // Only A is CW → reorder A.
|
|
3386
|
+
} else {
|
|
3387
|
+
return triangleIntersectionCCW2D(vertex1TriangleA, vertex3TriangleA, vertex2TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB); // Triangle A is CCW.
|
|
3388
|
+
}
|
|
3389
|
+
} else {
|
|
3390
|
+
if (triangleOrientation2D(vertex1TriangleB, vertex2TriangleB, vertex3TriangleB) < 0) { // If only B is CW → reorder B.
|
|
3391
|
+
return triangleIntersectionCCW2D(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex3TriangleB, vertex2TriangleB); // Both A and B are CCW → no reordering.
|
|
3392
|
+
} else {
|
|
3393
|
+
return triangleIntersectionCCW2D(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB);
|
|
3394
|
+
}
|
|
3395
|
+
}
|
|
3396
|
+
};
|
|
3397
|
+
|
|
3398
|
+
triangleOrientation2D = function(a, b, c) {
|
|
3399
|
+
// Compute the signed area of the triangle (a, b, c).
|
|
3400
|
+
return (a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x);
|
|
3401
|
+
};
|
|
3402
|
+
|
|
3403
|
+
triangleIntersectionCCW2D = function(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB) {
|
|
3404
|
+
// If vertex1TriangleB is on or to the left of edge vertex1TriangleA-vertex2TriangleA-vertex3TriangleA.
|
|
3405
|
+
if (triangleOrientation2D(vertex1TriangleB, vertex2TriangleB, vertex1TriangleA) >= 0) {
|
|
3406
|
+
// If vertex2TriangleB is on or to the left of edge vertex2TriangleA-vertex3TriangleA-vertex1TriangleA.
|
|
3407
|
+
if (triangleOrientation2D(vertex2TriangleB, vertex3TriangleB, vertex1TriangleA) >= 0) {
|
|
3408
|
+
// If vertex3TriangleB is on or to the left of edge vertex3TriangleA-vertex1TriangleA-vertex1TriangleA.
|
|
3409
|
+
if (triangleOrientation2D(vertex3TriangleB, vertex1TriangleB, vertex1TriangleA) >= 0) {
|
|
3410
|
+
return true; // All vertices of B are inside A.
|
|
3411
|
+
// Then vertex3TriangleB is outside, test edge intersection.
|
|
3412
|
+
} else {
|
|
3413
|
+
return intersectionTestEdge2D(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB); // Then vertex2TriangleB is outside.
|
|
3414
|
+
}
|
|
3415
|
+
} else {
|
|
3416
|
+
|
|
3417
|
+
// If vertex3TriangleB is on or to the left of edge vertex3TriangleA-vertex1TriangleA-vertex1TriangleA, test edge intersection.
|
|
3418
|
+
if (triangleOrientation2D(vertex3TriangleB, vertex1TriangleB, vertex1TriangleA) >= 0) {
|
|
3419
|
+
return intersectionTestEdge2D(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex3TriangleB, vertex1TriangleB, vertex2TriangleB); // Then both vertex2TriangleB and vertex3TriangleB are outside, test vertex intersection.
|
|
3420
|
+
} else {
|
|
3421
|
+
return intersectionTestVertex2D(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB); // Then vertex2TriangleB is on or to the left of edge vertex2TriangleA-vertex3TriangleA-vertex1TriangleA.
|
|
3422
|
+
}
|
|
3423
|
+
}
|
|
3424
|
+
} else {
|
|
3425
|
+
|
|
3426
|
+
// If vertex2TriangleB is on or to the left of edge vertex2TriangleA-vertex3TriangleA-vertex1TriangleA.
|
|
3427
|
+
if (triangleOrientation2D(vertex2TriangleB, vertex3TriangleB, vertex1TriangleA) >= 0) {
|
|
3428
|
+
// If vertex3TriangleB is on or to the left of edge vertex3TriangleA-vertex1TriangleA-vertex1TriangleA, test edge intersection.
|
|
3429
|
+
if (triangleOrientation2D(vertex3TriangleB, vertex1TriangleB, vertex1TriangleA) >= 0) {
|
|
3430
|
+
return intersectionTestEdge2D(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex2TriangleB, vertex3TriangleB, vertex1TriangleB); // Then vertex3TriangleB is outside, test vertex intersection.
|
|
3431
|
+
} else {
|
|
3432
|
+
return intersectionTestVertex2D(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex2TriangleB, vertex3TriangleB, vertex1TriangleB); // Then both vertex1TriangleB and vertex2TriangleB are outside, test vertex intersection.
|
|
3433
|
+
}
|
|
3434
|
+
} else {
|
|
3435
|
+
return intersectionTestVertex2D(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex3TriangleB, vertex1TriangleB, vertex2TriangleB);
|
|
3436
|
+
}
|
|
3437
|
+
}
|
|
3438
|
+
};
|
|
3439
|
+
|
|
3440
|
+
intersectionTestEdge2D = function(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB) {
|
|
3441
|
+
var a1, a2, b1, b2, edgesA, edgesB, i, j, len, len1, segmentIntersectsInclusive;
|
|
3442
|
+
segmentIntersectsInclusive = function(a1, a2, b1, b2) {
|
|
3443
|
+
var maxAx, maxAy, maxBx, maxBy, minAx, minAy, minBx, minBy, o1, o2, o3, o4;
|
|
3444
|
+
// Fast reject by bounding boxes.
|
|
3445
|
+
minAx = Math.min(a1.x, a2.x);
|
|
3446
|
+
maxAx = Math.max(a1.x, a2.x);
|
|
3447
|
+
minAy = Math.min(a1.y, a2.y);
|
|
3448
|
+
maxAy = Math.max(a1.y, a2.y);
|
|
3449
|
+
minBx = Math.min(b1.x, b2.x);
|
|
3450
|
+
maxBx = Math.max(b1.x, b2.x);
|
|
3451
|
+
minBy = Math.min(b1.y, b2.y);
|
|
3452
|
+
maxBy = Math.max(b1.y, b2.y);
|
|
3453
|
+
if (maxAx < minBx || maxBx < minAx || maxAy < minBy || maxBy < minAy) {
|
|
3454
|
+
return false;
|
|
3455
|
+
}
|
|
3456
|
+
o1 = triangleOrientation2D(a1, a2, b1);
|
|
3457
|
+
o2 = triangleOrientation2D(a1, a2, b2);
|
|
3458
|
+
o3 = triangleOrientation2D(b1, b2, a1);
|
|
3459
|
+
o4 = triangleOrientation2D(b1, b2, a2);
|
|
3460
|
+
if ((o1 > 0 && o2 < 0 || o1 < 0 && o2 > 0) && (o3 > 0 && o4 < 0 || o3 < 0 && o4 > 0)) {
|
|
3461
|
+
return true; // Proper intersection.
|
|
3462
|
+
}
|
|
3463
|
+
|
|
3464
|
+
// Collinear / endpoint inclusion checks.
|
|
3465
|
+
if (Math.abs(o1) <= EPS2D && pointOnSegmentInclusive2D(b1, a1, a2, EPS2D)) {
|
|
3466
|
+
return true;
|
|
3467
|
+
}
|
|
3468
|
+
if (Math.abs(o2) <= EPS2D && pointOnSegmentInclusive2D(b2, a1, a2, EPS2D)) {
|
|
3469
|
+
return true;
|
|
3470
|
+
}
|
|
3471
|
+
if (Math.abs(o3) <= EPS2D && pointOnSegmentInclusive2D(a1, b1, b2, EPS2D)) {
|
|
3472
|
+
return true;
|
|
3473
|
+
}
|
|
3474
|
+
if (Math.abs(o4) <= EPS2D && pointOnSegmentInclusive2D(a2, b1, b2, EPS2D)) {
|
|
3475
|
+
return true;
|
|
3476
|
+
}
|
|
3477
|
+
return false;
|
|
3478
|
+
};
|
|
3479
|
+
edgesA = [[vertex1TriangleA, vertex2TriangleA], [vertex2TriangleA, vertex3TriangleA], [vertex3TriangleA, vertex1TriangleA]];
|
|
3480
|
+
edgesB = [[vertex1TriangleB, vertex2TriangleB], [vertex2TriangleB, vertex3TriangleB], [vertex3TriangleB, vertex1TriangleB]];
|
|
3481
|
+
for (i = 0, len = edgesA.length; i < len; i++) {
|
|
3482
|
+
[a1, a2] = edgesA[i];
|
|
3483
|
+
for (j = 0, len1 = edgesB.length; j < len1; j++) {
|
|
3484
|
+
[b1, b2] = edgesB[j];
|
|
3485
|
+
if (segmentIntersectsInclusive(a1, a2, b1, b2)) {
|
|
3486
|
+
return true;
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
3490
|
+
return false;
|
|
3491
|
+
};
|
|
3492
|
+
|
|
3493
|
+
intersectionTestVertex2D = function(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB) {
|
|
3494
|
+
if (pointInTriangleInclusive2D(vertex1TriangleB, vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, EPS2D)) {
|
|
3495
|
+
// Returns true if any vertex of triangle B lies inside (or on) triangle A OR any vertex of triangle A lies inside (or on) triangle B.
|
|
3496
|
+
return true;
|
|
3497
|
+
}
|
|
3498
|
+
if (pointInTriangleInclusive2D(vertex2TriangleB, vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, EPS2D)) {
|
|
3499
|
+
return true;
|
|
3500
|
+
}
|
|
3501
|
+
if (pointInTriangleInclusive2D(vertex3TriangleB, vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, EPS2D)) {
|
|
3502
|
+
return true;
|
|
3503
|
+
}
|
|
3504
|
+
if (pointInTriangleInclusive2D(vertex1TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, EPS2D)) {
|
|
3505
|
+
return true;
|
|
3506
|
+
}
|
|
3507
|
+
if (pointInTriangleInclusive2D(vertex2TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, EPS2D)) {
|
|
3508
|
+
return true;
|
|
3509
|
+
}
|
|
3510
|
+
if (pointInTriangleInclusive2D(vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, EPS2D)) {
|
|
3511
|
+
return true;
|
|
3512
|
+
}
|
|
3513
|
+
return false;
|
|
3514
|
+
};
|
|
3515
|
+
|
|
3516
|
+
constructIntersection = function(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, additions) {
|
|
3517
|
+
var alpha, crossNormal;
|
|
3518
|
+
alpha = void 0;
|
|
3519
|
+
crossNormal = new Vector3();
|
|
3520
|
+
// Compute cross product for triangle orientation.
|
|
3521
|
+
tempVector1.subVectors(vertex2TriangleA, vertex1TriangleA);
|
|
3522
|
+
tempVector2.subVectors(vertex3TriangleB, vertex1TriangleA);
|
|
3523
|
+
crossNormal.copy(tempVector1).cross(tempVector2);
|
|
3524
|
+
tempVector3.subVectors(vertex1TriangleB, vertex1TriangleA);
|
|
3525
|
+
if (tempVector3.dot(crossNormal) > 0) {
|
|
3526
|
+
// Check orientation with triangle A's third vertex.
|
|
3527
|
+
tempVector1.subVectors(vertex3TriangleA, vertex1TriangleA);
|
|
3528
|
+
crossNormal.copy(tempVector1).cross(tempVector2);
|
|
3529
|
+
if (tempVector3.dot(crossNormal) <= 0) {
|
|
3530
|
+
// Check orientation with triangle B's second vertex.
|
|
3531
|
+
tempVector2.subVectors(vertex2TriangleB, vertex1TriangleA);
|
|
3532
|
+
crossNormal.copy(tempVector1).cross(tempVector2);
|
|
3533
|
+
if (tempVector3.dot(crossNormal) > 0) {
|
|
3534
|
+
// Compute intersection segment endpoints (case 1).
|
|
3535
|
+
tempVector1.subVectors(vertex1TriangleA, vertex1TriangleB);
|
|
3536
|
+
tempVector2.subVectors(vertex1TriangleA, vertex3TriangleA);
|
|
3537
|
+
alpha = tempVector1.dot(additions.normal2) / tempVector2.dot(additions.normal2);
|
|
3538
|
+
if (!isFinite(alpha)) {
|
|
3539
|
+
return false;
|
|
3540
|
+
}
|
|
3541
|
+
tempVector1.copy(tempVector2).multiplyScalar(alpha);
|
|
3542
|
+
additions.source.subVectors(vertex1TriangleA, tempVector1);
|
|
3543
|
+
tempVector1.subVectors(vertex1TriangleB, vertex1TriangleA);
|
|
3544
|
+
tempVector2.subVectors(vertex1TriangleB, vertex3TriangleB);
|
|
3545
|
+
alpha = tempVector1.dot(additions.normal1) / tempVector2.dot(additions.normal1);
|
|
3546
|
+
if (!isFinite(alpha)) {
|
|
3547
|
+
return false;
|
|
3548
|
+
}
|
|
3549
|
+
tempVector1.copy(tempVector2).multiplyScalar(alpha);
|
|
3550
|
+
additions.target.subVectors(vertex1TriangleB, tempVector1);
|
|
3551
|
+
return true;
|
|
3552
|
+
} else {
|
|
3553
|
+
// Compute intersection segment endpoints (case 2).
|
|
3554
|
+
tempVector1.subVectors(vertex1TriangleB, vertex1TriangleA);
|
|
3555
|
+
tempVector2.subVectors(vertex1TriangleB, vertex2TriangleB);
|
|
3556
|
+
alpha = tempVector1.dot(additions.normal1) / tempVector2.dot(additions.normal1);
|
|
3557
|
+
if (!isFinite(alpha)) {
|
|
3558
|
+
return false;
|
|
3559
|
+
}
|
|
3560
|
+
tempVector1.copy(tempVector2).multiplyScalar(alpha);
|
|
3561
|
+
additions.source.subVectors(vertex1TriangleB, tempVector1);
|
|
3562
|
+
tempVector1.subVectors(vertex1TriangleB, vertex1TriangleA);
|
|
3563
|
+
tempVector2.subVectors(vertex1TriangleB, vertex3TriangleB);
|
|
3564
|
+
alpha = tempVector1.dot(additions.normal1) / tempVector2.dot(additions.normal1);
|
|
3565
|
+
if (!isFinite(alpha)) {
|
|
3566
|
+
return false;
|
|
3567
|
+
}
|
|
3568
|
+
tempVector1.copy(tempVector2).multiplyScalar(alpha);
|
|
3569
|
+
additions.target.subVectors(vertex1TriangleB, tempVector1);
|
|
3570
|
+
return true;
|
|
3571
|
+
}
|
|
3572
|
+
} else {
|
|
3573
|
+
return false; // No intersection, orientation test failed.
|
|
3574
|
+
}
|
|
3575
|
+
} else {
|
|
3576
|
+
tempVector2.subVectors(vertex2TriangleB, vertex1TriangleA);
|
|
3577
|
+
crossNormal.copy(tempVector1).cross(tempVector2);
|
|
3578
|
+
if (tempVector3.dot(crossNormal) < 0) {
|
|
3579
|
+
return false; // No intersection, orientation test failed.
|
|
3580
|
+
} else {
|
|
3581
|
+
tempVector1.subVectors(vertex3TriangleA, vertex1TriangleA);
|
|
3582
|
+
crossNormal.copy(tempVector1).cross(tempVector2);
|
|
3583
|
+
if (tempVector3.dot(crossNormal) < 0) {
|
|
3584
|
+
// Compute intersection segment endpoints (case 3).
|
|
3585
|
+
tempVector1.subVectors(vertex1TriangleB, vertex1TriangleA);
|
|
3586
|
+
tempVector2.subVectors(vertex1TriangleB, vertex2TriangleB);
|
|
3587
|
+
alpha = tempVector1.dot(additions.normal1) / tempVector2.dot(additions.normal1);
|
|
3588
|
+
if (!isFinite(alpha)) {
|
|
3589
|
+
return false;
|
|
3590
|
+
}
|
|
3591
|
+
tempVector1.copy(tempVector2).multiplyScalar(alpha);
|
|
3592
|
+
additions.source.subVectors(vertex1TriangleB, tempVector1);
|
|
3593
|
+
tempVector1.subVectors(vertex1TriangleB, vertex1TriangleA);
|
|
3594
|
+
tempVector2.subVectors(vertex1TriangleB, vertex3TriangleB);
|
|
3595
|
+
alpha = tempVector1.dot(additions.normal1) / tempVector2.dot(additions.normal1);
|
|
3596
|
+
if (!isFinite(alpha)) {
|
|
3597
|
+
return false;
|
|
3598
|
+
}
|
|
3599
|
+
tempVector1.copy(tempVector2).multiplyScalar(alpha);
|
|
3600
|
+
additions.target.subVectors(vertex1TriangleB, tempVector1);
|
|
3601
|
+
return true;
|
|
3602
|
+
} else {
|
|
3603
|
+
// Compute intersection segment endpoints (case 4).
|
|
3604
|
+
tempVector1.subVectors(vertex1TriangleA, vertex1TriangleB);
|
|
3605
|
+
tempVector2.subVectors(vertex1TriangleA, vertex3TriangleA);
|
|
3606
|
+
alpha = tempVector1.dot(additions.normal2) / tempVector2.dot(additions.normal2);
|
|
3607
|
+
if (!isFinite(alpha)) {
|
|
3608
|
+
return false;
|
|
3609
|
+
}
|
|
3610
|
+
tempVector1.copy(tempVector2).multiplyScalar(alpha);
|
|
3611
|
+
additions.source.subVectors(vertex1TriangleA, tempVector1);
|
|
3612
|
+
tempVector1.subVectors(vertex1TriangleA, vertex1TriangleB);
|
|
3613
|
+
tempVector2.subVectors(vertex1TriangleA, vertex2TriangleA);
|
|
3614
|
+
alpha = tempVector1.dot(additions.normal2) / tempVector2.dot(additions.normal2);
|
|
3615
|
+
if (!isFinite(alpha)) {
|
|
3616
|
+
return false;
|
|
3617
|
+
}
|
|
3618
|
+
tempVector1.copy(tempVector2).multiplyScalar(alpha);
|
|
3619
|
+
additions.target.subVectors(vertex1TriangleA, tempVector1);
|
|
3620
|
+
return true;
|
|
3621
|
+
}
|
|
3622
|
+
}
|
|
3623
|
+
}
|
|
3624
|
+
return false; // If none of the above, no intersection found.
|
|
3625
|
+
};
|
|
3626
|
+
// Generated by CoffeeScript 2.7.0
|
|
3627
|
+
module.exports.Polytree = Polytree;
|
|
3628
|
+
|
|
3629
|
+
module.exports.Plane = Plane;
|
|
3630
|
+
|
|
3631
|
+
module.exports.Vertex = Vertex;
|
|
3632
|
+
|
|
3633
|
+
module.exports.Polygon = Polygon;
|
|
3634
|
+
|
|
3635
|
+
module.exports.rayIntersectsTriangle = rayIntersectsTriangle;
|
|
3636
|
+
|
|
3637
|
+
module.exports.triangleIntersectsTriangle = triangleIntersectsTriangle;
|
|
3638
|
+
|
|
3639
|
+
module.exports.resolveTriangleIntersection = resolveTriangleIntersection;
|
|
3640
|
+
|
|
3641
|
+
module.exports.resolveCoplanarTriangleIntersection = resolveCoplanarTriangleIntersection;
|
|
3642
|
+
|
|
3643
|
+
module.exports.trianglesOverlap2D = trianglesOverlap2D;
|
|
3644
|
+
|
|
3645
|
+
module.exports.triangleOrientation2D = triangleOrientation2D;
|
|
3646
|
+
|
|
3647
|
+
module.exports.triangleIntersectionCCW2D = triangleIntersectionCCW2D;
|
|
3648
|
+
|
|
3649
|
+
module.exports.intersectionTestEdge2D = intersectionTestEdge2D;
|
|
3650
|
+
|
|
3651
|
+
module.exports.intersectionTestVertex2D = intersectionTestVertex2D;
|
|
3652
|
+
|
|
3653
|
+
module.exports.constructIntersection = constructIntersection;
|