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

7.8 KiB
Raw Permalink Blame History

Deterministic Generation — Seed-Based Layout Methodology

Детерминированная генерация паттернов с использованием seeded PRNG. Основано на patterngen-oss.

Обзор

Каждая генерация паттернов детерминирована — одинаковый seed всегда даёт идентичный layout. Это позволяет:

  • Повторно воспроизводить удачные комбинации
  • Синхронизировать дизайн между Figma и PatternGen
  • Кэшировать результаты генерации
  • Делиться сценами через JSON с seed

Seeded PRNG

Алгоритм

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

{
  "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

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).

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 цикл.

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

const FIXED_COLORS = ['#000000', '#FFFFFF'];

Черный и белый всегда доступны.

Custom Colors

До 3 кастомных цветов добавляются через color picker. Color pairs для паттернов генерируются алгоритмически:

// Для каждого pair custom_color:
bg = customColor;
fg = generateComplementary(customColor);

SVG Colorization

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

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

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