@woosh/meep-engine 2.33.54 → 2.33.55

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.
@@ -62,4 +62,21 @@ export class AbstractShape3D {
62
62
  get surface_area() {
63
63
  throw new Error('Not Implemented');
64
64
  }
65
+
66
+ /**
67
+ * @template {AbstractShape3D} T
68
+ * @param {T} other
69
+ * @returns {boolean}
70
+ */
71
+ equals(other) {
72
+ return this.constructor === other.constructor;
73
+ }
74
+
75
+ /**
76
+ *
77
+ * @returns {number}
78
+ */
79
+ hash() {
80
+ return 0;
81
+ }
65
82
  }
@@ -1,6 +1,8 @@
1
1
  import { mat4, vec3 } from "gl-matrix";
2
2
  import { AbstractShape3D } from "./AbstractShape3D.js";
3
3
  import { sign } from "../../../math/sign.js";
4
+ import { isArrayEqualStrict } from "../../../collection/array/isArrayEqualStrict.js";
5
+ import { computeHashFloatArray } from "../../../math/hash/computeHashFloatArray.js";
4
6
 
5
7
  /**
6
8
  *
@@ -140,9 +142,9 @@ export class TransformedShape3D extends AbstractShape3D {
140
142
  // normalize gradient
141
143
  const gd = distance / Math.hypot(scratch_v3_0[0], scratch_v3_0[1], scratch_v3_0[2]);
142
144
 
143
- const scaled_dx = scaled_gx * gd ;
144
- const scaled_dy = scaled_gy * gd ;
145
- const scaled_dz = scaled_gz * gd ;
145
+ const scaled_dx = scaled_gx * gd;
146
+ const scaled_dy = scaled_gy * gd;
147
+ const scaled_dz = scaled_gz * gd;
146
148
 
147
149
  // determine sign
148
150
  const result_sign = sign(distance);
@@ -178,4 +180,27 @@ export class TransformedShape3D extends AbstractShape3D {
178
180
  result[1] = scratch_v3_0[1];
179
181
  result[2] = scratch_v3_0[2];
180
182
  }
183
+
184
+ /**
185
+ *
186
+ * @param {TransformedShape3D} other
187
+ * @returns {boolean}
188
+ */
189
+ equals(other) {
190
+ return super.equals(other)
191
+ && this.__subject.equals(other.subject)
192
+ && isArrayEqualStrict(this.__matrix, other.__matrix)
193
+ ;
194
+ }
195
+
196
+ hash() {
197
+ // to avoid recursion, only use matrix for hash
198
+ return computeHashFloatArray(this.__matrix);
199
+ }
181
200
  }
201
+
202
+ /**
203
+ * @readonly
204
+ * @type {boolean}
205
+ */
206
+ TransformedShape3D.isTransformedShape3D = true;
@@ -1,5 +1,6 @@
1
1
  import { UnitCubeShape3D } from "./UnitCubeShape3D.js";
2
2
  import { TransformedShape3D } from "./TransformedShape3D.js";
3
+ import { UnitSphereShape3D } from "./UnitSphereShape3D.js";
3
4
 
4
5
  test('signed distance identity transform', () => {
5
6
 
@@ -31,3 +32,24 @@ test('signed distance scale', () => {
31
32
 
32
33
  expect(s.signed_distance_at_point([2, 0, 0])).toBeCloseTo(1);
33
34
  });
35
+
36
+ test('equals', () => {
37
+
38
+ const a = TransformedShape3D.from_translation_scale(new UnitCubeShape3D(), [0, 0, 0], [2, 2, 2]);
39
+ const b = TransformedShape3D.from_translation_scale(new UnitCubeShape3D(), [0, 0, 0], [2, 2, 2]);
40
+
41
+ expect(a.equals(b)).toBe(true);
42
+
43
+ const c = TransformedShape3D.from_translation_scale(new UnitCubeShape3D(), [0, 1, 0], [2, 2, 2]);
44
+
45
+ expect(a.equals(c)).toBe(false);
46
+
47
+ const d = TransformedShape3D.from_translation_scale(new UnitCubeShape3D(), [0, 0, 0], [2, 1, 2]);
48
+
49
+ expect(a.equals(d)).toBe(false);
50
+
51
+ const e = TransformedShape3D.from_translation_scale(new UnitSphereShape3D(), [0, 0, 0], [2, 2, 2]);
52
+
53
+ expect(a.equals(e)).toBe(false);
54
+
55
+ });
@@ -4,6 +4,7 @@ import { binarySearchHighIndex } from "../../../collection/array/binarySearchHig
4
4
  import { compareNumbers } from "../../../primitives/numbers/compareNumbers.js";
5
5
  import { min2 } from "../../../math/min2.js";
6
6
  import { compute_signed_distance_gradient_by_sampling } from "./util/compute_signed_distance_gradient_by_sampling.js";
7
+ import { isArrayEqual } from "../../../collection/ArrayUtils.js";
7
8
 
8
9
  /**
9
10
  * To avoid severe performance overhead, we limit number of possible rejections
@@ -257,4 +258,27 @@ export class UnionShape3D extends AbstractShape3D {
257
258
  break;
258
259
  }
259
260
  }
261
+
262
+ /**
263
+ *
264
+ * @param {UnionShape3D} other
265
+ * @returns {boolean}
266
+ */
267
+ equals(other) {
268
+ return super.equals(other)
269
+ && isArrayEqual(this.children, other.children);
270
+ }
271
+
272
+ hash() {
273
+ const children = this.children;
274
+ const child_count = children.length;
275
+
276
+ let hash = child_count;
277
+
278
+ if (child_count > 0) {
279
+ hash ^= children[0].hash();
280
+ }
281
+
282
+ return hash;
283
+ }
260
284
  }
@@ -98,6 +98,10 @@ export class UnitCubeShape3D extends AbstractShape3D {
98
98
  sample_random_point_in_volume(result, result_offset, random) {
99
99
  randomPointInBox(random, result, result_offset);
100
100
  }
101
+
102
+ hash() {
103
+ return 1;
104
+ }
101
105
  }
102
106
 
103
107
  UnitCubeShape3D.INSTANCE = new UnitCubeShape3D();
@@ -52,6 +52,10 @@ export class UnitSphereShape3D extends AbstractShape3D {
52
52
  sample_random_point_in_volume(result, result_offset, random) {
53
53
  randomPointInSphere(random, result, result_offset);
54
54
  }
55
+
56
+ hash() {
57
+ return 13;
58
+ }
55
59
  }
56
60
 
57
61
  UnitSphereShape3D.INSTANCE = new UnitSphereShape3D();
@@ -0,0 +1,19 @@
1
+ import { type_adapters } from "./type_adapters.js";
2
+
3
+ /**
4
+ *
5
+ * @param {Object} j
6
+ * @returns {AbstractShape3D}
7
+ */
8
+ export function shape_from_json(j) {
9
+ const type = j.type;
10
+
11
+ const adapter = type_adapters[type];
12
+
13
+ if (adapter === undefined) {
14
+ throw new Error(`Unsupported shape type '${type}'`);
15
+ }
16
+
17
+ return adapter.read(j);
18
+ }
19
+
@@ -0,0 +1,21 @@
1
+ import { shape_to_type } from "./shape_to_type.js";
2
+ import { type_adapters } from "./type_adapters.js";
3
+
4
+ /**
5
+ *
6
+ * @param {AbstractShape3D} shape
7
+ * @returns {Object}
8
+ */
9
+ export function shape_to_json(shape) {
10
+ const type = shape_to_type(shape);
11
+
12
+ const adapter = type_adapters[type];
13
+
14
+ if (adapter === undefined) {
15
+ throw new Error(`Unsupported shape type '${type}'`);
16
+ }
17
+
18
+ const shape_json = adapter.write(shape);
19
+
20
+ return Object.assign({ type }, shape_json);
21
+ }
@@ -0,0 +1,23 @@
1
+ import { UnionShape3D } from "../UnionShape3D.js";
2
+ import { UnitCubeShape3D } from "../UnitCubeShape3D.js";
3
+ import { UnitSphereShape3D } from "../UnitSphereShape3D.js";
4
+ import { TransformedShape3D } from "../TransformedShape3D.js";
5
+
6
+ /**
7
+ *
8
+ * @param {AbstractShape3D} shape
9
+ * @returns {string}
10
+ */
11
+ export function shape_to_type(shape) {
12
+ if (shape instanceof UnionShape3D) {
13
+ return 'union';
14
+ } else if (shape instanceof UnitCubeShape3D) {
15
+ return 'cube';
16
+ } else if (shape instanceof UnitSphereShape3D) {
17
+ return 'sphere';
18
+ } else if (shape instanceof TransformedShape3D) {
19
+ return 'transform';
20
+ } else {
21
+ throw new Error('Unsupported shape');
22
+ }
23
+ }
@@ -0,0 +1,58 @@
1
+ import { UnitCubeShape3D } from "../UnitCubeShape3D.js";
2
+ import { UnitSphereShape3D } from "../UnitSphereShape3D.js";
3
+ import { TransformedShape3D } from "../TransformedShape3D.js";
4
+ import { UnionShape3D } from "../UnionShape3D.js";
5
+ import { shape_from_json} from "./shape_from_json.js";
6
+ import { shape_to_json } from "./shape_to_json.js";
7
+
8
+ export const type_adapters = {
9
+ 'cube': {
10
+ read() {
11
+ return UnitCubeShape3D.INSTANCE;
12
+ },
13
+ write() {
14
+ return {};
15
+ }
16
+ },
17
+ 'sphere': {
18
+ read() {
19
+ return UnitSphereShape3D.INSTANCE;
20
+ },
21
+ write() {
22
+ return {};
23
+ }
24
+ },
25
+ 'transform': {
26
+ read({ transform, subject }) {
27
+ const subject_shape = shape_from_json(subject);
28
+
29
+ return TransformedShape3D.from_m4(subject_shape, transform);
30
+ },
31
+ /**
32
+ *
33
+ * @param {TransformedShape3D} object
34
+ */
35
+ write(object) {
36
+ return {
37
+ subject: shape_to_json(object.subject),
38
+ transform: Array.from(object.transform)
39
+ };
40
+ }
41
+ },
42
+ 'union': {
43
+ read({ members }) {
44
+ const member_shapes = members.map(shape_from_json);
45
+
46
+ return UnionShape3D.from(member_shapes);
47
+ },
48
+ /**
49
+ *
50
+ * @param {UnionShape3D} object
51
+ */
52
+ write(object) {
53
+ return {
54
+ members: object.children.map(shape_to_json)
55
+ };
56
+ }
57
+ }
58
+ };
@@ -128,16 +128,6 @@ test("computeHashFloat", () => {
128
128
  expect(computeHashFloat(500)).not.toEqual(computeHashFloat(400))
129
129
  });
130
130
 
131
- test("computeHashFloatArray", () => {
132
- expect(computeHashFloatArray([1, 2, 1.5], 1, 2)).toEqual(computeHashFloatArray([1, 2, 1.5], 1, 2));
133
-
134
- expect(computeHashFloatArray([1, 2, 1.5], 1, 3)).not.toEqual(computeHashFloatArray([1, 2, 1.5], 1, 2));
135
-
136
- expect(computeHashFloatArray([1, 2, 1.5], 2, 3)).not.toEqual(computeHashFloatArray([1, 2, 1.5], 1, 3));
137
-
138
- expect(computeHashFloatArray([1, 2, 3], 1, 3)).not.toEqual(computeHashFloatArray([1, 2, 1.5], 1, 3));
139
- });
140
-
141
131
 
142
132
  test('sign', () => {
143
133
  expect(sign(1)).toBe(1);
@@ -3,28 +3,17 @@ import { computeHashFloat } from "./computeHashFloat.js";
3
3
  /**
4
4
  *
5
5
  * @param {number[]|Float32Array|Float64Array} values
6
- * @param {number} min
7
- * @param {number} max
8
6
  */
9
- export function computeHashFloatArray(values, min, max) {
7
+ export function computeHashFloatArray(values) {
10
8
  const dataLength = values.length;
11
9
 
12
- const dataRange = max - min;
13
-
14
- /*
15
- hash min and max
16
- */
17
- const minHash = computeHashFloat(min);
18
- const maxHash = computeHashFloat(min);
19
-
20
- let hash = ((minHash << 5) - minHash) + maxHash;
10
+ let hash = dataLength;
21
11
 
22
12
  for (let i = 0; i < dataLength; i++) {
23
13
  const singleValue = values[i];
24
- //normalize value
25
- const valueNormalized = (singleValue - min) / dataRange;
14
+
26
15
  //scale value to integer range
27
- const integerValue = valueNormalized * 4294967295;
16
+ const integerValue = computeHashFloat(singleValue);
28
17
 
29
18
  hash = ((hash << 5) - hash) + integerValue;
30
19
  hash |= 0; // Convert to 32bit integer
@@ -0,0 +1,11 @@
1
+ import { computeHashFloatArray } from "./computeHashFloatArray.js";
2
+
3
+ test("computeHashFloatArray", () => {
4
+ expect(computeHashFloatArray([1, 2, 1.5])).toEqual(computeHashFloatArray([1, 2, 1.5]));
5
+
6
+ expect(computeHashFloatArray([1, 2, 1.5])).not.toEqual(computeHashFloatArray([1, 3, 1.5]));
7
+
8
+ expect(computeHashFloatArray([1, 2, 1.5])).not.toEqual(computeHashFloatArray([1, 2, 2.5]));
9
+
10
+ expect(computeHashFloatArray([1, 2, 3])).not.toEqual(computeHashFloatArray([1, 2, 1.5]));
11
+ });
@@ -296,8 +296,8 @@ export class ParameterLookupTable {
296
296
  //special case, all values are the same
297
297
  return computeHashIntegerArray(valueMin);
298
298
  } else {
299
- const dataHash = computeHashFloatArray(this.data, valueMin, valueMax);
300
- const positionHash = computeHashFloatArray(this.positions, 0, 1);
299
+ const dataHash = computeHashFloatArray(this.data);
300
+ const positionHash = computeHashFloatArray(this.positions);
301
301
 
302
302
  return computeHashIntegerArray(dataHash, positionHash, this.itemSize);
303
303
  }
@@ -3,6 +3,7 @@ import { DataType2TypedArrayConstructorMapping } from "../../../../../../../core
3
3
  import { array_copy } from "../../../../../../../core/collection/array/copyArray.js";
4
4
  import { min2 } from "../../../../../../../core/math/min2.js";
5
5
  import { assert } from "../../../../../../../core/assert.js";
6
+ import { ParameterLookupTable } from "../../parameter/ParameterLookupTable.js";
6
7
 
7
8
  export class AttributeValue {
8
9
  constructor() {
@@ -142,4 +143,57 @@ export class AttributeValue {
142
143
  read_value(destination) {
143
144
  array_copy(this.__value, 0, destination, 0, this.spec.itemSize);
144
145
  }
146
+
147
+ /**
148
+ *
149
+ * @param {number[]} [value]
150
+ * @param {Object} [lookup]
151
+ * @param {number} [lookup_value]
152
+ */
153
+ fromJSON({
154
+ value,
155
+ lookup,
156
+ lookup_value
157
+ }) {
158
+
159
+ if (value !== undefined) {
160
+ if (Array.isArray(value)) {
161
+
162
+ if (lookup !== undefined) {
163
+ console.warn("Both 'lookup' and 'value' were specified, expected one or the other. Ignoring the value attribute.");
164
+ } else {
165
+ this.write_value(value);
166
+ }
167
+
168
+ } else {
169
+ throw new Error(`Expected 'value' to be an array, instead got something else (=${value})`);
170
+ }
171
+ } else {
172
+ this.__value = [];
173
+ }
174
+
175
+ if (lookup !== undefined) {
176
+ const lut = new ParameterLookupTable();
177
+
178
+ lut.fromJSON(lookup);
179
+
180
+ this.lookup = lut;
181
+ } else {
182
+ this.lookup = null;
183
+ }
184
+
185
+ if (lookup_value !== undefined) {
186
+ assert.isNumber(lookup_value, 'lookup_value');
187
+ assert.isFiniteNumber(lookup_value, 'lookup_value');
188
+
189
+ if (lookup === undefined) {
190
+ console.warn("found 'lookup_value' attribute, but no 'lookup' table. Ignoring lookup value");
191
+ } else {
192
+ this.lookup_value = lookup_value;
193
+ }
194
+ } else if (lookup !== undefined) {
195
+ // supply a default lookup value
196
+ this.lookup_value = 0;
197
+ }
198
+ }
145
199
  }
@@ -12,6 +12,7 @@ import { AttributeValue } from "./AttributeValue.js";
12
12
  import { PARTICULAR_PARTICLE_SPECIFICATION } from "../../emitter/PARTICULAR_PARTICLE_SPECIFICATION.js";
13
13
  import { SerializationMetadata } from "../../../../../../ecs/components/SerializationMetadata.js";
14
14
  import { UnitCubeShape3D } from "../../../../../../../core/geom/3d/shape/UnitCubeShape3D.js";
15
+ import { shape_from_json } from "../../../../../../../core/geom/3d/shape/json/shape_from_json.js";
15
16
 
16
17
  /**
17
18
  *
@@ -320,6 +321,50 @@ export class ParticleVolume {
320
321
  attributes.size.write_value_silent([particle_size]);
321
322
  }
322
323
 
324
+ /**
325
+ *
326
+ * @param {number} density
327
+ * @param {boolean} [is_density_size_adjusted]
328
+ * @param {number} [particle_size]
329
+ * @param {Object} shape
330
+ * @param {boolean} [is_sorted]
331
+ * @param {string} sprite
332
+ * @param {Object<Object>} [attributes]
333
+ */
334
+ fromJSON({
335
+ density,
336
+ is_density_size_adjusted = true,
337
+ particle_size = 1,
338
+ shape,
339
+ is_sorted = true,
340
+ sprite,
341
+ attributes = {}
342
+ }) {
343
+
344
+ const shape_object = shape_from_json(shape);
345
+
346
+ const actual_density = is_density_size_adjusted ? density / particle_size : density;
347
+
348
+ this.from({
349
+ density: actual_density,
350
+ shape: shape_object,
351
+ particle_size,
352
+ sort: is_sorted,
353
+ sprite
354
+ });
355
+
356
+ for (const attributesKey in attributes) {
357
+ const attribute_object = this.attributes[attributesKey];
358
+
359
+ if (attribute_object === undefined) {
360
+ console.warn(`No '${attributesKey}' attribute, skipping`);
361
+ }
362
+
363
+ const attribute_json = attributes[attributesKey];
364
+ attribute_object.fromJSON(attribute_json);
365
+ }
366
+ }
367
+
323
368
  /**
324
369
  *
325
370
  * @private
@@ -328,9 +328,8 @@ async function sample_scene_0(engine) {
328
328
  ecd: ecd,
329
329
  lut: heatmap_lut_0,
330
330
  shape: UnionShape3D.from([
331
- TransformedShape3D.from_m4(new UnitCubeShape3D(), [14.980091485108495, 0, 0, 0, 0, 2.812495250139616, 0, 0, 0, 0, 7.469446746375293, 0, 54.83080514238756, 1.568241402881399, 92.51937192481576, 1]),
332
- TransformedShape3D.from_m4(new UnitCubeShape3D(), [6.644515553810907, 0, 0, 0, 0, 2.812495250139616, 0, 0, 0, 0, 5.507507223409315, 0, 46.95032009188344, 1.568241402881399, 95.54148649329517, 1]),
333
- TransformedShape3D.from_m4(new UnitCubeShape3D(), [23.762890107594952, 0, 0, 0, 0, 2.812495250139616, 0, 0, 0, 0, 34.57956316503163, 0, 55.436098243647756, 1.568241402881399, 113.46988143189215, 1])
331
+ TransformedShape3D.from_m4(new UnitCubeShape3D(), mat_from_aabb3(new AABB3(67.73, 0, 89, 115.84, 3, 118.34))),
332
+ TransformedShape3D.from_m4(new UnitCubeShape3D(), mat_from_aabb3(new AABB3(98, 0, 76.5, 109.5, 3, 89)))
334
333
  ]),
335
334
  particle_size: 2,
336
335
  density: 1,
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "productName": "Meep",
6
6
  "description": "production-ready JavaScript game engine based on Entity Component System Architecture",
7
7
  "author": "Alexander Goldring",
8
- "version": "2.33.54",
8
+ "version": "2.33.55",
9
9
  "dependencies": {
10
10
  "gl-matrix": "3.4.3",
11
11
  "fast-levenshtein": "2.0.6",