@woosh/meep-engine 2.39.5 → 2.39.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 (50) hide show
  1. package/core/binary/BinaryBuffer.js +1 -0
  2. package/core/geom/3d/triangle/computeTriangleRayIntersection.js +39 -0
  3. package/core/math/FLT_EPSILON_32.js +7 -0
  4. package/core/math/random/seededRandom_Mulberry32.js +1 -1
  5. package/core/model/stat/LinearModifier.js +7 -0
  6. package/core/process/task/RemainingTimeEstimator.js +49 -0
  7. package/core/process/task/Task.js +3 -1
  8. package/engine/Engine.js +0 -83
  9. package/engine/asset/AssetManager.d.ts +3 -0
  10. package/engine/asset/AssetManager.js +5 -4
  11. package/engine/ecs/terrain/ecs/Terrain.js +33 -0
  12. package/engine/ecs/terrain/ecs/layers/TerrainLayer.js +13 -0
  13. package/engine/ecs/terrain/ecs/layers/TerrainLayers.js +44 -2
  14. package/engine/ecs/terrain/ecs/splat/SplatMapping.js +12 -4
  15. package/engine/graphics/ecs/path/testPathDisplaySystem.js +58 -53
  16. package/engine/graphics/ecs/path/tube/PathNormalType.d.ts +4 -0
  17. package/engine/graphics/ecs/path/tube/PathNormalType.js +11 -0
  18. package/engine/graphics/ecs/path/tube/TubePathStyle.d.ts +4 -0
  19. package/engine/graphics/ecs/path/tube/TubePathStyle.js +21 -3
  20. package/engine/graphics/ecs/path/tube/build/build_geometry_catmullrom.js +4 -1
  21. package/engine/graphics/ecs/path/tube/build/build_geometry_linear.js +4 -1
  22. package/engine/graphics/ecs/path/tube/build/computeFrenetFrames.js +41 -24
  23. package/engine/graphics/geometry/VertexDataSpec.js +24 -0
  24. package/engine/graphics/geometry/buffered/query/RaycastNearestHitComputingVisitor.js +25 -5
  25. package/engine/graphics/shaders/TerrainShader.js +3 -7
  26. package/engine/knowledge/database/StaticKnowledgeDataTable.js +66 -28
  27. package/engine/navigation/ecs/components/Path.js +58 -232
  28. package/engine/notify/Notification.js +3 -0
  29. package/engine/scene/transitionToScene.js +2 -1
  30. package/engine/ui/notification/AnimatedObjectEmitter.js +3 -2
  31. package/engine/ui/notification/ViewEmitter.js +8 -0
  32. package/engine/ui/scene/initializeNotifications.js +3 -0
  33. package/generation/{GridGenerator.js → GridTaskGroup.js} +21 -5
  34. package/generation/example/SampleGenerator0.js +2 -2
  35. package/generation/example/main.js +2 -1
  36. package/generation/filtering/core/CellFilterUnaryOperation.js +5 -8
  37. package/generation/filtering/numeric/util/CellFilterDisplaced.js +7 -7
  38. package/generation/grid/generation/GridTaskSequence.js +5 -3
  39. package/generation/markers/actions/MarkerNodeActionImaginary.js +86 -0
  40. package/generation/markers/actions/terrain/MarkerNodeActionPaintTerrain.js +265 -0
  41. package/generation/theme/TerrainLayerDescription.js +11 -0
  42. package/generation/theme/ThemeEngine.js +5 -6
  43. package/package.json +1 -1
  44. package/samples/terrain/from_image.js +2 -1
  45. package/view/common/ListView.js +7 -1
  46. package/view/elements/windrose/WindRoseDiagram.js +13 -0
  47. package/view/task/TaskLoadingScreen.js +172 -0
  48. package/view/{common → task}/TaskProgressView.js +4 -36
  49. package/view/tooltip/gml/GMLEngine.js +13 -0
  50. package/generation/markers/actions/MarkerNodeActionSelectRandom.js +0 -65
@@ -0,0 +1,86 @@
1
+ import { MarkerNodeAction } from "./MarkerNodeAction.js";
2
+ import { MarkerNodeConsumerBuffer } from "../emitter/MarkerNodeConsumerBuffer.js";
3
+ import Vector3 from "../../../core/geom/Vector3.js";
4
+ import { assert } from "../../../core/assert.js";
5
+
6
+ /**
7
+ * Spawns imaginary markers that are not added to the grid, and executes further actions on these
8
+ * Useful when desired additional markers are not present in the grid
9
+ */
10
+ export class MarkerNodeActionImaginary extends MarkerNodeAction {
11
+ /**
12
+ * @type {MarkerNodeEmitter}
13
+ */
14
+ emitter;
15
+
16
+ /**
17
+ * @type {MarkerNodeAction}
18
+ */
19
+ action;
20
+
21
+ /**
22
+ * Pre-multiplies emitted marker transform by the transform of the consumed node
23
+ * @type {boolean}
24
+ * @private
25
+ */
26
+ propagate_scale = true;
27
+
28
+ _buffer = new MarkerNodeConsumerBuffer();
29
+
30
+ /**
31
+ *
32
+ * @param {MarkerNodeEmitter} emitter
33
+ * @param {MarkerNodeAction} action
34
+ * @param {boolean} [propagate_scale]
35
+ * @returns {MarkerNodeActionImaginary}
36
+ */
37
+ static from({
38
+ emitter,
39
+ action,
40
+ propagate_scale = true
41
+ }) {
42
+
43
+ assert.defined(emitter, 'emitter');
44
+ assert.equal(emitter.isMarkerNodeEmitter, true, 'emitter.isMarkerNodeEmitter !== true');
45
+
46
+ assert.defined(action, 'emitter');
47
+ assert.equal(action.isMarkerNodeAction, true, 'action.isMarkerNodeAction !== true');
48
+
49
+ assert.isBoolean(propagate_scale, 'propagate_scale');
50
+
51
+ const r = new MarkerNodeActionImaginary();
52
+
53
+ r.emitter = emitter;
54
+ r.action = action;
55
+ r.propagate_scale = propagate_scale;
56
+
57
+ return r;
58
+ }
59
+
60
+ execute(grid, ecd, node) {
61
+ this._buffer.reset();
62
+
63
+ const rotation = node.transform.rotation.computeTwistAngle(Vector3.down);
64
+
65
+ this.emitter.execute(grid, node.position.x, node.position.y, rotation, this._buffer);
66
+
67
+ const n = this._buffer.size();
68
+
69
+ for (let i = 0; i < n; i++) {
70
+ const imaginary_node = this._buffer.get(i);
71
+
72
+ if (this.propagate_scale) {
73
+ imaginary_node.transform.scale.multiply(node.transform.scale);
74
+
75
+ imaginary_node.size *= node.size;
76
+ }
77
+
78
+ this.action.execute(grid, ecd, imaginary_node);
79
+ }
80
+ }
81
+
82
+ initialize(grid, ecd, seed) {
83
+ this.emitter.initialize(grid, seed);
84
+ this.action.initialize(grid, ecd, seed);
85
+ }
86
+ }
@@ -0,0 +1,265 @@
1
+ import { MarkerNodeAction } from "../MarkerNodeAction.js";
2
+ import { CellFilterLiteralFloat } from "../../../filtering/numeric/CellFilterLiteralFloat.js";
3
+ import LinearModifier from "../../../../core/model/stat/LinearModifier.js";
4
+ import Terrain from "../../../../engine/ecs/terrain/ecs/Terrain.js";
5
+ import { seededRandom } from "../../../../core/math/random/seededRandom.js";
6
+ import { v2_distance } from "../../../../core/geom/Vector2.js";
7
+ import TransitionFunctions from "../../../../engine/animation/TransitionFunctions.js";
8
+ import { inverseLerp } from "../../../../core/math/inverseLerp.js";
9
+ import { clamp01 } from "../../../../core/math/clamp01.js";
10
+ import { clamp } from "../../../../core/math/clamp.js";
11
+ import { assert } from "../../../../core/assert.js";
12
+ import { TerrainLayer } from "../../../../engine/ecs/terrain/ecs/layers/TerrainLayer.js";
13
+ import { max2 } from "../../../../core/math/max2.js";
14
+
15
+ export class MarkerNodeActionPaintTerrain extends MarkerNodeAction {
16
+ /**
17
+ *
18
+ * @CellFilter
19
+ */
20
+ weight = CellFilterLiteralFloat.ONE;
21
+
22
+ /**
23
+ * What to paint
24
+ * @type {TerrainLayerDescription}
25
+ */
26
+ layer = null;
27
+
28
+ /**
29
+ * Actual layer index, will be taken from the terrain upon initialization
30
+ * @type {number}
31
+ * @private
32
+ */
33
+ _layer_index = -1;
34
+
35
+ /**
36
+ *
37
+ * @type {SplatMapping}
38
+ * @private
39
+ */
40
+ _splat_map = null;
41
+
42
+ /**
43
+ *
44
+ * @type {Terrain}
45
+ * @private
46
+ */
47
+ _terrain = null;
48
+
49
+ /**
50
+ * How far does the area extend from the marker's shape, has both % and constant component
51
+ * Can grow or shrink the area
52
+ * @type {LinearModifier}
53
+ */
54
+ dilation = LinearModifier.CONSTANT_ZERO;
55
+
56
+ /**
57
+ * Function to apply smoothing to the area that falls into "edge"
58
+ * @type {function(number): number}
59
+ */
60
+ weight_edge_easing_function = TransitionFunctions.EaseInOut;
61
+
62
+ /**
63
+ * How wide is the edge, computed from the effective region including padding, must produce a non-negative number
64
+ * @type {LinearModifier}
65
+ */
66
+ weight_edge_easing = LinearModifier.CONSTANT_ZERO;
67
+
68
+ /**
69
+ *
70
+ * @type {function():number}
71
+ * @private
72
+ */
73
+ _random;
74
+
75
+ /**
76
+ *
77
+ * @param {LinearModifier} dilation
78
+ * @param {function(number):number} border_blending
79
+ * @param {LinearModifier} border
80
+ * @param {CellFilter} weight
81
+ * @param {TerrainLayerDescription} layer
82
+ * @returns {MarkerNodeActionPaintTerrain}
83
+ */
84
+ static from({
85
+ dilation = LinearModifier.CONSTANT_ZERO,
86
+ border_blending = TransitionFunctions.EaseInOut,
87
+ border = LinearModifier.CONSTANT_ZERO,
88
+ weight = CellFilterLiteralFloat.ONE,
89
+ layer
90
+ }) {
91
+
92
+ assert.defined(layer, 'layer');
93
+ assert.notNull(layer, 'layer');
94
+ assert.equal(layer.isTerrainLayerDescription, true, 'layer.isTerrainLayerDescription !== true');
95
+
96
+ const r = new MarkerNodeActionPaintTerrain();
97
+
98
+
99
+ r.dilation = dilation;
100
+ r.weight_edge_easing = border;
101
+ r.weight_edge_easing_function = border_blending;
102
+ r.weight = weight;
103
+ r.layer = layer;
104
+
105
+ return r;
106
+ }
107
+
108
+ /**
109
+ *
110
+ * @param {number} x
111
+ * @param {number} y
112
+ * @param {MarkerNode} node
113
+ * @param {GridData} grid
114
+ * @param {number} radius_outer
115
+ * @param {number} radius_inner
116
+ * @returns {number}
117
+ * @private
118
+ */
119
+ _getPointWeight(x, y, node, grid, radius_outer, radius_inner) {
120
+ // compute distance from node center
121
+ const distance_from_center = v2_distance(node.position.x, node.position.y, x, y);
122
+
123
+ if (distance_from_center > radius_outer) {
124
+ // too far away
125
+ return 0;
126
+ }
127
+
128
+ const weight_sampled = this.weight.execute(grid, x, y, 0);
129
+
130
+
131
+ // compute position in the padding region, where 0 is at the outer edge of the padding, and 1 is at the
132
+ const edge_normalized_position = clamp01(inverseLerp(radius_inner, radius_outer, distance_from_center));
133
+
134
+ // apply edge smoothing
135
+ const edge_weight_bias = this.weight_edge_easing_function(1 - edge_normalized_position);
136
+
137
+ return weight_sampled * edge_weight_bias;
138
+ }
139
+
140
+ _getLayerIndex() {
141
+ if (this._layer_index === -1) {
142
+ // bind terrain layer
143
+ const layers = this._terrain.layers;
144
+ const layer_descriptor = this.layer;
145
+
146
+ this._layer_index = layers.findIndexByDiffuseTextureURL(layer_descriptor.diffuse);
147
+
148
+
149
+ if (this._layer_index !== -1) {
150
+ // check to see if size matches
151
+ const actual_layer = layers.get(this._layer_index);
152
+ if (!actual_layer.size.equals(layer_descriptor.size)) {
153
+ console.warn(`Actual terrain layer with texture '${layer_descriptor.diffuse}' has size ${actual_layer.size}, instead of desired ${layer_descriptor.size}. Ignoring this difference, layer will still be used`);
154
+ }
155
+ } else {
156
+
157
+ // layer not found, let's create it
158
+ this._layer_index = this._terrain.addLayer(TerrainLayer.fromDescription(layer_descriptor));
159
+ }
160
+
161
+ }
162
+
163
+ return this._layer_index;
164
+ }
165
+
166
+ execute(grid, ecd, node) {
167
+ const _splat_layer_index = this._getLayerIndex();
168
+
169
+ const splat = this._splat_map;
170
+
171
+ const splat_resolution_x = splat.size.x;
172
+ const splat_resolution_y = splat.size.y;
173
+
174
+ // compute scale between grid and splat
175
+ const splat_scale_x = splat_resolution_x / grid.width;
176
+ const splat_scale_y = splat_resolution_y / grid.height;
177
+
178
+ const splat_to_grid_x = 1 / splat_scale_x;
179
+ const splat_to_grid_y = 1 / splat_scale_y;
180
+
181
+ const radius_outer = node.size * (1 + this.dilation.a) + this.dilation.b;
182
+
183
+ const edge_width = node.size * this.weight_edge_easing.a + this.weight_edge_easing.b;
184
+ const radius_inner = radius_outer - edge_width;
185
+
186
+ // we shift to the center of the grid cell
187
+ const node_p_x = node.position.x + splat_to_grid_x;
188
+ const node_p_y = node.position.y + splat_to_grid_y;
189
+
190
+ // compute affected region in Splat space
191
+ const x0 = Math.floor((node_p_x - radius_outer) * splat_scale_x + 0.5);
192
+ const x1 = Math.floor((node_p_x + radius_outer) * splat_scale_x + 0.5);
193
+ const y0 = Math.ceil((node_p_y - radius_outer) * splat_scale_y + 0.5);
194
+ const y1 = Math.ceil((node_p_y + radius_outer) * splat_scale_y + 0.5);
195
+
196
+ const layer_count = splat.depth;
197
+
198
+ const splat_data = splat.weightData;
199
+
200
+ // execute blending
201
+ for (let y = y0; y <= y1; y++) {
202
+ for (let x = x0; x <= x1; x++) {
203
+
204
+ // convert position into grid coordinates
205
+ const grid_x = (x - 0.5) * splat_to_grid_x - splat_to_grid_x;
206
+ const grid_y = (y - 0.5) * splat_to_grid_y - splat_to_grid_y;
207
+
208
+ const weight = this._getPointWeight(grid_x, grid_y, node, grid, radius_outer, radius_inner);
209
+
210
+ if (weight === 0) {
211
+ continue;
212
+ }
213
+
214
+ const weight_scaled = weight * 255;
215
+
216
+ // paint
217
+ const weight_reciprocal_multiplier = max2(0, 1 - weight);
218
+
219
+ for (let layer_index = 0; layer_index < layer_count; layer_index++) {
220
+
221
+ const layer_address = splat_resolution_x * splat_resolution_y * layer_index;
222
+ const texel_address = layer_address + y * splat_resolution_x + x;
223
+
224
+ const source_value = splat_data[texel_address];
225
+
226
+ let destination_value;
227
+
228
+ if (_splat_layer_index === layer_index) {
229
+ // the channel that we are stamping
230
+ destination_value = source_value + weight_scaled;
231
+ } else {
232
+ // other layers get erased by proportional amount
233
+ destination_value = source_value * weight_reciprocal_multiplier;
234
+ }
235
+
236
+ splat_data[texel_address] = clamp(destination_value, 0, 255);
237
+
238
+ }
239
+ }
240
+ }
241
+
242
+ // mark splat map for update
243
+ splat.weightTexture.needsUpdate = true;
244
+ }
245
+
246
+ initialize(grid, ecd, seed) {
247
+ // find layer index
248
+ const terrain = ecd.getAnyComponent(Terrain);
249
+
250
+ if (terrain.component === null) {
251
+ throw new Error('No terrain present');
252
+ }
253
+
254
+ this._terrain = terrain.component;
255
+
256
+ // bind splats
257
+ this._splat_map = terrain.component.splat;
258
+
259
+ this._layer_index = -1;
260
+
261
+ this._random = seededRandom(seed);
262
+
263
+ this.weight.ensureInitialized(grid, seed);
264
+ }
265
+ }
@@ -34,6 +34,17 @@ export class TerrainLayerDescription {
34
34
  && this.size.equals(other.size);
35
35
  }
36
36
 
37
+ toJSON() {
38
+ return {
39
+ diffuse: this.diffuse,
40
+ size: this.size.toJSON()
41
+ };
42
+ }
43
+
44
+ toString() {
45
+ return JSON.stringify(this.toJSON());
46
+ }
47
+
37
48
  static from({
38
49
  diffuse,
39
50
  size
@@ -244,11 +244,7 @@ export class ThemeEngine {
244
244
  });
245
245
 
246
246
  layer_descriptors.forEach(l => {
247
- terrain.layers.addLayer(TerrainLayer.from(
248
- l.diffuse,
249
- l.size.x,
250
- l.size.y
251
- ));
247
+ terrain.layers.addLayer(TerrainLayer.fromDescription(l));
252
248
  });
253
249
 
254
250
  terrain.clearFlag(TerrainFlags.Built);
@@ -587,10 +583,13 @@ export class ThemeEngine {
587
583
  const tCells = this.applyCellRules(grid, ecd);
588
584
 
589
585
  tTerrain.addDependency(tInitializeThemes);
586
+
590
587
  tNodes.addDependencies([
591
588
  tInitializeThemes,
592
- tTerrainGeometry
589
+ tTerrainGeometry,
590
+ tTerrain
593
591
  ]);
592
+
594
593
  tCells.addDependency(tInitializeThemes);
595
594
  tTerrainGeometry.addDependency(tCells);
596
595
 
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "productName": "Meep",
6
6
  "description": "production-ready JavaScript game engine based on Entity Component System Architecture",
7
7
  "author": "Alexander Goldring",
8
- "version": "2.39.5",
8
+ "version": "2.39.9",
9
9
  "dependencies": {
10
10
  "gl-matrix": "3.4.3",
11
11
  "fast-levenshtein": "2.0.6",
@@ -33,6 +33,7 @@ import {
33
33
  } from "../../engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js";
34
34
  import { CellFilterAngleToNormal } from "../../generation/filtering/numeric/complex/CellFilterAngleToNormal.js";
35
35
  import { CellFilterCache } from "../../generation/filtering/numeric/CellFilterCache.js";
36
+ import { TaskLoadingScreen } from "../../view/task/TaskLoadingScreen.js";
36
37
 
37
38
  const HEIGHT_RANGE = 100;
38
39
 
@@ -267,7 +268,7 @@ async function main(engine) {
267
268
 
268
269
  const task = theme_engine.apply(grid, ecd);
269
270
 
270
- const promise_load = engine.loadSlowTask(task);
271
+ const promise_load = TaskLoadingScreen.load(engine, task);
271
272
 
272
273
  engine.executor.runGroup(task);
273
274
 
@@ -8,6 +8,7 @@ import View from "../View.js";
8
8
  import { assert } from "../../core/assert.js";
9
9
  import { noop, returnTrue } from "../../core/function/Functions.js";
10
10
  import { Cache } from "../../core/cache/Cache.js";
11
+ import List from "../../core/collection/list/List.js";
11
12
 
12
13
  /**
13
14
  * @template T
@@ -52,7 +53,7 @@ export class ListView extends View {
52
53
  /**
53
54
  * List representation
54
55
  * @template E
55
- * @param {List<E>} model List to be represented
56
+ * @param {List<E>|E[]} model List to be represented
56
57
  * @param {string[]} [classList] collection of CSS classes
57
58
  * @param {function(E):View} elementFactory factory function, takes a list element and returns a view
58
59
  * @param {function(E, View, number, ListView<E>)} [addHook] hook function to be called when a view is created
@@ -90,6 +91,11 @@ export class ListView extends View {
90
91
 
91
92
  assert.typeOf(elementFactory, 'function', 'elementFactory');
92
93
 
94
+ if (Array.isArray(model)) {
95
+ // wrap array as a list
96
+ model = new List(model);
97
+ }
98
+
93
99
  /**
94
100
  *
95
101
  * @type {List<E>}
@@ -0,0 +1,13 @@
1
+ import View from "../../View.js";
2
+
3
+ export class WindRoseDiagram extends View {
4
+ constructor() {
5
+ super();
6
+
7
+ this.el = document.createElement('div');
8
+
9
+ this.addClass('ui-wind-rose-diagram-view');
10
+
11
+
12
+ }
13
+ }
@@ -0,0 +1,172 @@
1
+ import TaskProgressView from "./TaskProgressView.js";
2
+ import { AssetLoaderStatusView } from "../asset/AssetLoaderStatusView.js";
3
+ import { logger } from "../../engine/logging/GlobalLogger.js";
4
+ import { array_remove_first } from "../../core/collection/array/array_remove_first.js";
5
+ import { assert } from "../../core/assert.js";
6
+
7
+ /**
8
+ * @type {TaskLoadingScreen}
9
+ */
10
+ let _instance;
11
+
12
+ export class TaskLoadingScreen {
13
+ _active_promise = Promise.resolve();
14
+ /**
15
+ *
16
+ * @type {number}
17
+ * @private
18
+ */
19
+ _release_delay_seconds = 0.5;
20
+
21
+ /**
22
+ *
23
+ * @type {number}
24
+ * @private
25
+ */
26
+ _release_delay_handle = -1;
27
+
28
+ _pending_cleanup = [];
29
+ _state_stack = [];
30
+
31
+ _clearReleaseTimeout() {
32
+ if (this._release_delay_handle !== -1) {
33
+ clearTimeout(this._release_delay_handle);
34
+ this._release_delay_handle = -1;
35
+ }
36
+ }
37
+
38
+ /**
39
+ *
40
+ * @param {Engine} engine
41
+ * @param {Task|TaskGroup} task
42
+ * @returns {Promise<void>}
43
+ */
44
+ async load(engine, task) {
45
+
46
+ assert.defined(task, 'task');
47
+ assert.notNull(task, 'task');
48
+
49
+ assert.defined(engine, 'engine');
50
+ assert.notNull(engine, 'engine');
51
+
52
+
53
+ await Promise.allSettled([this._active_promise]);
54
+
55
+ this._clearReleaseTimeout();
56
+
57
+ const localization = engine.localization;
58
+
59
+ const taskProgressView = new TaskProgressView({ task, localization });
60
+ taskProgressView.el.classList.add('loading-screen');
61
+
62
+ //add asset manager loading progress
63
+ const loaderStatusView = new AssetLoaderStatusView({ assetManager: engine.assetManager, localization });
64
+ taskProgressView.addChild(loaderStatusView);
65
+ taskProgressView.link();
66
+
67
+ const graphics = engine.graphics;
68
+
69
+ const engine_state = {
70
+ engine_rendering_enabled: engine.renderingEnabled,
71
+ graphics_auto_draw: graphics.autoDraw
72
+ };
73
+
74
+ this._state_stack.push(engine_state);
75
+
76
+ const domParent = document.body;
77
+
78
+ domParent.appendChild(taskProgressView.el);
79
+
80
+ // suspend rendering
81
+ engine.renderingEnabled = false;
82
+ graphics.autoDraw = false;
83
+
84
+ const attempt_cleanup = () => {
85
+ if (this._active_promise !== promise) {
86
+ return;
87
+ }
88
+
89
+ // clear any pending timeouts if they exist
90
+ this._clearReleaseTimeout();
91
+
92
+ // run all remaining pending cleanup tasks
93
+ for (let i = 0; i < this._pending_cleanup.length; i++) {
94
+ const t = this._pending_cleanup[i];
95
+
96
+ if (t === cleanup) {
97
+ // skip self
98
+ continue;
99
+ }
100
+
101
+ try {
102
+ t();
103
+ } catch (e) {
104
+ logger.error(e);
105
+ }
106
+
107
+ // just to make sure, try to remove it again
108
+ array_remove_first(this._pending_cleanup, t);
109
+
110
+ // update iterator
111
+ i--;
112
+ }
113
+
114
+ // this is the last task in the queue
115
+ this._release_delay_handle = setTimeout(() => {
116
+ cleanup();
117
+ }, Math.ceil(this._release_delay_seconds * 1000));
118
+ }
119
+
120
+ const cleanup = () => {
121
+ array_remove_first(this._pending_cleanup, cleanup);
122
+
123
+ domParent.removeChild(taskProgressView.el);
124
+
125
+ taskProgressView.unlink();
126
+
127
+ // restore state
128
+ const state = this._state_stack.pop();
129
+
130
+ engine.renderingEnabled = state.engine_rendering_enabled;
131
+ engine.graphics.autoDraw = state.graphics_auto_draw;
132
+ };
133
+
134
+ this._pending_cleanup.push(cleanup);
135
+
136
+ const promise = new Promise((resolve, reject) => {
137
+
138
+ const p = task.promise();
139
+
140
+ p.finally(attempt_cleanup);
141
+ p.then(resolve, reject);
142
+
143
+ });
144
+
145
+ this._active_promise = promise;
146
+
147
+ return promise;
148
+ }
149
+
150
+ /**
151
+ *
152
+ * @param {Engine} engine
153
+ * @param {Task|TaskGroup} task
154
+ * @returns {Promise<void>}
155
+ */
156
+ static load(engine, task) {
157
+ return TaskLoadingScreen.instance.load(engine, task);
158
+ }
159
+
160
+ /**
161
+ *
162
+ * @returns {TaskLoadingScreen}
163
+ */
164
+ static get instance() {
165
+ if (_instance === undefined) {
166
+ _instance = new TaskLoadingScreen();
167
+ }
168
+
169
+ return _instance;
170
+ }
171
+ }
172
+
@@ -5,47 +5,15 @@
5
5
 
6
6
  import View from '../View.js';
7
7
  import SmoothProgressBar from '../elements/progress/SmoothProgressBar.js';
8
- import LabelView from './LabelView.js';
8
+ import LabelView from '../common/LabelView.js';
9
9
 
10
10
  import TaskState from '../../core/process/task/TaskState.js';
11
11
 
12
12
  import dom from '../DOM.js';
13
13
  import Clock from "../../engine/Clock.js";
14
14
  import Vector1 from "../../core/geom/Vector1.js";
15
- import { LocalizedLabelView } from "./LocalizedLabelView.js";
16
-
17
- function ReimaingTimeEstimator(sampleCount) {
18
- this.sampleCount = sampleCount;
19
- this.rates = new Float32Array(sampleCount);
20
- this.cursor = 0;
21
- }
22
-
23
- ReimaingTimeEstimator.prototype.update = function (timeElapsed, progress) {
24
- const index = this.cursor;
25
- this.cursor = (this.cursor + 1) % this.sampleCount;
26
-
27
- const rate = progress / timeElapsed;
28
-
29
- this.rates[index] = rate;
30
- };
31
-
32
- ReimaingTimeEstimator.prototype.estimate = function (currentProgress) {
33
- let rateSum = 0;
34
-
35
- for (let i = 0; i < this.sampleCount; i++) {
36
- rateSum += this.rates[i];
37
- }
38
-
39
- const rate = rateSum / this.sampleCount;
40
-
41
- if (rate === 0) {
42
- return Infinity;
43
- }
44
-
45
- const result = (1 - currentProgress) / rate;
46
-
47
- return result;
48
- };
15
+ import { LocalizedLabelView } from "../common/LocalizedLabelView.js";
16
+ import { RemainingTimeEstimator } from "../../core/process/task/RemainingTimeEstimator.js";
49
17
 
50
18
  class TaskProgressView extends View {
51
19
  /**
@@ -131,7 +99,7 @@ class TaskProgressView extends View {
131
99
  state.on.linked.add(updateState);
132
100
  state.bindSignal(task.state.onChanged, updateState);
133
101
 
134
- const remainingTimeEstimator = new ReimaingTimeEstimator(100);
102
+ const remainingTimeEstimator = new RemainingTimeEstimator(100);
135
103
  const remainingTime = new Vector1(0);
136
104
 
137
105
  const lRemainingTime = new LabelView(remainingTime, {