@jgphilpott/polytree 0.1.3 → 0.1.5
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/package.json +10 -5
- package/polytree.bundle.browser.esm.js +3759 -0
- package/polytree.bundle.browser.js +33 -9
- package/polytree.bundle.browser.min.js +1 -1
- package/polytree.bundle.js +33 -9
- package/polytree.bundle.min.js +1 -1
|
@@ -0,0 +1,3759 @@
|
|
|
1
|
+
// polytree.bundle.browser.js
|
|
2
|
+
import { Vector2, Vector3, Box3, Line3, Sphere, Matrix3, Triangle, Ray, Raycaster, Mesh, DoubleSide, BufferGeometry, BufferAttribute, Plane as ThreePlane } from "three";
|
|
3
|
+
var ASYNC_OPERATION_TIMEOUT;
|
|
4
|
+
var DEBUG_COLOR_BACK;
|
|
5
|
+
var DEBUG_COLOR_COPLANAR;
|
|
6
|
+
var DEBUG_COLOR_FRONT;
|
|
7
|
+
var DEBUG_COLOR_SPANNING;
|
|
8
|
+
var DEBUG_GEOMETRY_VALIDATION;
|
|
9
|
+
var DEBUG_INTERSECTION_VERIFICATION;
|
|
10
|
+
var DEBUG_PERFORMANCE_TIMING;
|
|
11
|
+
var DEBUG_VERBOSE_LOGGING;
|
|
12
|
+
var DEFAULT_BUFFER_SIZE;
|
|
13
|
+
var DEFAULT_COORDINATE_PRECISION;
|
|
14
|
+
var DEFAULT_MATERIAL_INDEX;
|
|
15
|
+
var GARBAGE_COLLECTION_THRESHOLD;
|
|
16
|
+
var GEOMETRIC_EPSILON;
|
|
17
|
+
var GEOMETRY_CACHE_SIZE;
|
|
18
|
+
var INTERSECTION_CACHE_SIZE;
|
|
19
|
+
var MAX_REFINEMENT_ITERATIONS;
|
|
20
|
+
var MAX_WORKER_THREADS;
|
|
21
|
+
var MEMORY_USAGE_WARNING_LIMIT;
|
|
22
|
+
var POINT_COINCIDENCE_THRESHOLD;
|
|
23
|
+
var POLYGON_BACK;
|
|
24
|
+
var POLYGON_COPLANAR;
|
|
25
|
+
var POLYGON_FRONT;
|
|
26
|
+
var POLYGON_SPANNING;
|
|
27
|
+
var POLYTREE_MAX_DEPTH;
|
|
28
|
+
var POLYTREE_MAX_POLYGONS_PER_NODE;
|
|
29
|
+
var POLYTREE_MIN_NODE_SIZE;
|
|
30
|
+
var RAY_INTERSECTION_EPSILON;
|
|
31
|
+
var TRIANGLE_2D_EPSILON;
|
|
32
|
+
var WINDING_NUMBER_FULL_ROTATION;
|
|
33
|
+
var defaultRayDirection;
|
|
34
|
+
var meshOperationNormalVector;
|
|
35
|
+
var meshOperationVertexVector;
|
|
36
|
+
var polygonID;
|
|
37
|
+
var rayTriangleEdge1;
|
|
38
|
+
var rayTriangleEdge2;
|
|
39
|
+
var rayTriangleHVector;
|
|
40
|
+
var rayTriangleQVector;
|
|
41
|
+
var rayTriangleSVector;
|
|
42
|
+
var temporaryBoundingBox;
|
|
43
|
+
var temporaryMatrix3;
|
|
44
|
+
var temporaryMatrixWithNormalCalc;
|
|
45
|
+
var temporaryRay;
|
|
46
|
+
var temporaryRaycaster;
|
|
47
|
+
var temporaryTriangleVertex;
|
|
48
|
+
var temporaryTriangleVertexSecondary;
|
|
49
|
+
var temporaryVector3Primary;
|
|
50
|
+
var temporaryVector3Quaternary;
|
|
51
|
+
var temporaryVector3Secondary;
|
|
52
|
+
var temporaryVector3Tertiary;
|
|
53
|
+
var windingNumberEpsilonOffsets;
|
|
54
|
+
var windingNumberEpsilonOffsetsCount;
|
|
55
|
+
var windingNumberMatrix3;
|
|
56
|
+
var windingNumberTestPoint;
|
|
57
|
+
var windingNumberVector1;
|
|
58
|
+
var windingNumberVector2;
|
|
59
|
+
var windingNumberVector3;
|
|
60
|
+
GEOMETRIC_EPSILON = 1e-8;
|
|
61
|
+
RAY_INTERSECTION_EPSILON = 1e-12;
|
|
62
|
+
TRIANGLE_2D_EPSILON = 1e-14;
|
|
63
|
+
WINDING_NUMBER_FULL_ROTATION = 4 * Math.PI;
|
|
64
|
+
POLYGON_COPLANAR = 0;
|
|
65
|
+
POLYGON_FRONT = 1;
|
|
66
|
+
POLYGON_BACK = 2;
|
|
67
|
+
POLYGON_SPANNING = 3;
|
|
68
|
+
temporaryVector3Primary = new Vector3();
|
|
69
|
+
temporaryVector3Secondary = new Vector3();
|
|
70
|
+
temporaryVector3Tertiary = new Vector3();
|
|
71
|
+
temporaryVector3Quaternary = new Vector3();
|
|
72
|
+
temporaryBoundingBox = new Box3();
|
|
73
|
+
temporaryRaycaster = new Raycaster();
|
|
74
|
+
temporaryRay = new Ray();
|
|
75
|
+
defaultRayDirection = new Vector3(0, 0, 1);
|
|
76
|
+
windingNumberVector1 = new Vector3();
|
|
77
|
+
windingNumberVector2 = new Vector3();
|
|
78
|
+
windingNumberVector3 = new Vector3();
|
|
79
|
+
windingNumberTestPoint = new Vector3();
|
|
80
|
+
windingNumberEpsilonOffsets = [new Vector3(GEOMETRIC_EPSILON, 0, 0), new Vector3(0, GEOMETRIC_EPSILON, 0), new Vector3(0, 0, GEOMETRIC_EPSILON), new Vector3(-GEOMETRIC_EPSILON, 0, 0), new Vector3(0, -GEOMETRIC_EPSILON, 0), new Vector3(0, 0, -GEOMETRIC_EPSILON)];
|
|
81
|
+
windingNumberEpsilonOffsetsCount = windingNumberEpsilonOffsets.length;
|
|
82
|
+
windingNumberMatrix3 = new Matrix3();
|
|
83
|
+
rayTriangleEdge1 = new Vector3();
|
|
84
|
+
rayTriangleEdge2 = new Vector3();
|
|
85
|
+
rayTriangleHVector = new Vector3();
|
|
86
|
+
rayTriangleSVector = new Vector3();
|
|
87
|
+
rayTriangleQVector = new Vector3();
|
|
88
|
+
temporaryTriangleVertex = new Vector3();
|
|
89
|
+
temporaryTriangleVertexSecondary = new Vector3();
|
|
90
|
+
temporaryMatrix3 = new Matrix3();
|
|
91
|
+
temporaryMatrixWithNormalCalc = new Matrix3();
|
|
92
|
+
temporaryMatrixWithNormalCalc.getNormalMatrix = function(matrix) {
|
|
93
|
+
return this.setFromMatrix4(matrix).invert().transpose();
|
|
94
|
+
};
|
|
95
|
+
meshOperationNormalVector = new Vector3();
|
|
96
|
+
meshOperationVertexVector = new Vector3();
|
|
97
|
+
POLYTREE_MAX_DEPTH = 1e3;
|
|
98
|
+
POLYTREE_MAX_POLYGONS_PER_NODE = 1e5;
|
|
99
|
+
POLYTREE_MIN_NODE_SIZE = 1e-6;
|
|
100
|
+
DEFAULT_MATERIAL_INDEX = 0;
|
|
101
|
+
MAX_REFINEMENT_ITERATIONS = 1e4;
|
|
102
|
+
DEFAULT_COORDINATE_PRECISION = 15;
|
|
103
|
+
POINT_COINCIDENCE_THRESHOLD = 1e-15;
|
|
104
|
+
DEFAULT_BUFFER_SIZE = 16384;
|
|
105
|
+
MAX_WORKER_THREADS = Infinity;
|
|
106
|
+
ASYNC_OPERATION_TIMEOUT = Infinity;
|
|
107
|
+
GARBAGE_COLLECTION_THRESHOLD = 1e5;
|
|
108
|
+
MEMORY_USAGE_WARNING_LIMIT = 0.95;
|
|
109
|
+
GEOMETRY_CACHE_SIZE = Infinity;
|
|
110
|
+
INTERSECTION_CACHE_SIZE = Infinity;
|
|
111
|
+
polygonID = 0;
|
|
112
|
+
DEBUG_VERBOSE_LOGGING = false;
|
|
113
|
+
DEBUG_PERFORMANCE_TIMING = false;
|
|
114
|
+
DEBUG_GEOMETRY_VALIDATION = false;
|
|
115
|
+
DEBUG_INTERSECTION_VERIFICATION = false;
|
|
116
|
+
DEBUG_COLOR_FRONT = 65280;
|
|
117
|
+
DEBUG_COLOR_BACK = 16711680;
|
|
118
|
+
DEBUG_COLOR_COPLANAR = 255;
|
|
119
|
+
DEBUG_COLOR_SPANNING = 16776960;
|
|
120
|
+
if (typeof module !== "undefined" && module.exports) {
|
|
121
|
+
}
|
|
122
|
+
var calculateWindingNumberFromBuffer;
|
|
123
|
+
var checkMemoryUsage;
|
|
124
|
+
var clearGeometryCache;
|
|
125
|
+
var clearIntersectionCache;
|
|
126
|
+
var createIntersectionCacheKey;
|
|
127
|
+
var createVector2Buffer;
|
|
128
|
+
var createVector3Buffer;
|
|
129
|
+
var disposePolytreeResources;
|
|
130
|
+
var extractCoordinatesFromArray;
|
|
131
|
+
var geometryCache;
|
|
132
|
+
var geometryCacheKeys;
|
|
133
|
+
var handleIntersectingPolytrees;
|
|
134
|
+
var intersectionCache;
|
|
135
|
+
var intersectionCacheKeys;
|
|
136
|
+
var operationCounter;
|
|
137
|
+
var prepareTriangleBufferFromPolygons;
|
|
138
|
+
var roundPointCoordinates;
|
|
139
|
+
var signedVolumeOfTriangle;
|
|
140
|
+
var sortRaycastIntersectionsByDistance;
|
|
141
|
+
var splitPolygonByPlane;
|
|
142
|
+
var splitPolygonVertexArray;
|
|
143
|
+
var testPolygonInsideUsingWindingNumber;
|
|
144
|
+
var testRayTriangleIntersection;
|
|
145
|
+
intersectionCache = /* @__PURE__ */ new Map();
|
|
146
|
+
intersectionCacheKeys = [];
|
|
147
|
+
geometryCache = /* @__PURE__ */ new Map();
|
|
148
|
+
geometryCacheKeys = [];
|
|
149
|
+
operationCounter = 0;
|
|
150
|
+
clearIntersectionCache = function() {
|
|
151
|
+
var i, key, keysToRemove, len, results;
|
|
152
|
+
if (INTERSECTION_CACHE_SIZE !== Infinity && intersectionCache.size > INTERSECTION_CACHE_SIZE) {
|
|
153
|
+
keysToRemove = intersectionCacheKeys.splice(0, Math.floor(INTERSECTION_CACHE_SIZE / 2));
|
|
154
|
+
results = [];
|
|
155
|
+
for (i = 0, len = keysToRemove.length; i < len; i++) {
|
|
156
|
+
key = keysToRemove[i];
|
|
157
|
+
results.push(intersectionCache.delete(key));
|
|
158
|
+
}
|
|
159
|
+
return results;
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
clearGeometryCache = function() {
|
|
163
|
+
var i, key, keysToRemove, len, results;
|
|
164
|
+
if (GEOMETRY_CACHE_SIZE !== Infinity && geometryCache.size > GEOMETRY_CACHE_SIZE) {
|
|
165
|
+
keysToRemove = geometryCacheKeys.splice(0, Math.floor(GEOMETRY_CACHE_SIZE / 2));
|
|
166
|
+
results = [];
|
|
167
|
+
for (i = 0, len = keysToRemove.length; i < len; i++) {
|
|
168
|
+
key = keysToRemove[i];
|
|
169
|
+
results.push(geometryCache.delete(key));
|
|
170
|
+
}
|
|
171
|
+
return results;
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
createIntersectionCacheKey = function(polygonA, polygonB) {
|
|
175
|
+
return `${polygonA.id}_${polygonB.id}`;
|
|
176
|
+
};
|
|
177
|
+
checkMemoryUsage = function() {
|
|
178
|
+
var heapTotalMB, heapUsedMB, memoryUsage, usageRatio;
|
|
179
|
+
if (typeof process !== "undefined" && process.memoryUsage) {
|
|
180
|
+
memoryUsage = process.memoryUsage();
|
|
181
|
+
heapUsedMB = memoryUsage.heapUsed / 1024 / 1024;
|
|
182
|
+
heapTotalMB = memoryUsage.heapTotal / 1024 / 1024;
|
|
183
|
+
usageRatio = memoryUsage.heapUsed / memoryUsage.heapTotal;
|
|
184
|
+
if (DEBUG_VERBOSE_LOGGING) {
|
|
185
|
+
console.log(`Memory usage: ${heapUsedMB.toFixed(2)}MB / ${heapTotalMB.toFixed(2)}MB (${(usageRatio * 100).toFixed(1)}%)`);
|
|
186
|
+
}
|
|
187
|
+
if (usageRatio > MEMORY_USAGE_WARNING_LIMIT) {
|
|
188
|
+
console.warn(`Memory usage is high: ${(usageRatio * 100).toFixed(1)}%`);
|
|
189
|
+
if (typeof (typeof global !== "undefined" && global !== null ? global.gc : void 0) === "function") {
|
|
190
|
+
console.log("Triggering garbage collection...");
|
|
191
|
+
global.gc();
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return usageRatio;
|
|
195
|
+
}
|
|
196
|
+
return 0;
|
|
197
|
+
};
|
|
198
|
+
createVector2Buffer = function(vectorCount = DEFAULT_BUFFER_SIZE) {
|
|
199
|
+
return {
|
|
200
|
+
top: 0,
|
|
201
|
+
array: new Float32Array(vectorCount * 2),
|
|
202
|
+
write: function(vector) {
|
|
203
|
+
this.array[this.top++] = vector.x;
|
|
204
|
+
return this.array[this.top++] = vector.y;
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
};
|
|
208
|
+
createVector3Buffer = function(vectorCount = DEFAULT_BUFFER_SIZE) {
|
|
209
|
+
return {
|
|
210
|
+
top: 0,
|
|
211
|
+
array: new Float32Array(vectorCount * 3),
|
|
212
|
+
write: function(vector) {
|
|
213
|
+
this.array[this.top++] = vector.x;
|
|
214
|
+
this.array[this.top++] = vector.y;
|
|
215
|
+
return this.array[this.top++] = vector.z;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
};
|
|
219
|
+
sortRaycastIntersectionsByDistance = function(intersectionA, intersectionB) {
|
|
220
|
+
return intersectionA.distance - intersectionB.distance;
|
|
221
|
+
};
|
|
222
|
+
roundPointCoordinates = function(point, decimalPlaces = DEFAULT_COORDINATE_PRECISION) {
|
|
223
|
+
point.x = +point.x.toFixed(decimalPlaces);
|
|
224
|
+
point.y = +point.y.toFixed(decimalPlaces);
|
|
225
|
+
point.z = +point.z.toFixed(decimalPlaces);
|
|
226
|
+
return point;
|
|
227
|
+
};
|
|
228
|
+
extractCoordinatesFromArray = function(coordinatesArray, startIndex) {
|
|
229
|
+
return {
|
|
230
|
+
x: coordinatesArray[startIndex],
|
|
231
|
+
y: coordinatesArray[startIndex + 1],
|
|
232
|
+
z: coordinatesArray[startIndex + 2]
|
|
233
|
+
};
|
|
234
|
+
};
|
|
235
|
+
splitPolygonByPlane = function(polygon, plane, result = []) {
|
|
236
|
+
var backPolygonFragments, backVertices, currentVertex, currentVertexType, distanceToPlane, fragmentIndex, frontPolygonFragments, frontVertices, i, interpolatedVertex, intersectionParameter, j, k, l, nextVertex, nextVertexIndex, nextVertexType, polygonType, ref, ref1, ref2, ref3, returnPolygon, vertexIndex, vertexType, vertexTypes;
|
|
237
|
+
returnPolygon = {
|
|
238
|
+
polygon,
|
|
239
|
+
type: "undecided"
|
|
240
|
+
};
|
|
241
|
+
polygonType = 0;
|
|
242
|
+
vertexTypes = [];
|
|
243
|
+
for (vertexIndex = i = 0, ref = polygon.vertices.length; 0 <= ref ? i < ref : i > ref; vertexIndex = 0 <= ref ? ++i : --i) {
|
|
244
|
+
distanceToPlane = plane.normal.dot(polygon.vertices[vertexIndex].pos) - plane.distanceFromOrigin;
|
|
245
|
+
vertexType = distanceToPlane < -GEOMETRIC_EPSILON ? POLYGON_BACK : distanceToPlane > GEOMETRIC_EPSILON ? POLYGON_FRONT : POLYGON_COPLANAR;
|
|
246
|
+
polygonType |= vertexType;
|
|
247
|
+
vertexTypes.push(vertexType);
|
|
248
|
+
}
|
|
249
|
+
switch (polygonType) {
|
|
250
|
+
case POLYGON_COPLANAR:
|
|
251
|
+
returnPolygon.type = plane.normal.dot(polygon.plane.normal) > 0 ? "coplanar-front" : "coplanar-back";
|
|
252
|
+
result.push(returnPolygon);
|
|
253
|
+
break;
|
|
254
|
+
case POLYGON_FRONT:
|
|
255
|
+
returnPolygon.type = "front";
|
|
256
|
+
result.push(returnPolygon);
|
|
257
|
+
break;
|
|
258
|
+
case POLYGON_BACK:
|
|
259
|
+
returnPolygon.type = "back";
|
|
260
|
+
result.push(returnPolygon);
|
|
261
|
+
break;
|
|
262
|
+
case POLYGON_SPANNING:
|
|
263
|
+
frontVertices = [];
|
|
264
|
+
backVertices = [];
|
|
265
|
+
for (vertexIndex = j = 0, ref1 = polygon.vertices.length; 0 <= ref1 ? j < ref1 : j > ref1; vertexIndex = 0 <= ref1 ? ++j : --j) {
|
|
266
|
+
nextVertexIndex = (vertexIndex + 1) % polygon.vertices.length;
|
|
267
|
+
currentVertexType = vertexTypes[vertexIndex];
|
|
268
|
+
nextVertexType = vertexTypes[nextVertexIndex];
|
|
269
|
+
currentVertex = polygon.vertices[vertexIndex];
|
|
270
|
+
nextVertex = polygon.vertices[nextVertexIndex];
|
|
271
|
+
if (currentVertexType !== POLYGON_BACK) {
|
|
272
|
+
frontVertices.push(currentVertex);
|
|
273
|
+
}
|
|
274
|
+
if (currentVertexType !== POLYGON_FRONT) {
|
|
275
|
+
backVertices.push(currentVertexType !== POLYGON_BACK ? currentVertex.clone() : currentVertex);
|
|
276
|
+
}
|
|
277
|
+
if ((currentVertexType | nextVertexType) === POLYGON_SPANNING) {
|
|
278
|
+
intersectionParameter = (plane.distanceFromOrigin - plane.normal.dot(currentVertex.pos)) / plane.normal.dot(temporaryTriangleVertex.copy(nextVertex.pos).sub(currentVertex.pos));
|
|
279
|
+
interpolatedVertex = currentVertex.interpolate(nextVertex, intersectionParameter);
|
|
280
|
+
frontVertices.push(interpolatedVertex);
|
|
281
|
+
backVertices.push(interpolatedVertex.clone());
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (frontVertices.length >= 3) {
|
|
285
|
+
if (frontVertices.length > 3) {
|
|
286
|
+
frontPolygonFragments = splitPolygonVertexArray(frontVertices);
|
|
287
|
+
for (fragmentIndex = k = 0, ref2 = frontPolygonFragments.length; 0 <= ref2 ? k < ref2 : k > ref2; fragmentIndex = 0 <= ref2 ? ++k : --k) {
|
|
288
|
+
result.push({
|
|
289
|
+
polygon: new Polygon(frontPolygonFragments[fragmentIndex], polygon.shared),
|
|
290
|
+
type: "front"
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
} else {
|
|
294
|
+
result.push({
|
|
295
|
+
polygon: new Polygon(frontVertices, polygon.shared),
|
|
296
|
+
type: "front"
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (backVertices.length >= 3) {
|
|
301
|
+
if (backVertices.length > 3) {
|
|
302
|
+
backPolygonFragments = splitPolygonVertexArray(backVertices);
|
|
303
|
+
for (fragmentIndex = l = 0, ref3 = backPolygonFragments.length; 0 <= ref3 ? l < ref3 : l > ref3; fragmentIndex = 0 <= ref3 ? ++l : --l) {
|
|
304
|
+
result.push({
|
|
305
|
+
polygon: new Polygon(backPolygonFragments[fragmentIndex], polygon.shared),
|
|
306
|
+
type: "back"
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
} else {
|
|
310
|
+
result.push({
|
|
311
|
+
polygon: new Polygon(backVertices, polygon.shared),
|
|
312
|
+
type: "back"
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (result.length === 0) {
|
|
318
|
+
result.push(returnPolygon);
|
|
319
|
+
}
|
|
320
|
+
return result;
|
|
321
|
+
};
|
|
322
|
+
splitPolygonVertexArray = function(vertexArray) {
|
|
323
|
+
var i, ref, triangleFragments, triangleIndex, triangleVertices;
|
|
324
|
+
triangleFragments = [];
|
|
325
|
+
if (vertexArray.length > 4) {
|
|
326
|
+
console.warn("[splitPolygonVertexArray] vertexArray.length > 4", vertexArray.length);
|
|
327
|
+
for (triangleIndex = i = 3, ref = vertexArray.length; 3 <= ref ? i <= ref : i >= ref; triangleIndex = 3 <= ref ? ++i : --i) {
|
|
328
|
+
triangleVertices = [];
|
|
329
|
+
triangleVertices.push(vertexArray[0].clone());
|
|
330
|
+
triangleVertices.push(vertexArray[triangleIndex - 2].clone());
|
|
331
|
+
triangleVertices.push(vertexArray[triangleIndex - 1].clone());
|
|
332
|
+
triangleFragments.push(triangleVertices);
|
|
333
|
+
}
|
|
334
|
+
} else {
|
|
335
|
+
if (vertexArray[0].pos.distanceTo(vertexArray[2].pos) <= vertexArray[1].pos.distanceTo(vertexArray[3].pos)) {
|
|
336
|
+
triangleFragments.push([vertexArray[0].clone(), vertexArray[1].clone(), vertexArray[2].clone()], [vertexArray[0].clone(), vertexArray[2].clone(), vertexArray[3].clone()]);
|
|
337
|
+
} else {
|
|
338
|
+
triangleFragments.push([vertexArray[0].clone(), vertexArray[1].clone(), vertexArray[3].clone()], [vertexArray[1].clone(), vertexArray[2].clone(), vertexArray[3].clone()]);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return triangleFragments;
|
|
342
|
+
};
|
|
343
|
+
calculateWindingNumberFromBuffer = function(triangleDataArray, testPoint) {
|
|
344
|
+
var i, ref, solidAngle, triangleStartIndex, vectorLengthA, vectorLengthB, vectorLengthC, windingNumber;
|
|
345
|
+
windingNumber = 0;
|
|
346
|
+
for (triangleStartIndex = i = 0, ref = triangleDataArray.length; i < ref; triangleStartIndex = i += 9) {
|
|
347
|
+
windingNumberVector1.subVectors(extractCoordinatesFromArray(triangleDataArray, triangleStartIndex), testPoint);
|
|
348
|
+
windingNumberVector2.subVectors(extractCoordinatesFromArray(triangleDataArray, triangleStartIndex + 3), testPoint);
|
|
349
|
+
windingNumberVector3.subVectors(extractCoordinatesFromArray(triangleDataArray, triangleStartIndex + 6), testPoint);
|
|
350
|
+
vectorLengthA = windingNumberVector1.length();
|
|
351
|
+
vectorLengthB = windingNumberVector2.length();
|
|
352
|
+
vectorLengthC = windingNumberVector3.length();
|
|
353
|
+
if (vectorLengthA < POINT_COINCIDENCE_THRESHOLD || vectorLengthB < POINT_COINCIDENCE_THRESHOLD || vectorLengthC < POINT_COINCIDENCE_THRESHOLD) {
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
windingNumberMatrix3.set(windingNumberVector1.x, windingNumberVector1.y, windingNumberVector1.z, windingNumberVector2.x, windingNumberVector2.y, windingNumberVector2.z, windingNumberVector3.x, windingNumberVector3.y, windingNumberVector3.z);
|
|
357
|
+
solidAngle = 2 * Math.atan2(windingNumberMatrix3.determinant(), vectorLengthA * vectorLengthB * vectorLengthC + windingNumberVector1.dot(windingNumberVector2) * vectorLengthC + windingNumberVector2.dot(windingNumberVector3) * vectorLengthA + windingNumberVector3.dot(windingNumberVector1) * vectorLengthB);
|
|
358
|
+
windingNumber += solidAngle;
|
|
359
|
+
}
|
|
360
|
+
windingNumber = Math.round(windingNumber / WINDING_NUMBER_FULL_ROTATION);
|
|
361
|
+
return windingNumber;
|
|
362
|
+
};
|
|
363
|
+
testPolygonInsideUsingWindingNumber = function(triangleDataArray, testPoint, isCoplanar) {
|
|
364
|
+
var i, isInside, maxOffsetTests, offsetIndex, ref, windingNumber;
|
|
365
|
+
isInside = false;
|
|
366
|
+
windingNumberTestPoint.copy(testPoint);
|
|
367
|
+
windingNumber = calculateWindingNumberFromBuffer(triangleDataArray, windingNumberTestPoint);
|
|
368
|
+
if (windingNumber === 0) {
|
|
369
|
+
if (isCoplanar) {
|
|
370
|
+
maxOffsetTests = Math.min(windingNumberEpsilonOffsetsCount, MAX_REFINEMENT_ITERATIONS);
|
|
371
|
+
for (offsetIndex = i = 0, ref = maxOffsetTests; 0 <= ref ? i < ref : i > ref; offsetIndex = 0 <= ref ? ++i : --i) {
|
|
372
|
+
windingNumberTestPoint.copy(testPoint).add(windingNumberEpsilonOffsets[offsetIndex]);
|
|
373
|
+
windingNumber = calculateWindingNumberFromBuffer(triangleDataArray, windingNumberTestPoint);
|
|
374
|
+
if (windingNumber !== 0) {
|
|
375
|
+
isInside = true;
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
} else {
|
|
381
|
+
isInside = true;
|
|
382
|
+
}
|
|
383
|
+
return isInside;
|
|
384
|
+
};
|
|
385
|
+
prepareTriangleBufferFromPolygons = function(polygonArray) {
|
|
386
|
+
var bufferIndex, coordinateArray, i, polygonIndex, ref, triangle, triangleCount;
|
|
387
|
+
triangleCount = polygonArray.length;
|
|
388
|
+
coordinateArray = new Float32Array(triangleCount * 3 * 3);
|
|
389
|
+
bufferIndex = 0;
|
|
390
|
+
for (polygonIndex = i = 0, ref = triangleCount; 0 <= ref ? i < ref : i > ref; polygonIndex = 0 <= ref ? ++i : --i) {
|
|
391
|
+
triangle = polygonArray[polygonIndex].triangle;
|
|
392
|
+
coordinateArray[bufferIndex++] = triangle.a.x;
|
|
393
|
+
coordinateArray[bufferIndex++] = triangle.a.y;
|
|
394
|
+
coordinateArray[bufferIndex++] = triangle.a.z;
|
|
395
|
+
coordinateArray[bufferIndex++] = triangle.b.x;
|
|
396
|
+
coordinateArray[bufferIndex++] = triangle.b.y;
|
|
397
|
+
coordinateArray[bufferIndex++] = triangle.b.z;
|
|
398
|
+
coordinateArray[bufferIndex++] = triangle.c.x;
|
|
399
|
+
coordinateArray[bufferIndex++] = triangle.c.y;
|
|
400
|
+
coordinateArray[bufferIndex++] = triangle.c.z;
|
|
401
|
+
}
|
|
402
|
+
return coordinateArray;
|
|
403
|
+
};
|
|
404
|
+
testRayTriangleIntersection = function(ray, triangle, targetVector = new Vector3()) {
|
|
405
|
+
var determinant, firstBarycentricCoordinate, intersectionDistance, inverseDeterminant, secondBarycentricCoordinate;
|
|
406
|
+
rayTriangleEdge1.subVectors(triangle.b, triangle.a);
|
|
407
|
+
rayTriangleEdge2.subVectors(triangle.c, triangle.a);
|
|
408
|
+
rayTriangleHVector.crossVectors(ray.direction, rayTriangleEdge2);
|
|
409
|
+
determinant = rayTriangleEdge1.dot(rayTriangleHVector);
|
|
410
|
+
if (determinant > -RAY_INTERSECTION_EPSILON && determinant < RAY_INTERSECTION_EPSILON) {
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
inverseDeterminant = 1 / determinant;
|
|
414
|
+
rayTriangleSVector.subVectors(ray.origin, triangle.a);
|
|
415
|
+
firstBarycentricCoordinate = inverseDeterminant * rayTriangleSVector.dot(rayTriangleHVector);
|
|
416
|
+
if (firstBarycentricCoordinate < 0 || firstBarycentricCoordinate > 1) {
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
rayTriangleQVector.crossVectors(rayTriangleSVector, rayTriangleEdge1);
|
|
420
|
+
secondBarycentricCoordinate = inverseDeterminant * ray.direction.dot(rayTriangleQVector);
|
|
421
|
+
if (secondBarycentricCoordinate < 0 || firstBarycentricCoordinate + secondBarycentricCoordinate > 1) {
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
424
|
+
intersectionDistance = inverseDeterminant * rayTriangleEdge2.dot(rayTriangleQVector);
|
|
425
|
+
if (intersectionDistance > RAY_INTERSECTION_EPSILON) {
|
|
426
|
+
return targetVector.copy(ray.direction).multiplyScalar(intersectionDistance).add(ray.origin);
|
|
427
|
+
}
|
|
428
|
+
return null;
|
|
429
|
+
};
|
|
430
|
+
handleIntersectingPolytrees = function(polytreeA, polytreeB, processBothDirections = true) {
|
|
431
|
+
var polytreeABuffer, polytreeBBuffer;
|
|
432
|
+
operationCounter++;
|
|
433
|
+
if (operationCounter >= GARBAGE_COLLECTION_THRESHOLD) {
|
|
434
|
+
operationCounter = 0;
|
|
435
|
+
clearIntersectionCache();
|
|
436
|
+
clearGeometryCache();
|
|
437
|
+
checkMemoryUsage();
|
|
438
|
+
}
|
|
439
|
+
polytreeABuffer = void 0;
|
|
440
|
+
polytreeBBuffer = void 0;
|
|
441
|
+
if (Polytree.useWindingNumber === true) {
|
|
442
|
+
if (processBothDirections) {
|
|
443
|
+
polytreeABuffer = prepareTriangleBufferFromPolygons(polytreeA.getPolygons());
|
|
444
|
+
}
|
|
445
|
+
polytreeBBuffer = prepareTriangleBufferFromPolygons(polytreeB.getPolygons());
|
|
446
|
+
}
|
|
447
|
+
polytreeA.handleIntersectingPolygons(polytreeB, polytreeBBuffer);
|
|
448
|
+
if (processBothDirections) {
|
|
449
|
+
polytreeB.handleIntersectingPolygons(polytreeA, polytreeABuffer);
|
|
450
|
+
}
|
|
451
|
+
if (polytreeABuffer !== void 0) {
|
|
452
|
+
polytreeABuffer = void 0;
|
|
453
|
+
}
|
|
454
|
+
if (polytreeBBuffer !== void 0) {
|
|
455
|
+
return polytreeBBuffer = void 0;
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
signedVolumeOfTriangle = function(vertex1, vertex2, vertex3) {
|
|
459
|
+
temporaryVector3Primary.copy(vertex2).cross(vertex3);
|
|
460
|
+
return vertex1.dot(temporaryVector3Primary) / 6;
|
|
461
|
+
};
|
|
462
|
+
disposePolytreeResources = function(...polytreeInstances) {
|
|
463
|
+
if (Polytree.disposePolytree) {
|
|
464
|
+
return polytreeInstances.forEach(function(polytreeInstance) {
|
|
465
|
+
return polytreeInstance.delete();
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
var handleObjectForOperation;
|
|
470
|
+
var operationHandler;
|
|
471
|
+
operationHandler = function(operationObject, returnPolytrees = false, buildTargetPolytree = true, options = {
|
|
472
|
+
objCounter: 0
|
|
473
|
+
}, firstRun = true, async = true) {
|
|
474
|
+
var allPolygons, defaultMaterial, finalMesh, firstOperand, materialForMesh, ref, resultPolytree, secondOperand;
|
|
475
|
+
if (async) {
|
|
476
|
+
return new Promise(function(resolve, reject) {
|
|
477
|
+
var asyncError, firstOperand2, materialForMesh2, operandPromise, operandPromises, originalMaterialA, originalMaterialB, ref2, ref1, resultPolytree2, secondOperand2;
|
|
478
|
+
try {
|
|
479
|
+
firstOperand2 = void 0;
|
|
480
|
+
secondOperand2 = void 0;
|
|
481
|
+
resultPolytree2 = void 0;
|
|
482
|
+
materialForMesh2 = void 0;
|
|
483
|
+
originalMaterialA = (ref2 = operationObject.objA) != null ? ref2.material : void 0;
|
|
484
|
+
originalMaterialB = (ref1 = operationObject.objB) != null ? ref1.material : void 0;
|
|
485
|
+
if (operationObject.material) {
|
|
486
|
+
materialForMesh2 = operationObject.material;
|
|
487
|
+
}
|
|
488
|
+
operandPromises = [];
|
|
489
|
+
if (operationObject.objA) {
|
|
490
|
+
operandPromise = handleObjectForOperation(operationObject.objA, returnPolytrees, buildTargetPolytree, options, 0, async);
|
|
491
|
+
operandPromises.push(operandPromise);
|
|
492
|
+
}
|
|
493
|
+
if (operationObject.objB) {
|
|
494
|
+
operandPromise = handleObjectForOperation(operationObject.objB, returnPolytrees, buildTargetPolytree, options, 1, async);
|
|
495
|
+
operandPromises.push(operandPromise);
|
|
496
|
+
}
|
|
497
|
+
return Promise.allSettled(operandPromises).then(function(promiseResults) {
|
|
498
|
+
var operationPromise;
|
|
499
|
+
promiseResults.forEach(function(promiseResult) {
|
|
500
|
+
if (promiseResult.status === "fulfilled") {
|
|
501
|
+
if (promiseResult.value.objIndex === 0) {
|
|
502
|
+
return firstOperand2 = promiseResult.value;
|
|
503
|
+
} else if (promiseResult.value.objIndex === 1) {
|
|
504
|
+
return secondOperand2 = promiseResult.value;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
if (returnPolytrees === true) {
|
|
509
|
+
if (firstOperand2) {
|
|
510
|
+
operationObject.objA = firstOperand2.original;
|
|
511
|
+
firstOperand2 = firstOperand2.result;
|
|
512
|
+
}
|
|
513
|
+
if (secondOperand2) {
|
|
514
|
+
operationObject.objB = secondOperand2.original;
|
|
515
|
+
secondOperand2 = secondOperand2.result;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
operationPromise = void 0;
|
|
519
|
+
switch (operationObject.op) {
|
|
520
|
+
case "unite":
|
|
521
|
+
if (firstOperand2 && secondOperand2) {
|
|
522
|
+
operationPromise = Polytree.async.unite(firstOperand2, secondOperand2, buildTargetPolytree);
|
|
523
|
+
} else {
|
|
524
|
+
operationPromise = Promise.resolve(firstOperand2 || secondOperand2 || new Polytree());
|
|
525
|
+
}
|
|
526
|
+
break;
|
|
527
|
+
case "subtract":
|
|
528
|
+
if (firstOperand2 && secondOperand2) {
|
|
529
|
+
operationPromise = Polytree.async.subtract(firstOperand2, secondOperand2, buildTargetPolytree);
|
|
530
|
+
} else {
|
|
531
|
+
operationPromise = Promise.resolve(firstOperand2 || new Polytree());
|
|
532
|
+
}
|
|
533
|
+
break;
|
|
534
|
+
case "intersect":
|
|
535
|
+
if (firstOperand2 && secondOperand2) {
|
|
536
|
+
operationPromise = Polytree.async.intersect(firstOperand2, secondOperand2, buildTargetPolytree);
|
|
537
|
+
} else {
|
|
538
|
+
operationPromise = Promise.resolve(new Polytree());
|
|
539
|
+
}
|
|
540
|
+
break;
|
|
541
|
+
default:
|
|
542
|
+
operationPromise = Promise.resolve(new Polytree());
|
|
543
|
+
}
|
|
544
|
+
return operationPromise.then(function(resultPolytree3) {
|
|
545
|
+
var allPolygons2, defaultMaterial2, finalMesh2;
|
|
546
|
+
if (resultPolytree3 && !resultPolytree3.box && resultPolytree3.bounds) {
|
|
547
|
+
resultPolytree3.buildTree();
|
|
548
|
+
}
|
|
549
|
+
if (firstRun && !returnPolytrees) {
|
|
550
|
+
allPolygons2 = resultPolytree3.getPolygons();
|
|
551
|
+
if (!resultPolytree3 || allPolygons2.length === 0) {
|
|
552
|
+
resolve(void 0);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
if (materialForMesh2) {
|
|
556
|
+
finalMesh2 = Polytree.toMesh(resultPolytree3, materialForMesh2);
|
|
557
|
+
} else {
|
|
558
|
+
defaultMaterial2 = void 0;
|
|
559
|
+
if (originalMaterialA) {
|
|
560
|
+
defaultMaterial2 = Array.isArray(originalMaterialA) ? originalMaterialA[0] : originalMaterialA;
|
|
561
|
+
defaultMaterial2 = defaultMaterial2.clone();
|
|
562
|
+
} else if (originalMaterialB) {
|
|
563
|
+
defaultMaterial2 = Array.isArray(originalMaterialB) ? originalMaterialB[0] : originalMaterialB;
|
|
564
|
+
defaultMaterial2 = defaultMaterial2.clone();
|
|
565
|
+
} else {
|
|
566
|
+
resolve(void 0);
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
finalMesh2 = Polytree.toMesh(resultPolytree3, defaultMaterial2);
|
|
570
|
+
}
|
|
571
|
+
disposePolytreeResources(resultPolytree3);
|
|
572
|
+
resolve(finalMesh2);
|
|
573
|
+
} else if (firstRun && returnPolytrees) {
|
|
574
|
+
if (materialForMesh2) {
|
|
575
|
+
finalMesh2 = Polytree.toMesh(resultPolytree3, materialForMesh2);
|
|
576
|
+
disposePolytreeResources(resultPolytree3);
|
|
577
|
+
resolve({
|
|
578
|
+
result: finalMesh2,
|
|
579
|
+
operationTree: operationObject
|
|
580
|
+
});
|
|
581
|
+
} else {
|
|
582
|
+
resolve({
|
|
583
|
+
result: resultPolytree3,
|
|
584
|
+
operationTree: operationObject
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
} else {
|
|
588
|
+
resolve(resultPolytree3);
|
|
589
|
+
}
|
|
590
|
+
if (!returnPolytrees) {
|
|
591
|
+
if (firstOperand2 || secondOperand2) {
|
|
592
|
+
return disposePolytreeResources(firstOperand2, secondOperand2);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}).catch(function(operationError) {
|
|
596
|
+
return reject(operationError);
|
|
597
|
+
});
|
|
598
|
+
});
|
|
599
|
+
} catch (error) {
|
|
600
|
+
asyncError = error;
|
|
601
|
+
return reject(asyncError);
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
} else {
|
|
605
|
+
firstOperand = void 0;
|
|
606
|
+
secondOperand = void 0;
|
|
607
|
+
resultPolytree = void 0;
|
|
608
|
+
materialForMesh = void 0;
|
|
609
|
+
if (operationObject.material) {
|
|
610
|
+
materialForMesh = operationObject.material;
|
|
611
|
+
}
|
|
612
|
+
if (operationObject.objA) {
|
|
613
|
+
firstOperand = handleObjectForOperation(operationObject.objA, returnPolytrees, buildTargetPolytree, options, void 0, async);
|
|
614
|
+
if (returnPolytrees === true) {
|
|
615
|
+
operationObject.objA = firstOperand.original;
|
|
616
|
+
firstOperand = firstOperand.result;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
if (operationObject.objB) {
|
|
620
|
+
secondOperand = handleObjectForOperation(operationObject.objB, returnPolytrees, buildTargetPolytree, options, void 0, async);
|
|
621
|
+
if (returnPolytrees === true) {
|
|
622
|
+
operationObject.objB = secondOperand.original;
|
|
623
|
+
secondOperand = secondOperand.result;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
switch (operationObject.op) {
|
|
627
|
+
case "unite":
|
|
628
|
+
if (firstOperand && secondOperand) {
|
|
629
|
+
resultPolytree = Polytree.unite(firstOperand, secondOperand, false);
|
|
630
|
+
} else {
|
|
631
|
+
resultPolytree = firstOperand || secondOperand || new Polytree();
|
|
632
|
+
}
|
|
633
|
+
break;
|
|
634
|
+
case "subtract":
|
|
635
|
+
if (firstOperand && secondOperand) {
|
|
636
|
+
resultPolytree = Polytree.subtract(firstOperand, secondOperand, false);
|
|
637
|
+
} else {
|
|
638
|
+
resultPolytree = firstOperand || new Polytree();
|
|
639
|
+
}
|
|
640
|
+
break;
|
|
641
|
+
case "intersect":
|
|
642
|
+
if (firstOperand && secondOperand) {
|
|
643
|
+
resultPolytree = Polytree.intersect(firstOperand, secondOperand, false);
|
|
644
|
+
} else {
|
|
645
|
+
resultPolytree = new Polytree();
|
|
646
|
+
}
|
|
647
|
+
break;
|
|
648
|
+
default:
|
|
649
|
+
resultPolytree = new Polytree();
|
|
650
|
+
}
|
|
651
|
+
if (resultPolytree && !resultPolytree.box && resultPolytree.bounds) {
|
|
652
|
+
resultPolytree.buildTree();
|
|
653
|
+
}
|
|
654
|
+
if (!returnPolytrees) {
|
|
655
|
+
if (firstOperand || secondOperand) {
|
|
656
|
+
disposePolytreeResources(firstOperand, secondOperand);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
if (firstRun && !returnPolytrees) {
|
|
660
|
+
allPolygons = resultPolytree.getPolygons();
|
|
661
|
+
if (!resultPolytree || allPolygons.length === 0) {
|
|
662
|
+
return void 0;
|
|
663
|
+
}
|
|
664
|
+
if (materialForMesh) {
|
|
665
|
+
finalMesh = Polytree.toMesh(resultPolytree, materialForMesh);
|
|
666
|
+
} else {
|
|
667
|
+
defaultMaterial = void 0;
|
|
668
|
+
if ((ref = operationObject.objA) != null ? ref.material : void 0) {
|
|
669
|
+
defaultMaterial = Array.isArray(operationObject.objA.material) ? operationObject.objA.material[0] : operationObject.objA.material;
|
|
670
|
+
defaultMaterial = defaultMaterial.clone();
|
|
671
|
+
} else {
|
|
672
|
+
return void 0;
|
|
673
|
+
}
|
|
674
|
+
finalMesh = Polytree.toMesh(resultPolytree, defaultMaterial);
|
|
675
|
+
}
|
|
676
|
+
disposePolytreeResources(resultPolytree);
|
|
677
|
+
return finalMesh;
|
|
678
|
+
}
|
|
679
|
+
if (firstRun && returnPolytrees) {
|
|
680
|
+
return {
|
|
681
|
+
result: resultPolytree,
|
|
682
|
+
operationTree: operationObject
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
return resultPolytree;
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
handleObjectForOperation = function(inputObject, returnPolytrees, buildTargetPolytree, options, objectIndex, async = true) {
|
|
689
|
+
var processedObject;
|
|
690
|
+
if (async) {
|
|
691
|
+
return new Promise(function(resolve, reject) {
|
|
692
|
+
var processedObject2, processingError;
|
|
693
|
+
try {
|
|
694
|
+
processedObject2 = void 0;
|
|
695
|
+
if (inputObject.isMesh) {
|
|
696
|
+
processedObject2 = Polytree.fromMesh(inputObject, options.objCounter++);
|
|
697
|
+
if (returnPolytrees) {
|
|
698
|
+
processedObject2 = {
|
|
699
|
+
result: processedObject2,
|
|
700
|
+
original: processedObject2.clone()
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
processedObject2.objIndex = objectIndex;
|
|
704
|
+
return resolve(processedObject2);
|
|
705
|
+
} else if (inputObject.isPolytree) {
|
|
706
|
+
processedObject2 = inputObject;
|
|
707
|
+
if (returnPolytrees) {
|
|
708
|
+
processedObject2 = {
|
|
709
|
+
result: inputObject,
|
|
710
|
+
original: inputObject.clone()
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
processedObject2.objIndex = objectIndex;
|
|
714
|
+
return resolve(processedObject2);
|
|
715
|
+
} else if (inputObject.op) {
|
|
716
|
+
return Polytree.operation(inputObject, returnPolytrees, buildTargetPolytree, options, false, async).then(function(nestedResult) {
|
|
717
|
+
if (returnPolytrees) {
|
|
718
|
+
nestedResult = {
|
|
719
|
+
result: nestedResult,
|
|
720
|
+
original: inputObject
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
nestedResult.objIndex = objectIndex;
|
|
724
|
+
return resolve(nestedResult);
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
} catch (error) {
|
|
728
|
+
processingError = error;
|
|
729
|
+
return reject(processingError);
|
|
730
|
+
}
|
|
731
|
+
});
|
|
732
|
+
} else {
|
|
733
|
+
processedObject = void 0;
|
|
734
|
+
if (inputObject.isMesh) {
|
|
735
|
+
processedObject = Polytree.fromMesh(inputObject, options.objCounter++);
|
|
736
|
+
if (returnPolytrees) {
|
|
737
|
+
processedObject = {
|
|
738
|
+
result: processedObject,
|
|
739
|
+
original: processedObject.clone()
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
} else if (inputObject.isPolytree) {
|
|
743
|
+
processedObject = inputObject;
|
|
744
|
+
if (returnPolytrees) {
|
|
745
|
+
processedObject = {
|
|
746
|
+
result: inputObject,
|
|
747
|
+
original: inputObject.clone()
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
} else if (inputObject.op) {
|
|
751
|
+
processedObject = Polytree.operation(inputObject, returnPolytrees, buildTargetPolytree, options, false, async);
|
|
752
|
+
if (returnPolytrees) {
|
|
753
|
+
processedObject = {
|
|
754
|
+
result: processedObject,
|
|
755
|
+
original: inputObject
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
return processedObject;
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
var Polytree;
|
|
763
|
+
Polytree = (function() {
|
|
764
|
+
class Polytree2 {
|
|
765
|
+
// Main constructor for creating Polytree nodes.
|
|
766
|
+
// Initializes core properties and sets up polygon array management.
|
|
767
|
+
// @param box - Optional bounding box for this tree node (used internally for polytree subdivision).
|
|
768
|
+
// @param parent - Optional parent Polytree node (used internally, null for root).
|
|
769
|
+
constructor(box = null, parent = null) {
|
|
770
|
+
this.box = box;
|
|
771
|
+
this.polygons = [];
|
|
772
|
+
this.replacedPolygons = [];
|
|
773
|
+
this.parent = parent;
|
|
774
|
+
this.subTrees = [];
|
|
775
|
+
this.level = 0;
|
|
776
|
+
this.originalMatrixWorld;
|
|
777
|
+
this.mesh;
|
|
778
|
+
this.polygonArrays = void 0;
|
|
779
|
+
this.addPolygonsArrayToRoot(this.polygons);
|
|
780
|
+
}
|
|
781
|
+
// === SMALL HELPERS AND GETTERS/SETTERS ===
|
|
782
|
+
isEmpty() {
|
|
783
|
+
return this.polygons.length === 0;
|
|
784
|
+
}
|
|
785
|
+
getMesh() {
|
|
786
|
+
var current, ref, visited;
|
|
787
|
+
visited = /* @__PURE__ */ new Set();
|
|
788
|
+
current = this;
|
|
789
|
+
while (current && !visited.has(current)) {
|
|
790
|
+
visited.add(current);
|
|
791
|
+
if (!current.parent) {
|
|
792
|
+
return (ref = current.mesh) != null ? ref : null;
|
|
793
|
+
}
|
|
794
|
+
current = current.parent;
|
|
795
|
+
}
|
|
796
|
+
return null;
|
|
797
|
+
}
|
|
798
|
+
newPolytree(box, parent) {
|
|
799
|
+
return new this.constructor(box, parent);
|
|
800
|
+
}
|
|
801
|
+
setPolygonIndex(index) {
|
|
802
|
+
if (index === void 0) {
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
if (this.polygonArrays) {
|
|
806
|
+
return this.polygonArrays.forEach(function(polygonsArray) {
|
|
807
|
+
if (polygonsArray != null ? polygonsArray.length : void 0) {
|
|
808
|
+
return polygonsArray.forEach(function(polygon) {
|
|
809
|
+
return polygon.shared = index;
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
// === OBJECT CREATION AND COPYING ===
|
|
816
|
+
clone() {
|
|
817
|
+
return new this.constructor().copy(this);
|
|
818
|
+
}
|
|
819
|
+
copy(source) {
|
|
820
|
+
var i, k, ref, ref1, subTree;
|
|
821
|
+
if (!source) {
|
|
822
|
+
return this;
|
|
823
|
+
}
|
|
824
|
+
this.deletePolygonsArrayFromRoot(this.polygons);
|
|
825
|
+
this.polygons = source.polygons ? source.polygons.map(function(polygon) {
|
|
826
|
+
return polygon.clone();
|
|
827
|
+
}) : [];
|
|
828
|
+
this.addPolygonsArrayToRoot(this.polygons);
|
|
829
|
+
this.replacedPolygons = source.replacedPolygons ? source.replacedPolygons.map(function(polygon) {
|
|
830
|
+
return polygon.clone();
|
|
831
|
+
}) : [];
|
|
832
|
+
if (source.mesh) {
|
|
833
|
+
this.mesh = source.mesh;
|
|
834
|
+
}
|
|
835
|
+
if (source.originalMatrixWorld) {
|
|
836
|
+
this.originalMatrixWorld = source.originalMatrixWorld.clone();
|
|
837
|
+
}
|
|
838
|
+
this.box = source.box ? source.box.clone() : null;
|
|
839
|
+
this.level = (ref = source.level) != null ? ref : 0;
|
|
840
|
+
if (source.subTrees) {
|
|
841
|
+
for (i = k = 0, ref1 = source.subTrees.length; 0 <= ref1 ? k < ref1 : k > ref1; i = 0 <= ref1 ? ++k : --k) {
|
|
842
|
+
subTree = new this.constructor(void 0, this).copy(source.subTrees[i]);
|
|
843
|
+
this.subTrees.push(subTree);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
return this;
|
|
847
|
+
}
|
|
848
|
+
// === CSG OPERATIONS (INSTANCE METHODS) ===
|
|
849
|
+
// Perform union operation (combines both meshes).
|
|
850
|
+
unite(mesh1, mesh2, targetMaterial = null) {
|
|
851
|
+
return Polytree2.unite(mesh1, mesh2, targetMaterial);
|
|
852
|
+
}
|
|
853
|
+
// Perform subtraction operation (mesh1 minus mesh2).
|
|
854
|
+
subtract(mesh1, mesh2, targetMaterial = null) {
|
|
855
|
+
return Polytree2.subtract(mesh1, mesh2, targetMaterial);
|
|
856
|
+
}
|
|
857
|
+
// Perform intersection operation (keep only overlapping volume).
|
|
858
|
+
intersect(mesh1, mesh2, targetMaterial = null) {
|
|
859
|
+
return Polytree2.intersect(mesh1, mesh2, targetMaterial);
|
|
860
|
+
}
|
|
861
|
+
// === POLYGON ARRAY MANAGEMENT ===
|
|
862
|
+
// Add polygon array to root node's collection (internal helper).
|
|
863
|
+
addPolygonsArrayToRoot(array) {
|
|
864
|
+
if (this.parent) {
|
|
865
|
+
return this.parent.addPolygonsArrayToRoot(array);
|
|
866
|
+
} else {
|
|
867
|
+
if (this.polygonArrays === void 0) {
|
|
868
|
+
this.polygonArrays = [];
|
|
869
|
+
}
|
|
870
|
+
return this.polygonArrays.push(array);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
// Remove polygon array from root node's collection (internal helper).
|
|
874
|
+
deletePolygonsArrayFromRoot(array) {
|
|
875
|
+
var index;
|
|
876
|
+
if (this.parent) {
|
|
877
|
+
return this.parent.deletePolygonsArrayFromRoot(array);
|
|
878
|
+
} else {
|
|
879
|
+
index = this.polygonArrays.indexOf(array);
|
|
880
|
+
if (index > -1) {
|
|
881
|
+
return this.polygonArrays.splice(index, 1);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
// === CORE POLYGON OPERATIONS ===
|
|
886
|
+
// Add a polygon to this tree node with spatial bounds calculation.
|
|
887
|
+
addPolygon(polygon, trianglesSet) {
|
|
888
|
+
var triangle;
|
|
889
|
+
if (!this.bounds) {
|
|
890
|
+
this.bounds = new Box3();
|
|
891
|
+
}
|
|
892
|
+
triangle = polygon.triangle;
|
|
893
|
+
if (trianglesSet && !isUniqueTriangle(triangle, trianglesSet)) {
|
|
894
|
+
return this;
|
|
895
|
+
}
|
|
896
|
+
this.bounds.expandByPoint(triangle.a);
|
|
897
|
+
this.bounds.expandByPoint(triangle.b);
|
|
898
|
+
this.bounds.expandByPoint(triangle.c);
|
|
899
|
+
this.polygons.push(polygon);
|
|
900
|
+
return this;
|
|
901
|
+
}
|
|
902
|
+
calcBox() {
|
|
903
|
+
var offset;
|
|
904
|
+
if (!this.bounds) {
|
|
905
|
+
this.bounds = new Box3();
|
|
906
|
+
}
|
|
907
|
+
this.box = this.bounds.clone();
|
|
908
|
+
offset = POLYTREE_MIN_NODE_SIZE;
|
|
909
|
+
this.box.min.x -= offset;
|
|
910
|
+
this.box.min.y -= offset;
|
|
911
|
+
this.box.min.z -= offset;
|
|
912
|
+
this.box.max.x += offset;
|
|
913
|
+
this.box.max.y += offset;
|
|
914
|
+
this.box.max.z += offset;
|
|
915
|
+
return this;
|
|
916
|
+
}
|
|
917
|
+
// === TREE CONSTRUCTION AND SPATIAL PARTITIONING ===
|
|
918
|
+
// Split this node into 8 octree children based on spatial subdivision.
|
|
919
|
+
// This creates a polytree by recursively subdividing space until polygon density is acceptable.
|
|
920
|
+
split(level) {
|
|
921
|
+
var box, found, halfSize, i, k, l, len, m, maxDepthLimit, n, nodeSize, o, polygon, polygonLimit, ref, ref1, subTrees, vectorPosition, x, y, z;
|
|
922
|
+
subTrees = [];
|
|
923
|
+
if (!this.box) {
|
|
924
|
+
return this;
|
|
925
|
+
}
|
|
926
|
+
halfSize = temporaryVector3Secondary.copy(this.box.max).sub(this.box.min).multiplyScalar(0.5);
|
|
927
|
+
for (x = k = 0; k <= 1; x = ++k) {
|
|
928
|
+
for (y = l = 0; l <= 1; y = ++l) {
|
|
929
|
+
for (z = m = 0; m <= 1; z = ++m) {
|
|
930
|
+
box = new Box3();
|
|
931
|
+
vectorPosition = temporaryVector3Primary.set(x, y, z);
|
|
932
|
+
box.min.copy(this.box.min).add(vectorPosition.multiply(halfSize));
|
|
933
|
+
box.max.copy(box.min).add(halfSize);
|
|
934
|
+
box.expandByScalar(GEOMETRIC_EPSILON);
|
|
935
|
+
subTrees.push(this.newPolytree(box, this));
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
polygon = void 0;
|
|
940
|
+
while (polygon = this.polygons.pop()) {
|
|
941
|
+
found = false;
|
|
942
|
+
for (i = n = 0, ref = subTrees.length; 0 <= ref ? n < ref : n > ref; i = 0 <= ref ? ++n : --n) {
|
|
943
|
+
if (subTrees[i].box.containsPoint(polygon.getMidpoint())) {
|
|
944
|
+
subTrees[i].polygons.push(polygon);
|
|
945
|
+
found = true;
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
if (!found) {
|
|
949
|
+
throw new Error(`Unable to find subtree for triangle at level ${level}.`);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
for (i = o = 0, ref1 = subTrees.length; 0 <= ref1 ? o < ref1 : o > ref1; i = 0 <= ref1 ? ++o : --o) {
|
|
953
|
+
subTrees[i].level = level + 1;
|
|
954
|
+
len = subTrees[i].polygons.length;
|
|
955
|
+
nodeSize = subTrees[i].box.getSize(temporaryVector3Tertiary).length();
|
|
956
|
+
maxDepthLimit = Math.min(Polytree2.maxLevel, POLYTREE_MAX_DEPTH);
|
|
957
|
+
polygonLimit = Math.min(Polytree2.polygonsPerTree, POLYTREE_MAX_POLYGONS_PER_NODE);
|
|
958
|
+
if (len > polygonLimit && level < maxDepthLimit && nodeSize > POLYTREE_MIN_NODE_SIZE) {
|
|
959
|
+
subTrees[i].split(level + 1);
|
|
960
|
+
}
|
|
961
|
+
this.subTrees.push(subTrees[i]);
|
|
962
|
+
}
|
|
963
|
+
return this;
|
|
964
|
+
}
|
|
965
|
+
buildTree() {
|
|
966
|
+
this.calcBox();
|
|
967
|
+
this.split(0);
|
|
968
|
+
this.processTree();
|
|
969
|
+
return this;
|
|
970
|
+
}
|
|
971
|
+
// Process tree nodes to update bounding boxes after polygon distribution.
|
|
972
|
+
processTree() {
|
|
973
|
+
var i, k, l, ref, ref1, results;
|
|
974
|
+
if (!this.isEmpty()) {
|
|
975
|
+
temporaryBoundingBox.copy(this.box);
|
|
976
|
+
for (i = k = 0, ref = this.polygons.length; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) {
|
|
977
|
+
this.box.expandByPoint(this.polygons[i].triangle.a);
|
|
978
|
+
this.box.expandByPoint(this.polygons[i].triangle.b);
|
|
979
|
+
this.box.expandByPoint(this.polygons[i].triangle.c);
|
|
980
|
+
}
|
|
981
|
+
this.expandParentBox();
|
|
982
|
+
}
|
|
983
|
+
results = [];
|
|
984
|
+
for (i = l = 0, ref1 = this.subTrees.length; 0 <= ref1 ? l < ref1 : l > ref1; i = 0 <= ref1 ? ++l : --l) {
|
|
985
|
+
results.push(this.subTrees[i].processTree());
|
|
986
|
+
}
|
|
987
|
+
return results;
|
|
988
|
+
}
|
|
989
|
+
// Recursively expand parent bounding boxes up the tree hierarchy.
|
|
990
|
+
expandParentBox(visited = /* @__PURE__ */ new Set()) {
|
|
991
|
+
if (this.parent && !visited.has(this.parent)) {
|
|
992
|
+
visited.add(this.parent);
|
|
993
|
+
this.parent.box.expandByPoint(this.box.min);
|
|
994
|
+
this.parent.box.expandByPoint(this.box.max);
|
|
995
|
+
return this.parent.expandParentBox(visited);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
// === POLYGON QUERIES AND INTERSECTION TESTING ===
|
|
999
|
+
// Invert all polygons by flipping their face normals.
|
|
1000
|
+
invert() {
|
|
1001
|
+
return this.polygonArrays.forEach(function(polygonsArray) {
|
|
1002
|
+
if (polygonsArray.length) {
|
|
1003
|
+
return polygonsArray.forEach(function(polygon) {
|
|
1004
|
+
return polygon.flip();
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
// Get all valid polygons from all polygon arrays in this tree.
|
|
1010
|
+
getPolygons(polygons = []) {
|
|
1011
|
+
this.polygonArrays.forEach(function(polygonsArray) {
|
|
1012
|
+
var i, k, ref, results;
|
|
1013
|
+
if (polygonsArray.length) {
|
|
1014
|
+
results = [];
|
|
1015
|
+
for (i = k = 0, ref = polygonsArray.length; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) {
|
|
1016
|
+
if (polygonsArray[i].valid) {
|
|
1017
|
+
if (polygons.indexOf(polygonsArray[i]) === -1) {
|
|
1018
|
+
results.push(polygons.push(polygonsArray[i]));
|
|
1019
|
+
} else {
|
|
1020
|
+
results.push(void 0);
|
|
1021
|
+
}
|
|
1022
|
+
} else {
|
|
1023
|
+
results.push(void 0);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
return results;
|
|
1027
|
+
}
|
|
1028
|
+
});
|
|
1029
|
+
return polygons;
|
|
1030
|
+
}
|
|
1031
|
+
// Extract triangles from all valid polygons in the tree.
|
|
1032
|
+
// This provides triangle data format as complement to getPolygons().
|
|
1033
|
+
getTriangles(triangles = []) {
|
|
1034
|
+
var polygons;
|
|
1035
|
+
polygons = this.getPolygons();
|
|
1036
|
+
polygons.forEach(function(polygon) {
|
|
1037
|
+
return triangles.push(polygon.triangle);
|
|
1038
|
+
});
|
|
1039
|
+
return triangles;
|
|
1040
|
+
}
|
|
1041
|
+
// Collect polygons that intersect with a ray for raycasting operations.
|
|
1042
|
+
getRayPolygons(ray, polygons = []) {
|
|
1043
|
+
var i, k, l, ref, ref1;
|
|
1044
|
+
if (this.polygons.length > 0) {
|
|
1045
|
+
for (i = k = 0, ref = this.polygons.length; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) {
|
|
1046
|
+
if (this.polygons[i].valid && this.polygons[i].originalValid) {
|
|
1047
|
+
if (polygons.indexOf(this.polygons[i]) === -1) {
|
|
1048
|
+
polygons.push(this.polygons[i]);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
if (this.replacedPolygons.length > 0) {
|
|
1054
|
+
polygons.push(...this.replacedPolygons);
|
|
1055
|
+
}
|
|
1056
|
+
for (i = l = 0, ref1 = this.subTrees.length; 0 <= ref1 ? l < ref1 : l > ref1; i = 0 <= ref1 ? ++l : --l) {
|
|
1057
|
+
if (ray.intersectsBox(this.subTrees[i].box)) {
|
|
1058
|
+
this.subTrees[i].getRayPolygons(ray, polygons);
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
return polygons;
|
|
1062
|
+
}
|
|
1063
|
+
// Extract triangles from polygons that intersect with a ray.
|
|
1064
|
+
// This provides triangle data format as complement to getRayPolygons().
|
|
1065
|
+
getRayTriangles(ray, triangles = []) {
|
|
1066
|
+
var polygons;
|
|
1067
|
+
polygons = this.getRayPolygons(ray);
|
|
1068
|
+
polygons.forEach(function(polygon) {
|
|
1069
|
+
return triangles.push(polygon.triangle);
|
|
1070
|
+
});
|
|
1071
|
+
return triangles;
|
|
1072
|
+
}
|
|
1073
|
+
// Perform ray intersection testing against all polygons in the tree.
|
|
1074
|
+
// Returns array of intersection results sorted by distance.
|
|
1075
|
+
rayIntersect(ray, matrixWorld, intersects = []) {
|
|
1076
|
+
var distance, i, k, newDistance, polygons, ref, result;
|
|
1077
|
+
if (ray.direction.length() === 0) {
|
|
1078
|
+
return [];
|
|
1079
|
+
}
|
|
1080
|
+
distance = 1e100;
|
|
1081
|
+
polygons = this.getRayPolygons(ray);
|
|
1082
|
+
for (i = k = 0, ref = polygons.length; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) {
|
|
1083
|
+
result = void 0;
|
|
1084
|
+
if (Polytree2.rayIntersectTriangleType === "regular") {
|
|
1085
|
+
result = ray.intersectTriangle(polygons[i].triangle.a, polygons[i].triangle.b, polygons[i].triangle.c, false, temporaryVector3Primary);
|
|
1086
|
+
if (result) {
|
|
1087
|
+
temporaryVector3Primary.applyMatrix4(matrixWorld);
|
|
1088
|
+
distance = temporaryVector3Primary.distanceTo(ray.origin);
|
|
1089
|
+
if (distance < 0 || distance > Infinity) {
|
|
1090
|
+
console.warn("[rayIntersect] Failed ray distance check.", ray);
|
|
1091
|
+
} else {
|
|
1092
|
+
intersects.push({
|
|
1093
|
+
distance,
|
|
1094
|
+
polygon: polygons[i],
|
|
1095
|
+
position: temporaryVector3Primary.clone()
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
} else {
|
|
1100
|
+
result = testRayTriangleIntersection(ray, polygons[i].triangle, temporaryVector3Primary);
|
|
1101
|
+
if (result) {
|
|
1102
|
+
newDistance = result.clone().sub(ray.origin).length();
|
|
1103
|
+
if (distance > newDistance) {
|
|
1104
|
+
distance = newDistance;
|
|
1105
|
+
}
|
|
1106
|
+
if (distance < 1e100) {
|
|
1107
|
+
intersects.push({
|
|
1108
|
+
distance,
|
|
1109
|
+
polygon: polygons[i],
|
|
1110
|
+
position: result.clone().add(ray.origin)
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
intersects.length && intersects.sort(sortRaycastIntersectionsByDistance);
|
|
1117
|
+
return intersects;
|
|
1118
|
+
}
|
|
1119
|
+
// Get all polygons marked as intersecting from polygon arrays.
|
|
1120
|
+
getIntersectingPolygons(polygons = []) {
|
|
1121
|
+
this.polygonArrays.forEach(function(polygonsArray) {
|
|
1122
|
+
var i, k, ref, results;
|
|
1123
|
+
if (polygonsArray.length) {
|
|
1124
|
+
results = [];
|
|
1125
|
+
for (i = k = 0, ref = polygonsArray.length; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) {
|
|
1126
|
+
if (polygonsArray[i].valid && polygonsArray[i].intersects) {
|
|
1127
|
+
results.push(polygons.push(polygonsArray[i]));
|
|
1128
|
+
} else {
|
|
1129
|
+
results.push(void 0);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
return results;
|
|
1133
|
+
}
|
|
1134
|
+
});
|
|
1135
|
+
return polygons;
|
|
1136
|
+
}
|
|
1137
|
+
// Find all polygons that intersect with a target polygon using spatial partitioning.
|
|
1138
|
+
getPolygonsIntersectingPolygon(targetPolygon, polygons = []) {
|
|
1139
|
+
var allPolygons, i, k, l, m, polygon, ref, ref1, ref2;
|
|
1140
|
+
if (this.box.intersectsTriangle(targetPolygon.triangle)) {
|
|
1141
|
+
if (this.polygons.length > 0) {
|
|
1142
|
+
allPolygons = this.polygons.slice();
|
|
1143
|
+
if (this.replacedPolygons.length > 0) {
|
|
1144
|
+
for (i = k = 0, ref = this.replacedPolygons.length; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) {
|
|
1145
|
+
allPolygons.push(this.replacedPolygons[i]);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
for (i = l = 0, ref1 = allPolygons.length; 0 <= ref1 ? l < ref1 : l > ref1; i = 0 <= ref1 ? ++l : --l) {
|
|
1149
|
+
polygon = allPolygons[i];
|
|
1150
|
+
if (!(polygon.originalValid && polygon.valid && polygon.intersects)) {
|
|
1151
|
+
continue;
|
|
1152
|
+
}
|
|
1153
|
+
if (triangleIntersectsTriangle(targetPolygon.triangle, polygon.triangle)) {
|
|
1154
|
+
polygons.push(polygon);
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
for (i = m = 0, ref2 = this.subTrees.length; 0 <= ref2 ? m < ref2 : m > ref2; i = 0 <= ref2 ? ++m : --m) {
|
|
1160
|
+
this.subTrees[i].getPolygonsIntersectingPolygon(targetPolygon, polygons);
|
|
1161
|
+
}
|
|
1162
|
+
return polygons;
|
|
1163
|
+
}
|
|
1164
|
+
// === POLYGON MODIFICATION AND STATE MANAGEMENT ===
|
|
1165
|
+
// Replace a polygon with one or more new polygons during CSG operations.
|
|
1166
|
+
replacePolygon(polygon, newPolygons) {
|
|
1167
|
+
var i, k, polygonIndex, ref, results;
|
|
1168
|
+
if (!Array.isArray(newPolygons)) {
|
|
1169
|
+
newPolygons = [newPolygons];
|
|
1170
|
+
}
|
|
1171
|
+
if (this.polygons.length > 0) {
|
|
1172
|
+
polygonIndex = this.polygons.indexOf(polygon);
|
|
1173
|
+
if (polygonIndex > -1) {
|
|
1174
|
+
if (polygon.originalValid === true) {
|
|
1175
|
+
this.replacedPolygons.push(polygon);
|
|
1176
|
+
} else {
|
|
1177
|
+
polygon.setInvalid();
|
|
1178
|
+
}
|
|
1179
|
+
this.polygons.splice(polygonIndex, 1, ...newPolygons);
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
results = [];
|
|
1183
|
+
for (i = k = 0, ref = this.subTrees.length; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) {
|
|
1184
|
+
results.push(this.subTrees[i].replacePolygon(polygon, newPolygons));
|
|
1185
|
+
}
|
|
1186
|
+
return results;
|
|
1187
|
+
}
|
|
1188
|
+
// === COMPLEX CSG OPERATIONS AND STATE MANAGEMENT ===
|
|
1189
|
+
// Delete polygons based on complex state rules for CSG operations.
|
|
1190
|
+
// This implements the polygon classification logic for boolean operations.
|
|
1191
|
+
deletePolygonsByStateRules(rulesArr, firstRun = true) {
|
|
1192
|
+
return this.polygonArrays.forEach(function(polygonsArray) {
|
|
1193
|
+
var polygonArr;
|
|
1194
|
+
if (polygonsArray.length) {
|
|
1195
|
+
polygonArr = polygonsArray.filter(function(polygon) {
|
|
1196
|
+
return polygon.valid === true && polygon.intersects === true;
|
|
1197
|
+
});
|
|
1198
|
+
return polygonArr.forEach(function(polygon) {
|
|
1199
|
+
var found, i, j, k, l, mainStatesObj, polygonIndex, ref, ref1, state, states, statesObj;
|
|
1200
|
+
found = false;
|
|
1201
|
+
for (j = k = 0, ref = rulesArr.length; 0 <= ref ? k < ref : k > ref; j = 0 <= ref ? ++k : --k) {
|
|
1202
|
+
if (rulesArr[j].array) {
|
|
1203
|
+
states = rulesArr[j].rule;
|
|
1204
|
+
if (states.includes(polygon.state) && (polygon.previousState !== "undecided" && states.includes(polygon.previousState) || polygon.previousState === "undecided")) {
|
|
1205
|
+
found = true;
|
|
1206
|
+
statesObj = {};
|
|
1207
|
+
mainStatesObj = {};
|
|
1208
|
+
states.forEach(function(state2) {
|
|
1209
|
+
return statesObj[state2] = false;
|
|
1210
|
+
});
|
|
1211
|
+
states.forEach(function(state2) {
|
|
1212
|
+
return mainStatesObj[state2] = false;
|
|
1213
|
+
});
|
|
1214
|
+
statesObj[polygon.state] = true;
|
|
1215
|
+
for (i = l = 0, ref1 = polygon.previousStates.length; 0 <= ref1 ? l < ref1 : l > ref1; i = 0 <= ref1 ? ++l : --l) {
|
|
1216
|
+
if (!states.includes(polygon.previousStates[i])) {
|
|
1217
|
+
found = false;
|
|
1218
|
+
break;
|
|
1219
|
+
} else {
|
|
1220
|
+
statesObj[polygon.previousStates[i]] = true;
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
if (found) {
|
|
1224
|
+
for (state in statesObj) {
|
|
1225
|
+
if (statesObj[state] === false) {
|
|
1226
|
+
found = false;
|
|
1227
|
+
break;
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
if (found) {
|
|
1231
|
+
break;
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
} else {
|
|
1236
|
+
if (polygon.checkAllStates(rulesArr[j].rule)) {
|
|
1237
|
+
found = true;
|
|
1238
|
+
break;
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
if (found) {
|
|
1243
|
+
polygonIndex = polygonsArray.indexOf(polygon);
|
|
1244
|
+
if (polygonIndex > -1) {
|
|
1245
|
+
polygon.setInvalid();
|
|
1246
|
+
polygonsArray.splice(polygonIndex, 1);
|
|
1247
|
+
}
|
|
1248
|
+
if (firstRun) {
|
|
1249
|
+
return polygon.delete();
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
// Delete polygons based on their intersection status (simpler filtering).
|
|
1257
|
+
deletePolygonsByIntersection(intersects, firstRun = true) {
|
|
1258
|
+
if (intersects === void 0) {
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
return this.polygonArrays.forEach(function(polygonsArray) {
|
|
1262
|
+
var polygonArr;
|
|
1263
|
+
if (polygonsArray.length) {
|
|
1264
|
+
polygonArr = polygonsArray.slice();
|
|
1265
|
+
return polygonArr.forEach(function(polygon) {
|
|
1266
|
+
var polygonIndex;
|
|
1267
|
+
if (polygon.valid) {
|
|
1268
|
+
if (polygon.intersects === intersects) {
|
|
1269
|
+
polygonIndex = polygonsArray.indexOf(polygon);
|
|
1270
|
+
if (polygonIndex > -1) {
|
|
1271
|
+
polygon.setInvalid();
|
|
1272
|
+
polygonsArray.splice(polygonIndex, 1);
|
|
1273
|
+
}
|
|
1274
|
+
if (firstRun) {
|
|
1275
|
+
return polygon.delete();
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1281
|
+
});
|
|
1282
|
+
}
|
|
1283
|
+
// Check if a polygon intersects with this tree's bounding box.
|
|
1284
|
+
isPolygonIntersecting(polygon) {
|
|
1285
|
+
if (!this.box.intersectsTriangle(polygon.triangle)) {
|
|
1286
|
+
return false;
|
|
1287
|
+
}
|
|
1288
|
+
return true;
|
|
1289
|
+
}
|
|
1290
|
+
// Mark polygons as intersecting with target polytree.
|
|
1291
|
+
markIntesectingPolygons(targetPolytree) {
|
|
1292
|
+
return this.polygonArrays.forEach(function(polygonsArray) {
|
|
1293
|
+
if (polygonsArray.length) {
|
|
1294
|
+
return polygonsArray.forEach(function(polygon) {
|
|
1295
|
+
return polygon.intersects = targetPolytree.isPolygonIntersecting(polygon);
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
// Reset polygon states for CSG operation preparation.
|
|
1301
|
+
resetPolygons(resetOriginal = true) {
|
|
1302
|
+
return this.polygonArrays.forEach(function(polygonsArray) {
|
|
1303
|
+
if (polygonsArray.length) {
|
|
1304
|
+
return polygonsArray.forEach(function(polygon) {
|
|
1305
|
+
return polygon.reset(resetOriginal);
|
|
1306
|
+
});
|
|
1307
|
+
}
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
// Complex CSG intersection handling - splits and classifies polygons.
|
|
1311
|
+
// This is the core method for polygon classification in boolean operations.
|
|
1312
|
+
handleIntersectingPolygons(targetPolytree, targetPolytreeBuffer) {
|
|
1313
|
+
var currentPolygon, i, inside, intersects, j, k, l, m, n, o, point, polygon, polygonStack, ref, ref1, ref2, ref3, ref4, results, splitResults, target, targetPolygons;
|
|
1314
|
+
if (this.polygons.length > 0) {
|
|
1315
|
+
polygonStack = this.polygons.filter(function(polygon2) {
|
|
1316
|
+
return polygon2.valid === true && polygon2.intersects === true && polygon2.state === "undecided";
|
|
1317
|
+
});
|
|
1318
|
+
currentPolygon = polygonStack.pop();
|
|
1319
|
+
while (currentPolygon) {
|
|
1320
|
+
if (currentPolygon.state !== "undecided") {
|
|
1321
|
+
continue;
|
|
1322
|
+
}
|
|
1323
|
+
if (!currentPolygon.valid) {
|
|
1324
|
+
continue;
|
|
1325
|
+
}
|
|
1326
|
+
targetPolygons = targetPolytree.getPolygonsIntersectingPolygon(currentPolygon);
|
|
1327
|
+
if (targetPolygons.length > 0) {
|
|
1328
|
+
for (j = k = 0, ref = targetPolygons.length; 0 <= ref ? k < ref : k > ref; j = 0 <= ref ? ++k : --k) {
|
|
1329
|
+
target = targetPolygons[j];
|
|
1330
|
+
splitResults = splitPolygonByPlane(currentPolygon, target.plane);
|
|
1331
|
+
if (splitResults.length > 1) {
|
|
1332
|
+
for (i = l = 0, ref1 = splitResults.length; 0 <= ref1 ? l < ref1 : l > ref1; i = 0 <= ref1 ? ++l : --l) {
|
|
1333
|
+
polygon = splitResults[i].polygon;
|
|
1334
|
+
polygon.intersects = currentPolygon.intersects;
|
|
1335
|
+
polygon.newPolygon = true;
|
|
1336
|
+
polygonStack.push(polygon);
|
|
1337
|
+
}
|
|
1338
|
+
this.replacePolygon(currentPolygon, splitResults.map(function(result) {
|
|
1339
|
+
return result.polygon;
|
|
1340
|
+
}));
|
|
1341
|
+
break;
|
|
1342
|
+
} else {
|
|
1343
|
+
if (currentPolygon.id !== splitResults[0].polygon.id) {
|
|
1344
|
+
splitResults[0].polygon.intersects = currentPolygon.intersects;
|
|
1345
|
+
splitResults[0].polygon.newPolygon = true;
|
|
1346
|
+
polygonStack.push(splitResults[0].polygon);
|
|
1347
|
+
this.replacePolygon(currentPolygon, splitResults[0].polygon);
|
|
1348
|
+
break;
|
|
1349
|
+
} else {
|
|
1350
|
+
if (splitResults[0].type === "coplanar-front" || splitResults[0].type === "coplanar-back") {
|
|
1351
|
+
currentPolygon.setState(splitResults[0].type);
|
|
1352
|
+
currentPolygon.coplanar = true;
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
currentPolygon = polygonStack.pop();
|
|
1359
|
+
}
|
|
1360
|
+
polygonStack = this.polygons.filter(function(polygon2) {
|
|
1361
|
+
return polygon2.valid === true && polygon2.intersects === true;
|
|
1362
|
+
});
|
|
1363
|
+
currentPolygon = polygonStack.pop();
|
|
1364
|
+
inside = false;
|
|
1365
|
+
while (currentPolygon) {
|
|
1366
|
+
if (!currentPolygon.valid) {
|
|
1367
|
+
continue;
|
|
1368
|
+
}
|
|
1369
|
+
inside = false;
|
|
1370
|
+
if (targetPolytree.box.containsPoint(currentPolygon.getMidpoint())) {
|
|
1371
|
+
if (Polytree2.useWindingNumber === true) {
|
|
1372
|
+
inside = testPolygonInsideUsingWindingNumber(targetPolytreeBuffer, currentPolygon.getMidpoint(), currentPolygon.coplanar);
|
|
1373
|
+
} else {
|
|
1374
|
+
point = roundPointCoordinates(temporaryVector3Secondary.copy(currentPolygon.getMidpoint()));
|
|
1375
|
+
if (Polytree2.usePolytreeRay !== true && targetPolytree.mesh) {
|
|
1376
|
+
defaultRayDirection.copy(currentPolygon.plane.normal);
|
|
1377
|
+
temporaryRaycaster.set(point, defaultRayDirection);
|
|
1378
|
+
intersects = temporaryRaycaster.intersectObject(targetPolytree.mesh);
|
|
1379
|
+
if (intersects.length) {
|
|
1380
|
+
if (defaultRayDirection.dot(intersects[0].face.normal) > 0) {
|
|
1381
|
+
inside = true;
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
if (!(inside || !currentPolygon.coplanar)) {
|
|
1385
|
+
for (j = m = 0, ref2 = windingNumberEpsilonOffsetsCount; 0 <= ref2 ? m < ref2 : m > ref2; j = 0 <= ref2 ? ++m : --m) {
|
|
1386
|
+
temporaryRaycaster.ray.origin.copy(point).add(windingNumberEpsilonOffsets[j]);
|
|
1387
|
+
intersects = temporaryRaycaster.intersectObject(targetPolytree.mesh);
|
|
1388
|
+
if (intersects.length) {
|
|
1389
|
+
if (defaultRayDirection.dot(intersects[0].face.normal) > 0) {
|
|
1390
|
+
inside = true;
|
|
1391
|
+
break;
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
} else {
|
|
1397
|
+
temporaryRay.origin.copy(point);
|
|
1398
|
+
defaultRayDirection.copy(currentPolygon.plane.normal);
|
|
1399
|
+
temporaryRay.direction.copy(currentPolygon.plane.normal);
|
|
1400
|
+
intersects = targetPolytree.rayIntersect(temporaryRay, targetPolytree.originalMatrixWorld);
|
|
1401
|
+
if (intersects.length) {
|
|
1402
|
+
if (defaultRayDirection.dot(intersects[0].polygon.plane.normal) > 0) {
|
|
1403
|
+
inside = true;
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
if (!(inside || !currentPolygon.coplanar)) {
|
|
1407
|
+
for (j = n = 0, ref3 = windingNumberEpsilonOffsetsCount; 0 <= ref3 ? n < ref3 : n > ref3; j = 0 <= ref3 ? ++n : --n) {
|
|
1408
|
+
temporaryRay.origin.copy(point).add(windingNumberEpsilonOffsets[j]);
|
|
1409
|
+
defaultRayDirection.copy(currentPolygon.plane.normal);
|
|
1410
|
+
temporaryRay.direction.copy(currentPolygon.plane.normal);
|
|
1411
|
+
intersects = targetPolytree.rayIntersect(temporaryRay, targetPolytree.originalMatrixWorld);
|
|
1412
|
+
if (intersects.length) {
|
|
1413
|
+
if (defaultRayDirection.dot(intersects[0].polygon.plane.normal) > 0) {
|
|
1414
|
+
inside = true;
|
|
1415
|
+
break;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
if (inside === true) {
|
|
1424
|
+
currentPolygon.setState("inside");
|
|
1425
|
+
} else {
|
|
1426
|
+
currentPolygon.setState("outside");
|
|
1427
|
+
}
|
|
1428
|
+
currentPolygon = polygonStack.pop();
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
results = [];
|
|
1432
|
+
for (i = o = 0, ref4 = this.subTrees.length; 0 <= ref4 ? o < ref4 : o > ref4; i = 0 <= ref4 ? ++o : --o) {
|
|
1433
|
+
results.push(this.subTrees[i].handleIntersectingPolygons(targetPolytree, targetPolytreeBuffer));
|
|
1434
|
+
}
|
|
1435
|
+
return results;
|
|
1436
|
+
}
|
|
1437
|
+
// Apply transformation matrix to all polygons in this tree.
|
|
1438
|
+
applyMatrix(matrix, normalMatrix, firstRun = true) {
|
|
1439
|
+
var i, k, l, ref, ref1;
|
|
1440
|
+
if (matrix.isMesh) {
|
|
1441
|
+
matrix.updateMatrix();
|
|
1442
|
+
matrix = matrix.matrix;
|
|
1443
|
+
}
|
|
1444
|
+
if (this.box) {
|
|
1445
|
+
this.box.makeEmpty();
|
|
1446
|
+
}
|
|
1447
|
+
normalMatrix = normalMatrix || temporaryMatrixWithNormalCalc.getNormalMatrix(matrix);
|
|
1448
|
+
if (this.polygons.length > 0) {
|
|
1449
|
+
for (i = k = 0, ref = this.polygons.length; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) {
|
|
1450
|
+
if (this.polygons[i].valid) {
|
|
1451
|
+
this.polygons[i].applyMatrix(matrix, normalMatrix);
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
for (i = l = 0, ref1 = this.subTrees.length; 0 <= ref1 ? l < ref1 : l > ref1; i = 0 <= ref1 ? ++l : --l) {
|
|
1456
|
+
this.subTrees[i].applyMatrix(matrix, normalMatrix, false);
|
|
1457
|
+
}
|
|
1458
|
+
if (firstRun) {
|
|
1459
|
+
return this.processTree();
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
// === ADVANCED COLLISION DETECTION ===
|
|
1463
|
+
// Test intersection between a sphere and a triangle.
|
|
1464
|
+
// Returns intersection data or false if no intersection.
|
|
1465
|
+
// @param sphere - Sphere object with center and radius properties.
|
|
1466
|
+
// @param triangle - Triangle object with a, b, c vertex properties.
|
|
1467
|
+
// @return Object with normal, point, depth properties or false if no intersection.
|
|
1468
|
+
triangleSphereIntersect(sphere, triangle) {
|
|
1469
|
+
var distanceSquared, i, intersectionDepth, k, planePoint, radiusSquared, ref, temporaryLine, temporaryVector1, temporaryVector2, triangleEdges, trianglePlane;
|
|
1470
|
+
temporaryVector1 = new Vector3();
|
|
1471
|
+
temporaryVector2 = new Vector3();
|
|
1472
|
+
temporaryLine = new Line3();
|
|
1473
|
+
trianglePlane = new ThreePlane();
|
|
1474
|
+
triangle.getPlane(trianglePlane);
|
|
1475
|
+
if (!sphere.intersectsPlane(trianglePlane)) {
|
|
1476
|
+
return false;
|
|
1477
|
+
}
|
|
1478
|
+
intersectionDepth = Math.abs(trianglePlane.distanceToSphere(sphere));
|
|
1479
|
+
radiusSquared = sphere.radius * sphere.radius - intersectionDepth * intersectionDepth;
|
|
1480
|
+
planePoint = trianglePlane.projectPoint(sphere.center, temporaryVector1);
|
|
1481
|
+
if (triangle.containsPoint(sphere.center)) {
|
|
1482
|
+
return {
|
|
1483
|
+
depth: Math.abs(trianglePlane.distanceToSphere(sphere)),
|
|
1484
|
+
normal: trianglePlane.normal.clone(),
|
|
1485
|
+
point: planePoint.clone()
|
|
1486
|
+
};
|
|
1487
|
+
}
|
|
1488
|
+
triangleEdges = [[triangle.a, triangle.b], [triangle.b, triangle.c], [triangle.c, triangle.a]];
|
|
1489
|
+
for (i = k = 0, ref = triangleEdges.length; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) {
|
|
1490
|
+
temporaryLine.set(triangleEdges[i][0], triangleEdges[i][1]);
|
|
1491
|
+
temporaryLine.closestPointToPoint(planePoint, true, temporaryVector2);
|
|
1492
|
+
distanceSquared = temporaryVector2.distanceToSquared(sphere.center);
|
|
1493
|
+
if (distanceSquared < radiusSquared) {
|
|
1494
|
+
return {
|
|
1495
|
+
depth: sphere.radius - Math.sqrt(distanceSquared),
|
|
1496
|
+
normal: sphere.center.clone().sub(temporaryVector2).normalize(),
|
|
1497
|
+
point: temporaryVector2.clone()
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
return false;
|
|
1502
|
+
}
|
|
1503
|
+
// Collect triangles that intersect with a sphere using spatial partitioning.
|
|
1504
|
+
// This method recursively traverses the polytree to find relevant triangles.
|
|
1505
|
+
// @param sphere - Sphere object to test intersection against.
|
|
1506
|
+
// @param triangles - Array to collect intersecting triangles.
|
|
1507
|
+
getSphereTriangles(sphere, triangles) {
|
|
1508
|
+
var currentPolygon, currentSubTree, k, polygonIndex, ref, results, subTreeIndex;
|
|
1509
|
+
results = [];
|
|
1510
|
+
for (subTreeIndex = k = 0, ref = this.subTrees.length; 0 <= ref ? k < ref : k > ref; subTreeIndex = 0 <= ref ? ++k : --k) {
|
|
1511
|
+
currentSubTree = this.subTrees[subTreeIndex];
|
|
1512
|
+
if (!sphere.intersectsBox(currentSubTree.box)) {
|
|
1513
|
+
continue;
|
|
1514
|
+
}
|
|
1515
|
+
if (currentSubTree.polygons.length > 0) {
|
|
1516
|
+
results.push((function() {
|
|
1517
|
+
var l, ref1, results1;
|
|
1518
|
+
results1 = [];
|
|
1519
|
+
for (polygonIndex = l = 0, ref1 = currentSubTree.polygons.length; 0 <= ref1 ? l < ref1 : l > ref1; polygonIndex = 0 <= ref1 ? ++l : --l) {
|
|
1520
|
+
currentPolygon = currentSubTree.polygons[polygonIndex];
|
|
1521
|
+
if (!currentPolygon.valid) {
|
|
1522
|
+
continue;
|
|
1523
|
+
}
|
|
1524
|
+
if (triangles.indexOf(currentPolygon.triangle) === -1) {
|
|
1525
|
+
results1.push(triangles.push(currentPolygon.triangle));
|
|
1526
|
+
} else {
|
|
1527
|
+
results1.push(void 0);
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
return results1;
|
|
1531
|
+
})());
|
|
1532
|
+
} else {
|
|
1533
|
+
results.push(currentSubTree.getSphereTriangles(sphere, triangles));
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
return results;
|
|
1537
|
+
}
|
|
1538
|
+
// Perform high-level sphere intersection testing against the entire polytree.
|
|
1539
|
+
// Returns collision data with adjusted position and penetration depth.
|
|
1540
|
+
// @param sphere - Sphere object to test collision against.
|
|
1541
|
+
// @return Object with normal and depth properties or false if no collision.
|
|
1542
|
+
sphereIntersect(sphere) {
|
|
1543
|
+
var adjustedSphere, collisionDetected, collisionVector, currentTriangle, intersectingTriangles, intersectionResult, k, penetrationDepth, ref, triangleIndex;
|
|
1544
|
+
collisionDetected = false;
|
|
1545
|
+
intersectionResult = void 0;
|
|
1546
|
+
intersectingTriangles = [];
|
|
1547
|
+
adjustedSphere = new Sphere();
|
|
1548
|
+
adjustedSphere.copy(sphere);
|
|
1549
|
+
this.getSphereTriangles(sphere, intersectingTriangles);
|
|
1550
|
+
for (triangleIndex = k = 0, ref = intersectingTriangles.length; 0 <= ref ? k < ref : k > ref; triangleIndex = 0 <= ref ? ++k : --k) {
|
|
1551
|
+
currentTriangle = intersectingTriangles[triangleIndex];
|
|
1552
|
+
if (intersectionResult = this.triangleSphereIntersect(adjustedSphere, currentTriangle)) {
|
|
1553
|
+
collisionDetected = true;
|
|
1554
|
+
adjustedSphere.center.add(intersectionResult.normal.multiplyScalar(intersectionResult.depth));
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
if (collisionDetected) {
|
|
1558
|
+
collisionVector = adjustedSphere.center.clone().sub(sphere.center);
|
|
1559
|
+
penetrationDepth = collisionVector.length();
|
|
1560
|
+
return {
|
|
1561
|
+
normal: collisionVector.normalize(),
|
|
1562
|
+
depth: penetrationDepth
|
|
1563
|
+
};
|
|
1564
|
+
}
|
|
1565
|
+
return false;
|
|
1566
|
+
}
|
|
1567
|
+
// Build polytree from Three.js scene graph (Group or Object3D).
|
|
1568
|
+
// This method traverses the scene graph and converts all meshes to polytree data.
|
|
1569
|
+
// @param group - Three.js Group or Object3D to traverse.
|
|
1570
|
+
fromGraphNode(sceneGraphNode) {
|
|
1571
|
+
var targetPolytreeInstance;
|
|
1572
|
+
sceneGraphNode.updateWorldMatrix(true, true);
|
|
1573
|
+
targetPolytreeInstance = this;
|
|
1574
|
+
sceneGraphNode.traverse(function(sceneObject) {
|
|
1575
|
+
if (sceneObject.isMesh === true) {
|
|
1576
|
+
return Polytree2.fromMesh(sceneObject, void 0, targetPolytreeInstance, false);
|
|
1577
|
+
}
|
|
1578
|
+
});
|
|
1579
|
+
return this.buildTree();
|
|
1580
|
+
}
|
|
1581
|
+
// === CLEANUP AND DISPOSAL METHODS ===
|
|
1582
|
+
// Clean up replaced polygons from CSG operations.
|
|
1583
|
+
deleteReplacedPolygons() {
|
|
1584
|
+
var i, k, ref, results;
|
|
1585
|
+
if (this.replacedPolygons.length > 0) {
|
|
1586
|
+
this.replacedPolygons.forEach(function(polygon) {
|
|
1587
|
+
return polygon.delete();
|
|
1588
|
+
});
|
|
1589
|
+
this.replacedPolygons.length = 0;
|
|
1590
|
+
}
|
|
1591
|
+
results = [];
|
|
1592
|
+
for (i = k = 0, ref = this.subTrees.length; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) {
|
|
1593
|
+
results.push(this.subTrees[i].deleteReplacedPolygons());
|
|
1594
|
+
}
|
|
1595
|
+
return results;
|
|
1596
|
+
}
|
|
1597
|
+
// Mark all polygons as original (not generated by CSG operations).
|
|
1598
|
+
markPolygonsAsOriginal() {
|
|
1599
|
+
return this.polygonArrays.forEach(function(polygonsArray) {
|
|
1600
|
+
if (polygonsArray.length) {
|
|
1601
|
+
return polygonsArray.forEach(function(polygon) {
|
|
1602
|
+
return polygon.originalValid = true;
|
|
1603
|
+
});
|
|
1604
|
+
}
|
|
1605
|
+
});
|
|
1606
|
+
}
|
|
1607
|
+
// Get polygon clones via callback for CSG operations.
|
|
1608
|
+
getPolygonCloneCallback(cbFunc, trianglesSet) {
|
|
1609
|
+
return this.polygonArrays.forEach(function(polygonsArray) {
|
|
1610
|
+
var i, k, ref, results;
|
|
1611
|
+
if (polygonsArray.length) {
|
|
1612
|
+
results = [];
|
|
1613
|
+
for (i = k = 0, ref = polygonsArray.length; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) {
|
|
1614
|
+
if (polygonsArray[i].valid) {
|
|
1615
|
+
results.push(cbFunc(polygonsArray[i].clone(), trianglesSet));
|
|
1616
|
+
} else {
|
|
1617
|
+
results.push(void 0);
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
return results;
|
|
1621
|
+
}
|
|
1622
|
+
});
|
|
1623
|
+
}
|
|
1624
|
+
// Delete this tree and all its data (primary cleanup method).
|
|
1625
|
+
delete(deletePolygons = true) {
|
|
1626
|
+
var i, k, ref;
|
|
1627
|
+
if (this.polygons.length > 0 && deletePolygons) {
|
|
1628
|
+
this.polygons.forEach(function(polygon) {
|
|
1629
|
+
return polygon.delete();
|
|
1630
|
+
});
|
|
1631
|
+
this.polygons.length = 0;
|
|
1632
|
+
}
|
|
1633
|
+
if (this.replacedPolygons.length > 0 && deletePolygons) {
|
|
1634
|
+
this.replacedPolygons.forEach(function(polygon) {
|
|
1635
|
+
return polygon.delete();
|
|
1636
|
+
});
|
|
1637
|
+
this.replacedPolygons.length = 0;
|
|
1638
|
+
}
|
|
1639
|
+
if (this.polygonArrays) {
|
|
1640
|
+
this.polygonArrays.length = 0;
|
|
1641
|
+
}
|
|
1642
|
+
if (this.subTrees.length) {
|
|
1643
|
+
for (i = k = 0, ref = this.subTrees.length; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) {
|
|
1644
|
+
this.subTrees[i].delete(deletePolygons);
|
|
1645
|
+
}
|
|
1646
|
+
this.subTrees.length = 0;
|
|
1647
|
+
}
|
|
1648
|
+
this.mesh = void 0;
|
|
1649
|
+
this.originalMatrixWorld = void 0;
|
|
1650
|
+
this.box = void 0;
|
|
1651
|
+
this.parent = void 0;
|
|
1652
|
+
return this.level = void 0;
|
|
1653
|
+
}
|
|
1654
|
+
// Dispose of this tree (alias for delete).
|
|
1655
|
+
dispose(deletePolygons = true) {
|
|
1656
|
+
return this.delete(deletePolygons);
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
;
|
|
1660
|
+
Polytree2.prototype.isPolytree = true;
|
|
1661
|
+
Polytree2.usePolytreeRay = true;
|
|
1662
|
+
Polytree2.disposePolytree = true;
|
|
1663
|
+
Polytree2.useWindingNumber = false;
|
|
1664
|
+
Polytree2.maxLevel = POLYTREE_MAX_DEPTH;
|
|
1665
|
+
Polytree2.polygonsPerTree = POLYTREE_MAX_POLYGONS_PER_NODE;
|
|
1666
|
+
Polytree2.rayIntersectTriangleType = "MollerTrumbore";
|
|
1667
|
+
Polytree2.operation = operationHandler;
|
|
1668
|
+
Polytree2.rayIntersectsTriangle = testRayTriangleIntersection;
|
|
1669
|
+
return Polytree2;
|
|
1670
|
+
}).call(void 0);
|
|
1671
|
+
Polytree.toGeometry = function(polytree) {
|
|
1672
|
+
var colors, defaultGroup, geometry, groupBase, groups, i, index, k, l, len, len1, m, n, normals, polygon, polygons, positions, ref, ref1, totalTriangles, uvs, vertices, verticesLen;
|
|
1673
|
+
groups = [];
|
|
1674
|
+
defaultGroup = [];
|
|
1675
|
+
uvs = void 0;
|
|
1676
|
+
colors = void 0;
|
|
1677
|
+
totalTriangles = 0;
|
|
1678
|
+
polygons = polytree.getPolygons();
|
|
1679
|
+
for (k = 0, len = polygons.length; k < len; k++) {
|
|
1680
|
+
polygon = polygons[k];
|
|
1681
|
+
totalTriangles += Math.max(0, polygon.vertices.length - 2);
|
|
1682
|
+
}
|
|
1683
|
+
positions = createVector3Buffer(totalTriangles * 3);
|
|
1684
|
+
normals = createVector3Buffer(totalTriangles * 3);
|
|
1685
|
+
for (l = 0, len1 = polygons.length; l < len1; l++) {
|
|
1686
|
+
polygon = polygons[l];
|
|
1687
|
+
vertices = polygon.vertices;
|
|
1688
|
+
verticesLen = vertices.length;
|
|
1689
|
+
if (polygon.shared !== void 0) {
|
|
1690
|
+
if (!groups[polygon.shared]) {
|
|
1691
|
+
groups[polygon.shared] = [];
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
if (verticesLen > 0) {
|
|
1695
|
+
if (vertices[0].uv !== void 0) {
|
|
1696
|
+
uvs || (uvs = createVector2Buffer(totalTriangles * 3));
|
|
1697
|
+
}
|
|
1698
|
+
if (vertices[0].color !== void 0) {
|
|
1699
|
+
colors || (colors = createVector3Buffer(totalTriangles * 3));
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
for (i = m = 3, ref = verticesLen; 3 <= ref ? m <= ref : m >= ref; i = 3 <= ref ? ++m : --m) {
|
|
1703
|
+
(polygon.shared === void 0 ? defaultGroup : groups[polygon.shared]).push(positions.top / 3, positions.top / 3 + 1, positions.top / 3 + 2);
|
|
1704
|
+
positions.write(vertices[0].pos);
|
|
1705
|
+
positions.write(vertices[i - 2].pos);
|
|
1706
|
+
positions.write(vertices[i - 1].pos);
|
|
1707
|
+
normals.write(vertices[0].normal);
|
|
1708
|
+
normals.write(vertices[i - 2].normal);
|
|
1709
|
+
normals.write(vertices[i - 1].normal);
|
|
1710
|
+
if (uvs != null) {
|
|
1711
|
+
uvs.write(vertices[0].uv);
|
|
1712
|
+
uvs.write(vertices[i - 2].uv);
|
|
1713
|
+
uvs.write(vertices[i - 1].uv);
|
|
1714
|
+
}
|
|
1715
|
+
if (colors != null) {
|
|
1716
|
+
colors.write(vertices[0].color);
|
|
1717
|
+
colors.write(vertices[i - 2].color);
|
|
1718
|
+
colors.write(vertices[i - 1].color);
|
|
1719
|
+
}
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
geometry = new BufferGeometry();
|
|
1723
|
+
geometry.setAttribute("position", new BufferAttribute(positions.array, 3));
|
|
1724
|
+
geometry.setAttribute("normal", new BufferAttribute(normals.array, 3));
|
|
1725
|
+
uvs && geometry.setAttribute("uv", new BufferAttribute(uvs.array, 2));
|
|
1726
|
+
colors && geometry.setAttribute("color", new BufferAttribute(colors.array, 3));
|
|
1727
|
+
if (groups.length > 0) {
|
|
1728
|
+
index = [];
|
|
1729
|
+
groupBase = 0;
|
|
1730
|
+
for (i = n = 0, ref1 = groups.length; 0 <= ref1 ? n < ref1 : n > ref1; i = 0 <= ref1 ? ++n : --n) {
|
|
1731
|
+
groups[i] = groups[i] || [];
|
|
1732
|
+
geometry.addGroup(groupBase, groups[i].length, i);
|
|
1733
|
+
groupBase += groups[i].length;
|
|
1734
|
+
index = index.concat(groups[i]);
|
|
1735
|
+
}
|
|
1736
|
+
if (defaultGroup.length) {
|
|
1737
|
+
geometry.addGroup(groupBase, defaultGroup.length, groups.length);
|
|
1738
|
+
index = index.concat(defaultGroup);
|
|
1739
|
+
}
|
|
1740
|
+
geometry.setIndex(index);
|
|
1741
|
+
}
|
|
1742
|
+
return geometry;
|
|
1743
|
+
};
|
|
1744
|
+
Polytree.toMesh = function(polytree, toMaterial) {
|
|
1745
|
+
var geometry;
|
|
1746
|
+
geometry = Polytree.toGeometry(polytree);
|
|
1747
|
+
return new Mesh(geometry, toMaterial);
|
|
1748
|
+
};
|
|
1749
|
+
Polytree.fromMesh = function(obj, objectIndex, polytree = new Polytree(), buildTargetPolytree = true) {
|
|
1750
|
+
var color, colorattr, geometry, group, groups, i, index, j, k, l, len, m, n, normal, normalattr, polygon, polys, pos, posattr, positionIndex, ref, ref1, uvCoords, uvIndex, uvattr, vertexIndex, vertices;
|
|
1751
|
+
if (obj.isPolytree) {
|
|
1752
|
+
return obj;
|
|
1753
|
+
}
|
|
1754
|
+
if (Polytree.rayIntersectTriangleType === "regular") {
|
|
1755
|
+
polytree.originalMatrixWorld = obj.matrixWorld.clone();
|
|
1756
|
+
}
|
|
1757
|
+
obj.updateWorldMatrix(true, true);
|
|
1758
|
+
geometry = obj.geometry;
|
|
1759
|
+
temporaryMatrixWithNormalCalc.getNormalMatrix(obj.matrix);
|
|
1760
|
+
groups = geometry.groups;
|
|
1761
|
+
uvattr = geometry.attributes.uv;
|
|
1762
|
+
colorattr = geometry.attributes.color;
|
|
1763
|
+
posattr = geometry.attributes.position;
|
|
1764
|
+
normalattr = geometry.attributes.normal;
|
|
1765
|
+
index = geometry.index ? geometry.index.array : Array(posattr.array.length / posattr.itemSize | 0).fill().map(function(_, i2) {
|
|
1766
|
+
return i2;
|
|
1767
|
+
});
|
|
1768
|
+
polys = [];
|
|
1769
|
+
for (i = k = 0, ref = index.length; k < ref; i = k += 3) {
|
|
1770
|
+
vertices = [];
|
|
1771
|
+
for (j = l = 0; l < 3; j = ++l) {
|
|
1772
|
+
vertexIndex = index[i + j];
|
|
1773
|
+
positionIndex = vertexIndex * 3;
|
|
1774
|
+
uvIndex = vertexIndex * 2;
|
|
1775
|
+
pos = new Vector3(posattr.array[positionIndex], posattr.array[positionIndex + 1], posattr.array[positionIndex + 2]);
|
|
1776
|
+
normal = new Vector3(normalattr.array[positionIndex], normalattr.array[positionIndex + 1], normalattr.array[positionIndex + 2]);
|
|
1777
|
+
pos.applyMatrix4(obj.matrix);
|
|
1778
|
+
normal.applyMatrix3(temporaryMatrixWithNormalCalc);
|
|
1779
|
+
uvCoords = uvattr ? {
|
|
1780
|
+
x: uvattr.array[uvIndex],
|
|
1781
|
+
y: uvattr.array[uvIndex + 1]
|
|
1782
|
+
} : void 0;
|
|
1783
|
+
color = colorattr ? {
|
|
1784
|
+
x: colorattr.array[uvIndex],
|
|
1785
|
+
y: colorattr.array[uvIndex + 1],
|
|
1786
|
+
z: colorattr.array[uvIndex + 2]
|
|
1787
|
+
} : void 0;
|
|
1788
|
+
vertices.push(new Vertex(pos, normal, uvCoords, color));
|
|
1789
|
+
}
|
|
1790
|
+
if (objectIndex === void 0 && groups && groups.length > 0) {
|
|
1791
|
+
polygon = void 0;
|
|
1792
|
+
for (m = 0, len = groups.length; m < len; m++) {
|
|
1793
|
+
group = groups[m];
|
|
1794
|
+
if (index[i] >= group.start && index[i] < group.start + group.count) {
|
|
1795
|
+
polygon = new Polygon(vertices, group.materialIndex);
|
|
1796
|
+
polygon.originalValid = true;
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
if (polygon) {
|
|
1800
|
+
polys.push(polygon);
|
|
1801
|
+
}
|
|
1802
|
+
} else {
|
|
1803
|
+
polygon = new Polygon(vertices, objectIndex);
|
|
1804
|
+
polygon.originalValid = true;
|
|
1805
|
+
polys.push(polygon);
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
for (i = n = 0, ref1 = polys.length; 0 <= ref1 ? n < ref1 : n > ref1; i = 0 <= ref1 ? ++n : --n) {
|
|
1809
|
+
if (isValidTriangle(polys[i].triangle)) {
|
|
1810
|
+
polytree.addPolygon(polys[i]);
|
|
1811
|
+
} else {
|
|
1812
|
+
polys[i].delete();
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
buildTargetPolytree && polytree.buildTree();
|
|
1816
|
+
if (Polytree.usePolytreeRay !== true) {
|
|
1817
|
+
polytree.mesh = obj;
|
|
1818
|
+
}
|
|
1819
|
+
return polytree;
|
|
1820
|
+
};
|
|
1821
|
+
Polytree.getSurface = function(meshOrGeometry) {
|
|
1822
|
+
var geometry, i, index, k, posattr, ref, surface, triangle, v1, v1Index, v2, v2Index, v3, v3Index;
|
|
1823
|
+
surface = 0;
|
|
1824
|
+
if (!meshOrGeometry) {
|
|
1825
|
+
throw new Error("Input is required.");
|
|
1826
|
+
}
|
|
1827
|
+
geometry = meshOrGeometry.geometry ? meshOrGeometry.geometry : meshOrGeometry;
|
|
1828
|
+
if (!(geometry && geometry.attributes)) {
|
|
1829
|
+
throw new Error("No geometry found.");
|
|
1830
|
+
}
|
|
1831
|
+
posattr = geometry.attributes.position;
|
|
1832
|
+
if (!posattr) {
|
|
1833
|
+
throw new Error("Geometry has no position attribute.");
|
|
1834
|
+
}
|
|
1835
|
+
index = geometry.index ? geometry.index.array : Array(posattr.array.length / posattr.itemSize | 0).fill().map(function(_, i2) {
|
|
1836
|
+
return i2;
|
|
1837
|
+
});
|
|
1838
|
+
for (i = k = 0, ref = index.length; k < ref; i = k += 3) {
|
|
1839
|
+
v1Index = index[i + 0] * 3;
|
|
1840
|
+
v2Index = index[i + 1] * 3;
|
|
1841
|
+
v3Index = index[i + 2] * 3;
|
|
1842
|
+
v1 = new Vector3(posattr.array[v1Index], posattr.array[v1Index + 1], posattr.array[v1Index + 2]);
|
|
1843
|
+
v2 = new Vector3(posattr.array[v2Index], posattr.array[v2Index + 1], posattr.array[v2Index + 2]);
|
|
1844
|
+
v3 = new Vector3(posattr.array[v3Index], posattr.array[v3Index + 1], posattr.array[v3Index + 2]);
|
|
1845
|
+
triangle = new Triangle(v1, v2, v3);
|
|
1846
|
+
surface += triangle.getArea();
|
|
1847
|
+
}
|
|
1848
|
+
return surface;
|
|
1849
|
+
};
|
|
1850
|
+
Polytree.prototype.getSurface = function(meshOrGeometry) {
|
|
1851
|
+
return Polytree.getSurface(meshOrGeometry);
|
|
1852
|
+
};
|
|
1853
|
+
Polytree.getVolume = function(input) {
|
|
1854
|
+
var bufferGeometry, face, faces, geometry, index, k, l, position, ref, ref1, v1, v2, v3, volume;
|
|
1855
|
+
volume = 0;
|
|
1856
|
+
v1 = new Vector3();
|
|
1857
|
+
v2 = new Vector3();
|
|
1858
|
+
v3 = new Vector3();
|
|
1859
|
+
geometry = null;
|
|
1860
|
+
if (input.isMesh) {
|
|
1861
|
+
geometry = input.geometry;
|
|
1862
|
+
} else if (input.isBufferGeometry) {
|
|
1863
|
+
geometry = input;
|
|
1864
|
+
} else {
|
|
1865
|
+
throw new Error("Input must be a Three.js Mesh or BufferGeometry.");
|
|
1866
|
+
}
|
|
1867
|
+
bufferGeometry = (typeof geometry.toBuffer === "function" ? geometry.toBuffer() : void 0) || geometry;
|
|
1868
|
+
if (!bufferGeometry.isBufferGeometry) {
|
|
1869
|
+
throw new Error("Unable to convert input to BufferGeometry.");
|
|
1870
|
+
}
|
|
1871
|
+
position = bufferGeometry.attributes.position;
|
|
1872
|
+
if (!position) {
|
|
1873
|
+
return 0;
|
|
1874
|
+
}
|
|
1875
|
+
if (bufferGeometry.index) {
|
|
1876
|
+
index = bufferGeometry.index;
|
|
1877
|
+
faces = index.count / 3;
|
|
1878
|
+
for (face = k = 0, ref = faces; 0 <= ref ? k < ref : k > ref; face = 0 <= ref ? ++k : --k) {
|
|
1879
|
+
v1.fromBufferAttribute(position, index.array[face * 3 + 0]);
|
|
1880
|
+
v2.fromBufferAttribute(position, index.array[face * 3 + 1]);
|
|
1881
|
+
v3.fromBufferAttribute(position, index.array[face * 3 + 2]);
|
|
1882
|
+
volume += signedVolumeOfTriangle(v1, v2, v3);
|
|
1883
|
+
}
|
|
1884
|
+
} else {
|
|
1885
|
+
faces = position.count / 3;
|
|
1886
|
+
for (face = l = 0, ref1 = faces; 0 <= ref1 ? l < ref1 : l > ref1; face = 0 <= ref1 ? ++l : --l) {
|
|
1887
|
+
v1.fromBufferAttribute(position, face * 3 + 0);
|
|
1888
|
+
v2.fromBufferAttribute(position, face * 3 + 1);
|
|
1889
|
+
v3.fromBufferAttribute(position, face * 3 + 2);
|
|
1890
|
+
volume += signedVolumeOfTriangle(v1, v2, v3);
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
return Math.abs(volume);
|
|
1894
|
+
};
|
|
1895
|
+
Polytree.prototype.getVolume = function(input) {
|
|
1896
|
+
return Polytree.getVolume(input);
|
|
1897
|
+
};
|
|
1898
|
+
var convertToPolytree;
|
|
1899
|
+
convertToPolytree = function(input) {
|
|
1900
|
+
var tempMaterial, tempMesh;
|
|
1901
|
+
if (!input) {
|
|
1902
|
+
return null;
|
|
1903
|
+
}
|
|
1904
|
+
if (input.isPolytree) {
|
|
1905
|
+
return {
|
|
1906
|
+
polytree: input,
|
|
1907
|
+
shouldCleanup: false
|
|
1908
|
+
};
|
|
1909
|
+
} else if (input.isMesh) {
|
|
1910
|
+
return {
|
|
1911
|
+
polytree: Polytree.fromMesh(input),
|
|
1912
|
+
shouldCleanup: true
|
|
1913
|
+
};
|
|
1914
|
+
} else if (input.isBufferGeometry) {
|
|
1915
|
+
tempMaterial = {
|
|
1916
|
+
isMaterial: true,
|
|
1917
|
+
type: "MeshBasicMaterial"
|
|
1918
|
+
};
|
|
1919
|
+
tempMesh = new Mesh(input, tempMaterial);
|
|
1920
|
+
return {
|
|
1921
|
+
polytree: Polytree.fromMesh(tempMesh),
|
|
1922
|
+
shouldCleanup: true
|
|
1923
|
+
};
|
|
1924
|
+
} else {
|
|
1925
|
+
return null;
|
|
1926
|
+
}
|
|
1927
|
+
};
|
|
1928
|
+
Polytree.closestPointToPoint = function(input, targetPoint, target = {}, maxDistance = Infinity) {
|
|
1929
|
+
var closestDistance, closestPoint, closestTriangle, currentDistance, j, len, polytree, result, shouldCleanup, testTriangle, triangle, trianglePoint, triangles;
|
|
1930
|
+
if (!(input && targetPoint)) {
|
|
1931
|
+
return null;
|
|
1932
|
+
}
|
|
1933
|
+
result = convertToPolytree(input);
|
|
1934
|
+
if (!result) {
|
|
1935
|
+
return null;
|
|
1936
|
+
}
|
|
1937
|
+
({ polytree, shouldCleanup } = result);
|
|
1938
|
+
closestDistance = maxDistance;
|
|
1939
|
+
closestPoint = null;
|
|
1940
|
+
closestTriangle = null;
|
|
1941
|
+
triangles = polytree.getTriangles();
|
|
1942
|
+
for (j = 0, len = triangles.length; j < len; j++) {
|
|
1943
|
+
triangle = triangles[j];
|
|
1944
|
+
testTriangle = new Triangle(triangle.a, triangle.b, triangle.c);
|
|
1945
|
+
trianglePoint = new Vector3();
|
|
1946
|
+
testTriangle.closestPointToPoint(targetPoint, trianglePoint);
|
|
1947
|
+
currentDistance = trianglePoint.distanceTo(targetPoint);
|
|
1948
|
+
if (currentDistance < closestDistance) {
|
|
1949
|
+
closestDistance = currentDistance;
|
|
1950
|
+
closestPoint = trianglePoint.clone();
|
|
1951
|
+
closestTriangle = triangle;
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
if (closestPoint) {
|
|
1955
|
+
target.point = closestPoint;
|
|
1956
|
+
target.distance = closestDistance;
|
|
1957
|
+
target.triangle = closestTriangle;
|
|
1958
|
+
shouldCleanup && polytree.delete();
|
|
1959
|
+
return target;
|
|
1960
|
+
}
|
|
1961
|
+
shouldCleanup && polytree.delete();
|
|
1962
|
+
return null;
|
|
1963
|
+
};
|
|
1964
|
+
Polytree.distanceToPoint = function(input, targetPoint) {
|
|
1965
|
+
var result;
|
|
1966
|
+
result = Polytree.closestPointToPoint(input, targetPoint);
|
|
1967
|
+
return (result != null ? result.distance : void 0) || Infinity;
|
|
1968
|
+
};
|
|
1969
|
+
Polytree.intersectsSphere = function(input, sphere) {
|
|
1970
|
+
var closestPoint, distance, j, len, polytree, result, shouldCleanup, testTriangle, triangle, triangles;
|
|
1971
|
+
if (!(input && sphere)) {
|
|
1972
|
+
return false;
|
|
1973
|
+
}
|
|
1974
|
+
result = convertToPolytree(input);
|
|
1975
|
+
if (!result) {
|
|
1976
|
+
return false;
|
|
1977
|
+
}
|
|
1978
|
+
({ polytree, shouldCleanup } = result);
|
|
1979
|
+
triangles = polytree.getTriangles();
|
|
1980
|
+
for (j = 0, len = triangles.length; j < len; j++) {
|
|
1981
|
+
triangle = triangles[j];
|
|
1982
|
+
testTriangle = new Triangle(triangle.a, triangle.b, triangle.c);
|
|
1983
|
+
closestPoint = new Vector3();
|
|
1984
|
+
testTriangle.closestPointToPoint(sphere.center, closestPoint);
|
|
1985
|
+
distance = closestPoint.distanceTo(sphere.center);
|
|
1986
|
+
if (distance <= sphere.radius) {
|
|
1987
|
+
shouldCleanup && polytree.delete();
|
|
1988
|
+
return true;
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
shouldCleanup && polytree.delete();
|
|
1992
|
+
return false;
|
|
1993
|
+
};
|
|
1994
|
+
Polytree.intersectsBox = function(input, boundingBox) {
|
|
1995
|
+
var j, len, polytree, result, shouldCleanup, testTriangle, triangle, triangles;
|
|
1996
|
+
if (!(input && boundingBox)) {
|
|
1997
|
+
return false;
|
|
1998
|
+
}
|
|
1999
|
+
result = convertToPolytree(input);
|
|
2000
|
+
if (!result) {
|
|
2001
|
+
return false;
|
|
2002
|
+
}
|
|
2003
|
+
({ polytree, shouldCleanup } = result);
|
|
2004
|
+
triangles = polytree.getTriangles();
|
|
2005
|
+
for (j = 0, len = triangles.length; j < len; j++) {
|
|
2006
|
+
triangle = triangles[j];
|
|
2007
|
+
testTriangle = new Triangle(triangle.a, triangle.b, triangle.c);
|
|
2008
|
+
if (boundingBox.intersectsTriangle(testTriangle)) {
|
|
2009
|
+
shouldCleanup && polytree.delete();
|
|
2010
|
+
return true;
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
shouldCleanup && polytree.delete();
|
|
2014
|
+
return false;
|
|
2015
|
+
};
|
|
2016
|
+
Polytree.intersectPlane = function(input, plane, target = []) {
|
|
2017
|
+
var edge, endDist, endPoint, intersectionPoint, intersectionPoints, j, k, len, len1, pointExists, polytree, pt, result, shouldCleanup, startDist, startPoint, t, testTriangle, triangle, triangleEdges, triangles;
|
|
2018
|
+
if (!(input && plane)) {
|
|
2019
|
+
return target;
|
|
2020
|
+
}
|
|
2021
|
+
result = convertToPolytree(input);
|
|
2022
|
+
if (!result) {
|
|
2023
|
+
return target;
|
|
2024
|
+
}
|
|
2025
|
+
({ polytree, shouldCleanup } = result);
|
|
2026
|
+
triangles = polytree.getTriangles();
|
|
2027
|
+
for (j = 0, len = triangles.length; j < len; j++) {
|
|
2028
|
+
triangle = triangles[j];
|
|
2029
|
+
testTriangle = new Triangle(triangle.a, triangle.b, triangle.c);
|
|
2030
|
+
intersectionPoints = [];
|
|
2031
|
+
pointExists = function(point, array, tolerance = 1e-10) {
|
|
2032
|
+
var existingPoint, k2, len12;
|
|
2033
|
+
for (k2 = 0, len12 = array.length; k2 < len12; k2++) {
|
|
2034
|
+
existingPoint = array[k2];
|
|
2035
|
+
if (existingPoint.distanceToSquared(point) < tolerance) {
|
|
2036
|
+
return true;
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
return false;
|
|
2040
|
+
};
|
|
2041
|
+
triangleEdges = [[triangle.a, triangle.b], [triangle.b, triangle.c], [triangle.c, triangle.a]];
|
|
2042
|
+
for (k = 0, len1 = triangleEdges.length; k < len1; k++) {
|
|
2043
|
+
edge = triangleEdges[k];
|
|
2044
|
+
startPoint = edge[0];
|
|
2045
|
+
endPoint = edge[1];
|
|
2046
|
+
startDist = plane.normal.dot(startPoint) + plane.constant;
|
|
2047
|
+
endDist = plane.normal.dot(endPoint) + plane.constant;
|
|
2048
|
+
if (startDist === 0 && endDist === 0) {
|
|
2049
|
+
continue;
|
|
2050
|
+
}
|
|
2051
|
+
if (startDist * endDist <= 0) {
|
|
2052
|
+
if (startDist === 0) {
|
|
2053
|
+
pt = startPoint.clone();
|
|
2054
|
+
if (!pointExists(pt, intersectionPoints)) {
|
|
2055
|
+
intersectionPoints.push(pt);
|
|
2056
|
+
}
|
|
2057
|
+
} else if (endDist === 0) {
|
|
2058
|
+
pt = endPoint.clone();
|
|
2059
|
+
if (!pointExists(pt, intersectionPoints)) {
|
|
2060
|
+
intersectionPoints.push(pt);
|
|
2061
|
+
}
|
|
2062
|
+
} else {
|
|
2063
|
+
t = startDist / (startDist - endDist);
|
|
2064
|
+
intersectionPoint = new Vector3();
|
|
2065
|
+
intersectionPoint.lerpVectors(startPoint, endPoint, t);
|
|
2066
|
+
intersectionPoints.push(intersectionPoint);
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
if (intersectionPoints.length === 2) {
|
|
2071
|
+
target.push(new Line3(intersectionPoints[0], intersectionPoints[1]));
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
shouldCleanup && polytree.delete();
|
|
2075
|
+
return target;
|
|
2076
|
+
};
|
|
2077
|
+
Polytree.sliceIntoLayers = function(input, layerHeight, minZ, maxZ, normal = new Vector3(0, 0, 1)) {
|
|
2078
|
+
var absEndDist, absStartDist, angleFactor, baseEpsilon, currentZ, dotWithNormal, edge, edgeDir, edgeLength, edgeVector, endDist, endPoint, epsilon, intersectionPoint, intersectionPoints, j, k, layerSegments, layers, len, len1, planeConstant, planeNormal, pointExists, polytree, pt, result, shouldCleanup, startDist, startPoint, t, triangle, triangleEdges, triangles;
|
|
2079
|
+
if (!(input && layerHeight > 0 && minZ <= maxZ)) {
|
|
2080
|
+
return [];
|
|
2081
|
+
}
|
|
2082
|
+
result = convertToPolytree(input);
|
|
2083
|
+
if (!result) {
|
|
2084
|
+
return [];
|
|
2085
|
+
}
|
|
2086
|
+
({ polytree, shouldCleanup } = result);
|
|
2087
|
+
layers = [];
|
|
2088
|
+
currentZ = minZ;
|
|
2089
|
+
while (currentZ <= maxZ) {
|
|
2090
|
+
planeNormal = normal.clone();
|
|
2091
|
+
planeConstant = -currentZ;
|
|
2092
|
+
layerSegments = [];
|
|
2093
|
+
triangles = polytree.getTriangles();
|
|
2094
|
+
for (j = 0, len = triangles.length; j < len; j++) {
|
|
2095
|
+
triangle = triangles[j];
|
|
2096
|
+
intersectionPoints = [];
|
|
2097
|
+
pointExists = function(point, array, tolerance = 1e-10) {
|
|
2098
|
+
var existingPoint, k2, len12;
|
|
2099
|
+
for (k2 = 0, len12 = array.length; k2 < len12; k2++) {
|
|
2100
|
+
existingPoint = array[k2];
|
|
2101
|
+
if (existingPoint.distanceToSquared(point) < tolerance) {
|
|
2102
|
+
return true;
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
return false;
|
|
2106
|
+
};
|
|
2107
|
+
triangleEdges = [[triangle.a, triangle.b], [triangle.b, triangle.c], [triangle.c, triangle.a]];
|
|
2108
|
+
for (k = 0, len1 = triangleEdges.length; k < len1; k++) {
|
|
2109
|
+
edge = triangleEdges[k];
|
|
2110
|
+
startPoint = edge[0];
|
|
2111
|
+
endPoint = edge[1];
|
|
2112
|
+
startDist = planeNormal.dot(startPoint) + planeConstant;
|
|
2113
|
+
endDist = planeNormal.dot(endPoint) + planeConstant;
|
|
2114
|
+
edgeVector = new Vector3().subVectors(endPoint, startPoint);
|
|
2115
|
+
edgeLength = edgeVector.length();
|
|
2116
|
+
if (edgeLength < 1e-10) {
|
|
2117
|
+
continue;
|
|
2118
|
+
}
|
|
2119
|
+
edgeDir = edgeVector.clone().divideScalar(edgeLength);
|
|
2120
|
+
dotWithNormal = Math.abs(edgeDir.dot(planeNormal));
|
|
2121
|
+
baseEpsilon = Math.max(1e-10, edgeLength * 1e-9);
|
|
2122
|
+
angleFactor = dotWithNormal < 0.02 ? 100 : 1;
|
|
2123
|
+
epsilon = baseEpsilon * angleFactor;
|
|
2124
|
+
absStartDist = Math.abs(startDist);
|
|
2125
|
+
absEndDist = Math.abs(endDist);
|
|
2126
|
+
if (absStartDist < epsilon && absEndDist < epsilon) {
|
|
2127
|
+
continue;
|
|
2128
|
+
}
|
|
2129
|
+
if (startDist * endDist <= 0) {
|
|
2130
|
+
if (absStartDist < epsilon) {
|
|
2131
|
+
pt = startPoint.clone();
|
|
2132
|
+
if (!pointExists(pt, intersectionPoints)) {
|
|
2133
|
+
intersectionPoints.push(pt);
|
|
2134
|
+
}
|
|
2135
|
+
} else if (absEndDist < epsilon) {
|
|
2136
|
+
pt = endPoint.clone();
|
|
2137
|
+
if (!pointExists(pt, intersectionPoints)) {
|
|
2138
|
+
intersectionPoints.push(pt);
|
|
2139
|
+
}
|
|
2140
|
+
} else {
|
|
2141
|
+
t = startDist / (startDist - endDist);
|
|
2142
|
+
intersectionPoint = new Vector3();
|
|
2143
|
+
intersectionPoint.lerpVectors(startPoint, endPoint, t);
|
|
2144
|
+
intersectionPoints.push(intersectionPoint);
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
if (intersectionPoints.length === 2) {
|
|
2149
|
+
layerSegments.push(new Line3(intersectionPoints[0], intersectionPoints[1]));
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
layers.push(layerSegments);
|
|
2153
|
+
currentZ += layerHeight;
|
|
2154
|
+
}
|
|
2155
|
+
shouldCleanup && polytree.delete();
|
|
2156
|
+
return layers;
|
|
2157
|
+
};
|
|
2158
|
+
Polytree.shapecast = function(input, queryCallback, collectCallback = null) {
|
|
2159
|
+
var j, len, polytree, result, results, shouldCleanup, testTriangle, triangle, triangles;
|
|
2160
|
+
if (!(input && queryCallback)) {
|
|
2161
|
+
return [];
|
|
2162
|
+
}
|
|
2163
|
+
result = convertToPolytree(input);
|
|
2164
|
+
if (!result) {
|
|
2165
|
+
return [];
|
|
2166
|
+
}
|
|
2167
|
+
({ polytree, shouldCleanup } = result);
|
|
2168
|
+
results = [];
|
|
2169
|
+
triangles = polytree.getTriangles();
|
|
2170
|
+
for (j = 0, len = triangles.length; j < len; j++) {
|
|
2171
|
+
triangle = triangles[j];
|
|
2172
|
+
testTriangle = new Triangle(triangle.a, triangle.b, triangle.c);
|
|
2173
|
+
if (queryCallback(testTriangle, triangle)) {
|
|
2174
|
+
if (collectCallback) {
|
|
2175
|
+
result = collectCallback(testTriangle, triangle);
|
|
2176
|
+
if (result != null) {
|
|
2177
|
+
results.push(result);
|
|
2178
|
+
}
|
|
2179
|
+
} else {
|
|
2180
|
+
results.push(triangle);
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
shouldCleanup && polytree.delete();
|
|
2185
|
+
return results;
|
|
2186
|
+
};
|
|
2187
|
+
Polytree.getTrianglesNearPoint = function(input, targetPoint, searchRadius) {
|
|
2188
|
+
if (!(input && targetPoint && searchRadius > 0)) {
|
|
2189
|
+
return [];
|
|
2190
|
+
}
|
|
2191
|
+
return Polytree.shapecast(input, function(testTriangle, originalTriangle) {
|
|
2192
|
+
return testTriangle.a.distanceTo(targetPoint) <= searchRadius || testTriangle.b.distanceTo(targetPoint) <= searchRadius || testTriangle.c.distanceTo(targetPoint) <= searchRadius;
|
|
2193
|
+
});
|
|
2194
|
+
};
|
|
2195
|
+
Polytree.estimateVolumeViaSampling = function(input, sampleCount = 1e4, boundingBox = null) {
|
|
2196
|
+
var boxSize, boxVolume, i, identityMatrix, insideCount, intersections, j, k, len, polytree, ref, result, samplePoint, shouldCleanup, testRay, triangle, triangles, volumeRatio;
|
|
2197
|
+
if (!input) {
|
|
2198
|
+
return 0;
|
|
2199
|
+
}
|
|
2200
|
+
result = convertToPolytree(input);
|
|
2201
|
+
if (!result) {
|
|
2202
|
+
return 0;
|
|
2203
|
+
}
|
|
2204
|
+
({ polytree, shouldCleanup } = result);
|
|
2205
|
+
if (!boundingBox) {
|
|
2206
|
+
boundingBox = new Box3();
|
|
2207
|
+
triangles = polytree.getTriangles();
|
|
2208
|
+
for (j = 0, len = triangles.length; j < len; j++) {
|
|
2209
|
+
triangle = triangles[j];
|
|
2210
|
+
boundingBox.expandByPoint(triangle.a);
|
|
2211
|
+
boundingBox.expandByPoint(triangle.b);
|
|
2212
|
+
boundingBox.expandByPoint(triangle.c);
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
boxSize = new Vector3();
|
|
2216
|
+
boundingBox.getSize(boxSize);
|
|
2217
|
+
boxVolume = boxSize.x * boxSize.y * boxSize.z;
|
|
2218
|
+
if (boxVolume === 0) {
|
|
2219
|
+
return 0;
|
|
2220
|
+
}
|
|
2221
|
+
insideCount = 0;
|
|
2222
|
+
for (i = k = 0, ref = sampleCount; 0 <= ref ? k < ref : k > ref; i = 0 <= ref ? ++k : --k) {
|
|
2223
|
+
samplePoint = new Vector3(boundingBox.min.x + Math.random() * boxSize.x, boundingBox.min.y + Math.random() * boxSize.y, boundingBox.min.z + Math.random() * boxSize.z);
|
|
2224
|
+
testRay = new Ray(samplePoint, new Vector3(1, 0, 0));
|
|
2225
|
+
identityMatrix = {
|
|
2226
|
+
elements: [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
|
|
2227
|
+
isMatrix4: true
|
|
2228
|
+
};
|
|
2229
|
+
intersections = polytree.rayIntersect(testRay, identityMatrix);
|
|
2230
|
+
if (intersections.length % 2 === 1) {
|
|
2231
|
+
insideCount++;
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
volumeRatio = insideCount / sampleCount;
|
|
2235
|
+
shouldCleanup && polytree.delete();
|
|
2236
|
+
return volumeRatio * boxVolume;
|
|
2237
|
+
};
|
|
2238
|
+
var uniteRules;
|
|
2239
|
+
uniteRules = {
|
|
2240
|
+
// Rules for object A: Remove polygons that are inside B or coplanar-back with B.
|
|
2241
|
+
a: [
|
|
2242
|
+
{
|
|
2243
|
+
array: true,
|
|
2244
|
+
rule: [
|
|
2245
|
+
"inside",
|
|
2246
|
+
"coplanar-back"
|
|
2247
|
+
]
|
|
2248
|
+
},
|
|
2249
|
+
{
|
|
2250
|
+
array: false,
|
|
2251
|
+
rule: "inside"
|
|
2252
|
+
}
|
|
2253
|
+
],
|
|
2254
|
+
// Rules for object B: Remove polygons inside A or coplanar with A.
|
|
2255
|
+
b: [
|
|
2256
|
+
{
|
|
2257
|
+
array: true,
|
|
2258
|
+
rule: [
|
|
2259
|
+
"inside",
|
|
2260
|
+
"coplanar-back"
|
|
2261
|
+
]
|
|
2262
|
+
},
|
|
2263
|
+
{
|
|
2264
|
+
array: true,
|
|
2265
|
+
rule: [
|
|
2266
|
+
"inside",
|
|
2267
|
+
"coplanar-front"
|
|
2268
|
+
]
|
|
2269
|
+
},
|
|
2270
|
+
{
|
|
2271
|
+
array: false,
|
|
2272
|
+
rule: "inside"
|
|
2273
|
+
}
|
|
2274
|
+
]
|
|
2275
|
+
};
|
|
2276
|
+
Polytree.unite = function(mesh1, mesh2, async = true) {
|
|
2277
|
+
var polytreeA, polytreeB, resultMesh, resultPolytree, targetMaterial;
|
|
2278
|
+
if (async) {
|
|
2279
|
+
if (mesh1.isPolytree && mesh2.isPolytree) {
|
|
2280
|
+
return Polytree.async.unite(mesh1, mesh2, true);
|
|
2281
|
+
} else {
|
|
2282
|
+
polytreeA = Polytree.fromMesh(mesh1);
|
|
2283
|
+
polytreeB = Polytree.fromMesh(mesh2);
|
|
2284
|
+
targetMaterial = Array.isArray(mesh1.material) ? mesh1.material[0] : mesh1.material;
|
|
2285
|
+
targetMaterial = targetMaterial.clone();
|
|
2286
|
+
return Polytree.async.unite(polytreeA, polytreeB, false).then(function(resultPolytree2) {
|
|
2287
|
+
var resultMesh2;
|
|
2288
|
+
resultMesh2 = Polytree.toMesh(resultPolytree2, targetMaterial);
|
|
2289
|
+
disposePolytreeResources(polytreeA, polytreeB, resultPolytree2);
|
|
2290
|
+
return resultMesh2;
|
|
2291
|
+
});
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
if (mesh1.isPolytree && mesh2.isPolytree) {
|
|
2295
|
+
polytreeA = mesh1;
|
|
2296
|
+
polytreeB = mesh2;
|
|
2297
|
+
return this.uniteCore(polytreeA, polytreeB, true);
|
|
2298
|
+
} else {
|
|
2299
|
+
polytreeA = Polytree.fromMesh(mesh1);
|
|
2300
|
+
polytreeB = Polytree.fromMesh(mesh2);
|
|
2301
|
+
targetMaterial = Array.isArray(mesh1.material) ? mesh1.material[0] : mesh1.material;
|
|
2302
|
+
targetMaterial = targetMaterial.clone();
|
|
2303
|
+
resultPolytree = this.uniteCore(polytreeA, polytreeB, false);
|
|
2304
|
+
resultMesh = Polytree.toMesh(resultPolytree, targetMaterial);
|
|
2305
|
+
disposePolytreeResources(polytreeA, polytreeB, resultPolytree);
|
|
2306
|
+
return resultMesh;
|
|
2307
|
+
}
|
|
2308
|
+
};
|
|
2309
|
+
Polytree.uniteCore = function(polytreeA, polytreeB, buildTargetPolytree = true) {
|
|
2310
|
+
var currentMeshSideA, currentMeshSideB, polytree, trianglesSet;
|
|
2311
|
+
polytree = new Polytree();
|
|
2312
|
+
trianglesSet = /* @__PURE__ */ new Set();
|
|
2313
|
+
if (polytreeA.box.intersectsBox(polytreeB.box)) {
|
|
2314
|
+
currentMeshSideA = void 0;
|
|
2315
|
+
currentMeshSideB = void 0;
|
|
2316
|
+
if (polytreeA.mesh) {
|
|
2317
|
+
currentMeshSideA = polytreeA.mesh.material.side;
|
|
2318
|
+
polytreeA.mesh.material.side = DoubleSide;
|
|
2319
|
+
}
|
|
2320
|
+
if (polytreeB.mesh) {
|
|
2321
|
+
currentMeshSideB = polytreeB.mesh.material.side;
|
|
2322
|
+
polytreeB.mesh.material.side = DoubleSide;
|
|
2323
|
+
}
|
|
2324
|
+
polytreeA.resetPolygons(false);
|
|
2325
|
+
polytreeB.resetPolygons(false);
|
|
2326
|
+
polytreeA.markIntesectingPolygons(polytreeB);
|
|
2327
|
+
polytreeB.markIntesectingPolygons(polytreeA);
|
|
2328
|
+
handleIntersectingPolytrees(polytreeA, polytreeB);
|
|
2329
|
+
polytreeA.deleteReplacedPolygons();
|
|
2330
|
+
polytreeB.deleteReplacedPolygons();
|
|
2331
|
+
polytreeA.deletePolygonsByStateRules(uniteRules.a);
|
|
2332
|
+
polytreeB.deletePolygonsByStateRules(uniteRules.b);
|
|
2333
|
+
polytreeA.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
2334
|
+
polytreeB.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
2335
|
+
if (polytreeA.mesh && polytreeA.mesh.material.side !== currentMeshSideA) {
|
|
2336
|
+
polytreeA.mesh.material.side = currentMeshSideA;
|
|
2337
|
+
}
|
|
2338
|
+
if (polytreeB.mesh && polytreeB.mesh.material.side !== currentMeshSideB) {
|
|
2339
|
+
polytreeB.mesh.material.side = currentMeshSideB;
|
|
2340
|
+
}
|
|
2341
|
+
} else {
|
|
2342
|
+
polytreeA.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
2343
|
+
polytreeB.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
2344
|
+
}
|
|
2345
|
+
trianglesSet.clear();
|
|
2346
|
+
trianglesSet = void 0;
|
|
2347
|
+
polytree.markPolygonsAsOriginal();
|
|
2348
|
+
buildTargetPolytree && polytree.buildTree();
|
|
2349
|
+
return polytree;
|
|
2350
|
+
};
|
|
2351
|
+
var subtractRules;
|
|
2352
|
+
subtractRules = {
|
|
2353
|
+
// Rules for object A: Remove polygons that are inside B.
|
|
2354
|
+
a: [
|
|
2355
|
+
{
|
|
2356
|
+
array: true,
|
|
2357
|
+
rule: [
|
|
2358
|
+
"inside",
|
|
2359
|
+
"coplanar-back"
|
|
2360
|
+
]
|
|
2361
|
+
},
|
|
2362
|
+
{
|
|
2363
|
+
array: true,
|
|
2364
|
+
rule: [
|
|
2365
|
+
"inside",
|
|
2366
|
+
"coplanar-front"
|
|
2367
|
+
]
|
|
2368
|
+
},
|
|
2369
|
+
{
|
|
2370
|
+
array: false,
|
|
2371
|
+
rule: "inside"
|
|
2372
|
+
}
|
|
2373
|
+
],
|
|
2374
|
+
// Rules for object B: Remove polygons that are outside A.
|
|
2375
|
+
b: [
|
|
2376
|
+
{
|
|
2377
|
+
array: true,
|
|
2378
|
+
rule: [
|
|
2379
|
+
"outside",
|
|
2380
|
+
"coplanar-back"
|
|
2381
|
+
]
|
|
2382
|
+
},
|
|
2383
|
+
{
|
|
2384
|
+
array: true,
|
|
2385
|
+
rule: [
|
|
2386
|
+
"outside",
|
|
2387
|
+
"coplanar-front"
|
|
2388
|
+
]
|
|
2389
|
+
},
|
|
2390
|
+
{
|
|
2391
|
+
array: true,
|
|
2392
|
+
rule: [
|
|
2393
|
+
"inside",
|
|
2394
|
+
"coplanar-front"
|
|
2395
|
+
]
|
|
2396
|
+
},
|
|
2397
|
+
{
|
|
2398
|
+
array: false,
|
|
2399
|
+
rule: "outside"
|
|
2400
|
+
}
|
|
2401
|
+
]
|
|
2402
|
+
};
|
|
2403
|
+
Polytree.subtract = function(mesh1, mesh2, async = true) {
|
|
2404
|
+
var polytreeA, polytreeB, resultMesh, resultPolytree, targetMaterial;
|
|
2405
|
+
if (async) {
|
|
2406
|
+
if (mesh1.isPolytree && mesh2.isPolytree) {
|
|
2407
|
+
return Polytree.async.subtract(mesh1, mesh2, true);
|
|
2408
|
+
} else {
|
|
2409
|
+
polytreeA = Polytree.fromMesh(mesh1);
|
|
2410
|
+
polytreeB = Polytree.fromMesh(mesh2);
|
|
2411
|
+
targetMaterial = Array.isArray(mesh1.material) ? mesh1.material[0] : mesh1.material;
|
|
2412
|
+
targetMaterial = targetMaterial.clone();
|
|
2413
|
+
return Polytree.async.subtract(polytreeA, polytreeB, false).then(function(resultPolytree2) {
|
|
2414
|
+
var resultMesh2;
|
|
2415
|
+
resultMesh2 = Polytree.toMesh(resultPolytree2, targetMaterial);
|
|
2416
|
+
disposePolytreeResources(polytreeA, polytreeB, resultPolytree2);
|
|
2417
|
+
return resultMesh2;
|
|
2418
|
+
});
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
if (mesh1.isPolytree && mesh2.isPolytree) {
|
|
2422
|
+
polytreeA = mesh1;
|
|
2423
|
+
polytreeB = mesh2;
|
|
2424
|
+
return this.subtractCore(polytreeA, polytreeB, true);
|
|
2425
|
+
} else {
|
|
2426
|
+
polytreeA = Polytree.fromMesh(mesh1);
|
|
2427
|
+
polytreeB = Polytree.fromMesh(mesh2);
|
|
2428
|
+
targetMaterial = Array.isArray(mesh1.material) ? mesh1.material[0] : mesh1.material;
|
|
2429
|
+
targetMaterial = targetMaterial.clone();
|
|
2430
|
+
resultPolytree = this.subtractCore(polytreeA, polytreeB, false);
|
|
2431
|
+
resultMesh = Polytree.toMesh(resultPolytree, targetMaterial);
|
|
2432
|
+
disposePolytreeResources(polytreeA, polytreeB, resultPolytree);
|
|
2433
|
+
return resultMesh;
|
|
2434
|
+
}
|
|
2435
|
+
};
|
|
2436
|
+
Polytree.subtractCore = function(polytreeA, polytreeB, buildTargetPolytree = true) {
|
|
2437
|
+
var currentMeshSideA, currentMeshSideB, polytree, trianglesSet;
|
|
2438
|
+
polytree = new Polytree();
|
|
2439
|
+
trianglesSet = /* @__PURE__ */ new Set();
|
|
2440
|
+
if (polytreeA.box.intersectsBox(polytreeB.box)) {
|
|
2441
|
+
currentMeshSideA = void 0;
|
|
2442
|
+
currentMeshSideB = void 0;
|
|
2443
|
+
if (polytreeA.mesh) {
|
|
2444
|
+
currentMeshSideA = polytreeA.mesh.material.side;
|
|
2445
|
+
polytreeA.mesh.material.side = DoubleSide;
|
|
2446
|
+
}
|
|
2447
|
+
if (polytreeB.mesh) {
|
|
2448
|
+
currentMeshSideB = polytreeB.mesh.material.side;
|
|
2449
|
+
polytreeB.mesh.material.side = DoubleSide;
|
|
2450
|
+
}
|
|
2451
|
+
polytreeA.resetPolygons(false);
|
|
2452
|
+
polytreeB.resetPolygons(false);
|
|
2453
|
+
polytreeA.markIntesectingPolygons(polytreeB);
|
|
2454
|
+
polytreeB.markIntesectingPolygons(polytreeA);
|
|
2455
|
+
handleIntersectingPolytrees(polytreeA, polytreeB);
|
|
2456
|
+
polytreeA.deleteReplacedPolygons();
|
|
2457
|
+
polytreeB.deleteReplacedPolygons();
|
|
2458
|
+
polytreeA.deletePolygonsByStateRules(subtractRules.a);
|
|
2459
|
+
polytreeB.deletePolygonsByStateRules(subtractRules.b);
|
|
2460
|
+
polytreeB.deletePolygonsByIntersection(false);
|
|
2461
|
+
polytreeB.invert();
|
|
2462
|
+
polytreeA.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
2463
|
+
polytreeB.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
2464
|
+
if (polytreeA.mesh && polytreeA.mesh.material.side !== currentMeshSideA) {
|
|
2465
|
+
polytreeA.mesh.material.side = currentMeshSideA;
|
|
2466
|
+
}
|
|
2467
|
+
if (polytreeB.mesh && polytreeB.mesh.material.side !== currentMeshSideB) {
|
|
2468
|
+
polytreeB.mesh.material.side = currentMeshSideB;
|
|
2469
|
+
}
|
|
2470
|
+
} else {
|
|
2471
|
+
polytreeA.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
2472
|
+
}
|
|
2473
|
+
trianglesSet.clear();
|
|
2474
|
+
trianglesSet = void 0;
|
|
2475
|
+
polytree.markPolygonsAsOriginal();
|
|
2476
|
+
buildTargetPolytree && polytree.buildTree();
|
|
2477
|
+
return polytree;
|
|
2478
|
+
};
|
|
2479
|
+
var intersectRules;
|
|
2480
|
+
intersectRules = {
|
|
2481
|
+
// Rules for object A: Remove inside+coplanar-back, outside+coplanar-front/back, and outside.
|
|
2482
|
+
a: [
|
|
2483
|
+
{
|
|
2484
|
+
array: true,
|
|
2485
|
+
rule: [
|
|
2486
|
+
"inside",
|
|
2487
|
+
"coplanar-back"
|
|
2488
|
+
]
|
|
2489
|
+
},
|
|
2490
|
+
{
|
|
2491
|
+
array: true,
|
|
2492
|
+
rule: [
|
|
2493
|
+
"outside",
|
|
2494
|
+
"coplanar-front"
|
|
2495
|
+
]
|
|
2496
|
+
},
|
|
2497
|
+
{
|
|
2498
|
+
array: true,
|
|
2499
|
+
rule: [
|
|
2500
|
+
"outside",
|
|
2501
|
+
"coplanar-back"
|
|
2502
|
+
]
|
|
2503
|
+
},
|
|
2504
|
+
{
|
|
2505
|
+
array: false,
|
|
2506
|
+
rule: "outside"
|
|
2507
|
+
}
|
|
2508
|
+
],
|
|
2509
|
+
// Rules for object B: Remove inside+coplanar-front/back, outside+coplanar-front/back, and outside.
|
|
2510
|
+
b: [
|
|
2511
|
+
{
|
|
2512
|
+
array: true,
|
|
2513
|
+
rule: [
|
|
2514
|
+
"inside",
|
|
2515
|
+
"coplanar-front"
|
|
2516
|
+
]
|
|
2517
|
+
},
|
|
2518
|
+
{
|
|
2519
|
+
array: true,
|
|
2520
|
+
rule: [
|
|
2521
|
+
"inside",
|
|
2522
|
+
"coplanar-back"
|
|
2523
|
+
]
|
|
2524
|
+
},
|
|
2525
|
+
{
|
|
2526
|
+
array: true,
|
|
2527
|
+
rule: [
|
|
2528
|
+
"outside",
|
|
2529
|
+
"coplanar-front"
|
|
2530
|
+
]
|
|
2531
|
+
},
|
|
2532
|
+
{
|
|
2533
|
+
array: true,
|
|
2534
|
+
rule: [
|
|
2535
|
+
"outside",
|
|
2536
|
+
"coplanar-back"
|
|
2537
|
+
]
|
|
2538
|
+
},
|
|
2539
|
+
{
|
|
2540
|
+
array: false,
|
|
2541
|
+
rule: "outside"
|
|
2542
|
+
}
|
|
2543
|
+
]
|
|
2544
|
+
};
|
|
2545
|
+
Polytree.intersect = function(mesh1, mesh2, async = true) {
|
|
2546
|
+
var polytreeA, polytreeB, resultMesh, resultPolytree, targetMaterial;
|
|
2547
|
+
if (async) {
|
|
2548
|
+
if (mesh1.isPolytree && mesh2.isPolytree) {
|
|
2549
|
+
return Polytree.async.intersect(mesh1, mesh2, true);
|
|
2550
|
+
} else {
|
|
2551
|
+
polytreeA = Polytree.fromMesh(mesh1);
|
|
2552
|
+
polytreeB = Polytree.fromMesh(mesh2);
|
|
2553
|
+
targetMaterial = Array.isArray(mesh1.material) ? mesh1.material[0] : mesh1.material;
|
|
2554
|
+
targetMaterial = targetMaterial.clone();
|
|
2555
|
+
return Polytree.async.intersect(polytreeA, polytreeB, false).then(function(resultPolytree2) {
|
|
2556
|
+
var resultMesh2;
|
|
2557
|
+
resultMesh2 = Polytree.toMesh(resultPolytree2, targetMaterial);
|
|
2558
|
+
disposePolytreeResources(polytreeA, polytreeB, resultPolytree2);
|
|
2559
|
+
return resultMesh2;
|
|
2560
|
+
});
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
if (mesh1.isPolytree && mesh2.isPolytree) {
|
|
2564
|
+
polytreeA = mesh1;
|
|
2565
|
+
polytreeB = mesh2;
|
|
2566
|
+
return this.intersectCore(polytreeA, polytreeB, true);
|
|
2567
|
+
} else {
|
|
2568
|
+
polytreeA = Polytree.fromMesh(mesh1);
|
|
2569
|
+
polytreeB = Polytree.fromMesh(mesh2);
|
|
2570
|
+
targetMaterial = Array.isArray(mesh1.material) ? mesh1.material[0] : mesh1.material;
|
|
2571
|
+
targetMaterial = targetMaterial.clone();
|
|
2572
|
+
resultPolytree = this.intersectCore(polytreeA, polytreeB, false);
|
|
2573
|
+
resultMesh = Polytree.toMesh(resultPolytree, targetMaterial);
|
|
2574
|
+
disposePolytreeResources(polytreeA, polytreeB, resultPolytree);
|
|
2575
|
+
return resultMesh;
|
|
2576
|
+
}
|
|
2577
|
+
};
|
|
2578
|
+
Polytree.intersectCore = function(polytreeA, polytreeB, buildTargetPolytree = true) {
|
|
2579
|
+
var currentMeshSideA, currentMeshSideB, polytree, trianglesSet;
|
|
2580
|
+
polytree = new Polytree();
|
|
2581
|
+
trianglesSet = /* @__PURE__ */ new Set();
|
|
2582
|
+
if (polytreeA.box.intersectsBox(polytreeB.box)) {
|
|
2583
|
+
currentMeshSideA = void 0;
|
|
2584
|
+
currentMeshSideB = void 0;
|
|
2585
|
+
if (polytreeA.mesh) {
|
|
2586
|
+
currentMeshSideA = polytreeA.mesh.material.side;
|
|
2587
|
+
polytreeA.mesh.material.side = DoubleSide;
|
|
2588
|
+
}
|
|
2589
|
+
if (polytreeB.mesh) {
|
|
2590
|
+
currentMeshSideB = polytreeB.mesh.material.side;
|
|
2591
|
+
polytreeB.mesh.material.side = DoubleSide;
|
|
2592
|
+
}
|
|
2593
|
+
polytreeA.resetPolygons(false);
|
|
2594
|
+
polytreeB.resetPolygons(false);
|
|
2595
|
+
polytreeA.markIntesectingPolygons(polytreeB);
|
|
2596
|
+
polytreeB.markIntesectingPolygons(polytreeA);
|
|
2597
|
+
handleIntersectingPolytrees(polytreeA, polytreeB);
|
|
2598
|
+
polytreeA.deleteReplacedPolygons();
|
|
2599
|
+
polytreeB.deleteReplacedPolygons();
|
|
2600
|
+
polytreeA.deletePolygonsByStateRules(intersectRules.a);
|
|
2601
|
+
polytreeB.deletePolygonsByStateRules(intersectRules.b);
|
|
2602
|
+
polytreeA.deletePolygonsByIntersection(false);
|
|
2603
|
+
polytreeB.deletePolygonsByIntersection(false);
|
|
2604
|
+
polytreeA.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
2605
|
+
polytreeB.getPolygonCloneCallback(polytree.addPolygon.bind(polytree), trianglesSet);
|
|
2606
|
+
if (polytreeA.mesh && polytreeA.mesh.material.side !== currentMeshSideA) {
|
|
2607
|
+
polytreeA.mesh.material.side = currentMeshSideA;
|
|
2608
|
+
}
|
|
2609
|
+
if (polytreeB.mesh && polytreeB.mesh.material.side !== currentMeshSideB) {
|
|
2610
|
+
polytreeB.mesh.material.side = currentMeshSideB;
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
trianglesSet.clear();
|
|
2614
|
+
trianglesSet = void 0;
|
|
2615
|
+
polytree.markPolygonsAsOriginal();
|
|
2616
|
+
buildTargetPolytree && polytree.buildTree();
|
|
2617
|
+
return polytree;
|
|
2618
|
+
};
|
|
2619
|
+
Polytree.async = {
|
|
2620
|
+
// Default batch size for processing large arrays of objects.
|
|
2621
|
+
// Objects arrays larger than this size will be processed in batches to prevent memory issues.
|
|
2622
|
+
batchSize: DEFAULT_BUFFER_SIZE,
|
|
2623
|
+
// Maximum worker threads hint for parallel processing (informational).
|
|
2624
|
+
// Set to Infinity for unlimited parallelism when supported by the environment.
|
|
2625
|
+
maxWorkerThreads: MAX_WORKER_THREADS,
|
|
2626
|
+
// Perform asynchronous union operation between two polytree objects.
|
|
2627
|
+
// Creates a new polytree containing the combined volume of both input polytrees.
|
|
2628
|
+
// @param polytreeA - First polytree operand for the union operation.
|
|
2629
|
+
// @param polytreeB - Second polytree operand for the union operation.
|
|
2630
|
+
// @param buildTargetPolytree - Whether to build the target polytree structure (default: true).
|
|
2631
|
+
// @return Promise that resolves to the resulting polytree from the union operation.
|
|
2632
|
+
unite: function(polytreeA, polytreeB, buildTargetPolytree = true) {
|
|
2633
|
+
return new Promise(function(resolve, reject) {
|
|
2634
|
+
var error, result, timeoutId;
|
|
2635
|
+
timeoutId = null;
|
|
2636
|
+
if (ASYNC_OPERATION_TIMEOUT !== Infinity) {
|
|
2637
|
+
timeoutId = setTimeout(function() {
|
|
2638
|
+
return reject(new Error(`Union operation timed out after ${ASYNC_OPERATION_TIMEOUT}ms`));
|
|
2639
|
+
}, ASYNC_OPERATION_TIMEOUT);
|
|
2640
|
+
}
|
|
2641
|
+
try {
|
|
2642
|
+
result = Polytree.uniteCore(polytreeA, polytreeB, buildTargetPolytree);
|
|
2643
|
+
disposePolytreeResources(polytreeA, polytreeB);
|
|
2644
|
+
if (timeoutId) {
|
|
2645
|
+
clearTimeout(timeoutId);
|
|
2646
|
+
}
|
|
2647
|
+
return resolve(result);
|
|
2648
|
+
} catch (error1) {
|
|
2649
|
+
error = error1;
|
|
2650
|
+
if (timeoutId) {
|
|
2651
|
+
clearTimeout(timeoutId);
|
|
2652
|
+
}
|
|
2653
|
+
disposePolytreeResources(polytreeA, polytreeB);
|
|
2654
|
+
return reject(error);
|
|
2655
|
+
}
|
|
2656
|
+
});
|
|
2657
|
+
},
|
|
2658
|
+
// Perform asynchronous subtraction operation between two polytree objects.
|
|
2659
|
+
// Creates a new polytree by removing the volume of polytreeB from polytreeA.
|
|
2660
|
+
// @param polytreeA - The polytree to subtract from (minuend).
|
|
2661
|
+
// @param polytreeB - The polytree to subtract (subtrahend).
|
|
2662
|
+
// @param buildTargetPolytree - Whether to build the target polytree structure (default: true).
|
|
2663
|
+
// @return Promise that resolves to the resulting polytree from the subtraction operation.
|
|
2664
|
+
subtract: function(polytreeA, polytreeB, buildTargetPolytree = true) {
|
|
2665
|
+
return new Promise(function(resolve, reject) {
|
|
2666
|
+
var error, result, timeoutId;
|
|
2667
|
+
timeoutId = null;
|
|
2668
|
+
if (ASYNC_OPERATION_TIMEOUT !== Infinity) {
|
|
2669
|
+
timeoutId = setTimeout(function() {
|
|
2670
|
+
return reject(new Error(`Subtract operation timed out after ${ASYNC_OPERATION_TIMEOUT}ms`));
|
|
2671
|
+
}, ASYNC_OPERATION_TIMEOUT);
|
|
2672
|
+
}
|
|
2673
|
+
try {
|
|
2674
|
+
result = Polytree.subtractCore(polytreeA, polytreeB, buildTargetPolytree);
|
|
2675
|
+
disposePolytreeResources(polytreeA, polytreeB);
|
|
2676
|
+
if (timeoutId) {
|
|
2677
|
+
clearTimeout(timeoutId);
|
|
2678
|
+
}
|
|
2679
|
+
return resolve(result);
|
|
2680
|
+
} catch (error1) {
|
|
2681
|
+
error = error1;
|
|
2682
|
+
if (timeoutId) {
|
|
2683
|
+
clearTimeout(timeoutId);
|
|
2684
|
+
}
|
|
2685
|
+
disposePolytreeResources(polytreeA, polytreeB);
|
|
2686
|
+
return reject(error);
|
|
2687
|
+
}
|
|
2688
|
+
});
|
|
2689
|
+
},
|
|
2690
|
+
// Perform asynchronous intersection operation between two polytree objects.
|
|
2691
|
+
// Creates a new polytree containing only the overlapping volume of both input polytrees.
|
|
2692
|
+
// @param polytreeA - First polytree operand for the intersection operation.
|
|
2693
|
+
// @param polytreeB - Second polytree operand for the intersection operation.
|
|
2694
|
+
// @param buildTargetPolytree - Whether to build the target polytree structure (default: true).
|
|
2695
|
+
// @return Promise that resolves to the resulting polytree from the intersection operation.
|
|
2696
|
+
intersect: function(polytreeA, polytreeB, buildTargetPolytree = true) {
|
|
2697
|
+
return new Promise(function(resolve, reject) {
|
|
2698
|
+
var error, result, timeoutId;
|
|
2699
|
+
timeoutId = null;
|
|
2700
|
+
if (ASYNC_OPERATION_TIMEOUT !== Infinity) {
|
|
2701
|
+
timeoutId = setTimeout(function() {
|
|
2702
|
+
return reject(new Error(`Intersect operation timed out after ${ASYNC_OPERATION_TIMEOUT}ms`));
|
|
2703
|
+
}, ASYNC_OPERATION_TIMEOUT);
|
|
2704
|
+
}
|
|
2705
|
+
try {
|
|
2706
|
+
result = Polytree.intersectCore(polytreeA, polytreeB, buildTargetPolytree);
|
|
2707
|
+
disposePolytreeResources(polytreeA, polytreeB);
|
|
2708
|
+
if (timeoutId) {
|
|
2709
|
+
clearTimeout(timeoutId);
|
|
2710
|
+
}
|
|
2711
|
+
return resolve(result);
|
|
2712
|
+
} catch (error1) {
|
|
2713
|
+
error = error1;
|
|
2714
|
+
if (timeoutId) {
|
|
2715
|
+
clearTimeout(timeoutId);
|
|
2716
|
+
}
|
|
2717
|
+
disposePolytreeResources(polytreeA, polytreeB);
|
|
2718
|
+
return reject(error);
|
|
2719
|
+
}
|
|
2720
|
+
});
|
|
2721
|
+
},
|
|
2722
|
+
// Perform asynchronous union operation on an array of objects.
|
|
2723
|
+
// Efficiently processes large arrays using batching and parallel execution.
|
|
2724
|
+
// This method can handle arrays of meshes or polytrees and will convert them as needed.
|
|
2725
|
+
// @param objectArray - Array of meshes or polytrees to unite.
|
|
2726
|
+
// @param materialIndexMax - Maximum material index for assignment (default: Infinity).
|
|
2727
|
+
// @return Promise that resolves to a single polytree containing the union of all objects.
|
|
2728
|
+
uniteArray: function(objectArray, materialIndexMax = Infinity) {
|
|
2729
|
+
return Polytree.async.processArrayWithOperation(objectArray, materialIndexMax, Polytree.async.unite, Polytree.async.uniteArray, "union");
|
|
2730
|
+
},
|
|
2731
|
+
// Perform asynchronous subtraction operation on an array of objects.
|
|
2732
|
+
// Efficiently processes large arrays using batching and parallel execution.
|
|
2733
|
+
// This method subtracts all subsequent objects from the first object in the array.
|
|
2734
|
+
// @param objectArray - Array of meshes or polytrees to process with subtraction.
|
|
2735
|
+
// @param materialIndexMax - Maximum material index for assignment (default: Infinity).
|
|
2736
|
+
// @return Promise that resolves to a single polytree with all subtractions applied.
|
|
2737
|
+
subtractArray: function(objectArray, materialIndexMax = Infinity) {
|
|
2738
|
+
return Polytree.async.processArrayWithOperation(objectArray, materialIndexMax, Polytree.async.subtract, Polytree.async.subtractArray, "subtraction");
|
|
2739
|
+
},
|
|
2740
|
+
// Perform asynchronous intersection operation on an array of objects.
|
|
2741
|
+
// Efficiently processes large arrays using batching and parallel execution.
|
|
2742
|
+
// This method finds the overlapping volume common to all objects in the array.
|
|
2743
|
+
// @param objectArray - Array of meshes or polytrees to intersect.
|
|
2744
|
+
// @param materialIndexMax - Maximum material index for assignment (default: Infinity).
|
|
2745
|
+
// @return Promise that resolves to a single polytree containing the intersection of all objects.
|
|
2746
|
+
intersectArray: function(objectArray, materialIndexMax = Infinity) {
|
|
2747
|
+
return Polytree.async.processArrayWithOperation(objectArray, materialIndexMax, Polytree.async.intersect, Polytree.async.intersectArray, "intersection");
|
|
2748
|
+
},
|
|
2749
|
+
// Main operation handler that delegates to the synchronous operation method.
|
|
2750
|
+
// This provides a unified interface for complex CSG operations with async support.
|
|
2751
|
+
// @param operationObject - Object containing the operation definition.
|
|
2752
|
+
// @param returnPolytrees - Whether to return polytree objects instead of meshes.
|
|
2753
|
+
// @param buildTargetPolytree - Whether to build the target polytree structure.
|
|
2754
|
+
// @param options - Configuration options including objCounter for unique IDs.
|
|
2755
|
+
// @param firstRun - Whether this is the top-level operation call.
|
|
2756
|
+
// @return Result of the operation (mesh, polytree, or operation tree).
|
|
2757
|
+
operation: function(operationObject, returnPolytrees = false, buildTargetPolytree = true, options = {
|
|
2758
|
+
objCounter: 0
|
|
2759
|
+
}, firstRun = true) {
|
|
2760
|
+
return Polytree.operation(operationObject, returnPolytrees, buildTargetPolytree, options, firstRun, true);
|
|
2761
|
+
},
|
|
2762
|
+
// Generic helper method for processing array operations with any CSG operation.
|
|
2763
|
+
// This reduces code duplication between uniteArray, subtractArray, and intersectArray.
|
|
2764
|
+
// @param objectArray - Array of meshes or polytrees to process.
|
|
2765
|
+
// @param materialIndexMax - Maximum material index for assignment.
|
|
2766
|
+
// @param operationMethod - The async CSG method to use (unite, subtract, or intersect).
|
|
2767
|
+
// @param arrayOperationMethod - The corresponding array method for recursive calls.
|
|
2768
|
+
// @param operationName - Name of the operation for error messages.
|
|
2769
|
+
// @return Promise that resolves to the final result polytree.
|
|
2770
|
+
processArrayWithOperation: function(objectArray, materialIndexMax, operationMethod, arrayOperationMethod, operationName) {
|
|
2771
|
+
return new Promise(function(resolve, reject) {
|
|
2772
|
+
var batchArray, batchPromise, convertedPolytree, currentBatch, currentBatchIndex, error, i, isMainPolytreeUsed, j, leftOverPolytree, leftOverPromise, mainPolytree, materialIndex, objectIndex, operationPromises, pairPromise, pairStartIndex, polytreesArray, ref, ref1, shouldUseBatching;
|
|
2773
|
+
try {
|
|
2774
|
+
shouldUseBatching = Polytree.async.batchSize > 4 && Polytree.async.batchSize < objectArray.length;
|
|
2775
|
+
mainPolytree = void 0;
|
|
2776
|
+
isMainPolytreeUsed = false;
|
|
2777
|
+
operationPromises = [];
|
|
2778
|
+
if (shouldUseBatching) {
|
|
2779
|
+
batchArray = [];
|
|
2780
|
+
currentBatchIndex = 0;
|
|
2781
|
+
while (currentBatchIndex < objectArray.length) {
|
|
2782
|
+
batchArray.push(objectArray.slice(currentBatchIndex, currentBatchIndex + Polytree.async.batchSize));
|
|
2783
|
+
currentBatchIndex += Polytree.async.batchSize;
|
|
2784
|
+
}
|
|
2785
|
+
currentBatch = batchArray.shift();
|
|
2786
|
+
while (currentBatch) {
|
|
2787
|
+
batchPromise = arrayOperationMethod(currentBatch, 0);
|
|
2788
|
+
operationPromises.push(batchPromise);
|
|
2789
|
+
currentBatch = batchArray.shift();
|
|
2790
|
+
}
|
|
2791
|
+
shouldUseBatching = true;
|
|
2792
|
+
isMainPolytreeUsed = true;
|
|
2793
|
+
objectArray.length = 0;
|
|
2794
|
+
} else {
|
|
2795
|
+
polytreesArray = [];
|
|
2796
|
+
for (objectIndex = i = 0, ref = objectArray.length; 0 <= ref ? i < ref : i > ref; objectIndex = 0 <= ref ? ++i : --i) {
|
|
2797
|
+
materialIndex = objectIndex > materialIndexMax ? materialIndexMax : objectIndex;
|
|
2798
|
+
convertedPolytree = void 0;
|
|
2799
|
+
if (objectArray[objectIndex].isMesh) {
|
|
2800
|
+
convertedPolytree = Polytree.fromMesh(objectArray[objectIndex], materialIndexMax > -1 ? materialIndex : void 0);
|
|
2801
|
+
} else {
|
|
2802
|
+
convertedPolytree = objectArray[objectIndex];
|
|
2803
|
+
if (materialIndexMax > -1) {
|
|
2804
|
+
convertedPolytree.setPolygonIndex(materialIndex);
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
convertedPolytree.polytreeIndex = objectIndex;
|
|
2808
|
+
polytreesArray.push(convertedPolytree);
|
|
2809
|
+
}
|
|
2810
|
+
mainPolytree = polytreesArray.shift();
|
|
2811
|
+
leftOverPolytree = void 0;
|
|
2812
|
+
for (pairStartIndex = j = 0, ref1 = polytreesArray.length; j < ref1; pairStartIndex = j += 2) {
|
|
2813
|
+
if (pairStartIndex + 1 >= polytreesArray.length) {
|
|
2814
|
+
leftOverPolytree = polytreesArray[pairStartIndex];
|
|
2815
|
+
break;
|
|
2816
|
+
}
|
|
2817
|
+
pairPromise = operationMethod(polytreesArray[pairStartIndex], polytreesArray[pairStartIndex + 1]);
|
|
2818
|
+
operationPromises.push(pairPromise);
|
|
2819
|
+
}
|
|
2820
|
+
if (leftOverPolytree) {
|
|
2821
|
+
leftOverPromise = operationMethod(mainPolytree, leftOverPolytree);
|
|
2822
|
+
operationPromises.push(leftOverPromise);
|
|
2823
|
+
isMainPolytreeUsed = true;
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
return Promise.allSettled(operationPromises).then(function(promiseResults) {
|
|
2827
|
+
var successfulPolytrees;
|
|
2828
|
+
successfulPolytrees = [];
|
|
2829
|
+
promiseResults.forEach(function(promiseResult) {
|
|
2830
|
+
if (promiseResult.status === "fulfilled") {
|
|
2831
|
+
return successfulPolytrees.push(promiseResult.value);
|
|
2832
|
+
}
|
|
2833
|
+
});
|
|
2834
|
+
if (!isMainPolytreeUsed) {
|
|
2835
|
+
successfulPolytrees.unshift(mainPolytree);
|
|
2836
|
+
}
|
|
2837
|
+
if (successfulPolytrees.length > 0) {
|
|
2838
|
+
if (successfulPolytrees.length === 1) {
|
|
2839
|
+
return resolve(successfulPolytrees[0]);
|
|
2840
|
+
} else if (successfulPolytrees.length > 3) {
|
|
2841
|
+
return arrayOperationMethod(successfulPolytrees, shouldUseBatching ? 0 : -1).then(function(finalResult) {
|
|
2842
|
+
return resolve(finalResult);
|
|
2843
|
+
}).catch(function(error2) {
|
|
2844
|
+
return reject(error2);
|
|
2845
|
+
});
|
|
2846
|
+
} else {
|
|
2847
|
+
return operationMethod(successfulPolytrees[0], successfulPolytrees[1]).then(function(intermediateResult) {
|
|
2848
|
+
if (successfulPolytrees.length === 3) {
|
|
2849
|
+
return operationMethod(intermediateResult, successfulPolytrees[2]).then(function(finalResult) {
|
|
2850
|
+
return resolve(finalResult);
|
|
2851
|
+
}).catch(function(error2) {
|
|
2852
|
+
return reject(error2);
|
|
2853
|
+
});
|
|
2854
|
+
} else {
|
|
2855
|
+
return resolve(intermediateResult);
|
|
2856
|
+
}
|
|
2857
|
+
}).catch(function(error2) {
|
|
2858
|
+
return reject(error2);
|
|
2859
|
+
});
|
|
2860
|
+
}
|
|
2861
|
+
} else {
|
|
2862
|
+
return reject(`Unable to find any result polytree after ${operationName} operation.`);
|
|
2863
|
+
}
|
|
2864
|
+
});
|
|
2865
|
+
} catch (error1) {
|
|
2866
|
+
error = error1;
|
|
2867
|
+
return reject(error);
|
|
2868
|
+
}
|
|
2869
|
+
});
|
|
2870
|
+
}
|
|
2871
|
+
};
|
|
2872
|
+
var Plane;
|
|
2873
|
+
Plane = class Plane2 {
|
|
2874
|
+
// Main constructor for creating plane instances.
|
|
2875
|
+
// Creates a plane defined by the equation: normal·point = distanceFromOrigin
|
|
2876
|
+
// @param normal - Three.js Vector3 representing the plane's normal direction.
|
|
2877
|
+
// @param distanceFromOrigin - Distance from origin along the normal direction.
|
|
2878
|
+
constructor(normal, distanceFromOrigin) {
|
|
2879
|
+
this.normal = normal;
|
|
2880
|
+
this.distanceFromOrigin = distanceFromOrigin;
|
|
2881
|
+
}
|
|
2882
|
+
// === OBJECT CREATION AND COPYING ===
|
|
2883
|
+
clone() {
|
|
2884
|
+
return new Plane2(this.normal.clone(), this.distanceFromOrigin);
|
|
2885
|
+
}
|
|
2886
|
+
// === GEOMETRIC TRANSFORMATIONS ===
|
|
2887
|
+
flip() {
|
|
2888
|
+
this.normal.negate();
|
|
2889
|
+
return this.distanceFromOrigin = -this.distanceFromOrigin;
|
|
2890
|
+
}
|
|
2891
|
+
// === COMPARISON OPERATIONS ===
|
|
2892
|
+
// Test if this plane is identical to another plane.
|
|
2893
|
+
// Compares both normal vector and distance parameter for equality.
|
|
2894
|
+
// @param otherPlane - The other plane to compare against.
|
|
2895
|
+
// @return Boolean indicating whether planes are identical.
|
|
2896
|
+
equals(otherPlane) {
|
|
2897
|
+
return this.normal.equals(otherPlane.normal) && this.distanceFromOrigin === otherPlane.distanceFromOrigin;
|
|
2898
|
+
}
|
|
2899
|
+
// === CLEANUP AND DISPOSAL METHODS ===
|
|
2900
|
+
// Clean up plane data by setting all properties to undefined.
|
|
2901
|
+
// Used for memory management during intensive operations.
|
|
2902
|
+
delete() {
|
|
2903
|
+
this.normal = void 0;
|
|
2904
|
+
return this.distanceFromOrigin = void 0;
|
|
2905
|
+
}
|
|
2906
|
+
// === STATIC FACTORY METHODS ===
|
|
2907
|
+
// Create a plane from three points in 3D space.
|
|
2908
|
+
// Uses cross product to compute normal and dot product for distance.
|
|
2909
|
+
// Points should be ordered counter-clockwise when viewed from the front face.
|
|
2910
|
+
// @param firstPoint - First point (Three.js Vector3).
|
|
2911
|
+
// @param secondPoint - Second point (Three.js Vector3).
|
|
2912
|
+
// @param thirdPoint - Third point (Three.js Vector3).
|
|
2913
|
+
// @return New Plane instance passing through the three points.
|
|
2914
|
+
static fromPoints(firstPoint, secondPoint, thirdPoint) {
|
|
2915
|
+
var planeNormal;
|
|
2916
|
+
planeNormal = temporaryTriangleVertex.copy(secondPoint).sub(firstPoint).cross(temporaryTriangleVertexSecondary.copy(thirdPoint).sub(firstPoint)).normalize().clone();
|
|
2917
|
+
return new Plane2(planeNormal, planeNormal.dot(firstPoint));
|
|
2918
|
+
}
|
|
2919
|
+
};
|
|
2920
|
+
var Polygon;
|
|
2921
|
+
Polygon = class Polygon2 {
|
|
2922
|
+
// Main constructor for creating polygon instances.
|
|
2923
|
+
// Initializes all geometric and state properties for CSG processing.
|
|
2924
|
+
// @param vertices - Array of Vertex instances representing the polygon corners.
|
|
2925
|
+
// @param shared - Material or shared data index for grouping polygons.
|
|
2926
|
+
constructor(vertices, shared) {
|
|
2927
|
+
this.id = polygonID++;
|
|
2928
|
+
this.vertices = vertices.map(function(vertex) {
|
|
2929
|
+
return vertex.clone();
|
|
2930
|
+
});
|
|
2931
|
+
this.shared = shared;
|
|
2932
|
+
this.plane = Plane.fromPoints(this.vertices[0].pos, this.vertices[1].pos, this.vertices[2].pos);
|
|
2933
|
+
this.triangle = new Triangle(this.vertices[0].pos, this.vertices[1].pos, this.vertices[2].pos);
|
|
2934
|
+
this.intersects = false;
|
|
2935
|
+
this.state = "undecided";
|
|
2936
|
+
this.previousState = "undecided";
|
|
2937
|
+
this.previousStates = [];
|
|
2938
|
+
this.valid = true;
|
|
2939
|
+
this.coplanar = false;
|
|
2940
|
+
this.originalValid = false;
|
|
2941
|
+
this.newPolygon = false;
|
|
2942
|
+
}
|
|
2943
|
+
// === SMALL HELPERS AND GETTERS ===
|
|
2944
|
+
// Get the midpoint of the polygon triangle.
|
|
2945
|
+
// Caches the result for performance optimization.
|
|
2946
|
+
// @return Three.js Vector3 representing the triangle midpoint.
|
|
2947
|
+
getMidpoint() {
|
|
2948
|
+
if (this.triangle.midPoint) {
|
|
2949
|
+
return this.triangle.midPoint;
|
|
2950
|
+
} else {
|
|
2951
|
+
return this.triangle.midPoint = this.triangle.getMidpoint(new Vector3());
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
// === GEOMETRIC TRANSFORMATIONS ===
|
|
2955
|
+
// Apply a transformation matrix to the polygon vertices and update derived geometry.
|
|
2956
|
+
// Updates vertex positions, normals, and recalculates plane and triangle data.
|
|
2957
|
+
// @param matrix - Three.js Matrix4 transformation to apply to vertices.
|
|
2958
|
+
// @param normalMatrix - Three.js Matrix3 for transforming normals (auto-calculated if not provided).
|
|
2959
|
+
applyMatrix(matrix, normalMatrix) {
|
|
2960
|
+
normalMatrix = normalMatrix || temporaryMatrixWithNormalCalc.getNormalMatrix(matrix);
|
|
2961
|
+
this.vertices.forEach(function(vertex) {
|
|
2962
|
+
vertex.pos.applyMatrix4(matrix);
|
|
2963
|
+
return vertex.normal.applyMatrix3(normalMatrix);
|
|
2964
|
+
});
|
|
2965
|
+
this.plane.delete();
|
|
2966
|
+
this.plane = Plane.fromPoints(this.vertices[0].pos, this.vertices[1].pos, this.vertices[2].pos);
|
|
2967
|
+
this.triangle.set(this.vertices[0].pos, this.vertices[1].pos, this.vertices[2].pos);
|
|
2968
|
+
if (this.triangle.midPoint) {
|
|
2969
|
+
return this.triangle.getMidpoint(this.triangle.midPoint);
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
// Flip the polygon orientation by reversing vertex order and flipping normals.
|
|
2973
|
+
// Used for changing polygon winding and surface normal direction.
|
|
2974
|
+
flip() {
|
|
2975
|
+
var tmp;
|
|
2976
|
+
this.vertices.reverse().forEach(function(vertex) {
|
|
2977
|
+
return vertex.flip();
|
|
2978
|
+
});
|
|
2979
|
+
tmp = this.triangle.a;
|
|
2980
|
+
this.triangle.a = this.triangle.c;
|
|
2981
|
+
this.triangle.c = tmp;
|
|
2982
|
+
return this.plane.flip();
|
|
2983
|
+
}
|
|
2984
|
+
// === CSG STATE MANAGEMENT ===
|
|
2985
|
+
// Reset polygon state to initial values for fresh CSG operations.
|
|
2986
|
+
// Clears intersection flags and classification states.
|
|
2987
|
+
// @param resetOriginal - Whether to reset the originalValid flag (default: true).
|
|
2988
|
+
reset(resetOriginal = true) {
|
|
2989
|
+
this.intersects = false;
|
|
2990
|
+
this.state = "undecided";
|
|
2991
|
+
this.previousState = "undecided";
|
|
2992
|
+
this.previousStates.length = 0;
|
|
2993
|
+
this.valid = true;
|
|
2994
|
+
this.coplanar = false;
|
|
2995
|
+
resetOriginal && (this.originalValid = false);
|
|
2996
|
+
return this.newPolygon = false;
|
|
2997
|
+
}
|
|
2998
|
+
// Set the classification state of the polygon during CSG operations.
|
|
2999
|
+
// Maintains state history for complex CSG rule evaluation.
|
|
3000
|
+
// @param state - New state to assign ("inside", "outside", "coplanar-front", etc.).
|
|
3001
|
+
// @param keepState - State to preserve (won't change if current state matches this).
|
|
3002
|
+
setState(state, keepState) {
|
|
3003
|
+
if (this.state === keepState) {
|
|
3004
|
+
return;
|
|
3005
|
+
}
|
|
3006
|
+
this.previousState = this.state;
|
|
3007
|
+
this.state !== "undecided" && this.previousStates.push(this.state);
|
|
3008
|
+
return this.state = state;
|
|
3009
|
+
}
|
|
3010
|
+
// Check if polygon has consistently been in the specified state.
|
|
3011
|
+
// Used for CSG rule evaluation requiring state consistency.
|
|
3012
|
+
// @param state - State to check for consistency.
|
|
3013
|
+
// @return Boolean indicating if all states match the specified state.
|
|
3014
|
+
checkAllStates(state) {
|
|
3015
|
+
var i, len, previousState, ref;
|
|
3016
|
+
if (this.state !== state || this.previousState !== state && this.previousState !== "undecided") {
|
|
3017
|
+
return false;
|
|
3018
|
+
}
|
|
3019
|
+
ref = this.previousStates;
|
|
3020
|
+
for (i = 0, len = ref.length; i < len; i++) {
|
|
3021
|
+
previousState = ref[i];
|
|
3022
|
+
if (previousState !== state) {
|
|
3023
|
+
return false;
|
|
3024
|
+
}
|
|
3025
|
+
}
|
|
3026
|
+
return true;
|
|
3027
|
+
}
|
|
3028
|
+
// === VALIDITY MANAGEMENT ===
|
|
3029
|
+
// Mark polygon as invalid (will be excluded from processing).
|
|
3030
|
+
setInvalid() {
|
|
3031
|
+
return this.valid = false;
|
|
3032
|
+
}
|
|
3033
|
+
// Mark polygon as valid (will be included in processing).
|
|
3034
|
+
setValid() {
|
|
3035
|
+
return this.valid = true;
|
|
3036
|
+
}
|
|
3037
|
+
// === OBJECT CREATION AND COPYING ===
|
|
3038
|
+
// Create a polygon with default material index.
|
|
3039
|
+
// Convenience method for creating polygons without specifying material.
|
|
3040
|
+
// @param vertices - Array of Vertex instances representing the polygon corners.
|
|
3041
|
+
// @return New Polygon instance with default material.
|
|
3042
|
+
static createWithDefaultMaterial(vertices) {
|
|
3043
|
+
return new Polygon2(vertices, DEFAULT_MATERIAL_INDEX);
|
|
3044
|
+
}
|
|
3045
|
+
// Create a deep copy of this polygon with all properties.
|
|
3046
|
+
// Preserves all state information and geometric data.
|
|
3047
|
+
// @return New Polygon instance with copied data.
|
|
3048
|
+
clone() {
|
|
3049
|
+
var polygon;
|
|
3050
|
+
polygon = new Polygon2(this.vertices.map(function(vertex) {
|
|
3051
|
+
return vertex.clone();
|
|
3052
|
+
}), this.shared);
|
|
3053
|
+
polygon.intersects = this.intersects;
|
|
3054
|
+
polygon.valid = this.valid;
|
|
3055
|
+
polygon.coplanar = this.coplanar;
|
|
3056
|
+
polygon.state = this.state;
|
|
3057
|
+
polygon.originalValid = this.originalValid;
|
|
3058
|
+
polygon.newPolygon = this.newPolygon;
|
|
3059
|
+
polygon.previousState = this.previousState;
|
|
3060
|
+
polygon.previousStates = this.previousStates.slice();
|
|
3061
|
+
if (this.triangle.midPoint) {
|
|
3062
|
+
polygon.triangle.midPoint = this.triangle.midPoint.clone();
|
|
3063
|
+
}
|
|
3064
|
+
return polygon;
|
|
3065
|
+
}
|
|
3066
|
+
// === CLEANUP AND DISPOSAL METHODS ===
|
|
3067
|
+
// Clean up polygon data and free memory.
|
|
3068
|
+
// Deletes vertices, geometric objects, and marks polygon as invalid.
|
|
3069
|
+
delete() {
|
|
3070
|
+
this.vertices.forEach(function(vertex) {
|
|
3071
|
+
return vertex.delete();
|
|
3072
|
+
});
|
|
3073
|
+
this.vertices.length = 0;
|
|
3074
|
+
if (this.plane) {
|
|
3075
|
+
this.plane.delete();
|
|
3076
|
+
this.plane = void 0;
|
|
3077
|
+
}
|
|
3078
|
+
this.triangle = void 0;
|
|
3079
|
+
this.shared = void 0;
|
|
3080
|
+
return this.setInvalid();
|
|
3081
|
+
}
|
|
3082
|
+
};
|
|
3083
|
+
var constructIntersection;
|
|
3084
|
+
var intersectionTestEdge2D;
|
|
3085
|
+
var intersectionTestVertex2D;
|
|
3086
|
+
var isUniqueTriangle;
|
|
3087
|
+
var isValidTriangle;
|
|
3088
|
+
var pointInTriangleInclusive2D;
|
|
3089
|
+
var pointOnSegmentInclusive2D;
|
|
3090
|
+
var pointsEqual2D;
|
|
3091
|
+
var resolveCoplanarTriangleIntersection;
|
|
3092
|
+
var resolveTriangleIntersection;
|
|
3093
|
+
var triangleIntersectionCCW2D;
|
|
3094
|
+
var triangleIntersectsTriangle;
|
|
3095
|
+
var triangleOrientation2D;
|
|
3096
|
+
var trianglesOverlap2D;
|
|
3097
|
+
isValidTriangle = function(triangle) {
|
|
3098
|
+
if (triangle.a.equals(triangle.b)) {
|
|
3099
|
+
return false;
|
|
3100
|
+
}
|
|
3101
|
+
if (triangle.a.equals(triangle.c)) {
|
|
3102
|
+
return false;
|
|
3103
|
+
}
|
|
3104
|
+
if (triangle.b.equals(triangle.c)) {
|
|
3105
|
+
return false;
|
|
3106
|
+
}
|
|
3107
|
+
return true;
|
|
3108
|
+
};
|
|
3109
|
+
isUniqueTriangle = function(triangle, set, map) {
|
|
3110
|
+
var hash1;
|
|
3111
|
+
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}}`;
|
|
3112
|
+
if (set.has(hash1) === true) {
|
|
3113
|
+
return false;
|
|
3114
|
+
} else {
|
|
3115
|
+
set.add(hash1);
|
|
3116
|
+
if (map) {
|
|
3117
|
+
map.set(triangle, triangle);
|
|
3118
|
+
}
|
|
3119
|
+
return true;
|
|
3120
|
+
}
|
|
3121
|
+
};
|
|
3122
|
+
pointsEqual2D = function(p, q, eps = TRIANGLE_2D_EPSILON) {
|
|
3123
|
+
return Math.abs(p.x - q.x) <= eps && Math.abs(p.y - q.y) <= eps;
|
|
3124
|
+
};
|
|
3125
|
+
pointOnSegmentInclusive2D = function(p, a, b, eps = TRIANGLE_2D_EPSILON) {
|
|
3126
|
+
var maxX, maxY, minX, minY;
|
|
3127
|
+
if (pointsEqual2D(a, b, eps)) {
|
|
3128
|
+
return pointsEqual2D(p, a, eps) || pointsEqual2D(p, b, eps);
|
|
3129
|
+
}
|
|
3130
|
+
if (Math.abs(triangleOrientation2D(p, a, b)) > eps) {
|
|
3131
|
+
return false;
|
|
3132
|
+
}
|
|
3133
|
+
minX = Math.min(a.x, b.x) - eps;
|
|
3134
|
+
maxX = Math.max(a.x, b.x) + eps;
|
|
3135
|
+
minY = Math.min(a.y, b.y) - eps;
|
|
3136
|
+
maxY = Math.max(a.y, b.y) + eps;
|
|
3137
|
+
return p.x >= minX && p.x <= maxX && p.y >= minY && p.y <= maxY;
|
|
3138
|
+
};
|
|
3139
|
+
pointInTriangleInclusive2D = function(p, a, b, c, eps = TRIANGLE_2D_EPSILON) {
|
|
3140
|
+
var o, s1, s2, s3;
|
|
3141
|
+
o = triangleOrientation2D(a, b, c);
|
|
3142
|
+
if (o > eps) {
|
|
3143
|
+
s1 = triangleOrientation2D(p, a, b);
|
|
3144
|
+
s2 = triangleOrientation2D(p, b, c);
|
|
3145
|
+
s3 = triangleOrientation2D(p, c, a);
|
|
3146
|
+
return s1 >= -eps && s2 >= -eps && s3 >= -eps;
|
|
3147
|
+
} else if (o < -eps) {
|
|
3148
|
+
s1 = triangleOrientation2D(p, a, b);
|
|
3149
|
+
s2 = triangleOrientation2D(p, b, c);
|
|
3150
|
+
s3 = triangleOrientation2D(p, c, a);
|
|
3151
|
+
return s1 <= eps && s2 <= eps && s3 <= eps;
|
|
3152
|
+
} else {
|
|
3153
|
+
if (pointsEqual2D(a, b, eps) && pointsEqual2D(b, c, eps)) {
|
|
3154
|
+
return pointsEqual2D(p, a, eps);
|
|
3155
|
+
}
|
|
3156
|
+
if (pointsEqual2D(a, b, eps)) {
|
|
3157
|
+
return pointOnSegmentInclusive2D(p, b, c, eps);
|
|
3158
|
+
}
|
|
3159
|
+
if (pointsEqual2D(b, c, eps)) {
|
|
3160
|
+
return pointOnSegmentInclusive2D(p, a, b, eps);
|
|
3161
|
+
}
|
|
3162
|
+
if (pointsEqual2D(c, a, eps)) {
|
|
3163
|
+
return pointOnSegmentInclusive2D(p, a, b, eps);
|
|
3164
|
+
}
|
|
3165
|
+
return pointOnSegmentInclusive2D(p, a, b, eps) || pointOnSegmentInclusive2D(p, b, c, eps) || pointOnSegmentInclusive2D(p, c, a, eps);
|
|
3166
|
+
}
|
|
3167
|
+
};
|
|
3168
|
+
triangleIntersectsTriangle = function(triangleA, triangleB, additions = {
|
|
3169
|
+
coplanar: false,
|
|
3170
|
+
source: new Vector3(),
|
|
3171
|
+
target: new Vector3()
|
|
3172
|
+
}) {
|
|
3173
|
+
var distanceVertex1A, distanceVertex1B, distanceVertex2A, distanceVertex2B, distanceVertex3A, distanceVertex3B, normal1, normal2, vertex1TriangleA, vertex1TriangleB, vertex2TriangleA, vertex2TriangleB, vertex3TriangleA, vertex3TriangleB;
|
|
3174
|
+
vertex1TriangleA = triangleA.a;
|
|
3175
|
+
vertex2TriangleA = triangleA.b;
|
|
3176
|
+
vertex3TriangleA = triangleA.c;
|
|
3177
|
+
vertex1TriangleB = triangleB.a;
|
|
3178
|
+
vertex2TriangleB = triangleB.b;
|
|
3179
|
+
vertex3TriangleB = triangleB.c;
|
|
3180
|
+
temporaryVector3Primary.copy(vertex1TriangleB).sub(vertex3TriangleB);
|
|
3181
|
+
temporaryVector3Secondary.copy(vertex2TriangleB).sub(vertex3TriangleB);
|
|
3182
|
+
normal2 = new Vector3().copy(temporaryVector3Primary).cross(temporaryVector3Secondary);
|
|
3183
|
+
temporaryVector3Primary.copy(vertex1TriangleA).sub(vertex3TriangleB);
|
|
3184
|
+
distanceVertex1A = temporaryVector3Primary.dot(normal2);
|
|
3185
|
+
temporaryVector3Primary.copy(vertex2TriangleA).sub(vertex3TriangleB);
|
|
3186
|
+
distanceVertex2A = temporaryVector3Primary.dot(normal2);
|
|
3187
|
+
temporaryVector3Primary.copy(vertex3TriangleA).sub(vertex3TriangleB);
|
|
3188
|
+
distanceVertex3A = temporaryVector3Primary.dot(normal2);
|
|
3189
|
+
if (distanceVertex1A * distanceVertex2A > 0 && distanceVertex1A * distanceVertex3A > 0) {
|
|
3190
|
+
return false;
|
|
3191
|
+
}
|
|
3192
|
+
temporaryVector3Primary.copy(vertex2TriangleA).sub(vertex1TriangleA);
|
|
3193
|
+
temporaryVector3Secondary.copy(vertex3TriangleA).sub(vertex1TriangleA);
|
|
3194
|
+
normal1 = new Vector3().copy(temporaryVector3Primary).cross(temporaryVector3Secondary);
|
|
3195
|
+
temporaryVector3Primary.copy(vertex1TriangleB).sub(vertex3TriangleA);
|
|
3196
|
+
distanceVertex1B = temporaryVector3Primary.dot(normal1);
|
|
3197
|
+
temporaryVector3Primary.copy(vertex2TriangleB).sub(vertex3TriangleA);
|
|
3198
|
+
distanceVertex2B = temporaryVector3Primary.dot(normal1);
|
|
3199
|
+
temporaryVector3Primary.copy(vertex3TriangleB).sub(vertex3TriangleA);
|
|
3200
|
+
distanceVertex3B = temporaryVector3Primary.dot(normal1);
|
|
3201
|
+
if (distanceVertex1B * distanceVertex2B > 0 && distanceVertex1B * distanceVertex3B > 0) {
|
|
3202
|
+
return false;
|
|
3203
|
+
}
|
|
3204
|
+
additions.normal1 = normal1;
|
|
3205
|
+
additions.normal2 = normal2;
|
|
3206
|
+
if (distanceVertex1A > 0) {
|
|
3207
|
+
if (distanceVertex2A > 0) {
|
|
3208
|
+
return resolveTriangleIntersection(vertex3TriangleA, vertex1TriangleA, vertex2TriangleA, vertex1TriangleB, vertex3TriangleB, vertex2TriangleB, distanceVertex1B, distanceVertex3B, distanceVertex2B, additions);
|
|
3209
|
+
} else if (distanceVertex3A > 0) {
|
|
3210
|
+
return resolveTriangleIntersection(vertex2TriangleA, vertex3TriangleA, vertex1TriangleA, vertex1TriangleB, vertex3TriangleB, vertex2TriangleB, distanceVertex1B, distanceVertex3B, distanceVertex2B, additions);
|
|
3211
|
+
} else {
|
|
3212
|
+
return resolveTriangleIntersection(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, distanceVertex1B, distanceVertex2B, distanceVertex3B, additions);
|
|
3213
|
+
}
|
|
3214
|
+
} else if (distanceVertex1A < 0) {
|
|
3215
|
+
if (distanceVertex2A < 0) {
|
|
3216
|
+
return resolveTriangleIntersection(vertex3TriangleA, vertex1TriangleA, vertex2TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, distanceVertex1B, distanceVertex2B, distanceVertex3B, additions);
|
|
3217
|
+
} else if (distanceVertex3A < 0) {
|
|
3218
|
+
return resolveTriangleIntersection(vertex2TriangleA, vertex3TriangleA, vertex1TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, distanceVertex1B, distanceVertex2B, distanceVertex3B, additions);
|
|
3219
|
+
} else {
|
|
3220
|
+
return resolveTriangleIntersection(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex3TriangleB, vertex2TriangleB, distanceVertex1B, distanceVertex3B, distanceVertex2B, additions);
|
|
3221
|
+
}
|
|
3222
|
+
} else {
|
|
3223
|
+
if (distanceVertex2A < 0) {
|
|
3224
|
+
if (distanceVertex3A >= 0) {
|
|
3225
|
+
return resolveTriangleIntersection(vertex2TriangleA, vertex3TriangleA, vertex1TriangleA, vertex1TriangleB, vertex3TriangleB, vertex2TriangleB, distanceVertex1B, distanceVertex3B, distanceVertex2B, additions);
|
|
3226
|
+
} else {
|
|
3227
|
+
return resolveTriangleIntersection(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, distanceVertex1B, distanceVertex2B, distanceVertex3B, additions);
|
|
3228
|
+
}
|
|
3229
|
+
} else if (distanceVertex2A > 0) {
|
|
3230
|
+
if (distanceVertex3A > 0) {
|
|
3231
|
+
return resolveTriangleIntersection(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex3TriangleB, vertex2TriangleB, distanceVertex1B, distanceVertex3B, distanceVertex2B, additions);
|
|
3232
|
+
} else {
|
|
3233
|
+
return resolveTriangleIntersection(vertex2TriangleA, vertex3TriangleA, vertex1TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, distanceVertex1B, distanceVertex2B, distanceVertex3B, additions);
|
|
3234
|
+
}
|
|
3235
|
+
} else {
|
|
3236
|
+
if (distanceVertex3A > 0) {
|
|
3237
|
+
return resolveTriangleIntersection(vertex3TriangleA, vertex1TriangleA, vertex2TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, distanceVertex1B, distanceVertex2B, distanceVertex3B, additions);
|
|
3238
|
+
} else if (distanceVertex3A < 0) {
|
|
3239
|
+
return resolveTriangleIntersection(vertex3TriangleA, vertex1TriangleA, vertex2TriangleA, vertex1TriangleB, vertex3TriangleB, vertex2TriangleB, distanceVertex1B, distanceVertex3B, distanceVertex2B, additions);
|
|
3240
|
+
} else {
|
|
3241
|
+
additions.coplanar = true;
|
|
3242
|
+
return resolveCoplanarTriangleIntersection(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, normal1, normal2);
|
|
3243
|
+
}
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3246
|
+
};
|
|
3247
|
+
resolveTriangleIntersection = function(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, distanceVertex1B, distanceVertex2B, distanceVertex3B, additions) {
|
|
3248
|
+
if (distanceVertex1B > 0 && distanceVertex2B > 0 && distanceVertex3B > 0 || distanceVertex1B < 0 && distanceVertex2B < 0 && distanceVertex3B < 0) {
|
|
3249
|
+
return false;
|
|
3250
|
+
}
|
|
3251
|
+
if (distanceVertex1B > 0) {
|
|
3252
|
+
if (distanceVertex2B > 0) {
|
|
3253
|
+
return constructIntersection(vertex1TriangleA, vertex3TriangleA, vertex2TriangleA, vertex3TriangleB, vertex1TriangleB, vertex2TriangleB, additions);
|
|
3254
|
+
} else if (distanceVertex3B > 0) {
|
|
3255
|
+
return constructIntersection(vertex1TriangleA, vertex3TriangleA, vertex2TriangleA, vertex2TriangleB, vertex3TriangleB, vertex1TriangleB, additions);
|
|
3256
|
+
} else {
|
|
3257
|
+
return constructIntersection(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, additions);
|
|
3258
|
+
}
|
|
3259
|
+
} else if (distanceVertex1B < 0) {
|
|
3260
|
+
if (distanceVertex2B < 0) {
|
|
3261
|
+
return constructIntersection(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex3TriangleB, vertex1TriangleB, vertex2TriangleB, additions);
|
|
3262
|
+
} else if (distanceVertex3B < 0) {
|
|
3263
|
+
return constructIntersection(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex2TriangleB, vertex3TriangleB, vertex1TriangleB, additions);
|
|
3264
|
+
} else {
|
|
3265
|
+
return constructIntersection(vertex1TriangleA, vertex3TriangleA, vertex2TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, additions);
|
|
3266
|
+
}
|
|
3267
|
+
} else {
|
|
3268
|
+
if (distanceVertex2B < 0) {
|
|
3269
|
+
if (distanceVertex3B >= 0) {
|
|
3270
|
+
return constructIntersection(vertex1TriangleA, vertex3TriangleA, vertex2TriangleA, vertex2TriangleB, vertex3TriangleB, vertex1TriangleB, additions);
|
|
3271
|
+
} else {
|
|
3272
|
+
return constructIntersection(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, additions);
|
|
3273
|
+
}
|
|
3274
|
+
} else if (distanceVertex2B > 0) {
|
|
3275
|
+
if (distanceVertex3B > 0) {
|
|
3276
|
+
return constructIntersection(vertex1TriangleA, vertex3TriangleA, vertex2TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, additions);
|
|
3277
|
+
} else {
|
|
3278
|
+
return constructIntersection(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex2TriangleB, vertex3TriangleB, vertex1TriangleB, additions);
|
|
3279
|
+
}
|
|
3280
|
+
} else {
|
|
3281
|
+
if (distanceVertex3B > 0) {
|
|
3282
|
+
return constructIntersection(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex3TriangleB, vertex1TriangleB, vertex2TriangleB, additions);
|
|
3283
|
+
} else if (distanceVertex3B < 0) {
|
|
3284
|
+
return constructIntersection(vertex1TriangleA, vertex3TriangleA, vertex2TriangleA, vertex3TriangleB, vertex1TriangleB, vertex2TriangleB, additions);
|
|
3285
|
+
} else {
|
|
3286
|
+
additions.coplanar = true;
|
|
3287
|
+
return resolveCoplanarTriangleIntersection(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, additions.normal1, additions.normal2);
|
|
3288
|
+
}
|
|
3289
|
+
}
|
|
3290
|
+
}
|
|
3291
|
+
};
|
|
3292
|
+
resolveCoplanarTriangleIntersection = function(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, normalTriangleA, normalTriangleB) {
|
|
3293
|
+
var normalAbsX, normalAbsY, normalAbsZ, vertex1TriangleA2D, vertex1TriangleB2D, vertex2TriangleA2D, vertex2TriangleB2D, vertex3TriangleA2D, vertex3TriangleB2D;
|
|
3294
|
+
vertex1TriangleA2D = new Vector2();
|
|
3295
|
+
vertex2TriangleA2D = new Vector2();
|
|
3296
|
+
vertex3TriangleA2D = new Vector2();
|
|
3297
|
+
vertex1TriangleB2D = new Vector2();
|
|
3298
|
+
vertex2TriangleB2D = new Vector2();
|
|
3299
|
+
vertex3TriangleB2D = new Vector2();
|
|
3300
|
+
normalAbsX = Math.abs(normalTriangleA.x);
|
|
3301
|
+
normalAbsY = Math.abs(normalTriangleA.y);
|
|
3302
|
+
normalAbsZ = Math.abs(normalTriangleA.z);
|
|
3303
|
+
if (normalAbsX > normalAbsZ && normalAbsX >= normalAbsY) {
|
|
3304
|
+
vertex1TriangleA2D.set(vertex1TriangleA.y, vertex1TriangleA.z);
|
|
3305
|
+
vertex2TriangleA2D.set(vertex2TriangleA.y, vertex2TriangleA.z);
|
|
3306
|
+
vertex3TriangleA2D.set(vertex3TriangleA.y, vertex3TriangleA.z);
|
|
3307
|
+
vertex1TriangleB2D.set(vertex1TriangleB.y, vertex1TriangleB.z);
|
|
3308
|
+
vertex2TriangleB2D.set(vertex2TriangleB.y, vertex2TriangleB.z);
|
|
3309
|
+
vertex3TriangleB2D.set(vertex3TriangleB.y, vertex3TriangleB.z);
|
|
3310
|
+
} else if (normalAbsY > normalAbsZ && normalAbsY >= normalAbsX) {
|
|
3311
|
+
vertex1TriangleA2D.set(vertex1TriangleA.x, vertex1TriangleA.z);
|
|
3312
|
+
vertex2TriangleA2D.set(vertex2TriangleA.x, vertex2TriangleA.z);
|
|
3313
|
+
vertex3TriangleA2D.set(vertex3TriangleA.x, vertex3TriangleA.z);
|
|
3314
|
+
vertex1TriangleB2D.set(vertex1TriangleB.x, vertex1TriangleB.z);
|
|
3315
|
+
vertex2TriangleB2D.set(vertex2TriangleB.x, vertex2TriangleB.z);
|
|
3316
|
+
vertex3TriangleB2D.set(vertex3TriangleB.x, vertex3TriangleB.z);
|
|
3317
|
+
} else {
|
|
3318
|
+
vertex1TriangleA2D.set(vertex1TriangleA.x, vertex1TriangleA.y);
|
|
3319
|
+
vertex2TriangleA2D.set(vertex2TriangleA.x, vertex2TriangleA.y);
|
|
3320
|
+
vertex3TriangleA2D.set(vertex3TriangleA.x, vertex3TriangleA.y);
|
|
3321
|
+
vertex1TriangleB2D.set(vertex1TriangleB.x, vertex1TriangleB.y);
|
|
3322
|
+
vertex2TriangleB2D.set(vertex2TriangleB.x, vertex2TriangleB.y);
|
|
3323
|
+
vertex3TriangleB2D.set(vertex3TriangleB.x, vertex3TriangleB.y);
|
|
3324
|
+
}
|
|
3325
|
+
return trianglesOverlap2D(vertex1TriangleA2D, vertex2TriangleA2D, vertex3TriangleA2D, vertex1TriangleB2D, vertex2TriangleB2D, vertex3TriangleB2D);
|
|
3326
|
+
};
|
|
3327
|
+
trianglesOverlap2D = function(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB) {
|
|
3328
|
+
var aE, aMax, aMin, aS, bE, bMax, bMin, bS, extractSegment, isLineDegenerate, maxAx, maxAy, maxBx, maxBy, minAx, minAy, minBx, minBy, spanAx, spanAy;
|
|
3329
|
+
if (pointsEqual2D(vertex1TriangleB, vertex2TriangleB, TRIANGLE_2D_EPSILON) && pointsEqual2D(vertex2TriangleB, vertex3TriangleB, TRIANGLE_2D_EPSILON)) {
|
|
3330
|
+
return pointInTriangleInclusive2D(vertex1TriangleB, vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, TRIANGLE_2D_EPSILON);
|
|
3331
|
+
}
|
|
3332
|
+
if (pointsEqual2D(vertex1TriangleA, vertex2TriangleA, TRIANGLE_2D_EPSILON) && pointsEqual2D(vertex2TriangleA, vertex3TriangleA, TRIANGLE_2D_EPSILON)) {
|
|
3333
|
+
return pointInTriangleInclusive2D(vertex1TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, TRIANGLE_2D_EPSILON);
|
|
3334
|
+
}
|
|
3335
|
+
isLineDegenerate = function(v1, v2, v3) {
|
|
3336
|
+
return pointsEqual2D(v1, v2, TRIANGLE_2D_EPSILON) && !pointsEqual2D(v2, v3, TRIANGLE_2D_EPSILON) || pointsEqual2D(v2, v3, TRIANGLE_2D_EPSILON) && !pointsEqual2D(v1, v2, TRIANGLE_2D_EPSILON) || pointsEqual2D(v3, v1, TRIANGLE_2D_EPSILON) && !pointsEqual2D(v1, v2, TRIANGLE_2D_EPSILON);
|
|
3337
|
+
};
|
|
3338
|
+
extractSegment = function(v1, v2, v3) {
|
|
3339
|
+
if (pointsEqual2D(v1, v2, TRIANGLE_2D_EPSILON)) {
|
|
3340
|
+
return [v2, v3];
|
|
3341
|
+
} else if (pointsEqual2D(v2, v3, TRIANGLE_2D_EPSILON)) {
|
|
3342
|
+
return [v1, v2];
|
|
3343
|
+
} else {
|
|
3344
|
+
return [
|
|
3345
|
+
v1,
|
|
3346
|
+
v2
|
|
3347
|
+
// fallback (should not reach if collinear case handled earlier)
|
|
3348
|
+
];
|
|
3349
|
+
}
|
|
3350
|
+
};
|
|
3351
|
+
if (isLineDegenerate(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA) && isLineDegenerate(vertex1TriangleB, vertex2TriangleB, vertex3TriangleB)) {
|
|
3352
|
+
[aS, aE] = extractSegment(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA);
|
|
3353
|
+
[bS, bE] = extractSegment(vertex1TriangleB, vertex2TriangleB, vertex3TriangleB);
|
|
3354
|
+
minAx = Math.min(aS.x, aE.x);
|
|
3355
|
+
maxAx = Math.max(aS.x, aE.x);
|
|
3356
|
+
minAy = Math.min(aS.y, aE.y);
|
|
3357
|
+
maxAy = Math.max(aS.y, aE.y);
|
|
3358
|
+
minBx = Math.min(bS.x, bE.x);
|
|
3359
|
+
maxBx = Math.max(bS.x, bE.x);
|
|
3360
|
+
minBy = Math.min(bS.y, bE.y);
|
|
3361
|
+
maxBy = Math.max(bS.y, bE.y);
|
|
3362
|
+
if (maxAx < minBx - TRIANGLE_2D_EPSILON || maxBx < minAx - TRIANGLE_2D_EPSILON || maxAy < minBy - TRIANGLE_2D_EPSILON || maxBy < minAy - TRIANGLE_2D_EPSILON) {
|
|
3363
|
+
return false;
|
|
3364
|
+
}
|
|
3365
|
+
if (Math.abs(triangleOrientation2D(aS, aE, bS)) > TRIANGLE_2D_EPSILON) {
|
|
3366
|
+
return false;
|
|
3367
|
+
}
|
|
3368
|
+
spanAx = Math.abs(aE.x - aS.x);
|
|
3369
|
+
spanAy = Math.abs(aE.y - aS.y);
|
|
3370
|
+
if (spanAx >= spanAy) {
|
|
3371
|
+
aMin = Math.min(aS.x, aE.x) - TRIANGLE_2D_EPSILON;
|
|
3372
|
+
aMax = Math.max(aS.x, aE.x) + TRIANGLE_2D_EPSILON;
|
|
3373
|
+
bMin = Math.min(bS.x, bE.x) - TRIANGLE_2D_EPSILON;
|
|
3374
|
+
bMax = Math.max(bS.x, bE.x) + TRIANGLE_2D_EPSILON;
|
|
3375
|
+
return !(aMax < bMin || bMax < aMin);
|
|
3376
|
+
} else {
|
|
3377
|
+
aMin = Math.min(aS.y, aE.y) - TRIANGLE_2D_EPSILON;
|
|
3378
|
+
aMax = Math.max(aS.y, aE.y) + TRIANGLE_2D_EPSILON;
|
|
3379
|
+
bMin = Math.min(bS.y, bE.y) - TRIANGLE_2D_EPSILON;
|
|
3380
|
+
bMax = Math.max(bS.y, bE.y) + TRIANGLE_2D_EPSILON;
|
|
3381
|
+
return !(aMax < bMin || bMax < aMin);
|
|
3382
|
+
}
|
|
3383
|
+
}
|
|
3384
|
+
if (triangleOrientation2D(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA) < 0) {
|
|
3385
|
+
if (triangleOrientation2D(vertex1TriangleB, vertex2TriangleB, vertex3TriangleB) < 0) {
|
|
3386
|
+
return triangleIntersectionCCW2D(vertex1TriangleA, vertex3TriangleA, vertex2TriangleA, vertex1TriangleB, vertex3TriangleB, vertex2TriangleB);
|
|
3387
|
+
} else {
|
|
3388
|
+
return triangleIntersectionCCW2D(vertex1TriangleA, vertex3TriangleA, vertex2TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB);
|
|
3389
|
+
}
|
|
3390
|
+
} else {
|
|
3391
|
+
if (triangleOrientation2D(vertex1TriangleB, vertex2TriangleB, vertex3TriangleB) < 0) {
|
|
3392
|
+
return triangleIntersectionCCW2D(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex3TriangleB, vertex2TriangleB);
|
|
3393
|
+
} else {
|
|
3394
|
+
return triangleIntersectionCCW2D(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB);
|
|
3395
|
+
}
|
|
3396
|
+
}
|
|
3397
|
+
};
|
|
3398
|
+
triangleOrientation2D = function(a, b, c) {
|
|
3399
|
+
return (a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x);
|
|
3400
|
+
};
|
|
3401
|
+
triangleIntersectionCCW2D = function(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB) {
|
|
3402
|
+
if (triangleOrientation2D(vertex1TriangleB, vertex2TriangleB, vertex1TriangleA) >= 0) {
|
|
3403
|
+
if (triangleOrientation2D(vertex2TriangleB, vertex3TriangleB, vertex1TriangleA) >= 0) {
|
|
3404
|
+
if (triangleOrientation2D(vertex3TriangleB, vertex1TriangleB, vertex1TriangleA) >= 0) {
|
|
3405
|
+
return true;
|
|
3406
|
+
} else {
|
|
3407
|
+
return intersectionTestEdge2D(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB);
|
|
3408
|
+
}
|
|
3409
|
+
} else {
|
|
3410
|
+
if (triangleOrientation2D(vertex3TriangleB, vertex1TriangleB, vertex1TriangleA) >= 0) {
|
|
3411
|
+
return intersectionTestEdge2D(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex3TriangleB, vertex1TriangleB, vertex2TriangleB);
|
|
3412
|
+
} else {
|
|
3413
|
+
return intersectionTestVertex2D(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB);
|
|
3414
|
+
}
|
|
3415
|
+
}
|
|
3416
|
+
} else {
|
|
3417
|
+
if (triangleOrientation2D(vertex2TriangleB, vertex3TriangleB, vertex1TriangleA) >= 0) {
|
|
3418
|
+
if (triangleOrientation2D(vertex3TriangleB, vertex1TriangleB, vertex1TriangleA) >= 0) {
|
|
3419
|
+
return intersectionTestEdge2D(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex2TriangleB, vertex3TriangleB, vertex1TriangleB);
|
|
3420
|
+
} else {
|
|
3421
|
+
return intersectionTestVertex2D(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex2TriangleB, vertex3TriangleB, vertex1TriangleB);
|
|
3422
|
+
}
|
|
3423
|
+
} else {
|
|
3424
|
+
return intersectionTestVertex2D(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex3TriangleB, vertex1TriangleB, vertex2TriangleB);
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
};
|
|
3428
|
+
intersectionTestEdge2D = function(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB) {
|
|
3429
|
+
var a1, a2, b1, b2, edgesA, edgesB, i, j, len, len1, segmentIntersectsInclusive;
|
|
3430
|
+
segmentIntersectsInclusive = function(a12, a22, b12, b22) {
|
|
3431
|
+
var maxAx, maxAy, maxBx, maxBy, minAx, minAy, minBx, minBy, o1, o2, o3, o4;
|
|
3432
|
+
minAx = Math.min(a12.x, a22.x);
|
|
3433
|
+
maxAx = Math.max(a12.x, a22.x);
|
|
3434
|
+
minAy = Math.min(a12.y, a22.y);
|
|
3435
|
+
maxAy = Math.max(a12.y, a22.y);
|
|
3436
|
+
minBx = Math.min(b12.x, b22.x);
|
|
3437
|
+
maxBx = Math.max(b12.x, b22.x);
|
|
3438
|
+
minBy = Math.min(b12.y, b22.y);
|
|
3439
|
+
maxBy = Math.max(b12.y, b22.y);
|
|
3440
|
+
if (maxAx < minBx || maxBx < minAx || maxAy < minBy || maxBy < minAy) {
|
|
3441
|
+
return false;
|
|
3442
|
+
}
|
|
3443
|
+
o1 = triangleOrientation2D(a12, a22, b12);
|
|
3444
|
+
o2 = triangleOrientation2D(a12, a22, b22);
|
|
3445
|
+
o3 = triangleOrientation2D(b12, b22, a12);
|
|
3446
|
+
o4 = triangleOrientation2D(b12, b22, a22);
|
|
3447
|
+
if ((o1 > 0 && o2 < 0 || o1 < 0 && o2 > 0) && (o3 > 0 && o4 < 0 || o3 < 0 && o4 > 0)) {
|
|
3448
|
+
return true;
|
|
3449
|
+
}
|
|
3450
|
+
if (Math.abs(o1) <= TRIANGLE_2D_EPSILON && pointOnSegmentInclusive2D(b12, a12, a22, TRIANGLE_2D_EPSILON)) {
|
|
3451
|
+
return true;
|
|
3452
|
+
}
|
|
3453
|
+
if (Math.abs(o2) <= TRIANGLE_2D_EPSILON && pointOnSegmentInclusive2D(b22, a12, a22, TRIANGLE_2D_EPSILON)) {
|
|
3454
|
+
return true;
|
|
3455
|
+
}
|
|
3456
|
+
if (Math.abs(o3) <= TRIANGLE_2D_EPSILON && pointOnSegmentInclusive2D(a12, b12, b22, TRIANGLE_2D_EPSILON)) {
|
|
3457
|
+
return true;
|
|
3458
|
+
}
|
|
3459
|
+
if (Math.abs(o4) <= TRIANGLE_2D_EPSILON && pointOnSegmentInclusive2D(a22, b12, b22, TRIANGLE_2D_EPSILON)) {
|
|
3460
|
+
return true;
|
|
3461
|
+
}
|
|
3462
|
+
return false;
|
|
3463
|
+
};
|
|
3464
|
+
edgesA = [[vertex1TriangleA, vertex2TriangleA], [vertex2TriangleA, vertex3TriangleA], [vertex3TriangleA, vertex1TriangleA]];
|
|
3465
|
+
edgesB = [[vertex1TriangleB, vertex2TriangleB], [vertex2TriangleB, vertex3TriangleB], [vertex3TriangleB, vertex1TriangleB]];
|
|
3466
|
+
for (i = 0, len = edgesA.length; i < len; i++) {
|
|
3467
|
+
[a1, a2] = edgesA[i];
|
|
3468
|
+
for (j = 0, len1 = edgesB.length; j < len1; j++) {
|
|
3469
|
+
[b1, b2] = edgesB[j];
|
|
3470
|
+
if (segmentIntersectsInclusive(a1, a2, b1, b2)) {
|
|
3471
|
+
return true;
|
|
3472
|
+
}
|
|
3473
|
+
}
|
|
3474
|
+
}
|
|
3475
|
+
return false;
|
|
3476
|
+
};
|
|
3477
|
+
intersectionTestVertex2D = function(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB) {
|
|
3478
|
+
if (pointInTriangleInclusive2D(vertex1TriangleB, vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, TRIANGLE_2D_EPSILON)) {
|
|
3479
|
+
return true;
|
|
3480
|
+
}
|
|
3481
|
+
if (pointInTriangleInclusive2D(vertex2TriangleB, vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, TRIANGLE_2D_EPSILON)) {
|
|
3482
|
+
return true;
|
|
3483
|
+
}
|
|
3484
|
+
if (pointInTriangleInclusive2D(vertex3TriangleB, vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, TRIANGLE_2D_EPSILON)) {
|
|
3485
|
+
return true;
|
|
3486
|
+
}
|
|
3487
|
+
if (pointInTriangleInclusive2D(vertex1TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, TRIANGLE_2D_EPSILON)) {
|
|
3488
|
+
return true;
|
|
3489
|
+
}
|
|
3490
|
+
if (pointInTriangleInclusive2D(vertex2TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, TRIANGLE_2D_EPSILON)) {
|
|
3491
|
+
return true;
|
|
3492
|
+
}
|
|
3493
|
+
if (pointInTriangleInclusive2D(vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, TRIANGLE_2D_EPSILON)) {
|
|
3494
|
+
return true;
|
|
3495
|
+
}
|
|
3496
|
+
return false;
|
|
3497
|
+
};
|
|
3498
|
+
constructIntersection = function(vertex1TriangleA, vertex2TriangleA, vertex3TriangleA, vertex1TriangleB, vertex2TriangleB, vertex3TriangleB, additions) {
|
|
3499
|
+
var alpha, crossNormal;
|
|
3500
|
+
alpha = void 0;
|
|
3501
|
+
crossNormal = new Vector3();
|
|
3502
|
+
temporaryVector3Primary.subVectors(vertex2TriangleA, vertex1TriangleA);
|
|
3503
|
+
temporaryVector3Secondary.subVectors(vertex3TriangleB, vertex1TriangleA);
|
|
3504
|
+
crossNormal.copy(temporaryVector3Primary).cross(temporaryVector3Secondary);
|
|
3505
|
+
temporaryVector3Quaternary.subVectors(vertex1TriangleB, vertex1TriangleA);
|
|
3506
|
+
if (temporaryVector3Quaternary.dot(crossNormal) > 0) {
|
|
3507
|
+
temporaryVector3Primary.subVectors(vertex3TriangleA, vertex1TriangleA);
|
|
3508
|
+
crossNormal.copy(temporaryVector3Primary).cross(temporaryVector3Secondary);
|
|
3509
|
+
if (temporaryVector3Quaternary.dot(crossNormal) <= 0) {
|
|
3510
|
+
temporaryVector3Secondary.subVectors(vertex2TriangleB, vertex1TriangleA);
|
|
3511
|
+
crossNormal.copy(temporaryVector3Primary).cross(temporaryVector3Secondary);
|
|
3512
|
+
if (temporaryVector3Quaternary.dot(crossNormal) > 0) {
|
|
3513
|
+
temporaryVector3Primary.subVectors(vertex1TriangleA, vertex1TriangleB);
|
|
3514
|
+
temporaryVector3Secondary.subVectors(vertex1TriangleA, vertex3TriangleA);
|
|
3515
|
+
alpha = temporaryVector3Primary.dot(additions.normal2) / temporaryVector3Secondary.dot(additions.normal2);
|
|
3516
|
+
if (!isFinite(alpha)) {
|
|
3517
|
+
return false;
|
|
3518
|
+
}
|
|
3519
|
+
temporaryVector3Primary.copy(temporaryVector3Secondary).multiplyScalar(alpha);
|
|
3520
|
+
additions.source.subVectors(vertex1TriangleA, temporaryVector3Primary);
|
|
3521
|
+
temporaryVector3Primary.subVectors(vertex1TriangleB, vertex1TriangleA);
|
|
3522
|
+
temporaryVector3Secondary.subVectors(vertex1TriangleB, vertex3TriangleB);
|
|
3523
|
+
alpha = temporaryVector3Primary.dot(additions.normal1) / temporaryVector3Secondary.dot(additions.normal1);
|
|
3524
|
+
if (!isFinite(alpha)) {
|
|
3525
|
+
return false;
|
|
3526
|
+
}
|
|
3527
|
+
temporaryVector3Primary.copy(temporaryVector3Secondary).multiplyScalar(alpha);
|
|
3528
|
+
additions.target.subVectors(vertex1TriangleB, temporaryVector3Primary);
|
|
3529
|
+
return true;
|
|
3530
|
+
} else {
|
|
3531
|
+
temporaryVector3Primary.subVectors(vertex1TriangleB, vertex1TriangleA);
|
|
3532
|
+
temporaryVector3Secondary.subVectors(vertex1TriangleB, vertex2TriangleB);
|
|
3533
|
+
alpha = temporaryVector3Primary.dot(additions.normal1) / temporaryVector3Secondary.dot(additions.normal1);
|
|
3534
|
+
if (!isFinite(alpha)) {
|
|
3535
|
+
return false;
|
|
3536
|
+
}
|
|
3537
|
+
temporaryVector3Primary.copy(temporaryVector3Secondary).multiplyScalar(alpha);
|
|
3538
|
+
additions.source.subVectors(vertex1TriangleB, temporaryVector3Primary);
|
|
3539
|
+
temporaryVector3Primary.subVectors(vertex1TriangleB, vertex1TriangleA);
|
|
3540
|
+
temporaryVector3Secondary.subVectors(vertex1TriangleB, vertex3TriangleB);
|
|
3541
|
+
alpha = temporaryVector3Primary.dot(additions.normal1) / temporaryVector3Secondary.dot(additions.normal1);
|
|
3542
|
+
if (!isFinite(alpha)) {
|
|
3543
|
+
return false;
|
|
3544
|
+
}
|
|
3545
|
+
temporaryVector3Primary.copy(temporaryVector3Secondary).multiplyScalar(alpha);
|
|
3546
|
+
additions.target.subVectors(vertex1TriangleB, temporaryVector3Primary);
|
|
3547
|
+
return true;
|
|
3548
|
+
}
|
|
3549
|
+
} else {
|
|
3550
|
+
return false;
|
|
3551
|
+
}
|
|
3552
|
+
} else {
|
|
3553
|
+
temporaryVector3Secondary.subVectors(vertex2TriangleB, vertex1TriangleA);
|
|
3554
|
+
crossNormal.copy(temporaryVector3Primary).cross(temporaryVector3Secondary);
|
|
3555
|
+
if (temporaryVector3Quaternary.dot(crossNormal) < 0) {
|
|
3556
|
+
return false;
|
|
3557
|
+
} else {
|
|
3558
|
+
temporaryVector3Primary.subVectors(vertex3TriangleA, vertex1TriangleA);
|
|
3559
|
+
crossNormal.copy(temporaryVector3Primary).cross(temporaryVector3Secondary);
|
|
3560
|
+
if (temporaryVector3Quaternary.dot(crossNormal) < 0) {
|
|
3561
|
+
temporaryVector3Primary.subVectors(vertex1TriangleB, vertex1TriangleA);
|
|
3562
|
+
temporaryVector3Secondary.subVectors(vertex1TriangleB, vertex2TriangleB);
|
|
3563
|
+
alpha = temporaryVector3Primary.dot(additions.normal1) / temporaryVector3Secondary.dot(additions.normal1);
|
|
3564
|
+
if (!isFinite(alpha)) {
|
|
3565
|
+
return false;
|
|
3566
|
+
}
|
|
3567
|
+
temporaryVector3Primary.copy(temporaryVector3Secondary).multiplyScalar(alpha);
|
|
3568
|
+
additions.source.subVectors(vertex1TriangleB, temporaryVector3Primary);
|
|
3569
|
+
temporaryVector3Primary.subVectors(vertex1TriangleB, vertex1TriangleA);
|
|
3570
|
+
temporaryVector3Secondary.subVectors(vertex1TriangleB, vertex3TriangleB);
|
|
3571
|
+
alpha = temporaryVector3Primary.dot(additions.normal1) / temporaryVector3Secondary.dot(additions.normal1);
|
|
3572
|
+
if (!isFinite(alpha)) {
|
|
3573
|
+
return false;
|
|
3574
|
+
}
|
|
3575
|
+
temporaryVector3Primary.copy(temporaryVector3Secondary).multiplyScalar(alpha);
|
|
3576
|
+
additions.target.subVectors(vertex1TriangleB, temporaryVector3Primary);
|
|
3577
|
+
return true;
|
|
3578
|
+
} else {
|
|
3579
|
+
temporaryVector3Primary.subVectors(vertex1TriangleA, vertex1TriangleB);
|
|
3580
|
+
temporaryVector3Secondary.subVectors(vertex1TriangleA, vertex3TriangleA);
|
|
3581
|
+
alpha = temporaryVector3Primary.dot(additions.normal2) / temporaryVector3Secondary.dot(additions.normal2);
|
|
3582
|
+
if (!isFinite(alpha)) {
|
|
3583
|
+
return false;
|
|
3584
|
+
}
|
|
3585
|
+
temporaryVector3Primary.copy(temporaryVector3Secondary).multiplyScalar(alpha);
|
|
3586
|
+
additions.source.subVectors(vertex1TriangleA, temporaryVector3Primary);
|
|
3587
|
+
temporaryVector3Primary.subVectors(vertex1TriangleA, vertex1TriangleB);
|
|
3588
|
+
temporaryVector3Secondary.subVectors(vertex1TriangleA, vertex2TriangleA);
|
|
3589
|
+
alpha = temporaryVector3Primary.dot(additions.normal2) / temporaryVector3Secondary.dot(additions.normal2);
|
|
3590
|
+
if (!isFinite(alpha)) {
|
|
3591
|
+
return false;
|
|
3592
|
+
}
|
|
3593
|
+
temporaryVector3Primary.copy(temporaryVector3Secondary).multiplyScalar(alpha);
|
|
3594
|
+
additions.target.subVectors(vertex1TriangleA, temporaryVector3Primary);
|
|
3595
|
+
return true;
|
|
3596
|
+
}
|
|
3597
|
+
}
|
|
3598
|
+
}
|
|
3599
|
+
return false;
|
|
3600
|
+
};
|
|
3601
|
+
var Vertex;
|
|
3602
|
+
Vertex = class Vertex2 {
|
|
3603
|
+
// Main constructor for creating vertex instances.
|
|
3604
|
+
// Initializes position, normal, and optional texture/color attributes.
|
|
3605
|
+
// @param pos - Three.js Vector3 representing the vertex position in 3D space.
|
|
3606
|
+
// @param normal - Three.js Vector3 representing the surface normal at this vertex.
|
|
3607
|
+
// @param uv - Optional Three.js Vector2 for texture coordinates.
|
|
3608
|
+
// @param color - Optional Three.js Vector3 for vertex color information.
|
|
3609
|
+
constructor(pos, normal, uv, color) {
|
|
3610
|
+
this.pos = new Vector3().copy(pos);
|
|
3611
|
+
this.normal = new Vector3().copy(normal);
|
|
3612
|
+
uv && (this.uv = new Vector2().copy(uv));
|
|
3613
|
+
color && (this.color = new Vector3().copy(color));
|
|
3614
|
+
}
|
|
3615
|
+
// === OBJECT CREATION AND COPYING ===
|
|
3616
|
+
clone() {
|
|
3617
|
+
return new Vertex2(this.pos.clone(), this.normal.clone(), this.uv && this.uv.clone(), this.color && this.color.clone());
|
|
3618
|
+
}
|
|
3619
|
+
// === GEOMETRIC TRANSFORMATIONS ===
|
|
3620
|
+
flip() {
|
|
3621
|
+
return this.normal.negate();
|
|
3622
|
+
}
|
|
3623
|
+
// Creates a new vertex interpolated between this vertex and another.
|
|
3624
|
+
// All properties (position, normal, UV, color) are interpolated if present on both vertices.
|
|
3625
|
+
// @param other - The target vertex to interpolate towards.
|
|
3626
|
+
// @param interpolationFactor - Factor from 0.0 (this vertex) to 1.0 (other vertex).
|
|
3627
|
+
// @return New interpolated Vertex instance.
|
|
3628
|
+
interpolate(other, interpolationFactor) {
|
|
3629
|
+
return new Vertex2(this.pos.clone().lerp(other.pos, interpolationFactor), this.normal.clone().lerp(other.normal, interpolationFactor), this.uv && other.uv && this.uv.clone().lerp(other.uv, interpolationFactor), this.color && other.color && this.color.clone().lerp(other.color, interpolationFactor));
|
|
3630
|
+
}
|
|
3631
|
+
// === CLEANUP AND DISPOSAL METHODS ===
|
|
3632
|
+
// Clean up vertex data by setting all properties to undefined.
|
|
3633
|
+
// Used for memory management during intensive operations.
|
|
3634
|
+
delete() {
|
|
3635
|
+
this.pos = void 0;
|
|
3636
|
+
this.normal = void 0;
|
|
3637
|
+
this.uv && (this.uv = void 0);
|
|
3638
|
+
return this.color && (this.color = void 0);
|
|
3639
|
+
}
|
|
3640
|
+
};
|
|
3641
|
+
var _export_toGeometry = Polytree.toGeometry;
|
|
3642
|
+
var _export_toMesh = Polytree.toMesh;
|
|
3643
|
+
var _export_fromMesh = Polytree.fromMesh;
|
|
3644
|
+
var _export_getSurface = Polytree.getSurface;
|
|
3645
|
+
var _export_getVolume = Polytree.getVolume;
|
|
3646
|
+
var _export_closestPointToPoint = Polytree.closestPointToPoint;
|
|
3647
|
+
var _export_distanceToPoint = Polytree.distanceToPoint;
|
|
3648
|
+
var _export_intersectsSphere = Polytree.intersectsSphere;
|
|
3649
|
+
var _export_intersectsBox = Polytree.intersectsBox;
|
|
3650
|
+
var _export_intersectPlane = Polytree.intersectPlane;
|
|
3651
|
+
var _export_sliceIntoLayers = Polytree.sliceIntoLayers;
|
|
3652
|
+
var _export_shapecast = Polytree.shapecast;
|
|
3653
|
+
var _export_getTrianglesNearPoint = Polytree.getTrianglesNearPoint;
|
|
3654
|
+
var _export_estimateVolumeViaSampling = Polytree.estimateVolumeViaSampling;
|
|
3655
|
+
var polytree_bundle_browser_default = Polytree;
|
|
3656
|
+
export {
|
|
3657
|
+
ASYNC_OPERATION_TIMEOUT,
|
|
3658
|
+
DEBUG_COLOR_BACK,
|
|
3659
|
+
DEBUG_COLOR_COPLANAR,
|
|
3660
|
+
DEBUG_COLOR_FRONT,
|
|
3661
|
+
DEBUG_COLOR_SPANNING,
|
|
3662
|
+
DEBUG_GEOMETRY_VALIDATION,
|
|
3663
|
+
DEBUG_INTERSECTION_VERIFICATION,
|
|
3664
|
+
DEBUG_PERFORMANCE_TIMING,
|
|
3665
|
+
DEBUG_VERBOSE_LOGGING,
|
|
3666
|
+
DEFAULT_BUFFER_SIZE,
|
|
3667
|
+
DEFAULT_COORDINATE_PRECISION,
|
|
3668
|
+
DEFAULT_MATERIAL_INDEX,
|
|
3669
|
+
GARBAGE_COLLECTION_THRESHOLD,
|
|
3670
|
+
GEOMETRIC_EPSILON,
|
|
3671
|
+
GEOMETRY_CACHE_SIZE,
|
|
3672
|
+
INTERSECTION_CACHE_SIZE,
|
|
3673
|
+
MAX_REFINEMENT_ITERATIONS,
|
|
3674
|
+
MAX_WORKER_THREADS,
|
|
3675
|
+
MEMORY_USAGE_WARNING_LIMIT,
|
|
3676
|
+
POINT_COINCIDENCE_THRESHOLD,
|
|
3677
|
+
POLYGON_BACK,
|
|
3678
|
+
POLYGON_COPLANAR,
|
|
3679
|
+
POLYGON_FRONT,
|
|
3680
|
+
POLYGON_SPANNING,
|
|
3681
|
+
POLYTREE_MAX_DEPTH,
|
|
3682
|
+
POLYTREE_MAX_POLYGONS_PER_NODE,
|
|
3683
|
+
POLYTREE_MIN_NODE_SIZE,
|
|
3684
|
+
Plane,
|
|
3685
|
+
Polygon,
|
|
3686
|
+
Polytree,
|
|
3687
|
+
RAY_INTERSECTION_EPSILON,
|
|
3688
|
+
TRIANGLE_2D_EPSILON,
|
|
3689
|
+
Vertex,
|
|
3690
|
+
WINDING_NUMBER_FULL_ROTATION,
|
|
3691
|
+
calculateWindingNumberFromBuffer,
|
|
3692
|
+
_export_closestPointToPoint as closestPointToPoint,
|
|
3693
|
+
constructIntersection,
|
|
3694
|
+
createVector2Buffer,
|
|
3695
|
+
createVector3Buffer,
|
|
3696
|
+
polytree_bundle_browser_default as default,
|
|
3697
|
+
defaultRayDirection,
|
|
3698
|
+
disposePolytreeResources,
|
|
3699
|
+
_export_distanceToPoint as distanceToPoint,
|
|
3700
|
+
_export_estimateVolumeViaSampling as estimateVolumeViaSampling,
|
|
3701
|
+
extractCoordinatesFromArray,
|
|
3702
|
+
_export_fromMesh as fromMesh,
|
|
3703
|
+
_export_getSurface as getSurface,
|
|
3704
|
+
_export_getTrianglesNearPoint as getTrianglesNearPoint,
|
|
3705
|
+
_export_getVolume as getVolume,
|
|
3706
|
+
handleIntersectingPolytrees,
|
|
3707
|
+
_export_intersectPlane as intersectPlane,
|
|
3708
|
+
intersectionTestEdge2D,
|
|
3709
|
+
intersectionTestVertex2D,
|
|
3710
|
+
_export_intersectsBox as intersectsBox,
|
|
3711
|
+
_export_intersectsSphere as intersectsSphere,
|
|
3712
|
+
isUniqueTriangle,
|
|
3713
|
+
isValidTriangle,
|
|
3714
|
+
meshOperationNormalVector,
|
|
3715
|
+
meshOperationVertexVector,
|
|
3716
|
+
polygonID,
|
|
3717
|
+
prepareTriangleBufferFromPolygons,
|
|
3718
|
+
testRayTriangleIntersection as rayIntersectsTriangle,
|
|
3719
|
+
rayTriangleEdge1,
|
|
3720
|
+
rayTriangleEdge2,
|
|
3721
|
+
rayTriangleHVector,
|
|
3722
|
+
rayTriangleQVector,
|
|
3723
|
+
rayTriangleSVector,
|
|
3724
|
+
resolveCoplanarTriangleIntersection,
|
|
3725
|
+
resolveTriangleIntersection,
|
|
3726
|
+
roundPointCoordinates,
|
|
3727
|
+
_export_shapecast as shapecast,
|
|
3728
|
+
signedVolumeOfTriangle,
|
|
3729
|
+
_export_sliceIntoLayers as sliceIntoLayers,
|
|
3730
|
+
sortRaycastIntersectionsByDistance,
|
|
3731
|
+
splitPolygonByPlane,
|
|
3732
|
+
splitPolygonVertexArray,
|
|
3733
|
+
temporaryBoundingBox,
|
|
3734
|
+
temporaryMatrix3,
|
|
3735
|
+
temporaryMatrixWithNormalCalc,
|
|
3736
|
+
temporaryRay,
|
|
3737
|
+
temporaryRaycaster,
|
|
3738
|
+
temporaryTriangleVertex,
|
|
3739
|
+
temporaryTriangleVertexSecondary,
|
|
3740
|
+
temporaryVector3Primary,
|
|
3741
|
+
temporaryVector3Quaternary,
|
|
3742
|
+
temporaryVector3Secondary,
|
|
3743
|
+
temporaryVector3Tertiary,
|
|
3744
|
+
testPolygonInsideUsingWindingNumber,
|
|
3745
|
+
testRayTriangleIntersection,
|
|
3746
|
+
_export_toGeometry as toGeometry,
|
|
3747
|
+
_export_toMesh as toMesh,
|
|
3748
|
+
triangleIntersectionCCW2D,
|
|
3749
|
+
triangleIntersectsTriangle,
|
|
3750
|
+
triangleOrientation2D,
|
|
3751
|
+
trianglesOverlap2D,
|
|
3752
|
+
windingNumberEpsilonOffsets,
|
|
3753
|
+
windingNumberEpsilonOffsetsCount,
|
|
3754
|
+
windingNumberMatrix3,
|
|
3755
|
+
windingNumberTestPoint,
|
|
3756
|
+
windingNumberVector1,
|
|
3757
|
+
windingNumberVector2,
|
|
3758
|
+
windingNumberVector3
|
|
3759
|
+
};
|