@woosh/meep-engine 2.43.22 → 2.43.24
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/core/geom/3d/tetrahedra/delaunay/compute_delaunay_tetrahedral_mesh.js +2 -2
- package/core/geom/3d/topology/struct/{BinaryElementPool.js → binary/BinaryElementPool.js} +7 -11
- package/core/geom/3d/topology/struct/{BinaryElementPool.spec.js → binary/BinaryElementPool.spec.js} +0 -0
- package/core/geom/3d/topology/struct/{BinaryTopology.js → binary/BinaryTopology.js} +123 -6
- package/core/geom/3d/topology/struct/{BinaryTopology.spec.js → binary/BinaryTopology.spec.js} +0 -0
- package/core/geom/3d/topology/struct/binary/io/OrderedEdge.js +66 -0
- package/core/geom/3d/topology/struct/binary/io/bt_index_geometry_to_topology.js +188 -0
- package/core/geom/3d/topology/struct/binary/io/bt_index_geometry_to_topology.spec.js +84 -0
- package/core/geom/3d/topology/struct/binary/io/bt_mesh_calc_edges.js +51 -0
- package/core/geom/3d/topology/struct/binary/io/create_edge.js +106 -0
- package/core/geom/3d/topology/struct/binary/io/get_or_create_edge_map.js +26 -0
- package/core/geom/3d/topology/struct/binary/query/bt_mesh_edge_has_vertex.js +16 -0
- package/core/geom/3d/topology/struct/binary/query/bt_mesh_edge_has_vertex.spec.js +15 -0
- package/core/geom/3d/topology/struct/binary/query/bt_mesh_edge_other_vertex.js +21 -0
- package/core/geom/3d/topology/struct/binary/query/bt_mesh_edge_other_vertex.spec.js +16 -0
- package/core/geom/3d/topology/struct/prototypeBinaryTopology.js +14 -14
- package/core/path/PATH_SEPARATOR.js +1 -0
- package/core/path/computeFileExtension.js +24 -0
- package/core/path/computeFileExtension.spec.js +13 -0
- package/core/path/computePathBase.js +21 -0
- package/core/path/computePathBase.spec.js +13 -0
- package/core/path/computePathDirectory.js +25 -0
- package/editor/Editor.js +3 -2
- package/editor/view/library/MeshLibraryView.js +1 -1
- package/engine/EngineHarness.js +25 -1
- package/engine/asset/guessAssetType.js +1 -1
- package/engine/asset/loaders/image/ImageRGBADataLoader.js +1 -1
- package/engine/asset/loaders/texture/TextureAssetLoader.js +1 -1
- package/engine/ecs/storage/BinaryBufferSerializer.js +7 -0
- package/engine/graphics/camera/camera_compute_distance_to_fit_length.js +5 -1
- package/engine/graphics/ecs/mesh-v2/render/adapters/AbstractRenderAdapter.js +33 -0
- package/engine/graphics/ecs/mesh-v2/sample/prototypeShadedGeometry.js +1 -0
- package/engine/graphics/material/composeCompile.js +3 -5
- package/engine/graphics/micron/plugin/GLTFAssetTransformer.js +1 -1
- package/engine/graphics/sh3/README.md +2 -0
- package/engine/graphics/sh3/path_tracer/PathTracer.js +0 -14
- package/engine/graphics/sh3/path_tracer/prototypePathTracer.js +11 -7
- package/engine/graphics/texture/cubemap/load_environment_map.js +1 -1
- package/engine/graphics/util/makeMeshPreviewScene.js +38 -26
- package/engine/network/RemoteController.js +138 -0
- package/engine/network/remoteEditor.js +36 -0
- package/package.json +1 -1
- package/view/elements/MeshPreview.js +5 -1
- package/core/FilePath.js +0 -73
- package/core/FilePath.spec.js +0 -25
- package/core/geom/3d/topology/struct/bt_index_geometry_to_topology.js +0 -329
- 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 "
|
|
2
|
-
import { assert } from "
|
|
3
|
-
import { typed_array_copy } from "
|
|
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.
|
|
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.
|
|
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
|
}
|
package/core/geom/3d/topology/struct/{BinaryElementPool.spec.js → binary/BinaryElementPool.spec.js}
RENAMED
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BinaryElementPool } from "./BinaryElementPool.js";
|
|
2
|
-
import { array_copy } from "
|
|
3
|
-
import { assert } from "
|
|
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,
|
|
298
|
-
this.loop_write_radial_prev(id,
|
|
299
|
-
this.loop_write_next(id,
|
|
300
|
-
this.loop_write_prev(id,
|
|
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
|
/**
|
package/core/geom/3d/topology/struct/{BinaryTopology.spec.js → binary/BinaryTopology.spec.js}
RENAMED
|
File without changes
|
|
@@ -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
|
+
}
|