@woosh/meep-engine 2.84.3 → 2.84.5

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 (34) hide show
  1. package/build/meep.cjs +163 -109
  2. package/build/meep.min.js +1 -1
  3. package/build/meep.module.js +163 -109
  4. package/editor/view/ecs/EntityEditor.js +7 -8
  5. package/package.json +1 -1
  6. package/src/core/math/random/roundFair.js +5 -2
  7. package/src/core/math/random/roundFair.spec.js +12 -0
  8. package/src/core/math/random/seededRandom_Mulberry32.spec.js +19 -0
  9. package/src/core/model/node-graph/node/NodeInstancePortReference.spec.js +25 -2
  10. package/src/core/model/node-graph/node/Port.js +8 -6
  11. package/src/core/model/node-graph/node/Port.spec.js +49 -0
  12. package/src/core/process/matcher/and.js +16 -0
  13. package/src/core/process/matcher/isDefined.js +8 -0
  14. package/src/core/process/matcher/isEqualTo.js +10 -0
  15. package/src/core/process/matcher/isGreaterThan.js +10 -0
  16. package/src/core/process/matcher/isGreaterThanOrEqualTo.js +10 -0
  17. package/src/core/process/matcher/isInstanceOf.js +10 -0
  18. package/src/core/process/matcher/isLessThan.js +10 -0
  19. package/src/core/process/matcher/isLessThanOrEqualTo.js +10 -0
  20. package/src/core/process/matcher/isNull.js +8 -0
  21. package/src/core/process/matcher/isTypeOf.js +16 -0
  22. package/src/core/process/matcher/not.js +10 -0
  23. package/src/core/process/matcher/or.js +16 -0
  24. package/src/core/process/task/util/actionTask.spec.js +14 -0
  25. package/src/core/process/task/util/countTask.js +1 -1
  26. package/src/core/process/task/util/countTask.spec.js +18 -0
  27. package/src/engine/asset/AssetDescription.js +6 -0
  28. package/src/engine/ecs/Entity.js +9 -4
  29. package/src/engine/ecs/EntityComponentDataset.js +78 -26
  30. package/src/view/common/LabelView.js +3 -1
  31. package/src/view/elements/windrose/WindRoseDiagram.js +3 -3
  32. package/src/core/math/spline/spline_catmullrom_1d.js +0 -120
  33. package/src/core/process/IntervalExecutor.js +0 -78
  34. package/src/core/process/matcher/Matchers.js +0 -145
@@ -0,0 +1,10 @@
1
+ /**
2
+ * performs instanceof match
3
+ * @param {function} type
4
+ * @returns {function(*): boolean}
5
+ */
6
+ export function isInstanceOf(type) {
7
+ return function (m) {
8
+ return m instanceof type;
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ *
3
+ * @param {number} value
4
+ * @returns {function(*): boolean}
5
+ */
6
+ export function isLessThan(value) {
7
+ return function (x) {
8
+ return x < value;
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ *
3
+ * @param {number} value
4
+ * @returns {function(*): boolean}
5
+ */
6
+ export function isLessThanOrEqualTo(value) {
7
+ return function (x) {
8
+ return x <= value;
9
+ }
10
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ *
3
+ * @param {*} value
4
+ * @returns {boolean}
5
+ */
6
+ export function isNull(value) {
7
+ return value === null;
8
+ }
@@ -0,0 +1,16 @@
1
+ import { assert } from "../../assert.js";
2
+
3
+ const VALID_TYPES = ['number', 'boolean', 'string', 'function', 'object', 'undefined'];
4
+
5
+ /**
6
+ * performs typeof match
7
+ * @param {string} type
8
+ * @returns {function(*): boolean}
9
+ */
10
+ export function isTypeOf(type) {
11
+ assert.notEqual(VALID_TYPES.indexOf(type), -1, `type must be one of [${VALID_TYPES.join(', ')}], instead was '${type}'`);
12
+
13
+ return function (m) {
14
+ return typeof m === type;
15
+ }
16
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ *
3
+ * @param {function} m
4
+ * @returns {function(*): boolean}
5
+ */
6
+ export function not(m) {
7
+ return function (v) {
8
+ return !m(v);
9
+ }
10
+ }
@@ -0,0 +1,16 @@
1
+ import { assert } from "../../assert.js";
2
+
3
+ /**
4
+ * Joins two matchers via OR
5
+ * @param {function(*):boolean} a
6
+ * @param {function(*):boolean} b
7
+ * @returns {function(*):boolean}
8
+ */
9
+ export function or(a, b) {
10
+ assert.isFunction(a, 'a');
11
+ assert.isFunction(b, 'b');
12
+
13
+ return function (m) {
14
+ return a(m) || b(m);
15
+ }
16
+ }
@@ -0,0 +1,14 @@
1
+ import { jest } from "@jest/globals";
2
+ import { actionTask } from "./actionTask.js";
3
+
4
+ test("main", () => {
5
+
6
+ const mock = jest.fn();
7
+
8
+ const task = actionTask(mock);
9
+
10
+ task.executeSync();
11
+
12
+ expect(mock).toHaveBeenCalledTimes(1);
13
+
14
+ });
@@ -4,7 +4,7 @@ import { TaskSignal } from "../TaskSignal.js";
4
4
  /**
5
5
  *
6
6
  * @param {number|function(*):number} initial
7
- * @param {number|function(*):number} limit
7
+ * @param {number|function(*):number} limit limit is excluded from the count, same as `for(;i < limit;)`
8
8
  * @param {function(index:int)} callback
9
9
  * @returns {Task}
10
10
  */
@@ -0,0 +1,18 @@
1
+ import { jest } from "@jest/globals";
2
+ import { countTask } from "./countTask.js";
3
+
4
+ test("static count", () => {
5
+
6
+ const mock = jest.fn();
7
+
8
+ const task = countTask(3, 7, mock);
9
+
10
+ task.executeSync();
11
+
12
+ expect(mock).toHaveBeenCalledTimes(4);
13
+
14
+ expect(mock).toHaveBeenCalledWith(3)
15
+ expect(mock).toHaveBeenCalledWith(4)
16
+ expect(mock).toHaveBeenCalledWith(5)
17
+ expect(mock).toHaveBeenCalledWith(6)
18
+ });
@@ -49,3 +49,9 @@ export class AssetDescription {
49
49
  return `{ type: '${this.type}', path: '${this.path}' }`;
50
50
  }
51
51
  }
52
+
53
+ /**
54
+ * @readonly
55
+ * @type {boolean}
56
+ */
57
+ AssetDescription.prototype.isAssetDescription = true;
@@ -1,8 +1,8 @@
1
+ import { assert } from "../../core/assert.js";
1
2
  import Signal from "../../core/events/signal/Signal.js";
2
- import {assert} from "../../core/assert.js";
3
- import {EventType} from "./EntityManager.js";
4
- import {isDefined} from "../../core/process/matcher/Matchers.js";
5
- import {EntityFlags} from "./EntityFlags.js";
3
+ import { isDefined } from "../../core/process/matcher/isDefined.js";
4
+ import { EntityFlags } from "./EntityFlags.js";
5
+ import { EventType } from "./EntityManager.js";
6
6
 
7
7
  /**
8
8
  * Set of default flags
@@ -205,6 +205,8 @@ class Entity {
205
205
  * @returns {*|null}
206
206
  */
207
207
  removeComponent(klass) {
208
+ assert.defined(klass, 'klass');
209
+
208
210
  const elements = this.components;
209
211
  const n = elements.length;
210
212
 
@@ -332,6 +334,7 @@ class Entity {
332
334
  dataset.removeEntity(entity);
333
335
 
334
336
  this.id = -1;
337
+ this.generation = -1;
335
338
 
336
339
  this.clearFlag(EntityFlags.Built);
337
340
 
@@ -359,6 +362,7 @@ class Entity {
359
362
  }
360
363
 
361
364
  const entity = this.id = dataset.createEntity();
365
+ this.generation = dataset.getEntityGeneration(entity);
362
366
  this.dataset = dataset;
363
367
 
364
368
  let i;
@@ -416,6 +420,7 @@ class Entity {
416
420
  r.setFlag(EntityFlags.Built);
417
421
  r.id = entity;
418
422
  r.dataset = dataset;
423
+ r.generation = dataset.getEntityGeneration(entity);
419
424
 
420
425
  return r;
421
426
  }
@@ -1,13 +1,14 @@
1
- import {BitSet} from "../../core/binary/BitSet.js";
2
- import {assert} from "../../core/assert.js";
1
+ import { assert } from "../../core/assert.js";
2
+ import { BitSet } from "../../core/binary/BitSet.js";
3
+ import { array_shrink_to_size } from "../../core/collection/array/array_shrink_to_size.js";
4
+ import { arraySetDiff } from "../../core/collection/array/arraySetDiff.js";
3
5
  import Signal, {
4
6
  findSignalHandlerIndexByHandle,
5
7
  findSignalHandlerIndexByHandleAndContext
6
8
  } from "../../core/events/signal/Signal.js";
7
- import {EventType} from "./EntityManager.js";
8
- import {SignalHandler} from "../../core/events/signal/SignalHandler.js";
9
- import {arraySetDiff} from "../../core/collection/array/arraySetDiff.js";
10
- import {array_shrink_to_size} from "../../core/collection/array/array_shrink_to_size.js";
9
+ import { SignalHandler } from "../../core/events/signal/SignalHandler.js";
10
+ import { max3 } from "../../core/math/max3.js";
11
+ import { EventType } from "./EntityManager.js";
11
12
 
12
13
  /**
13
14
  *
@@ -116,6 +117,15 @@ export class EntityComponentDataset {
116
117
  * @type {BitSet}
117
118
  */
118
119
  entityOccupancy = new BitSet();
120
+
121
+ /**
122
+ * For each entity ID records generation when entity was created
123
+ * Values are invalid for unused entity IDs
124
+ * @private
125
+ * @type {Uint32Array}
126
+ */
127
+ entityGeneration = new Uint32Array(0);
128
+
119
129
  /**
120
130
  * @private
121
131
  * @type {BitSet}
@@ -167,7 +177,7 @@ export class EntityComponentDataset {
167
177
 
168
178
  /**
169
179
  * @readonly
170
- * @type {Signal}
180
+ * @type {Signal<number>}
171
181
  */
172
182
  onEntityCreated = new Signal();
173
183
 
@@ -448,6 +458,7 @@ export class EntityComponentDataset {
448
458
  */
449
459
  setComponentTypeMap(map) {
450
460
  assert.defined(map, "map");
461
+ assert.isArray(map, 'map');
451
462
 
452
463
  const newComponentTypeCount = map.length;
453
464
 
@@ -726,38 +737,79 @@ export class EntityComponentDataset {
726
737
 
727
738
  /**
728
739
  *
729
- * @returns {number} entityIndex
740
+ * @param {number} min_size
730
741
  */
731
- createEntity() {
732
- const entityIndex = this.entityOccupancy.nextClearBit(0);
733
- this.entityOccupancy.set(entityIndex, true);
742
+ enlargeGenerationTable(min_size) {
743
+ const old_generation_table_size = this.entityGeneration.length;
734
744
 
735
- this.entityCount++;
745
+ const new_size = max3(
746
+ min_size,
747
+ Math.ceil(old_generation_table_size * 1.2),
748
+ old_generation_table_size + 16
749
+ );
736
750
 
737
- this.onEntityCreated.send1(entityIndex);
751
+ const new_generation_table = new Uint32Array(new_size);
738
752
 
739
- this.generation++;
753
+ new_generation_table.set(this.entityGeneration);
740
754
 
741
- return entityIndex;
755
+ this.entityGeneration = new_generation_table
742
756
  }
743
757
 
744
758
  /**
745
- *
746
- * @param {number} entityIndex
747
- * @throws {Error} if entity index is already in use
759
+ * Produces generation ID for a given entity
760
+ * @param {number} entity_id
761
+ * @returns {number}
762
+ */
763
+ getEntityGeneration(entity_id) {
764
+ return this.entityGeneration[entity_id];
765
+ }
766
+
767
+ /**
768
+ * @private
769
+ * @param {number} entity_id
748
770
  */
749
- createEntitySpecific(entityIndex) {
750
- if (this.entityExists(entityIndex)) {
751
- throw new Error(`EntityId ${entityIndex} is already in use`);
771
+ createEntityUnsafe(entity_id) {
772
+ this.entityOccupancy.set(entity_id, true);
773
+
774
+ // record entity generation
775
+ if (this.entityGeneration.length <= entity_id) {
776
+ // needs to be resized
777
+ this.enlargeGenerationTable(entity_id + 1);
752
778
  }
753
779
 
754
- this.entityOccupancy.set(entityIndex, true);
780
+ const current_generation = this.generation;
781
+ this.generation = current_generation + 1;
782
+
783
+ this.entityGeneration[entity_id] = current_generation;
755
784
 
756
785
  this.entityCount++;
757
786
 
758
- this.onEntityCreated.send1(entityIndex);
787
+ this.onEntityCreated.send1(entity_id);
788
+ }
789
+
790
+ /**
791
+ *
792
+ * @returns {number} entityIndex
793
+ */
794
+ createEntity() {
795
+ const entity_id = this.entityOccupancy.nextClearBit(0);
796
+
797
+ this.createEntityUnsafe(entity_id);
798
+
799
+ return entity_id;
800
+ }
801
+
802
+ /**
803
+ *
804
+ * @param {number} entity_id
805
+ * @throws {Error} if entity index is already in use
806
+ */
807
+ createEntitySpecific(entity_id) {
808
+ if (this.entityExists(entity_id)) {
809
+ throw new Error(`EntityId ${entity_id} is already in use`);
810
+ }
759
811
 
760
- this.generation++;
812
+ this.createEntityUnsafe(entity_id);
761
813
  }
762
814
 
763
815
  /**
@@ -887,7 +939,7 @@ export class EntityComponentDataset {
887
939
  const componentClass = this.componentTypeMap[componentIndex];
888
940
 
889
941
  //dispatch event to components
890
- this.sendEvent(entityIndex, EventType.ComponentRemoved, {klass: componentClass, instance: componentInstance});
942
+ this.sendEvent(entityIndex, EventType.ComponentRemoved, { klass: componentClass, instance: componentInstance });
891
943
  }
892
944
 
893
945
  /**
@@ -999,7 +1051,7 @@ export class EntityComponentDataset {
999
1051
  const componentClass = this.componentTypeMap[componentIndex];
1000
1052
 
1001
1053
  //dispatch event to components
1002
- this.sendEvent(entityIndex, EventType.ComponentAdded, {klass: componentClass, instance: componentInstance});
1054
+ this.sendEvent(entityIndex, EventType.ComponentAdded, { klass: componentClass, instance: componentInstance });
1003
1055
  }
1004
1056
 
1005
1057
  /**
@@ -14,7 +14,9 @@ import ObservedString from "../../core/model/ObservedString.js";
14
14
  import ObservedValue from "../../core/model/ObservedValue.js";
15
15
  import Stat from "../../core/model/stat/Stat.js";
16
16
  import { number_pretty_print } from "../../core/primitives/numbers/number_pretty_print.js";
17
- import { isInstanceOf, isTypeOf, or } from "../../core/process/matcher/Matchers.js";
17
+ import { isInstanceOf } from "../../core/process/matcher/isInstanceOf.js";
18
+ import { isTypeOf } from "../../core/process/matcher/isTypeOf.js";
19
+ import { or } from "../../core/process/matcher/or.js";
18
20
  import { frameThrottle } from "../../engine/graphics/FrameThrottle.js";
19
21
  import View from "../View.js";
20
22
 
@@ -1,10 +1,10 @@
1
- import View from "../../View.js";
2
1
  import { assert } from "../../../core/assert.js";
3
- import EmptyView from "../EmptyView.js";
2
+ import { isDefined } from "../../../core/process/matcher/isDefined.js";
4
3
  import LabelView from "../../common/LabelView.js";
5
4
  import SVG from "../../SVG.js";
5
+ import View from "../../View.js";
6
+ import EmptyView from "../EmptyView.js";
6
7
  import ImageView from "../image/ImageView.js";
7
- import { isDefined } from "../../../core/process/matcher/Matchers.js";
8
8
 
9
9
  export class WindRoseDiagram extends View {
10
10
 
@@ -1,120 +0,0 @@
1
-
2
- /**
3
- *
4
- * @param {number} p0
5
- * @param {number} p1
6
- * @param {number} half_alpha between 0..0.5
7
- * @returns {number}
8
- */
9
- function getT(p0, p1, half_alpha) {
10
- const dx = p0 - p1;
11
-
12
- const a = dx * dx;
13
-
14
- return Math.pow(a, half_alpha);
15
- }
16
-
17
-
18
- /**
19
- * Alpha assumed to be 0.5
20
- * @param {number} f
21
- * @param {number} p0
22
- * @param {number} p1
23
- * @param {number} p2
24
- * @param {number} p3
25
- * @returns {number}
26
- */
27
- export function spline_catmullrom_1d(
28
- f,
29
- p0, p1, p2, p3
30
- ) {
31
- const half_alpha = 0.25;
32
-
33
- // calculate T
34
- const t0 = 0;
35
- let t_01 = getT(p0, p1, half_alpha);
36
- let t_02 = getT(p1, p2, half_alpha);
37
- let t_03 = getT(p2, p3, half_alpha);
38
-
39
- // safety check for repeated points, to prevent division by 0
40
- if (t_01 < 1e-5) {
41
- t_01 = 1;
42
- }
43
- if (t_02 < 1e-5) {
44
- t_02 = t_01;
45
- }
46
- if (t_03 < 1e-5) {
47
- t_03 = t_02;
48
- }
49
-
50
- const t1 = t_01 + t0;
51
- const t2 = t_02 + t1;
52
- const t3 = t_03 + t2;
53
-
54
- /**
55
- * Interpolation between points 1 and 2
56
- * @type {number}
57
- */
58
- const t = t1 * (1 - f) + t2 * f;
59
-
60
- /*
61
- Vector2 A1 = (t1-t)/(t1-t0)*p0 + (t-t0)/(t1-t0)*p1;
62
- Vector2 A2 = (t2-t)/(t2-t1)*p1 + (t-t1)/(t2-t1)*p2;
63
- Vector2 A3 = (t3-t)/(t3-t2)*p2 + (t-t2)/(t3-t2)*p3;
64
-
65
- Vector2 B1 = (t2-t)/(t2-t0)*A1 + (t-t0)/(t2-t0)*A2;
66
- Vector2 B2 = (t3-t)/(t3-t1)*A2 + (t-t1)/(t3-t1)*A3;
67
-
68
- Vector2 C = (t2-t)/(t2-t1)*B1 + (t-t1)/(t2-t1)*B2;
69
- */
70
-
71
- const d_t1_t0 = t1 - t0;
72
-
73
- const m_A1_0 = (t1 - t) / d_t1_t0;
74
- const m_A1_1 = (t - t0) / d_t1_t0;
75
-
76
- const d_t2_t1 = t2 - t1;
77
-
78
- const m_A2_0 = (t2 - t) / d_t2_t1;
79
-
80
- const d_t_t1 = t - t1;
81
-
82
- const m_A2_1 = d_t_t1 / d_t2_t1;
83
-
84
- const d_t3_t2 = t3 - t2;
85
-
86
- const d_t3_t = t3 - t;
87
-
88
- const m_A3_0 = d_t3_t / d_t3_t2;
89
- const m_A3_1 = (t - t2) / d_t3_t2;
90
-
91
- const d_t2_t0 = t2 - t0;
92
-
93
- const m_B1_0 = (t2 - t) / d_t2_t0;
94
- const m_B1_1 = (t - t0) / d_t2_t0;
95
-
96
- const d_t3_t1 = t3 - t1;
97
-
98
- const m_B2_0 = d_t3_t / d_t3_t1;
99
- const m_B2_1 = d_t_t1 / d_t3_t1;
100
-
101
- const m_C_0 = (t2 - t) / d_t2_t1;
102
- const m_C_1 = d_t_t1 / d_t2_t1;
103
-
104
- // read vector values for the dimension
105
- const v0 = p0;
106
- const v1 = p1;
107
- const v2 = p2;
108
- const v3 = p3;
109
-
110
- // compute resulting value in this dimension
111
- const A1 = m_A1_0 * v0 + m_A1_1 * v1;
112
- const A2 = m_A2_0 * v1 + m_A2_1 * v2;
113
- const A3 = m_A3_0 * v2 + m_A3_1 * v3;
114
-
115
- const B1 = m_B1_0 * A1 + m_B1_1 * A2;
116
- const B2 = m_B2_0 * A2 + m_B2_1 * A3;
117
-
118
- return m_C_0 * B1 + m_C_1 * B2;
119
-
120
- }
@@ -1,78 +0,0 @@
1
- /**
2
- * Created by Alex on 22/05/2016.
3
- */
4
-
5
- /**
6
- *
7
- * @param {number} quietTime in milliseconds
8
- * @param {number} workTime in milliseconds
9
- * @constructor
10
- */
11
- function IntervalExecutor(quietTime, workTime) {
12
- this.quietTime = quietTime;
13
- this.workTime = workTime;
14
- this.tasks = [];
15
- this.busy = false;
16
- this.timeLastWorkDone = 0;
17
- }
18
-
19
- /**
20
- *
21
- * @param {function} task
22
- */
23
- IntervalExecutor.prototype.add = function (task) {
24
- this.tasks.push(task);
25
- this.prod();
26
- };
27
-
28
- IntervalExecutor.prototype.prod = function () {
29
- const self = this;
30
- const tasks = this.tasks;
31
-
32
- function processCycle() {
33
- const t = Date.now();
34
- let timeNow = t;
35
-
36
- let workTime = 0;
37
-
38
- while (tasks.length > 0) {
39
- const task = tasks.shift();
40
- task();
41
-
42
- timeNow = Date.now();
43
- workTime = timeNow - t;
44
- if (workTime >= self.workTime) {
45
- //time slice ended
46
- break;
47
- }
48
- }
49
-
50
- self.timeLastWorkDone = timeNow;
51
-
52
- if (tasks.length > 0) {
53
- //compute adaptive quiet time based on how long we worked for
54
- const workOverBudget = workTime - self.workTime;
55
- const sleepTime = self.quietTime + Math.max(workOverBudget, 0);
56
-
57
- //still some tasks left, schedule next slice
58
- setTimeout(processCycle, sleepTime);
59
- } else {
60
- //all tasks done, reset the 'busy' flag
61
- self.busy = false;
62
- }
63
- }
64
-
65
- if (!this.busy && tasks.length > 0) {
66
- this.busy = true;
67
- const timeSinceLastWork = Date.now() - this.timeLastWorkDone;
68
- const quietTimeDelta = this.quietTime - timeSinceLastWork;
69
- if (quietTimeDelta < 0) {
70
- processCycle();
71
- } else {
72
- setTimeout(processCycle, quietTimeDelta);
73
- }
74
- }
75
- };
76
-
77
-
78
- export default IntervalExecutor;