@woosh/meep-engine 2.51.0 → 2.53.0

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 (160) hide show
  1. package/editor/actions/concrete/WriteGridValueAction.js +1 -1
  2. package/editor/ecs/component/prototypeObjectEditor.js +1 -1
  3. package/editor/process/ObstacleGridDisplayProcess.js +2 -2
  4. package/editor/process/symbolic/makeGridPositionSymbolDisplay.js +1 -1
  5. package/editor/tools/GridPaintTool.js +2 -2
  6. package/package.json +4 -3
  7. package/samples/generation/SampleGenerator0.js +1 -1
  8. package/samples/generation/generators/interactive/mir_generator_place_buff_objects.js +1 -1
  9. package/samples/generation/generators/mir_generator_place_bases.js +1 -1
  10. package/samples/generation/generators/mir_generator_place_road_decorators.js +1 -1
  11. package/samples/generation/generators/mir_generator_place_starting_point.js +1 -1
  12. package/samples/generation/main.js +2 -2
  13. package/samples/generation/rules/matcher_play_area.js +1 -1
  14. package/samples/generation/rules/matcher_tag_occupied.js +1 -1
  15. package/samples/generation/rules/matcher_tag_traversable.js +1 -1
  16. package/samples/generation/themes/SampleTheme0.js +5 -5
  17. package/src/core/NumberFormat.js +0 -71
  18. package/src/core/bvh2/bvh3/ExplicitBinaryBoundingVolumeHierarchy.js +1 -0
  19. package/src/core/bvh2/bvh3/query/BVHQueryAny.js +16 -0
  20. package/src/core/bvh2/bvh3/query/bvh_collect_user_data.js +5 -5
  21. package/src/core/bvh2/bvh3/query/bvh_query_leaves_generic.js +2 -3
  22. package/src/core/bvh2/bvh3/query/bvh_query_leaves_generic.spec.js +22 -0
  23. package/src/core/bvh2/bvh3/query/bvh_query_leaves_ray.js +2 -2
  24. package/src/core/bvh2/bvh3/query/bvh_query_leaves_ray.spec.js +64 -0
  25. package/src/core/bvh2/bvh3/query/bvh_query_user_data_generic.js +2 -3
  26. package/src/core/collection/table/RowFirstTableSpec.js +8 -4
  27. package/src/core/geom/3d/aabb/aabb3_from_threejs_geometry.js +41 -0
  28. package/src/core/geom/3d/matrix/MATRIX_4_IDENTITY.spec.js +10 -0
  29. package/src/core/geom/3d/topology/struct/TopoMesh.js +3 -2
  30. package/src/core/geom/3d/topology/struct/prototypeBinaryTopology.js +4 -4
  31. package/src/core/geom/Vector2.d.ts +9 -0
  32. package/src/core/geom/Vector2.js +8 -22
  33. package/src/core/geom/Vector2.spec.js +153 -0
  34. package/src/core/model/ModuleRegistry.js +2 -4
  35. package/src/core/model/ModuleRegistry.spec.js +31 -0
  36. package/src/core/model/reactive/js/compileReactiveToJS.spec.js +14 -0
  37. package/src/core/model/reactive/model/logic/ReactiveAnd.spec.js +31 -1
  38. package/src/core/model/reactive/model/logic/ReactiveOr.spec.js +53 -0
  39. package/src/core/model/stat/LinearModifier.js +2 -2
  40. package/src/core/model/stat/LinearModifier.spec.js +62 -0
  41. package/src/core/model/stat/Stat.js +12 -0
  42. package/src/core/model/stat/Stat.spec.js +36 -0
  43. package/src/core/primitives/array/computeStridedIntegerArrayHash.spec.js +28 -0
  44. package/src/core/primitives/numbers/number_format_by_thousands.js +14 -0
  45. package/src/core/primitives/numbers/number_pretty_print.js +49 -0
  46. package/src/core/primitives/strings/computeStringHash.spec.js +12 -5
  47. package/src/core/process/BaseProcess.js +8 -8
  48. package/src/engine/Clock.js +30 -29
  49. package/src/engine/Clock.spec.js +26 -0
  50. package/src/engine/Engine.spec.js +4 -0
  51. package/src/engine/ecs/EntityBuilder.js +3 -1
  52. package/src/engine/ecs/EntityBuilder.spec.js +21 -0
  53. package/src/engine/ecs/EntityComponentDataset.js +1 -1
  54. package/src/engine/ecs/EntityObserver.spec.js +1 -1
  55. package/src/engine/ecs/dynamic_actions/actions/definition/{WhiteToBlackboardActionDescription.js → WriteToBlackboardActionDescription.js} +2 -2
  56. package/src/engine/ecs/dynamic_actions/actions/definition/deserializeActionFromJSON.js +2 -2
  57. package/src/engine/ecs/gui/GUIElement.js +1 -1
  58. package/src/engine/ecs/gui/position/ViewportPosition.js +3 -3
  59. package/src/engine/ecs/guid/GUIDSerializationAdapter.spec.js +21 -0
  60. package/src/engine/ecs/parent/EntityNode.js +1 -1
  61. package/src/engine/ecs/parent/EntityNode.spec.js +104 -0
  62. package/src/engine/ecs/speaker/Voice.js +4 -0
  63. package/src/engine/ecs/team/Team.js +0 -44
  64. package/src/engine/ecs/team/TeamSerializationAdapter.js +46 -0
  65. package/src/engine/ecs/terrain/ecs/TerrainClassifier.js +1 -1
  66. package/src/engine/ecs/terrain/ecs/layers/TerrainLayers.js +1 -1
  67. package/src/engine/ecs/transform/Transform.js +2 -1
  68. package/src/engine/ecs/transform/Transform.spec.js +24 -0
  69. package/src/engine/graphics/camera/testClippingPlaneComputation.js +2 -6
  70. package/src/engine/graphics/ecs/animation/animator/graph/AnimationGraph.spec.js +5 -0
  71. package/src/engine/graphics/ecs/animation/animator/graph/definition/AnimationGraphDefinition.js +24 -25
  72. package/src/engine/graphics/ecs/animation/animator/graph/definition/AnimationGraphDefinition.spec.js +5 -0
  73. package/src/engine/graphics/ecs/decal/DecalSystem.js +2 -20
  74. package/src/engine/graphics/ecs/decal/v2/Decal.js +32 -32
  75. package/src/engine/graphics/ecs/decal/v2/Decal.spec.js +5 -0
  76. package/src/engine/graphics/ecs/decal/v2/FPDecalSystem.js +4 -2
  77. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +4 -33
  78. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +8 -4
  79. package/src/engine/graphics/ecs/mesh-v2/aggregate/SGMesh.spec.js +5 -0
  80. package/src/engine/graphics/ecs/mesh-v2/sample/prototypeShadedGeometry.js +0 -12
  81. package/src/engine/graphics/ecs/path/testPathDisplaySystem.js +0 -2
  82. package/src/engine/graphics/ecs/path/tube/prototypeAnimatedPathMask.js +2 -6
  83. package/src/engine/graphics/ecs/water2/shader/testWaterShader.js +2 -4
  84. package/src/engine/graphics/geometry/buffered/computeBufferAttributeHash.js +1 -1
  85. package/src/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +0 -2
  86. package/src/engine/graphics/render/buffer/buffers/prototypeNormalFrameBuffer.js +2 -6
  87. package/src/engine/graphics/render/forward_plus/plugin/ptototypeFPPlugin.js +2 -6
  88. package/src/engine/graphics/render/forward_plus/prototype/prototypeLightManager.js +2 -2
  89. package/src/engine/graphics/render/visibility/hiz/prototypeHiZ.js +4 -8
  90. package/src/engine/graphics/sh3/path_tracer/GeometryBVHBatched.js +2 -1
  91. package/src/engine/graphics/sh3/path_tracer/PathTracer.js +6 -1
  92. package/src/engine/graphics/sh3/path_tracer/prototypePathTracer.js +2 -2
  93. package/src/engine/graphics/shadows/testShadowMapRendering.js +0 -2
  94. package/src/engine/graphics/texture/TextureBackedMemoryRegion.js +4 -1
  95. package/src/engine/graphics/texture/sampler/Sampler2D.js +1 -1
  96. package/src/engine/graphics/texture/sampler/filter/filter_lanczos3.js +7 -2
  97. package/src/engine/graphics/three/expand_aabb_by_transformed_three_object.js +3 -23
  98. package/src/engine/grid/grid2transform/GridPosition2Transform.js +122 -0
  99. package/src/engine/grid/grid2transform/GridPosition2TransformSerializationAdapter.js +70 -0
  100. package/src/engine/grid/grid2transform/GridPosition2TransformSerializationUpgrader_0_1.js +40 -0
  101. package/src/engine/grid/{systems → grid2transform}/GridPosition2TransformSystem.js +3 -3
  102. package/src/engine/grid/{components → obstacle}/GridObstacle.js +45 -25
  103. package/src/engine/grid/obstacle/GridObstacle.spec.js +175 -0
  104. package/src/engine/grid/{components → position}/GridPosition.js +6 -14
  105. package/src/engine/grid/{systems → position}/GridPositionSystem.js +1 -1
  106. package/src/engine/grid/transform2grid/Transform2GridPositionSystem.js +1 -1
  107. package/src/engine/input/devices/PointerDevice.spec.js +5 -0
  108. package/src/engine/navigation/ecs/components/computeCatmullRomSpline.js +5 -1
  109. package/src/engine/navigation/ecs/path_following/PathFollower.js +40 -32
  110. package/src/engine/navigation/ecs/path_following/PathFollower.spec.js +27 -0
  111. package/src/engine/plugin/EnginePlugin.js +12 -14
  112. package/src/engine/plugin/EnginePlugin.spec.js +5 -0
  113. package/src/engine/plugin/EnginePluginManager.js +18 -22
  114. package/src/engine/scene/transitionToScene.js +12 -1
  115. package/src/engine/simulation/Ticker.js +1 -1
  116. package/src/generation/filtering/CellFilter.js +6 -9
  117. package/src/generation/filtering/boolean/CellFilterLiteralBoolean.js +1 -4
  118. package/src/generation/filtering/core/CellFilterBinaryOperation.js +12 -15
  119. package/src/generation/filtering/core/CellFilterOperationTertiary.js +15 -19
  120. package/src/generation/filtering/numeric/CellFilterLiteralFloat.js +6 -9
  121. package/src/generation/grid/actions/ContinuousGridCellActionSetTerrainObstacle.js +1 -1
  122. package/src/generation/grid/actions/ContinuousGridCellActionWriteObstacle.js +2 -2
  123. package/src/generation/grid/generation/road/GridTaskGenerateRoads.js +1 -1
  124. package/src/generation/markers/actions/MarkerNodeActionEntityPlacement.js +1 -1
  125. package/src/generation/theme/TerrainLayerDescription.js +1 -1
  126. package/src/view/common/LabelView.js +3 -3
  127. package/src/engine/ecs/components/AimController.js +0 -18
  128. package/src/engine/ecs/components/Attacker.js +0 -13
  129. package/src/engine/ecs/components/MeshCollider.js +0 -15
  130. package/src/engine/ecs/components/RangedAttack.js +0 -12
  131. package/src/engine/ecs/components/TargetAI.js +0 -11
  132. package/src/engine/ecs/components/ViewportMeshProjection.js +0 -18
  133. package/src/engine/ecs/systems/AimControllerSystem.js +0 -63
  134. package/src/engine/ecs/systems/AttackerSystem.js +0 -67
  135. package/src/engine/ecs/systems/MeshColliderSystem.js +0 -47
  136. package/src/engine/ecs/systems/MonsterAISystem.js +0 -163
  137. package/src/engine/ecs/systems/MortalitySystem.js +0 -46
  138. package/src/engine/ecs/systems/RangedAttackSystem.js +0 -132
  139. package/src/engine/ecs/systems/SerializationMetadataSystem.js +0 -10
  140. package/src/engine/ecs/systems/TargetAISystem.js +0 -107
  141. package/src/engine/ecs/systems/ViewportMeshProjectionSystem.js +0 -68
  142. package/src/engine/graphics/particles/ParticleEmitterLibrary.js +0 -87
  143. package/src/engine/graphics/texture/ExportCanvasAsPNG.js +0 -7
  144. package/src/engine/grid/components/GridObstacle.spec.js +0 -46
  145. package/src/engine/grid/components/GridPosition2Transform.js +0 -232
  146. package/src/engine/grid/components/ViewportGridProjection.js +0 -20
  147. package/src/engine/grid/systems/GridObstacleSystem.js +0 -58
  148. package/src/engine/grid/systems/ViewportGridProjectionSystem.js +0 -105
  149. package/src/engine/navigation/PathFinder.js +0 -172
  150. package/src/engine/navigation/PathWorker.js +0 -78
  151. package/src/engine/navigation/ecs/systems/PathFinderSystem.js +0 -63
  152. package/src/engine/navigation/ecs/systems/PathSystem.js +0 -19
  153. package/src/engine/navigation/funnel/Funnel.js +0 -196
  154. package/src/generation/ZoneNode.js +0 -9
  155. /package/{src/generation → samples/generation/grid}/GridTags.js +0 -0
  156. /package/src/{engine → core/primitives/array}/computeStridedIntegerArrayHash.js +0 -0
  157. /package/src/engine/grid/{components → grid2transform}/GridPosition2TransformFlags.js +0 -0
  158. /package/src/engine/grid/{components → obstacle}/GridObstacleSerializationAdapter.js +0 -0
  159. /package/src/engine/grid/{components → position}/GridPositionSerializationAdapter.js +0 -0
  160. /package/src/engine/grid/{components → position}/GridPositionSerializationUpdater_0_1.js +0 -0
@@ -450,7 +450,10 @@ class Vector2 {
450
450
  * @param {number} fraction
451
451
  */
452
452
  lerpVectors(a, b, fraction) {
453
- v2Lerp(this, a, b, fraction);
453
+ const x = lerp(a.x, b.x, fraction);
454
+ const y = lerp(a.y, b.y, fraction);
455
+
456
+ this.set(x, y);
454
457
  }
455
458
 
456
459
  /**
@@ -538,7 +541,7 @@ class Vector2 {
538
541
  *
539
542
  * @returns {number}
540
543
  */
541
- hashCode() {
544
+ hash() {
542
545
  const x = Math.sin(this.x) * 1367130550;
543
546
  const y = Math.sin(this.y) * 1367130550;
544
547
 
@@ -550,7 +553,7 @@ class Vector2 {
550
553
  }
551
554
 
552
555
  /**
553
- *
556
+ * Rotation is counter-clockwise
554
557
  * @param {number} angle in radians
555
558
  */
556
559
  rotate(angle) {
@@ -558,7 +561,7 @@ class Vector2 {
558
561
  const cos = Math.cos(angle);
559
562
 
560
563
  const x = this.x * cos - this.y * sin
561
- const y = this.x * sin - this.y * cos;
564
+ const y = this.x * sin + this.y * cos;
562
565
 
563
566
  this.set(x, y);
564
567
  }
@@ -597,7 +600,7 @@ class Vector2 {
597
600
  * @return {boolean}
598
601
  */
599
602
  roughlyEquals(other, tolerance) {
600
- return this._roughlyEquals(other.x, other.y, tolerance);
603
+ return this._roughlyEquals(other.x, other.y, tolerance);
601
604
  }
602
605
 
603
606
  /**
@@ -677,23 +680,6 @@ Vector2.one = Object.freeze(new Vector2(1, 1));
677
680
  * @type {boolean}
678
681
  */
679
682
  Vector2.prototype.isVector2 = true;
680
-
681
- /**
682
- *
683
- * @param {Vector2} result
684
- * @param {Vector2} a
685
- * @param {Vector2} b
686
- * @param {number} fraction
687
- */
688
- function v2Lerp(result, a, b, fraction) {
689
-
690
- const x = lerp(a.x, b.x, fraction);
691
- const y = lerp(a.y, b.y, fraction);
692
-
693
- result.set(x, y);
694
- }
695
-
696
-
697
683
  /**
698
684
  *
699
685
  * @param {number} x0
@@ -8,6 +8,21 @@ test("setting x and y via constructor", () => {
8
8
  expect(v.y).toBe(13);
9
9
  });
10
10
 
11
+ test("array-style accessors", () => {
12
+
13
+ const v = new Vector2();
14
+
15
+ v[0] = 3;
16
+ v[1] = -7;
17
+
18
+ expect(v[0]).toEqual(3);
19
+ expect(v.x).toEqual(3);
20
+
21
+ expect(v[1]).toEqual(-7);
22
+ expect(v.y).toEqual(-7);
23
+
24
+ });
25
+
11
26
  test('add works', () => {
12
27
  const a = new Vector2(1, 3);
13
28
  const b = new Vector2(5, 7);
@@ -140,3 +155,141 @@ test("onChange is dispatched when y changes via set", () => {
140
155
 
141
156
  expect(changeHandler).toHaveBeenLastCalledWith(2, 3, 1, 3);
142
157
  });
158
+
159
+ test("rotate", () => {
160
+
161
+ const v = new Vector2(0, 0);
162
+
163
+ v.rotate(0);
164
+
165
+ expect(v.x).toBeCloseTo(0);
166
+ expect(v.y).toBeCloseTo(0);
167
+
168
+ v.set(1, 0);
169
+
170
+ v.rotate(0);
171
+
172
+ expect(v.x).toBeCloseTo(1);
173
+ expect(v.y).toBeCloseTo(0);
174
+
175
+ v.set(0, 1);
176
+
177
+ v.rotate(0);
178
+
179
+ expect(v.x).toBeCloseTo(0);
180
+ expect(v.y).toBeCloseTo(1);
181
+
182
+ v.set(1, 0);
183
+
184
+ v.rotate(Math.PI);
185
+
186
+ expect(v.x).toBeCloseTo(-1);
187
+ expect(v.y).toBeCloseTo(0);
188
+
189
+ v.set(-1, 0);
190
+
191
+ v.rotate(Math.PI);
192
+
193
+ expect(v.x).toBeCloseTo(1);
194
+ expect(v.y).toBeCloseTo(0);
195
+
196
+ v.set(0, 1);
197
+
198
+ v.rotate(Math.PI);
199
+
200
+ expect(v.x).toBeCloseTo(0);
201
+ expect(v.y).toBeCloseTo(-1);
202
+
203
+ v.set(0, -1);
204
+
205
+ v.rotate(Math.PI);
206
+
207
+ expect(v.x).toBeCloseTo(0);
208
+ expect(v.y).toBeCloseTo(1);
209
+
210
+ v.set(1, 1);
211
+
212
+ v.rotate(Math.PI * 0.5);
213
+
214
+ expect(v.x).toBeCloseTo(-1);
215
+ expect(v.y).toBeCloseTo(1);
216
+ });
217
+
218
+ test("hash", () => {
219
+
220
+ function validate(x, y) {
221
+
222
+ const v = new Vector2(x, y);
223
+
224
+ const hash = v.hash();
225
+
226
+ expect(hash).toEqual(v.hash()); // stability
227
+ expect(typeof hash).toBe("number");
228
+ expect(Number.isInteger(hash)).toBe(true);
229
+
230
+ }
231
+
232
+ validate(0, 0);
233
+ validate(0, 1);
234
+ validate(1, 0);
235
+ validate(1, 1);
236
+
237
+ validate(0, -1);
238
+ validate(-1, 0);
239
+ validate(-1, -1);
240
+ });
241
+
242
+ test("normalize", () => {
243
+
244
+ const v = new Vector2(1, 1);
245
+
246
+ v.normalize();
247
+
248
+ const s = 1 / Math.SQRT2;
249
+
250
+ expect(v.x).toBeCloseTo(s);
251
+ expect(v.y).toBeCloseTo(s);
252
+
253
+ v.set(-1, -1);
254
+
255
+ v.normalize();
256
+
257
+ expect(v.x).toBeCloseTo(-s);
258
+ expect(v.y).toBeCloseTo(-s);
259
+
260
+ });
261
+
262
+ test("length", () => {
263
+
264
+ expect(new Vector2(1, 0).length()).toBeCloseTo(1);
265
+ expect(new Vector2(0, 1).length()).toBeCloseTo(1);
266
+
267
+ expect(new Vector2(1, 1).length()).toBeCloseTo(Math.SQRT2);
268
+
269
+ expect(new Vector2(-1, 1).length()).toBeCloseTo(Math.SQRT2);
270
+
271
+ expect(new Vector2(1, -1).length()).toBeCloseTo(Math.SQRT2);
272
+
273
+ expect(new Vector2(-1, -1).length()).toBeCloseTo(Math.SQRT2);
274
+ });
275
+
276
+
277
+ test("manhattanDistanceTo", () => {
278
+ const a = new Vector2(1, 3);
279
+ const b = new Vector2(5, 11);
280
+
281
+ expect(a.manhattanDistanceTo(b)).toEqual(12)
282
+ });
283
+
284
+ test("lerpVectors", () => {
285
+
286
+
287
+ const a = new Vector2(-1, 3);
288
+ const b = new Vector2(5, -11);
289
+
290
+ a.lerpVectors(a, b, 0.1);
291
+
292
+ expect(a.x).toBeCloseTo(-1 * 0.9 + 5 * 0.1);
293
+ expect(a.y).toBeCloseTo(3 * 0.9 - 11 * 0.1);
294
+
295
+ });
@@ -15,9 +15,7 @@ export class ModuleRegistry {
15
15
 
16
16
  constructor() {
17
17
 
18
- const self = this;
19
-
20
- this.proxy = new Proxy({}, {
18
+ this.proxy = new Proxy(this, {
21
19
  /**
22
20
  *
23
21
  * @param target
@@ -26,7 +24,7 @@ export class ModuleRegistry {
26
24
  * @returns {Class}
27
25
  */
28
26
  get(target, p, receiver) {
29
- const module = self.get(p);
27
+ const module = target.get(p);
30
28
 
31
29
  if (module === undefined) {
32
30
  throw new Error(`Module '${p}' not found in the registry`);
@@ -0,0 +1,31 @@
1
+ import { ModuleRegistry } from "./ModuleRegistry.js";
2
+
3
+ test("constructor does not throw", () => {
4
+
5
+ expect(() => new ModuleRegistry()).not.toThrow();
6
+
7
+ });
8
+
9
+ test("add/retrieve", () => {
10
+
11
+ const registry = new ModuleRegistry();
12
+
13
+ const module = {};
14
+
15
+ registry.add("x", module);
16
+
17
+ expect(registry.get("x")).toBe(module);
18
+
19
+ });
20
+
21
+ test("retrieve by proxy", () => {
22
+
23
+ const registry = new ModuleRegistry();
24
+
25
+ const module = {};
26
+
27
+ registry.add("x", module);
28
+
29
+ expect(registry.proxy.x).toBe(module);
30
+
31
+ });
@@ -0,0 +1,14 @@
1
+ import { compileReactiveToJS } from "./compileReactiveToJS.js";
2
+ import { ReactiveLiteralBoolean } from "../model/terminal/ReactiveLiteralBoolean.js";
3
+ import { ReactiveLiteralNumber } from "../model/terminal/ReactiveLiteralNumber.js";
4
+ import { ReactiveLiteralString } from "../model/terminal/ReactiveLiteralString.js";
5
+
6
+ test("literal expressions", () => {
7
+
8
+ expect(compileReactiveToJS(ReactiveLiteralBoolean.from(true))).toEqual("true");
9
+
10
+ expect(compileReactiveToJS(ReactiveLiteralNumber.from(13.1))).toEqual("13.1");
11
+
12
+ expect(compileReactiveToJS(ReactiveLiteralString.from("a"))).toEqual("'a'");
13
+
14
+ });
@@ -1,7 +1,7 @@
1
1
  import { ReactiveLiteralBoolean } from "../terminal/ReactiveLiteralBoolean.js";
2
2
  import { ReactiveAnd } from "./ReactiveAnd.js";
3
3
 
4
- test('.copy method must not change the original', () => {
4
+ test('copy method does not modify the sources', () => {
5
5
 
6
6
  const b0 = ReactiveLiteralBoolean.from(false);
7
7
  const b1 = ReactiveLiteralBoolean.from(true);
@@ -19,3 +19,33 @@ test('.copy method must not change the original', () => {
19
19
  expect(b1.getValue()).toBe(true);
20
20
 
21
21
  });
22
+
23
+ test("equals", () => {
24
+
25
+ const a = ReactiveAnd.from(ReactiveLiteralBoolean.from(true), ReactiveLiteralBoolean.from(true));
26
+ const b = ReactiveAnd.from(ReactiveLiteralBoolean.from(true), ReactiveLiteralBoolean.from(true));
27
+ const c = ReactiveAnd.from(ReactiveLiteralBoolean.from(true), ReactiveLiteralBoolean.from(false));
28
+ const d = ReactiveAnd.from(ReactiveLiteralBoolean.from(false), ReactiveLiteralBoolean.from(true));
29
+
30
+ expect(a.equals(b)).toBe(true);
31
+ expect(a.equals(c)).toBe(false);
32
+ expect(a.equals(d)).toBe(false);
33
+
34
+ expect(a.equals(ReactiveLiteralBoolean.from(true))).toBe(false);
35
+ });
36
+
37
+ test("evaluate", () => {
38
+ const a = ReactiveAnd.from(ReactiveLiteralBoolean.from(true), ReactiveLiteralBoolean.from(true));
39
+ const b = ReactiveAnd.from(ReactiveLiteralBoolean.from(true), ReactiveLiteralBoolean.from(false));
40
+
41
+ expect(a.evaluate({})).toBe(true);
42
+ expect(b.evaluate({})).toBe(false);
43
+ });
44
+
45
+ test("clone", () => {
46
+ const source = ReactiveAnd.from(ReactiveLiteralBoolean.from(true), ReactiveLiteralBoolean.from(false));
47
+
48
+ const clone = source.clone();
49
+
50
+ expect(clone.equals(source)).toBe(true);
51
+ });
@@ -0,0 +1,53 @@
1
+ import { ReactiveLiteralBoolean } from "../terminal/ReactiveLiteralBoolean.js";
2
+ import { ReactiveOr } from "./ReactiveOr.js";
3
+
4
+ test('copy method does not modify the sources', () => {
5
+
6
+ const b0 = ReactiveLiteralBoolean.from(false);
7
+ const b1 = ReactiveLiteralBoolean.from(true);
8
+
9
+ const a = new ReactiveOr();
10
+
11
+
12
+ a.connect(b0, b1);
13
+
14
+ const b = new ReactiveOr();
15
+
16
+ b.copy(a);
17
+
18
+ expect(b0.getValue()).toBe(false);
19
+ expect(b1.getValue()).toBe(true);
20
+
21
+ });
22
+
23
+ test("equals", () => {
24
+
25
+ const a = ReactiveOr.from(ReactiveLiteralBoolean.from(true), ReactiveLiteralBoolean.from(true));
26
+ const b = ReactiveOr.from(ReactiveLiteralBoolean.from(true), ReactiveLiteralBoolean.from(true));
27
+ const c = ReactiveOr.from(ReactiveLiteralBoolean.from(true), ReactiveLiteralBoolean.from(false));
28
+ const d = ReactiveOr.from(ReactiveLiteralBoolean.from(false), ReactiveLiteralBoolean.from(true));
29
+
30
+ expect(a.equals(b)).toBe(true);
31
+ expect(a.equals(c)).toBe(false);
32
+ expect(a.equals(d)).toBe(false);
33
+
34
+ expect(a.equals(ReactiveLiteralBoolean.from(true))).toBe(false);
35
+ });
36
+
37
+ test("evaluate", () => {
38
+ const a = ReactiveOr.from(ReactiveLiteralBoolean.from(true), ReactiveLiteralBoolean.from(true));
39
+ const b = ReactiveOr.from(ReactiveLiteralBoolean.from(true), ReactiveLiteralBoolean.from(false));
40
+ const c = ReactiveOr.from(ReactiveLiteralBoolean.from(false), ReactiveLiteralBoolean.from(false));
41
+
42
+ expect(a.evaluate({})).toBe(true);
43
+ expect(b.evaluate({})).toBe(true);
44
+ expect(c.evaluate({})).toBe(false);
45
+ });
46
+
47
+ test("clone", () => {
48
+ const source = ReactiveOr.from(ReactiveLiteralBoolean.from(true), ReactiveLiteralBoolean.from(false));
49
+
50
+ const clone = source.clone();
51
+
52
+ expect(clone.equals(source)).toBe(true);
53
+ });
@@ -13,8 +13,8 @@ class LinearModifier {
13
13
  * @constructor
14
14
  */
15
15
  constructor(a = 1, b = 0) {
16
- assert.typeOf(a, 'number', 'a');
17
- assert.typeOf(b, 'number', 'b');
16
+ assert.isNumber(a, 'a');
17
+ assert.isNumber(b, 'b');
18
18
 
19
19
  /**
20
20
  * gradient (slope)
@@ -33,3 +33,65 @@ test("equals method", () => {
33
33
  expect(a.equals(c)).toBe(false);
34
34
  expect(a.equals(d)).toBe(false);
35
35
  });
36
+
37
+ test("hash", () => {
38
+ const mod = new LinearModifier(3, -7);
39
+
40
+ const hash = mod.hash();
41
+
42
+ expect(mod.hash()).toEqual(hash); // idempotency
43
+ expect(typeof hash).toEqual("number");
44
+ expect(Number.isInteger(hash)).toBe(true);
45
+ });
46
+
47
+ test("copy", () => {
48
+
49
+ const source = new LinearModifier(3, -7);
50
+ const target = new LinearModifier(1, 1);
51
+
52
+ target.copy(source);
53
+
54
+ expect(target.a).toBe(3);
55
+ expect(target.b).toBe(-7);
56
+
57
+ // check that original was not changed
58
+
59
+ expect(source.a).toBe(3);
60
+ expect(source.b).toBe(-7);
61
+ });
62
+
63
+ test("clone", () => {
64
+
65
+ const source = new LinearModifier(3, -7);
66
+
67
+ const clone = source.clone();
68
+
69
+ expect(clone.equals(source)).toBe(true);
70
+
71
+ });
72
+
73
+ test("to/from JSON consistency", () => {
74
+
75
+ const source = new LinearModifier(3, -7);
76
+ const target = new LinearModifier(1, 1);
77
+
78
+ target.fromJSON(source.toJSON());
79
+
80
+ expect(target.a).toBe(3);
81
+ expect(target.b).toBe(-7);
82
+
83
+ // check that original was not changed
84
+
85
+ expect(source.a).toBe(3);
86
+ expect(source.b).toBe(-7);
87
+ });
88
+
89
+ test("toString produces a valid string", () => {
90
+
91
+ const s = new LinearModifier(3, -7).toString();
92
+
93
+ expect(typeof s).toBe("string");
94
+ expect(s.trim().length).toBeGreaterThan(0);
95
+
96
+ });
97
+
@@ -140,6 +140,15 @@ class Stat extends Number {
140
140
  this.updateValue();
141
141
  }
142
142
 
143
+ /**
144
+ *
145
+ * @param {LinearModifier} mod
146
+ * @return {boolean}
147
+ */
148
+ hasModifier(mod) {
149
+ return this.__modifiers.contains(mod);
150
+ }
151
+
143
152
  /**
144
153
  *
145
154
  * @param {LinearModifier} mod
@@ -174,6 +183,8 @@ class Stat extends Number {
174
183
  copy(other) {
175
184
  this.base.copy(other.base);
176
185
  this.__modifiers.copy(other.__modifiers);
186
+
187
+ this.updateValue();
177
188
  }
178
189
 
179
190
  /**
@@ -226,6 +237,7 @@ class Stat extends Number {
226
237
  fromJSON(json) {
227
238
  this.base.fromJSON(json.base);
228
239
  this.__modifiers.fromJSON(json.modifiers, LinearModifier);
240
+ this.updateValue();
229
241
  }
230
242
 
231
243
  /**
@@ -73,3 +73,39 @@ test("order of modifiers doesn't matter", () => {
73
73
  expect(s.getValue()).toEqual(-45);
74
74
  }
75
75
  });
76
+
77
+ test("toString produces a valid string", () => {
78
+ const stat = new Stat(1);
79
+
80
+ const s = stat.toString();
81
+
82
+ expect(typeof s).toBe("string");
83
+ expect(s.trim().length).toBeGreaterThan(0);
84
+ });
85
+
86
+ test("copy", () => {
87
+ const source = new Stat(7);
88
+
89
+ source.addModifier(LinearModifier.CONSTANT_ONE);
90
+
91
+ const other = new Stat(1);
92
+
93
+ other.copy(source);
94
+
95
+ expect(other.getBaseValue()).toBe(7);
96
+ expect(other.hasModifier(LinearModifier.CONSTANT_ONE));
97
+
98
+ expect(other.equals(source)).toBe(true);
99
+ });
100
+
101
+ test("to/from JSON consistency", () => {
102
+
103
+ const source = new Stat(7);
104
+ source.addModifier(LinearModifier.CONSTANT_ONE);
105
+
106
+ const destination = new Stat(1);
107
+
108
+ destination.fromJSON(source.toJSON());
109
+
110
+ expect(destination.equals(source)).toBe(true);
111
+ });
@@ -0,0 +1,28 @@
1
+ import { computeStridedIntegerArrayHash } from "./computeStridedIntegerArrayHash.js";
2
+
3
+ /**
4
+ *
5
+ * @param {number[]} value
6
+ * @param {number} [stride]
7
+ */
8
+ function check(value, stride = 1) {
9
+ const hash = computeStridedIntegerArrayHash(value, 0, value.length, 1);
10
+
11
+ expect(typeof hash).toBe('number');
12
+ expect(Number.isInteger(hash)).toBe(true);
13
+
14
+ }
15
+
16
+ test("sanity", () => {
17
+
18
+ check([], 1);
19
+
20
+ check([1], 1);
21
+ check([1], 3);
22
+
23
+ check([1, 3], 1);
24
+ check([1, 3], 3);
25
+
26
+ check([1, 3, 7], 3);
27
+
28
+ });
@@ -0,0 +1,14 @@
1
+ import { assert } from "../../assert.js";
2
+
3
+ /**
4
+ *
5
+ * @param {number} x
6
+ * @param {string} [separator=',']
7
+ * @returns {string}
8
+ */
9
+ export function number_format_by_thousands(x, separator = ',') {
10
+ assert.isNumber(x, 'x');
11
+ assert.isString(separator, 'separator');
12
+
13
+ return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, separator);
14
+ }
@@ -0,0 +1,49 @@
1
+ import { assert } from "../../assert.js";
2
+ import { number_format_by_thousands } from "./number_format_by_thousands.js";
3
+
4
+ /**
5
+ *
6
+ * @param {string} value
7
+ * @returns {number}
8
+ */
9
+ function countDecimals(value) {
10
+ if (value % 1 === 0) {
11
+ //whole number
12
+ return 0;
13
+ }
14
+ const s = value.toString();
15
+ const index = s.indexOf('.');
16
+
17
+ if (index === -1) {
18
+ return 0;
19
+ }
20
+
21
+ //find last 0
22
+ let endIndex = s.length - 1;
23
+ for (; endIndex > index && s.charAt(endIndex) === "0"; endIndex--) {
24
+
25
+ }
26
+ return endIndex - index;
27
+ }
28
+
29
+ /**
30
+ *
31
+ * @param {number} value
32
+ * @returns {string|number}
33
+ */
34
+ export function number_pretty_print(value) {
35
+ assert.isNumber(value, 'value');
36
+
37
+ const MAX_DECIMALS = 2;
38
+
39
+ const fraction = value % 1;
40
+ if (fraction !== 0 && Math.abs(value) < 100) {
41
+ const decimals = countDecimals(value.toFixed(MAX_DECIMALS));
42
+ const decimalsToPrint = Math.min(decimals, MAX_DECIMALS);
43
+ return value.toFixed(decimalsToPrint);
44
+ } else {
45
+ //no fraction
46
+ return number_format_by_thousands(value - fraction, ",");
47
+ }
48
+
49
+ }
@@ -1,13 +1,20 @@
1
1
  import { computeStringHash } from "./computeStringHash.js";
2
2
 
3
- test('hash', () => {
3
+ function check(value) {
4
4
  // must produce a number
5
- expect(typeof computeStringHash('')).toBe('number');
5
+ const hash = computeStringHash(value);
6
+ expect(typeof hash).toBe('number');
7
+ expect(Number.isInteger(hash)).toBe(true);
8
+ expect(computeStringHash(value)).toEqual(hash); // stability
9
+ }
6
10
 
7
- // check for identical results from identical inputs
8
- expect(computeStringHash('')).toBe(computeStringHash(''));
11
+ test('hash', () => {
9
12
 
10
- expect(computeStringHash('cat')).toBe(computeStringHash('cat'));
13
+ check('');
14
+ check('cat');
15
+ check('tomato');
16
+ check(null);
17
+ check(undefined);
11
18
 
12
19
  expect(computeStringHash('tomato')).not.toBe(computeStringHash('potato'));
13
20
  });
@@ -4,14 +4,14 @@ import ObservedEnum from "../model/ObservedEnum.js";
4
4
  import { ProcessState } from "./ProcessState.js";
5
5
 
6
6
  export class BaseProcess {
7
- constructor() {
8
- this.id = "";
9
- /**
10
- * @readonly
11
- * @type {ObservedEnum.<ProcessState>}
12
- */
13
- this.__state = new ObservedEnum(ProcessState.New, ProcessState);
14
- }
7
+
8
+ id = "";
9
+ /**
10
+ * @readonly
11
+ * @type {ObservedEnum.<ProcessState>}
12
+ */
13
+ __state = new ObservedEnum(ProcessState.New, ProcessState);
14
+
15
15
 
16
16
  /**
17
17
  * @returns {ObservedEnum<ProcessState>}