@woosh/meep-engine 2.43.22 → 2.43.23

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 (42) hide show
  1. package/core/geom/3d/tetrahedra/delaunay/compute_delaunay_tetrahedral_mesh.js +2 -2
  2. package/core/geom/3d/topology/struct/{BinaryElementPool.js → binary/BinaryElementPool.js} +7 -11
  3. package/core/geom/3d/topology/struct/{BinaryElementPool.spec.js → binary/BinaryElementPool.spec.js} +0 -0
  4. package/core/geom/3d/topology/struct/{BinaryTopology.js → binary/BinaryTopology.js} +123 -6
  5. package/core/geom/3d/topology/struct/{BinaryTopology.spec.js → binary/BinaryTopology.spec.js} +0 -0
  6. package/core/geom/3d/topology/struct/binary/io/OrderedEdge.js +66 -0
  7. package/core/geom/3d/topology/struct/binary/io/bt_index_geometry_to_topology.js +188 -0
  8. package/core/geom/3d/topology/struct/binary/io/bt_index_geometry_to_topology.spec.js +84 -0
  9. package/core/geom/3d/topology/struct/binary/io/bt_mesh_calc_edges.js +51 -0
  10. package/core/geom/3d/topology/struct/binary/io/create_edge.js +106 -0
  11. package/core/geom/3d/topology/struct/binary/io/get_or_create_edge_map.js +26 -0
  12. package/core/geom/3d/topology/struct/binary/query/bt_mesh_edge_has_vertex.js +16 -0
  13. package/core/geom/3d/topology/struct/binary/query/bt_mesh_edge_has_vertex.spec.js +15 -0
  14. package/core/geom/3d/topology/struct/binary/query/bt_mesh_edge_other_vertex.js +21 -0
  15. package/core/geom/3d/topology/struct/binary/query/bt_mesh_edge_other_vertex.spec.js +16 -0
  16. package/core/geom/3d/topology/struct/prototypeBinaryTopology.js +14 -14
  17. package/core/path/PATH_SEPARATOR.js +1 -0
  18. package/core/path/computeFileExtension.js +24 -0
  19. package/core/path/computeFileExtension.spec.js +13 -0
  20. package/core/path/computePathBase.js +21 -0
  21. package/core/path/computePathBase.spec.js +13 -0
  22. package/core/path/computePathDirectory.js +25 -0
  23. package/editor/view/library/MeshLibraryView.js +1 -1
  24. package/engine/asset/guessAssetType.js +1 -1
  25. package/engine/asset/loaders/image/ImageRGBADataLoader.js +1 -1
  26. package/engine/asset/loaders/texture/TextureAssetLoader.js +1 -1
  27. package/engine/ecs/storage/BinaryBufferSerializer.js +7 -0
  28. package/engine/graphics/material/composeCompile.js +3 -5
  29. package/engine/graphics/micron/plugin/GLTFAssetTransformer.js +1 -1
  30. package/engine/graphics/sh3/README.md +2 -0
  31. package/engine/graphics/sh3/path_tracer/PathTracer.js +0 -14
  32. package/engine/graphics/sh3/path_tracer/prototypePathTracer.js +11 -7
  33. package/engine/graphics/texture/cubemap/load_environment_map.js +1 -1
  34. package/engine/graphics/util/makeMeshPreviewScene.js +38 -26
  35. package/engine/network/RemoteController.js +98 -0
  36. package/engine/network/remoteEditor.js +33 -0
  37. package/package.json +1 -1
  38. package/view/elements/MeshPreview.js +5 -1
  39. package/core/FilePath.js +0 -73
  40. package/core/FilePath.spec.js +0 -25
  41. package/core/geom/3d/topology/struct/bt_index_geometry_to_topology.js +0 -329
  42. package/core/geom/3d/topology/struct/bt_index_geometry_to_topology.spec.js +0 -26
@@ -17,10 +17,10 @@ import { assert } from "../../../../assert.js";
17
17
  * @see [1] "One machine, one minute, three billion tetrahedra" by Célestin Marot et al
18
18
  * @param {TetrahedralMesh} mesh
19
19
  * @param {number[]} input serialized set of 3d points (x,y,z)
20
- * @param {number} n number of points in the input
20
+ * @param {number} [n] number of points in the input
21
21
  * @returns {boolean}
22
22
  */
23
- export function compute_delaunay_tetrahedral_mesh(mesh, input, n) {
23
+ export function compute_delaunay_tetrahedral_mesh(mesh, input, n = input.length / 3) {
24
24
  assert.notNull(mesh, 'mesh');
25
25
  assert.defined(mesh, 'mesh');
26
26
 
@@ -1,6 +1,6 @@
1
- import { max3 } from "../../../../math/max3.js";
2
- import { assert } from "../../../../assert.js";
3
- import { typed_array_copy } from "../../../../collection/array/typed/typed_array_copy.js";
1
+ import { max3 } from "../../../../../math/max3.js";
2
+ import { assert } from "../../../../../assert.js";
3
+ import { typed_array_copy } from "../../../../../collection/array/typed/typed_array_copy.js";
4
4
 
5
5
  /**
6
6
  * How many items to reserve by default
@@ -74,7 +74,7 @@ export class BinaryElementPool {
74
74
  this.__data_uint8 = new Uint8Array(this.__data_buffer);
75
75
  this.__data_uint32 = new Uint32Array(this.__data_buffer);
76
76
  this.__data_float32 = new Float32Array(this.__data_buffer);
77
- this.__data_view = new DataView(this.__data_buffer);
77
+ this.data_view = new DataView(this.__data_buffer);
78
78
 
79
79
  this.__capacity = initial_capacity;
80
80
 
@@ -119,10 +119,6 @@ export class BinaryElementPool {
119
119
  return this.__data_float32;
120
120
  }
121
121
 
122
- get data_view() {
123
- return this.__data_view;
124
- }
125
-
126
122
  /**
127
123
  * Get rid of excess capacity
128
124
  */
@@ -187,7 +183,7 @@ export class BinaryElementPool {
187
183
  this.__data_uint8 = new Uint8Array(new_data_buffer);
188
184
  this.__data_uint32 = new Uint32Array(this.__data_buffer);
189
185
  this.__data_float32 = new Float32Array(this.__data_buffer);
190
- this.__data_view = new DataView(new_data_buffer);
186
+ this.data_view = new DataView(new_data_buffer);
191
187
 
192
188
  // copy old data
193
189
  typed_array_copy(old_data_uint8, this.__data_uint8);
@@ -241,7 +237,7 @@ export class BinaryElementPool {
241
237
  this.__grow_capacity(this.__size);
242
238
  }
243
239
 
244
- assert.greaterThan(this.__data_buffer.byteLength, result * this.__item_size, 'memory underflow');
240
+ // assert.greaterThan(this.__data_buffer.byteLength, result * this.__item_size, 'memory underflow');
245
241
 
246
242
  return result;
247
243
  }
@@ -260,7 +256,7 @@ export class BinaryElementPool {
260
256
  this.__grow_capacity(this.__size);
261
257
  }
262
258
 
263
- assert.greaterThanOrEqual(this.__data_buffer.byteLength, (offset + count) * this.__item_size, 'memory underflow');
259
+ // assert.greaterThanOrEqual(this.__data_buffer.byteLength, (offset + count) * this.__item_size, 'memory underflow');
264
260
 
265
261
  return offset;
266
262
  }
@@ -1,6 +1,6 @@
1
1
  import { BinaryElementPool } from "./BinaryElementPool.js";
2
- import { array_copy } from "../../../../collection/array/copyArray.js";
3
- import { assert } from "../../../../assert.js";
2
+ import { array_copy } from "../../../../../collection/array/copyArray.js";
3
+ import { assert } from "../../../../../assert.js";
4
4
 
5
5
  /**
6
6
  * Byte size of FLOAT_32
@@ -254,6 +254,7 @@ export class BinaryTopology {
254
254
  pool.data_view.setUint32(address + UINT_32_SIZE, value);
255
255
  }
256
256
 
257
+
257
258
  /**
258
259
  * @param {number} id edge ID
259
260
  * @returns {number}
@@ -281,6 +282,122 @@ export class BinaryTopology {
281
282
  pool.data_view.setUint32(address + 2 * UINT_32_SIZE, value);
282
283
  }
283
284
 
285
+ /**
286
+ * @param {number} id edge ID
287
+ * @returns {number}
288
+ */
289
+ edge_read_v1_disk_next(id) {
290
+ assert.isNonNegativeInteger(id, 'id');
291
+ const pool = this.__edge_pool;
292
+
293
+ // assert.equal(pool.is_allocated(id), true, 'element is not allocated');
294
+
295
+ const address = pool.element_address(id);
296
+ return pool.data_view.getUint32(address + 3 * UINT_32_SIZE);
297
+
298
+ }
299
+
300
+ /**
301
+ *
302
+ * @param {number} value
303
+ * @param {number} id edge ID
304
+ */
305
+ edge_write_v1_disk_next(id, value) {
306
+ const pool = this.__edge_pool;
307
+
308
+ // assert.equal(pool.is_allocated(id), true, 'element is not allocated');
309
+
310
+ const address = pool.element_address(id);
311
+ pool.data_view.setUint32(address + 3 * UINT_32_SIZE, value);
312
+ }
313
+
314
+ /**
315
+ * @param {number} id edge ID
316
+ * @returns {number}
317
+ */
318
+ edge_read_v1_disk_prev(id) {
319
+ assert.isNonNegativeInteger(id, 'id');
320
+ const pool = this.__edge_pool;
321
+
322
+ // assert.equal(pool.is_allocated(id), true, 'element is not allocated');
323
+
324
+ const address = pool.element_address(id);
325
+ return pool.data_view.getUint32(address + 4 * UINT_32_SIZE);
326
+
327
+ }
328
+
329
+ /**
330
+ *
331
+ * @param {number} value
332
+ * @param {number} id edge ID
333
+ */
334
+ edge_write_v1_disk_prev(id, value) {
335
+ const pool = this.__edge_pool;
336
+
337
+ // assert.equal(pool.is_allocated(id), true, 'element is not allocated');
338
+
339
+ const address = pool.element_address(id);
340
+ pool.data_view.setUint32(address + 4 * UINT_32_SIZE, value);
341
+ }
342
+
343
+ /**
344
+ * @param {number} id edge ID
345
+ * @returns {number}
346
+ */
347
+ edge_read_v2_disk_next(id) {
348
+ assert.isNonNegativeInteger(id, 'id');
349
+ const pool = this.__edge_pool;
350
+
351
+ // assert.equal(pool.is_allocated(id), true, 'element is not allocated');
352
+
353
+ const address = pool.element_address(id);
354
+ return pool.data_view.getUint32(address + 5 * UINT_32_SIZE);
355
+
356
+ }
357
+
358
+ /**
359
+ *
360
+ * @param {number} value
361
+ * @param {number} id edge ID
362
+ */
363
+ edge_write_v2_disk_next(id, value) {
364
+ const pool = this.__edge_pool;
365
+
366
+ // assert.equal(pool.is_allocated(id), true, 'element is not allocated');
367
+
368
+ const address = pool.element_address(id);
369
+ pool.data_view.setUint32(address + 5 * UINT_32_SIZE, value);
370
+ }
371
+
372
+ /**
373
+ * @param {number} id edge ID
374
+ * @returns {number}
375
+ */
376
+ edge_read_v2_disk_prev(id) {
377
+ assert.isNonNegativeInteger(id, 'id');
378
+ const pool = this.__edge_pool;
379
+
380
+ // assert.equal(pool.is_allocated(id), true, 'element is not allocated');
381
+
382
+ const address = pool.element_address(id);
383
+ return pool.data_view.getUint32(address + 6 * UINT_32_SIZE);
384
+
385
+ }
386
+
387
+ /**
388
+ *
389
+ * @param {number} value
390
+ * @param {number} id edge ID
391
+ */
392
+ edge_write_v2_disk_prev(id, value) {
393
+ const pool = this.__edge_pool;
394
+
395
+ // assert.equal(pool.is_allocated(id), true, 'element is not allocated');
396
+
397
+ const address = pool.element_address(id);
398
+ pool.data_view.setUint32(address + 6 * UINT_32_SIZE, value);
399
+ }
400
+
284
401
  loop_create() {
285
402
  const id = this.__loop_pool.allocate();
286
403
 
@@ -294,10 +411,10 @@ export class BinaryTopology {
294
411
  * @param {number} id
295
412
  */
296
413
  loop_initialize(id) {
297
- this.loop_write_radial_next(id, NULL_POINTER);
298
- this.loop_write_radial_prev(id, NULL_POINTER);
299
- this.loop_write_next(id, NULL_POINTER);
300
- this.loop_write_prev(id, NULL_POINTER);
414
+ this.loop_write_radial_next(id, id);
415
+ this.loop_write_radial_prev(id, id);
416
+ this.loop_write_next(id, id);
417
+ this.loop_write_prev(id, id);
301
418
  }
302
419
 
303
420
  /**
@@ -0,0 +1,66 @@
1
+ import { split_by_2 } from "../../../../morton/mortonEncode_magicbits.js";
2
+
3
+ export class OrderedEdge {
4
+ /**
5
+ *
6
+ * @param {number} v1
7
+ * @param {number} v2
8
+ */
9
+ constructor(v1, v2) {
10
+ if (v1 < v2) {
11
+ this.v_low = v1;
12
+ this.v_high = v2;
13
+ } else {
14
+ this.v_low = v2;
15
+ this.v_high = v1;
16
+ }
17
+ }
18
+
19
+ /**
20
+ *
21
+ * @param {number} v1
22
+ * @param {number} v2
23
+ */
24
+ set(v1, v2) {
25
+ if (v1 < v2) {
26
+ this.v_low = v1;
27
+ this.v_high = v2;
28
+ } else {
29
+ this.v_low = v2;
30
+ this.v_high = v1;
31
+ }
32
+ }
33
+
34
+ /**
35
+ *
36
+ * @param {OrderedEdge} other
37
+ */
38
+ copy(other) {
39
+ this.v_low = other.v_low;
40
+ this.v_high = other.v_high;
41
+ }
42
+
43
+ clone() {
44
+ return new OrderedEdge(this.v_low, this.v_high);
45
+ }
46
+
47
+ /**
48
+ *
49
+ * @return {number}
50
+ */
51
+ hash() {
52
+ // resulting hashes are close by
53
+ return split_by_2(this.v_low) | (split_by_2(this.v_high) << 1);
54
+ }
55
+
56
+ /**
57
+ *
58
+ * @param {OrderedEdge} other
59
+ * @returns {boolean}
60
+ */
61
+ equals(other) {
62
+ return this.v_low === other.v_low
63
+ && this.v_high === other.v_high
64
+ ;
65
+ }
66
+ }
@@ -0,0 +1,188 @@
1
+ import { NULL_POINTER } from "../BinaryTopology.js";
2
+ import { create_edge } from "./create_edge.js";
3
+
4
+
5
+ /**
6
+ * Populates supplied topology with data from supplied indexed geometry
7
+ * @see https://github.com/blender/blender/blob/9cb061f4f0119e647173e7d354e1457e97632333/source/blender/blenkernel/intern/mesh_calc_edges.cc
8
+ * @see https://github.com/blender/blender/blob/9cb061f4f0119e647173e7d354e1457e97632333/source/blender/io/stl/importer/stl_import_mesh.cc#L63
9
+ * @param {BinaryTopology} out
10
+ * @param {number[]} indices
11
+ * @param {number[]} vertex_positions
12
+ * @param {number[]} [vertex_normals]
13
+ */
14
+ export function bt_index_geometry_to_topology(
15
+ out,
16
+ indices,
17
+ vertex_positions,
18
+ vertex_normals
19
+ ) {
20
+
21
+ // remove any existing data
22
+ out.clear();
23
+
24
+ /*
25
+ NOTES:
26
+ - we use {@link BinaryElementPool#allocate_continuous} to avoid having to allocate additional mapping structure
27
+ */
28
+
29
+ // ingest all vertices
30
+ const vertex_count = vertex_positions.length / 3;
31
+
32
+ const vertex_allocation_offset = out.vertices.allocate_continuous(vertex_count);
33
+
34
+ for (let i = 0; i < vertex_count; i++) {
35
+ const i3 = i * 3;
36
+
37
+ const vertex_id = i + vertex_allocation_offset;
38
+
39
+ out.vertex_write_coordinate(vertex_id, vertex_positions, i3);
40
+
41
+ if (vertex_normals !== undefined) {
42
+ out.vertex_write_normal(vertex_id, vertex_normals, i3);
43
+ }
44
+
45
+ // initialize edge to NULL
46
+ out.vertex_write_edge(vertex_id, NULL_POINTER);
47
+ }
48
+
49
+ const face_count = indices.length / 3;
50
+
51
+ // create faces
52
+ const face_allocation_offset = out.faces.allocate_continuous(face_count);
53
+
54
+ // reserve some memory to avoid extra allocations
55
+ out.loops.ensure_capacity(face_count * 3);
56
+ out.edges.ensure_capacity(face_count * 1.6);
57
+
58
+ for (let i = 0; i < face_count; i++) {
59
+ const i3 = i * 3;
60
+
61
+ const a = indices[i3];
62
+ const b = indices[i3 + 1];
63
+ const c = indices[i3 + 2];
64
+
65
+ const vertex_a = a + vertex_allocation_offset;
66
+ const vertex_b = b + vertex_allocation_offset;
67
+ const vertex_c = c + vertex_allocation_offset;
68
+
69
+ const face_id = i + face_allocation_offset;
70
+
71
+ // create edges
72
+ const edge_ab = get_or_create_edge_2(vertex_a, vertex_b, out);
73
+ const edge_bc = get_or_create_edge_2(vertex_b, vertex_c, out);
74
+ const edge_ca = get_or_create_edge_2(vertex_c, vertex_a, out);
75
+
76
+ // allocate loops
77
+ const loop_a = out.loop_create();
78
+ const loop_b = out.loop_create();
79
+ const loop_c = out.loop_create();
80
+
81
+ // write loops
82
+ out.loop_write_vertex(loop_a, vertex_a);
83
+ out.loop_write_vertex(loop_b, vertex_b);
84
+ out.loop_write_vertex(loop_c, vertex_c);
85
+
86
+ // link loops to edges
87
+ link_loop_to_edge(loop_a, edge_ab, out);
88
+ link_loop_to_edge(loop_b, edge_bc, out);
89
+ link_loop_to_edge(loop_c, edge_ca, out);
90
+
91
+ out.loop_write_face(loop_a, face_id);
92
+ out.loop_write_face(loop_b, face_id);
93
+ out.loop_write_face(loop_c, face_id);
94
+
95
+ /*
96
+ NOTE: Vertices (#BMLoop.v & #BMLoop.next.v) always contain vertices from (#BMEdge.v1 & #BMEdge.v2).
97
+ */
98
+ // link up loops around the face
99
+ out.loop_write_next(loop_a, loop_b);
100
+ out.loop_write_prev(loop_a, loop_c);
101
+
102
+ out.loop_write_next(loop_b, loop_c);
103
+ out.loop_write_prev(loop_b, loop_a);
104
+
105
+ out.loop_write_next(loop_c, loop_a);
106
+ out.loop_write_prev(loop_c, loop_b);
107
+
108
+ // update face
109
+ out.face_write_loop(face_id, loop_a);
110
+
111
+ }
112
+ }
113
+
114
+ /**
115
+ *
116
+ * @param {number} loop_id
117
+ * @param {number} edge_id
118
+ * @param {BinaryTopology} mesh
119
+ */
120
+ function link_loop_to_edge(loop_id, edge_id, mesh) {
121
+ mesh.loop_write_edge(loop_id, edge_id);
122
+
123
+ /**
124
+ * First loop attached to the edge
125
+ * @type {number}
126
+ */
127
+ const existing_loop = mesh.edge_read_loop(edge_id);
128
+
129
+ if (existing_loop === NULL_POINTER) {
130
+ // no loops connected yet
131
+ mesh.edge_write_loop(edge_id, loop_id);
132
+
133
+
134
+ } else {
135
+ // insert loop between existing loop and the next one
136
+ const next = mesh.loop_read_radial_next(existing_loop);
137
+
138
+ mesh.loop_write_radial_next(existing_loop, loop_id);
139
+ mesh.loop_write_radial_prev(next, loop_id);
140
+
141
+ mesh.loop_write_radial_next(loop_id, next);
142
+ mesh.loop_write_radial_prev(loop_id, existing_loop);
143
+ }
144
+
145
+ }
146
+
147
+
148
+ /**
149
+ * Ensure an edge between two vertices
150
+ * NOTE: works without having to use a separate hash map, resulting in faster execution and smaller RAM requirement
151
+ * @param {number} v0
152
+ * @param {number} v1
153
+ * @param {BinaryTopology} mesh
154
+ */
155
+ function get_or_create_edge_2(v0, v1, mesh) {
156
+ const first_edge = mesh.vertex_read_edge(v0);
157
+
158
+ if (first_edge !== NULL_POINTER) {
159
+
160
+ // iterate over all edges around the vertex
161
+ let edge = first_edge;
162
+
163
+ do {
164
+ const edge_v1 = mesh.edge_read_vertex1(edge);
165
+ const edge_v2 = mesh.edge_read_vertex2(edge);
166
+
167
+ if (edge_v1 === v1 || edge_v2 === v1) {
168
+ // found edge that connects between v0 and v1
169
+ return edge;
170
+ }
171
+
172
+ if (edge_v1 === v0) {
173
+ edge = mesh.edge_read_v1_disk_next(edge);
174
+ } else if (edge_v2 === v0) {
175
+ edge = mesh.edge_read_v2_disk_next(edge);
176
+ } else {
177
+ // invalid edge connection
178
+ throw new Error('Invalid edge, not connected to first vertex');
179
+ }
180
+
181
+ } while (edge !== first_edge);
182
+
183
+ }
184
+
185
+ // if match is found - return the match, else create a new edge and assign
186
+
187
+ return create_edge(mesh, v0, v1);
188
+ }
@@ -0,0 +1,84 @@
1
+ import { bt_index_geometry_to_topology } from "./bt_index_geometry_to_topology.js";
2
+ import { BinaryTopology } from "../BinaryTopology.js";
3
+ import { bt_mesh_edge_has_vertex } from "../query/bt_mesh_edge_has_vertex.js";
4
+
5
+ test('Empty geometry work as expected', () => {
6
+ const mesh = new BinaryTopology();
7
+ bt_index_geometry_to_topology(mesh, [], []);
8
+
9
+ expect(mesh.faces.size).toBe(0);
10
+ expect(mesh.vertices.size).toBe(0);
11
+ expect(mesh.edges.size).toBe(0);
12
+ expect(mesh.loops.size).toBe(0);
13
+ });
14
+
15
+ test('Single triangle', () => {
16
+ const mesh = new BinaryTopology();
17
+ bt_index_geometry_to_topology(mesh, [0, 1, 2], [
18
+ 1, 3, 7,
19
+ 11, 13, 17,
20
+ 23, 27, -1
21
+ ]);
22
+
23
+ expect(mesh.faces.size).toBe(1);
24
+ expect(mesh.loops.size).toBe(3);
25
+ expect(mesh.edges.size).toBe(3);
26
+ expect(mesh.vertices.size).toBe(3);
27
+
28
+ // validate loops
29
+ for (let loop_id = 0; loop_id < 3; loop_id++) {
30
+ const face = mesh.loop_read_face(loop_id);
31
+ expect(face).toEqual(0);
32
+
33
+ const loop_next = mesh.loop_read_next(loop_id);
34
+ expect(loop_next).not.toEqual(loop_id);
35
+ expect(mesh.loop_read_prev(loop_next)).toEqual(loop_id);
36
+
37
+ const loop_prev = mesh.loop_read_prev(loop_id);
38
+ expect(loop_prev).not.toEqual(loop_id);
39
+ expect(mesh.loop_read_next(loop_prev)).toEqual(loop_id);
40
+
41
+ expect(loop_prev).not.toEqual(loop_next);
42
+
43
+ // should loop around
44
+ expect(mesh.loop_read_next(loop_next)).toEqual(loop_prev);
45
+ expect(mesh.loop_read_prev(loop_prev)).toEqual(loop_next);
46
+ }
47
+
48
+
49
+ // validate edges
50
+ for (let edge_id = 0; edge_id < 3; edge_id++) {
51
+ const v1 = mesh.edge_read_vertex1(edge_id);
52
+ const v2 = mesh.edge_read_vertex2(edge_id);
53
+
54
+ // different vertices
55
+ expect(v1).not.toEqual(v2);
56
+
57
+ // validate disk links
58
+ const disk_v1_next = mesh.edge_read_v1_disk_next(edge_id);
59
+
60
+ expect(disk_v1_next).not.toEqual(edge_id);
61
+
62
+ expect(bt_mesh_edge_has_vertex(mesh, disk_v1_next, v1)).toBe(true);
63
+
64
+ const disk_v1_prev = mesh.edge_read_v1_disk_prev(edge_id);
65
+
66
+ expect(disk_v1_prev).not.toEqual(edge_id);
67
+
68
+ expect(bt_mesh_edge_has_vertex(mesh, disk_v1_prev, v1)).toBe(true);
69
+
70
+ const disk_v2_next = mesh.edge_read_v2_disk_next(edge_id);
71
+
72
+ expect(disk_v2_next).not.toEqual(edge_id);
73
+
74
+ expect(bt_mesh_edge_has_vertex(mesh, disk_v2_next, v2)).toBe(true);
75
+
76
+ const disk_v2_prev = mesh.edge_read_v2_disk_prev(edge_id);
77
+
78
+ expect(disk_v2_prev).not.toEqual(edge_id);
79
+
80
+ expect(bt_mesh_edge_has_vertex(mesh, disk_v2_prev, v2)).toBe(true);
81
+
82
+ }
83
+
84
+ });
@@ -0,0 +1,51 @@
1
+ import { OrderedEdge } from "./OrderedEdge.js";
2
+ import { HashMap } from "../../../../../../collection/HashMap.d.ts";
3
+
4
+ /**
5
+ * @see https://github.com/blender/blender/blob/9cb061f4f0119e647173e7d354e1457e97632333/source/blender/blenkernel/intern/mesh_calc_edges.cc#L209
6
+ * @param {BinaryTopology} mesh
7
+ */
8
+ function bt_mesh_calc_edges(mesh) {
9
+ /**
10
+ *
11
+ * @type {HashMap<OrderedEdge, unknown>}
12
+ */
13
+ const map = new HashMap();
14
+
15
+ add_polygon_edges_to_map(mesh, map);
16
+ }
17
+
18
+ /**
19
+ * @see https://github.com/blender/blender/blob/9cb061f4f0119e647173e7d354e1457e97632333/source/blender/blenkernel/intern/mesh_calc_edges.cc#L96
20
+ * @param {BinaryTopology} mesh
21
+ * @param {HashMap<OrderedEdge,*>} edge_map
22
+ */
23
+ function add_polygon_edges_to_map(mesh, edge_map) {
24
+
25
+ const faces = mesh.faces;
26
+ const face_count = faces.size;
27
+
28
+ for (let i = 0; i < face_count; i++) {
29
+ if (!faces.is_allocated(i)) {
30
+ continue;
31
+ }
32
+
33
+ const first_loop = mesh.face_read_loop(i);
34
+ let prev_loop = first_loop + j;
35
+
36
+ for (let j = 0; j < 3; j++) {
37
+ const loop = first_loop + j;
38
+
39
+
40
+ const prev_v = mesh.loop_read_vertex(prev_loop);
41
+ const next_v = mesh.loop_read_vertex(loop);
42
+
43
+ const edge = new OrderedEdge(prev_v, next_v);
44
+
45
+ edge_map.getOrSet(edge, null);
46
+
47
+ prev_loop = loop;
48
+ }
49
+ }
50
+
51
+ }