Math::Quaternion(3pm)
π Math::Quaternion π
okay bestie, welcome to Math::Quaternion, a Perl class to represent quaternions. yes, that's the whole vibe. it's the <pink>User Contributed Perl Documentation</pink> girlie and she's here to help you spin things around in 3D space. let's get into it π¦
β¨ SYNOPSIS β¨
here's the whole lewk, no edits, just vibes:
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);
like?? that's it. that's the whole entry point. iconic honestly π
πΈ DESCRIPTION πΈ
okay so this package lets you create and manipulate quaternions. a quaternion is a mathematical object that was developed as a kind of generalization of complex numbers, and it's usually represented by an array of four real numbers. it's often used to represent rotations in three-dimensional space. so basically: it's the math that makes your camera go brrr π
if you want the deep lore on the mathematics of quaternions, see, for example, http://mathworld.wolfram.com/Quaternion.html for more details.
quaternions can be added, subtracted, and scaled just like complex numbers or vectors β they can also be multiplied, but bestie. listen. 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. so like, order matters, always, no exceptions, periodt.
do NOT flip the order and expect the same result. that is not a thing. the math said no. π
if you just want to represent rotations and don't care about the internal mathematical details, this should be all you need to know, and I mean all of it, no gatekeeping here:
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. that's the rule. rotations are unit quaternions. full stop. β
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({ angle => 0.1, axis => [ 2,3,4]});
heads up: that angle is in radians, not degrees. do not come crying to me at 3am because you passed 90 and your cube is doing somersaults π
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. π yeah. the one on the right happens first. that's the tea. iykyk.
if you perform many iterated quaternion operations, the result may not quite be a unit quaternion due to numerical inaccuracies. π it's not you, it's floating point. 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.
and now for the main character moment, the slerp π§β¨ 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);
...
}
smooth camera movement?? we love to see it. obsessed. π«Ά
okay that's the top of the page bestie, but we are not done β there is more coming, so don't go anywhere. π
β¨ 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).
});
okay bestie, this is THE constructor, the moment everything starts π here's the tea on how it decides what you get:
- pass nothing β you get a unit quaternion back
- pass one non-reference parameter β you get a "scalar" quaternion
- pass one parameter that's a reference to a quaternion or an array of four numbers β you get the matching quaternion object
- pass three parameters β you get a "vector" quaternion
- pass four parameters β you get the corresponding quaternion, periodt
Rotation quaternions can also show up if you hand over a hashref with the axis and angle of rotation, or by giving two vectors for start and finish directions. Heads up though: that second method takes the shortest path between the two vectors and straight-up ignores the "roll" angle. no cap.
π unit
Returns a unit quaternion. it's giving minimalism β¨
my $u = Math::Quaternion->unit; # Returns the quaternion (1,0,0,0).
π conjugate
Returns the conjugate of its argument. little flip, big energy π
my $q = Math::Quaternion->new(1,2,3,4);
my $p = $q->conjugate; # (1,-2,-3,-4)
π inverse
Returns the inverse of its argument. undoing the thing, main character behaviour π
my $q = Math::Quaternion->new(1,2,3,4);
my $qi = $q->inverse;
π normalize
Returns its argument, normalized to unit norm. glow-up but for numbers π¦
my $q = Math::Quaternion->new(1,2,3,4);
my $qn = $q->normalize;
π modulus
Returns the modulus of its argument β which is defined as the square root of the scalar you get by multiplying the quaternion by its conjugate. math is mathing π₯
my $q = Math::Quaternion->new(1,2,3,4);
print $q->modulus;
π isreal
Returns 1 if the given quaternion is real (i.e. it has no quaternion part), or else 0. yes/no, iconic binary π
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 it does a scalar multiplication instead. flexible fr fr.
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. adding, slaying, the usual π
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. taking it away, respectfully π
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. exponent era β‘
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. flip the sign, flip the vibe π
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. stretch, but mathematically π
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. this is the whole point of the module bestie, pay attention π
If you give it three arguments, it reads them as an angle plus 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 handed a scalar angle and a reference to a vector (in either order!), and it'll generate the matching rotation quaternion. order? don't care, we're chill like that π§
my @axis = (0,0,1); # Rotate about Z axis
$theta = pi/2;
$rotquat = Math::Quaternion::rotation($theta,\@axis);
And if both arguments to rotation() are references, they get read as two vectors, and you get back a quaternion that 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 that rotations will happen around, for the rotation represented by the quaternion argument. the axis, the whole axis, nothing but the axis π
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 you call this as a method on a rotation quaternion, it uses that quaternion to perform the corresponding rotation on the vector argument. spin spin spin πͺοΈ
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. graphics besties, this one's for you π¨
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 it returns a list of two array references. The first is a reference to the rotation matrix; the second is a reference to its inverse. this one's lowkey a trick for rendering sprites β you can multiply by the rotation matrix for the viewer position, do some translations, and then multiply by the inverse, so any rectangles you draw will always face the viewer. iconic behaviour fr π
my $rotquat = Math::Quaternion::rotation($theta,@axis); # My rotation.
my ($matref,$invref) = $rotquat->matrix4x4andinverse;
π stringify
Returns a string representation of the quaternion. It's also used to overload the '""' operator, so quaternions can just vibe inside strings, freely interpolated π
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 returns the first quaternion argument
- a scalar argument of one returns the second
smooth transitions, no jumps, we love to see it π
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) ).
look at that formula go π
my $q = Math::Quaternion->new(1,2,3,4);
print Math::Quaternion::exp($q);
π log
Returns the logarithm of its argument. Careful here: the logarithm of a negative real quaternion can take any value of the form (log(-q0),u*pi) for any unit vector u. In those cases, u is chosen to be (1,0,0). so it just picks one for you, periodt.
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, and Luc Vereecken and Bruce Gray for patches. the collaborators ATE, no crumbs left πΈ
β¨ SEE ALSO β¨
- <http://mathworld.wolfram.com/Quaternion.html>
- <http://sjbaker.org/steve/omniv/eulers_are_evil.html>
- Acts 12:4
yes bestie, that last one is really in the list. iykyk π
β¨ 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.
and that's the whole page, you made it to the end π perl v5.34.0, dated 2022-06-15, Math::Quaternion(3pm) β thanks for reading bestie, now go rotate something πβ¨π¦
π¦ where this page comes from
- debian bookworm: libmath-quaternion-perl 0.07-3 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- debian trixie: libmath-quaternion-perl 0.07-3 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu noble: libmath-quaternion-perl 0.07-3 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu resolute: libmath-quaternion-perl 0.07-3 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu stonking: libmath-quaternion-perl 0.07-3 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms