three-stdlib 2.14.3 → 2.15.1
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/csm/CSM.cjs.js +1 -1
- package/csm/CSM.js +8 -8
- package/csm/CSMFrustum.cjs.js +1 -0
- package/csm/CSMFrustum.d.ts +19 -0
- package/csm/{Frustum.js → CSMFrustum.js} +8 -7
- package/csm/CSMShader.cjs.js +1 -0
- package/csm/CSMShader.d.ts +4 -0
- package/csm/{Shader.js → CSMShader.js} +52 -36
- package/curves/CurveExtras.d.ts +52 -54
- package/geometries/ConvexGeometry.cjs.js +1 -1
- package/geometries/ConvexGeometry.js +1 -1
- package/geometries/ParametricGeometries.cjs.js +1 -1
- package/geometries/ParametricGeometries.js +91 -99
- package/geometries/ParametricGeometry.cjs.js +1 -0
- package/geometries/ParametricGeometry.d.ts +18 -0
- package/geometries/ParametricGeometry.js +87 -0
- package/index.cjs.js +1 -1
- package/index.d.ts +57 -77
- package/index.js +55 -52
- package/interactive/InteractiveGroup.cjs.js +1 -0
- package/interactive/InteractiveGroup.d.ts +5 -0
- package/interactive/InteractiveGroup.js +87 -0
- package/loaders/GLTFLoader.d.ts +2 -9
- package/loaders/LUT3dlLoader.d.ts +2 -2
- package/loaders/LUTCubeLoader.d.ts +2 -2
- package/loaders/VOXLoader.d.ts +2 -2
- package/math/ConvexHull.cjs.js +1 -1
- package/math/ConvexHull.js +639 -603
- package/objects/Reflector.d.ts +1 -0
- package/objects/ReflectorForSSRPass.d.ts +4 -4
- package/objects/Refractor.d.ts +1 -0
- package/objects/Water2.d.ts +1 -1
- package/package.json +2 -1
- package/postprocessing/LUTPass.d.ts +3 -3
- package/postprocessing/SSAARenderPass.d.ts +1 -1
- package/postprocessing/SSRPass.d.ts +3 -3
- package/shaders/BokehShader2.d.ts +27 -72
- package/shaders/BokehShader2.js +0 -1
- package/csm/Frustum.cjs.js +0 -1
- package/csm/Shader.cjs.js +0 -1
- package/loaders/VRMLoader.d.ts +0 -19
- package/nodes/Nodes.d.ts +0 -106
- package/nodes/accessors/CameraNode.d.ts +0 -29
- package/nodes/accessors/NormalNode.d.ts +0 -13
- package/nodes/accessors/PositionNode.d.ts +0 -15
- package/nodes/accessors/ReflectNode.d.ts +0 -12
- package/nodes/accessors/UVNode.d.ts +0 -10
- package/nodes/core/AttributeNode.d.ts +0 -13
- package/nodes/core/ConstNode.d.ts +0 -22
- package/nodes/core/ExpressionNode.d.ts +0 -5
- package/nodes/core/FunctionCallNode.d.ts +0 -17
- package/nodes/core/FunctionNode.d.ts +0 -28
- package/nodes/core/InputNode.d.ts +0 -12
- package/nodes/core/Node.d.ts +0 -34
- package/nodes/core/NodeBuilder.d.ts +0 -149
- package/nodes/core/NodeFrame.d.ts +0 -17
- package/nodes/core/NodeUniform.d.ts +0 -17
- package/nodes/core/NodeUtils.d.ts +0 -7
- package/nodes/core/TempNode.d.ts +0 -23
- package/nodes/core/VarNode.d.ts +0 -12
- package/nodes/materials/MeshStandardNodeMaterial.d.ts +0 -21
- package/nodes/materials/NodeMaterial.d.ts +0 -28
- package/nodes/math/CondNode.d.ts +0 -26
- package/nodes/math/MathNode.d.ts +0 -57
- package/nodes/math/OperatorNode.d.ts +0 -17
- package/nodes/procedural/CheckerNode.d.ts +0 -17
- package/nodes/utils/JoinNode.d.ts +0 -15
- package/nodes/utils/TimerNode.d.ts +0 -19
- package/objects/ReflectorRTT.d.ts +0 -6
- package/shaders/index.cjs.js +0 -1
- package/shaders/index.d.ts +0 -53
- package/shaders/index.js +0 -53
package/math/ConvexHull.js
CHANGED
@@ -4,12 +4,21 @@ import { Vector3, Line3, Plane, Triangle } from 'three';
|
|
4
4
|
* Ported from: https://github.com/maurizzzio/quickhull3d/ by Mauricio Poppe (https://github.com/maurizzzio)
|
5
5
|
*/
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
var Deleted = 1;
|
10
|
-
var v1 = new Vector3();
|
7
|
+
const Visible = 0;
|
8
|
+
const Deleted = 1;
|
11
9
|
|
12
|
-
|
10
|
+
const _v1 = new Vector3();
|
11
|
+
|
12
|
+
const _line3 = new Line3();
|
13
|
+
|
14
|
+
const _plane = new Plane();
|
15
|
+
|
16
|
+
const _closestPoint = new Vector3();
|
17
|
+
|
18
|
+
const _triangle = new Triangle();
|
19
|
+
|
20
|
+
class ConvexHull {
|
21
|
+
constructor() {
|
13
22
|
this.tolerance = -1;
|
14
23
|
this.faces = []; // the generated faces of the convex hull
|
15
24
|
|
@@ -30,16 +39,9 @@ var ConvexHull = function () {
|
|
30
39
|
this.vertices = []; // vertices of the hull (internal representation of given geometry data)
|
31
40
|
}
|
32
41
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
console.error('THREE.ConvexHull: Points parameter is not an array.');
|
37
|
-
}
|
38
|
-
|
39
|
-
if (points.length < 4) {
|
40
|
-
console.error('THREE.ConvexHull: The algorithm needs at least four points.');
|
41
|
-
}
|
42
|
-
|
42
|
+
setFromPoints(points) {
|
43
|
+
// The algorithm needs at least four points.
|
44
|
+
if (points.length >= 4) {
|
43
45
|
this.makeEmpty();
|
44
46
|
|
45
47
|
for (let i = 0, l = points.length; i < l; i++) {
|
@@ -47,528 +49,543 @@ var ConvexHull = function () {
|
|
47
49
|
}
|
48
50
|
|
49
51
|
this.compute();
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
52
|
+
}
|
53
|
+
|
54
|
+
return this;
|
55
|
+
}
|
56
|
+
|
57
|
+
setFromObject(object) {
|
58
|
+
const points = [];
|
59
|
+
object.updateMatrixWorld(true);
|
60
|
+
object.traverse(function (node) {
|
61
|
+
const geometry = node.geometry;
|
62
|
+
|
63
|
+
if (geometry !== undefined) {
|
64
|
+
if (geometry.isGeometry) {
|
65
|
+
console.error('THREE.ConvexHull no longer supports Geometry. Use THREE.BufferGeometry instead.');
|
66
|
+
return;
|
67
|
+
} else if (geometry.isBufferGeometry) {
|
68
|
+
const attribute = geometry.attributes.position;
|
69
|
+
|
70
|
+
if (attribute !== undefined) {
|
71
|
+
for (let i = 0, l = attribute.count; i < l; i++) {
|
72
|
+
const point = new Vector3();
|
73
|
+
point.fromBufferAttribute(attribute, i).applyMatrix4(node.matrixWorld);
|
74
|
+
points.push(point);
|
72
75
|
}
|
73
76
|
}
|
74
77
|
}
|
75
|
-
});
|
76
|
-
return this.setFromPoints(points);
|
77
|
-
},
|
78
|
-
containsPoint: function (point) {
|
79
|
-
var faces = this.faces;
|
80
|
-
|
81
|
-
for (let i = 0, l = faces.length; i < l; i++) {
|
82
|
-
var face = faces[i]; // compute signed distance and check on what half space the point lies
|
83
|
-
|
84
|
-
if (face.distanceToPoint(point) > this.tolerance) return false;
|
85
78
|
}
|
79
|
+
});
|
80
|
+
return this.setFromPoints(points);
|
81
|
+
}
|
86
82
|
|
87
|
-
|
88
|
-
|
89
|
-
intersectRay: function (ray, target) {
|
90
|
-
// based on "Fast Ray-Convex Polyhedron Intersection" by Eric Haines, GRAPHICS GEMS II
|
91
|
-
var faces = this.faces;
|
92
|
-
var tNear = -Infinity;
|
93
|
-
var tFar = Infinity;
|
83
|
+
containsPoint(point) {
|
84
|
+
const faces = this.faces;
|
94
85
|
|
95
|
-
|
96
|
-
|
86
|
+
for (let i = 0, l = faces.length; i < l; i++) {
|
87
|
+
const face = faces[i]; // compute signed distance and check on what half space the point lies
|
97
88
|
|
98
|
-
|
99
|
-
|
100
|
-
// the ray is turned away or parallel to the plane, there is no intersection
|
89
|
+
if (face.distanceToPoint(point) > this.tolerance) return false;
|
90
|
+
}
|
101
91
|
|
102
|
-
|
92
|
+
return true;
|
93
|
+
}
|
103
94
|
|
104
|
-
|
105
|
-
|
95
|
+
intersectRay(ray, target) {
|
96
|
+
// based on "Fast Ray-Convex Polyhedron Intersection" by Eric Haines, GRAPHICS GEMS II
|
97
|
+
const faces = this.faces;
|
98
|
+
let tNear = -Infinity;
|
99
|
+
let tFar = Infinity;
|
106
100
|
|
107
|
-
|
101
|
+
for (let i = 0, l = faces.length; i < l; i++) {
|
102
|
+
const face = faces[i]; // interpret faces as planes for the further computation
|
108
103
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
} else {
|
113
|
-
// front-face
|
114
|
-
tNear = Math.max(t, tNear);
|
115
|
-
}
|
104
|
+
const vN = face.distanceToPoint(ray.origin);
|
105
|
+
const vD = face.normal.dot(ray.direction); // if the origin is on the positive side of a plane (so the plane can "see" the origin) and
|
106
|
+
// the ray is turned away or parallel to the plane, there is no intersection
|
116
107
|
|
117
|
-
|
118
|
-
// if tNear ever is greater than tFar, the ray must miss the convex hull
|
119
|
-
return null;
|
120
|
-
}
|
121
|
-
} // evaluate intersection point
|
122
|
-
// always try tNear first since its the closer intersection point
|
108
|
+
if (vN > 0 && vD >= 0) return null; // compute the distance from the ray’s origin to the intersection with the plane
|
123
109
|
|
110
|
+
const t = vD !== 0 ? -vN / vD : 0; // only proceed if the distance is positive. a negative distance means the intersection point
|
111
|
+
// lies "behind" the origin
|
124
112
|
|
125
|
-
if (
|
126
|
-
ray.at(tNear, target);
|
127
|
-
} else {
|
128
|
-
ray.at(tFar, target);
|
129
|
-
}
|
113
|
+
if (t <= 0) continue; // now categorized plane as front-facing or back-facing
|
130
114
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
return this.intersectRay(ray, v1) !== null;
|
135
|
-
},
|
136
|
-
makeEmpty: function () {
|
137
|
-
this.faces = [];
|
138
|
-
this.vertices = [];
|
139
|
-
return this;
|
140
|
-
},
|
141
|
-
// Adds a vertex to the 'assigned' list of vertices and assigns it to the given face
|
142
|
-
addVertexToFace: function (vertex, face) {
|
143
|
-
vertex.face = face;
|
144
|
-
|
145
|
-
if (face.outside === null) {
|
146
|
-
this.assigned.append(vertex);
|
115
|
+
if (vD > 0) {
|
116
|
+
// plane faces away from the ray, so this plane is a back-face
|
117
|
+
tFar = Math.min(t, tFar);
|
147
118
|
} else {
|
148
|
-
|
119
|
+
// front-face
|
120
|
+
tNear = Math.max(t, tNear);
|
149
121
|
}
|
150
122
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
// Removes a vertex from the 'assigned' list of vertices and from the given face
|
155
|
-
removeVertexFromFace: function (vertex, face) {
|
156
|
-
if (vertex === face.outside) {
|
157
|
-
// fix face.outside link
|
158
|
-
if (vertex.next !== null && vertex.next.face === face) {
|
159
|
-
// face has at least 2 outside vertices, move the 'outside' reference
|
160
|
-
face.outside = vertex.next;
|
161
|
-
} else {
|
162
|
-
// vertex was the only outside vertex that face had
|
163
|
-
face.outside = null;
|
164
|
-
}
|
123
|
+
if (tNear > tFar) {
|
124
|
+
// if tNear ever is greater than tFar, the ray must miss the convex hull
|
125
|
+
return null;
|
165
126
|
}
|
127
|
+
} // evaluate intersection point
|
128
|
+
// always try tNear first since its the closer intersection point
|
166
129
|
|
167
|
-
this.assigned.remove(vertex);
|
168
|
-
return this;
|
169
|
-
},
|
170
|
-
// Removes all the visible vertices that a given face is able to see which are stored in the 'assigned' vertext list
|
171
|
-
removeAllVerticesFromFace: function (face) {
|
172
|
-
if (face.outside !== null) {
|
173
|
-
// reference to the first and last vertex of this face
|
174
|
-
var start = face.outside;
|
175
|
-
var end = face.outside;
|
176
|
-
|
177
|
-
while (end.next !== null && end.next.face === face) {
|
178
|
-
end = end.next;
|
179
|
-
}
|
180
130
|
|
181
|
-
|
131
|
+
if (tNear !== -Infinity) {
|
132
|
+
ray.at(tNear, target);
|
133
|
+
} else {
|
134
|
+
ray.at(tFar, target);
|
135
|
+
}
|
182
136
|
|
183
|
-
|
137
|
+
return target;
|
138
|
+
}
|
139
|
+
|
140
|
+
intersectsRay(ray) {
|
141
|
+
return this.intersectRay(ray, _v1) !== null;
|
142
|
+
}
|
143
|
+
|
144
|
+
makeEmpty() {
|
145
|
+
this.faces = [];
|
146
|
+
this.vertices = [];
|
147
|
+
return this;
|
148
|
+
} // Adds a vertex to the 'assigned' list of vertices and assigns it to the given face
|
149
|
+
|
150
|
+
|
151
|
+
addVertexToFace(vertex, face) {
|
152
|
+
vertex.face = face;
|
153
|
+
|
154
|
+
if (face.outside === null) {
|
155
|
+
this.assigned.append(vertex);
|
156
|
+
} else {
|
157
|
+
this.assigned.insertBefore(face.outside, vertex);
|
158
|
+
}
|
159
|
+
|
160
|
+
face.outside = vertex;
|
161
|
+
return this;
|
162
|
+
} // Removes a vertex from the 'assigned' list of vertices and from the given face
|
163
|
+
|
164
|
+
|
165
|
+
removeVertexFromFace(vertex, face) {
|
166
|
+
if (vertex === face.outside) {
|
167
|
+
// fix face.outside link
|
168
|
+
if (vertex.next !== null && vertex.next.face === face) {
|
169
|
+
// face has at least 2 outside vertices, move the 'outside' reference
|
170
|
+
face.outside = vertex.next;
|
171
|
+
} else {
|
172
|
+
// vertex was the only outside vertex that face had
|
184
173
|
face.outside = null;
|
185
|
-
return start;
|
186
174
|
}
|
187
|
-
}
|
188
|
-
// Removes all the visible vertices that 'face' is able to see
|
189
|
-
deleteFaceVertices: function (face, absorbingFace) {
|
190
|
-
var faceVertices = this.removeAllVerticesFromFace(face);
|
191
|
-
|
192
|
-
if (faceVertices !== undefined) {
|
193
|
-
if (absorbingFace === undefined) {
|
194
|
-
// mark the vertices to be reassigned to some other face
|
195
|
-
this.unassigned.appendChain(faceVertices);
|
196
|
-
} else {
|
197
|
-
// if there's an absorbing face try to assign as many vertices as possible to it
|
198
|
-
var vertex = faceVertices;
|
175
|
+
}
|
199
176
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
var nextVertex = vertex.next;
|
204
|
-
var distance = absorbingFace.distanceToPoint(vertex.point); // check if 'vertex' is able to see 'absorbingFace'
|
177
|
+
this.assigned.remove(vertex);
|
178
|
+
return this;
|
179
|
+
} // Removes all the visible vertices that a given face is able to see which are stored in the 'assigned' vertext list
|
205
180
|
|
206
|
-
if (distance > this.tolerance) {
|
207
|
-
this.addVertexToFace(vertex, absorbingFace);
|
208
|
-
} else {
|
209
|
-
this.unassigned.append(vertex);
|
210
|
-
} // now assign next vertex
|
211
181
|
|
182
|
+
removeAllVerticesFromFace(face) {
|
183
|
+
if (face.outside !== null) {
|
184
|
+
// reference to the first and last vertex of this face
|
185
|
+
const start = face.outside;
|
186
|
+
let end = face.outside;
|
212
187
|
|
213
|
-
|
214
|
-
|
215
|
-
}
|
188
|
+
while (end.next !== null && end.next.face === face) {
|
189
|
+
end = end.next;
|
216
190
|
}
|
217
191
|
|
218
|
-
|
219
|
-
},
|
220
|
-
// Reassigns as many vertices as possible from the unassigned list to the new faces
|
221
|
-
resolveUnassignedPoints: function (newFaces) {
|
222
|
-
if (this.unassigned.isEmpty() === false) {
|
223
|
-
var vertex = this.unassigned.first();
|
192
|
+
this.assigned.removeSubList(start, end); // fix references
|
224
193
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
194
|
+
start.prev = end.next = null;
|
195
|
+
face.outside = null;
|
196
|
+
return start;
|
197
|
+
}
|
198
|
+
} // Removes all the visible vertices that 'face' is able to see
|
230
199
|
|
231
|
-
for (let i = 0; i < newFaces.length; i++) {
|
232
|
-
var face = newFaces[i];
|
233
200
|
|
234
|
-
|
235
|
-
|
201
|
+
deleteFaceVertices(face, absorbingFace) {
|
202
|
+
const faceVertices = this.removeAllVerticesFromFace(face);
|
236
203
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
204
|
+
if (faceVertices !== undefined) {
|
205
|
+
if (absorbingFace === undefined) {
|
206
|
+
// mark the vertices to be reassigned to some other face
|
207
|
+
this.unassigned.appendChain(faceVertices);
|
208
|
+
} else {
|
209
|
+
// if there's an absorbing face try to assign as many vertices as possible to it
|
210
|
+
let vertex = faceVertices;
|
241
211
|
|
242
|
-
|
243
|
-
|
244
|
-
|
212
|
+
do {
|
213
|
+
// we need to buffer the subsequent vertex at this point because the 'vertex.next' reference
|
214
|
+
// will be changed by upcoming method calls
|
215
|
+
const nextVertex = vertex.next;
|
216
|
+
const distance = absorbingFace.distanceToPoint(vertex.point); // check if 'vertex' is able to see 'absorbingFace'
|
245
217
|
|
218
|
+
if (distance > this.tolerance) {
|
219
|
+
this.addVertexToFace(vertex, absorbingFace);
|
220
|
+
} else {
|
221
|
+
this.unassigned.append(vertex);
|
222
|
+
} // now assign next vertex
|
246
223
|
|
247
|
-
if (maxFace !== null) {
|
248
|
-
this.addVertexToFace(vertex, maxFace);
|
249
|
-
}
|
250
224
|
|
251
225
|
vertex = nextVertex;
|
252
226
|
} while (vertex !== null);
|
253
227
|
}
|
228
|
+
}
|
254
229
|
|
255
|
-
|
256
|
-
|
257
|
-
// Computes the extremes of a simplex which will be the initial hull
|
258
|
-
computeExtremes: function () {
|
259
|
-
var min = new Vector3();
|
260
|
-
var max = new Vector3();
|
261
|
-
var minVertices = [];
|
262
|
-
var maxVertices = [];
|
263
|
-
var i, l, j; // initially assume that the first vertex is the min/max
|
264
|
-
|
265
|
-
for (i = 0; i < 3; i++) {
|
266
|
-
minVertices[i] = maxVertices[i] = this.vertices[0];
|
267
|
-
}
|
230
|
+
return this;
|
231
|
+
} // Reassigns as many vertices as possible from the unassigned list to the new faces
|
268
232
|
|
269
|
-
min.copy(this.vertices[0].point);
|
270
|
-
max.copy(this.vertices[0].point); // compute the min/max vertex on all six directions
|
271
233
|
|
272
|
-
|
273
|
-
|
274
|
-
|
234
|
+
resolveUnassignedPoints(newFaces) {
|
235
|
+
if (this.unassigned.isEmpty() === false) {
|
236
|
+
let vertex = this.unassigned.first();
|
275
237
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
} // update the max coordinates
|
238
|
+
do {
|
239
|
+
// buffer 'next' reference, see .deleteFaceVertices()
|
240
|
+
const nextVertex = vertex.next;
|
241
|
+
let maxDistance = this.tolerance;
|
242
|
+
let maxFace = null;
|
282
243
|
|
244
|
+
for (let i = 0; i < newFaces.length; i++) {
|
245
|
+
const face = newFaces[i];
|
283
246
|
|
284
|
-
|
285
|
-
|
286
|
-
max.setComponent(j, point.getComponent(j));
|
287
|
-
maxVertices[j] = vertex;
|
288
|
-
}
|
289
|
-
}
|
290
|
-
} // use min/max vectors to compute an optimal epsilon
|
291
|
-
|
292
|
-
|
293
|
-
this.tolerance = 3 * Number.EPSILON * (Math.max(Math.abs(min.x), Math.abs(max.x)) + Math.max(Math.abs(min.y), Math.abs(max.y)) + Math.max(Math.abs(min.z), Math.abs(max.z)));
|
294
|
-
return {
|
295
|
-
min: minVertices,
|
296
|
-
max: maxVertices
|
297
|
-
};
|
298
|
-
},
|
299
|
-
// Computes the initial simplex assigning to its faces all the points
|
300
|
-
// that are candidates to form part of the hull
|
301
|
-
computeInitialHull: function () {
|
302
|
-
var line3, plane, closestPoint;
|
303
|
-
return function computeInitialHull() {
|
304
|
-
if (line3 === undefined) {
|
305
|
-
line3 = new Line3();
|
306
|
-
plane = new Plane();
|
307
|
-
closestPoint = new Vector3();
|
308
|
-
}
|
247
|
+
if (face.mark === Visible) {
|
248
|
+
const distance = face.distanceToPoint(vertex.point);
|
309
249
|
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
var max = extremes.max;
|
315
|
-
var v0, v1, v2, v3;
|
316
|
-
var i, l, j; // 1. Find the two vertices 'v0' and 'v1' with the greatest 1d separation
|
317
|
-
// (max.x - min.x)
|
318
|
-
// (max.y - min.y)
|
319
|
-
// (max.z - min.z)
|
250
|
+
if (distance > maxDistance) {
|
251
|
+
maxDistance = distance;
|
252
|
+
maxFace = face;
|
253
|
+
}
|
320
254
|
|
321
|
-
|
322
|
-
|
323
|
-
|
255
|
+
if (maxDistance > 1000 * this.tolerance) break;
|
256
|
+
}
|
257
|
+
} // 'maxFace' can be null e.g. if there are identical vertices
|
324
258
|
|
325
|
-
for (i = 0; i < 3; i++) {
|
326
|
-
distance = max[i].point.getComponent(i) - min[i].point.getComponent(i);
|
327
259
|
|
328
|
-
|
329
|
-
|
330
|
-
index = i;
|
331
|
-
}
|
260
|
+
if (maxFace !== null) {
|
261
|
+
this.addVertexToFace(vertex, maxFace);
|
332
262
|
}
|
333
263
|
|
334
|
-
|
335
|
-
|
264
|
+
vertex = nextVertex;
|
265
|
+
} while (vertex !== null);
|
266
|
+
}
|
336
267
|
|
337
|
-
|
338
|
-
|
268
|
+
return this;
|
269
|
+
} // Computes the extremes of a simplex which will be the initial hull
|
339
270
|
|
340
|
-
for (i = 0, l = this.vertices.length; i < l; i++) {
|
341
|
-
vertex = vertices[i];
|
342
271
|
|
343
|
-
|
344
|
-
|
345
|
-
|
272
|
+
computeExtremes() {
|
273
|
+
const min = new Vector3();
|
274
|
+
const max = new Vector3();
|
275
|
+
const minVertices = [];
|
276
|
+
const maxVertices = []; // initially assume that the first vertex is the min/max
|
346
277
|
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
}
|
351
|
-
}
|
352
|
-
} // 3. The next vertex 'v3' is the one farthest to the plane 'v0', 'v1', 'v2'
|
278
|
+
for (let i = 0; i < 3; i++) {
|
279
|
+
minVertices[i] = maxVertices[i] = this.vertices[0];
|
280
|
+
}
|
353
281
|
|
282
|
+
min.copy(this.vertices[0].point);
|
283
|
+
max.copy(this.vertices[0].point); // compute the min/max vertex on all six directions
|
354
284
|
|
355
|
-
|
356
|
-
|
285
|
+
for (let i = 0, l = this.vertices.length; i < l; i++) {
|
286
|
+
const vertex = this.vertices[i];
|
287
|
+
const point = vertex.point; // update the min coordinates
|
357
288
|
|
358
|
-
|
359
|
-
|
289
|
+
for (let j = 0; j < 3; j++) {
|
290
|
+
if (point.getComponent(j) < min.getComponent(j)) {
|
291
|
+
min.setComponent(j, point.getComponent(j));
|
292
|
+
minVertices[j] = vertex;
|
293
|
+
}
|
294
|
+
} // update the max coordinates
|
360
295
|
|
361
|
-
if (vertex !== v0 && vertex !== v1 && vertex !== v2) {
|
362
|
-
distance = Math.abs(plane.distanceToPoint(vertex.point));
|
363
296
|
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
}
|
297
|
+
for (let j = 0; j < 3; j++) {
|
298
|
+
if (point.getComponent(j) > max.getComponent(j)) {
|
299
|
+
max.setComponent(j, point.getComponent(j));
|
300
|
+
maxVertices[j] = vertex;
|
369
301
|
}
|
302
|
+
}
|
303
|
+
} // use min/max vectors to compute an optimal epsilon
|
370
304
|
|
371
|
-
var faces = [];
|
372
305
|
|
373
|
-
|
374
|
-
|
375
|
-
|
306
|
+
this.tolerance = 3 * Number.EPSILON * (Math.max(Math.abs(min.x), Math.abs(max.x)) + Math.max(Math.abs(min.y), Math.abs(max.y)) + Math.max(Math.abs(min.z), Math.abs(max.z)));
|
307
|
+
return {
|
308
|
+
min: minVertices,
|
309
|
+
max: maxVertices
|
310
|
+
};
|
311
|
+
} // Computes the initial simplex assigning to its faces all the points
|
312
|
+
// that are candidates to form part of the hull
|
376
313
|
|
377
|
-
for (i = 0; i < 3; i++) {
|
378
|
-
j = (i + 1) % 3; // join face[ i ] i > 0, with the first face
|
379
314
|
|
380
|
-
|
315
|
+
computeInitialHull() {
|
316
|
+
const vertices = this.vertices;
|
317
|
+
const extremes = this.computeExtremes();
|
318
|
+
const min = extremes.min;
|
319
|
+
const max = extremes.max; // 1. Find the two vertices 'v0' and 'v1' with the greatest 1d separation
|
320
|
+
// (max.x - min.x)
|
321
|
+
// (max.y - min.y)
|
322
|
+
// (max.z - min.z)
|
381
323
|
|
382
|
-
|
383
|
-
|
384
|
-
} else {
|
385
|
-
// the face is able to see the point so 'plane.normal' is pointing inside the tetrahedron
|
386
|
-
faces.push(Face.create(v0, v2, v1), Face.create(v3, v0, v1), Face.create(v3, v1, v2), Face.create(v3, v2, v0)); // set the twin edge
|
324
|
+
let maxDistance = 0;
|
325
|
+
let index = 0;
|
387
326
|
|
388
|
-
|
389
|
-
|
327
|
+
for (let i = 0; i < 3; i++) {
|
328
|
+
const distance = max[i].point.getComponent(i) - min[i].point.getComponent(i);
|
390
329
|
|
391
|
-
|
330
|
+
if (distance > maxDistance) {
|
331
|
+
maxDistance = distance;
|
332
|
+
index = i;
|
333
|
+
}
|
334
|
+
}
|
392
335
|
|
393
|
-
|
394
|
-
|
395
|
-
|
336
|
+
const v0 = min[index];
|
337
|
+
const v1 = max[index];
|
338
|
+
let v2;
|
339
|
+
let v3; // 2. The next vertex 'v2' is the one farthest to the line formed by 'v0' and 'v1'
|
340
|
+
|
341
|
+
maxDistance = 0;
|
396
342
|
|
343
|
+
_line3.set(v0.point, v1.point);
|
397
344
|
|
398
|
-
|
399
|
-
|
400
|
-
} // initial assignment of vertices to the faces of the tetrahedron
|
345
|
+
for (let i = 0, l = this.vertices.length; i < l; i++) {
|
346
|
+
const vertex = vertices[i];
|
401
347
|
|
348
|
+
if (vertex !== v0 && vertex !== v1) {
|
349
|
+
_line3.closestPointToPoint(vertex.point, true, _closestPoint);
|
402
350
|
|
403
|
-
|
404
|
-
vertex = vertices[i];
|
351
|
+
const distance = _closestPoint.distanceToSquared(vertex.point);
|
405
352
|
|
406
|
-
|
407
|
-
|
408
|
-
|
353
|
+
if (distance > maxDistance) {
|
354
|
+
maxDistance = distance;
|
355
|
+
v2 = vertex;
|
356
|
+
}
|
357
|
+
}
|
358
|
+
} // 3. The next vertex 'v3' is the one farthest to the plane 'v0', 'v1', 'v2'
|
409
359
|
|
410
|
-
for (j = 0; j < 4; j++) {
|
411
|
-
distance = this.faces[j].distanceToPoint(vertex.point);
|
412
360
|
|
413
|
-
|
414
|
-
maxDistance = distance;
|
415
|
-
maxFace = this.faces[j];
|
416
|
-
}
|
417
|
-
}
|
361
|
+
maxDistance = -1;
|
418
362
|
|
419
|
-
|
420
|
-
this.addVertexToFace(vertex, maxFace);
|
421
|
-
}
|
422
|
-
}
|
423
|
-
}
|
363
|
+
_plane.setFromCoplanarPoints(v0.point, v1.point, v2.point);
|
424
364
|
|
425
|
-
|
426
|
-
|
427
|
-
}(),
|
428
|
-
// Removes inactive faces
|
429
|
-
reindexFaces: function () {
|
430
|
-
var activeFaces = [];
|
365
|
+
for (let i = 0, l = this.vertices.length; i < l; i++) {
|
366
|
+
const vertex = vertices[i];
|
431
367
|
|
432
|
-
|
433
|
-
|
368
|
+
if (vertex !== v0 && vertex !== v1 && vertex !== v2) {
|
369
|
+
const distance = Math.abs(_plane.distanceToPoint(vertex.point));
|
434
370
|
|
435
|
-
if (
|
436
|
-
|
371
|
+
if (distance > maxDistance) {
|
372
|
+
maxDistance = distance;
|
373
|
+
v3 = vertex;
|
437
374
|
}
|
438
375
|
}
|
376
|
+
}
|
377
|
+
|
378
|
+
const faces = [];
|
379
|
+
|
380
|
+
if (_plane.distanceToPoint(v3.point) < 0) {
|
381
|
+
// the face is not able to see the point so 'plane.normal' is pointing outside the tetrahedron
|
382
|
+
faces.push(Face.create(v0, v1, v2), Face.create(v3, v1, v0), Face.create(v3, v2, v1), Face.create(v3, v0, v2)); // set the twin edge
|
383
|
+
|
384
|
+
for (let i = 0; i < 3; i++) {
|
385
|
+
const j = (i + 1) % 3; // join face[ i ] i > 0, with the first face
|
386
|
+
|
387
|
+
faces[i + 1].getEdge(2).setTwin(faces[0].getEdge(j)); // join face[ i ] with face[ i + 1 ], 1 <= i <= 3
|
388
|
+
|
389
|
+
faces[i + 1].getEdge(1).setTwin(faces[j + 1].getEdge(0));
|
390
|
+
}
|
391
|
+
} else {
|
392
|
+
// the face is able to see the point so 'plane.normal' is pointing inside the tetrahedron
|
393
|
+
faces.push(Face.create(v0, v2, v1), Face.create(v3, v0, v1), Face.create(v3, v1, v2), Face.create(v3, v2, v0)); // set the twin edge
|
439
394
|
|
440
|
-
|
441
|
-
|
442
|
-
},
|
443
|
-
// Finds the next vertex to create faces with the current hull
|
444
|
-
nextVertexToAdd: function () {
|
445
|
-
// if the 'assigned' list of vertices is empty, no vertices are left. return with 'undefined'
|
446
|
-
if (this.assigned.isEmpty() === false) {
|
447
|
-
var eyeVertex,
|
448
|
-
maxDistance = 0; // grap the first available face and start with the first visible vertex of that face
|
395
|
+
for (let i = 0; i < 3; i++) {
|
396
|
+
const j = (i + 1) % 3; // join face[ i ] i > 0, with the first face
|
449
397
|
|
450
|
-
|
451
|
-
var vertex = eyeFace.outside; // now calculate the farthest vertex that face can see
|
398
|
+
faces[i + 1].getEdge(2).setTwin(faces[0].getEdge((3 - i) % 3)); // join face[ i ] with face[ i + 1 ]
|
452
399
|
|
453
|
-
|
454
|
-
|
400
|
+
faces[i + 1].getEdge(0).setTwin(faces[j + 1].getEdge(1));
|
401
|
+
}
|
402
|
+
} // the initial hull is the tetrahedron
|
403
|
+
|
404
|
+
|
405
|
+
for (let i = 0; i < 4; i++) {
|
406
|
+
this.faces.push(faces[i]);
|
407
|
+
} // initial assignment of vertices to the faces of the tetrahedron
|
408
|
+
|
409
|
+
|
410
|
+
for (let i = 0, l = vertices.length; i < l; i++) {
|
411
|
+
const vertex = vertices[i];
|
412
|
+
|
413
|
+
if (vertex !== v0 && vertex !== v1 && vertex !== v2 && vertex !== v3) {
|
414
|
+
maxDistance = this.tolerance;
|
415
|
+
let maxFace = null;
|
416
|
+
|
417
|
+
for (let j = 0; j < 4; j++) {
|
418
|
+
const distance = this.faces[j].distanceToPoint(vertex.point);
|
455
419
|
|
456
420
|
if (distance > maxDistance) {
|
457
421
|
maxDistance = distance;
|
458
|
-
|
422
|
+
maxFace = this.faces[j];
|
459
423
|
}
|
424
|
+
}
|
460
425
|
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
return eyeVertex;
|
426
|
+
if (maxFace !== null) {
|
427
|
+
this.addVertexToFace(vertex, maxFace);
|
428
|
+
}
|
465
429
|
}
|
466
|
-
}
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
// (actually 'crossEdge.twin' was the edge who called this method recursively)
|
481
|
-
edge = crossEdge.next;
|
430
|
+
}
|
431
|
+
|
432
|
+
return this;
|
433
|
+
} // Removes inactive faces
|
434
|
+
|
435
|
+
|
436
|
+
reindexFaces() {
|
437
|
+
const activeFaces = [];
|
438
|
+
|
439
|
+
for (let i = 0; i < this.faces.length; i++) {
|
440
|
+
const face = this.faces[i];
|
441
|
+
|
442
|
+
if (face.mark === Visible) {
|
443
|
+
activeFaces.push(face);
|
482
444
|
}
|
445
|
+
}
|
446
|
+
|
447
|
+
this.faces = activeFaces;
|
448
|
+
return this;
|
449
|
+
} // Finds the next vertex to create faces with the current hull
|
450
|
+
|
451
|
+
|
452
|
+
nextVertexToAdd() {
|
453
|
+
// if the 'assigned' list of vertices is empty, no vertices are left. return with 'undefined'
|
454
|
+
if (this.assigned.isEmpty() === false) {
|
455
|
+
let eyeVertex,
|
456
|
+
maxDistance = 0; // grap the first available face and start with the first visible vertex of that face
|
457
|
+
|
458
|
+
const eyeFace = this.assigned.first().face;
|
459
|
+
let vertex = eyeFace.outside; // now calculate the farthest vertex that face can see
|
483
460
|
|
484
461
|
do {
|
485
|
-
|
486
|
-
var oppositeFace = twinEdge.face;
|
462
|
+
const distance = eyeFace.distanceToPoint(vertex.point);
|
487
463
|
|
488
|
-
if (
|
489
|
-
|
490
|
-
|
491
|
-
this.computeHorizon(eyePoint, twinEdge, oppositeFace, horizon);
|
492
|
-
} else {
|
493
|
-
// the opposite face can't see the vertex, so this edge is part of the horizon
|
494
|
-
horizon.push(edge);
|
495
|
-
}
|
464
|
+
if (distance > maxDistance) {
|
465
|
+
maxDistance = distance;
|
466
|
+
eyeVertex = vertex;
|
496
467
|
}
|
497
468
|
|
498
|
-
|
499
|
-
} while (
|
500
|
-
|
501
|
-
return
|
502
|
-
}
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
469
|
+
vertex = vertex.next;
|
470
|
+
} while (vertex !== null && vertex.face === eyeFace);
|
471
|
+
|
472
|
+
return eyeVertex;
|
473
|
+
}
|
474
|
+
} // Computes a chain of half edges in CCW order called the 'horizon'.
|
475
|
+
// For an edge to be part of the horizon it must join a face that can see
|
476
|
+
// 'eyePoint' and a face that cannot see 'eyePoint'.
|
477
|
+
|
478
|
+
|
479
|
+
computeHorizon(eyePoint, crossEdge, face, horizon) {
|
480
|
+
// moves face's vertices to the 'unassigned' vertex list
|
481
|
+
this.deleteFaceVertices(face);
|
482
|
+
face.mark = Deleted;
|
483
|
+
let edge;
|
484
|
+
|
485
|
+
if (crossEdge === null) {
|
486
|
+
edge = crossEdge = face.getEdge(0);
|
487
|
+
} else {
|
488
|
+
// start from the next edge since 'crossEdge' was already analyzed
|
489
|
+
// (actually 'crossEdge.twin' was the edge who called this method recursively)
|
490
|
+
edge = crossEdge.next;
|
491
|
+
}
|
492
|
+
|
493
|
+
do {
|
494
|
+
const twinEdge = edge.twin;
|
495
|
+
const oppositeFace = twinEdge.face;
|
496
|
+
|
497
|
+
if (oppositeFace.mark === Visible) {
|
498
|
+
if (oppositeFace.distanceToPoint(eyePoint) > this.tolerance) {
|
499
|
+
// the opposite face can see the vertex, so proceed with next edge
|
500
|
+
this.computeHorizon(eyePoint, twinEdge, oppositeFace, horizon);
|
526
501
|
} else {
|
527
|
-
//
|
528
|
-
|
502
|
+
// the opposite face can't see the vertex, so this edge is part of the horizon
|
503
|
+
horizon.push(edge);
|
529
504
|
}
|
505
|
+
}
|
506
|
+
|
507
|
+
edge = edge.next;
|
508
|
+
} while (edge !== crossEdge);
|
509
|
+
|
510
|
+
return this;
|
511
|
+
} // Creates a face with the vertices 'eyeVertex.point', 'horizonEdge.tail' and 'horizonEdge.head' in CCW order
|
512
|
+
|
513
|
+
|
514
|
+
addAdjoiningFace(eyeVertex, horizonEdge) {
|
515
|
+
// all the half edges are created in ccw order thus the face is always pointing outside the hull
|
516
|
+
const face = Face.create(eyeVertex, horizonEdge.tail(), horizonEdge.head());
|
517
|
+
this.faces.push(face); // join face.getEdge( - 1 ) with the horizon's opposite edge face.getEdge( - 1 ) = face.getEdge( 2 )
|
518
|
+
|
519
|
+
face.getEdge(-1).setTwin(horizonEdge.twin);
|
520
|
+
return face.getEdge(0); // the half edge whose vertex is the eyeVertex
|
521
|
+
} // Adds 'horizon.length' faces to the hull, each face will be linked with the
|
522
|
+
// horizon opposite face and the face on the left/right
|
523
|
+
|
530
524
|
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
this.addNewFaces(eyeVertex, horizon); // reassign 'unassigned' vertices to the new faces
|
547
|
-
|
548
|
-
this.resolveUnassignedPoints(this.newFaces);
|
549
|
-
return this;
|
550
|
-
},
|
551
|
-
cleanup: function () {
|
552
|
-
this.assigned.clear();
|
553
|
-
this.unassigned.clear();
|
554
|
-
this.newFaces = [];
|
555
|
-
return this;
|
556
|
-
},
|
557
|
-
compute: function () {
|
558
|
-
var vertex;
|
559
|
-
this.computeInitialHull(); // add all available vertices gradually to the hull
|
560
|
-
|
561
|
-
while ((vertex = this.nextVertexToAdd()) !== undefined) {
|
562
|
-
this.addVertexToHull(vertex);
|
525
|
+
addNewFaces(eyeVertex, horizon) {
|
526
|
+
this.newFaces = [];
|
527
|
+
let firstSideEdge = null;
|
528
|
+
let previousSideEdge = null;
|
529
|
+
|
530
|
+
for (let i = 0; i < horizon.length; i++) {
|
531
|
+
const horizonEdge = horizon[i]; // returns the right side edge
|
532
|
+
|
533
|
+
const sideEdge = this.addAdjoiningFace(eyeVertex, horizonEdge);
|
534
|
+
|
535
|
+
if (firstSideEdge === null) {
|
536
|
+
firstSideEdge = sideEdge;
|
537
|
+
} else {
|
538
|
+
// joins face.getEdge( 1 ) with previousFace.getEdge( 0 )
|
539
|
+
sideEdge.next.setTwin(previousSideEdge);
|
563
540
|
}
|
564
541
|
|
565
|
-
this.
|
566
|
-
|
567
|
-
|
542
|
+
this.newFaces.push(sideEdge.face);
|
543
|
+
previousSideEdge = sideEdge;
|
544
|
+
} // perform final join of new faces
|
545
|
+
|
546
|
+
|
547
|
+
firstSideEdge.next.setTwin(previousSideEdge);
|
548
|
+
return this;
|
549
|
+
} // Adds a vertex to the hull
|
550
|
+
|
551
|
+
|
552
|
+
addVertexToHull(eyeVertex) {
|
553
|
+
const horizon = [];
|
554
|
+
this.unassigned.clear(); // remove 'eyeVertex' from 'eyeVertex.face' so that it can't be added to the 'unassigned' vertex list
|
555
|
+
|
556
|
+
this.removeVertexFromFace(eyeVertex, eyeVertex.face);
|
557
|
+
this.computeHorizon(eyeVertex.point, null, eyeVertex.face, horizon);
|
558
|
+
this.addNewFaces(eyeVertex, horizon); // reassign 'unassigned' vertices to the new faces
|
559
|
+
|
560
|
+
this.resolveUnassignedPoints(this.newFaces);
|
561
|
+
return this;
|
562
|
+
}
|
563
|
+
|
564
|
+
cleanup() {
|
565
|
+
this.assigned.clear();
|
566
|
+
this.unassigned.clear();
|
567
|
+
this.newFaces = [];
|
568
|
+
return this;
|
569
|
+
}
|
570
|
+
|
571
|
+
compute() {
|
572
|
+
let vertex;
|
573
|
+
this.computeInitialHull(); // add all available vertices gradually to the hull
|
574
|
+
|
575
|
+
while ((vertex = this.nextVertexToAdd()) !== undefined) {
|
576
|
+
this.addVertexToHull(vertex);
|
568
577
|
}
|
569
|
-
}); //
|
570
578
|
|
571
|
-
|
579
|
+
this.reindexFaces();
|
580
|
+
this.cleanup();
|
581
|
+
return this;
|
582
|
+
}
|
583
|
+
|
584
|
+
} //
|
585
|
+
|
586
|
+
|
587
|
+
class Face {
|
588
|
+
constructor() {
|
572
589
|
this.normal = new Vector3();
|
573
590
|
this.midpoint = new Vector3();
|
574
591
|
this.area = 0;
|
@@ -580,58 +597,61 @@ var ConvexHull = function () {
|
|
580
597
|
this.edge = null;
|
581
598
|
}
|
582
599
|
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
var e2 = new HalfEdge(c, face); // join edges
|
600
|
+
static create(a, b, c) {
|
601
|
+
const face = new Face();
|
602
|
+
const e0 = new HalfEdge(a, face);
|
603
|
+
const e1 = new HalfEdge(b, face);
|
604
|
+
const e2 = new HalfEdge(c, face); // join edges
|
589
605
|
|
590
|
-
|
591
|
-
|
592
|
-
|
606
|
+
e0.next = e2.prev = e1;
|
607
|
+
e1.next = e0.prev = e2;
|
608
|
+
e2.next = e1.prev = e0; // main half edge reference
|
593
609
|
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
});
|
598
|
-
Object.assign(Face.prototype, {
|
599
|
-
getEdge: function (i) {
|
600
|
-
var edge = this.edge;
|
601
|
-
|
602
|
-
while (i > 0) {
|
603
|
-
edge = edge.next;
|
604
|
-
i--;
|
605
|
-
}
|
610
|
+
face.edge = e0;
|
611
|
+
return face.compute();
|
612
|
+
}
|
606
613
|
|
607
|
-
|
608
|
-
|
609
|
-
i++;
|
610
|
-
}
|
614
|
+
getEdge(i) {
|
615
|
+
let edge = this.edge;
|
611
616
|
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
var triangle;
|
616
|
-
return function compute() {
|
617
|
-
if (triangle === undefined) triangle = new Triangle();
|
618
|
-
var a = this.edge.tail();
|
619
|
-
var b = this.edge.head();
|
620
|
-
var c = this.edge.next.head();
|
621
|
-
triangle.set(a.point, b.point, c.point);
|
622
|
-
triangle.getNormal(this.normal);
|
623
|
-
triangle.getMidpoint(this.midpoint);
|
624
|
-
this.area = triangle.getArea();
|
625
|
-
this.constant = this.normal.dot(this.midpoint);
|
626
|
-
return this;
|
627
|
-
};
|
628
|
-
}(),
|
629
|
-
distanceToPoint: function (point) {
|
630
|
-
return this.normal.dot(point) - this.constant;
|
617
|
+
while (i > 0) {
|
618
|
+
edge = edge.next;
|
619
|
+
i--;
|
631
620
|
}
|
632
|
-
}); // Entity for a Doubly-Connected Edge List (DCEL).
|
633
621
|
|
634
|
-
|
622
|
+
while (i < 0) {
|
623
|
+
edge = edge.prev;
|
624
|
+
i++;
|
625
|
+
}
|
626
|
+
|
627
|
+
return edge;
|
628
|
+
}
|
629
|
+
|
630
|
+
compute() {
|
631
|
+
const a = this.edge.tail();
|
632
|
+
const b = this.edge.head();
|
633
|
+
const c = this.edge.next.head();
|
634
|
+
|
635
|
+
_triangle.set(a.point, b.point, c.point);
|
636
|
+
|
637
|
+
_triangle.getNormal(this.normal);
|
638
|
+
|
639
|
+
_triangle.getMidpoint(this.midpoint);
|
640
|
+
|
641
|
+
this.area = _triangle.getArea();
|
642
|
+
this.constant = this.normal.dot(this.midpoint);
|
643
|
+
return this;
|
644
|
+
}
|
645
|
+
|
646
|
+
distanceToPoint(point) {
|
647
|
+
return this.normal.dot(point) - this.constant;
|
648
|
+
}
|
649
|
+
|
650
|
+
} // Entity for a Doubly-Connected Edge List (DCEL).
|
651
|
+
|
652
|
+
|
653
|
+
class HalfEdge {
|
654
|
+
constructor(vertex, face) {
|
635
655
|
this.vertex = vertex;
|
636
656
|
this.prev = null;
|
637
657
|
this.next = null;
|
@@ -639,160 +659,176 @@ var ConvexHull = function () {
|
|
639
659
|
this.face = face;
|
640
660
|
}
|
641
661
|
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
},
|
646
|
-
tail: function () {
|
647
|
-
return this.prev ? this.prev.vertex : null;
|
648
|
-
},
|
649
|
-
length: function () {
|
650
|
-
var head = this.head();
|
651
|
-
var tail = this.tail();
|
652
|
-
|
653
|
-
if (tail !== null) {
|
654
|
-
return tail.point.distanceTo(head.point);
|
655
|
-
}
|
662
|
+
head() {
|
663
|
+
return this.vertex;
|
664
|
+
}
|
656
665
|
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
var head = this.head();
|
661
|
-
var tail = this.tail();
|
666
|
+
tail() {
|
667
|
+
return this.prev ? this.prev.vertex : null;
|
668
|
+
}
|
662
669
|
|
663
|
-
|
664
|
-
|
665
|
-
|
670
|
+
length() {
|
671
|
+
const head = this.head();
|
672
|
+
const tail = this.tail();
|
666
673
|
|
667
|
-
|
668
|
-
|
669
|
-
setTwin: function (edge) {
|
670
|
-
this.twin = edge;
|
671
|
-
edge.twin = this;
|
672
|
-
return this;
|
674
|
+
if (tail !== null) {
|
675
|
+
return tail.point.distanceTo(head.point);
|
673
676
|
}
|
674
|
-
}); // A vertex as a double linked list node.
|
675
677
|
|
676
|
-
|
678
|
+
return -1;
|
679
|
+
}
|
680
|
+
|
681
|
+
lengthSquared() {
|
682
|
+
const head = this.head();
|
683
|
+
const tail = this.tail();
|
684
|
+
|
685
|
+
if (tail !== null) {
|
686
|
+
return tail.point.distanceToSquared(head.point);
|
687
|
+
}
|
688
|
+
|
689
|
+
return -1;
|
690
|
+
}
|
691
|
+
|
692
|
+
setTwin(edge) {
|
693
|
+
this.twin = edge;
|
694
|
+
edge.twin = this;
|
695
|
+
return this;
|
696
|
+
}
|
697
|
+
|
698
|
+
} // A vertex as a double linked list node.
|
699
|
+
|
700
|
+
|
701
|
+
class VertexNode {
|
702
|
+
constructor(point) {
|
677
703
|
this.point = point;
|
678
704
|
this.prev = null;
|
679
705
|
this.next = null;
|
680
706
|
this.face = null; // the face that is able to see this vertex
|
681
|
-
}
|
707
|
+
}
|
708
|
+
|
709
|
+
} // A double linked list that contains vertex nodes.
|
682
710
|
|
683
711
|
|
684
|
-
|
712
|
+
class VertexList {
|
713
|
+
constructor() {
|
685
714
|
this.head = null;
|
686
715
|
this.tail = null;
|
687
716
|
}
|
688
717
|
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
},
|
693
|
-
last: function () {
|
694
|
-
return this.tail;
|
695
|
-
},
|
696
|
-
clear: function () {
|
697
|
-
this.head = this.tail = null;
|
698
|
-
return this;
|
699
|
-
},
|
700
|
-
// Inserts a vertex before the target vertex
|
701
|
-
insertBefore: function (target, vertex) {
|
702
|
-
vertex.prev = target.prev;
|
703
|
-
vertex.next = target;
|
704
|
-
|
705
|
-
if (vertex.prev === null) {
|
706
|
-
this.head = vertex;
|
707
|
-
} else {
|
708
|
-
vertex.prev.next = vertex;
|
709
|
-
}
|
718
|
+
first() {
|
719
|
+
return this.head;
|
720
|
+
}
|
710
721
|
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
// Inserts a vertex after the target vertex
|
715
|
-
insertAfter: function (target, vertex) {
|
716
|
-
vertex.prev = target;
|
717
|
-
vertex.next = target.next;
|
722
|
+
last() {
|
723
|
+
return this.tail;
|
724
|
+
}
|
718
725
|
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
}
|
726
|
+
clear() {
|
727
|
+
this.head = this.tail = null;
|
728
|
+
return this;
|
729
|
+
} // Inserts a vertex before the target vertex
|
724
730
|
|
725
|
-
target.next = vertex;
|
726
|
-
return this;
|
727
|
-
},
|
728
|
-
// Appends a vertex to the end of the linked list
|
729
|
-
append: function (vertex) {
|
730
|
-
if (this.head === null) {
|
731
|
-
this.head = vertex;
|
732
|
-
} else {
|
733
|
-
this.tail.next = vertex;
|
734
|
-
}
|
735
731
|
|
736
|
-
|
737
|
-
|
732
|
+
insertBefore(target, vertex) {
|
733
|
+
vertex.prev = target.prev;
|
734
|
+
vertex.next = target;
|
738
735
|
|
739
|
-
|
740
|
-
|
741
|
-
}
|
742
|
-
|
743
|
-
|
744
|
-
if (this.head === null) {
|
745
|
-
this.head = vertex;
|
746
|
-
} else {
|
747
|
-
this.tail.next = vertex;
|
748
|
-
}
|
736
|
+
if (vertex.prev === null) {
|
737
|
+
this.head = vertex;
|
738
|
+
} else {
|
739
|
+
vertex.prev.next = vertex;
|
740
|
+
}
|
749
741
|
|
750
|
-
|
742
|
+
target.prev = vertex;
|
743
|
+
return this;
|
744
|
+
} // Inserts a vertex after the target vertex
|
751
745
|
|
752
|
-
while (vertex.next !== null) {
|
753
|
-
vertex = vertex.next;
|
754
|
-
}
|
755
746
|
|
747
|
+
insertAfter(target, vertex) {
|
748
|
+
vertex.prev = target;
|
749
|
+
vertex.next = target.next;
|
750
|
+
|
751
|
+
if (vertex.next === null) {
|
756
752
|
this.tail = vertex;
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
remove: function (vertex) {
|
761
|
-
if (vertex.prev === null) {
|
762
|
-
this.head = vertex.next;
|
763
|
-
} else {
|
764
|
-
vertex.prev.next = vertex.next;
|
765
|
-
}
|
753
|
+
} else {
|
754
|
+
vertex.next.prev = vertex;
|
755
|
+
}
|
766
756
|
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
vertex.next.prev = vertex.prev;
|
771
|
-
}
|
757
|
+
target.next = vertex;
|
758
|
+
return this;
|
759
|
+
} // Appends a vertex to the end of the linked list
|
772
760
|
|
773
|
-
return this;
|
774
|
-
},
|
775
|
-
// Removes a list of vertices whose 'head' is 'a' and whose 'tail' is b
|
776
|
-
removeSubList: function (a, b) {
|
777
|
-
if (a.prev === null) {
|
778
|
-
this.head = b.next;
|
779
|
-
} else {
|
780
|
-
a.prev.next = b.next;
|
781
|
-
}
|
782
761
|
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
762
|
+
append(vertex) {
|
763
|
+
if (this.head === null) {
|
764
|
+
this.head = vertex;
|
765
|
+
} else {
|
766
|
+
this.tail.next = vertex;
|
767
|
+
}
|
768
|
+
|
769
|
+
vertex.prev = this.tail;
|
770
|
+
vertex.next = null; // the tail has no subsequent vertex
|
771
|
+
|
772
|
+
this.tail = vertex;
|
773
|
+
return this;
|
774
|
+
} // Appends a chain of vertices where 'vertex' is the head.
|
788
775
|
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
776
|
+
|
777
|
+
appendChain(vertex) {
|
778
|
+
if (this.head === null) {
|
779
|
+
this.head = vertex;
|
780
|
+
} else {
|
781
|
+
this.tail.next = vertex;
|
782
|
+
}
|
783
|
+
|
784
|
+
vertex.prev = this.tail; // ensure that the 'tail' reference points to the last vertex of the chain
|
785
|
+
|
786
|
+
while (vertex.next !== null) {
|
787
|
+
vertex = vertex.next;
|
793
788
|
}
|
794
|
-
|
795
|
-
|
796
|
-
|
789
|
+
|
790
|
+
this.tail = vertex;
|
791
|
+
return this;
|
792
|
+
} // Removes a vertex from the linked list
|
793
|
+
|
794
|
+
|
795
|
+
remove(vertex) {
|
796
|
+
if (vertex.prev === null) {
|
797
|
+
this.head = vertex.next;
|
798
|
+
} else {
|
799
|
+
vertex.prev.next = vertex.next;
|
800
|
+
}
|
801
|
+
|
802
|
+
if (vertex.next === null) {
|
803
|
+
this.tail = vertex.prev;
|
804
|
+
} else {
|
805
|
+
vertex.next.prev = vertex.prev;
|
806
|
+
}
|
807
|
+
|
808
|
+
return this;
|
809
|
+
} // Removes a list of vertices whose 'head' is 'a' and whose 'tail' is b
|
810
|
+
|
811
|
+
|
812
|
+
removeSubList(a, b) {
|
813
|
+
if (a.prev === null) {
|
814
|
+
this.head = b.next;
|
815
|
+
} else {
|
816
|
+
a.prev.next = b.next;
|
817
|
+
}
|
818
|
+
|
819
|
+
if (b.next === null) {
|
820
|
+
this.tail = a.prev;
|
821
|
+
} else {
|
822
|
+
b.next.prev = a.prev;
|
823
|
+
}
|
824
|
+
|
825
|
+
return this;
|
826
|
+
}
|
827
|
+
|
828
|
+
isEmpty() {
|
829
|
+
return this.head === null;
|
830
|
+
}
|
831
|
+
|
832
|
+
}
|
797
833
|
|
798
834
|
export { ConvexHull };
|