@woosh/meep-engine 2.48.17 → 2.48.19

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 (48) hide show
  1. package/package.json +1 -1
  2. package/src/core/binary/dec2hex.spec.js +13 -0
  3. package/src/core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.d.ts +18 -0
  4. package/src/core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.js +18 -0
  5. package/src/core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.spec.js +56 -0
  6. package/src/core/cache/Cache.d.ts +5 -0
  7. package/src/core/cache/Cache.js +8 -1
  8. package/src/core/cache/Cache.spec.js +47 -3
  9. package/src/core/codegen/LineBuilder.js +117 -106
  10. package/src/core/collection/HashMap.spec.js +32 -0
  11. package/src/core/collection/list/List.js +6 -4
  12. package/src/core/geom/Matrix4.js +4 -1
  13. package/src/core/geom/Quaternion.js +4 -0
  14. package/src/core/geom/Vector3.js +9 -31
  15. package/src/core/graph/Edge.js +11 -2
  16. package/src/core/graph/v2/Graph.js +36 -31
  17. package/src/core/graph/v2/Graph.spec.js +309 -1
  18. package/src/core/math/fract.spec.js +11 -0
  19. package/src/core/math/isPowerOfTwo.spec.js +9 -0
  20. package/src/core/math/isPowerOrTwo.js +5 -0
  21. package/src/core/math/newton_solver_1d.js +1 -1
  22. package/src/core/math/newton_solver_1d.spec.js +9 -0
  23. package/src/core/math/pingpong.spec.js +11 -0
  24. package/src/core/math/random/randomBytes.js +16 -0
  25. package/src/core/math/random/randomFloatBetween.spec.js +8 -0
  26. package/src/core/math/random/randomFromArray.js +3 -3
  27. package/src/core/math/random/randomIntegerBetween.js +5 -0
  28. package/src/core/math/random/randomIntegerBetween.spec.js +7 -0
  29. package/src/core/math/statistics/halton_sequence.spec.js +17 -0
  30. package/src/engine/asset/AssetManager.d.ts +11 -1
  31. package/src/engine/asset/AssetManager.spec.js +2 -2
  32. package/src/engine/control/ControlContext.js +2 -1
  33. package/src/engine/ecs/EntityBuilder.d.ts +6 -0
  34. package/src/engine/ecs/EntityBuilder.js +2 -22
  35. package/src/engine/ecs/EntityBuilder.spec.js +71 -1
  36. package/src/engine/ecs/EntityBuilderFlags.js +20 -0
  37. package/src/engine/ecs/EntityComponentDataset.js +3 -6
  38. package/src/engine/ecs/dynamic_actions/DynamicActorSystem.js +2 -1
  39. package/src/engine/ecs/gui/menu/radial/RadialContextMenu.js +2 -1
  40. package/src/engine/ecs/guid/GUID.js +48 -26
  41. package/src/engine/ecs/guid/GUID.spec.js +56 -10
  42. package/src/engine/ecs/speaker/VoiceSystem.js +2 -1
  43. package/src/engine/ecs/transform/Transform.d.ts +11 -1
  44. package/src/engine/ecs/transform/Transform.js +6 -3
  45. package/src/engine/ecs/transform/Transform.spec.js +67 -1
  46. package/src/engine/graphics/ecs/decal/v2/prototypeDecalSystem.js +2 -1
  47. package/src/engine/physics/fluid/Fluid.js +3 -0
  48. /package/src/core/geom/{Matrix3.js → m3_determinant.js} +0 -0
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "description": "Fully featured ECS game engine written in JavaScript",
6
6
  "type": "module",
7
7
  "author": "Alexander Goldring",
8
- "version": "2.48.17",
8
+ "version": "2.48.19",
9
9
  "main": "build/meep.module.js",
10
10
  "module": "build/meep.module.js",
11
11
  "exports": {
@@ -0,0 +1,13 @@
1
+ import { dec2hex } from "./dec2hex.js";
2
+
3
+ test("zero padding", () => {
4
+ expect(dec2hex(0)).toBe("00");
5
+ expect(dec2hex(10)).toBe("0a");
6
+ });
7
+
8
+ test("two digits", () => {
9
+
10
+ expect(dec2hex(16)).toBe("10");
11
+ expect(dec2hex(255)).toBe("ff");
12
+
13
+ });
@@ -1,6 +1,8 @@
1
1
  export class ExplicitBinaryBoundingVolumeHierarchy {
2
2
  readonly root: number
3
3
 
4
+ node_capacity: number
5
+
4
6
  allocate_node(): number
5
7
 
6
8
  release_node(node: number): void
@@ -19,6 +21,22 @@ export class ExplicitBinaryBoundingVolumeHierarchy {
19
21
 
20
22
  node_get_user_data(node: number): number
21
23
 
24
+ node_set_child1(node: number, child1: number): void
25
+
26
+ node_get_child1(node: number): number
27
+
28
+ node_set_child2(node: number, child1: number): void
29
+
30
+ node_get_child2(node: number): number
31
+
32
+ node_set_parent(node: number, parent: number): void
33
+
34
+ node_get_parent(node: number): number
35
+
36
+ node_set_height(node: number, height: number): void
37
+
38
+ node_get_height(node: number): number
39
+
22
40
  release_all(): void
23
41
 
24
42
  trim(): void
@@ -130,6 +130,18 @@ export class ExplicitBinaryBoundingVolumeHierarchy {
130
130
  return this.__capacity;
131
131
  }
132
132
 
133
+ /**
134
+ *
135
+ * @param {number} v
136
+ */
137
+ set node_capacity(v) {
138
+ if (this.__size > v) {
139
+ throw new Error(`Can't shrink capacity to ${v}, because it's below occupancy(${this.__size}).`);
140
+ }
141
+
142
+ this.__set_capacity(v);
143
+ }
144
+
133
145
  __grow_capacity() {
134
146
  const new_capacity = Math.ceil(max2(
135
147
  this.__capacity * CAPACITY_GROW_MULTIPLIER,
@@ -145,6 +157,12 @@ export class ExplicitBinaryBoundingVolumeHierarchy {
145
157
  * @private
146
158
  */
147
159
  __set_capacity(new_capacity) {
160
+ assert.isNonNegativeInteger(new_capacity, 'new_capacity');
161
+
162
+ if (this.__capacity === new_capacity) {
163
+ // already at the exact desired capacity
164
+ return;
165
+ }
148
166
 
149
167
  const old_data_uint32 = this.__data_uint32;
150
168
 
@@ -162,3 +162,59 @@ test("release_all with 1 leaf", () => {
162
162
 
163
163
  expect(bvh.root).toBe(NULL_NODE);
164
164
  });
165
+
166
+ test("capacity grows as necessary when allocating", () => {
167
+ const bvh = new ExplicitBinaryBoundingVolumeHierarchy();
168
+
169
+ bvh.node_capacity = 1;
170
+
171
+ bvh.allocate_node();
172
+
173
+ bvh.allocate_node();
174
+
175
+ expect(bvh.node_capacity).toBeGreaterThanOrEqual(2);
176
+ });
177
+
178
+ test("trim sets capacity to exact allocated size", () => {
179
+ const bvh = new ExplicitBinaryBoundingVolumeHierarchy();
180
+
181
+ bvh.node_capacity = 1;
182
+
183
+ bvh.allocate_node();
184
+
185
+ bvh.allocate_node();
186
+
187
+ bvh.trim();
188
+
189
+ expect(bvh.node_capacity).toBe(2);
190
+ });
191
+
192
+
193
+ test("node property setters/getters", () => {
194
+
195
+ const bvh = new ExplicitBinaryBoundingVolumeHierarchy();
196
+
197
+ const node = bvh.allocate_node();
198
+
199
+ bvh.node_set_user_data(node, 3);
200
+
201
+ expect(bvh.node_get_user_data(node)).toBe(3);
202
+
203
+ bvh.node_set_child1(node, 7);
204
+ bvh.node_set_child2(node, 11);
205
+ bvh.node_set_parent(node, 13);
206
+ bvh.node_set_height(node, 17);
207
+
208
+ const expected_aabb = new Float32Array([1.1, 3.2, 7.3, 11.4, 13.5, 17.6]);
209
+ bvh.node_set_aabb(node, expected_aabb);
210
+
211
+ expect(bvh.node_get_child1(node)).toBe(7);
212
+ expect(bvh.node_get_child2(node)).toBe(11);
213
+ expect(bvh.node_get_parent(node)).toBe(13);
214
+ expect(bvh.node_get_height(node)).toBe(17);
215
+
216
+ const actual_aabb = [];
217
+ bvh.node_get_aabb(node, actual_aabb)
218
+
219
+ expect(actual_aabb).toEqual(Array.from(expected_aabb));
220
+ });
@@ -1,3 +1,5 @@
1
+ import Signal from "../events/signal/Signal";
2
+
1
3
  export class Cache<K, V> {
2
4
  constructor({
3
5
  maxWeight,
@@ -15,6 +17,9 @@ export class Cache<K, V> {
15
17
  capacity?: number
16
18
  })
17
19
 
20
+ readonly onEvicted: Signal<K, V>
21
+ readonly onRemoved: Signal<K, V>
22
+
18
23
  readonly weight: number
19
24
  readonly maxWeight: number
20
25
 
@@ -3,6 +3,7 @@ import { HashMap } from "../collection/HashMap.js";
3
3
  import Signal from "../events/signal/Signal.js";
4
4
  import { returnOne, returnZero, strictEquals } from "../function/Functions.js";
5
5
  import { CacheElement } from "./CacheElement.js";
6
+ import { collectIteratorValueToArray } from "../collection/IteratorUtils.js";
6
7
 
7
8
  /**
8
9
  * TODO validate hashes of held elements to keep them up to date. Keys are assumed to be immutable, but this assumption may be broken by users
@@ -419,7 +420,13 @@ export class Cache {
419
420
  * Remove all elements from the cache
420
421
  */
421
422
  clear() {
422
- const keys = Array.from(this.data.keys());
423
+ /**
424
+ *
425
+ * @type {Key[]}
426
+ */
427
+ const keys = [];
428
+
429
+ collectIteratorValueToArray(keys,this.data.keys());
423
430
 
424
431
  const n = keys.length;
425
432
  for (let i = 0; i < n; i++) {
@@ -88,11 +88,55 @@ test("remove method", () => {
88
88
  expect(cache.get("a")).toBe(null);
89
89
  });
90
90
 
91
- test("replacing element with the same key",()=>{
91
+ test("replacing element with the same key", () => {
92
92
  const cache = new Cache();
93
93
 
94
- cache.put("a","b");
95
- cache.put("a","c");
94
+ cache.put("a", "b");
95
+ cache.put("a", "c");
96
96
 
97
97
  expect(cache.get("a")).toBe("c");
98
98
  });
99
+
100
+ test("getOrCompute", () => {
101
+
102
+ const cache = new Cache();
103
+
104
+ const tokens = [1, 3, 5];
105
+
106
+ const compute = () => tokens.shift();
107
+
108
+ expect(cache.getOrCompute("x", compute)).toBe(1);
109
+
110
+ expect(cache.get("x")).toBe(1);
111
+
112
+ expect(cache.getOrCompute("x", compute)).toBe(1);
113
+
114
+ expect(cache.getOrCompute("y", compute)).toBe(3);
115
+
116
+ expect(cache.get("y")).toBe(3);
117
+
118
+ expect(cache.getOrCompute("y", compute)).toBe(3);
119
+
120
+ })
121
+
122
+ test("clear on empty does not throw", () => {
123
+
124
+ const cache = new Cache();
125
+
126
+ cache.clear();
127
+
128
+ });
129
+
130
+ test("clear with a single entry", () => {
131
+
132
+ const cache = new Cache();
133
+
134
+ cache.set("x", 1);
135
+
136
+ expect(cache.contains("x")).toBe(true);
137
+
138
+ cache.clear();
139
+
140
+ expect(cache.contains("x")).toBe(false);
141
+
142
+ });
@@ -3,146 +3,157 @@
3
3
  */
4
4
 
5
5
 
6
- /**
7
- *
8
- * @param {String} text
9
- * @param {Number} indent
10
- * @constructor
11
- */
12
- const Line = function (text, indent) {
13
- this.text = text;
14
- this.indentation = indent;
15
- };
6
+ class Line {
7
+ /**
8
+ *
9
+ * @param {string} text
10
+ * @param {number} indent
11
+ * @constructor
12
+ */
13
+ constructor(text, indent) {
14
+ this.text = text;
15
+ this.indentation = indent;
16
+ }
17
+ }
18
+
19
+ class LineBuilder {
16
20
 
17
- const LineBuilder = function () {
18
21
  /**
19
22
  *
20
- * @type {Array.<Line>}
23
+ * @type {Line[]}
21
24
  */
22
- this.lines = [];
23
- this.indentation = 0;
24
- this.indentSpaces = 4;
25
- };
25
+ lines = [];
26
+ /**
27
+ *
28
+ * @type {number}
29
+ */
30
+ indentation = 0;
31
+ /**
32
+ *
33
+ * @type {number}
34
+ */
35
+ indentSpaces = 4;
26
36
 
27
- /**
28
- *
29
- * @param {string} text
30
- * @returns {LineBuilder}
31
- */
32
- LineBuilder.fromText = function (text) {
33
- const r = new LineBuilder();
37
+ /**
38
+ *
39
+ * @return {boolean}
40
+ * @param {string} term
41
+ */
42
+ containsSubstring(term) {
43
+ const lines = this.lines;
44
+ const n = lines.length;
45
+ for (let i = 0; i < n; i++) {
46
+ const line = lines[i];
34
47
 
35
- const lines = text.split('\n');
48
+ const termIndex = line.text.indexOf(term);
36
49
 
37
- const n = lines.length;
50
+ if (termIndex !== -1) {
51
+ return true;
52
+ }
53
+ }
38
54
 
39
- for (let i = 0; i < n; i++) {
55
+ return false;
56
+ }
40
57
 
41
- r.add(lines[i]);
58
+ /**
59
+ *
60
+ * @returns {LineBuilder}
61
+ */
62
+ indent() {
63
+ this.indentation++;
64
+ return this;
65
+ }
42
66
 
67
+ /**
68
+ *
69
+ * @returns {LineBuilder}
70
+ */
71
+ dedent() {
72
+ this.indentation--;
73
+ return this;
43
74
  }
44
75
 
45
- return r;
46
- }
76
+ /**
77
+ *
78
+ * @param {string} line_text
79
+ * @returns {LineBuilder}
80
+ */
81
+ add(line_text) {
82
+ const line = new Line(line_text, this.indentation);
83
+ this.lines.push(line);
84
+ return this;
85
+ }
47
86
 
48
- /**
49
- *
50
- * @return {boolean}
51
- * @param {string} term
52
- */
53
- LineBuilder.prototype.containsSubstring = function (term) {
54
- const lines = this.lines;
55
- const n = lines.length;
56
- for (let i = 0; i < n; i++) {
57
- const line = lines[i];
87
+ /**
88
+ *
89
+ * @param {LineBuilder} lines
90
+ */
91
+ addLines(lines) {
58
92
 
59
- const termIndex = line.text.indexOf(term);
93
+ const other_lines = lines.lines;
60
94
 
61
- if (termIndex !== -1) {
62
- return true;
63
- }
64
- }
95
+ const other_line_count = other_lines.length;
65
96
 
66
- return false;
67
- };
97
+ for (let i = 0; i < other_line_count; i++) {
98
+ const otherLine = other_lines[i];
68
99
 
69
- /**
70
- *
71
- * @returns {LineBuilder}
72
- */
73
- LineBuilder.prototype.indent = function () {
74
- this.indentation++;
75
- return this;
76
- };
100
+ const line = new Line(otherLine.text, otherLine.indentation + this.indentation);
77
101
 
78
- /**
79
- *
80
- * @returns {LineBuilder}
81
- */
82
- LineBuilder.prototype.dedent = function () {
83
- this.indentation--;
84
- return this;
85
- };
102
+ this.lines.push(line);
103
+ }
86
104
 
87
- /**
88
- *
89
- * @param {string} l
90
- * @returns {LineBuilder}
91
- */
92
- LineBuilder.prototype.add = function (l) {
93
- const line = new Line(l, this.indentation);
94
- this.lines.push(line);
95
- return this;
96
- };
105
+ }
97
106
 
98
- /**
99
- *
100
- * @param {LineBuilder} lines
101
- */
102
- LineBuilder.prototype.addLines = function (lines) {
107
+ clear() {
108
+ this.lines = [];
109
+ this.indentation = 0;
110
+ this.indentSpaces = 4;
111
+ }
112
+
113
+ /**
114
+ *
115
+ * @returns {string}
116
+ */
117
+ build() {
118
+ const result = [];
119
+ let i, j, l;
103
120
 
104
- const other_lines = lines.lines;
105
121
 
106
- const other_line_count = other_lines.length;
122
+ const lines = this.lines;
123
+ for (i = 0, l = lines.length; i < l; i++) {
124
+ const line = lines[i];
107
125
 
108
- for (let i = 0; i < other_line_count; i++) {
109
- const otherLine = other_lines[i];
126
+ let indentString = '';
127
+ for (j = 0; j < line.indentation * this.indentSpaces; j++) {
128
+ indentString += ' ';
129
+ }
110
130
 
111
- const line = new Line(otherLine.text, otherLine.indentation + this.indentation);
131
+ result.push(indentString + line.text);
132
+ }
112
133
 
113
- this.lines.push(line);
134
+ return result.join('\n');
114
135
  }
115
136
 
116
- }
137
+ /**
138
+ *
139
+ * @param {string} text
140
+ * @returns {LineBuilder}
141
+ */
142
+ static fromText(text) {
143
+ const r = new LineBuilder();
117
144
 
118
- LineBuilder.prototype.clear = function () {
119
- this.lines = [];
120
- this.indentation = 0;
121
- this.indentSpaces = 4;
122
- };
145
+ const lines = text.split('\n');
123
146
 
124
- /**
125
- *
126
- * @returns {string}
127
- */
128
- LineBuilder.prototype.build = function () {
129
- const result = [];
130
- let i, j, l;
147
+ const n = lines.length;
131
148
 
149
+ for (let i = 0; i < n; i++) {
132
150
 
133
- const lines = this.lines;
134
- for (i = 0, l = lines.length; i < l; i++) {
135
- const line = lines[i];
151
+ r.add(lines[i]);
136
152
 
137
- let indentString = '';
138
- for (j = 0; j < line.indentation * this.indentSpaces; j++) {
139
- indentString += ' ';
140
153
  }
141
154
 
142
- result.push(indentString + line.text);
155
+ return r;
143
156
  }
144
-
145
- return result.join('\n');
146
- };
157
+ }
147
158
 
148
159
  export default LineBuilder;
@@ -50,6 +50,38 @@ test("put 2 values with same hash", () => {
50
50
  expect(m.get("b")).toBe(42);
51
51
  });
52
52
 
53
+ test("overwrite a value with the same key", () => {
54
+
55
+ const map = new HashMap({
56
+ keyHashFunction: returnOne,
57
+ keyEqualityFunction: strictEquals
58
+ });
59
+
60
+ map.set("x", 3);
61
+ map.set("x", 7);
62
+
63
+ expect(map.get("x")).toBe(7);
64
+
65
+ });
66
+
67
+ test("getOrCompute", () => {
68
+
69
+ const values = [1, 3, 5];
70
+
71
+ const compute = () => values.shift();
72
+
73
+ const map = new HashMap({
74
+ keyHashFunction: returnOne,
75
+ keyEqualityFunction: strictEquals
76
+ });
77
+
78
+ expect(map.getOrCompute("x", compute)).toBe(1);
79
+ expect(map.getOrCompute("x", compute)).toBe(1);
80
+
81
+ expect(map.getOrCompute("y", compute)).toBe(3);
82
+ expect(map.getOrCompute("y", compute)).toBe(3);
83
+ });
84
+
53
85
  test("retrieval works as intended after hash map grows", () => {
54
86
  const m = new HashMap({
55
87
  keyHashFunction: passThrough,
@@ -171,7 +171,9 @@ List.prototype.crop = function (startIndex, endIndex) {
171
171
  };
172
172
 
173
173
  /**
174
- *
174
+ * Replace the data, replacements is performed surgically, meaning that diff is computed and add/remove operations are performed on the set
175
+ * This method is tailored to work well with visualisation as only elements that's missing from the new set is removed, and only elements that are new to are added
176
+ * Conversely, relevant events are dispatched that a visualisation can observe. This results in fewer changes required to the visualisation
175
177
  * @param {T[]} newOutput
176
178
  */
177
179
  List.prototype.patch = function (newOutput) {
@@ -754,7 +756,7 @@ List.prototype.toJSON = function () {
754
756
  List.prototype.fromJSON = function (json, constructor) {
755
757
  this.reset();
756
758
 
757
- assert.isArray(json,'json');
759
+ assert.isArray(json, 'json');
758
760
 
759
761
  if (typeof constructor === "function") {
760
762
  this.addAll(json.map(function (elJSON) {
@@ -780,7 +782,7 @@ List.prototype.toBinaryBuffer = function (buffer) {
780
782
  for (let i = 0; i < n; i++) {
781
783
  const item = this.data[i];
782
784
 
783
- if(typeof item.toBinaryBuffer !== "function"){
785
+ if (typeof item.toBinaryBuffer !== "function") {
784
786
  throw new Error('item.toBinaryBuffer is not a function');
785
787
  }
786
788
 
@@ -798,7 +800,7 @@ List.prototype.fromBinaryBuffer = function (buffer, constructor) {
798
800
 
799
801
  const el = new constructor();
800
802
 
801
- if(typeof el.fromBinaryBuffer !== "function"){
803
+ if (typeof el.fromBinaryBuffer !== "function") {
802
804
  throw new Error('item.fromBinaryBuffer is not a function');
803
805
  }
804
806
 
@@ -4,6 +4,9 @@ const _z = new Vector3();
4
4
  const _x = new Vector3();
5
5
  const _y = new Vector3();
6
6
 
7
+ /**
8
+ * @deprecated use arrays instead or mat4 from gl-matrix
9
+ */
7
10
  export class Matrix4 {
8
11
  /**
9
12
  *
@@ -197,7 +200,7 @@ export class Matrix4 {
197
200
  const sz = Math.hypot(this.c0, this.c1, this.c2);
198
201
 
199
202
  // if determine is negative, we need to invert one scale
200
- const det = this.determinant();
203
+ const det = this.determinant();
201
204
  if (det < 0) sx = -sx;
202
205
 
203
206
  position.set(
@@ -1675,6 +1675,10 @@ class Quaternion {
1675
1675
  );
1676
1676
  }
1677
1677
 
1678
+ toString() {
1679
+ return `{ x: ${this.x}, y: ${this.y}, z: ${this.z}, w: ${this.w} }`;
1680
+ }
1681
+
1678
1682
  /**
1679
1683
  *
1680
1684
  * @param {function} random random number generator function