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