diff --git a/lib/progress.ts b/lib/progress.ts index 061a2ca7874ff8f0be9f38d1224ee9e5f014945f..f43bae9900a26396d4f888dd659733032f8bad0b 100644 --- a/lib/progress.ts +++ b/lib/progress.ts @@ -453,7 +453,7 @@ function newNode( estimator = estimateCompletion; } else estimator = new Ema(); } - estimator?.sample(Date.now(), 0); + estimator?.sample(Date.now(), 0, 0); function mutate(key: keyof ReadOnlyNode) { if (state.detached) return; @@ -464,7 +464,7 @@ function newNode( // originally, auto-estimation would use the Root's timing primitives, // but this makes it so that `state.estimatedTime` is in terms of the // Root, which is unintuitive for everyone but the renderer. - const time = estimator.sample(Date.now(), state.value / state.total); + const time = estimator.sample(Date.now(), state.value, state.total); if (time && (!state.estimatedTime || state.estimatedTime !== time)) { state.estimatedTime = time; owner.emit("node-change", state, "estimatedTime"); @@ -1671,9 +1671,10 @@ class KeyPool { export interface EstimationAlgorithm { /** * given a timestamp `time` and a progress value `progress`, return the - * timestamp that the action will be finished on. + * timestamp that the action will be finished on. called with (t, 0, 0) + * to initialize the algorithm. */ - sample(time: number, progress: number): number | null; + sample(time: number, current: number, total: number): number | null; } /** @@ -1685,33 +1686,29 @@ export class Ema implements EstimationAlgorithm { start: number | null = null; estimate: number | null = null; /** smoothing factor (0-1) */ - alpha: number = 0.25; + alpha: number; - constructor(alpha: number = 0.1) { + constructor(alpha: number = 0.25) { this.alpha = alpha; } - /** - * given a timestamp `time` and a progress value `progress`, return the - * timestamp that the action will be finished on. - */ - sample(time: number, progress: number): number | null { - ASSERT( - progress >= 0 && progress < 1, - `progress must be a number between 0 and 1, got ${progress}`, - ); - if (this.start == null) { + sample(time: number, current: number, total: number): number | null { + ASSERT(current >= 0 && current <= total); + if (this.start == null || current === 0) { this.start = time; + this.estimate = null; return null; } - const value = (time - this.start) / progress; + const elapsed = time - this.start; + if (elapsed === 0) return null; // too quick + + const rate = current / elapsed; this.estimate = this.estimate != null - ? ((1 - this.alpha) * this.estimate + - this.alpha * value) - : value; - - return time + (1 - progress) * this.estimate; + ? (1 - this.alpha) * this.estimate + this.alpha * rate + : rate; + const remaining = total - current; + return time + remaining / this.estimate; } reset(): void {