@grundyjs/algiviz 0.8.3 → 0.9.0
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/CHANGELOG.md +9 -0
- package/README.md +2 -2
- package/dist/playback/player.d.ts +4 -1
- package/dist/playback/player.js +32 -0
- package/package.json +1 -1
- package/ru/README.md +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.0 � 2026-10-27
|
|
4
|
+
|
|
5
|
+
- Add player.nextStep() for exact manual stepping in generic and array players.
|
|
6
|
+
- Finish an in-progress transition without reading another snapshot; at a boundary,
|
|
7
|
+
consume one next snapshot. Complete terminal holds without extra source reads.
|
|
8
|
+
- Preserve rendering, disposal and error handling contracts; avoid rounding errors
|
|
9
|
+
when mixing timed advancement with manual steps.
|
|
10
|
+
- Update the minimal example, English/Russian READMEs and highlighted GIF to use nextStep().
|
|
11
|
+
|
|
3
12
|
## 0.8.3 — 2026-10-27
|
|
4
13
|
|
|
5
14
|
- Streamline English and Russian READMEs around installation and a minimal example.
|
package/README.md
CHANGED
|
@@ -81,11 +81,11 @@ const player = createPlayer(steps(), {
|
|
|
81
81
|
renderer.render(ctx, frame);
|
|
82
82
|
},
|
|
83
83
|
});
|
|
84
|
-
document.querySelector('#next').onclick = () => player.
|
|
84
|
+
document.querySelector('#next').onclick = () => player.nextStep();
|
|
85
85
|
```
|
|
86
86
|
|
|
87
87
|
The first frame (`x = 40`) is drawn immediately inside `createPlayer(...)`. Each
|
|
88
|
-
click
|
|
88
|
+
click requests the next state: the player resumes the loop until the next
|
|
89
89
|
`yield`, then renders. There is no interpolation here: the handler draws only
|
|
90
90
|
`current`. Each snapshot is created afresh and never mutated. Call
|
|
91
91
|
`player.dispose()` when removing the visualization.
|
|
@@ -5,6 +5,9 @@ export interface Player<S, E> {
|
|
|
5
5
|
readonly finished: boolean;
|
|
6
6
|
/** Advance by playback time; pass zero to redraw without consuming steps. */
|
|
7
7
|
advance(deltaMs: number): Frame<S, E>;
|
|
8
|
+
/** Finish the current transition, or consume one next step at a boundary.
|
|
9
|
+
* At the terminal boundary, finish the hold. Renders once, even if finished. */
|
|
10
|
+
nextStep(): Frame<S, E>;
|
|
8
11
|
/** Close the source early. The last frame remains readable. */
|
|
9
12
|
dispose(): void;
|
|
10
13
|
}
|
|
@@ -13,6 +16,6 @@ export declare function createPlayer<S, E>(steps: Iterable<Step<S, E>>, options:
|
|
|
13
16
|
stepDurationMs: number;
|
|
14
17
|
finalHoldMs: number;
|
|
15
18
|
isTerminal: (step: Step<S, E>) => boolean;
|
|
16
|
-
/** Called initially and on each advance, including
|
|
19
|
+
/** Called initially and on each advance or nextStep, including after completion. */
|
|
17
20
|
render?: (frame: Frame<S, E>) => void;
|
|
18
21
|
}): Player<S, E>;
|
package/dist/playback/player.js
CHANGED
|
@@ -59,6 +59,38 @@ export function createPlayer(steps, options) {
|
|
|
59
59
|
return Object.freeze({
|
|
60
60
|
get frame() { return frame(); },
|
|
61
61
|
get finished() { return finished; },
|
|
62
|
+
nextStep() {
|
|
63
|
+
if (disposed)
|
|
64
|
+
throw new Error("Player has been disposed");
|
|
65
|
+
try {
|
|
66
|
+
if (!finished) {
|
|
67
|
+
if (elapsed === stepDurationMs) {
|
|
68
|
+
if (options.isTerminal(current)) {
|
|
69
|
+
hold = finalHoldMs;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
const next = iterator.next();
|
|
73
|
+
if (next.done)
|
|
74
|
+
throw new RangeError("Step source ended without a terminal step");
|
|
75
|
+
previous = current;
|
|
76
|
+
current = next.value;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Assign the endpoint directly: time arithmetic can undershoot or
|
|
80
|
+
// spill a rounding remainder into an unwanted additional step.
|
|
81
|
+
elapsed = stepDurationMs;
|
|
82
|
+
complete();
|
|
83
|
+
}
|
|
84
|
+
const result = frame();
|
|
85
|
+
options.render?.(result);
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
disposed = true;
|
|
90
|
+
closeAfterFailure();
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
},
|
|
62
94
|
advance(deltaMs) {
|
|
63
95
|
if (disposed)
|
|
64
96
|
throw new Error("Player has been disposed");
|
package/package.json
CHANGED
package/ru/README.md
CHANGED
|
@@ -80,11 +80,11 @@ const player = createPlayer(steps(), {
|
|
|
80
80
|
renderer.render(ctx, frame);
|
|
81
81
|
},
|
|
82
82
|
});
|
|
83
|
-
document.querySelector('#next').onclick = () => player.
|
|
83
|
+
document.querySelector('#next').onclick = () => player.nextStep();
|
|
84
84
|
```
|
|
85
85
|
|
|
86
86
|
Первый кадр (`x = 40`) рисуется сразу внутри `createPlayer(...)`. Каждый клик
|
|
87
|
-
|
|
87
|
+
запрашивает следующее состояние: плеер возобновляет цикл до следующего
|
|
88
88
|
`yield`, затем вызывает отрисовку. Здесь нет плавного перехода: обработчик рисует
|
|
89
89
|
только `current`. Каждый снимок создаётся заново и больше не изменяется. При
|
|
90
90
|
удалении визуализации вызовите `player.dispose()`.
|