@grundyjs/algiviz 0.8.2 → 0.8.3

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/ru/README.md CHANGED
@@ -4,13 +4,6 @@
4
4
 
5
5
  **Ваши алгоритмы и объекты. Пошаговое воспроизведение и собственная отрисовка.**
6
6
 
7
- ![BFS из вершины A: собственные вершины графа, рёбра и очередь, анимированные с AlgiViz](https://raw.githubusercontent.com/urffin/algiviz/master/docs/assets/bfs.gif)
8
-
9
- *Алгоритм обхода графа в ширину с собственными объектами, реализованный в приложении.
10
- Экспортировано с [CanvasTake](https://github.com/urffin/canvas-take) —
11
- [попробуйте интерактивную сортировку](https://grundyjs.ru/algorithms/insertion-sort/)
12
- или изучите [исходный код BFS](https://github.com/urffin/algiviz/blob/master/examples/graph-demo.mjs).*
13
-
14
7
  TypeScript-библиотека для интерактивного объяснения алгоритмов. Напишите алгоритм
15
8
  как генератор, выдавайте неизменяемые состояния и определяйте внешний вид объектов.
16
9
  AlgiViz управляет воспроизведением и сопоставляет объекты сцен; операции над массивами
@@ -19,7 +12,7 @@ AlgiViz управляет воспроизведением и сопостав
19
12
  [Интерактивная сортировка](https://grundyjs.ru/algorithms/insertion-sort/) ·
20
13
  [Обзор библиотеки](https://grundyjs.ru/algiviz/) ·
21
14
  [Пакет npm](https://www.npmjs.com/package/@grundyjs/algiviz) ·
22
- [Исходный код минимального примера](https://github.com/urffin/algiviz/blob/master/examples/minimal.mjs)
15
+ [Исходный код минимального примера](https://github.com/urffin/algiviz/blob/master/examples/visualization-pipeline.html)
23
16
 
24
17
  - **Ваш алгоритм:** вы управляете циклами, условиями и порядком операций.
25
18
  - **Ваши объекты:** определяйте собственные типы и обработчики для Canvas, SVG или другой среды отрисовки.
@@ -30,7 +23,19 @@ AlgiViz управляет воспроизведением и сопостав
30
23
 
31
24
  Используйте библиотеку для учебных примеров, интерактивных статей и пошаговых разборов.
32
25
  Алгоритмы и расположение объектов остаются в приложении; AlgiViz предоставляет API
33
- воспроизведения и отрисовки. Этот README описывает версию 0.8.2.
26
+ воспроизведения и отрисовки.
27
+
28
+ ## Установка
29
+
30
+ ```sh
31
+ npm install @grundyjs/algiviz
32
+ ```
33
+
34
+ У пакета нет корневой точки входа и сборки CommonJS. Импортируйте нужные модули:
35
+ `/array`, `/playback`, `/scene` и `/canvas`.
36
+
37
+ Алгоритмы находятся в коде приложения и примерах. В версии 0.x минорные релизы могут нарушать обратную совместимость;
38
+ при обновлении сверяйтесь с [CHANGELOG.md](../CHANGELOG.md).
34
39
 
35
40
  ## Начните с одного собственного объекта
36
41
 
@@ -39,6 +44,10 @@ AlgiViz управляет воспроизведением и сопостав
39
44
  Добавьте Canvas и кнопку в HTML, а JavaScript ниже подключите как модуль через
40
45
  сборщик, разрешающий импорты установленного npm-пакета.
41
46
 
47
+ ![Минимальный пример AlgiViz: код, последовательная подсветка вызовов и состояние Canvas](https://raw.githubusercontent.com/urffin/algiviz/master/docs/assets/minimal.gif)
48
+
49
+ *Реальные вызовы примера показаны замедленно. Точка меняет позицию на этапе отрисовки. [Открыть интерактивный разбор](https://github.com/urffin/algiviz/blob/master/examples/visualization-pipeline.html).*
50
+
42
51
  ```html
43
52
  <canvas width="600" height="160"></canvas>
44
53
  <button id="next">Следующее состояние</button>
@@ -82,571 +91,64 @@ document.querySelector('#next').onclick = () => player.advance(1000);
82
91
 
83
92
  [Интерактивный разбор с последовательной подсветкой кода](https://github.com/urffin/algiviz/blob/master/examples/visualization-pipeline.html)
84
93
  показывает создание первого кадра по кнопке «Начать» и последующие итерации цикла.
85
- Это страница примера в репозитории; запускайте её локально по инструкции ниже.
86
-
87
- [Пошаговое руководство](https://github.com/urffin/algiviz/blob/master/docs/minimal-example.md)
88
- объясняет локальный запуск, владение снимками состояния и жизненный цикл.
89
- Более крупный пример с собственными объектами описан в
90
- [разборе BFS](https://github.com/urffin/algiviz/blob/master/docs/custom-graph-example.md).
91
-
92
- Цельный пример на TypeScript с двумя типами объектов и кнопками запуска, паузы
93
- и перезапуска: [custom-objects.ts](../examples/custom-objects.ts).
94
- [Инструкция запуска](../docs/minimal-example.md#a-complete-typescript-integration)
95
- объясняет типизацию, подключение и очистку при удалении визуализации.
96
-
97
- ## Установка
98
-
99
- ```sh
100
- npm install @grundyjs/algiviz
101
- ```
102
-
103
- У пакета нет корневой точки входа и сборки CommonJS. Импортируйте нужные модули:
104
- `/array`, `/playback`, `/scene` и `/canvas`.
105
-
106
- Алгоритмы находятся в коде приложения и примерах. Для новых интеграций используйте
107
- API ниже. В версии 0.x минорные релизы могут нарушать обратную совместимость;
108
- при обновлении сверяйтесь с [CHANGELOG.md](../CHANGELOG.md).
94
+ Это страница примера в репозитории; запускайте её по [инструкции локальной настройки](https://github.com/urffin/algiviz/blob/master/docs/minimal-example.md).
109
95
 
110
- ## Собственные алгоритмы
96
+ ## Как это устроено
111
97
 
112
- Новые приложения используют следующие точки входа, не импортирующие готовые алгоритмы:
98
+ - **Алгоритм** выдаёт независимые снимки состояния через генератор.
99
+ - **Плеер** запрашивает шаги и передаёт кадр вашей функции отрисовки.
100
+ - **Сцена** описывает объекты: стабильный `id`, тип и собственные данные.
101
+ - **Обработчик** выбирает геометрию и способ рисования. Плавность — его решение.
113
102
 
114
- - `@grundyjs/algiviz/array`: операции над массивами и определение генераторов.
115
- - `@grundyjs/algiviz/playback`: универсальное воспроизведение неизменяемых состояний и событий.
116
- - `@grundyjs/algiviz/scene`: типизированные объекты и пользовательские визуализации.
117
- - `@grundyjs/algiviz/canvas`: готовый рендерер массива.
118
-
119
- Циклы, условия и порядок операций определяете вы. Регистрация в библиотеке
120
- или изменение AlgiViz не требуются. Например, разворот массива:
121
-
122
- ```js
123
- import { defineArrayAlgorithm, createArrayPlayer, createArrayTimeline } from "@grundyjs/algiviz/array";
124
- import { createArrayRenderer } from "@grundyjs/algiviz/canvas";
125
-
126
- const reverse = defineArrayAlgorithm(function* (array) {
127
- yield array.start();
128
- for (let i = 0; i < Math.floor(array.length / 2); i++) {
129
- yield array.highlight([i, array.length - 1 - i]);
130
- yield array.swap(i, array.length - 1 - i);
131
- }
132
- yield array.done();
133
- });
103
+ Состояния после выдачи нельзя изменять. Плееру нужен явный завершающий шаг;
104
+ при удалении визуализации вызовите `dispose()`. Алгоритмы, раскладка и цикл
105
+ анимации принадлежат приложению.
134
106
 
135
- const options = { stepDurationMs: 350, finalHoldMs: 1000 };
136
- const ctx = canvas.getContext("2d");
137
- const renderer = createArrayRenderer({ theme: "dark" });
138
- const player = createArrayPlayer(reverse.iterate([1, 2, 3, 4]), {
139
- ...options,
140
- render: frame => renderer.render(ctx, frame)
141
- });
142
- // Your animation loop calls player.advance(deltaMs * speed).
143
- // No calls while paused; advance(0) redraws after resizing.
144
- // On teardown: cancel your animation loop and call player.dispose().
107
+ ## Режимы воспроизведения
145
108
 
146
- // Alternatively, retain history and seek:
147
- const timeline = createArrayTimeline(reverse.steps([1, 2, 3, 4]), options);
148
- renderer.render(ctx, timeline.sample(500));
149
- ```
109
+ | | Генератор: `createPlayer` | История: `createTimeline` |
110
+ | --- | --- | --- |
111
+ | Получение шагов | По мере продвижения | Все шаги заранее |
112
+ | Хранение | Соседние снимки | Вся история |
113
+ | Перемотка назад | Нет | Да, через `sample(timeMs)` |
114
+ | Общая длительность | Заранее неизвестна | Доступна |
150
115
 
151
- `defineArrayAlgorithm` возвращает `iterate(values)` и `steps(values)`. Каждый итератор
152
- владеет отдельным рабочим состоянием, копирует и проверяет входные данные при первом
153
- `next()`. `steps` собирает итератор в замороженный массив. Выдавайте результат каждой
154
- операции сразу через `yield`, начинайте с `start` и заканчивайте `done`. Операция
155
- обновляет рабочее состояние и возвращает неизменяемый снимок, но ничего не рисует.
156
- Плеер вызывает переданный `render` при создании и каждом `advance`.
157
- Ошибки отрисовки закрывают итератор и передаются вызывающему коду.
116
+ Генератор сокращает хранимую историю, но не стоимость создания каждого снимка.
117
+ [Подробнее о памяти](https://github.com/urffin/algiviz/blob/master/docs/ru/performance.md).
158
118
 
159
- ### Операции над массивом
119
+ ## Модули и документация
160
120
 
161
- | Операция | Действие |
121
+ | Модуль | Назначение |
162
122
  | --- | --- |
163
- | `at(index)`, `held`, `length` | Прочитать замороженный элемент, удерживаемый элемент или длину массива |
164
- | `start(regions?)` | Выдать начальное состояние; вызывается один раз |
165
- | `compare(left, right)` | Подсветить две позиции и учесть одно сравнение; любая позиция может быть `"held"` |
166
- | `highlight(positions)` | Подсветить элементы на этом шаге без изменения счётчиков |
167
- | `swap(left, right, regions?)` | Обменять занятые позиции и учесть две записи; поддерживаются несоседние позиции |
168
- | `select(from, regions?)` | Удержать элемент, оставив пустую позицию; записи не учитываются |
169
- | `shift(from, to, regions?)` | Переместить элемент в пустую позицию и учесть одну запись |
170
- | `insert(to, regions?)` | Заполнить пустую позицию удерживаемым элементом и учесть одну запись |
171
- | `pass(end, regions?)` | Описать завершённый проход без изменения элементов и счётчиков |
172
- | `done(regions?)` | Завершить работу; удерживаемого элемента быть не должно; не сортирует и не определяет отсортированные области |
173
-
174
- `regions` может содержать `sortedPrefixLength` и `sortedSuffixLength`.
175
- Пропущенные маркеры сохраняют предыдущие значения. Автор алгоритма решает, когда область
176
- отсортирована; AlgiViz проверяет границы, но не доказывает упорядоченность. Индексы
177
- начинаются с нуля. Неверные позиции, занятые места назначения и неправильный порядок
178
- операций вызывают исключение до изменения рабочего состояния. Читайте снимки, а не
179
- изменяйте элементы самостоятельно. `createArrayTrace(values)` предоставляет те же
180
- операции напрямую и сразу проверяет входные данные — для собственной оболочки генератора.
181
-
182
- Сортировки вставками и пузырьком реализованы в [examples/algorithms.mjs](../examples/algorithms.mjs).
183
- Алгоритмы принадлежат приложению и не требуют изменений библиотеки.
184
-
185
- ### Собственные структуры и рендереры
186
-
187
- `/playback` ничего не знает о массивах, сортировках, Canvas и названиях событий.
188
- Источник выдаёт объекты `Step<State, Event>` с числовым `index`,
189
- неизменяемыми `state` и `event`. Передайте `isTerminal`, чтобы определить последний
190
- шаг, и `render`, чтобы рисовать интерполированные кадры любым способом:
191
-
192
- Оба движка сохраняют исходный индекс: он не обязан начинаться с нуля или идти
193
- без пропусков. Время определяется порядком шагов. `createStepEmitter` для удобства
194
- создаёт последовательные индексы от нуля.
195
-
196
- ```js
197
- import { createPlayer, createTimeline } from "@grundyjs/algiviz/playback";
198
-
199
- const player = createPlayer(myTraversal(), {
200
- stepDurationMs: 300,
201
- finalHoldMs: 1000,
202
- isTerminal: step => step.event.kind === "finished",
203
- render: frame => drawGraph(frame.previous, frame.current, frame.progress, frame.event)
204
- });
205
- ```
206
-
207
- Функция `render` необязательна: без неё читайте `player.frame` и рисуйте самостоятельно.
208
- `createTimeline(steps, timing)` поддерживает перемотку с тем же универсальным форматом
209
- кадра; передавайте рендереру результат `sample(timeMs)`. Универсальный движок не
210
- копирует и не замораживает пользовательские состояния — это обязанность источника.
211
- Плеер хранит только соседние снимки, а временная шкала — переданную историю.
212
- Собственные события массива и операции с деревьями или графами могут использовать
213
- этот контракт и свой рендерер. API сцен ниже направляет отдельные объекты обработчикам;
214
- встроенного алгоритма или набора операций для графов и деревьев нет.
215
-
216
- ### Контракты воспроизведения
217
-
218
- При воспроизведении снимки приложения сохраняются без копирования: шаги, состояния
219
- и события должны оставаться неизменяемыми, пока используются. Временная шкала копирует
220
- только входной массив. Время определяется позицией в массиве, а конечные значения
221
- времени ограничиваются диапазоном шкалы. Генератор должен явно выдать завершающий
222
- шаг; раннее окончание вызывает исключение. `dispose()` закрывает источник не более
223
- одного раза, сохраняет доступ к последнему кадру и запрещает дальнейшее продвижение.
224
- Для перезапуска нужен новый источник.
225
-
226
- Начиная с 0.7.0 `stepIndex` временной шкалы сохраняет исходный `step.index`, как и плеер.
227
- Если чтение источника, проверка завершения или отрисовка выбрасывают исключение,
228
- выполняется попытка очистки без подмены исходной ошибки. Самостоятельная ошибка
229
- очистки при `dispose()` или штатном завершении по-прежнему передаётся вызывающему коду.
123
+ | [`/playback`](https://github.com/urffin/algiviz/blob/master/docs/ru/playback.md) | Шаги, плеер, история, завершение и жизненный цикл |
124
+ | [`/scene`](https://github.com/urffin/algiviz/blob/master/docs/ru/scene.md) | Собственные типы, сопоставление объектов и проекция |
125
+ | [`/array` и `/canvas`](https://github.com/urffin/algiviz/blob/master/docs/ru/array.md) | Операции массива, готовый рендерер и палитра |
230
126
 
231
- ### Вспомогательная функция выдачи шагов (с 0.6.0)
127
+ [Полный пример TypeScript](https://github.com/urffin/algiviz/blob/master/examples/custom-objects.ts) ·
128
+ [Настройка проекта и интеграция](https://github.com/urffin/algiviz/blob/master/docs/minimal-example.md) ·
129
+ [Разбор собственных объектов графа](https://github.com/urffin/algiviz/blob/master/docs/custom-graph-example.md)
232
130
 
233
- В версии 0.6.0 в `/playback` появилась `createStepEmitter`.
234
- Она автоматически нумерует шаги и замораживает объект-обёртку шага:
235
-
236
- ```js
237
- import { createStepEmitter } from '@grundyjs/algiviz/playback';
238
-
239
- function* counter() {
240
- let count = 0;
241
- const emit = createStepEmitter(() => Object.freeze({ count }));
242
- yield emit('start');
243
- count++;
244
- yield emit('done');
245
- }
246
- ```
247
-
248
- Создавайте emitter внутри каждого итератора. Его функция вызывается один раз на выдачу;
249
- индексы начинаются с нуля, а неудачный захват состояния не расходует индекс.
250
- Функция не копирует и не замораживает данные состояния и события, не определяет
251
- завершающие события и не хранит историю. Захват должен возвращать обособленные
252
- неизменяемые снимки; общие неизменяемые структуры можно переиспользовать.
253
- Возврат изменяемого рабочего объекта сам по себе не превращает его в снимок.
254
-
255
- ### Собственные объекты и визуализации
256
-
257
- Определите карту типов со своими данными и визуализацию для каждого типа.
258
- AlgiViz сопоставляет объекты по идентификаторам и вызывает обработчики;
259
- обработчики определяют геометрию и способ интерполяции данных.
260
-
261
- ```ts
262
- import { createSceneRenderer, type Scene } from "@grundyjs/algiviz/scene";
263
-
264
- interface MyObjects {
265
- badge: { x: number; y: number; label: string };
266
- }
267
-
268
- const renderer = createSceneRenderer<MyObjects, CanvasRenderingContext2D>({
269
- badge(ctx, object) {
270
- const from = (object.previous ?? object.current)!.data;
271
- const to = (object.current ?? object.previous)!.data;
272
- const x = from.x + (to.x - from.x) * object.progress;
273
- const y = from.y + (to.y - from.y) * object.progress;
274
- ctx.save();
275
- try {
276
- ctx.globalAlpha = object.presence;
277
- ctx.fillText(to.label, x, y);
278
- } finally { ctx.restore(); }
279
- }
280
- });
281
-
282
- const before: Scene<MyObjects> = {
283
- objects: [{ id: "first", type: "badge", data: { x: 20, y: 30, label: "A" } }]
284
- };
285
- const after: Scene<MyObjects> = {
286
- objects: [{ id: "first", type: "badge", data: { x: 120, y: 30, label: "A" } }]
287
- };
288
- // Keep these states immutable. A generic timeline/player can provide this frame.
289
- renderer.render(ctx, {
290
- previous: before, current: after, progress: 0.5, stepIndex: 1, event: "move"
291
- });
292
- ```
293
-
294
- - У объекта есть непустой стабильный `id`, `type`, пользовательские `data` и необязательный
295
- конечный `zIndex` (по умолчанию ноль). ID уникальны внутри снимка. Сохранившийся ID
296
- не может менять тип: для замены объектом другого типа нужен новый ID.
297
- - Обработчики получают объекты `previous` и `current`; отсутствующая сторона представлена
298
- `null`. `phase` принимает `enter`, `update` или `exit`. `progress` — исходный прогресс
299
- кадра от 0 до 1. `presence` равен progress при появлении, 1 при обновлении и 1−progress
300
- при исчезновении; его можно использовать для прозрачности или масштаба. Автоматически
301
- он не применяется.
302
- - Сначала рисуется меньший `zIndex`. При равенстве используется порядок объектов текущего
303
- снимка; удалённые объекты следуют в своём прежнем порядке. При изменении слоя действует
304
- текущее значение. Неявной раскладки и системы координат нет.
305
- - Третий аргумент обработчика предоставляет `scene.get(id)` для связей, указателей и других
306
- ссылок. Он возвращает полный переход объекта с обеими сторонами; для отсутствующего
307
- объекта — `undefined`. Приложение само обрабатывает такие случаи и интерполирует
308
- координаты или смену цели.
309
- - `createSceneFrame(frame)` возвращает те же вычисленные переходы без отрисовки. Реестр
310
- фиксируется при создании рендерера. Отсутствующие обработчики, повторные ID, неверный
311
- progress, некорректные слои и смена типа вызывают ошибку до вызова любого обработчика.
312
- - Обработка сцены не хранит состояния и поддерживает произвольную перемотку. Обработчик
313
- может получить объект с нулевым presence. Это отрисовка кадра, **а не гарантированные
314
- вызовы жизненного цикла**: при пропуске шагов появление или исчезновение может быть
315
- полностью пропущено. Очищайте Canvas в каждом кадре; SVG/DOM-рендерер с сохранением
316
- элементов должен сам сверять их с текущим кадром и удалять устаревшие ID. Сохраняйте
317
- и восстанавливайте состояние Canvas внутри каждого обработчика. Исключения передаются
318
- вызывающему коду; уже выполненная отрисовка не откатывается.
319
- - Снимки и пользовательские данные не копируются и не замораживаются рекурсивно.
320
- Их неизменяемость обеспечивает автор, как и при универсальном воспроизведении.
321
- Индексация сцены требует O(n) дополнительной памяти, сортировка по слоям — O(n log n)
322
- времени на кадр.
323
-
324
- `arrayScene(snapshot)` из `/canvas` преобразует массив в `Scene<ArrayObjects>`
325
- с объектами `bar`, в данных которых находятся `item`, `index` и `held`.
326
- Встроенный рендерер массива использует тот же механизм вызова обработчиков.
327
- Можно задать другую визуализацию `bar` через `createSceneRenderer<ArrayObjects, YourContext>`.
328
-
329
- ### Проекция состояния в сцену
330
-
331
- `createSceneProjector` преобразует кадры воспроизведения из вашей модели состояния
332
- в сцены без изменения алгоритма:
333
-
334
- ```js
335
- import { createSceneProjector } from '@grundyjs/algiviz/scene';
336
- const projectFrame = createSceneProjector(state => ({
337
- objects: state.items.map(item => ({ id: item.id, type: 'node', data: item }))
338
- }));
339
- // Reuse the projector across renders, with either history or generator playback.
340
- renderer.render(ctx, projectFrame(frame));
341
- ```
342
-
343
- Проекция получает только состояние, без события и прогресса. Используйте чистую функцию
344
- и неизменяемые состояния и сцены. Стороны перехода сравниваются через `Object.is`;
345
- изменение сохранённого состояния или захваченных настроек проекции не сбрасывает кэш.
346
- При смене настроек создавайте новый projector. Сохраняется не более двух проекций
347
- состояний; перемотка к старым состояниям может вычислять их повторно. События, индексы
348
- шагов и прогресс сохраняются. Раскладка под размер экрана и анимация относятся к
349
- обработчикам отрисовки, а не к этой кэшируемой проекции. Возвращаемый кадр заморожен;
350
- пользовательские состояния и сцены не копируются и не замораживаются рекурсивно.
351
- Проверка сцены по-прежнему выполняется в рендерере. Пример сортировки слиянием использует
352
- этот адаптер, чтобы не строить заново две сцены ячеек на каждом кадре анимации.
353
- Алгоритм, типы объектов и визуальное поведение остаются за пределами библиотеки.
354
-
355
- ## Воспроизведение и отрисовка
356
-
357
- Плееры и временные шкалы массивов используют тот же формат кадра, что и универсальное
358
- воспроизведение: `previous`, `current`, `progress`, `stepIndex` и `event`.
359
- Кадр описывает переход к текущему шагу; состояние шага соответствует состоянию
360
- после выполнения его операции.
361
-
362
- | API | Поведение |
363
- | --- | --- |
364
- | `createArrayPlayer(iterable, options)` | Воспроизведение массива; распознаёт событие `done` |
365
- | `createPlayer(iterable, options)` | Универсальное воспроизведение; требует `isTerminal(step)` |
366
- | `createArrayTimeline(steps, timing)` / `createTimeline(steps, timing)` | Сохранённая история с `durationMs` и `sample(timeMs)` |
367
- | `createArrayRenderer({ theme: "dark" \| "light" })` | Отрисовка полного кадра массива на Canvas |
368
- | `createSceneRenderer(handlers)` | Передаёт собственные объекты вашим обработчикам визуализации |
369
- | `createSceneProjector(project)` | Преобразует неизменяемые состояния перехода в сцены с ограниченным кэшем |
370
-
371
- Плеер читает первый шаг при создании и предоставляет `frame`, `advance(deltaMs)`,
372
- `finished` и `dispose()`. У начального кадра progress равен 1.
373
- `advance` принимает конечное неотрицательное прошедшее время воспроизведения;
374
- ноль не читает новые шаги. Для паузы не вызывайте продвижение. Для изменения скорости
375
- умножайте прошедшее время. Необязательный `render` вызывается при создании и каждом advance.
376
-
377
- Источник должен дойти до завершающего шага. Плеер закрывает итератор после последнего
378
- перехода и переходит в finished после `finalHoldMs`. Преждевременное окончание,
379
- ошибки источника и отрисовки передаются вызывающему коду и закрывают итератор.
380
- `dispose()` закрывает его досрочно и допускает повторные вызовы; кадр остаётся доступен,
381
- но продвижение освобождённого плеера вызывает исключение. Для перезапуска создайте
382
- новые итератор и плеер.
383
-
384
- Временной шкале нужен массив шагов. Шаг i завершается в `i * stepDurationMs`;
385
- `finalHoldMs` продлевает последний кадр. Выборка ограничивает конечное время диапазоном
386
- шкалы и отвергает неконечные значения. При воспроизведении генератора нет перемотки
387
- и заранее известной общей длительности.
388
-
389
- Планированием анимации управляет приложение. Например:
390
-
391
- ```js
392
- const player = createArrayPlayer(reverse.iterate([1, 2, 3, 4]), options);
393
- let last;
394
- let requestId;
395
- let paused = false;
396
- let speed = 1;
397
-
398
- function tick(now) {
399
- const delta = last === undefined ? 0 : Math.min(100, now - last);
400
- last = now;
401
- if (!paused) player.advance(delta * speed);
402
- renderer.render(ctx, player.frame);
403
- if (!player.finished) requestId = requestAnimationFrame(tick);
404
- }
405
- requestId = requestAnimationFrame(tick);
406
- // On teardown: cancelAnimationFrame(requestId); player.dispose();
407
- // To replay after finishing, create a new player and schedule a new loop.
408
- ```
409
-
410
- Это альтернатива настройке плеера из предыдущего примера с массивом; здесь используются
411
- его `reverse`, `options`, `ctx` и `renderer`. Большие интервалы времени синхронно читают
412
- промежуточные шаги, поэтому ограничивайте delta или приостанавливайте скрытые вкладки,
413
- чтобы избежать долгого наверстывания.
414
-
415
- Рендерер массива ожидает неизменяемые снимки. Для раскладки он использует размеры
416
- битовой карты Canvas, рисует полный кадр и восстанавливает состояние контекста.
417
- Высота столбца отрицательного значения определяется его модулем, а подпись сохраняет
418
- знак; это не график со знаковой осью. В плотных диаграммах подписи скрываются.
419
- Для записи используйте canvas с фиксированными размерами экспорта.
420
- Отрисовка не запускает цикл анимации и не записывает видео.
421
-
422
- #### Пользовательские цвета массива (с 0.8.0)
423
-
424
- Версия 0.8.0 принимает частичное переопределение палитры в `createArrayRenderer`
425
- и совместимом псевдониме `createSortRenderer`:
426
-
427
- ```ts
428
- import { createArrayRenderer, type ArrayPaletteOverrides } from '@grundyjs/algiviz/canvas';
429
- const colors: ArrayPaletteOverrides = { background: '#ffffff', text: '#182230', active: '#e87900' };
430
- const renderer = createArrayRenderer({ theme: 'light', colors });
431
- renderer.render(ctx, currentFrame);
432
- ```
433
-
434
- `ArrayPalette` задаёт шесть строковых ролей: `background` (весь canvas), `text`
435
- (все подписи), `bar` (обычные столбцы), `sorted` (отсортированный префикс/суффикс),
436
- `active` (подсвеченные, сравниваемые или перемещаемые элементы) и `held` (удерживаемый
437
- элемент; приоритет выше active и sorted). `ArrayPaletteOverrides` — частичная форма
438
- палитры, `ArrayRendererOptions` описывает объект настроек. Значения — строки цветов
439
- Canvas, передаваемые без CSS-проверки. Для пропущенных ролей и `undefined` используется
440
- выбранная тема. Вызовы только с темой и стандартные цвета не изменились.
441
-
442
- Переопределения копируются при создании рендерера. Последующее изменение входного
443
- объекта не влияет на результат. Для смены темы или палитры создайте новый рендерер
444
- и нарисуйте тот же текущий кадр. Меняются только цвета, но не воспроизведение,
445
- снимки, раскладка или обработка bitmap/DPR. Попробуйте темы, шесть полей выбора цвета
446
- и сброс в [лаборатории сортировок](../examples/index.html).
447
-
448
- ## Память и стоимость генерации
449
-
450
- Операции массива копируют входные данные, проверяют конечность чисел и отвергают
451
- разреженные массивы. `defineArrayAlgorithm` делает это при первом `next()` итератора,
452
- а `createArrayTrace` — сразу. Каждый итератор владеет своим рабочим состоянием.
453
- Шаги массива, события, снимки и элементы заморожены; неизменяемые элементы могут
454
- быть общими у нескольких снимков.
455
-
456
- Воспроизведение генератора сохраняет соседние снимки, а не всю историю. Поэтому для
457
- массива требуется O(n) памяти снимков, если вызывающий код не хранит старые кадры.
458
- Произвольные состояния и внутренние структуры алгоритма могут иметь другую стоимость.
459
- Каждый полный снимок массива всё равно копирует n позиций: ленивость уменьшает
460
- хранимую историю, а не стоимость каждого шага. Сбор всех шагов возвращает затраты на
461
- историю. Например, трасса квадратичной сортировки со снимками размера O(n) может
462
- потребовать O(n³) времени и памяти истории. Итерация синхронна и может блокировать UI.
463
-
464
- ## Примеры в браузере
465
-
466
- Соберите проект, раздайте корень репозитория статическим HTTP-сервером и откройте
467
- одну из страниц ниже. Примеры — код приложения в репозитории;
468
- в npm-архив они не входят.
131
+ ## Примеры
469
132
 
470
133
  | Пример | Что демонстрирует |
471
134
  | --- | --- |
472
135
  | [Как рождается кадр](https://github.com/urffin/algiviz/blob/master/examples/visualization-pipeline.html) | Путь от генератора через плеер и сцену к отрисовке, показанный средствами AlgiViz |
473
- | [Лаборатория сортировок](../examples/index.html) | Сортировки вставками и пузырьком с операциями массива и готовым рендерером |
474
- | [Сортировка слиянием](../examples/merge.html) | Собственное состояние и сцена из двух строк для основного массива и буфера |
475
- | [Разбиения быстрой сортировки](../examples/quick.html) | Разбиения Ломуто и трёхстороннее с собственными состоянием и сценой |
476
- | [Пирамидальная сортировка](../examples/heap.html) | Построение max-heap и извлечение с согласованными идентификаторами элементов дерева и массива |
477
- | [Бинарный поиск](../examples/binary-search.html) | Первое вхождение в отсортированном массиве, замкнутый интервал поиска и ленивое воспроизведение |
478
- | [BFS графа](../examples/graph.html) | Очередь, слои, кратчайшие пути, недостижимая цель и история/генератор |
479
- | [DFS графа](../examples/dfs.html) | Стек вызовов, возвраты, глубина и путь дерева; история/генератор |
480
- | [Обход дерева](../examples/tree.html) | Собственные узлы, рёбра и указатель с Canvas и SVG с сохранением элементов |
481
-
482
- Все примеры используют общие [demo-player.mjs](../examples/demo-player.mjs) и
483
- [player-settings.mjs](../examples/player-settings.mjs): 650 мс на шаг, удержание последнего
484
- кадра 1 000 мс, скорости от 0,25× до 32×, воспроизведение/пауза, перезапуск и переходы
485
- по шагам. История позволяет идти назад и перематывать; режим генератора отключает
486
- обе возможности. При reduced motion интерполяция отключается, скрытые вкладки
487
- приостанавливают воспроизведение.
488
-
489
- Все примеры сортировок принимают до **64 конечных значений с историей** или **2 000
490
- в режиме генератора**. Введите значения или создайте массив заданной длины в порядке
491
- Random, Sorted или Reverse. Это ограничения демонстраций, а не библиотеки. Для
492
- переключения большого массива на историю сначала загрузите массив допустимой длины.
493
- Плотные диаграммы скрывают подписи; примеры с собственными сценами сортировок
494
- показывают в текстовом статусе первые 24 ячейки.
495
-
496
- ### Сортировка слиянием
497
-
498
- [Алгоритм](../examples/merge-algorithm.mjs) и [рендерер](../examples/merge-scene.mjs)
499
- используют `/playback` и `/scene`. Равные значения получают буквенные суффиксы,
500
- чтобы показать устойчивость. Ячейки используют ID строки и позиции, поскольку
501
- копирование из буфера временно дублирует идентичности элементов. При равенстве
502
- выбирается левый элемент. Алгоритм использует O(n) дополнительной памяти;
503
- верхняя граница памяти полной истории снимков — O(n² log n).
504
-
505
- ### Быстрая сортировка
506
-
507
- Выбирайте разбиение Ломуто или трёхстороннее, сохраняя загруженные значения.
508
- Оба алгоритма и их сцена находятся в примере. Трёхсторонняя рекурсия пропускает
509
- область равных: для n ≥ 2 одинаковых элементов нужны 2n скалярных проверок и ноль
510
- обменов, а для этого варианта Ломуто — n(n−1)/2 проверок. Ни один вариант не
511
- гарантирует устойчивость. В худшем случае время квадратичное, рекурсия может
512
- потребовать O(n) памяти стека. Отдельная верхняя граница памяти полной истории — O(n³).
513
-
514
- ### Пирамидальная сортировка
515
-
516
- [Алгоритм](../examples/heap-algorithm.mjs) определяет неизменяемые состояния и события.
517
- [Сцена](../examples/heap-scene.mjs) задаёт каждому элементу отдельные визуальные ID в
518
- дереве и массиве и использует публичную проекцию сцен с состояниями, независимыми
519
- от размера экрана. Позиции дерева фиксированы при уменьшении кучи. До 15 ячеек
520
- массива переносятся на узких экранах; более крупные массивы используют плотную строку
521
- без нечитаемых подписей. Построение снизу вверх занимает O(n), сортировка в худшем
522
- случае — O(n log n), итеративный алгоритм на месте использует O(1) дополнительной
523
- памяти. Эта демонстрация копирует вход и хранит O(n) данных соседних снимков в режиме
524
- генератора; полная история может потребовать O(n² log n).
525
- Сортировка неустойчива: [2a, 2b, 1] превращается в [1, 2b, 2a].
526
-
527
- ### Бинарный поиск
528
-
529
- [Алгоритм](../examples/binary-search-algorithm.mjs) находит первое вхождение, в том числе
530
- при дубликатах. При равенстве сохраняется кандидат и поиск продолжается слева.
531
- Входной массив должен быть отсортирован; неверный ввод сохраняет текущий сеанс.
532
- Можно генерировать отсортированные массивы заданной длины с общими ограничениями
533
- режимов истории и генератора. Счётчик учитывает проверенные средние элементы.
534
-
535
- [Сцена](../examples/binary-search-scene.mjs) использует стабильные ID по индексам и
536
- проекцию, независимую от размера экрана. До 32 ячеек переносятся; более крупные
537
- массивы отображаются плотной полосой без нечитаемых подписей. Поиск занимает
538
- O(log n) времени и O(1) дополнительной памяти алгоритма. Демонстрация проверяет
539
- и копирует вход за O(n), разделяет замороженный массив между снимками и хранит
540
- O(n + log n) данных истории. Пример не входит в npm-пакет и не требует нового экспорта.
541
-
542
- ### Обход дерева
543
-
544
- Пример определяет собственные типы `node`, `edge` и `pointer`. Указатель проходит
545
- по рёбрам через общих предков между посещаемыми узлами. Выбор Renderer переключает
546
- Canvas и SVG без перезапуска воспроизведения. [tree-svg.mjs](../examples/tree-svg.mjs)
547
- сохраняет элементы по ID, восстанавливает порядок слоёв и удаляет устаревшие элементы,
548
- даже если перемотка пропустила переход исчезновения. Специальные алгоритм и рендерер
549
- дерева в пакет не добавляются.
550
-
551
- ### Обход графа в ширину
552
-
553
- Откройте [examples/graph.html](../examples/graph.html), выберите граф, старт и
554
- необязательную цель, затем нажмите Apply. BFS отмечает вершины при добавлении в
555
- очередь, полностью обходит достижимую компоненту и восстанавливает один кратчайший
556
- путь по предкам. Попробуйте изолированную H, путь до старта длиной 0, цепочку или
557
- ромб с равными путями. Исходный несвязный граф из GIF в README сохранён отдельным
558
- вариантом.
559
-
560
- Сцена показывает неподвижный граф, анимированную очередь, расстояния, рёбра предков
561
- и пути, отдельные счётчики обнаружения, обработки и просмотров соседей. На узком
562
- экране очередь переносится под граф. Canvas использует логические координаты и DPR.
563
- Поддержка петель и параллельных рёбер сохранена; порядок соседей может изменить
564
- выбранный кратчайший путь, но не его длину.
565
-
566
- Оба режима воспроизведения используют один генератор с неизменяемыми состояниями.
567
- Перезапуск и смена режима используют применённые граф/старт/цель, а не черновые поля.
568
- Генератор вычисляет шаги лениво, без предварительного сбора истории. Полная история
569
- позволяет перематывать назад и имеет отдельную верхнюю границу памяти снимков
570
- O(V(V + E)), помимо хранения графа и данных алгоритма.
571
-
572
- В [разборе собственного примера графа](../docs/custom-graph-example.md) описаны границы
573
- алгоритма, проекции и рендерера. Пример использует существующий публичный API
574
- и не добавляет специальные экспорты для графов.
575
-
576
- ### Обход графа в глубину
577
-
578
- Откройте [examples/dfs.html](../examples/dfs.html), чтобы сравнить DFS и BFS
579
- на одинаковых графах. Выберите граф, старт и необязательную цель. Каждый явный
580
- кадр вызова хранит курсор следующего соседа; вершина отмечается до входа.
581
- Возврат снимает завершённый вызов и возобновляет родителя. Стек содержит только
582
- активную ветку обхода.
583
-
584
- Порядок обнаружения по умолчанию — A, B, D, G, E, C, F; возвратов — F, C, E, G, D, B, A.
585
- Путь дерева DFS до F содержит 6 рёбер; BFS находит A → C → F длиной 2 ребра.
586
- Глубина дерева DFS не является кратчайшим расстоянием. H недостижима.
587
- Путь восстанавливается после полного обхода. Допустимы простые неориентированные
588
- графы; петли и параллельные рёбра отклоняются. Порядок соседей соответствует входу.
589
-
590
- На узком экране стек фиксированной вместимости переносится под граф.
591
- Canvas использует логические координаты и DPR. Оба режима используют один
592
- неизменяемый генератор и [общий плеер](../examples/demo-player.mjs); перезапуск
593
- повторяет применённые входные данные. Генератор вычисляет шаги лениво,
594
- история позволяет перематывать назад. Обход занимает O(V + E) времени и O(V)
595
- дополнительной памяти; верхняя граница памяти истории снимков отдельно составляет
596
- O(V(V + E)). Новые API графа или встроенный DFS в пакет не добавляются.
597
-
598
- ## Разработка
599
-
600
- Требуются Node.js 22 или новее и npm.
601
-
602
- ```sh
603
- npm ci
604
- npm test
605
- npm run test:package
606
- ```
607
-
608
- `npm test` собирает библиотеку, проверяет TypeScript-примеры и запускает тесты.
609
- `npm run test:package` собирает архив, устанавливает его без сети в изолированное
610
- временное приложение, компилирует код с установленными декларациями и проверяет
611
- публичный API, включая отказ для удалённого экспорта `/core`.
612
- Затем временное приложение удаляется.
613
-
614
- GitHub Actions запускает обе проверки при push в `master` и в pull request,
615
- используя Node.js 22 и 24 на Linux и Windows. Проверки CI не публикуют пакеты.
616
-
617
- `npm pack` автоматически пересобирает пакет. Архив включает dist, README, LICENSE,
618
- CHANGELOG и метаданные пакета. Исходный код и задачи:
619
- [urffin/algiviz](https://github.com/urffin/algiviz). Лицензия MIT.
620
-
621
- ### Проверки производительности
622
-
623
- Запустите `npm run benchmark` для массивов из 2 000 элементов: сортировки вставками,
624
- пузырьком, слиянием и оба варианта быстрой сортировки. Отчёт показывает генерацию
625
- 1 000 шагов, медиану CPU-времени отрисовки по пяти выборкам из 100 кадров и занятую
626
- кучу Node после каждых 5 000 шагов до 25 000. Canvas заменён заглушкой: это сравнение
627
- CPU, а не измерение FPS браузера или GPU. Замеры кучи используют явную сборку мусора
628
- и включают процесс Node; это диагностика, а не универсальные пороги прохождения теста.
629
-
630
- ## Выпуск версии
631
-
632
- Публикация выполняется сопровождающим проекта. Обновите версию пакета, lockfile
633
- и changelog; запустите `npm test` и `npm run test:package`, сделайте коммит,
634
- отправьте его и проверьте CI для этого коммита.
136
+ | [Лаборатория сортировок](https://github.com/urffin/algiviz/blob/master/examples/index.html) | Сортировки вставками и пузырьком с операциями массива и готовым рендерером |
137
+ | [Сортировка слиянием](https://github.com/urffin/algiviz/blob/master/examples/merge.html) | Собственное состояние и сцена из двух строк для основного массива и буфера |
138
+ | [Разбиения быстрой сортировки](https://github.com/urffin/algiviz/blob/master/examples/quick.html) | Разбиения Ломуто и трёхстороннее с собственными состоянием и сценой |
139
+ | [Пирамидальная сортировка](https://github.com/urffin/algiviz/blob/master/examples/heap.html) | Построение max-heap и извлечение с согласованными идентификаторами элементов дерева и массива |
140
+ | [Бинарный поиск](https://github.com/urffin/algiviz/blob/master/examples/binary-search.html) | Первое вхождение в отсортированном массиве, замкнутый интервал поиска и ленивое воспроизведение |
141
+ | [BFS графа](https://github.com/urffin/algiviz/blob/master/examples/graph.html) | Очередь, слои, кратчайшие пути, недостижимая цель и история/генератор |
142
+ | [DFS графа](https://github.com/urffin/algiviz/blob/master/examples/dfs.html) | Стек вызовов, возвраты, глубина и путь дерева; история/генератор |
143
+ | [Обход дерева](https://github.com/urffin/algiviz/blob/master/examples/tree.html) | Собственные узлы, рёбра и указатель с Canvas и SVG с сохранением элементов |
635
144
 
636
- [Workflow публикации](../.github/workflows/npm-publish.yml) запускается при публикации
637
- стабильного GitHub Release. Черновики, предварительные релизы и отправка одного
638
- лишь тега пакет не публикуют. Используйте `v` и точную версию пакета, например
639
- `v0.8.0`, на проверенном коммите.
145
+ [Полный каталог, локальный запуск и особенности алгоритмов](https://github.com/urffin/algiviz/blob/master/docs/ru/examples.md).
146
+ Примеры находятся в репозитории и не входят в npm-пакет.
640
147
 
641
- Workflow использует Node.js 24 и npm 11, проверяет версию и приложение с упакованным
642
- пакетом, затем публикует с provenance через npm trusted publishing (OIDC).
643
- Жизненный цикл публикации также запускает тесты и пересборку.
644
- Секрет `NPM_TOKEN` не требуется.
148
+ ## Разработка и лицензия
645
149
 
646
- Trusted publisher в npm должен соответствовать владельцу GitHub `urffin`,
647
- репозиторию `algiviz` и workflow `npm-publish.yml`. GitHub environment не используется.
648
- См. [npm trusted publishing](https://docs.npmjs.com/trusted-publishers/).
150
+ [Участие в разработке](https://github.com/urffin/algiviz/blob/master/ru/CONTRIBUTING.md) ·
151
+ [История изменений](https://github.com/urffin/algiviz/blob/master/CHANGELOG.md) ·
152
+ [Сообщить об ошибке](https://github.com/urffin/algiviz/issues)
649
153
 
650
- После публикации проверьте запуск Actions и точную версию в npm.
651
- Никогда не используйте повторно уже опубликованную версию npm. Если workflow завершился
652
- ошибкой, проверьте, успел ли пакет опубликоваться, прежде чем запускать его повторно.
154
+ Лицензия [MIT](https://github.com/urffin/algiviz/blob/master/LICENSE). В версии 0.x минорные релизы могут менять API.