# 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(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(); export function svgToImage(svgText: string): Promise { 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 ``` __.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