Location : Home > Languages > Perl > Package Title : Math::Quaternion |
![]() |
Math::Quaternion - 4元数表現の Perl クラス
use Math::Quaternion qw(slerp); my $q = Math::Quaternion->new; # 新規単位4元数の生成 # 軸 (0,1,0) に関する回転 my $q2 = Math::Quaternion->new({axis=>[0,1,0],angle=>0.1}); my @v = (1,2,3); # ベクトル my @vrotated = $q2->rotate_vector(@v); # Rotate @v about (0,1,0). my $q3 = Math::Quaternion::rotation(0.7,2,1,4); # 異なる回転 my $q4 = slerp($q2,$q3,0.5); # 補間された回転 my @vinterp = $q4->rotate_vector(@v);
本パッケージは4元数(quaternion)を生成し、演算することができる。4元数とは複素数の一般化として開発された数学的オブジェクトであり、4つの実数の配列により表現され、よく3次元の回転を表現するために用いられる。
4元数に関する詳しい数学的説明は、例えば http://mathworld.wolfram.com/Quaternion.html を見よ。
4元数は複素数やベクトルのように加算・積算・スケールが可能である。乗算は可能であるが、可換ではない。すなわち2つの4元数 $q1 と $q2 があれば、一般に $q1*$q2 != $q2*$q1 である。これは必ずしも可換でない、回転を表現するために用いられることと関係がある。
回転を表現できればよく、内部的な≦表現二興味がなければ以下のことにだけ気をつけておけばよい:全ての4元数は、ベクトルの長さにあたる「ノルム」と呼ばれる量を持つ。ノルムが1であれば「単位4元数」と呼ばれる。回転を表す全ての4元数は単位4元数である。
引数なしで new() を呼び出すと、回転を表さない単位4元数を生成する。
$q = Math::Quaternion->new;
所与の軸に関して所与の角度(ラジアン)の回転を表す4元数を生成することもできる。
$qrot = Math::Quaternion->new({ axis => 0.1, angle => [ 2,3,4]});
$q1 と $q2 があれば2種類の回転を考えることができるが、$q2 による回転の後に $q1 による回転を示したければ以下のように書く。
$q3 = $q2 * $q1; # $q2 による回転の後に $q1 による回転
これは順番を入れ替えた $q1 * $q2 とは同じものではないことに注意しよう。
もし4元数による計算を多数回繰り返したら、結果は数値的不正確さのために単位4元数とは異なる値になるだろう。もし単位長を保持したければ以下のようにするとよい。
$unitquat = $anyquat->normalize;
回転を示す4元数があれば、対応する 3x3 行列がある。これは以下のようにして取得する。
@matrix = $q->matrix3x3;
同様に、OpenGL に渡すために 4x4 行列を生成することもできる。
@glmatrix = $q->matrix4x4;
方向を示すベクトルがあれば、4元数 $q によってベクトルを回転させることもできる。
my @vector = (0,0,1); # Z 方向を示すベクトル my @newvec = $q->rotate_vector(@vector); # 新しい方向
カメラの方向を示すために4元数を用いるいためには、2つの4元数が必要である。1つは始点を示し、もう1つは終点を示す。その間の方向を示す全ての4元数を取得したければ slerp() ルーチンを用いて始点から終点までスムーズに移動することを許すことになる。
use Math::Quaternion qw(slerp); my ($qstart, $qend) = ... ; # 始点から終点まで9点を $tween で設定 for my $t (1..9) { my $tween = slerp($qstart,$qend,0.1*$t); ... }
new
my $q = Math::Quaternion->new; # 新しい単位4元数を生成 my $q2 = Math::Quaternion->new(1,2,3,4);# 特定の4元数を生成 my $q3 = Math::Quaternion->new($q2); # 既存の4元数をコピー my $q4 = Math::Quaternion->new(5.6); # 4元数 (5.6,0,0,0) を生成 my $q5 = Math::Quaternion->new(7,8,9); # 4元数 (0,7,8,9) を生成 my $q6 = Math::Quaternion->new({ axis => [ 1,2,3], # ベクトル (1,2,3) の周りの angle => 0.2, # 0.2 ラジアンの回転に対応する4元数を生成 }); my $q7 = Math::Quaternion->new({ 'v1' => [ 0,1,2], # ベクトル (-1,2,0) の上への 'v2' => [ -1,2,0], # ベクトル (0,1,2) の回転に対応する4元数を生成 });
パラメータが指定されなければ単位4元数を返す。1つの非参照パラメータが渡されればスカラ4元数を返す。1つのパラメータが与えられ、それが4元数または4つの数の配列への参照であるならば対応する4元数を返す。3つのパラメータが与えられればベクトル4元数を返す。4つのパラメータが与えられれば対応する4元数を返す。
回転を示す4元数は、軸と回転角へのハッシュ参照を渡すか、始点と終点を示す2つのベクトルを特定するかで生成することができる。後者の方法は回転角を無視することで2つのベクトル間の最短の経路を取ることに注意すること。
unit
単位4元数を返す。
my $u = Math::Quaternion->unit; # 4元数 (1,0,0,0) を返す。
conjugate
引数の共役4元数を返す。
my $q = Math::Quaternion->new(1,2,3,4); my $p = $q->conjugate; # (1,-2,-3,-4)
inverse
引数の逆4元数を返す。
my $q = Math::Quaternion->new(1,2,3,4); my $qi = $q->inverse;
normalize
引数で渡された4元数のノルムを1にして返す。
my $q = Math::Quaternion->new(1,2,3,4); my $qn = $q->normalize;
modulus
4元数にその共役4元数を乗じて得られたスカラの平方根で定義された絶対値を返す。
my $q = Math::Quaternion->new(1,2,3,4); print $q->modulus;
isreal
与えられた4元数が実数であれば 1 を返し、そうでなければ 0 を返す。
my $q1 = Math::Quaternion->new(1,2,3,4); my $q2 = Math::Quaternion->new(5,0,0,0); print $q1->isreal; # 1; print $q2->isreal; # 0;
multiply
2つの4元数に対し乗算を行う。一方の引数がスカラであればスカラ乗算を行う。
my $q1 = Math::Quaternion->new(1,2,3,4); my $q2 = Math::Quaternion->new(5,6,7,8); my $q3 = Math::Quaternion::multiply($q1,$q2); # (-60 12 30 24) my $q4 = Math::Quaternion::multiply($q1,$q1->inverse); # (1 0 0 0)
dot
2つの4元数の内積を返す。
my $q1=Math::Quaternion->new(1,2,3,4); my $q2=Math::Quaternion->new(2,4,5,6); my $q3 = Math::Quaternion::dot($q1,$q2);
plus
2つの引数の和算を行う。
my $q1 = Math::Quaternion->new(1,2,3,4); my $q2 = Math::Quaternion->new(5,6,7,8); my $q3 = Math::Quaternion::plus($q1,$q2); # (6 8 10 12)
minus
2つの引数の減算を行う。
my $q1 = Math::Quaternion->new(1,2,3,4); my $q2 = Math::Quaternion->new(5,6,7,8); my $q3 = Math::Quaternion::minus($q1,$q2); # (-4 -4 -4 -4)
power
4元数のスカラまたは4元数べき乗を計算する。
my $q1 = Math::Quaternion->new(1,2,3,4); my $q2 = Math::Quaternion::power($q1,4); # ( 668 -224 -336 -448 ) my $q3 = $q1->power(4); # ( 668 -224 -336 -448 ) my $q4 = $q1**(-1); # $q1->inverse と同じ use Math::Trig; my $q5 = exp(1)**( Math::Quaternion->new(pi,0,0) ); # おおよそ (-1 0 0 0)
negate
与えられた4元数の符号を反転させる。
my $q = Math::Quaternion->new(1,2,3,4); my $q1 = $q->negate; # (-1,-2,-3,-4)
squarednorm
引数のノルムの平方を返す。
my $q1 = Math::Quaternion->new(1,2,3,4); my $sn = $q1->squarednorm; # 30
scale
2つの引数のスカラ乗算を行う。
my $q = Math::Quaternion->new(1,2,3,4); my $qq = Math::Quaternion::scale($q,2); # ( 2 4 6 8) my $qqq= $q->scale(3); # ( 3 6 9 12 )
rotation
回転に対応する4元数を生成する。3つの引数が与えられた場合には3つの軸ベクトルのなす角として翻訳される。
use Math::Trig; # pi を定義。 my $theta = pi/2; # 回転角は my $rotquat = Math::Quaternion::rotation($theta,0,0,1); # $rotquat は Z 軸周りの 90 °の回転を示す my ($x,$y,$z) = (1,0,0); # X 軸方向の単位ベクトル my ($xx,$yy,$zz) = $rotquat->rotate_vector($x,$y,$z); # ($xx,$yy,$zz) は浮動小数点の誤差の範囲で ( 0, 1, 0)
rotation() はスカラ角とベクトルへの参照(どの順でもよい)を取り、対応する回転を示す4元数を生成する。
my @axis = (0,0,1); # Z 軸についての回転 $theta = pi/2; $rotquat = Math::Quaternion::rotation($theta,\@axis);
rotation() への引数がともに参照であれば2つのベクトルと解釈され、1つめのベクトルの2つめのベクトルの上への回転に対応する4元数を返す。
my @startvec = (0,1,0); # 北を指すベクトル my @endvec = (-1,0,0); # 西を指すベクトルt $rotquat = Math::Quaternion::rotation(\@startvec,\@endvec); my @newvec = $rotquat->rotate_vector(@startvec); # @endvec と同じ
rotation_angle
4元数引数により示された回転角を返す。
my $q = Math::Quaternion::rotation(0.1,2,3,4); my $theta = $q->rotation_angle; # 0.1 を返す。
rotation_axis
回転を行う軸を示す単位ベクトルを返す。回転は4元数で示される。
my $q = Math::Quaternion::rotation(0.1,1,1,0); my @v = $q->rotation_axis; # (0.5*sqrt(2),0.5*sqrt(2),0) を返す。
rotate_vector
回転を示す4元数についてのメソッドとして呼び出されたとき、この4元数はベクトル引数に関する回転のために用いられる。
use Math::Trig; # pi の定義 my $theta = pi/2; # 90°の回転 my $rotquat = Math::Quaternion::rotation($theta,0,0,1); # Z 軸 my ($x,$y,$z) = (1,0,0); # X 軸方向の単位ベクトル my ($xx,$yy,$zz) = $rotquat->rotate_vector($x,$y,$z) # ($xx,$yy,$zz) は浮動小数点の誤差の範囲内で ( 0, 1, 0)
matrix4x4
1つの引数、回転を示す4元数を取る。対応する回転を示す16要素の OpenGL 行列を返す。
my $rotquat = Math::Quaternion::rotation($theta,@axis); # My rotation. my @m = $rotquat->matrix4x4;
matrix3x3
1つの引数、回転を示す4元数を取る。対応する回転を示す9要素の行列を返す。
my $rotquat = Math::Quaternion::rotation($theta,@axis); # My rotation. my @m = $rotquat->matrix3x3;
matrix4x4andinverse
matrix4x4 と似ているが、こちらは2つの配列参照のリストを返す。1つめは回転行列への参照であり、2つめはその逆行列への参照である。これはレンダリングする際に有用である。視点に回転行列を乗じ、変換し、逆行列を乗じ、結果として得られる長方形は常に視点に面している。
my $rotquat = Math::Quaternion::rotation($theta,@axis); # My rotation. my ($matref,$invref) = $rotquat->matrix4x4andinverse;
stringify
4元数の文字列表現を返す。4元数を文字列で自由に補間できるように '""' 演算子をオーバーロードしている。
my $q = Math::Quaternion->new(1,2,3,4); print $q->stringify; # "( 1 2 3 4 )" print "$q"; # "( 1 2 3 4 )"
slerp
2つの4元数と1つのスカラを引数に取る。2つの4元数の間の球面上で線形補間を計算する。引数となる4元数は単位4元数とし、スカラは 0 と 1 の間の値をとる。0は1つめの4元数を示し、1は2つめの4元数を示す。
use Math::Trig; my @axis = (0,0,1); my $rq1 = Math::Quaternion::rotation(pi/2,\@axis); # Z 軸に関する90°の回転 my $rq2 = Math::Quaternion::rotation(pi,\@axis); # Z 軸に関する180°の回転 my $interp = Math::Quaternion::slerp($rq1,$rq2,0.5); # Z 軸に関する135°の回転
exp
e^q を計算する。4元数 q は x+uy と書かれる。ただし x は実数、u は単位4元数である。このとき exp(q) == exp(x) * ( cos(y) + u sin(y) ) である。
my $q = Math::Quaternion->new(1,2,3,4); print Math::Quaternion::exp($q);
log
引数の対数を返す。負の実数の4元数の対数は、単位ベクトル u に対し (log(-q0),u*pi) の形式の値をとる。この場合 u は (1,0,0) とする。
my $q = Math::Quaternion->new(1,2,3,4); print Math::Quaternion::log($q);
Jonathan Chin, <jon-quaternion.pm@earth.li>
Rene Uittenbogaard の有用な示唆に感謝。
Copyright 2003 by Jonathan Chin
本ライブラリはフリーソフトウェアであり、Perl 本体と同等の条件で修正/再配布してもよい。
![]() |
Updated : 2008/01/29 |