B-スプライン曲線(NURBS曲線)で90°の円弧を描く
※この記事は前回記事の続きです。 tecofalltolt.hatenablog.jp
目次
はじめに
前回、B-スプライン曲線でへにょりレーザー(東方Projectに登場する曲がるレーザー)を作成する記事を公開しました。 確かに制御点に従ってきれいに曲がる曲線は作成できるのですが、円弧を描こうとするとうまくいきません。
もちろん、制御点を増やすことで円弧を描くことは可能ですが、制御点が増えるということは曲線の軌道の計算量と制御点の作成コストが増えるばかりで望ましくありません。
そこで、前回の最後に名前だけ登場した、B-スプライン曲線を拡張した形であるNURBS曲線を利用します。
NURBS曲線
NURBSとは、Non-Uniform Rational B-Spline(非一様有理B-スプライン)の略で、B-スプラインよりも更に柔軟性と正確性が高い曲線や曲面を表すことができるモデルとして知られています。
このNURBS曲線はB-スプライン曲線の制御点に重みを追加したもので、番目の制御点の重みを用いて以下の式で表されます。
このとき、重みが大きいほど曲線への影響力が強くなります。「制御点が曲線を引っ張る力」と考えるのも良いでしょう。
また、すべての制御点の重みが1の場合は分母が1となり、従来のB-スプライン曲線と同じ結果となります。
90°の円弧状に曲げる
過去に自分がNURBSを用いた研究に使用していたCADデータ内に90°の円弧状に曲がっているデータが有ったため抽出したところ、 円弧状に曲げるには3つの制御点をL字に並べ、角の制御点の重みを倍にすれば良いようです。 つまり、
という制御点があった場合、それぞれの重みを
と設定することで円弧状に曲げることができます。
詳細な計算がなく大変申し訳無いのですが、実際に円弧状に曲がっているデータの数値をそのまま持ってきたものであり、 今回のテーマは円弧状に曲げることだけであるためご容赦ください。
実践
前回のプログラムを次のように書き換えて実行します。
変数weight
が重みの値です。
vector <Vec2D> points; float pointInterval = 1.0f / pointNum; Vec2D p; float buf; // 座標を計算 for (int i = 0; i < pointNum; i++) { p = Vec2D(0.0, 0.0); buf = 0.0f; for (int j = 0; j < controlPoint.size(); j++) { p += controlPoint[j] * baseF(j, degree, pointInterval * i) * weight[j]; buf += baseF(j, degree, pointInterval * i) * weight[j]; } p /= buf; points.push_back(p); }
以下の画像は、左が従来の重みを付ける前の曲線、右が上記プログラムで重みを付けたあとの曲線です。 円弧の形に大きく近づいています。
おわりに
先に述べたように重みがすべて1であれば従来のB-スプラインの挙動と同様になるため、曲げ方を変えたいところだけ重みを変化させることでその箇所だけ挙動を変化させることができます。 例えば、数値を1より大きくすることで一気に強く曲げる、数値をマイナスにすることで制御点から斥力を発生させる、といったことも可能です。重みを変えて好きな挙動を探してみると良いと思います。
NURBSはB-スプラインの拡張ではありますが、パラメータの追加は一つでさらに変化もしないため、前回記事の曲線の導入ができていれば拡張しやすいのではないでしょうか。 これまでのB-スプライン曲線よりも自己表現性に優れているため、おもしろパラメータをぜひ探してみてください。