ドキュメントの状態とレイヤー
DocState、Layer のユニオン型、mm 座標空間、そしてドキュメントが PanelConfig へシリアライズされる仕組み。
座標空間: ミリメートル、原点は左上
ドキュメントモデル内のすべての幾何学的な値——レイヤーの x/y、width/height、パスのアンカーとベジェハンドル、ストローク幅——は、パネルの左上の角を原点として、ミリメートル単位で保存されます。これは意図的な設計です。PCB の製造データは mm ベースであるため、@zpd/core 全体を通じて mm を唯一の保存空間としています。ピクセルはレンダリングの境界にのみ存在し、そこでアプリのカメラが表示のために mm を画面上の px へ変換します。ドキュメントモデル自体には、ピクセルベースのものは一切ありません。
DocState
interface DocState {
panelHp: number;
layers: PcbLayerStack; // Copper -> Solder mask -> Silkscreen
guides: Guide[]; // ルーラーガイド — レイヤーではなく表示用の付帯物
}panelHp— HP で表したパネルの幅(パネルサイズを参照)。layers— 物理的な下から上の順に並ぶ固定スタックです。各ルートは通常のレイヤー/グループ子を所有します。type PcbLayerRole = 'copper' | 'solder-mask' | 'silkscreen'; interface PcbLayerContainer { kind: 'pcb-layer'; id: `pcb-layer-${PcbLayerRole}`; role: PcbLayerRole; children: LayerNode[]; hidden?: boolean; } type PcbLayerStack = [ PcbLayerContainer<'copper'>, PcbLayerContainer<'solder-mask'>, PcbLayerContainer<'silkscreen'>, ];これらのルートは選択・変更できる通常グループではありません。
LayerNodeは leafLayerまたはネストした通常のGroupNodeで、名前変更、削除、 グループ化、移動ができるのは通常ノードだけです。guides— ドキュメントのルーラーガイド。必須の配列です(ガイドを参照)。
Layer ユニオン型
レイヤーは 5 つのバリアントのいずれかであり、type によって判別されます。すべてのバリアントは LayerBase を共有します。
interface LayerBase {
id: string;
name: string;
hidden?: boolean;
}| Type | Manufacturable? | 概要 |
|---|---|---|
shape | Yes | 長方形または楕円。所有コンテナのマテリアルで塗りつぶされます。 |
pattern | Yes | @zpd/patterns の名前付き・パラメータ化されたパターン(core からは不透明な値)。 |
path | Yes | ベジェパス。塗り・線・その両方のいずれか。ペンツールまたは画像トレースから生成されます。 |
text | Yes | 固定のフォント/サイズと所有マテリアルで描画されるテキスト。 |
image | No(設計時のみ) | ラスターの参照画像。パネルはラスターそのものではなく、そこからトレースされたベクターレイヤーをもとに製造されます。 |
Shape レイヤー
interface ShapeLayer extends LayerBase {
type: 'shape';
shape: 'rect' | 'ellipse';
x: number;
y: number;
width: number;
height: number;
rotation?: number; // deg clockwise around bbox center
color: ColorIndex;
}Pattern レイヤー
interface PatternLayer extends LayerBase {
type: 'pattern';
patternType: string;
params: Record<string, number>;
color: ColorIndex;
x: number;
y: number;
size: number;
}patternType と params は、@zpd/core がそのパターンを認識できない場合でも不透明なデータとして保持されます。パターンのレジストリはアプリレベルの関心事であり、core の依存対象ではありません。パターンレイヤーがヒットテストで特別な扱いを受けるのもこのためです。ジオメトリと編集の契約を参照してください。
Path レイヤー
interface PathPoint {
x: number; // anchor, mm
y: number;
hin?: { x: number; y: number }; // absolute bezier handle coords, mm
hout?: { x: number; y: number };
}
interface PathLayer extends LayerBase {
type: 'path';
points: PathPoint[]; // primary subpath (pen tool edits this one)
extraSubpaths?: PathPoint[][];
closed: boolean;
fill: ColorIndex | null;
stroke: ColorIndex | null;
strokeWidth: number; // mm
}extraSubpaths は、画像トレースによって生成される追加の閉じたサブパス——ある 1 つの色領域の穴や島——を保持します。これらはプライマリのサブパスと一緒に even-odd(偶奇)フィルで描画されるため、穴は穴として残ります。
color、fill、stroke は互換用フィールドです。コンテナ所属が null でない paint を所有マテリアルへ強制します。パスでは null と非 null の違いが fill/stroke の有効状態として残ります。
Text レイヤー
interface TextLayer extends LayerBase {
type: 'text';
content: string; // may contain newlines
fontFamily: string;
sizeMm: number; // font size in mm (canvas font px == mm in doc space)
x: number; // bbox top-left, mm
y: number;
rotation?: number;
color: ColorIndex;
}Image レイヤー
interface ImageLayer extends LayerBase {
type: 'image';
src: string; // dataURL
x: number;
y: number;
width: number;
height: number;
rotation?: number;
}src は設計時のソースにすぎません——ラスターはパネル上に製造できません。最終的なパネルは、このレイヤーではなく、そこからトレースされたベクターレイヤーを使用します。
ガイド
ガイドは、レイヤーを揃えやすくするためにエディタが描く、まっすぐな基準線です。ガイドは レイヤーではなく表示用の付帯物 です。DocState.guides に置かれ、layers には決して入りません。ですから色を持たず、製造されることもありません。
interface Guide {
id: string;
orientation: 'horizontal' | 'vertical';
position: number; // mm
hidden?: boolean;
}horizontal ガイドは直線 y = position(パネルの幅いっぱい)、vertical ガイドは x = position(高さいっぱい)です。guides が任意ではなく必須なのは、読み取り側が doc.guides ?? [] を必要としないようにするためで、後方互換はシリアライズの境界が担います(guides を持たない古い設定は [] として読み込まれます。後述を参照)。エディタ側の挙動——ルーラーからのガイドの引き出し、スナップ、「Show guides」トグル——はエディタ → ガイドで扱います。
PanelConfig へのシリアライズ
DocState はメモリ上の作業ドキュメントです。PanelConfig は、ユーザーがダウンロードして製造に引き渡す、バージョン付きのエクスポート形式です。
const PANEL_CONFIG_VERSION = 5;
interface PanelConfig {
version: 5;
app: 'zpd';
panel: { hp: number; widthMm: number; heightMm: number };
palette: string[];
layers: PcbLayerStack;
guides: Guide[];
}hp、layers、guides は正式なデータであり、ラウンドトリップします。panel.widthMm、panel.heightMm、palette は、人間や注文の読み手のための派生的で参考用の出力であり、エクスポート時に hp と固定パレットから計算されます。ファイルを読み込み直す際には、これらの値が再び信頼されることはありません。
v5 で固定 PCB マテリアルスタックが追加されました。v1–v4 の自由な ルート文書は、旧来の paint により Copper、Solder mask、Silkscreen へ 決定的に分割されます。複数マテリアルにまたがるグループは、可能な限り通常の 構造を保って分割され、id は決定的に修復され、null でない paint は所属へ 正規化されます。不正な v5 ルートも固定順へ正規化されます。 PanelConfig フォーマット → バージョニングを参照してください。
parsePanelConfig は決して例外を投げない
parsePanelConfig には、ユーザーが手で編集した JSON、古いエクスポート、あるいは別のツールが生成した JSON などが渡されるため、各フィールドを個別に防御します。不正な panel/layer フィールドはフォールバックまたは破棄され、v1–v4 は移行され、不正な v5 スタックは正規化されます。永続オートセーブ用の厳密なバージョン境界では、未対応または将来バージョンの設定を拒否します。
フィールドごとのパースルールの全容は、packages/ のソースを参照してください。