Files
wow-design-wiki/system/deterministic-generation.md
T

284 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Deterministic Generation — Seed-Based Layout Methodology
> Детерминированная генерация паттернов с использованием seeded PRNG.
> Основано на [patterngen-oss](https://github.com/halfof8/patterngen-oss).
## Обзор
Каждая генерация паттернов **детерминирована** — одинаковый seed всегда даёт
идентичный layout. Это позволяет:
- Повторно воспроизводить удачные комбинации
- Синхронизировать дизайн между Figma и PatternGen
- Кэшировать результаты генерации
- Делиться сценами через JSON с seed
---
## Seeded PRNG
### Алгоритм
```typescript
class SeededRNG {
private seed: number;
constructor(seed: number) {
this.seed = seed & 0x7FFFFFFF; // 31-bit positive
}
next(): number {
// LCG: Linear Congruential Generator
this.seed = (this.seed * 1103515245 + 12345) & 0x7FFFFFFF;
return this.seed / 0x7FFFFFFF; // range [0, 1)
}
nextInt(min: number, max: number): number {
return Math.floor(this.next() * (max - min + 1)) + min;
}
pick<T>(array: T[]): T {
return array[this.nextInt(0, array.length - 1)];
}
}
```
### Seed из Figma
```json
{
"seed": 123456789
}
```
31-bit integer генерируется плагином Figma. Значение зашито в JSON
и передается в PatternGen.
---
## Placement Engine
### Input parameters
| Параметр | Тип | Диапазон | Описание |
|----------|-----|----------|----------|
| `seed` | int | 0–2³¹-1 | Seed для PRNG |
| `density` | int | 1–20 | Количество элементов |
| `proximity` | int | 1–20 | Мин. расстояние до title (20 px grid units) |
### Placement algorithm
```
1. Создать RNG из seed
2. Для каждого элемента:
a. Сгенерировать случайную позицию (x, y)
b. Проверить proximity к существующим элементам и title
c. Если too close → пересадить с тем же RNG state
d. Иначе → записать позицию
3. Для каждого элемента:
a. Выбрать случайный pattern из доступных
b. Выбрать случайную clipSide (top/bottom/left/right)
c. Выбрать случайную анимацию
d. Сгенерировать stagger delay
```
### Stagger calculation
```typescript
const staggerWindow = Math.max(0, Math.min(0.85, (stagger / 5) * 0.8));
const staggeredStart = element.animDelay * staggerWindow * durationMs;
const elemDur = durationMs * (1 - staggerWindow);
const localMs = cycleMs - staggeredStart;
const p = Math.max(0, Math.min(1, localMs / elemDur));
```
---
## Animation Timeline
### Фаза 1: Title Reveal (0 — durationMs)
Title проявляется из центра по горизонтали (если wider) или вертикали (if taller).
```typescript
const titleT = min(1, max(0, elapsedMs / durationMs));
const titleEased = applyEasing(titleT, easing);
if (title.w >= title.h) {
// wide title: half-reveal horizontally
halfReveal = (title.w / 2) * titleEased;
revealBox = {
x: cx - halfReveal,
y: title.y,
w: halfReveal * 2,
h: title.h
};
} else {
// tall title: half-reveal vertically
halfReveal = (title.h / 2) * titleEased;
revealBox = {
x: title.x,
y: cy - halfReveal,
w: title.w,
h: halfReveal * 2
};
}
```
### Фаза 2: Pattern Reveal (durationMs — 2× durationMs)
Паттерны проявляются с stagger-задержкой.
### Фаза 3: Dots Loop (2× durationMs — 4× durationMs)
Точки пульсируют синусоидой 4000ms цикл.
```typescript
const fadeIn = min(1, loopMs / 800);
const wave = 0.5 + 0.5 * sin(2 * PI * dot.blinkSpeed * (loopMs / 1000) + dot.blinkPhase * 2 * PI);
const pulse = 0.15 + 0.85 * wave;
opacity = pulse * fadeIn;
```
---
## Color System
### Fixed Colors
```typescript
const FIXED_COLORS = ['#000000', '#FFFFFF'];
```
Черный и белый всегда доступны.
### Custom Colors
До 3 кастомных цветов добавляются через color picker.
Color pairs для паттернов генерируются алгоритмически:
```typescript
// Для каждого pair custom_color:
bg = customColor;
fg = generateComplementary(customColor);
```
### SVG Colorization
```typescript
function colorizeSvg(svgText: string, colors: ColorPair): string {
return svgText
.replace(/fill="white"/g, `fill="${colors.bg}"`)
.replace(/fill="black"/g, `fill="${colors.fg}"`)
.replace(/stroke="black"/g, `stroke="${colors.fg}"`);
}
```
SVG-паттерны хранят только `white` (фон) и `black` (foreground).
Окрашивание происходит в runtime.
---
## Image Caching
```typescript
const imageCache = new Map<string, HTMLImageElement>();
export function svgToImage(svgText: string): Promise<HTMLImageElement> {
if (imageCache.has(svgText)) return Promise.resolve(imageCache.get(svgText)!);
return new Promise((resolve, reject) => {
const img = new Image();
const blob = new Blob([svgText], { type: 'image/svg+xml;charset=utf-8' });
const url = URL.createObjectURL(blob);
img.onload = () => {
URL.revokeObjectURL(url);
imageCache.set(svgText, img);
resolve(img);
};
img.onerror = () => {
URL.revokeObjectURL(url);
reject(new Error('Failed to load SVG image'));
};
img.src = url;
});
}
export function clearImageCache() {
imageCache.clear();
}
```
---
## Export Pipeline
### Layer structure
```
titles: durationMs (1×) — plays once
patterns: durationMs (1×) — plays once
dots: 4000ms loop — infinite loop
```
### Export scale
- Canvas: 1920×1080
- Export Scale: 4× → 7680×4320
- FPS: 24
- Format: PNG (RGBA, transparent background)
### Frame calculation
```typescript
const totalFrames = Math.ceil((layerDuration / 1000) * FPS);
const frameRatio = totalFrames > 1 ? frame / (totalFrames - 1) : 1;
```
### Naming convention
```
<sceneName>_<layer>_<frame>.png
Пример:
MyTitle_titles_00000.png
MyTitle_titles_00001.png
MyTitle_patterns_00000.png
MyTitle_dots_00000.png
```
---
## Использование determinism в дизайне
### Scenario 1: Figma → PatternGen sync
1. Экспортировать сцену из Figma (содержит seed)
2. Загрузить в PatternGen
3. Паттерны генерируются точно так же каждый раз
4. Можно итерировать параметры и снова генерировать тот же layout
### Scenario 2: A/B testing
1. Создать базовую сцену с seed
2. Экспортировать 10 раз с разными параметрами density/stagger
3. Каждый раз layout идентичен, но timing разный
4. Сравнить конверсию/визуальное впечатление
### Scenario 3: Team collaboration
1. Дизайнер делится scene JSON
2. Коллега загружает и видит ту же генерацию
3. Может менять цвета, паттерны, параметры
4. Seed остаётся — layout predictable
---
## Референсы кода
- `src/core/rng.ts` — SeededRNG класс, LCG algorithm
- `src/core/placement.ts` — Placement engine, proximity check
- `src/engine/timeline.ts` — Phase 1/2/3, dots loop
- `src/export/exporter.ts` — PNG export, frame calculation
- `src/export/renderer.ts` — Canvas rendering per frame
- `src/types.ts` — AnimationState, PatternAnimType