table of contents
| Math::Quaternion(3pm) | User Contributed Perl Documentation | Math::Quaternion(3pm) |
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. Aquaternion is a mathematical object developed as a kind ofgeneralization of complex numbers, usually represented by an arrayof four real numbers, and is often used to represent rotations inthree-dimensional space.
See, for example, <http://mathworld.wolfram.com/Quaternion.html> formore details on the mathematics of quaternions.
Quaternions can be added, subtracted, and scaled just like complexnumbers or vectors -- they can also be multiplied, but quaternionmultiplication DOES NOT COMMUTE. That is to say, if you havequaternions $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 theinternal mathematical details, this should be all you need to know:
All quaternions have a quantity called the "norm", similar to thelength of a vector. A quaternion with norm equal to 1 is called a"unit quaternion". All quaternions which represent rotations areunit 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 givenangle (in radians) about a given axis:
$qrot = Math::Quaternion->new({ angle => 0.1, axis => [ 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 maynot quite be a unit quaternion due to numerical inaccuracies. Youcan make sure any quaternion has unit length, by doing:
$unitquat = $anyquat->normalize;
If you have a rotation quaternion, and you want to find the 3x3matrix which represents the corresponding rotation, then:
@matrix = $q->matrix3x3;
Similarly, you can generate a 4x4 matrix of the sort you'd pass toOpenGL:
@glmatrix = $q->matrix4x4;
If you have a vector representing a direction, and you want torotate 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 acamera, and you have two quaternions: one to represent astarting orientation, and another to represent a finishingposition. If you want to find all the quaternions representingthe orientations in between, allowing your camera to movesmoothly 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 onenon-reference parameter is given, a "scalar" quaternion is returned.If one parameter is given and it is a reference to a quaternion oran array of four numbers, the corresponding quaternion object isreturned. If three parameters are given, a "vector" quaternion isreturned. If four parameters are given, the correspondingquaternion is returned.
Rotation quaternions may also be created by passing a hashref withthe axis and angle of rotation, or by specifying two vectorsspecifying start and finish directions. Bear in mind that the lattermethod will take the shortest path between the two vectors, ignoringthe "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 thesquare root of the
scalar obtained by multiplying the quaternionby 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 quaternionpart, or
else 0.
my $q1 = Math::Quaternion->new(1,2,3,4); my $q2 = Math::Quaternion->new(5,0,0,0); print $q1->isreal; # 0; print $q2->isreal; # 1; - multiply
- Performs a quaternion multiplication of its two arguments.If one of the
arguments is a scalar, then performs a scalarmultiplication 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 thethree 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 quaternionargument.
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 whichrotations will be
performed, for the rotation represented by thequaternion 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 thisquaternion to
perform the corresponding rotation on the vectorargument.
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 OpenGLmatrix 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 3x3matrix which represents the corresponding rotation.
my $rotquat = Math::Quaternion::rotation($theta,@axis); # My rotation. my @m = $rotquat->matrix3x3; - matrix4x4andinverse
- Similar to matrix4x4, but returns a list of two arrayreferences. The first
is a reference to the rotation matrix;the second is a reference to its
inverse. This may be usefulwhen rendering sprites, since you can multiply
by the rotationmatrix for the viewer position, perform some translations,
andthen multiply by the inverse: any resulting rectangles drawnwill 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 usedto overload
the '""' operator, so that quaternions may befreely 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; performsspherical linear
interpolation between the two quaternions. Thequaternion arguments are
assumed to be unit quaternions, and thescalar is assumed to lie between 0
and 1: a scalar argument ofzero will return the first quaternion argument,
and a scalarargument 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 negativereal
quaternion can take any value of them form (log(-q0),u*pi) forany 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 and Daniel Connelly for useful suggestions, andLuc Vereecken and Bruce Gray for patches.
SEE ALSO¶
COPYRIGHT AND LICENSE¶
Copyright 2003 by Jonathan Chin
This library is free software; you can redistribute it and/or modifyit under the same terms as Perl itself.
| 2022-06-15 | perl v5.34.0 |