PerfectVector
著者: Irene Kim3 分で読める

SVGの相対パス命令:現在の点を追跡する

現在の点を追跡してSVGの絶対・相対パスを読み解きます。同じ図形を使った実例で、movetoの座標組、closepathのリセット、3次曲線の座標を検証します。

目次

絶対座標のSVGパス命令 M、L、C は、現在のユーザー座標系での位置を指定します。相対形式の m、l、c は、現在の点からのオフセットを指定します。パスを読むときや書き換えるときは、各区間の後でその点を追跡してください。数値を変えずに命令の文字だけを変えると、形状が変わる場合があります。

混乱の多くは2つの規則から生じます。パスの先頭にある m の最初の座標組は絶対位置への移動として扱われ、その後の座標組は相対的な直線になります。また、Z または z はサブパスを閉じ、現在の点をその始点に戻します。閉じた後に相対移動を行うと、その戻された点が基準になります。

位置とオフセットを区別して読む

W3Cのパス仕様では、現在の点、サブパス、描画命令によってパスを定義しています。座標はSVGのユーザー単位であり、必ずしも画面上のピクセルではありません。viewBox や変換によって、表示位置との対応が変わることがあります。

(40, 40) から始める場合、次の2つの命令は同じ直線を表します。

L180 40
l140 0

前者は終点 (180, 40) を指定します。後者は (40, 40) に (140, 0) を加えます。どちらも (180, 40) で終わり、その位置が次の区間の現在の点になります。

一方、l180 40 は (180, 40) を加えるので、終点は (220, 80) になります。構文としては有効ですが、別の直線を描きます。

命令位置の値が表す意味
M x y指定した位置からサブパスを開始します
m dx dy現在の点を基準にサブパスを開始します。ただし先頭のmには後述の規則があります
L x y指定した位置まで直線を描きます
l dx dy現在の点にオフセットを加えた位置まで直線を描きます
C / c2つの制御点と終点を、位置または同じ始点からのオフセットで指定します
Z / zサブパスの始点まで閉じ、現在の点を戻します

大文字と小文字の Z の効果は同じです。座標引数はありません。MDNのd属性リファレンスには、各命令とそれぞれの引数が掲載されています。

同じオリジナル図形を2通りの記述で比較する

次の完全なSVGを absolute.svg として保存し、ブラウザーで開いてください。正方形の穴がある青緑色の複合バッジと、別の珊瑚色の3次曲線を含んでいます。

<svg xmlns="http://www.w3.org/2000/svg" width="300" height="240" viewBox="0 0 300 240">
  <path class="badge" fill="#173b40" fill-rule="evenodd" d="M40 40L180 40L210 80L180 140L40 140Z M80 70L80 110L120 110L120 70Z"/>
  <path class="curve" fill="none" stroke="#f28567" stroke-width="6" d="M40 190C80 145 155 225 220 180"/>
</svg>

relative.svg ではSVGのサイズ、viewBox、スタイルをそのままにして、2つのpath要素を次の内容に置き換えます。

<path class="badge" fill="#173b40" fill-rule="evenodd"
  d="m40 40 140 0 30 40 -30 60 -140 0z m40 30 0 40 40 0 0 -40z"/>
<path class="curve" fill="none" stroke="#f28567" stroke-width="6"
  d="m40 190c40 -45 115 35 180 -10"/>

私たちは両方のオリジナルファイルを、絶対座標のバッジを意図的に壊したコピーと並べてブラウザーで描画しました。壊したコピーでは、最初の L180 40 だけを l180 40 に変え、数値は変えていません。

実際のSVG描画で絶対座標と相対座標の複合バッジと3次曲線が一致し、直線命令の大文字小文字だけを変えて座標を変換しないとバッジが変形する様子
最初の2つの記述は一致します。3つ目では最初の直線の終点が(180, 40)から(220, 80)に変わります。下のパネルでは閉じる処理、3次曲線のオフセット、比較対象のペアのブラウザー測定値を追跡しています。

このページではSVG標準の getTotalLength() と getPointAtLength() を使い、各パスの全長に対して同じ割合の位置で301個のサンプルを比較しました。表示された長さは、両方のバッジが 657.082 ユーザー単位、両方の曲線が 189.066 でした。サンプル点の最大差は、どちらのペアも 0.000000 と表示されました。

この測定は今回の文字列の一致を裏づけるもので、直線の頂点と3次曲線の点も以下の計算で一致します。任意の書き換えや変換ツールがすべてのSVG命令を処理できることの証明ではありません。比較では、変換を加えない同じビューポートと描画設定を終始使用しています。

最初のmと暗黙の直線を追跡する

バッジは次の記述で始まります。

m40 40 140 0 30 40 -30 60 -140 0z

移動を行うのは最初の座標組だけです。残りの組は、暗黙の相対 l 区間として動作します。movetoの定義では、先頭の m40 40 が絶対位置への開始として扱われても、その後の組は相対座標のままであると明記されています。

各組を順に追跡します。

座標組または命令操作操作後の現在の点
m40 40最初の移動(40, 40)
140 0暗黙の相対直線(180, 40)
30 40暗黙の相対直線(210, 80)
-30 60暗黙の相対直線(180, 140)
-140 0暗黙の相対直線(40, 140)
zこのサブパスの始点まで閉じる(40, 40)

比較すると、複数の座標組を持つ大文字の M では、2組目以降が暗黙の絶対 L 区間になります。すべての組が新たな移動になるわけではなく、この小文字の例と同じ差分も使いません。

path要素はそれぞれ独自のパス列を持ちます。その先頭の相対 m を、無関係な直前のpath要素の終点から始めないでください。

次の移動には閉じたサブパスの始点を使う

穴は最初の z の後に始まります。

m40 30 0 40 40 0 0 -40z

この時点で現在の点は (40, 40) です。そのため、新たな相対移動は (40, 30) を加え、穴の始点は (80, 70) になります。暗黙の直線はその後 (80, 110)、(120, 110)、(120, 70) に達してから閉じます。

外側のバッジの最後の直線の終点 (40, 140) を誤って基準にすると、移動先は (80, 170) になります。先の閉じる処理があるため、その計算は誤りです。closepathの定義では、現在の点を現在のサブパスの始点に戻します。

両サブパスは1つのpath要素に属しています。この例では fill-rule="evenodd" により、内側の正方形が穴になります。座標の書き換えを比較するときは塗りの規則と描画設定を維持し、塗りの変更が検証中の形状を見えにくくしないようにしてください。

3次曲線の各制御点を同じ始点から測る

別の曲線は (40, 190) から始まります。

M40 190 C80 145 155 225 220 180
m40 190 c40 -45 115 35 180 -10

この1つの小文字 c 区間では、3つの組すべてが (40, 190) からのオフセットです。第2制御点を第1制御点から測るのでも、終点を第2制御点から測るのでもありません。

点相対座標組絶対位置
第1制御点(40, -45)(80, 145)
第2制御点(115, 35)(155, 225)
終点(180, -10)(220, 180)

区間の後は、現在の点を終点 (220, 180) に更新します。次に相対区間があれば、その新しい始点を使います。MDNのパス入門では3次曲線の制御点と絶対・相対形式を説明しています。ベジェ曲線の接続ガイドでは、接線ハンドルと省略命令 S を別途扱っています。

すべての命令のすべての数値に、座標の引き算を一般化しないでください。円弧では半径、回転、フラグの役割が異なり、絶対・相対位置を切り替えるのは終点の座標組だけです。A や a のデータを書き換える前に、円弧フラグの解説を確認してください。この例が示すのは直線、移動、閉じる処理、通常の3次曲線1区間であり、汎用的なパス変換処理ではありません。

配置とパス構文を混同せずに図形を移動する

すべて相対座標で記述されたこの複合バッジでは、先頭の m40 40 を m60 60 に変えると、頂点が (20, 20) 移動します。後続の差分と相対サブパスの移動が、変更した始点に従うためです。別の曲線パスは変わりません。絶対・相対が混在するデータでは、後続の絶対位置が指定座標に固定されたままなので、最初の移動を編集するとパスが変形する場合があります。

2つのパスからなる図形全体をまとめて移動するには、両方の d 文字列を維持し、要素を transform="translate(20 20)" のグループ内に入れます。SVGの座標系仕様では、変換が座標の対応をどう定めるかを説明しています。グループの変換なら、元のパスデータを書き換えずに配置を変えられます。

移動後の図形が引き続きビューポート内に収まるか確認してください。グループを移動してもキャンバスは自動で広がりません。SVGの変換順序ガイドでは、移動と拡大縮小を組み合わせた場合の配置を扱っています。

表記を変える前に元の形状を確認する

図形がPNGやJPGでしか存在しない場合は、PerfectVectorを利用できます。SVGの候補を作成したら、パスデータを編集する前に、復元された輪郭、穴、曲線の形を元画像と比較してください。絶対表記か相対表記かだけでは、復元された形状が適切かどうかは判断できません。

ベクターのマスターがあればそのまま使い、単純な記号なら描き直した方が制御しやすい場合もあります。命令の書き換えでは、失われたラスター情報から意図した形状を復元できません。画像ベクター化の概要では、その別工程である再構築を説明しています。

使えるSVGを編集するときは元のコピーを残し、対応できる区間を1つずつ変え、実際のファイルを同じ縮尺で比較してください。現在の点だけでなくサブパスの始点も追跡します。比較中は塗りとストロークの設定を維持し、その後、書き出したファイルを受け取り側のアプリでテストしてください。

よくある質問

最初の小文字のmは別のパスからの相対移動ですか? いいえ。mがパスの最初の命令の場合、最初の座標組は絶対位置の開始として扱われます。追加の組は暗黙の相対直線であり、別のpath要素の終点が開始の基準になるわけではありません。

Zまたはzの後の現在の点はどこですか? 閉じたばかりのサブパスの始点です。後続の相対移動は、直前の直線の終点ではなく、この戻された点を使います。

命令を小文字にすればパスを変換できますか? いいえ。各命令の意味に応じて位置の値を再計算する必要があります。この例のL180 40は、現在の点(40, 40)からならl140 0になります。l180 40では別の直線を描きます。

参考資料

  1. W3C — SVG 2のパス — 現在の点、先頭のmoveto、暗黙の直線、閉じる処理、3次曲線の引数を定義しています。
  2. MDN — パス入門 — 絶対・相対の直線と曲線の命令を図解しています。
  3. MDN — d属性 — 円弧とclosepathを含む各命令の引数の役割を一覧にしています。
  4. W3C — SVG 2の座標系 — ユーザー座標の対応と、変換による図形の配置を説明しています。

ラスター画像しかないところから始める場合は、PerfectVectorでSVGパスを作成して、復元された輪郭と穴を確認してください。その後、理解している命令だけを編集し、納品するSVGを元画像と比較して検証します。

ブログのその他の記事

編集しやすい
よりきれいなSVGから始めましょう