@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
@@ -8,7 +8,7 @@
8
8
  */
9
9
  export function interpolate_irradiance_smith(distance, min, max) {
10
10
 
11
- if (distance < min) {
11
+ if (distance <= min) {
12
12
  return 1;
13
13
  } else if (distance > max) {
14
14
  return 0;
@@ -0,0 +1,20 @@
1
+ import { interpolate_irradiance_smith } from "./interpolate_irradiance_smith.js";
2
+
3
+ test("sanity check", () => {
4
+
5
+ expect(interpolate_irradiance_smith(3, 3, 3)).toBe(1);
6
+
7
+ expect(interpolate_irradiance_smith(3, 3, 7)).toBe(1);
8
+
9
+ expect(interpolate_irradiance_smith(7, 3, 7)).toBe(0);
10
+
11
+ expect(interpolate_irradiance_smith(0, 3, 7)).toBe(1);
12
+
13
+ expect(interpolate_irradiance_smith(11, 3, 7)).toBe(0);
14
+
15
+ const mid = interpolate_irradiance_smith(5, 3, 7);
16
+
17
+ expect(mid).toBeGreaterThan(0);
18
+ expect(mid).toBeLessThan(1);
19
+
20
+ });
@@ -0,0 +1,10 @@
1
+ import { seededRandomMersenneTwister } from "./seededRandomMersenneTwister.js";
2
+
3
+ test("sanity", () => {
4
+ const rng = seededRandomMersenneTwister(0);
5
+
6
+ const v0 = rng();
7
+
8
+ expect(v0).toBeLessThanOrEqual(1);
9
+ expect(v0).toBeGreaterThanOrEqual(0);
10
+ });
@@ -9,7 +9,7 @@
9
9
  * @param {number} p3 end point
10
10
  * @returns {number}
11
11
  */
12
- function spline_bezier3(t, p0, p1, p2, p3) {
12
+ export function spline_bezier3(t, p0, p1, p2, p3) {
13
13
  // first we compute necessary factors for each point
14
14
  const nt = 1 - t;
15
15
 
@@ -1,6 +1,7 @@
1
1
  import { min2 } from "../min2.js";
2
2
  import { max2 } from "../max2.js";
3
3
  import { assert } from "../../assert.js";
4
+ import { spline_bezier3 } from './spline_bezier3'
4
5
 
5
6
  /**
6
7
  * Compute bounds of a 3-rd degree bezier curve
@@ -15,7 +16,7 @@ import { assert } from "../../assert.js";
15
16
  * @param {number} p2
16
17
  * @param {number} p3
17
18
  */
18
- function spline_bezier3_bounds(result, result_offset, result_stride, p0, p1, p2, p3) {
19
+ export function spline_bezier3_bounds(result, result_offset, result_stride, p0, p1, p2, p3) {
19
20
  assert.greaterThan(result_stride, 0, 'result_stride must be greater than 0');
20
21
  assert.isInteger(result_stride, 'result_stride');
21
22
 
@@ -0,0 +1,37 @@
1
+ import { spline_bezier3_bounds } from "./spline_bezier3_bounds.js";
2
+
3
+ test("spline of 0 length", () => {
4
+ const bounds = [];
5
+
6
+ spline_bezier3_bounds(
7
+ bounds, 0, 1,
8
+ 0, 0, 0, 0
9
+ );
10
+
11
+ expect(bounds[0]).toBeCloseTo(0);
12
+ expect(bounds[1]).toBeCloseTo(0);
13
+ });
14
+
15
+ test("line-shaped spline of", () => {
16
+ const bounds = [];
17
+
18
+ spline_bezier3_bounds(
19
+ bounds, 0, 1,
20
+ -2, -1, 1, 2
21
+ );
22
+
23
+ expect(bounds[0]).toBeCloseTo(-2);
24
+ expect(bounds[1]).toBeCloseTo(2);
25
+ });
26
+
27
+ test("overshooting control point spline of", () => {
28
+ const bounds = [];
29
+
30
+ spline_bezier3_bounds(
31
+ bounds, 0, 1,
32
+ 0, -1, 1, 0
33
+ );
34
+
35
+ expect(bounds[0]).toBeLessThan(0);
36
+ expect(bounds[1]).toBeGreaterThan(0);
37
+ });
@@ -0,0 +1,12 @@
1
+ import { computeSampleSize_Cochran } from "./computeSampleSize_Cochran.js";
2
+
3
+ test("sanity for 95% confidence", () => {
4
+ function execute(error) {
5
+ return Math.floor(computeSampleSize_Cochran(1.95996, 0.5, error))
6
+ }
7
+
8
+ expect(execute(0.05)).toEqual(384);
9
+ expect(execute(0.035)).toEqual(783);
10
+ expect(execute(0.025)).toEqual(1536);
11
+ expect(execute(0.01)).toEqual(9603);
12
+ });
@@ -1,4 +1,5 @@
1
1
  import { compareNumbersAscending } from "../../function/Functions.js";
2
+ import { assert } from "../../assert.js";
2
3
 
3
4
  /**
4
5
  *
@@ -8,6 +9,9 @@ import { compareNumbersAscending } from "../../function/Functions.js";
8
9
  * @returns {number}
9
10
  */
10
11
  export function computeStatisticalPartialMedian(values, start, end) {
12
+ assert.isArrayLike(values, 'values');
13
+ assert.lessThan(end, values.length);
14
+
11
15
  const copy = values.slice();
12
16
 
13
17
  copy.sort(compareNumbersAscending);
@@ -0,0 +1,13 @@
1
+ import { computeStatisticalPartialMedian } from "./computeStatisticalPartialMedian.js";
2
+
3
+ test("single item", () => {
4
+ expect(computeStatisticalPartialMedian([7], 0, 0)).toEqual(7);
5
+ });
6
+
7
+ test("[1,2,1]", () => {
8
+ expect(computeStatisticalPartialMedian([1,2,1], 0, 2)).toEqual(1);
9
+ });
10
+
11
+ test("[7,6,2,4,1]", () => {
12
+ expect(computeStatisticalPartialMedian([7,6,2,4,1], 0, 4)).toEqual(4);
13
+ });
@@ -12,7 +12,9 @@ import {EnginePluginManager} from "./plugin/EnginePluginManager";
12
12
  import ConcurrentExecutor from "../core/process/executor/ConcurrentExecutor";
13
13
 
14
14
  export interface IEngineInitializationOptions {
15
- entityManager?: EntityManager
15
+ entityManager?: EntityManager,
16
+ enableAudio?: boolean
17
+ enableGraphics?: boolean
16
18
  }
17
19
 
18
20
  export default class Engine {
@@ -25,7 +27,7 @@ export default class Engine {
25
27
  public readonly sound: SoundEngine
26
28
  public readonly graphics: GraphicsEngine
27
29
  public readonly devices: { keyboard: KeyboardDevice, pointer: PointerDevice }
28
- public readonly assetManager: AssetManager
30
+ public readonly assetManager: AssetManager<Engine>
29
31
  public readonly ticker: Ticker
30
32
  public readonly plugins: EnginePluginManager
31
33
  public readonly executor: ConcurrentExecutor
@@ -5,7 +5,6 @@
5
5
  import ConcurrentExecutor from '../core/process/executor/ConcurrentExecutor.js';
6
6
 
7
7
  import { AssetManager } from './asset/AssetManager.js';
8
- import InputEngine from './InputEngine.js';
9
8
  import { GraphicsEngine } from './graphics/GraphicsEngine.js';
10
9
  import SoundEngine from './sound/SoundEngine.js';
11
10
  import { PerspectiveCamera as ThreePerspectiveCamera } from 'three';
@@ -49,9 +48,15 @@ class Engine {
49
48
  *
50
49
  * @param {EnginePlatform} platform
51
50
  * @param {EntityManager} [entityManager]
51
+ * @param enableGraphics
52
+ * @param enableAudio
52
53
  * @constructor
53
54
  */
54
- constructor(platform, { entityManager } = {}) {
55
+ constructor(platform, {
56
+ entityManager,
57
+ enableGraphics = true,
58
+ enableAudio = true
59
+ } = {}) {
55
60
  assert.defined(platform, 'platform');
56
61
 
57
62
  /**
@@ -105,7 +110,7 @@ class Engine {
105
110
  * @type {AssetManager<Engine>}
106
111
  */
107
112
  this.assetManager = new AssetManager({
108
- context:this,
113
+ context: this,
109
114
  executor: this.executor
110
115
  });
111
116
 
@@ -159,11 +164,6 @@ class Engine {
159
164
  });
160
165
  }
161
166
 
162
-
163
- this.initialize();
164
- }
165
-
166
- initialize() {
167
167
  // initialize performance metrics
168
168
  this.performance.create({
169
169
  name: METRIC_ID_RENDER
@@ -218,29 +218,39 @@ class Engine {
218
218
  this.camera = new ThreePerspectiveCamera(45, innerWidth / innerHeight, 1, 50);
219
219
 
220
220
 
221
- /**
222
- *
223
- * @type {GraphicsEngine}
224
- */
225
- const ge = this.graphics = new GraphicsEngine(this.camera, this.entityManager);
221
+ if (enableGraphics !== false) {
226
222
 
227
- try {
228
- ge.start();
229
- } catch (e) {
230
- logger.info("Failed to start GraphicEngine: ", e);
231
- }
223
+ const graphicsEngine = new GraphicsEngine(this.camera, this.entityManager);
232
224
 
233
- this.inputEngine = new InputEngine(ge.domElement, window);
225
+ /**
226
+ *
227
+ * @type {GraphicsEngine}
228
+ */
229
+ this.graphics = graphicsEngine;
234
230
 
235
- //sound engine
236
- const soundEngine = new SoundEngine();
237
- soundEngine.volume = 1;
231
+ try {
232
+ graphicsEngine.start();
233
+ } catch (e) {
234
+ logger.info("Failed to start GraphicEngine: ", e);
235
+ }
238
236
 
239
- /**
240
- *
241
- * @type {SoundEngine}
242
- */
243
- this.sound = soundEngine;
237
+ } else {
238
+ logger.info('enableGraphics option is not set, no graphics engine will be created');
239
+ }
240
+
241
+ if (enableAudio !== false) {
242
+ //sound engine
243
+ const soundEngine = new SoundEngine();
244
+ soundEngine.volume = 1;
245
+
246
+ /**
247
+ *
248
+ * @type {SoundEngine}
249
+ */
250
+ this.sound = soundEngine;
251
+ } else {
252
+ logger.info('enableAudio option is not set, no audio engine will be created');
253
+ }
244
254
 
245
255
  /**
246
256
  * Graphical User Interface engine
@@ -281,14 +291,20 @@ class Engine {
281
291
  this.plugins.initialize(this);
282
292
  }
283
293
 
294
+
295
+ get inputEngine() {
296
+ throw new Error('Deprecated, use .devices instead');
297
+ }
298
+
299
+ /**
300
+ * @deprecated
301
+ */
284
302
  get grid() {
285
303
  throw new Error('Deprecated, use systems/components to achieve the same functionality as before');
286
304
  }
287
305
 
288
306
  initializeViews() {
289
307
 
290
- const viewport = this.graphics.viewport;
291
-
292
308
  const gameView = new EmptyView();
293
309
 
294
310
  gameView.addClass('game-view');
@@ -299,23 +315,28 @@ class Engine {
299
315
  position: "absolute",
300
316
  pointerEvents: "none"
301
317
  });
302
-
303
- viewport.css({
304
- pointerEvents: "auto"
305
- });
306
-
307
318
  this.gameView = gameView;
308
319
 
309
- gameView.addChild(viewport);
310
320
 
311
321
  this.viewStack = new ViewStack();
312
- this.viewStack.push(gameView,'game-view');
322
+ this.viewStack.push(gameView, 'game-view');
313
323
 
314
- //bind size of renderer viewport to game view
315
- viewport.bindSignal(gameView.size.onChanged, viewport.size.set.bind(viewport.size));
316
- gameView.on.linked.add(function () {
317
- viewport.size.copy(gameView.size);
318
- });
324
+ if (this.graphics !== undefined) {
325
+
326
+ const viewport = this.graphics.viewport;
327
+
328
+ viewport.css({
329
+ pointerEvents: "auto"
330
+ });
331
+
332
+ gameView.addChild(viewport);
333
+ //bind size of renderer viewport to game view
334
+ viewport.bindSignal(gameView.size.onChanged, viewport.size.set.bind(viewport.size));
335
+ gameView.on.linked.add(function () {
336
+ viewport.size.copy(gameView.size);
337
+ });
338
+
339
+ }
319
340
  }
320
341
 
321
342
  initializeSettings() {
@@ -0,0 +1,11 @@
1
+ import Engine from "./Engine.js";
2
+ import { InMemoryEnginePlatform } from "./platform/InMemoryEnginePlatform.js";
3
+
4
+ test("constructor does not throw", () => {
5
+ const platform = new InMemoryEnginePlatform();
6
+
7
+ expect(() => new Engine(platform, {
8
+ enableAudio: false,
9
+ enableGraphics: false
10
+ })).not.toThrow();
11
+ });
@@ -4,8 +4,20 @@
4
4
  * Time: 20:15
5
5
  */
6
6
  import { KeyCodes } from './input/devices/KeyCodes.js';
7
+ import { assert } from "../core/assert.js";
7
8
 
9
+ /**
10
+ * @deprecated use PointerDevice and KeyboardDevice respectively
11
+ * @param pointerContext
12
+ * @param keyContext
13
+ * @constructor
14
+ */
8
15
  const InputEngine = function (pointerContext, keyContext) {
16
+ assert.defined(pointerContext, 'pointerContext');
17
+ assert.defined(keyContext, 'keyContext');
18
+
19
+ console.warn('deprecated, use PointerDevice and KeyboardDevice instead');
20
+
9
21
  const keyMap = [];
10
22
  const mouseMap = {};
11
23
 
@@ -0,0 +1,21 @@
1
+ import { Achievement } from "./Achievement.js";
2
+
3
+ test("constructor does not throw", () => {
4
+ expect(() => new Achievement()).not.toThrow();
5
+ });
6
+
7
+ test("fromJSON correctness", () => {
8
+ const achievement = new Achievement();
9
+
10
+ achievement.fromJSON({
11
+ id: "x",
12
+ condition: "hello == kitty",
13
+ icon: "y",
14
+ secret: true
15
+ });
16
+
17
+ expect(achievement.id).toEqual("x");
18
+ expect(achievement.condition).toEqual("hello == kitty");
19
+ expect(achievement.icon).toEqual("y");
20
+ expect(achievement.secret).toEqual(true);
21
+ });
@@ -4,7 +4,7 @@ import { Keyframe } from "../Keyframe.js";
4
4
  import { EngineConfiguration } from "../../../EngineConfiguration.js";
5
5
  import GUIElementSystem from "../../../ecs/gui/GUIElementSystem.js";
6
6
  import ViewportPositionSystem from "../../../ecs/gui/position/ViewportPositionSystem.js";
7
- import { animation_curve_to_float_array } from "./animation_curve_to_float_array.js";
7
+ import { sample_animation_curve_to_float_array } from "./sample_animation_curve_to_float_array.js";
8
8
  import { downsample_float_array_curve_by_error } from "./downsample_float_array_curve_by_error.js";
9
9
  import { build_plot_entity_from_array } from "../draw/build_plot_entity_from_array.js";
10
10
 
@@ -28,7 +28,7 @@ async function init() {
28
28
 
29
29
  function build_error_scale(ecd, curve) {
30
30
  const curve_data = [];
31
- animation_curve_to_float_array(curve_data, curve, 0, 256);
31
+ sample_animation_curve_to_float_array(curve_data, 0, curve, 256);
32
32
 
33
33
 
34
34
  build_plot_entity_from_array({
@@ -5,10 +5,15 @@
5
5
  * Sample entire animation curve into a flat array with discrete equal intervals
6
6
  * @param {number[]|Float32Array} out
7
7
  * @param {number} out_offset
8
- * @param {number} sample_count
9
8
  * @param {AnimationCurve} curve
9
+ * @param {number} sample_count
10
10
  */
11
- export function animation_curve_to_float_array(out, curve, out_offset = 0, sample_count = out.length) {
11
+ export function sample_animation_curve_to_float_array(
12
+ out,
13
+ out_offset,
14
+ curve,
15
+ sample_count = out.length
16
+ ) {
12
17
  const keyframes = curve.keys;
13
18
  const keyframe_count = keyframes.length;
14
19
 
@@ -25,8 +30,10 @@ export function animation_curve_to_float_array(out, curve, out_offset = 0, sampl
25
30
  const time_end = key_last.time;
26
31
  const time_span = time_end - time_start;
27
32
 
33
+ const multiplier = sample_count > 1 ? 1 / (sample_count - 1) : 0;
34
+
28
35
  for (let i = 0; i < sample_count; i++) {
29
- const f = i / (sample_count - 1);
36
+ const f = i * multiplier;
30
37
 
31
38
  const t = time_start + (f * time_span);
32
39
 
@@ -0,0 +1,29 @@
1
+ import { AnimationCurve } from "../AnimationCurve.js";
2
+ import { sample_animation_curve_to_float_array } from "./sample_animation_curve_to_float_array.js";
3
+ import { Keyframe } from "../Keyframe.js";
4
+
5
+ test("0 samples should produce valid result", () => {
6
+
7
+ const curve = new AnimationCurve();
8
+
9
+ const result = new Float32Array(0);
10
+
11
+ expect(() => sample_animation_curve_to_float_array(result, 0, curve, 0)).not.toThrow();
12
+ });
13
+
14
+ test("3 samples on a line", () => {
15
+
16
+
17
+ const curve = new AnimationCurve();
18
+ curve.add(Keyframe.from(-1, -1))
19
+ curve.add(Keyframe.from(1, 7))
20
+
21
+ curve.smoothAllTangents();
22
+
23
+ const result = new Float32Array(3);
24
+ sample_animation_curve_to_float_array(result, 0, curve, 3);
25
+
26
+ expect(result[0]).toBeCloseTo(-1);
27
+ expect(result[1]).toBeCloseTo(3);
28
+ expect(result[2]).toBeCloseTo(7);
29
+ });
@@ -1,10 +1,10 @@
1
1
  import Vector2 from "../../../../core/geom/Vector2.js";
2
2
  import EmptyView from "../../../../view/elements/EmptyView.js";
3
3
  import { CanvasView } from "../../../../view/elements/CanvasView.js";
4
- import AABB2 from "../../../../core/geom/AABB2.js";
4
+ import AABB2 from "../../../../core/geom/2d/aabb/AABB2.js";
5
5
  import Signal from "../../../../core/events/signal/Signal.js";
6
6
  import ObservedValue from "../../../../core/model/ObservedValue.js";
7
- import { animation_curve_to_float_array } from "../compression/animation_curve_to_float_array.js";
7
+ import { sample_animation_curve_to_float_array } from "../compression/sample_animation_curve_to_float_array.js";
8
8
  import { plot_data } from "./plot_data.js";
9
9
  import { DraggableAspect } from "../../../ui/DraggableAspect.js";
10
10
  import { min2 } from "../../../../core/math/min2.js";
@@ -123,7 +123,7 @@ export function build_curve_editor({
123
123
  const width = graph.size.x;
124
124
  const data = new Float32Array(width);
125
125
 
126
- animation_curve_to_float_array(data, curve, 0, data.length);
126
+ sample_animation_curve_to_float_array(data, 0, curve, data.length);
127
127
 
128
128
  plot_data({
129
129
  data,
@@ -58,7 +58,7 @@ export class RingBufferMetric extends AbstractMetric {
58
58
  } else {
59
59
 
60
60
  result.mean = computeStatisticalMean(array, 0, data_count);
61
- result.median = computeStatisticalPartialMedian(array, 0, data_count);
61
+ result.median = computeStatisticalPartialMedian(array, 0, data_count - 1);
62
62
  result.max = computeArrayMax(array, 0, data_count);
63
63
  result.min = computeArrayMin(array, 0, data_count);
64
64