NAME
    Math::Quaternion - Perl class to represent quaternions

SYNOPSIS
     use Math::Quaternion;
     my $q = Math::Quaternion->new;  # Make a new unit quaternion

     # Make a rotation about the axis (0,1,0)
     my $q2 = Math::Quaternion::rotation(0.1,0,1,0);
     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 = 0.5*($q2+$q3);                   # 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,inverse($q1)); # (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.

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.