Add patterngen-oss: pattern animations, blender pipeline, deterministic generation

This commit is contained in:
Neo committed 2026-06-20 17:30:26 +05:00
1 parent 2b42a76d6d
commit c7f0be809b
4 files changed
+726 -3

No files matched your search

+283
View File
@@ -0,0 +1,283 @@
# 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