ガイド
ガイドは、エディタの位置合わせ用の補助線です。ルーラーから引き出す、細くて無限に伸びる基準線で、レイヤーを揃える際の目安にします。ガイドは レイヤーではなく表示用の furniture(付帯物) です。DocState.guides に置かれ、doc.layers には決して入りません。ですからレイヤーリストに現れることも、色を持つことも、製造されることもありません。ガイドは、実際に作るもの を配置しやすくするためだけに存在します。
ガイドのモデル
ガイドは、ドキュメント空間内の 1 本の直線で、@zpd/core で定義されています。
type GuideOrientation = 'horizontal' | 'vertical';
interface Guide {
id: string;
orientation: GuideOrientation;
position: number; // mm
hidden?: boolean;
}水平(horizontal)ガイドは直線
y = positionで、パネルの幅いっぱいに広がります。垂直(vertical)ガイドは直線
x = positionで、パネルの高さいっぱいに広がります。positionはミリメートルで、ほかのすべてと同じドキュメント空間です(ドキュメントの状態とレイヤーを参照)。hiddenなガイドは薄く描画され、スナップにもつかみ取りにも一切参加しません。スナップとガイド表示トグルを参照してください。
DocState.guides は 必須(決して任意ではない)配列です。読み取り側はすっきり保たれ(doc.guides ?? [] は不要)、後方互換はシリアライズの境界が担います(guides を持たない古い設定は [] として読み込まれます。永続化を参照)。
ルーラーからの作成・移動・削除
3 つの操作はいずれもルーラーフレームまたはキャンバス上で始まる、純粋な @zpd/core/guides.ts のミューテーションで、それぞれが 1 つの undo エントリ としてコミットされます。
作成 — ルーラーの帯からキャンバスへドラッグして引き出します。向きは、どちらの帯をつかんだかで決まります。上 の帯は水平ガイド、左 の帯は垂直ガイドを作ります。キャンバス上でドロップすると追加され、キャンバスに届く前に(キャンバスの外で)離すとキャンセルされ、何もコミットされません。
移動 — キャンバス上の既存のガイドを(画面上 5px の許容範囲内で)つかんでドラッグします。このつかみ取りはツールのルーティングより 先に 判定され、イベントを飲み込みます。そのため、アクティブなツールはそれを見ず、下にあるレイヤーの選択よりもガイドが常に優先されます。移動が履歴に書き込まれるのは、位置が実際に変わったときだけです。
削除 — ガイドをキャンバスの外へ、ルーラーの上へドラッグします。そこで離すと削除されます。
ドラッグ中は、プレビュー線がポインタに追従します。削除ドラッグ(キャンバス外へ引き出したガイド)では、プレビューが赤い破線の「ドロップで削除」の線に切り替わり、離す前に結果がはっきり分かります。
なぜ window レベルのドラッグコントローラなのか
ガイドのドラッグは、本質的に 要素の境界をまたぎます。作成ドラッグはルーラーの帯で始まってキャンバスへ移り、削除ドラッグはキャンバスで始まってルーラーの上へ戻ります。これらは CSS グリッド内の兄弟要素なので、要素ごとのポインタハンドラだけではこのジェスチャーを追えません。そこで use-guide-drag.ts は、pointermove/pointerup のリスナーを window に取り付け、すべてをライブなキャンバス矩形に対する幾何計算で解決します。window のリスナーは、ポインタがどの要素の上にあっても、すべての move を受け取ります。
これは意図的に setPointerCapture を使っていません。キャプチャはすべてのイベントを 1 つの要素に束縛してしまい、このドラッグが知る必要のあること — ポインタが今 別の 要素(削除のためのルーラー)の上にあること — を覆い隠してしまうからです。キャンバスの ツール ドラッグは、Editor.tsx で今もポインタキャプチャを使います。それらはキャンバスから決して出ないからです。ガイドは、境界をまたぐ例外です。pointercancel や window の blur は、コミット せずに ジェスチャーを終えます。OS がポインタを奪ったので、ドロップは一度も行われていないからです。
描画
renderer.ts は、再描画ループの中で、ガイドを レイヤーの内容の上、選択クロムの下 に描きます(レンダリングとカメラ → 再描画ループを参照)。各ガイドは、ビューポート全体にわたる細い線で、独特の シアン で描かれます。ガイドが選択クロム(青)やパネルの輪郭(白)と紛れないよう選ばれた色です。線はデバイスピクセルグリッドにスナップされ(round(coord) + 0.5)、どんな devicePixelRatio でもくっきり保たれます。hidden なガイドは、まったく描かれないのではなく、薄く破線で描かれます。ドラッグ中のガイドは、コミット済みの位置ではなくライブのドラフトから描かれるため、下に古い複製を残すことなくポインタに追従します。
スナップ
ガイドは、選択ツールにスナップの対象を与えます。スナップは 2 段階 のモデルです(@zpd/core の snap.ts)。まずグリッドが常に捕まえ(すべての座標は 0.1mm セルの半分以内にグリッド線があります)、次に許容範囲内の明示的なガイドがそのグリッドの結果を 上書き します。ガイドは同点で勝つため、グリッド線とガイドから等距離にある座標はガイドに乗ります。
適用されるのは、レイヤーの 絶対 位置を動かすジェスチャー、すなわち選択の 移動 と、単一の
shape/imageの 軸に沿ったリサイズ です。回転したリサイズは浮動小数点の衛生管理のみ(ガイドの位置は絶対 mm であり、回転した辺に適用しても意味がありません)で、複数リサイズのスケールはガイドスナップしません。選択ツールは ズームに応じてスケールした 許容範囲(
GUIDE_SNAP_PX / pxPerMm)を渡すため、ズームインしていてもアウトしていても、ガイドは同じようにつかまえやすく感じられます。@zpd/core自体は純粋に mm のままで、ズームの概念を持ちません。hidden なガイドは決してスナップせず、つかみ取りもできません。ガイド自身の
hiddenフラグが両方から取り除くため、スナップはマスターの表示トグルとは独立に、ガイドごとにhiddenによって制御されます。
ガイド表示トグル
サイドバーの View セクションには Show guides チェックボックス(デフォルトは オン)があります。これはマスターの表示コントロールで、2 つのことを司ります。
描画 — オフのとき、レンダラーには空のガイドリストが渡されるため、ガイドの線(やドラッグのプレビュー)は一切描かれません。
ルーラー操作 — オフのとき、ルーラーから引き出しても何も作られず、キャンバス上で既存のガイドをつかむこともできません(ドラッグコントローラの
isEnabledゲートが短絡します)。
これは純粋な表示状態です。切り替えてもガイドが追加・削除されることはなく、ほかの View オプションと同じく、ドキュメントやエクスポートされる JSON に触れることは決してありません。
永続化
ガイドは、トップレベルの guides 配列として PanelConfig v2 で導入され、現在の v5 でも保持されます。v1〜v4 の入力は v5 へ移行され、ガイドのない入力は guides: [] として読み込まれます。フィールドごとのルール — ガイドは有効な orientation と有限の position を必要とし、そうでなければ破棄される、id が欠けていれば決定的なフォールバックが使われる、hidden は保持される — は、PanelConfig フォーマット → バージョニングと防御的パース → ガイドで扱います。