@woosh/meep-engine 2.49.7 → 2.49.9

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 (108) hide show
  1. package/editor/enableEditor.js +1 -8
  2. package/package.json +1 -1
  3. package/samples/terrain/editor.js +2 -2
  4. package/src/core/cache/Cache.js +1 -1
  5. package/src/core/collection/HashSet.js +1 -1
  6. package/src/core/collection/table/RowFirstTableSpec.js +110 -92
  7. package/src/core/collection/table/RowFirstTableSpec.spec.js +49 -0
  8. package/src/core/color/Color.d.ts +4 -2
  9. package/src/core/color/Color.js +7 -8
  10. package/src/core/color/Color.spec.js +93 -0
  11. package/src/core/color/hex2rgb.spec.js +10 -0
  12. package/src/core/color/rgb2hex.js +1 -1
  13. package/src/core/color/rgb2hex.spec.js +13 -0
  14. package/src/core/fsm/simple/SimpleStateMachine.spec.js +75 -0
  15. package/src/core/fsm/simple/SimpleStateMachineDescription.js +1 -1
  16. package/src/core/fsm/simple/SimpleStateMachineDescription.spec.js +32 -0
  17. package/src/core/geom/2d/Rectangle.spec.js +51 -0
  18. package/src/core/geom/3d/aabb/aabb3_intersects_ray.spec.js +90 -0
  19. package/src/core/geom/3d/line/line3_compute_nearest_point_to_point.spec.js +61 -0
  20. package/src/core/geom/3d/morton/mortonEncode_magicbits.js +1 -34
  21. package/src/core/geom/3d/morton/split_by_2.js +17 -0
  22. package/src/core/geom/3d/morton/split_by_3.js +16 -0
  23. package/src/core/geom/3d/plane/computePlaneLineIntersection.js +6 -1
  24. package/src/core/geom/3d/sphere/sphere_intersects_ray.spec.js +11 -0
  25. package/src/core/geom/3d/sphere/sphere_radius_sqr_from_v3_array_transformed.spec.js +8 -0
  26. package/src/core/geom/3d/topology/samples/sampleFloodFill.js +3 -3
  27. package/src/core/geom/3d/topology/struct/binary/io/OrderedEdge.js +1 -1
  28. package/src/core/geom/random/randomPointOnBox.spec.js +57 -0
  29. package/src/core/math/bessel_i0.spec.js +43 -0
  30. package/src/core/math/bessel_j0.js +30 -0
  31. package/src/core/math/hash/murmur3_32.spec.js +8 -0
  32. package/src/core/math/hash/squirrel3.spec.js +16 -0
  33. package/src/core/math/interval/NumericInterval.js +1 -0
  34. package/src/core/math/{bessel_i0.js → modified_bessel_i0.js} +5 -2
  35. package/src/core/math/noise/{create_noise_2d.js → create_simplex_noise_2d.js} +2 -2
  36. package/src/core/math/noise/create_simplex_noise_2d.spec.js +21 -0
  37. package/src/core/math/normalizeArrayVector.spec.js +15 -0
  38. package/src/core/math/physics/irradiance/interpolate_irradiance_linear.spec.js +20 -0
  39. package/src/core/math/physics/irradiance/interpolate_irradiance_lograrithmic.js +4 -4
  40. package/src/core/math/physics/irradiance/interpolate_irradiance_lograrithmic.spec.js +18 -0
  41. package/src/core/math/physics/irradiance/interpolate_irradiance_smith.js +1 -1
  42. package/src/core/math/physics/irradiance/interpolate_irradiance_smith.spec.js +20 -0
  43. package/src/core/math/random/seededRandomMersenneTwister.spec.js +10 -0
  44. package/src/core/math/spline/spline_bezier3.js +1 -1
  45. package/src/core/math/spline/spline_bezier3_bounds.js +2 -1
  46. package/src/core/math/spline/spline_bezier3_bounds.spec.js +37 -0
  47. package/src/core/math/statistics/computeSampleSize_Cochran.spec.js +12 -0
  48. package/src/core/math/statistics/computeStatisticalPartialMedian.js +4 -0
  49. package/src/core/math/statistics/computeStatisticalPartialMedian.spec.js +13 -0
  50. package/src/engine/Engine.d.ts +4 -2
  51. package/src/engine/Engine.js +62 -41
  52. package/src/engine/Engine.spec.js +11 -0
  53. package/src/engine/InputEngine.js +12 -0
  54. package/src/engine/achievements/Achievement.spec.js +21 -0
  55. package/src/engine/animation/curve/compression/prototypeCurveCompression.js +2 -2
  56. package/src/engine/animation/curve/compression/{animation_curve_to_float_array.js → sample_animation_curve_to_float_array.js} +10 -3
  57. package/src/engine/animation/curve/compression/sample_animation_curve_to_float_array.spec.js +29 -0
  58. package/src/engine/animation/curve/draw/build_curve_editor.js +3 -3
  59. package/src/engine/development/performance/RingBufferMetric.js +1 -1
  60. package/src/engine/ecs/animation/Animation.js +2 -180
  61. package/src/engine/ecs/animation/AnimationClip.js +132 -0
  62. package/src/engine/ecs/animation/AnimationClip.spec.js +5 -0
  63. package/src/engine/ecs/animation/AnimationClipFlag.js +7 -0
  64. package/src/engine/ecs/animation/AnimationFlags.js +8 -0
  65. package/src/engine/ecs/animation/AnimationSerializationAdapter.js +32 -0
  66. package/src/engine/ecs/systems/AnimationSystem.js +3 -1
  67. package/src/engine/graphics/GraphicsEngine.js +2 -13
  68. package/src/engine/graphics/camera/testClippingPlaneComputation.js +1 -1
  69. package/src/engine/graphics/ecs/animation/AnimationControllerSystem.js +2 -1
  70. package/src/engine/graphics/ecs/compileAllMaterials.js +1 -1
  71. package/src/engine/graphics/ecs/mesh/createTaskWaitForMeshesToLoad.js +1 -1
  72. package/src/engine/graphics/ecs/path/testPathDisplaySystem.js +1 -1
  73. package/src/engine/graphics/ecs/path/tube/prototypeAnimatedPathMask.js +1 -1
  74. package/src/engine/graphics/impostors/octahedral/ImpostorBaker.js +1 -1
  75. package/src/engine/graphics/load_and_set_cubemap_v0.js +21 -0
  76. package/src/engine/graphics/material/optimization/MaterialOptimizationContext.js +1 -1
  77. package/src/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +3 -5
  78. package/src/engine/graphics/render/buffer/buffers/prototypeNormalFrameBuffer.js +3 -5
  79. package/src/engine/graphics/render/forward_plus/plugin/ptototypeFPPlugin.js +2 -2
  80. package/src/engine/graphics/render/forward_plus/prototype/prototypeLightManager.js +1 -1
  81. package/src/engine/graphics/render/visibility/hiz/prototypeHiZ.js +3 -5
  82. package/src/engine/graphics/sh3/prototypeSH3Probe.js +4 -4
  83. package/src/engine/graphics/texture/sampler/copy_Sampler2D_channel_data.js +4 -3
  84. package/src/engine/graphics/texture/sampler/filter/kaiser_1.js +8 -4
  85. package/src/engine/graphics/texture/sampler/filter/kaiser_bessel_window.js +2 -0
  86. package/src/engine/graphics/texture/virtual/VirtualTexture.js +9 -0
  87. package/src/engine/graphics/texture/virtual/VirtualTexture.spec.js +5 -4
  88. package/src/engine/graphics/texture/virtual/tile/TileLoader.js +5 -0
  89. package/src/engine/input/devices/PointerDevice.spec.js +7 -0
  90. package/src/engine/platform/InMemoryEnginePlatform.js +20 -0
  91. package/src/engine/save/Storage.d.ts +7 -7
  92. package/src/engine/save/storage/InMemoryStorage.js +34 -0
  93. package/src/generation/filtering/numeric/complex/CellFilterAngleToNormal.js +11 -4
  94. package/src/generation/filtering/numeric/complex/CellFilterAngleToNormal.spec.js +30 -0
  95. package/src/generation/filtering/numeric/complex/CellFilterGaussianBlur.js +18 -2
  96. package/src/generation/filtering/numeric/complex/CellFilterGaussianBlur.spec.js +17 -0
  97. package/src/generation/filtering/numeric/complex/CellFilterSimplexNoise.js +2 -2
  98. package/src/generation/grid/GridData.js +0 -60
  99. package/src/generation/grid/generation/road/GridTaskGenerateRoads.js +2 -2
  100. /package/src/core/collection/{IteratorUtils.js → collectIteratorValueToArray.js} +0 -0
  101. /package/src/core/color/{ColorUtils.js → parseColor.js} +0 -0
  102. /package/src/engine/ecs/{animation → ik}/IKMath.js +0 -0
  103. /package/src/engine/ecs/{animation → ik}/IKProblem.js +0 -0
  104. /package/src/engine/ecs/{animation → ik}/IKSolver.js +0 -0
  105. /package/src/engine/ecs/{animation → ik}/InverseKinematics.js +0 -0
  106. /package/src/engine/ecs/{animation → ik}/InverseKinematicsSystem.js +0 -0
  107. /package/src/engine/ecs/{animation → ik}/OneBoneSurfaceAlignmentSolver.js +0 -0
  108. /package/src/engine/ecs/{animation → ik}/TwoBoneInverseKinematicsSolver.js +0 -0
@@ -61,14 +61,7 @@ export function enableEditor(engine, initialization = noop) {
61
61
  }
62
62
 
63
63
  //bind key for toggling editor
64
- engine.inputEngine.mapKey(144, {
65
- on: function () {
66
- toggleEditor();
67
- },
68
- off: function () {
69
-
70
- }
71
- });
64
+ engine.devices.keyboard.keys.num_lock.down.add(toggleEditor);
72
65
 
73
66
  console.warn('Editor mode enabled, use NumLock key to toggle editor mode');
74
67
 
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.49.7",
8
+ "version": "2.49.9",
9
9
  "main": "build/meep.module.js",
10
10
  "module": "build/meep.module.js",
11
11
  "exports": {
@@ -1,7 +1,6 @@
1
1
  import { EngineHarness } from "../../src/engine/EngineHarness.js";
2
2
  import EmptyView from "../../src/view/elements/EmptyView.js";
3
3
  import { EngineConfiguration } from "../../src/engine/EngineConfiguration.js";
4
- import { buildCubeURLs } from "../../src/engine/graphics/texture/cubemap/buildCubeURLs.js";
5
4
  import { ArrayBufferLoader } from "../../src/engine/asset/loaders/ArrayBufferLoader.js";
6
5
  import { TerrainFlags } from "../../src/engine/ecs/terrain/ecs/TerrainFlags.js";
7
6
  import Signal from "../../src/core/events/signal/Signal.js";
@@ -22,6 +21,7 @@ import { copy_Sampler2D_channel_data } from "../../src/engine/graphics/texture/s
22
21
  import { TextureAssetLoader } from "../../src/engine/asset/loaders/texture/TextureAssetLoader.js";
23
22
  import { CanvasView } from "../../src/view/elements/CanvasView.js";
24
23
  import convertSampler2D2Canvas from "../../src/engine/graphics/texture/sampler/Sampler2D2Canvas.js";
24
+ import { load_and_set_cubemap_v0 } from "../../src/engine/graphics/load_and_set_cubemap_v0.js";
25
25
 
26
26
  const engineHarness = new EngineHarness();
27
27
 
@@ -338,7 +338,7 @@ async function init(harness) {
338
338
 
339
339
  await makeConfig(engine).apply(engine);
340
340
 
341
- await engine.graphics.loadEnvironmentMap(buildCubeURLs('data/textures/cubemaps/hip_miramar/32/', '.png'));
341
+ await load_and_set_cubemap_v0(engine.graphics, 'data/textures/cubemaps/hip_miramar/32/', '.png');
342
342
 
343
343
  await harness.initialize();
344
344
 
@@ -3,7 +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
+ import { collectIteratorValueToArray } from "../collection/collectIteratorValueToArray.js";
7
7
 
8
8
  /**
9
9
  * Hash-based cache, uses LRU (least-recently-used) eviction policy
@@ -105,7 +105,7 @@ export class HashSet {
105
105
  }
106
106
 
107
107
  /**
108
- *
108
+ * Get the equivalent value that's already in the set or add the supplied value if it's not present
109
109
  * @param {T} value
110
110
  * @return {T}
111
111
  */
@@ -10,6 +10,7 @@ import { isArrayEqualStrict } from "../array/isArrayEqualStrict.js";
10
10
  import { BinaryDataType } from "../../binary/type/BinaryDataType.js";
11
11
  import { DataTypeByteSizes } from "../../binary/type/DataTypeByteSizes.js";
12
12
  import { assert } from "../../assert.js";
13
+ import { computeHashArray } from "../array/computeHashArray.js";
13
14
 
14
15
  /**
15
16
  * @readonly
@@ -152,123 +153,140 @@ function genCellReader(type, offset, endianType = EndianType.BigEndian) {
152
153
  });
153
154
  }
154
155
 
155
- /**
156
- *
157
- * @param {BinaryDataType[]} types
158
- * @param {EndianType} [endianType]
159
- * @constructor
160
- */
161
- export function RowFirstTableSpec(types, endianType = EndianType.BigEndian) {
162
- assert.isArray(types, 'types');
163
-
164
- const numTypes = types.length;
165
-
156
+ export class RowFirstTableSpec {
166
157
  /**
167
- * @readonly
168
- * @type {BinaryDataType[]}
158
+ *
159
+ * @param {BinaryDataType[]} types
160
+ * @param {EndianType} [endianType]
161
+ * @constructor
169
162
  */
170
- this.types = types;
163
+ constructor(types, endianType = EndianType.BigEndian) {
164
+ assert.isArray(types, 'types');
171
165
 
172
- /**
173
- * @readonly
174
- * @type {EndianType}
175
- */
176
- this.endianType = endianType;
166
+ const numTypes = types.length;
177
167
 
178
- /**
179
- * @readonly
180
- * @type {number[]}
181
- */
182
- this.columnOffsets = new Array(numTypes);
183
- let byteOffset = 0;
184
- types.forEach((type, index) => {
185
- this.columnOffsets[index] = byteOffset;
168
+ /**
169
+ * @readonly
170
+ * @type {BinaryDataType[]}
171
+ */
172
+ this.types = types;
186
173
 
187
- const columnByteSize = DataTypeByteSizes[type];
174
+ /**
175
+ * @readonly
176
+ * @type {EndianType}
177
+ */
178
+ this.endianType = endianType;
188
179
 
189
- byteOffset += columnByteSize;
190
- });
180
+ /**
181
+ * @readonly
182
+ * @type {number[]}
183
+ */
184
+ this.columnOffsets = new Uint32Array(numTypes);
191
185
 
186
+ let byteOffset = 0;
192
187
 
193
- /**
194
- * @readonly
195
- * @type {number}
196
- */
197
- this.bytesPerRecord = byteOffset;
188
+ for (let index = 0; index < numTypes; index++) {
189
+ const type = types[index];
190
+ this.columnOffsets[index] = byteOffset;
191
+
192
+ const columnByteSize = DataTypeByteSizes[type];
193
+
194
+ byteOffset += columnByteSize;
195
+ }
196
+
197
+
198
+ /**
199
+ * @readonly
200
+ * @type {number}
201
+ */
202
+ this.bytesPerRecord = byteOffset;
203
+
204
+ /**
205
+ * @readonly
206
+ * @type {function(DataView, number, number[]): void}
207
+ */
208
+ this.readRowMethod = genRowReader(types, endianType);
209
+
210
+ /**
211
+ * @readonly
212
+ * @type {function(DataView, number, number[]): void}
213
+ */
214
+ this.writeRowMethod = genRowWriter(types, endianType);
215
+
216
+
217
+ //generate cell readers/writers
218
+ this.cellWriters = new Array(numTypes);
219
+ this.cellReaders = new Array(numTypes);
220
+
221
+ for (let i = 0; i < numTypes; i++) {
222
+ this.cellReaders[i] = genCellReader(types[i], this.columnOffsets[i], endianType);
223
+ this.cellWriters[i] = genCellWriter(types[i], this.columnOffsets[i], endianType);
224
+ }
225
+ /**
226
+ * @readonly
227
+ * @type {boolean}
228
+ */
229
+ this.isRowFirstTableSpec = true;
230
+ }
198
231
 
199
232
  /**
200
- * @readonly
201
- * @type {function(DataView, number, number[]): void}
233
+ *
234
+ * @return {number}
202
235
  */
203
- this.readRowMethod = genRowReader(types, endianType);
236
+ getColumnCount() {
237
+ return this.types.length;
238
+ }
204
239
 
205
240
  /**
206
- * @readonly
207
- * @type {function(DataView, number, number[]): void}
241
+ *
242
+ * @return {number}
208
243
  */
209
- this.writeRowMethod = genRowWriter(types, endianType);
244
+ hash() {
245
+ let hash = this.endianType === EndianType.BigEndian ? 1 : 0;
210
246
 
247
+ hash += computeHashArray(this.types, computeStringHash);
211
248
 
212
- //generate cell readers/writers
213
- this.cellWriters = new Array(numTypes);
214
- this.cellReaders = new Array(numTypes);
215
-
216
- for (let i = 0; i < numTypes; i++) {
217
- this.cellReaders[i] = genCellReader(types[i], this.columnOffsets[i], endianType);
218
- this.cellWriters[i] = genCellWriter(types[i], this.columnOffsets[i], endianType);
249
+ return hash;
219
250
  }
220
- }
221
251
 
222
- /**
223
- *
224
- * @return {number}
225
- */
226
- RowFirstTableSpec.prototype.getColumnCount = function () {
227
- return this.types.length;
228
- }
252
+ /**
253
+ *
254
+ * @param {RowFirstTableSpec} other
255
+ * @returns {boolean}
256
+ */
257
+ equals(other) {
258
+ if (this.endianType !== other.endianType) {
259
+ return false;
260
+ }
229
261
 
230
- /**
231
- *
232
- * @param {RowFirstTableSpec} other
233
- * @returns {boolean}
234
- */
235
- RowFirstTableSpec.prototype.equals = function (other) {
236
- if (this.endianType !== other.endianType) {
237
- return false;
262
+ return isArrayEqualStrict(this.types, other.types);
238
263
  }
239
264
 
240
- return isArrayEqualStrict(this.types, other.types);
241
- };
242
-
243
- /**
244
- * @readonly
245
- * @type {boolean}
246
- */
247
- RowFirstTableSpec.prototype.isRowFirstTableSpec = true;
248
-
265
+ /**
266
+ *
267
+ * @param {BinaryDataType[]} types
268
+ * @param {EndianType} [endianType]
269
+ * @returns {RowFirstTableSpec}
270
+ */
271
+ static get(types, endianType = EndianType.BigEndian) {
272
+ //compute hash
273
+ const hash = types.join('.') + ':' + endianType;
249
274
 
250
- const cache = new Cache({
251
- keyHashFunction: computeStringHash
252
- });
275
+ const cachedValue = cache.get(hash);
253
276
 
254
- /**
255
- *
256
- * @param {BinaryDataType[]} types
257
- * @param {EndianType} [endianType]
258
- * @returns {RowFirstTableSpec}
259
- */
260
- RowFirstTableSpec.get = function (types, endianType = EndianType.BigEndian) {
261
- //compute hash
262
- const hash = types.join('.') + ':' + endianType;
277
+ if (cachedValue !== null) {
278
+ return cachedValue;
279
+ }
263
280
 
264
- const cachedValue = cache.get(hash);
281
+ const newValue = new RowFirstTableSpec(types);
282
+ cache.put(hash, newValue);
265
283
 
266
- if (cachedValue !== null) {
267
- return cachedValue;
284
+ return newValue;
268
285
  }
286
+ }
269
287
 
270
- const newValue = new RowFirstTableSpec(types);
271
- cache.put(hash, newValue);
272
288
 
273
- return newValue;
274
- };
289
+ const cache = new Cache({
290
+ keyHashFunction: computeStringHash
291
+ });
292
+
@@ -0,0 +1,49 @@
1
+ import { RowFirstTableSpec } from "./RowFirstTableSpec.js";
2
+ import { BinaryDataType } from "../../binary/type/BinaryDataType.js";
3
+ import { EndianType } from "../../binary/BinaryBuffer.js";
4
+
5
+ test("constructor with 1 column does not throw", () => {
6
+ expect(() => new RowFirstTableSpec([BinaryDataType.Float32])).not.toThrow();
7
+ });
8
+
9
+ test("hash produced a valid value", () => {
10
+ const spec = new RowFirstTableSpec([BinaryDataType.Float32]);
11
+
12
+ const hash = spec.hash();
13
+
14
+ expect(typeof hash).toBe("number");
15
+ expect(hash).not.toBeNaN();
16
+ expect(Number.isInteger(hash)).toBe(true);
17
+ });
18
+
19
+ test("hash is stable", () => {
20
+
21
+ const spec = new RowFirstTableSpec([BinaryDataType.Float32]);
22
+
23
+ expect(spec.hash()).toEqual(spec.hash());
24
+
25
+ });
26
+
27
+ test("hashes of identical specs must match", () => {
28
+ const a = new RowFirstTableSpec([BinaryDataType.Float32]);
29
+ const b = new RowFirstTableSpec([BinaryDataType.Float32]);
30
+
31
+ expect(a.hash()).toEqual(b.hash());
32
+ });
33
+
34
+ test("equals", () => {
35
+ const a = new RowFirstTableSpec([BinaryDataType.Float32, BinaryDataType.Uint8], EndianType.BigEndian);
36
+ const b = new RowFirstTableSpec([BinaryDataType.Float32, BinaryDataType.Uint8], EndianType.BigEndian);
37
+
38
+ const c = new RowFirstTableSpec([BinaryDataType.Uint8, BinaryDataType.Float32], EndianType.BigEndian);
39
+
40
+ const d = new RowFirstTableSpec([BinaryDataType.Float32, BinaryDataType.Uint8], EndianType.LittleEndian);
41
+
42
+ expect(a.equals(a)).toBe(true);
43
+ expect(a.equals(b)).toBe(true);
44
+
45
+ expect(a.equals(c)).toBe(false);
46
+ expect(c.equals(a)).toBe(false);
47
+
48
+ expect(a.equals(d)).toBe(false);
49
+ });
@@ -1,12 +1,12 @@
1
1
  import Signal from "../events/signal/Signal";
2
2
 
3
3
  export class Color {
4
- constructor(r?: number, g?: number, b?: number,a?:number)
4
+ constructor(r?: number, g?: number, b?: number, a?: number)
5
5
 
6
6
  r: number
7
7
  g: number
8
8
  b: number
9
- a:number
9
+ a: number
10
10
 
11
11
  readonly onChanged: Signal<number, number, number, number, number, number>
12
12
 
@@ -14,6 +14,8 @@ export class Color {
14
14
 
15
15
  setRGB(r: number, g: number, b: number): void
16
16
 
17
+ setA(a: number): void
18
+
17
19
  setHSL(h: number, s: number, l: number): void
18
20
 
19
21
  setHCL(h: number, c: number, l: number): void
@@ -1,11 +1,9 @@
1
1
  import { assert } from "../assert.js";
2
2
  import Signal from "../events/signal/Signal.js";
3
- import { parseColor } from "./ColorUtils.js";
3
+ import { parseColor } from "./parseColor.js";
4
4
  import { min2 } from "../math/min2.js";
5
5
  import { max2 } from "../math/max2.js";
6
6
  import { min3 } from "../math/min3.js";
7
- import { computeHashIntegerArray } from "../collection/array/computeHashIntegerArray.js";
8
- import { computeHashFloat } from "../primitives/numbers/computeHashFloat.js";
9
7
  import { rgb2hsv } from "./rgb2hsv.js";
10
8
  import { rgb2uint24 } from "./rgb2uint24.js";
11
9
  import { clamp01 } from "../math/clamp01.js";
@@ -156,6 +154,11 @@ export class Color {
156
154
  assert.isNumber(b, 'b');
157
155
  assert.isNumber(a, 'a');
158
156
 
157
+ assert.notNaN(r, 'r');
158
+ assert.notNaN(g, 'g');
159
+ assert.notNaN(b, 'b');
160
+ assert.notNaN(a, 'a');
161
+
159
162
  const _r = this.r;
160
163
  const _g = this.g;
161
164
  const _b = this.b;
@@ -462,11 +465,7 @@ export class Color {
462
465
  * @returns {number}
463
466
  */
464
467
  hash() {
465
- return computeHashIntegerArray(
466
- computeHashFloat(this.r),
467
- computeHashFloat(this.g),
468
- computeHashFloat(this.b)
469
- );
468
+ return this.toUint();
470
469
  }
471
470
 
472
471
  fromJSON({ r, g, b, a = 1 }) {
@@ -53,3 +53,96 @@ test("setHSV white", () => {
53
53
  expect(c.g).toBeCloseTo(1);
54
54
  expect(c.b).toBeCloseTo(1);
55
55
  });
56
+
57
+ test("setA sets alpha nd invokes 'onChanged' signal", () => {
58
+ const c = new Color();
59
+
60
+ const mock = jest.fn();
61
+
62
+ c.onChanged.add(mock);
63
+
64
+ c.setA(0.7);
65
+
66
+ expect(c.a).toBe(0.7);
67
+ expect(mock).toHaveBeenCalledTimes(1);
68
+ });
69
+
70
+ test("equals", () => {
71
+
72
+ expect(new Color(0, 0, 0, 0).equals(new Color(0, 0, 0, 0)))
73
+ .toBe(true);
74
+
75
+ expect(new Color(0.1, 0.2, 0.3, 0.7).equals(new Color(0.1, 0.2, 0.3, 0.7)))
76
+ .toBe(true);
77
+
78
+ expect(new Color(0, 0, 0, 0).equals(new Color(0, 0, 0, 1)))
79
+ .toBe(false);
80
+
81
+ expect(new Color(0, 0, 0, 0).equals(new Color(0, 0, 1, 0)))
82
+ .toBe(false);
83
+
84
+ expect(new Color(0, 0, 0, 0).equals(new Color(0, 1, 0, 0)))
85
+ .toBe(false);
86
+
87
+ expect(new Color(0, 0, 0, 0).equals(new Color(1, 0, 0, 0)))
88
+ .toBe(false);
89
+ });
90
+
91
+ test("hash is stable", () => {
92
+ expect(new Color(0.1, 0.3, 0.7, 0.11).hash())
93
+ .toEqual(new Color(0.1, 0.3, 0.7, 0.11).hash());
94
+
95
+ const c = new Color(0.1, 0.3, 0.7, 0.11);
96
+
97
+ expect(c.hash()).toEqual(c.hash());
98
+
99
+ const hash = c.hash();
100
+
101
+ expect(typeof hash).toEqual("number");
102
+ expect(Number.isInteger(hash)).toBe(true);
103
+ });
104
+
105
+ test("toJSON", () => {
106
+
107
+ expect(new Color(0.1, 0.3, 0.7, 0.11).toJSON())
108
+ .toEqual({
109
+ r: 0.1,
110
+ g: 0.3,
111
+ b: 0.7,
112
+ a: 0.11
113
+ });
114
+
115
+ });
116
+
117
+ test("fromJSON", () => {
118
+ const c = new Color();
119
+
120
+ c.fromJSON({
121
+ r: 0.1,
122
+ g: 0.3,
123
+ b: 0.7,
124
+ a: 0.11
125
+ });
126
+
127
+ expect(c.r).toBe(0.1);
128
+ expect(c.g).toBe(0.3);
129
+ expect(c.b).toBe(0.7);
130
+ expect(c.a).toBe(0.11);
131
+ });
132
+
133
+ test("iterator", () => {
134
+
135
+ const color = new Color(0.1, 0.3, 0.7, 0.11);
136
+
137
+ const iterator = color[Symbol.iterator]();
138
+
139
+ expect(iterator.next().value).toBe(0.1)
140
+ expect(iterator.next().value).toBe(0.3)
141
+ expect(iterator.next().value).toBe(0.7)
142
+ expect(iterator.next().value).toBe(0.11)
143
+
144
+ const last = iterator.next();
145
+
146
+ expect(last.done).toBe(true)
147
+ expect(last.value).toBeUndefined()
148
+ });
@@ -0,0 +1,10 @@
1
+ import { hex2rgb } from "./hex2rgb.js";
2
+
3
+ test("rgba support", () => {
4
+ expect(hex2rgb('#01020304')).toEqual({
5
+ r: 0x01,
6
+ g: 0x02,
7
+ b: 0x03,
8
+ a: 0x04
9
+ });
10
+ });
@@ -1,7 +1,7 @@
1
1
  import { dec2hex } from "../binary/dec2hex.js";
2
2
 
3
3
  /**
4
- *
4
+ * Takes values in 0..255 range
5
5
  * @param {number} r
6
6
  * @param {number} g
7
7
  * @param {number} b
@@ -0,0 +1,13 @@
1
+ import { rgb2hex } from "./rgb2hex.js";
2
+
3
+ test("white", () => {
4
+ expect(rgb2hex(255, 255, 255)).toBe("ffffff");
5
+ });
6
+
7
+ test("black", () => {
8
+ expect(rgb2hex(0, 0, 0)).toBe("000000");
9
+ });
10
+
11
+ test("unique channel values", () => {
12
+ expect(rgb2hex(11, 3, 7)).toBe("0b0307");
13
+ });
@@ -0,0 +1,75 @@
1
+ import { SimpleStateMachine } from "./SimpleStateMachine.js";
2
+ import { SimpleStateMachineDescription } from "./SimpleStateMachineDescription.js";
3
+
4
+ test("constructor does not throw", () => {
5
+ const description = new SimpleStateMachineDescription();
6
+
7
+ expect(() => new SimpleStateMachine(description)).not.toThrow();
8
+ });
9
+
10
+
11
+ test("state entry event handler is fired as expected", () => {
12
+
13
+ const description = new SimpleStateMachineDescription();
14
+
15
+ const a = description.createState();
16
+ const b = description.createState();
17
+
18
+ description.createEdge(a, b);
19
+
20
+ const fsm = new SimpleStateMachine(description);
21
+
22
+ fsm.setState(a);
23
+
24
+ const entry_callback = jest.fn();
25
+
26
+ fsm.addEventHandlerStateEntry(b, entry_callback);
27
+
28
+ fsm.setState(b);
29
+
30
+ expect(entry_callback).toHaveBeenCalledTimes(1);
31
+ expect(entry_callback).toHaveBeenCalledWith(b, a);
32
+ });
33
+
34
+ test("state exit event handler is fired as expected", () => {
35
+
36
+ const description = new SimpleStateMachineDescription();
37
+
38
+ const a = description.createState();
39
+ const b = description.createState();
40
+
41
+ description.createEdge(a, b);
42
+
43
+ const fsm = new SimpleStateMachine(description);
44
+
45
+ fsm.setState(a);
46
+
47
+ const entry_callback = jest.fn();
48
+
49
+ fsm.addEventHandlerStateExit(a, entry_callback);
50
+
51
+ fsm.setState(b);
52
+
53
+ expect(entry_callback).toHaveBeenCalledTimes(1);
54
+ expect(entry_callback).toHaveBeenCalledWith(a, b);
55
+ });
56
+
57
+ test("'advance' moves us to the next state in the chain", () => {
58
+
59
+
60
+ const description = new SimpleStateMachineDescription();
61
+
62
+ const a = description.createState();
63
+ const b = description.createState();
64
+
65
+ description.createEdge(a, b);
66
+
67
+ const fsm = new SimpleStateMachine(description);
68
+
69
+ fsm.setState(a);
70
+
71
+ fsm.advance(0);
72
+
73
+ expect(fsm.getState()).toBe(b);
74
+
75
+ });
@@ -141,7 +141,7 @@ export class SimpleStateMachineDescription {
141
141
  *
142
142
  * @param {number} start
143
143
  * @param {number} goal
144
- * @returns {number[]}
144
+ * @returns {number[]|null}
145
145
  */
146
146
  findPath(start, goal) {
147
147
  assert.ok(this.stateExists(start), `start state ${start} doesn't exist`);
@@ -69,3 +69,35 @@ test('getIncomingStates', () => {
69
69
  expect(smd.getIncomingStates(b)).toEqual([a]);
70
70
  expect(smd.getIncomingStates(c)).toEqual([a]);
71
71
  });
72
+
73
+ test('edgeExists', () => {
74
+
75
+ const smd = new SimpleStateMachineDescription();
76
+
77
+ const a = smd.createState();
78
+ const b = smd.createState();
79
+
80
+ expect(smd.edgeExists(a, b)).toBe(false);
81
+ expect(smd.edgeExists(b, a)).toBe(false);
82
+
83
+ smd.createEdge(a, b);
84
+
85
+ expect(smd.edgeExists(a, b)).toEqual(true);
86
+ expect(smd.edgeExists(b, a)).toEqual(false);
87
+ });
88
+
89
+ test("findPath sanity check", () => {
90
+
91
+ const smd = new SimpleStateMachineDescription();
92
+
93
+ const a = smd.createState();
94
+ const b = smd.createState();
95
+
96
+ expect(smd.findPath(a, a)).toEqual([a]);
97
+
98
+ expect(smd.findPath(a, b)).toEqual(null);
99
+
100
+ smd.createEdge(a, b);
101
+
102
+ expect(smd.findPath(a, b)).toEqual([a, b]);
103
+ });