@woosh/meep-engine 2.37.20 → 2.38.2

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.
Files changed (88) hide show
  1. package/core/collection/array/array_get_index_in_range.js +16 -0
  2. package/{engine/navigation/grid → core/collection/heap}/BinaryHeap.js +6 -1
  3. package/{engine/navigation/grid → core/collection/heap}/FastBinaryHeap.js +3 -2
  4. package/{engine/navigation/grid → core/collection/heap}/FastBinaryHeap.spec.js +3 -3
  5. package/core/collection/heap/Uin32Heap.spec.js +59 -0
  6. package/core/collection/heap/Uint32Heap.js +385 -0
  7. package/core/geom/3d/topology/computeTopoMeshVertexDuplicates.js +9 -6
  8. package/core/geom/3d/topology/expandConnectivityByLocality.js +5 -5
  9. package/core/geom/3d/topology/query/query_edge_is_boundary.js +7 -0
  10. package/core/geom/3d/topology/query/query_edge_is_manifold.js +13 -0
  11. package/core/geom/3d/topology/query/query_edge_is_manifold_or_boundary.js +11 -0
  12. package/core/geom/3d/topology/query/query_edge_is_wire.js +13 -0
  13. package/core/geom/3d/topology/query/query_edge_other_vertex.js +20 -0
  14. package/core/geom/3d/topology/query/query_edge_share_vert.js +9 -0
  15. package/core/geom/3d/topology/query/query_face_get_other_edges.js +39 -0
  16. package/core/geom/3d/topology/query/query_face_next_vertex.js +19 -0
  17. package/core/geom/3d/topology/query/query_face_prev_vertex.js +18 -0
  18. package/core/geom/3d/topology/query/query_vertex_in_edge.js +14 -0
  19. package/core/geom/3d/topology/query/query_vertex_pair_share_face.js +24 -0
  20. package/core/geom/3d/topology/query/query_vertices_in_edge.js +19 -0
  21. package/core/geom/3d/topology/simplify/collapseEdge.spec.js +3 -5
  22. package/core/geom/3d/topology/simplify/collapse_all_degenerate_edges.js +8 -10
  23. package/core/geom/3d/topology/simplify/compute_face_normal_change_dot_product.js +12 -2
  24. package/core/geom/3d/topology/simplify/decimate_edge_collapse_snap.js +277 -0
  25. package/core/geom/3d/topology/simplify/edge_collapse_quadratic.js +126 -0
  26. package/core/geom/3d/topology/simplify/prototypeMeshSimplification.js +502 -0
  27. package/core/geom/3d/topology/simplify/quadratic/Quadratic3.js +37 -5
  28. package/core/geom/3d/topology/simplify/quadratic/build_vertex_quadratics.js +86 -1
  29. package/core/geom/3d/topology/simplify/simplifyTopoMesh.js +4 -4
  30. package/core/geom/3d/topology/simplify/simplifyTopoMesh2.js +119 -0
  31. package/core/geom/3d/topology/simplify/tm_edge_collapse_is_degenerate_flip.js +81 -0
  32. package/core/geom/3d/topology/{TopoEdge.js → struct/TopoEdge.js} +47 -20
  33. package/core/geom/3d/topology/{TopoEdge.spec.js → struct/TopoEdge.spec.js} +0 -0
  34. package/core/geom/3d/topology/{TopoMesh.js → struct/TopoMesh.js} +20 -41
  35. package/core/geom/3d/topology/{TopoTriangle.js → struct/TopoTriangle.js} +15 -25
  36. package/core/geom/3d/topology/{TopoVertex.js → struct/TopoVertex.js} +21 -4
  37. package/core/geom/3d/topology/{TopoVertex.spec.js → struct/TopoVertex.spec.js} +0 -0
  38. package/core/geom/3d/topology/tm_edge_kill.js +24 -0
  39. package/core/geom/3d/topology/tm_edge_splice.js +42 -0
  40. package/core/geom/3d/topology/tm_face_area.js +18 -0
  41. package/core/geom/3d/topology/tm_face_kill.js +12 -0
  42. package/core/geom/3d/topology/tm_face_normal.js +14 -0
  43. package/core/geom/3d/topology/tm_kill_only_edge.js +35 -0
  44. package/core/geom/3d/topology/tm_kill_only_face.js +12 -0
  45. package/core/geom/3d/topology/tm_kill_only_vert.js +14 -0
  46. package/core/geom/3d/topology/tm_vert_kill.js +19 -0
  47. package/core/geom/3d/topology/tm_vert_splice.js +64 -0
  48. package/core/geom/3d/topology/tm_vertex_compute_normal.js +42 -0
  49. package/core/geom/3d/topology/topoMeshToBufferGeometry.js +18 -4
  50. package/core/geom/3d/topology/weld_duplicate_vertices.js +63 -0
  51. package/core/geom/packing/MaxRectangles.js +1 -1
  52. package/core/graph/cluster_mesh_metis.js +16 -0
  53. package/core/graph/coarsen_graph.js +1 -1
  54. package/core/graph/graph_k_means_cluster.js +1 -1
  55. package/core/math/random/seededRandom.js +2 -31
  56. package/core/math/random/seededRandom_Mulberry32.js +31 -0
  57. package/core/math/random/seededRandom_sine.js +33 -0
  58. package/editor/view/node-graph/NodeGraphView.js +2 -2
  59. package/editor/view/node-graph/NodeView.js +7 -9
  60. package/engine/ecs/parent/ParentEntitySystem.js +57 -0
  61. package/engine/ecs/terrain/util/tensionOptimizeUV.js +1 -1
  62. package/engine/graphics/ecs/mesh-v2/sg_hierarchy_compute_bounding_box_via_parent_entity.d.ts +4 -0
  63. package/engine/graphics/ecs/mesh-v2/sg_hierarchy_compute_bounding_box_via_parent_entity.js +31 -0
  64. package/engine/graphics/ecs/sprite/prototypeSpriteSystem.js +4 -0
  65. package/engine/graphics/micron/build/PatchRepresentation.js +7 -3
  66. package/engine/graphics/micron/build/buildMicronGeometryFromBufferGeometry.js +14 -8
  67. package/engine/graphics/micron/build/clustering/build_clustering_2.js +1 -1
  68. package/engine/graphics/micron/build/clustering/build_leaf_patches.js +2 -2
  69. package/engine/graphics/micron/build/clustering/build_leaf_patches_metis.js +1 -1
  70. package/engine/graphics/micron/build/hierarchy/buildAbstractPatchHierarchy.js +21 -3
  71. package/engine/graphics/micron/build/hierarchy/merge_patches.js +96 -43
  72. package/engine/graphics/micron/build/hierarchy/qvdr_build_simplified_clusters.js +11 -5
  73. package/engine/graphics/micron/format/VirtualGeometry.js +46 -3
  74. package/engine/graphics/micron/format/micron_build_proxy_geometry.js +4 -2
  75. package/engine/graphics/micron/prototypeVirtualGeometry.js +45 -8
  76. package/engine/graphics/micron/render/instanced/shader/shader_rewrite_standard.js +2 -2
  77. package/engine/graphics/micron/render/refinement/get_geometry_patch_cut.js +15 -3
  78. package/engine/graphics/micron/simplifyGeometry.js +1 -1
  79. package/engine/input/devices/PointerDevice.d.ts +1 -1
  80. package/engine/input/devices/PointerDevice.js +17 -2
  81. package/engine/navigation/grid/AStar.js +1 -1
  82. package/engine/navigation/grid/GridField.js +3 -2
  83. package/engine/ui/DraggableAspect.js +2 -2
  84. package/generation/grid/generation/discrete/GridTaskConnectRooms.js +1 -1
  85. package/generation/grid/generation/discrete/layer/GridTaskBuildSourceDistanceMap.js +1 -1
  86. package/generation/grid/generation/discrete/layer/GridTaskDistanceToMarkers.js +1 -1
  87. package/generation/grid/generation/road/GridTaskGenerateRoads.js +1 -1
  88. package/package.json +1 -1
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @template T
3
+ * @param {T[]} array
4
+ * @param {T} needle
5
+ * @param {number} min_index
6
+ * @param {number} max_index
7
+ */
8
+ export function array_get_index_in_range(array, needle, min_index, max_index) {
9
+ for (let i = min_index; i <= max_index; i++) {
10
+ if (array[i] === needle) {
11
+ return i;
12
+ }
13
+ }
14
+
15
+ return -1;
16
+ }
@@ -1,3 +1,8 @@
1
+ /**
2
+ * @deprecated use {@link FastBinaryHeap} instead
3
+ * @param scoreFunction
4
+ * @constructor
5
+ */
1
6
  function BinaryHeap(scoreFunction) {
2
7
  this.content = [];
3
8
  this.hash = [];
@@ -99,7 +104,7 @@ BinaryHeap.prototype = {
99
104
  element = content[n],
100
105
  elemScore = scoreFunction(element);
101
106
 
102
- for (;;) {
107
+ for (; ;) {
103
108
  // Compute the indices of the child elements.
104
109
  const child2N = (n + 1) << 1,
105
110
  child1N = child2N - 1;
@@ -1,4 +1,4 @@
1
- import { assert } from "../../../core/assert.js";
1
+ import { assert } from "../../assert.js";
2
2
 
3
3
  /**
4
4
  * Min-Heap implementation with a score function. The data structure is a binary heap where elements are removed in order defined by scoring function
@@ -6,7 +6,8 @@ import { assert } from "../../../core/assert.js";
6
6
  */
7
7
  class BinaryHeap {
8
8
  /**
9
- * @param {(T)=>number} scoreFunction
9
+ * @template T
10
+ * @param {function(el:T):number} scoreFunction
10
11
  * @constructor
11
12
  */
12
13
  constructor(scoreFunction) {
@@ -1,7 +1,7 @@
1
- import { seededRandom } from "../../../core/math/random/seededRandom.js";
1
+ import { seededRandom } from "../../math/random/seededRandom.js";
2
2
  import BinaryHeap from "./FastBinaryHeap.js";
3
- import { passThrough, returnZero } from "../../../core/function/Functions.js";
4
- import { randomIntegerBetween } from "../../../core/math/random/randomIntegerBetween.js";
3
+ import { passThrough, returnZero } from "../../function/Functions.js";
4
+ import { randomIntegerBetween } from "../../math/random/randomIntegerBetween.js";
5
5
 
6
6
  test("constructor doesn't throw", () => {
7
7
  new BinaryHeap(returnZero);
@@ -0,0 +1,59 @@
1
+ import { Uint32Heap } from "./Uint32Heap.js";
2
+
3
+ test('initial capacity setting via constructor', () => {
4
+ const heap = new Uint32Heap(3);
5
+
6
+ expect(heap.capacity).toBe(3);
7
+ });
8
+
9
+ test('heap capacity grows as needed', () => {
10
+ const heap = new Uint32Heap(0);
11
+
12
+ heap.insert(1, 1);
13
+
14
+ expect(heap.capacity).toBeGreaterThanOrEqual(1);
15
+ });
16
+
17
+ test('initial heap size is 0', () => {
18
+ const heap = new Uint32Heap();
19
+
20
+ expect(heap.size).toBe(0);
21
+ });
22
+
23
+ test('insert operation increases size by 1', () => {
24
+ const heap = new Uint32Heap();
25
+
26
+ const s0 = heap.size;
27
+
28
+ heap.insert(0, 0);
29
+
30
+ expect(heap.size).toBe(s0 + 1);
31
+ });
32
+
33
+ test('pop operation reduces size by 1', () => {
34
+ const heap = new Uint32Heap();
35
+
36
+ heap.insert(0, 0);
37
+
38
+ const s0 = heap.size;
39
+
40
+ heap.pop_min();
41
+
42
+ expect(heap.size).toBe(s0 - 1);
43
+ });
44
+
45
+ test('insert 5 out of order, retrieve in order', () => {
46
+ const heap = new Uint32Heap();
47
+
48
+ heap.insert(3, 4);
49
+ heap.insert(5, 5);
50
+ heap.insert(7, 2);
51
+ heap.insert(11, 3);
52
+ heap.insert(13, 1);
53
+
54
+ expect(heap.pop_min()).toBe(13);
55
+ expect(heap.pop_min()).toBe(7);
56
+ expect(heap.pop_min()).toBe(11);
57
+ expect(heap.pop_min()).toBe(3);
58
+ expect(heap.pop_min()).toBe(5);
59
+ });
@@ -0,0 +1,385 @@
1
+ import { assert } from "../../assert.js";
2
+ import { max2 } from "../../math/max2.js";
3
+
4
+ const UINT32_MAX = 4294967295;
5
+
6
+ const DEFAULT_CAPACITY = 64;
7
+ const ELEMENT_BYTE_SIZE = 8;
8
+
9
+ /**
10
+ * % to increase capacity by when growing
11
+ * NOTE: Must be greater than 1
12
+ * @type {number}
13
+ */
14
+ const RESIZE_GROW_FACTOR = 1.2;
15
+ /**
16
+ * Minimum number of elements to expand the size by when growing
17
+ * NOTE: Must be an integer
18
+ * NOTE: Must be greater than 0
19
+ * @type {number}
20
+ */
21
+ const RESIZE_GROW_MIN_COUNT = 16;
22
+
23
+ /**
24
+ *
25
+ * @param {number} i
26
+ * @returns {number}
27
+ */
28
+ function HEAP_PARENT(i) {
29
+ return ((i) - 1) >> 1;
30
+ }
31
+
32
+ /**
33
+ *
34
+ * @param {number} i
35
+ * @returns {number}
36
+ */
37
+ function HEAP_LEFT(i) {
38
+ return ((i) << 1) + 1;
39
+ }
40
+
41
+ /**
42
+ *
43
+ * @param {number} i
44
+ * @returns {number}
45
+ */
46
+ function HEAP_RIGHT(i) {
47
+ return ((i) << 1) + 2;
48
+ }
49
+
50
+ /**
51
+ * Binary Heap implementation that stores uin32 ID along with a floating point score value
52
+ * Inspired by Blender's heap implementation found here: https://github.com/blender/blender/blob/594f47ecd2d5367ca936cf6fc6ec8168c2b360d0/source/blender/blenlib/intern/BLI_heap.c
53
+ */
54
+ export class Uint32Heap {
55
+ /**
56
+ *
57
+ * @param {number} [capacity] Can supply initial capacity, heap will still grow when necessary. This allows to prevent needless re-allocations when max heap size is known in advance
58
+ */
59
+ constructor(capacity = DEFAULT_CAPACITY) {
60
+ assert.isNonNegativeInteger(capacity, 'capacity');
61
+
62
+ this.__data_buffer = new ArrayBuffer(capacity * ELEMENT_BYTE_SIZE);
63
+
64
+ /**
65
+ * Used to access stored IDs
66
+ * @type {Uint32Array}
67
+ * @private
68
+ */
69
+ this.__data_uint32 = new Uint32Array(this.__data_buffer);
70
+
71
+ /**
72
+ * Used to access stored Score values
73
+ * @type {Float32Array}
74
+ * @private
75
+ */
76
+ this.__data_float32 = new Float32Array(this.__data_buffer);
77
+
78
+ this.__capacity = capacity;
79
+ this.__size = 0;
80
+ }
81
+
82
+ /**
83
+ *
84
+ * @private
85
+ */
86
+ __capacity_grow() {
87
+ const old_capacity = this.__capacity;
88
+
89
+ const new_capacity = max2(
90
+ old_capacity * RESIZE_GROW_FACTOR,
91
+ old_capacity + RESIZE_GROW_MIN_COUNT
92
+ );
93
+
94
+ assert.greaterThan(new_capacity, old_capacity, 'invalid growth');
95
+
96
+ const new_buffer = new ArrayBuffer(new_capacity * ELEMENT_BYTE_SIZE);
97
+ const new_uint32 = new Uint32Array(new_buffer);
98
+
99
+ // copy old data into new container
100
+ new_uint32.set(this.__data_uint32);
101
+
102
+ this.__data_buffer = new_buffer;
103
+ this.__data_uint32 = new_uint32;
104
+ this.__data_float32 = new Float32Array(new_buffer);
105
+
106
+ // update capacity
107
+ this.__capacity = new_capacity;
108
+ }
109
+
110
+ /**
111
+ * @private
112
+ * @param {number} a index of an element
113
+ * @param {number} b index of an element
114
+ * @returns {boolean}
115
+ */
116
+ compare(a, b) {
117
+ const float32 = this.__data_float32;
118
+
119
+ return float32[a * 2] < float32[b * 2];
120
+ }
121
+
122
+ /**
123
+ * Swap two elements
124
+ * @private
125
+ * @param {number} i element index
126
+ * @param {number} j element index
127
+ */
128
+ swap(i, j) {
129
+ const i2 = i * 2;
130
+ const j2 = j * 2;
131
+
132
+ const uint32 = this.__data_uint32;
133
+
134
+ const mem_0 = uint32[i2];
135
+ uint32[i2] = uint32[j2];
136
+ uint32[j2] = mem_0;
137
+
138
+ const mem_1 = uint32[i2 + 1];
139
+ uint32[i2 + 1] = uint32[j2 + 1];
140
+ uint32[j2 + 1] = mem_1;
141
+ }
142
+
143
+ /**
144
+ * @private
145
+ * @param {number} index
146
+ */
147
+ heap_down(index) {
148
+ let i = index;
149
+
150
+ // size does not change, cache it for performance
151
+ const size = this.__size;
152
+
153
+ while (true) {
154
+ const l = ((i) << 1) + 1;
155
+ const r = l + 1;
156
+
157
+ let smallest = i;
158
+
159
+ if (l < size && this.compare(l, smallest)) {
160
+ smallest = l;
161
+ }
162
+
163
+ if (r < size && this.compare(r, smallest)) {
164
+ smallest = r;
165
+ }
166
+
167
+ if (smallest === i) {
168
+ break;
169
+ }
170
+
171
+ this.swap(i, smallest);
172
+
173
+ i = smallest;
174
+ }
175
+
176
+ }
177
+
178
+ /**
179
+ * Bubble up given element into its correct position
180
+ * @private
181
+ * @param {number} index
182
+ */
183
+ heap_up(index) {
184
+ let i = index;
185
+
186
+ while (i > 0) {
187
+ // get parent
188
+ const p = ((i) - 1) >> 1;
189
+
190
+ if (this.compare(p, i)) {
191
+ break;
192
+ }
193
+
194
+ this.swap(p, i);
195
+
196
+ i = p;
197
+ }
198
+ }
199
+
200
+ /**
201
+ *
202
+ * @returns {number}
203
+ */
204
+ get size() {
205
+ return this.__size;
206
+ }
207
+
208
+ /**
209
+ *
210
+ * @returns {number}
211
+ */
212
+ get capacity() {
213
+ return this.__capacity;
214
+ }
215
+
216
+ /**
217
+ * Node with the lowest score
218
+ * @returns {number}
219
+ */
220
+ get top_id() {
221
+ return this.__data_uint32[1];
222
+ }
223
+
224
+ /**
225
+ *
226
+ * @returns {boolean}
227
+ */
228
+ is_empty() {
229
+ return this.__size === 0;
230
+ }
231
+
232
+ pop_min() {
233
+ assert.greaterThan(this.__size, 0, 'heap is empty');
234
+
235
+ const result = this.top_id;
236
+
237
+ this.__size--;
238
+
239
+ if (this.__size > 0) {
240
+ // move top element to the bottom
241
+ this.swap(0, this.__size);
242
+
243
+ // rebalance
244
+ this.heap_down(0);
245
+ }
246
+
247
+ return result;
248
+ }
249
+
250
+ /**
251
+ *
252
+ * @param {number} id
253
+ * @returns {number}
254
+ */
255
+ find_index_by_id(id) {
256
+ const n = this.__size
257
+ const n2 = n * 2;
258
+
259
+ const uint32 = this.__data_uint32;
260
+
261
+ for (let address = 1; address < n2; address += 2) {
262
+ const _id = uint32[address];
263
+
264
+ if (_id === id) {
265
+ return (address >>> 1);
266
+ }
267
+ }
268
+
269
+ // not found
270
+ return -1;
271
+ }
272
+
273
+
274
+ /**
275
+ *
276
+ * @param {number} id
277
+ */
278
+ remove(id) {
279
+ const i = this.find_index_by_id(id);
280
+
281
+ if (i !== -1) {
282
+ this.__remove_by_index(i);
283
+ }
284
+ }
285
+
286
+ /**
287
+ *
288
+ * @param {number} index
289
+ */
290
+ __remove_by_index(index) {
291
+ assert.greaterThan(this.__size, 0, 'heap is empty');
292
+
293
+ let i = index;
294
+
295
+ while (i > 0) {
296
+ const p = HEAP_PARENT(i);
297
+ this.swap(p, i);
298
+
299
+ i = p;
300
+ }
301
+
302
+ this.pop_min();
303
+ }
304
+
305
+ /**
306
+ *
307
+ * @param {number} id
308
+ * @param {number} score
309
+ */
310
+ update_score(id, score) {
311
+ const index = this.find_index_by_id(id);
312
+
313
+ if (index === -1) {
314
+ throw new Error('Not found');
315
+ }
316
+
317
+ this.__update_score_by_index(index, score);
318
+ }
319
+
320
+ /**
321
+ *
322
+ * @param {number} index
323
+ * @param {number} score
324
+ */
325
+ __update_score_by_index(index, score) {
326
+
327
+ const float32 = this.__data_float32;
328
+ const index2 = index * 2;
329
+ const existing_score = float32[index2];
330
+
331
+ if (score < existing_score) {
332
+ float32[index2] = score;
333
+ this.heap_up(index);
334
+ } else if (score > existing_score) {
335
+ float32[index2] = score;
336
+ this.heap_down(index);
337
+ }
338
+ }
339
+
340
+
341
+ /**
342
+ *
343
+ * @param {number} id
344
+ * @param {number} score
345
+ */
346
+ insert_or_update(id, score) {
347
+ const i = this.find_index_by_id(id);
348
+
349
+ if (i === -1) {
350
+ this.insert(id, score);
351
+ } else {
352
+ this.__update_score_by_index(i, score);
353
+ }
354
+ }
355
+
356
+ /**
357
+ *
358
+ * @param {number} id
359
+ * @param {number} score
360
+ */
361
+ insert(id, score) {
362
+ assert.isNonNegativeInteger(id, 'value');
363
+ assert.lessThanOrEqual(id, UINT32_MAX - 1, 'must be less than or equal to (2^32 - 2)');
364
+
365
+ if (this.__size >= this.__capacity) {
366
+ // need to re-allocate
367
+ this.__capacity_grow();
368
+ }
369
+
370
+ // insert at the end
371
+ const index = this.__size;
372
+ const address = index * 2;
373
+
374
+ // write data
375
+ this.__data_float32[address] = score;
376
+ this.__data_uint32[address + 1] = id;
377
+
378
+ // record increased size
379
+ this.__size++;
380
+
381
+ this.heap_up(index);
382
+ }
383
+
384
+
385
+ }
@@ -5,11 +5,12 @@ import { mortonEncode_magicbits } from "../morton/mortonEncode_magicbits.js";
5
5
  *
6
6
  * @param {TopoVertex} a
7
7
  * @param {TopoVertex} b
8
+ * @param {number} tolerance
8
9
  */
9
- function isPositionEquivalent(a, b) {
10
- return epsilonEquals(b.x, a.x, EPSILON)
11
- && epsilonEquals(b.y, a.y, EPSILON)
12
- && epsilonEquals(b.z, a.z, EPSILON)
10
+ function isPositionEquivalent(a, b, tolerance) {
11
+ return epsilonEquals(b.x, a.x, tolerance)
12
+ && epsilonEquals(b.y, a.y, tolerance)
13
+ && epsilonEquals(b.z, a.z, tolerance)
13
14
  ;
14
15
  }
15
16
 
@@ -35,8 +36,10 @@ function recordDuplicate(result, a, b) {
35
36
  *
36
37
  * @param {TopoMesh} mesh
37
38
  * @param {AABB3} aabb
39
+ * @param {number} tolerance
40
+ * @returns {Map<TopoVertex, TopoVertex[]>}
38
41
  */
39
- export function computeTopoMeshVertexDuplicates(mesh, aabb) {
42
+ export function computeTopoMeshVertexDuplicates(mesh, aabb, tolerance = EPSILON) {
40
43
  const vertices = mesh.vertices;
41
44
 
42
45
  const n = vertices.length;
@@ -84,7 +87,7 @@ export function computeTopoMeshVertexDuplicates(mesh, aabb) {
84
87
  */
85
88
  const vertex_b = spatial_bucket[j];
86
89
 
87
- if (isPositionEquivalent(vertex_a, vertex_b)) {
90
+ if (isPositionEquivalent(vertex_a, vertex_b, tolerance)) {
88
91
  // same coordinates
89
92
  recordDuplicate(result, vertex_a, vertex_b);
90
93
  recordDuplicate(result, vertex_b, vertex_a);
@@ -76,16 +76,16 @@ export function expandConnectivityByLocality(mesh, aabb) {
76
76
 
77
77
  let j;
78
78
 
79
- for (let topoEdge of edges) {
79
+ for (let edge of edges) {
80
80
 
81
- const duplicate_v0s = vertex_duplicates.get(topoEdge.v0);
81
+ const duplicate_v0s = vertex_duplicates.get(edge.v0);
82
82
 
83
83
  if (duplicate_v0s === undefined) {
84
84
  // no duplicate vertices, edge is unique
85
85
  continue;
86
86
  }
87
87
 
88
- const duplicate_v1s = vertex_duplicates.get(topoEdge.v1);
88
+ const duplicate_v1s = vertex_duplicates.get(edge.v1);
89
89
 
90
90
  if (duplicate_v1s === undefined) {
91
91
  // no duplicate vertices, edge is unique
@@ -95,8 +95,8 @@ export function expandConnectivityByLocality(mesh, aabb) {
95
95
  const n = findCommonDuplicateEdges(duplicate_edges, duplicate_v0s, duplicate_v1s);
96
96
 
97
97
  for (j = 0; j < n; j++) {
98
- const dulicateEdge = duplicate_edges[j];
99
- connectEdges(topoEdge, dulicateEdge);
98
+ const duplicate_edge = duplicate_edges[j];
99
+ connectEdges(edge, duplicate_edge);
100
100
  }
101
101
  }
102
102
 
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @param {TopoEdge} edge
3
+ * @returns {boolean}
4
+ */
5
+ export function query_edge_is_boundary(edge) {
6
+ return edge.faces.length === 1;
7
+ }
@@ -0,0 +1,13 @@
1
+ import { assert } from "../../../../assert.js";
2
+
3
+ /**
4
+ * Tests whether this edge is manifold.
5
+ * A manifold edge has exactly 2 faces attached to it.
6
+ * @param {TopoEdge} edge
7
+ * @returns {boolean}
8
+ */
9
+ export function query_edge_is_manifold(edge) {
10
+ assert.notNull(edge, 'edge');
11
+ return edge.faces.length === 2;
12
+ }
13
+
@@ -0,0 +1,11 @@
1
+ import { query_edge_is_boundary } from "./query_edge_is_boundary.js";
2
+ import { query_edge_is_manifold } from "./query_edge_is_manifold.js";
3
+
4
+ /**
5
+ *
6
+ * @param {TopoEdge} edge
7
+ * @returns {boolean}
8
+ */
9
+ export function query_edge_is_manifold_or_boundary(edge) {
10
+ return query_edge_is_manifold(edge) || query_edge_is_boundary(edge);
11
+ }
@@ -0,0 +1,13 @@
1
+ import { assert } from "../../../../assert.js";
2
+
3
+ /**
4
+ *
5
+ * @param {TopoEdge} edge
6
+ * @returns {boolean}
7
+ */
8
+ export function query_edge_is_wire(edge) {
9
+ assert.notNull(edge, 'edge');
10
+
11
+ const faces = edge.faces;
12
+ return faces.length === 0;
13
+ }
@@ -0,0 +1,20 @@
1
+ import { assert } from "../../../../assert.js";
2
+
3
+ /**
4
+ * Given a vertex, returns other vertex on the edge
5
+ * @param {TopoEdge} edge
6
+ * @param {TopoVertex} v
7
+ * @returns {TopoVertex|null}
8
+ */
9
+ export function query_edge_other_vertex(edge, v) {
10
+ assert.notNull(edge, 'edge');
11
+ assert.notNull(v, 'v');
12
+
13
+ if (edge.v0 === v) {
14
+ return edge.v1;
15
+ } else if (edge.v1 === v) {
16
+ return edge.v0;
17
+ }
18
+
19
+ return null;
20
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ *
3
+ * @param {TopoEdge} a
4
+ * @param {TopoEdge} b
5
+ * @returns {boolean}
6
+ */
7
+ export function query_edge_share_vert(a, b) {
8
+ return a.v0 === b.v0 || a.v0 === b.v1 || a.v1 === b.v0 || a.v1 === b.v1;
9
+ }
@@ -0,0 +1,39 @@
1
+ import { assert } from "../../../../assert.js";
2
+
3
+ /**
4
+ *
5
+ * @param {TopoEdge[]} result
6
+ * @param {number} result_offset
7
+ * @param {TopoTriangle} face
8
+ * @param {TopoEdge} edge
9
+ */
10
+ export function query_face_get_other_edges(result, result_offset, face, edge) {
11
+
12
+ let a, b;
13
+
14
+ const edges = face.edges;
15
+
16
+ if (edges.length < 3) {
17
+ return false;
18
+ }
19
+
20
+ assert.equal(edges.length, 3, `must have 3 edges, instead has ${edges.length}`);
21
+
22
+ if (edges[0] === edge) {
23
+ a = 1;
24
+ b = 2;
25
+ } else if (edges[1] === edge) {
26
+ a = 0;
27
+ b = 2;
28
+ } else if (edges[2] === edge) {
29
+ a = 0;
30
+ b = 1;
31
+ } else {
32
+ throw new Error('Edge not contained in the face');
33
+ }
34
+
35
+ result[result_offset] = edges[a];
36
+ result[result_offset + 1] = edges[b];
37
+
38
+ return true;
39
+ }