Math::Quaternion(3pm)

πŸ“¦
version 0.07 Β· 2 versions
  • 0.07 Β· debian bookworm, debian trixie, ubuntu noble, ubuntu resolute, ubuntu stonking (latest)
  • 0.07 Β· ubuntu jammy
, in debian bookworm, debian trixie, ubuntu noble, ubuntu resolute, ubuntu stonking

πŸ’– 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 ✨

yes bestie, that last one is really in the list. iykyk πŸ™ˆ

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