SVG path の M/L/C/Q/A — Bézier 曲線と楕円弧、W3C SVG 2 を読む
DDEVELOPER
開発(更新: 2026-09-06)10 分で読める

SVG path の M/L/C/Q/A — Bézier 曲線と楕円弧、W3C SVG 2 を読む

SVGのpathは、移動・直線・曲線・閉路をコマンドで指定します。M10,10 C20,20 40,20 50,10のような文字列も、現在点と制御点に分ければ読めます。本記事ではW3C SVG 2(2018年10月4日勧告候補)のPaths章と円弧の実装ノートに沿って、曲線の数学と編集時の注意点を整理します。

#SVG#path#Bézier#数学#W3C

path コマンド一覧 — 10 種類 × 大文字/小文字

W3C SVG 2 仕様 §9.3 で定義されている path コマンドは以下の 10 種類です。各コマンドは大文字 (絶対座標) と小文字 (現在点からの相対座標) のペアで存在し、計20種の文字が使えます。ただしZ/zには座標引数がなく、両者は同じ動作です。

表が収まらない場合は、左右にスクロールできます。

文字名前引数意味
M / mmovetox yペンを移動 (描画なし)
L / llinetox y直線
H / hhorizontal linetox水平線
V / vvertical linetoy垂直線
C / ccubic Bézierx1 y1 x2 y2 x y3 次 Bézier 曲線
S / ssmooth cubicx2 y2 x y前のC/Sに応じて制御点を反転
Q / qquadratic Bézierx1 y1 x y2 次 Bézier 曲線
T / tsmooth quadraticx y前のQ/Tに応じて制御点を反転
A / aelliptical arcrx ry x-axis-rotation large-arc-flag sweep-flag x y楕円弧
Z / zclosepath(なし)現在のサブパスを閉じる

同じコマンドの引数を連続させるとコマンド文字を省略できます (例: L 10,20 30,40 50,60 = 3本の連続線)。これが SVG ファイルが暗号のように見える主因です。

Mに続く2組目以降の座標はLとして扱われます。Zは単にLで戻る場合と異なり、ストロークの端を閉路の接合として扱います。

3次Bézier曲線と4つの制御点

Bézier曲線は、制御点を用いて形を指定するパラメトリック曲線です。SVGでは2次と3次の曲線を直接指定できます。ここでは3次の場合を見ます。

3 次 Bézier 曲線は 4 つの制御点 P₀, P₁, P₂, P₃ から定義され、パラメータ t ∈ [0, 1] による曲線:

B(t) = (1-t)³ P₀ + 3(1-t)² t P₁ + 3(1-t) t² P₂ + t³ P₃

この式はバーンスタイン基底関数の和の形 (binomial expansion of (1-t)+t = 1) で、以下の重要な性質を持ちます。

  • 端点保存: t=0 で P₀、t=1 で P₃ を必ず通る (中間のP₁、P₂は一般には通らない)
  • 接線方向: 導関数はB′(0)=3(P₁−P₀)、B′(1)=3(P₃−P₂)。ベクトルが0なら接線方向はこの式だけでは決まらない
  • 凸包性: 曲線は 4 制御点の凸包の内側に必ず収まる (描画範囲が予測しやすい)
  • アフィン不変性: 制御点を平行移動・回転・スケールすると曲線も同様に変換される

SVG の C コマンド C x1,y1 x2,y2 x,y は、現在の点を P₀、引数の (x1,y1) を P₁、(x2,y2) を P₂、(x,y) を P₃ として扱います。

de Casteljau アルゴリズム — 数値的に安定な評価

Bézier 曲線を数値計算する際、上記の多項式を直接展開するよりde Casteljau アルゴリズム を使う方が安定です。これは線形補間を再帰的に適用する手法で、浮動小数点の丸め誤差がなくなるわけではありません。

// 3 次 Bézier の de Casteljau (パラメータ t での点を求める)
function deCasteljau(P0, P1, P2, P3, t):
  Q0 = lerp(P0, P1, t)   // (1-t)P0 + t P1
  Q1 = lerp(P1, P2, t)
  Q2 = lerp(P2, P3, t)
  R0 = lerp(Q0, Q1, t)
  R1 = lerp(Q1, Q2, t)
  return lerp(R0, R1, t)

このアルゴリズムは曲線分割にも使えます。t = 0.5 で得られる点と中間結果を組み合わせれば、元の曲線を 元の形状を正確に分けた2本のBézier に分割でき、SVG エディタの「曲線の途中に点を追加」機能の数学的基礎になっています。各エディタの全編集操作が同じ実装とは限りません。

S / T コマンド — 滑らかな接続

Sは直前がCまたはSの場合に、その最後の制御点を現在点に関して反転し、新しい第1制御点にします。直前がそれ以外なら第1制御点は現在点です。Tも直前がQまたはTの場合だけ反転し、それ以外では現在点を制御点にします。

reflectedX = 2 * currentX - previousControlX
reflectedY = 2 * currentY - previousControlY

M 0,0 C 10,10 30,10 40,20 S 60,10 70,20
M 0,0 C 10,10 30,10 40,20 C 50,30 60,10 70,20

この2つのpathは同じ曲線です。同じ次数で各区間をt∈[0,1]として比べると、反転により接続点の1階導関数が一致します。直前コマンドの条件を無視して、S/Tがあらゆる接続を滑らかにするとは扱えません。

A コマンド — 楕円弧の 7 引数

楕円弧 A は SVG path で最も難解とされるコマンドです。引数は 7 つ:

A rx ry x-axis-rotation large-arc-flag sweep-flag x y
  • rx, ry: 楕円の半径 (x 方向、y 方向)
  • x-axis-rotation: 楕円自体の回転角度 (度)
  • large-arc-flag: 0は180度以下、1は180度以上の弧を選択。半円では区別できない場合がある
  • sweep-flag: 0は負、1は正の角度方向。通常のy軸下向きのSVG座標系では、それぞれ反時計回り・時計回りに見える
  • x, y: 終点の座標

一般には、指定された半径と回転で端点を通る2つの楕円と、それぞれの弧から最大4通りが候補になります。ただし半径補正後の半円など、フラグの組合せが同じ形に重なる場合があります。負の半径は絶対値として扱い、どちらかの半径が0なら直線、始点と終点が同じなら円弧を省略します。1個のAで全円を指定することはできません。

endpoint → 中心点変換 — W3C SVG 1.1 Implementation Notes F.6.5

SVG の A コマンドは「始点 + 終点 + 半径 + フラグ」で曲線を指定しますが、中心形式を使う処理では「中心 + 半径 + 開始角 + 角度範囲」が必要です。両者を結ぶ変換式は W3C SVG 1.1 Implementation Notes Appendix F.6.5 で定義されています。

// 入力: 始点 (x1,y1), 終点 (x2,y2), rx, ry, φ (回転), fA (large), fS (sweep)

// 先に同一点・半径0を処理し、rx, ryは絶対値にする
// 三角関数に渡すφは度からラジアンへ変換する

// Step 1: 楕円が回転していない座標系に変換
x1' = cos(φ)·(x1-x2)/2 + sin(φ)·(y1-y2)/2
y1' = -sin(φ)·(x1-x2)/2 + cos(φ)·(y1-y2)/2

// Step 2: 半径補正 (端点間距離が直径より長いとき自動拡大)
λ = (x1'/rx)² + (y1'/ry)²
if λ > 1:
  rx *= sqrt(λ)
  ry *= sqrt(λ)

// Step 3: 中心点 (回転前座標で)
sign = (fA != fS) ? +1 : -1
factor = sign · sqrt(max(0, (rx²·ry² - rx²·y1'² - ry²·x1'²) / (rx²·y1'² + ry²·x1'²)))
cx' = factor · rx·y1' / ry
cy' = factor · -ry·x1' / rx

// Step 4: 元の座標系に戻す
cx = cos(φ)·cx' - sin(φ)·cy' + (x1+x2)/2
cy = sin(φ)·cx' + cos(φ)·cy' + (y1+y2)/2

// Step 5: 開始角と掃引角度
θ_start = atan2((y1'-cy')/ry, (x1'-cx')/rx)
Δθ      = atan2((-y1'-cy')/ry, (-x1'-cx')/rx) - θ_start
if !fS and Δθ > 0: Δθ -= 2π
if  fS and Δθ < 0: Δθ += 2π

疑似コードでは半径補正後の丸め誤差による小さな負値を0に丸めています。極端な値ではオーバーフロー等にも配慮が必要で、完成済みの数値ライブラリではありません。特定エディタの内部実装を保証するものでもありません。

実例 — 円を path で描く 4 つの方法

半径 50 の円を中心 (100, 100) に描く path を 4 通りに書き分けてみます。

// 方法 1: circle 要素 (path ではない)
<circle cx="100" cy="100" r="50"/>

// 方法 2: A コマンド 2 個 (上半分 + 下半分)
<path d="M 50,100 A 50,50 0 0 1 150,100 A 50,50 0 0 1 50,100"/>

// 方法 3: C コマンド 4 個 (近似。丸め前のκでは最大半径方向誤差は半径の約0.0273%)
//   κ = 4·(√2 - 1) / 3 ≒ 0.5523
<path d="M 100,50 C 127.614237,50 150,72.385763 150,100 C 150,127.614237 127.614237,150 100,150 C 72.385763,150 50,127.614237 50,100 C 50,72.385763 72.385763,50 100,50"/>

// 方法 4: Q コマンド 4 個 (近似、誤差大、円というより四角に近い)
<path d="M 100,50 Q 150,50 150,100 Q 150,150 100,150 Q 50,150 50,100 Q 50,50 100,50"/>

κ = 4(√2−1)/3は端点と中央を円に合わせる古典的な近似です。あらゆる誤差基準で最適な係数ではなく、最大半径方向誤差をさらに小さくする別の近似もあります。CやQによる多項式曲線は、一般に円弧そのものではありません。

SVGOのconvertShapeToPathはconvertArcs設定でcircle/ellipseをpath化できますが、「常に4つのCへ変換する」機能ではありません。要素数や表現を変えることが、必ずサイズ削減につながるわけでもありません。

Bézier の連続性 (G⁰ / G¹ / C¹ / C²)

曲線の接続は、位置・接線方向・導関数を分けて考えます。ここでは各区間のパラメータをt∈[0,1]とします。

表が収まらない場合は、左右にスクロールできます。

条件意味
G⁰位置が一致。角があるかどうかは未指定
位置と接線方向が一致(正則な接続を想定)
位置と1階導関数が一致
C¹に加え2階導関数が一致

曲率が連続という幾何学的条件とC²は同一ではありません。S/Tの反転が保証するのは、前述の条件での1階導関数の一致までです。SVG pathにはBスプラインやNURBSを直接指定するコマンドはなく、必要なら対応する区間や近似へ変換します。

SVG エディタが path を最適化するロジック

pathのサイズ削減では、次のような方法を検討します。

  • 連続点を集約: L 10,10 L 20,20L 10,10 20,20 (コマンド文字省略)
  • 絶対 → 相対座標 (条件付き): 大きな値より小さな相対値の方が文字数が少ないとき
  • 小数桁削減: 50.00000150
  • shape → path: <rect>M ... L ... L ... L ... Z に変換
  • 連続曲線を S/T に: 制御点が対称なら S/T で省略
  • 結合可能な path をマージ: 同属性の連続パスを1つに

コマンドの省略と座標の丸めは別です。丸めは形状を変え得て、path結合は塗り・マーカー・アニメーション・外部参照にも影響する場合があります。削減率や完全な見た目の維持を保証せず、元画像を残し、実際の拡大率と利用箇所で比較します。

まとめ

  • pathは10種類のコマンドで構成され、Z/zやMの繰返しには個別の規則がある。
  • Bézierは制御点から評価でき、de Casteljauは評価と分割に使える。
  • S/Tの反転には直前コマンドの条件がある。
  • Aでは退化ケース、半径補正、フラグと座標系を確認する。
  • 円のBézier近似や座標の丸めは、厳密な形状保存とは別である。

参考文献・ソース

記事作成に関する注記

本記事は AI(大規模言語モデル)を編集補助として活用して作成しています。 公開前に編集者が内容を確認していますが、事実誤認・仕様の解釈ミス・最新情報との齟齬が含まれる可能性があります。 重要な判断を行う際は、本文中の一次ソースや公式ドキュメントを必ずご自身でご確認ください。 誤りにお気づきの場合は、お問い合わせフォームよりご連絡いただけると助かります。

🔧 関連ツール

📚 関連記事