基本ベクトルの場合の最適化(3次元幾何)

数カ月触れていなかった自分が書いたコードを見た所、驚くべきものを発見した。

3次元空間内をある方向に向けてある距離だけ動いたオブジェクトが衝突するかどうかを判定し、また衝突するときはどれだけ動いたら衝突するか計算するコードがあった。 このようなロジックは、通常ベクトルを用いて方程式を立てて解く。その結果を実装するわけだが、この場合オブジェクトの動きを表すベクトルが以下のような特殊なものであった場合、計算がかなり簡略化できる。 - 単位ベクトル(単位ベクトルに変換する処理が含まれることが多いため) - 軸に平行なベクトル(複数の軸の成分を考えなくてよいため) よって、この性質を併せ持つ軸に沿った単位ベクトル、つまり基本ベクトルの場合、計算が非常に簡単になるわけだ。

問題は、実行中に来たベクトルが基本ベクトルかどうかを判定するのは若干手間であることだ。 通常、数値計算の結果(1, 0, 0)になる、というような値は、大抵数値誤差などのせいで(0.999999999, 7e-15, 5e-16)のような値になる。 もちろん適当なtoleranceを用いて判定することは可能だが、軸に平行な単位ベクトルが来ることがあまり期待できない場合、これは単にコストになってしまう。 さらに、ゲームやシミュレーションなどを考えた時、軸に沿ってちょうど距離1だけ移動する、というようなイベントはそうそう起きるものではない。

ではそれをする意味はどこにあるのか。それは、ユーザーからの入力がある場合である。 ユーザーからの入力は、まだ偶然生じるよりは基本ベクトルになる可能性がある。特に軸に平行なベクトルになる可能性はある(空間中に突然オブジェクトを作り、そのまま下に降ろしていく、など)。 そう思ったのかは知らないが、数カ月前の自分はそれを想定していたらしい。コード中に、3次元ベクトルクラスの他に基底ベクトルが存在したのだ。

基本ベクトルクラスは通常のインターフェースを持つが変更は許さず、積や和を作ると通常のベクトルクラスを作成して返す、というようになっている。 長さを計算するルーチンのみならずドット積やクロス積もオーバーロードされて高速化されている。 衝突判定のコードはというと、is_pointなどのメタ関数によるSFINAEが飛び交っており、非常に面白くなっていた。

このようにすると、型レベルで分岐が行われるので(コンパイル時に上手く決められるようにすると)基本ベクトルが来ることを保証できる。 なので、基本ベクトルを前提にした最適化が許されるというわけだ。

まあ、手間に見あうとはあまり思えないので、今から消すのだが。単にcollision_zのような関数を定義すれば済んでしまうので、ホンの少しだけ不格好なことを除けば何らデメリットはない。