b-スプライン曲線をベジェ曲線の集まりとして書く
ということはできるのか?→できた
なぜそのようなことがしたいのか
てきとうな点列があって、それに対して B-スプライン曲線を描きたい ラスターであれば適当にサンプリングして折れ線で近似するが、SVG で滑らかに出力したい
SVG で描ける最も自由度の高い曲線は3次ベジェ
以下、真面目に考えるコーナー
ベジェとして描きたい任意のパラメトリックな3次曲線を
$ p(t) = p_0 + p_1 t + p_2 t^2 + p_3 t^3
とする(ここで$ p とか$ p_1 とかはベクトルということにする。面倒なので太字にしないが……)。
で、3次ベジェ曲線を同じ感じで多項式っぽく書くと
$ b(t) = b_0 + ( -3 b_0 + 3 b_1 ) t + ( 3 b_0 - 6 b_1 + 3 b_2 ) t^2 + (-b_0 + 3 b_1 - 3 b_2 + b_3) t^3
となる(ここで$ b_0, b_1, b_2, b_3は制御点)。
ただの1次方程式ですね、面倒くさいが……
$ \left\{ \begin{aligned} p_0 &= b_0 \\ p_1 &= -3 b_0 + 3 b_1 \\ p_2 &= 3 b_0 - 6 b_1 + 3 b_2 \\ p_3 &= -b_0 + 3 b_1 - 3 b_2 + b_3 \end{aligned} \right.
これ行列で書けばよかったのでは????????????
まあいいでしょう、解くのは普通にだるいので Wolfram Alpha に入れます
p_0 = b_0, p_1 = -3 b_0 + 3 b_1, p_2 = 3 b_0 - 6 b_1 + 3 b_2, p_3 = -b_0 + 3 b_1 - 3 b_2 + b_3 solve for b_0, b_1, b_2, b_3
ありがとうスティーブン、生まれてきてくれて
https://gyazo.com/4619748519a55b8d9c07c7721542da47
完全に蛇足だけど ChatGPT にも聞いてみよう
https://gyazo.com/578e0e7f2072141484895487c9f6cc33
無能
うおおおおお動かねえ
黄色がサンプリングで近似したBスプライン
マゼンタがベジェとして描いた(描きたかった)なにか
https://gyazo.com/01b720e4dcaec9705776de141c78994d
インタラクティブのためのコードが多くて脳が混乱してきた。素 canvas 書く
→書いた。やっぱ同じだな…ということは数式がおかしいのか。。。
https://gyazo.com/625e66103cf235cec2bc2f5dab3e8bd4
いやよく考えたら上の数式は間違っている気がしてきたな……
p0, p1, p2, p3 を制御点とする B-スプラインは p0 (や、p1,p2, p3 )を通らないはずなのに、方程式の解では p0 = b0 になっている。b1はベジェの1番目の制御点なので、通る。おかしいな
諦めて歯みがいてたらわかった!!!!「p0, p1, p2, p3 を制御点とするBスプライン曲線」を描く多項式の係数はp0, p1, p2, p3じゃないわ、ここを混同していた、、、
つまり
「Bスプラインの制御点」→「3次曲線の係数」→「ベジェ曲線の制御点」
という2ステップの変換を行う必要があるわけだ。まあやろうと思えば一発でいけないこともないんだろうけど、とりあえずナイーブにやってみてからだな
一発で通そうとすると、Bスプラインの行列とベジェの行列を掛けて連立方程式として解く、みたいな感じになるんだと思う
まず「Bスプラインの制御点」→「3次曲線の係数」を考える。Bスプラインを行列で書くと
$ P(t) = \left[ \begin{matrix} 1 & t & t^2 & t^ 3 \end{matrix} \right] \frac{1}{6} \left[ \begin{matrix} 1 & 4 & 1 & 0 \\ -3 & 0 & 3 & 0 \\ 3 & -6 & 3 & 0 \\ -1 & 3 & -3 & 1 \end{matrix} \right] \left[ \begin{matrix} P_0 \\ P_1 \\ P_2 \\ P_3 \end{matrix} \right]
になる。これを展開する。助けてスティーブン!
code:mathematica
CoefficientList1, t, t^2, t^3 } . (1/6 { { 1, 4, 1, 0 }, { -3, 0, 3, 0 }, { 3, -6, 3, 0 }, { -1, 3, -3, 1 } })) . { p_0, p_1, p_2, p_3 }, t
→はい
https://gyazo.com/b334440b5b465e6691c66391b5af4b7b
合ってるのかこれ?とりあえず実装
https://gyazo.com/0841f044b59ab390387da6e42ad9f7bc
あってた!!!!!!!!ではSVG版にも実装しましょう
https://gyazo.com/6d3b250e8850681e8f051527ef5fc72c
どんなに拡大してもカクカクになりませんぜ、だってベジェ命令を使っているから……
https://gyazo.com/3e9a63e777eee00178dec677ba6dcdde
↓これは旧版。醜すぎます、polylineはカス、さようなら
https://gyazo.com/2fd7c2d1beb4d5ae357e92e17f7e5655
できたので行列計算で一発で変換するやつも作る。
code:js
const cubicBezierMatrix = (new DOMMatrix([
1, -3, 3, -1,
0, 3, -6, 3,
0, 0, 3, -3,
0, 0, 0, 1,
]));
const cubicBasisMatrix = new DOMMatrix([
1, -3, 3, -1,
4, 0, -6, 3,
1, 3, 3, -3,
0, 0, 0, 1,
].map(e => e / 6)));
const transformMatrix = cubicBezierMatrix.inverse().multiply(cubicBasisMatrix);
// control points of b-Spline
const p = [
{ x: 100, y: 100 },
{ x: 100, y: 200 },
{ x: 200, y: 200 },
{ x: 200, y: 100 },
];
const bx = transformMatrix.multiply(new DOMMatrix([
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
]));
const by = ... // same as x
const bezier = (
<path d={`
M ${bx.m11} ${bx.m11}
C ${bx.m12} ${bx.m12}, ${bx.m13} ${bx.m13}, ${bx.m14} ${bx.m14}$
`}>
);