@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
@@ -63964,7 +63964,7 @@ Task.prototype.isTask = true;
63964
63964
  /**
63965
63965
  *
63966
63966
  * @param {number|function(*):number} initial
63967
- * @param {number|function(*):number} limit
63967
+ * @param {number|function(*):number} limit limit is excluded from the count, same as `for(;i < limit;)`
63968
63968
  * @param {function(index:int)} callback
63969
63969
  * @returns {Task}
63970
63970
  */
@@ -69662,6 +69662,36 @@ function array_remove_first(array, element, start_index = 0, length = array.leng
69662
69662
  return false;
69663
69663
  }
69664
69664
 
69665
+ /**
69666
+ *
69667
+ * @param {*} value
69668
+ * @return {boolean}
69669
+ */
69670
+ function isDefined(value) {
69671
+ return value !== undefined;
69672
+ }
69673
+
69674
+ /**
69675
+ *
69676
+ * @enum
69677
+ */
69678
+ const EntityFlags = {
69679
+ /**
69680
+ * Whether the entity is built, set internally
69681
+ */
69682
+ Built: 1,
69683
+ /**
69684
+ * If component type is not registered on the {@link EntityComponentDataset} when calling {@link Entity#build} - will register the component first
69685
+ */
69686
+ RegisterComponents: 2,
69687
+ /**
69688
+ * Entity builder will watch destruction of the entity (subscribe to event), this will make the {@link Entity} automatically
69689
+ * recognize when the corresponding entity is destroyed outside the {@link Entity} API
69690
+ * NOTE: useful to turn off to save a bit of memory, as those event listeners take up a bit of space. Feel free to turn this flag off when you don't care to keep the reference to the {@link Entity}
69691
+ */
69692
+ WatchDestruction: 4
69693
+ };
69694
+
69665
69695
  /**
69666
69696
  * de Bruijn sequence
69667
69697
  * @see https://graphics.stanford.edu/~seander/bithacks.html
@@ -72274,78 +72304,6 @@ class EntityManager {
72274
72304
  }
72275
72305
  }
72276
72306
 
72277
- /**
72278
- * @author Alex Goldring
72279
- * @copyright Alex Goldring 2017
72280
- */
72281
-
72282
-
72283
- /**
72284
- *
72285
- * @param {*} value
72286
- * @return {boolean}
72287
- */
72288
- function isDefined(value) {
72289
- return value !== undefined;
72290
- }
72291
-
72292
- /**
72293
- * performs instanceof match
72294
- * @param {function} type
72295
- * @returns {function(*): boolean}
72296
- */
72297
- function isInstanceOf$1(type) {
72298
- return function (m) {
72299
- return m instanceof type;
72300
- }
72301
- }
72302
-
72303
- /**
72304
- * performs typeof match
72305
- * @param {string} type
72306
- * @returns {function(*): boolean}
72307
- */
72308
- function isTypeOf(type) {
72309
-
72310
- return function (m) {
72311
- return typeof m === type;
72312
- }
72313
- }
72314
-
72315
- /**
72316
- * Joins two matchers via OR
72317
- * @param {function(*):boolean} a
72318
- * @param {function(*):boolean} b
72319
- * @returns {function(*):boolean}
72320
- */
72321
- function or(a, b) {
72322
-
72323
- return function (m) {
72324
- return a(m) || b(m);
72325
- }
72326
- }
72327
-
72328
- /**
72329
- *
72330
- * @enum
72331
- */
72332
- const EntityFlags = {
72333
- /**
72334
- * Whether the entity is built, set internally
72335
- */
72336
- Built: 1,
72337
- /**
72338
- * If component type is not registered on the {@link EntityComponentDataset} when calling {@link Entity#build} - will register the component first
72339
- */
72340
- RegisterComponents: 2,
72341
- /**
72342
- * Entity builder will watch destruction of the entity (subscribe to event), this will make the {@link Entity} automatically
72343
- * recognize when the corresponding entity is destroyed outside the {@link Entity} API
72344
- * NOTE: useful to turn off to save a bit of memory, as those event listeners take up a bit of space. Feel free to turn this flag off when you don't care to keep the reference to the {@link Entity}
72345
- */
72346
- WatchDestruction: 4
72347
- };
72348
-
72349
72307
  /**
72350
72308
  * Set of default flags
72351
72309
  * @type {number}
@@ -72544,6 +72502,7 @@ class Entity {
72544
72502
  * @returns {*|null}
72545
72503
  */
72546
72504
  removeComponent(klass) {
72505
+
72547
72506
  const elements = this.components;
72548
72507
  const n = elements.length;
72549
72508
 
@@ -72669,6 +72628,7 @@ class Entity {
72669
72628
  dataset.removeEntity(entity);
72670
72629
 
72671
72630
  this.id = -1;
72631
+ this.generation = -1;
72672
72632
 
72673
72633
  this.clearFlag(EntityFlags.Built);
72674
72634
 
@@ -72694,6 +72654,7 @@ class Entity {
72694
72654
  }
72695
72655
 
72696
72656
  const entity = this.id = dataset.createEntity();
72657
+ this.generation = dataset.getEntityGeneration(entity);
72697
72658
  this.dataset = dataset;
72698
72659
 
72699
72660
  let i;
@@ -72751,6 +72712,7 @@ class Entity {
72751
72712
  r.setFlag(EntityFlags.Built);
72752
72713
  r.id = entity;
72753
72714
  r.dataset = dataset;
72715
+ r.generation = dataset.getEntityGeneration(entity);
72754
72716
 
72755
72717
  return r;
72756
72718
  }
@@ -84092,7 +84054,13 @@ class AssetDescription {
84092
84054
  toString() {
84093
84055
  return `{ type: '${this.type}', path: '${this.path}' }`;
84094
84056
  }
84095
- }
84057
+ }
84058
+
84059
+ /**
84060
+ * @readonly
84061
+ * @type {boolean}
84062
+ */
84063
+ AssetDescription.prototype.isAssetDescription = true;
84096
84064
 
84097
84065
  /**
84098
84066
  * @enum {number}
@@ -89823,7 +89791,7 @@ class InputDeviceSwitch {
89823
89791
  * @param klass
89824
89792
  * @returns {boolean}
89825
89793
  */
89826
- function isInstanceOf(thing, klass) {
89794
+ function isInstanceOf$1(thing, klass) {
89827
89795
  if (klass === undefined) {
89828
89796
  return false;
89829
89797
  }
@@ -89844,12 +89812,12 @@ function isInstanceOf(thing, klass) {
89844
89812
  * @param {Element} el
89845
89813
  */
89846
89814
  function isFocusable(el) {
89847
- return isInstanceOf(el, HTMLInputElement)
89848
- || isInstanceOf(el, HTMLSelectElement)
89849
- || isInstanceOf(el, HTMLTextAreaElement)
89850
- || isInstanceOf(el, HTMLAnchorElement)
89851
- || isInstanceOf(el, HTMLButtonElement)
89852
- || isInstanceOf(el, HTMLAreaElement)
89815
+ return isInstanceOf$1(el, HTMLInputElement)
89816
+ || isInstanceOf$1(el, HTMLSelectElement)
89817
+ || isInstanceOf$1(el, HTMLTextAreaElement)
89818
+ || isInstanceOf$1(el, HTMLAnchorElement)
89819
+ || isInstanceOf$1(el, HTMLButtonElement)
89820
+ || isInstanceOf$1(el, HTMLAreaElement)
89853
89821
  }
89854
89822
 
89855
89823
  /**
@@ -92402,6 +92370,15 @@ class EntityComponentDataset {
92402
92370
  * @type {BitSet}
92403
92371
  */
92404
92372
  entityOccupancy = new BitSet();
92373
+
92374
+ /**
92375
+ * For each entity ID records generation when entity was created
92376
+ * Values are invalid for unused entity IDs
92377
+ * @private
92378
+ * @type {Uint32Array}
92379
+ */
92380
+ entityGeneration = new Uint32Array(0);
92381
+
92405
92382
  /**
92406
92383
  * @private
92407
92384
  * @type {BitSet}
@@ -92453,7 +92430,7 @@ class EntityComponentDataset {
92453
92430
 
92454
92431
  /**
92455
92432
  * @readonly
92456
- * @type {Signal}
92433
+ * @type {Signal<number>}
92457
92434
  */
92458
92435
  onEntityCreated = new Signal();
92459
92436
 
@@ -93003,38 +92980,79 @@ class EntityComponentDataset {
93003
92980
 
93004
92981
  /**
93005
92982
  *
93006
- * @returns {number} entityIndex
92983
+ * @param {number} min_size
93007
92984
  */
93008
- createEntity() {
93009
- const entityIndex = this.entityOccupancy.nextClearBit(0);
93010
- this.entityOccupancy.set(entityIndex, true);
92985
+ enlargeGenerationTable(min_size) {
92986
+ const old_generation_table_size = this.entityGeneration.length;
93011
92987
 
93012
- this.entityCount++;
92988
+ const new_size = max3(
92989
+ min_size,
92990
+ Math.ceil(old_generation_table_size * 1.2),
92991
+ old_generation_table_size + 16
92992
+ );
93013
92993
 
93014
- this.onEntityCreated.send1(entityIndex);
92994
+ const new_generation_table = new Uint32Array(new_size);
93015
92995
 
93016
- this.generation++;
92996
+ new_generation_table.set(this.entityGeneration);
93017
92997
 
93018
- return entityIndex;
92998
+ this.entityGeneration = new_generation_table;
93019
92999
  }
93020
93000
 
93021
93001
  /**
93022
- *
93023
- * @param {number} entityIndex
93024
- * @throws {Error} if entity index is already in use
93002
+ * Produces generation ID for a given entity
93003
+ * @param {number} entity_id
93004
+ * @returns {number}
93025
93005
  */
93026
- createEntitySpecific(entityIndex) {
93027
- if (this.entityExists(entityIndex)) {
93028
- throw new Error(`EntityId ${entityIndex} is already in use`);
93006
+ getEntityGeneration(entity_id) {
93007
+ return this.entityGeneration[entity_id];
93008
+ }
93009
+
93010
+ /**
93011
+ * @private
93012
+ * @param {number} entity_id
93013
+ */
93014
+ createEntityUnsafe(entity_id) {
93015
+ this.entityOccupancy.set(entity_id, true);
93016
+
93017
+ // record entity generation
93018
+ if (this.entityGeneration.length <= entity_id) {
93019
+ // needs to be resized
93020
+ this.enlargeGenerationTable(entity_id + 1);
93029
93021
  }
93030
93022
 
93031
- this.entityOccupancy.set(entityIndex, true);
93023
+ const current_generation = this.generation;
93024
+ this.generation = current_generation + 1;
93025
+
93026
+ this.entityGeneration[entity_id] = current_generation;
93032
93027
 
93033
93028
  this.entityCount++;
93034
93029
 
93035
- this.onEntityCreated.send1(entityIndex);
93030
+ this.onEntityCreated.send1(entity_id);
93031
+ }
93036
93032
 
93037
- this.generation++;
93033
+ /**
93034
+ *
93035
+ * @returns {number} entityIndex
93036
+ */
93037
+ createEntity() {
93038
+ const entity_id = this.entityOccupancy.nextClearBit(0);
93039
+
93040
+ this.createEntityUnsafe(entity_id);
93041
+
93042
+ return entity_id;
93043
+ }
93044
+
93045
+ /**
93046
+ *
93047
+ * @param {number} entity_id
93048
+ * @throws {Error} if entity index is already in use
93049
+ */
93050
+ createEntitySpecific(entity_id) {
93051
+ if (this.entityExists(entity_id)) {
93052
+ throw new Error(`EntityId ${entity_id} is already in use`);
93053
+ }
93054
+
93055
+ this.createEntityUnsafe(entity_id);
93038
93056
  }
93039
93057
 
93040
93058
  /**
@@ -93159,7 +93177,7 @@ class EntityComponentDataset {
93159
93177
  const componentClass = this.componentTypeMap[componentIndex];
93160
93178
 
93161
93179
  //dispatch event to components
93162
- this.sendEvent(entityIndex, EventType.ComponentRemoved, {klass: componentClass, instance: componentInstance});
93180
+ this.sendEvent(entityIndex, EventType.ComponentRemoved, { klass: componentClass, instance: componentInstance });
93163
93181
  }
93164
93182
 
93165
93183
  /**
@@ -93260,7 +93278,7 @@ class EntityComponentDataset {
93260
93278
  const componentClass = this.componentTypeMap[componentIndex];
93261
93279
 
93262
93280
  //dispatch event to components
93263
- this.sendEvent(entityIndex, EventType.ComponentAdded, {klass: componentClass, instance: componentInstance});
93281
+ this.sendEvent(entityIndex, EventType.ComponentAdded, { klass: componentClass, instance: componentInstance });
93264
93282
  }
93265
93283
 
93266
93284
  /**
@@ -96121,6 +96139,42 @@ function number_pretty_print(value) {
96121
96139
 
96122
96140
  }
96123
96141
 
96142
+ /**
96143
+ * performs instanceof match
96144
+ * @param {function} type
96145
+ * @returns {function(*): boolean}
96146
+ */
96147
+ function isInstanceOf(type) {
96148
+ return function (m) {
96149
+ return m instanceof type;
96150
+ }
96151
+ }
96152
+
96153
+ /**
96154
+ * performs typeof match
96155
+ * @param {string} type
96156
+ * @returns {function(*): boolean}
96157
+ */
96158
+ function isTypeOf(type) {
96159
+
96160
+ return function (m) {
96161
+ return typeof m === type;
96162
+ }
96163
+ }
96164
+
96165
+ /**
96166
+ * Joins two matchers via OR
96167
+ * @param {function(*):boolean} a
96168
+ * @param {function(*):boolean} b
96169
+ * @returns {function(*):boolean}
96170
+ */
96171
+ function or(a, b) {
96172
+
96173
+ return function (m) {
96174
+ return a(m) || b(m);
96175
+ }
96176
+ }
96177
+
96124
96178
  /**
96125
96179
  * Created by Alex on 01/11/2014.
96126
96180
  * @copyright Alex Goldring 2014
@@ -96269,14 +96323,14 @@ function p(m, e, f) {
96269
96323
  * @type {Array.<ValueProcessor>}
96270
96324
  */
96271
96325
  const processors = [
96272
- p(isInstanceOf$1(ObservedBoolean), extractorGetValue, format),
96273
- p(isInstanceOf$1(ObservedValue), extractorGetValue, format),
96274
- p(isInstanceOf$1(ObservedString), extractorGetValue, format),
96275
- p(isInstanceOf$1(LinearValue), extractorGetValue, formatNumber),
96276
- p(isInstanceOf$1(BoundedValue), extractBoundedValue, formatArray),
96277
- p(isInstanceOf$1(Stat), extractorGetValue, formatNumber),
96278
- p(isInstanceOf$1(Vector1), extractorGetValue, formatNumber),
96279
- p(isInstanceOf$1(ObservedInteger), extractorGetValue, formatNumber),
96326
+ p(isInstanceOf(ObservedBoolean), extractorGetValue, format),
96327
+ p(isInstanceOf(ObservedValue), extractorGetValue, format),
96328
+ p(isInstanceOf(ObservedString), extractorGetValue, format),
96329
+ p(isInstanceOf(LinearValue), extractorGetValue, formatNumber),
96330
+ p(isInstanceOf(BoundedValue), extractBoundedValue, formatArray),
96331
+ p(isInstanceOf(Stat), extractorGetValue, formatNumber),
96332
+ p(isInstanceOf(Vector1), extractorGetValue, formatNumber),
96333
+ p(isInstanceOf(ObservedInteger), extractorGetValue, formatNumber),
96280
96334
  p(or(isTypedArray, Array.isArray), arrayUnwrap, formatArray),
96281
96335
  p(isTypeOf("number"), passThrough, formatNumber),
96282
96336
  p(isTypeOf("string"), passThrough, passThrough),
@@ -1,22 +1,21 @@
1
1
  /**
2
2
  * Created by Alex on 15/01/2017.
3
3
  */
4
- import View from "../../../src/view/View.js";
5
- import dom from "../../../src/view/DOM.js";
6
-
7
4
  import List from '../../../src/core/collection/list/List.js';
8
5
  import ObservedValue from '../../../src/core/model/ObservedValue.js';
6
+ import { isDefined } from "../../../src/core/process/matcher/isDefined.js";
7
+ import { EntityManager, EventType } from "../../../src/engine/ecs/EntityManager.js";
9
8
  import LabelView from '../../../src/view/common/LabelView.js';
9
+ import dom from "../../../src/view/DOM.js";
10
10
  import ButtonView from '../../../src/view/elements/button/ButtonView.js';
11
11
  import DropDownSelectionView from '../../../src/view/elements/DropDownSelectionView.js';
12
12
  import EmptyView from "../../../src/view/elements/EmptyView.js";
13
- import { EntityManager, EventType } from "../../../src/engine/ecs/EntityManager.js";
14
- import ComponentRemoveAction from "../../actions/concrete/ComponentRemoveAction.js";
13
+ import View from "../../../src/view/View.js";
15
14
  import ComponentAddAction from "../../actions/concrete/ComponentAddAction.js";
16
- import { LineView } from "./components/common/LineView.js";
17
- import { ComponentControlView } from "./ComponentControlView.js";
15
+ import ComponentRemoveAction from "../../actions/concrete/ComponentRemoveAction.js";
18
16
  import { buildObjectEditorFromRegistry } from "../../ecs/component/createObjectEditor.js";
19
- import { isDefined } from "../../../src/core/process/matcher/Matchers.js";
17
+ import { ComponentControlView } from "./ComponentControlView.js";
18
+ import { LineView } from "./components/common/LineView.js";
20
19
 
21
20
 
22
21
  class EntityEditor extends View {
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.84.3",
8
+ "version": "2.84.5",
9
9
  "main": "build/meep.module.js",
10
10
  "module": "build/meep.module.js",
11
11
  "exports": {
@@ -5,11 +5,14 @@
5
5
  * @returns {int}
6
6
  */
7
7
  export function roundFair(number, random) {
8
+
8
9
  const mantissa = number % 1;
9
10
  const roll = random();
11
+
10
12
  if (roll < mantissa) {
11
- return Math.ceil(number);
12
- } else {
13
13
  return Math.floor(number);
14
+ } else {
15
+ return Math.ceil(number);
14
16
  }
17
+
15
18
  }
@@ -0,0 +1,12 @@
1
+ import { roundFair } from "./roundFair.js";
2
+
3
+ test("basics", () => {
4
+
5
+ expect(roundFair(0.3, () => 0)).toEqual(0);
6
+ expect(roundFair(0.3, () => 0.1)).toEqual(0);
7
+ expect(roundFair(0.3, () => 0.299)).toEqual(0);
8
+ expect(roundFair(0.3, () => 0.31)).toEqual(1);
9
+ expect(roundFair(0.3, () => 0.9)).toEqual(1);
10
+ expect(roundFair(0.3, () => 1)).toEqual(1);
11
+
12
+ });
@@ -0,0 +1,19 @@
1
+ import { seededRandom_Mulberry32 } from "./seededRandom_Mulberry32.js";
2
+
3
+ test("predictable sequence", () => {
4
+
5
+ const random = seededRandom_Mulberry32(1337);
6
+
7
+ const seq_a = new Array(29).map(random);
8
+
9
+ const seed_a = random.getCurrentSeed();
10
+
11
+ random.setCurrentSeed(1337);
12
+
13
+ const seq_b = new Array(29).map(random);
14
+
15
+ const seed_b = random.getCurrentSeed();
16
+
17
+ expect(seq_b).toEqual(seq_a);
18
+ expect(seed_a).toEqual(seed_b);
19
+ });
@@ -49,8 +49,12 @@ test("incoming connections", () => {
49
49
  expect(port_b_ref.inConnections).toEqual([connection]);
50
50
  });
51
51
 
52
- test("hash", () => {
53
52
 
53
+ /**
54
+ *
55
+ * @returns {NodeInstancePortReference}
56
+ */
57
+ function sample_a() {
54
58
  const nd = new NodeDescription();
55
59
  const port_a_id = nd.createPort(DUMMY_TYPE, "a", PortDirection.Out);
56
60
 
@@ -58,9 +62,28 @@ test("hash", () => {
58
62
  const graph = new NodeGraph();
59
63
  const node_instance_id = graph.createNode(nd);
60
64
 
61
- const port_a_ref = graph.getConnectionEndpoint(node_instance_id, port_a_id);
65
+ return graph.getConnectionEndpoint(node_instance_id, port_a_id);
66
+ }
67
+
68
+ test("hash", () => {
69
+ const port_a_ref = sample_a();
62
70
 
63
71
  expect(port_a_ref.hash()).toEqual(port_a_ref.hash());
64
72
  expect(typeof port_a_ref.hash()).toBe("number");
65
73
  expect(Number.isInteger(port_a_ref.hash())).toBe(true);
74
+ });
75
+
76
+ test("toString", () => {
77
+
78
+ const s = sample_a().toString();
79
+
80
+ expect(typeof s).toEqual("string");
81
+ expect(s.length).toBeGreaterThan(0);
82
+ });
83
+
84
+ test("self equality", () => {
85
+
86
+ const ref = sample_a();
87
+
88
+ expect(ref.equals(ref)).toBe(true);
66
89
  });
@@ -1,9 +1,9 @@
1
- import {assert} from "../../../assert.js";
2
- import {computeHashIntegerArray} from "../../../collection/array/computeHashIntegerArray.js";
3
- import {computeStringHash} from "../../../primitives/strings/computeStringHash.js";
4
- import {objectKeyByValue} from "../../object/objectKeyByValue.js";
5
- import {DataType} from "../DataType.js";
6
- import {PortDirection} from "./PortDirection.js";
1
+ import { assert } from "../../../assert.js";
2
+ import { computeHashIntegerArray } from "../../../collection/array/computeHashIntegerArray.js";
3
+ import { computeStringHash } from "../../../primitives/strings/computeStringHash.js";
4
+ import { objectKeyByValue } from "../../object/objectKeyByValue.js";
5
+ import { DataType } from "../DataType.js";
6
+ import { PortDirection } from "./PortDirection.js";
7
7
 
8
8
  /**
9
9
  * @type {number}
@@ -94,6 +94,8 @@ export class Port {
94
94
  id, name, direction, dataType
95
95
  }, registry) {
96
96
 
97
+ assert.isNonNegativeInteger(id, 'id');
98
+
97
99
  this.id = id;
98
100
  this.name = name;
99
101
  this.direction = PortDirection[direction];
@@ -1,4 +1,7 @@
1
+ import { DataType } from "../DataType.js";
2
+ import { NodeRegistry } from "./NodeRegistry.js";
1
3
  import { Port } from "./Port.js";
4
+ import { PortDirection } from "./PortDirection.js";
2
5
 
3
6
  test("constructor does not throw", () => {
4
7
  expect(() => new Port()).not.toThrow();
@@ -51,3 +54,49 @@ test("toString on newly crated instance produces a valid string", () => {
51
54
  expect(typeof s).toBe('string');
52
55
  expect(s.trim().length).toBeGreaterThan(0); // non-empty
53
56
  });
57
+
58
+ test("fromJSON", () => {
59
+
60
+ const registry = new NodeRegistry();
61
+
62
+ registry.addType(DataType.from(7, "t"));
63
+
64
+ const port = new Port();
65
+
66
+ port.fromJSON({
67
+ id: 3,
68
+ name: "x",
69
+ direction: "In",
70
+ dataType: {
71
+ id: 7,
72
+ name: "t"
73
+ }
74
+ }, registry);
75
+
76
+ expect(port.id).toEqual(3);
77
+ expect(port.name).toEqual("x");
78
+ expect(port.direction).toEqual(PortDirection.In);
79
+ expect(port.dataType.id).toEqual(7);
80
+ expect(port.dataType.name).toEqual("t");
81
+ });
82
+
83
+ test("toJSON", () => {
84
+
85
+ const port = new Port();
86
+
87
+ port.id = 3;
88
+ port.name = "x";
89
+ port.direction = PortDirection.Out;
90
+ port.dataType = DataType.from(7, "t");
91
+
92
+ expect(port.toJSON()).toEqual({
93
+ id: 3,
94
+ name: "x",
95
+ direction: "Out",
96
+ dataType: {
97
+ id: 7,
98
+ name: "t"
99
+ }
100
+ });
101
+
102
+ });
@@ -0,0 +1,16 @@
1
+ import { assert } from "../../assert.js";
2
+
3
+ /**
4
+ * Joins two matchers via AND
5
+ * @param {function(*):boolean} a
6
+ * @param {function(*):boolean} b
7
+ * @returns {function(*):boolean}
8
+ */
9
+ export function and(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,8 @@
1
+ /**
2
+ *
3
+ * @param {*} value
4
+ * @return {boolean}
5
+ */
6
+ export function isDefined(value) {
7
+ return value !== undefined;
8
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @template T
3
+ * @param {T} value
4
+ * @returns {function(T): boolean}
5
+ */
6
+ export function isEqualTo(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 isGreaterThan(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 isGreaterThanOrEqualTo(value) {
7
+ return function (x) {
8
+ return x >= value;
9
+ }
10
+ }