@woosh/meep-engine 2.43.20 → 2.43.22

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 (25) hide show
  1. package/core/collection/HashMap.d.ts +2 -2
  2. package/core/collection/HashMap.js +22 -2
  3. package/core/geom/3d/morton/mortonEncode_magicbits.js +18 -0
  4. package/core/geom/3d/topology/struct/BinaryElementPool.js +292 -0
  5. package/core/geom/3d/topology/struct/BinaryElementPool.spec.js +36 -0
  6. package/core/geom/3d/topology/struct/BinaryTopology.js +454 -66
  7. package/core/geom/3d/topology/struct/BinaryTopology.spec.js +16 -0
  8. package/core/geom/3d/topology/struct/TopoEdge.js +4 -0
  9. package/core/geom/3d/topology/struct/TopoMesh.js +25 -0
  10. package/core/geom/3d/topology/struct/TopoTriangle.js +4 -0
  11. package/core/geom/3d/topology/struct/TopoVertex.js +8 -0
  12. package/core/geom/3d/topology/struct/bt_index_geometry_to_topology.js +329 -0
  13. package/core/geom/3d/topology/struct/bt_index_geometry_to_topology.spec.js +26 -0
  14. package/core/geom/3d/topology/struct/prototypeBinaryTopology.js +55 -0
  15. package/engine/graphics/ecs/mesh-v2/aggregate/SGMeshHighlightSystem.js +12 -3
  16. package/engine/graphics/micron/format/MicronGeometryPatch.d.ts +1 -1
  17. package/engine/graphics/micron/format/MicronGeometryPatch.js +2 -1
  18. package/engine/graphics/micron/plugin/GLTFAssetTransformer.js +89 -64
  19. package/engine/graphics/sh3/path_tracer/PathTracedMesh.js +4 -0
  20. package/engine/graphics/sh3/path_tracer/PathTracer.js +90 -36
  21. package/engine/graphics/sh3/path_tracer/getBiasedNormalSample.js +1 -1
  22. package/engine/graphics/sh3/path_tracer/prototypePathTracer.js +44 -10
  23. package/engine/graphics/sh3/path_tracer/ray_hit_apply_transform.js +28 -13
  24. package/engine/save/storage/IndexedDBStorage.js +1 -0
  25. package/package.json +1 -1
@@ -5,8 +5,8 @@ export class HashMap<K, V> implements Iterable<[V, K]> {
5
5
  capacity,
6
6
  loadFactor
7
7
  }: {
8
- keyHashFunction: (k: K) => number,
9
- keyEqualityFunction: (a: K, b: K) => boolean,
8
+ keyHashFunction?: (k: K) => number,
9
+ keyEqualityFunction?: (a: K, b: K) => boolean,
10
10
  capacity?: number,
11
11
  loadFactor?: number
12
12
  })
@@ -60,6 +60,8 @@ export class HashMap {
60
60
 
61
61
  assert.isFunction(keyHashFunction, 'keyHashFunction');
62
62
  assert.isFunction(keyEqualityFunction, 'keyEqualityFunction');
63
+ assert.isNonNegativeInteger(capacity, 'capacity');
64
+ assert.isNumber(loadFactor, 'loadFactor');
63
65
 
64
66
  /**
65
67
  *
@@ -275,8 +277,8 @@ export class HashMap {
275
277
 
276
278
  /**
277
279
  *
278
- * @param {Key} key
279
- * @param {function(Key):V} compute
280
+ * @param {K} key
281
+ * @param {function(K):V} compute
280
282
  * @param {*} [compute_context]
281
283
  * @return {V}
282
284
  */
@@ -294,6 +296,24 @@ export class HashMap {
294
296
  return value;
295
297
  }
296
298
 
299
+ /**
300
+ * Returns existing value if key exists, if it doesn't - sets it and returns what was passed in
301
+ * @param {K} key
302
+ * @param {V} value this value will be written at the key location if the key wasn't found in the Map
303
+ * @return {V}
304
+ */
305
+ getOrSet(key, value) {
306
+ const existing = this.get(key);
307
+
308
+ if (existing !== undefined) {
309
+ return existing;
310
+ }
311
+
312
+ this.set(key, value);
313
+
314
+ return value;
315
+ }
316
+
297
317
 
298
318
  /**
299
319
  *
@@ -15,6 +15,24 @@ function split_by_3(a) {
15
15
  return x;
16
16
  }
17
17
 
18
+ /**
19
+ * method to separate bits from a given integer 2 positions apart
20
+ *
21
+ * @example when input is ABC, output bits are A00B00C
22
+ * @see https://github.com/Forceflow/libmorton/blob/234a443ca8e2c64f6385f1a9d6ee10a70d08a3fa/include/libmorton/morton2D.h#L99
23
+ * @param {number} a
24
+ * @returns {number}
25
+ */
26
+ export function split_by_2(a) {
27
+ let x = a;
28
+ x = (x | x << 16) & 0x0000FFFF;
29
+ x = (x | x << 8) & 0x00FF00FF;
30
+ x = (x | x << 4) & 0x0F0F0F0F;
31
+ x = (x | x << 2) & 0x33333333;
32
+ x = (x | x << 1) & 0x55555555;
33
+ return x;
34
+ }
35
+
18
36
  /**
19
37
  * Based on article and C++ source code:
20
38
  * https://www.forceflow.be/2013/10/07/morton-encodingdecoding-through-bit-interleaving-implementations/
@@ -0,0 +1,292 @@
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
+
5
+ /**
6
+ * How many items to reserve by default
7
+ * @readonly
8
+ * @type {number}
9
+ */
10
+ const INITIAL_CAPACITY = 128;
11
+
12
+ /**
13
+ * @readonly
14
+ * @type {number}
15
+ */
16
+ const CAPACITY_GROW_MULTIPLIER = 1.2;
17
+
18
+ /**
19
+ * @readonly
20
+ * @type {number}
21
+ */
22
+ const CAPACITY_GROW_MIN_STEP = 32;
23
+
24
+ /**
25
+ * Align on 4 byte boundary
26
+ * @param {number} n
27
+ * @return {number}
28
+ */
29
+ function align_4(n) {
30
+ const result = ((n + 0x3) >> 2) << 2;
31
+
32
+ assert.greaterThanOrEqual(result, n, 'aligned result must be >= input');
33
+
34
+ return result;
35
+ }
36
+
37
+ /**
38
+ * @see https://github.com/blender/blender/blob/master/source/blender/blenlib/intern/BLI_mempool.c
39
+ */
40
+ export class BinaryElementPool {
41
+
42
+ /**
43
+ *
44
+ * @param {number} item_size in bytes
45
+ * @param {number} initial_capacity how many items to reverse in the newly created pool
46
+ */
47
+ constructor(item_size, initial_capacity = INITIAL_CAPACITY) {
48
+ assert.isNonNegativeInteger(item_size, 'item_size');
49
+ assert.greaterThan(item_size, 0, 'item_size must be greater than 0');
50
+
51
+ /**
52
+ * Size of a single pool item in bytes
53
+ * @type {number}
54
+ * @private
55
+ */
56
+ this.__item_size = item_size;
57
+
58
+ /**
59
+ * Unused slots
60
+ * @type {number[]}
61
+ * @private
62
+ */
63
+ this.__free = [];
64
+
65
+ /**
66
+ * Tracks last unallocated item in the list,
67
+ * this separate cursor is necessary to prevent re-allocation of the 'free' array
68
+ * @type {number}
69
+ * @private
70
+ */
71
+ this.__free_pointer = 0;
72
+
73
+ this.__data_buffer = new ArrayBuffer(align_4(initial_capacity * item_size));
74
+ this.__data_uint8 = new Uint8Array(this.__data_buffer);
75
+ this.__data_uint32 = new Uint32Array(this.__data_buffer);
76
+ this.__data_float32 = new Float32Array(this.__data_buffer);
77
+ this.__data_view = new DataView(this.__data_buffer);
78
+
79
+ this.__capacity = initial_capacity;
80
+
81
+ /**
82
+ *
83
+ * @type {number}
84
+ * @private
85
+ */
86
+ this.__size = 0;
87
+ }
88
+
89
+ /**
90
+ * Returns size of used region, this includes both elements that are allocated and those that aren't
91
+ * Please note that this value does not represent number of currently active elements, if you need that - you'll need to use something else
92
+ * @return {number}
93
+ */
94
+ get size() {
95
+ return this.__size;
96
+ }
97
+
98
+ /**
99
+ *
100
+ * @return {number}
101
+ */
102
+ get byteSize() {
103
+ return this.__capacity * this.__item_size;
104
+ }
105
+
106
+ /**
107
+ *
108
+ * @return {Uint32Array}
109
+ */
110
+ get data_uint32() {
111
+ return this.__data_uint32;
112
+ }
113
+
114
+ /**
115
+ *
116
+ * @return {Float32Array}
117
+ */
118
+ get data_float32() {
119
+ return this.__data_float32;
120
+ }
121
+
122
+ get data_view() {
123
+ return this.__data_view;
124
+ }
125
+
126
+ /**
127
+ * Get rid of excess capacity
128
+ */
129
+ trim() {
130
+ this.__set_capacity(this.__size);
131
+ }
132
+
133
+ /**
134
+ *
135
+ * @param {number} id
136
+ * @return {number}
137
+ */
138
+ element_address(id) {
139
+ assert.isNonNegativeInteger(id, 'id');
140
+
141
+ return this.__item_size * id;
142
+ }
143
+
144
+ /**
145
+ * Used alongside iterators to check if element is actually allocated or not
146
+ * @param {number} id
147
+ * @return {boolean}
148
+ */
149
+ is_allocated(id) {
150
+ if (id >= this.__size) {
151
+ // ID is past allocated region
152
+ return false;
153
+ }
154
+
155
+ const pointer = this.__free_pointer;
156
+
157
+ for (let i = 0; i < pointer; i++) {
158
+ const _id = this.__free[i];
159
+
160
+ if (id === _id) {
161
+ // found in unallocated set
162
+ return false;
163
+ }
164
+ }
165
+
166
+ return true;
167
+ }
168
+
169
+ /**
170
+ *
171
+ * @param {number} new_capacity
172
+ * @private
173
+ */
174
+ __set_capacity(new_capacity) {
175
+ if (this.__capacity === new_capacity) {
176
+ // no point
177
+ return;
178
+ }
179
+
180
+ const old_data_uint8 = this.__data_uint8;
181
+
182
+ const aligned_byte_size = align_4(new_capacity * this.__item_size);
183
+
184
+ const new_data_buffer = new ArrayBuffer(aligned_byte_size);
185
+
186
+ this.__data_buffer = new_data_buffer;
187
+ this.__data_uint8 = new Uint8Array(new_data_buffer);
188
+ this.__data_uint32 = new Uint32Array(this.__data_buffer);
189
+ this.__data_float32 = new Float32Array(this.__data_buffer);
190
+ this.__data_view = new DataView(new_data_buffer);
191
+
192
+ // copy old data
193
+ typed_array_copy(old_data_uint8, this.__data_uint8);
194
+
195
+ this.__capacity = new_capacity;
196
+ }
197
+
198
+ /**
199
+ *
200
+ * @param {number} min_capacity
201
+ * @private
202
+ */
203
+ __grow_capacity(min_capacity) {
204
+ const new_capacity = Math.ceil(max3(
205
+ min_capacity,
206
+ this.__capacity * CAPACITY_GROW_MULTIPLIER,
207
+ this.__capacity + CAPACITY_GROW_MIN_STEP
208
+ ));
209
+
210
+ this.__set_capacity(new_capacity);
211
+ }
212
+
213
+ /**
214
+ *
215
+ * @param {number} capacity
216
+ */
217
+ ensure_capacity(capacity) {
218
+ if (this.__capacity < capacity) {
219
+ this.__grow_capacity(capacity);
220
+ }
221
+ }
222
+
223
+
224
+ /**
225
+ *
226
+ * @return {number} ID of the allocated element
227
+ */
228
+ allocate() {
229
+ if (this.__free_pointer > 0) {
230
+ // get unused slot
231
+ this.__free_pointer--;
232
+ return this.__free[this.__free_pointer];
233
+ }
234
+
235
+ // allocate new
236
+ let result = this.__size;
237
+ this.__size++;
238
+
239
+ if (this.__size >= this.__capacity) {
240
+ // grow if necessary
241
+ this.__grow_capacity(this.__size);
242
+ }
243
+
244
+ assert.greaterThan(this.__data_buffer.byteLength, result * this.__item_size, 'memory underflow');
245
+
246
+ return result;
247
+ }
248
+
249
+ /**
250
+ * Allocate a continuous range of IDs in bulk
251
+ * @param {number} count
252
+ * @return {number} offset where the range starts, this is your first ID basically
253
+ */
254
+ allocate_continuous(count) {
255
+ const offset = this.__size;
256
+
257
+ this.__size += count;
258
+
259
+ if (this.__size >= this.__capacity) {
260
+ this.__grow_capacity(this.__size);
261
+ }
262
+
263
+ assert.greaterThanOrEqual(this.__data_buffer.byteLength, (offset + count) * this.__item_size, 'memory underflow');
264
+
265
+ return offset;
266
+ }
267
+
268
+ /**
269
+ * Please note that this method does not perform any checks at all.
270
+ * You have to make sure that the item is actually unneeded and no duplicate calls are made
271
+ * @param {number} id
272
+ */
273
+ release(id) {
274
+ if (id === this.__size - 1) {
275
+ // releasing item at the end of the reserved region, don't have to push it into free set
276
+ this.__size--;
277
+ } else if (id >= this.__size) {
278
+ // do nothing, just throw it away
279
+ // this should never happen
280
+ } else {
281
+ this.__free[this.__free_pointer++] = id;
282
+ }
283
+ }
284
+
285
+ /**
286
+ * Removed all data from the pool
287
+ */
288
+ clear() {
289
+ this.__size = 0;
290
+ this.__free_pointer = 0;
291
+ }
292
+ }
@@ -0,0 +1,36 @@
1
+ import { BinaryElementPool } from "./BinaryElementPool.js";
2
+
3
+ test('Constructor does not throw', () => {
4
+ new BinaryElementPool(1);
5
+ });
6
+
7
+ test('Allocation returns a valid non-negative ID', () => {
8
+ const pool = new BinaryElementPool(1);
9
+
10
+ const id = pool.allocate();
11
+
12
+ expect(id).toBeGreaterThanOrEqual(0);
13
+ expect(Number.isInteger(id)).toBe(true);
14
+ expect(Number.isFinite(id)).toBe(true);
15
+ });
16
+
17
+ test('Consecutive allocations return different IDs', () => {
18
+ const pool = new BinaryElementPool(1);
19
+
20
+ const a = pool.allocate();
21
+ const b = pool.allocate();
22
+
23
+ expect(a).not.toEqual(b);
24
+ });
25
+
26
+ test('Allocate, release, allocate produces the same ID',()=>{
27
+ const pool = new BinaryElementPool(1);
28
+
29
+ const a = pool.allocate();
30
+
31
+ pool.release(a);
32
+
33
+ const b = pool.allocate();
34
+
35
+ expect(a).toEqual(b);
36
+ });