The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Panotools::Matrix;

=head1 NAME

Panotools::Matrix - Miscellaneous math for panoramic images

=head1 SYNOPSIS

$matrix = rollpitchyaw2matrix ($roll, $pitch, $yaw);

All angles are in radians not degrees.

=head1 DESCRIPTION

rollpitchyaw2matrix returns a matrix arrayref that encapsulates a
transformation suitable for rotating a vector/point by three degrees of freedom
(roll, pitch and yaw).

roll is positive rotation around the x-axis

pitch is negative rotation around the y-axis

yaw is negative rotation around the z axis

=head1 USAGE

use Panotools::Matrix qw(matrix2rollpitchyaw rollpitchyaw2matrix multiply);

my $point  = [[$x1], [$y1], [$z1]];

my $matrix = rollpitchyaw2matrix ($roll, $pitch, $yaw);

my $result = multiply ($matrix, $point);

($x2, $y2, $z2) = ($result->[0][0], $result->[1][0], $result->[2][0]);

=cut

use Math::Trig;
use Math::Trig ':radial';
use strict;
use warnings;

use Exporter;
use vars qw(@ISA @EXPORT);
@ISA = qw(Exporter);
@EXPORT = qw(rollpitchyaw2matrix matrix2rollpitchyaw multiply); 

sub rollpitchyaw2matrix {
    my ($roll, $pitch, $yaw) = @_;

    my $cosr = cos ($roll);
    my $sinr = sin ($roll);
    my $cosp = cos ($pitch);
    my $sinp = sin (0 - $pitch);
    my $cosy = cos ($yaw);
    my $siny = sin (0 - $yaw);

    my $rollm  = [[        1,       0,       0 ],
                  [        0,   $cosr,-1*$sinr ],
                  [        0,   $sinr,   $cosr ]];

    my $pitchm = [[    $cosp,       0,   $sinp ],
                  [        0,       1,       0 ],
                  [ -1*$sinp,       0,   $cosp ]];

    my $yawm   = [[    $cosy,-1*$siny,       0 ],
                  [    $siny,   $cosy,       0 ],
                  [        0,       0,       1 ]];

    my $foo = multiply ($yawm, $pitchm);
    multiply ($foo, $rollm);
}

sub transpose
{
    my $matrix_in = shift;
    my $matrix_out;

    my $n = 0;
    for my $row (@{$matrix_in})
    {
        my $m = 0;
        for my $column (@{$row})
        {
            $matrix_out->[$m]->[$n] = $matrix_in->[$n]->[$m];
            $m++;
        }
        $n++;
    }
    return $matrix_out;
}

sub multiply
{
    my $matrix_a = shift;
    my $transposed_b = transpose (shift);
    my $matrix_out;

    return undef if (scalar @{$matrix_a->[0]} != scalar @{$transposed_b->[0]});
    for my $row (@{$matrix_a})
    {
        my $rescol = [];
        for my $column (@{$transposed_b})
        {
            push (@{$rescol}, vekpro ($row, $column));
        }
        push (@{$matrix_out}, $rescol);
    }
    return $matrix_out;
}

sub vekpro
{
    my ($a, $b) = @_;
    my $result = 0;

    for my $i (0 .. scalar @{$a} - 1)
    {
        $result += $a->[$i] * $b->[$i];
    }
    $result;
}

# following copied from a spreadsheet by Stuart Milne

sub matrix2rollpitchyaw
{
    my $matrix = shift;
    my $roll = atan2 ($matrix->[2]->[1], $matrix->[2]->[2]);
    my $pitch = -1 * asin (-1 * $matrix->[2]->[0]);
    my $yaw = atan2 (-1 * $matrix->[1]->[0], $matrix->[0]->[0]);
    return ($roll, $pitch, $yaw);
}

1;