ジェネレータ契約
@zpd/patterns は自己完結したパッケージです。PanelPatternGenerator の契約、手作業で列挙した組み込みのレジストリ、そしてサムネイルレンダラーからなります。このページでは、すべてのパターン — 組み込みでも新規でも — が実装しなければならない契約を解説します。
PanelPatternGenerator
interface PatternParamDef {
key: string;
label: string;
min: number;
max: number;
step: number;
defaultValue: number;
}
interface DrawOptions {
widthMm: number; // blank panel dimensions in millimetres
heightMm: number;
color: string; // a single palette hex the caller chose for this pattern
params: Record<string, number>; // keyed by PatternParamDef.key
}
interface PanelPatternGenerator {
name: string; // stable kebab id, e.g. 'dot-grid'
displayName: string; // human-facing label
paramDefs: PatternParamDef[];
draw(ctx: CanvasRenderingContext2D, opts: DrawOptions): void;
}スケール済み・クリップ済みの mm 空間で描く
draw(ctx, opts) は、呼び出し側 がすでにキャンバスの 1 単位 = 1mm となるようスケールし、さらにパネル矩形 (0,0)〜(widthMm, heightMm) にクリップした CanvasRenderingContext2D とともに呼ばれます。ジェネレータはパネルの mm 座標で直接描画します — ctx.arc(x, y, radius, ...) の x / y / radius はすべて mm で、draw() の内側での単位変換は一切ありません。
ここには、より大きなキャンバス / スライス / ビューポートといった間接化は意図的に ありません。パターンは、与えられたパネルの内側だけを、そのパネル自身の widthMm / heightMm を使って計算します。これは、レンダラーの mm 空間レイヤーパスがすべてのレイヤーに対して用意するのと同じ変換であり、サムネイルレンダラーがピッカーのプレビュー用に用意する同じ 30mm のスケール済みウィンドウでもあります。パターンの draw() は、2 つのコンテキストのどちらが呼び出しているかを知る必要も気にする必要もありません。
決定論的 — どこにもランダム性はない
描画は決定論的です。同一の入力は同一のピクセルを再現しなければならず(どこにもランダム性はない)、これによりエクスポートされた注文 JSON は忠実に再生できます。
これはスタイルの好みではなく、厳格な制約です。エクスポートされる注文 JSON は、ラスタライズされた画像ではなく、パターンレイヤーの patternType と params を保存します。もしジェネレータが Math.random() を呼んでいたら、同じ JSON を後で再生したとき — あるいは同じドキュメントを別のマシンで再レンダリングしたとき — 見た目の異なるパネルが生まれてしまいます。すべての組み込みは、見かけ上の有機的なばらつき(波の位相、光線の角度、格子のタイリング)を、ランダムシードではなく、パネル自身の寸法と宣言されたパラメータをもとにした純粋な三角関数で実現しています。
中央揃えでオーバースキャンする格子
ほとんどの組み込みは、繰り返し単位(ドット、六角セル、レンガ)をパネル全体にタイリングします。patterns.ts の共有ヘルパー centeredStart(span, pitch) は、タイリングがパネルに対して 中央揃え になる(1 つの目盛りが必ずちょうど span / 2 に来る)ように反復を開始する最小の格子座標を計算し、さらに両方向で端の外まで オーバースキャン します(ループ境界 span + pitch と組み合わせて)。中央揃えとオーバースキャンによって、どんなパネル幅でも、あらゆるパターンが意図してデザインされたように見え、半端な HP サイズでも見切れたり中心からずれたりしないのです。
パラメータのクランプ
すべての draw() は、パラメータを params[key] で直接ではなく、resolveParam(params, defs, key) を通して読み取ります。
function resolveParam(params, defs, key) {
const raw = params[key];
const finiteRaw = typeof raw === 'number' && Number.isFinite(raw);
const def = defs.find((d) => d.key === key);
if (!def) return finiteRaw ? raw : Number.NaN; // no def to clamp against
const value = finiteRaw ? raw : def.defaultValue;
return Math.min(def.max, Math.max(def.min, value));
}resolveParam は、入ってくる値が欠けているか非有限のときは def の defaultValue にフォールバックし、その後つねに [min, max] にクランプします。このクランプは見た目のためではありません。正のピッチ/カウントを保証するもので、呼び出し側が古い値・ゼロ・負の値を渡しても、すべての描画ループが有限のままであることを保ちます(ゼロや負のピッチは、さもなければ for ループを永遠に回してしまいます)。
レジストリ
patterns.ts の PATTERN_GENERATORS は、生成されるものではなく 手作業で列挙した配列 です。エディタのツール/インスペクタにあるようなプラグイン/自動検出の仕組みはここにはありません。新しい組み込みパターンを追加するには、この配列に直接加えます。
export const PATTERN_GENERATORS: PanelPatternGenerator[] = [
dotGrid, diagStripes, gridLines, concentricCircles, hexLattice, checker,
waveLines, crosshatch, radialBurst, brick, diamondLattice, scallops,
];Warning
'dot-grid' はこの配列の先頭に留め、その name を正確に保たなければなりません。コアのデフォルトドキュメント(新しいエディタセッションが開始時に読み込むデモドキュメント)が、これを直接参照しています。
レジストリの上には、2 つの小さなヘルパーがあります。
patternByName(name)—nameでジェネレータを探し、なければundefined。defaultParams(name)— 指定した名前のジェネレータが宣言するすべてのパラメータについて{ [key]: defaultValue }を返します(名前が解決しなければ空オブジェクト)。追加したばかりのパターンレイヤーや、すべてのサムネイル描画は、これをもとにparamsを初期化します。
すべての登録済みパターンの完全なパラメータ表については、組み込みカタログを参照してください。