1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
|
NAME
Math::Quaternion - Perl class to represent quaternions
SYNOPSIS
use Math::Quaternion qw(slerp);
my $q = Math::Quaternion->new; # Make a new unit quaternion
# Make a rotation about the axis (0,1,0)
my $q2 = Math::Quaternion->new({axis=>[0,1,0],angle=>0.1});
my @v = (1,2,3); # A vector.
my @vrotated = $q2->rotate_vector(@v); # Rotate @v about (0,1,0).
my $q3 = Math::Quaternion::rotation(0.7,2,1,4); # A different rotation.
my $q4 = slerp($q2,$q3,0.5); # Interpolated rotation.
my @vinterp = $q4->rotate_vector(@v);
DESCRIPTION
This package lets you create and manipulate quaternions. A quaternion is
a mathematical object developed as a kind of generalization of complex
numbers, usually represented by an array of four real numbers, and is
often used to represent rotations in three-dimensional space.
See, for example, <http://mathworld.wolfram.com/Quaternion.html> for
more details on the mathematics of quaternions.
Quaternions can be added, subtracted, and scaled just like complex
numbers or vectors -- they can also be multiplied, but quaternion
multiplication DOES NOT COMMUTE. That is to say, if you have quaternions
$q1 and $q2, then in general $q1*$q2 != $q2*$q1. This is related to
their use in representing rotations, which also do not commute.
If you just want to represent rotations and don't care about the
internal mathematical details, this should be all you need to know:
All quaternions have a quantity called the "norm", similar to the length
of a vector. A quaternion with norm equal to 1 is called a "unit
quaternion". All quaternions which represent rotations are unit
quaternions.
If you call new() without any arguments, it will give you a unit
quaternion which represents no rotation:
$q = Math::Quaternion->new;
You can make a quaternion which represents a rotation of a given angle
(in radians) about a given axis:
$qrot = Math::Quaternion->new({ axis => 0.1, angle => [ 2,3,4]});
Say you have two rotations, $q1 and $q2, and you want to make a
quaternion representing a rotation of $q1 followed by $q2. Then, you do:
$q3 = $q2 * $q1; # Rotate by $q1, followed by $q2.
Remember that this is NOT the same as $q1 * $q2, which will reverse the
order of the rotations.
If you perform many iterated quaternion operations, the result may not
quite be a unit quaternion due to numerical inaccuracies. You can make
sure any quaternion has unit length, by doing:
$unitquat = $anyquat->normalize;
If you have a rotation quaternion, and you want to find the 3x3 matrix
which represents the corresponding rotation, then:
@matrix = $q->matrix3x3;
Similarly, you can generate a 4x4 matrix of the sort you'd pass to
OpenGL:
@glmatrix = $q->matrix4x4;
If you have a vector representing a direction, and you want to rotate
the vector by a quaternion $q:
my @vector = (0,0,1); # Vector pointing in the Z direction.
my @newvec = $q->rotate_vector(@vector); # New direction.
Say you're using quaternions to represent the orientation of a camera,
and you have two quaternions: one to represent a starting orientation,
and another to represent a finishing position. If you want to find all
the quaternions representing the orientations in between, allowing your
camera to move smoothly from start to finish, use the slerp() routine:
use Math::Quaternion qw(slerp);
my ($qstart, $qend) = ... ;
# Set $tween to 9 points between start and end, exclusive.
for my $t (1..9) {
my $tween = slerp($qstart,$qend,0.1*$t);
...
}
METHODS
new
my $q = Math::Quaternion->new; # Make a new unit quaternion.
my $q2 = Math::Quaternion->new(1,2,3,4);# Make a specific quaternion.
my $q3 = Math::Quaternion->new($q2); # Copy an existing quaternion.
my $q4 = Math::Quaternion->new(5.6); # Make the quaternion (5.6,0,0,0)
my $q5 = Math::Quaternion->new(7,8,9); # Make the quaternion (0,7,8,9)
my $q6 = Math::Quaternion->new({ # Make a quaternion corresponding
axis => [ 1,2,3], # to a rotation of 0.2 radians
angle => 0.2, # about the vector (1,2,3).
});
my $q7 = Math::Quaternion->new({ # Make a quaternion which would
'v1' => [ 0,1,2], # rotate the vector (0,1,2) onto
'v2' => [ -1,2,0], # the vector (-1,2,0).
});
If no parameters are given, a unit quaternion is returned. If one
non-reference parameter is given, a "scalar" quaternion is returned. If
one parameter is given and it is a reference to a quaternion or an
array of four numbers, the corresponding quaternion object is returned.
If three parameters are given, a "vector" quaternion is returned. If
four parameters are given, the corresponding quaternion is returned.
Rotation quaternions may also be created by passing a hashref with the
axis and angle of rotation, or by specifying two vectors specifying
start and finish directions. Bear in mind that the latter method will
take the shortest path between the two vectors, ignoring the "roll"
angle.
unit
Returns a unit quaternion.
my $u = Math::Quaternion->unit; # Returns the quaternion (1,0,0,0).
conjugate
Returns the conjugate of its argument.
my $q = Math::Quaternion->new(1,2,3,4);
my $p = $q->conjugate; # (1,-2,-3,-4)
inverse
Returns the inverse of its argument.
my $q = Math::Quaternion->new(1,2,3,4);
my $qi = $q->inverse;
normalize
Returns its argument, normalized to unit norm.
my $q = Math::Quaternion->new(1,2,3,4);
my $qn = $q->normalize;
modulus
Returns the modulus of its argument, defined as the square root of the
scalar obtained by multiplying the quaternion by its conjugate.
my $q = Math::Quaternion->new(1,2,3,4);
print $q->modulus;
isreal
Returns 1 if the given quaternion is real ,ie has no quaternion part,
or else 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
Performs a quaternion multiplication of its two arguments. If one of
the arguments is a scalar, then performs a scalar multiplication
instead.
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
Returns the dot product of two quaternions.
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
Performs a quaternion addition of its two arguments.
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
Performs a quaternion subtraction of its two arguments.
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
Raise a quaternion to a scalar or quaternion power.
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); # Same as $q1->inverse
use Math::Trig;
my $q5 = exp(1)**( Math::Quaternion->new(pi,0,0) ); # approx (-1 0 0 0)
negate
Negates the given quaternion.
my $q = Math::Quaternion->new(1,2,3,4);
my $q1 = $q->negate; # (-1,-2,-3,-4)
squarednorm
Returns the squared norm of its argument.
my $q1 = Math::Quaternion->new(1,2,3,4);
my $sn = $q1->squarednorm; # 30
scale
Performs a scalar multiplication of its two arguments.
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
Generates a quaternion corresponding to a rotation.
If given three arguments, interprets them as an angle and the three
components of an axis vector.
use Math::Trig; # Define pi. my $theta = pi/2;
# Angle of rotation my $rotquat =
Math::Quaternion::rotation($theta,0,0,1);
# $rotquat now represents a rotation of 90 degrees about Z axis.
my ($x,$y,$z) = (1,0,0); # Unit vector in the X direction.
my ($xx,$yy,$zz) = $rotquat->rotate_vector($x,$y,$z);
# ($xx,$yy,$zz) is now ( 0, 1, 0), to within floating-point error.
rotation() can also be passed a scalar angle and a reference to a
vector (in either order), and will generate the corresponding rotation
quaternion.
my @axis = (0,0,1); # Rotate about Z axis
$theta = pi/2;
$rotquat = Math::Quaternion::rotation($theta,\@axis);
If the arguments to rotation() are both references, they are
interpreted as two vectors, and a quaternion is returned which rotates
the first vector onto the second.
my @startvec = (0,1,0); # Vector pointing north
my @endvec = (-1,0,0); # Vector pointing west
$rotquat = Math::Quaternion::rotation(\@startvec,\@endvec);
my @newvec = $rotquat->rotate_vector(@startvec); # Same as @endvec
rotation_angle
Returns the angle of rotation represented by the quaternion argument.
my $q = Math::Quaternion::rotation(0.1,2,3,4);
my $theta = $q->rotation_angle; # Returns 0.1 .
rotation_axis
Returns the unit vector representing the axis about which rotations
will be performed, for the rotation represented by the quaternion
argument.
my $q = Math::Quaternion::rotation(0.1,1,1,0);
my @v = $q->rotation_axis; # Returns (0.5*sqrt(2),0.5*sqrt(2),0)
rotate_vector
When called as a method on a rotation quaternion, uses this quaternion
to perform the corresponding rotation on the vector argument.
use Math::Trig; # Define pi.
my $theta = pi/2; # Rotate 90 degrees
my $rotquat = Math::Quaternion::rotation($theta,0,0,1); # about Z axis
my ($x,$y,$z) = (1,0,0); # Unit vector in the X direction.
my ($xx,$yy,$zz) = $rotquat->rotate_vector($x,$y,$z)
# ($xx,$yy,$zz) is now ( 0, 1, 0), to within floating-point error.
matrix4x4
Takes one argument: a rotation quaternion. Returns a 16-element array,
equal to the OpenGL matrix which represents the corresponding rotation.
my $rotquat = Math::Quaternion::rotation($theta,@axis); # My rotation.
my @m = $rotquat->matrix4x4;
matrix3x3
Takes one argument: a rotation quaternion. Returns a 9-element array,
equal to the 3x3 matrix which represents the corresponding rotation.
my $rotquat = Math::Quaternion::rotation($theta,@axis); # My rotation.
my @m = $rotquat->matrix3x3;
matrix4x4andinverse
Similar to matrix4x4, but returnes a list of two array references. The
first is a reference to the rotation matrix; the second is a reference
to its inverse. This may be useful when rendering sprites, since you
can multiply by the rotation matrix for the viewer position, perform
some translations, and then multiply by the inverse: any resulting
rectangles drawn will always face the viewer.
my $rotquat = Math::Quaternion::rotation($theta,@axis); # My rotation.
my ($matref,$invref) = $rotquat->matrix4x4andinverse;
stringify
Returns a string representation of the quaternion. This is used to
overload the '""' operator, so that quaternions may be freely
interpolated in strings.
my $q = Math::Quaternion->new(1,2,3,4);
print $q->stringify; # "( 1 2 3 4 )"
print "$q"; # "( 1 2 3 4 )"
slerp
Takes two quaternion arguments and one scalar; performs spherical
linear interpolation between the two quaternions. The quaternion
arguments are assumed to be unit quaternions, and the scalar is assumed
to lie between 0 and 1: a scalar argument of zero will return the first
quaternion argument, and a scalar argument of one will return the
second.
use Math::Trig;
my @axis = (0,0,1);
my $rq1 = Math::Quaternion::rotation(pi/2,\@axis); # 90 degs about Z
my $rq2 = Math::Quaternion::rotation(pi,\@axis); # 180 degs about Z
my $interp = Math::Quaternion::slerp($rq1,$rq2,0.5); # 135 degs about Z
exp
Exponential operator e^q. Any quaternion q can be written as x+uy,
where x is a real number, and u is a unit pure quaternion. Then, exp(q)
== exp(x) * ( cos(y) + u sin(y) ).
my $q = Math::Quaternion->new(1,2,3,4);
print Math::Quaternion::exp($q);
log
Returns the logarithm of its argument. The logarithm of a negative real
quaternion can take any value of them form (log(-q0),u*pi) for any unit
vector u. In these cases, u is chosen to be (1,0,0).
my $q = Math::Quaternion->new(1,2,3,4);
print Math::Quaternion::log($q);
AUTHOR
Jonathan Chin, <jon-quaternion.pm@earth.li>
ACKNOWLEDGEMENTS
Thanks to Rene Uittenbogaard for useful suggestions.
SEE ALSO
<http://mathworld.wolfram.com/Quaternion.html>
<http://sjbaker.org/steve/omniv/eulers_are_evil.html>
Acts 12:4
COPYRIGHT AND LICENSE
Copyright 2003 by Jonathan Chin
This library is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
|