Initial Commit
This commit is contained in:
525
database/perl/vendor/lib/Imager/Matrix2d.pm
vendored
Normal file
525
database/perl/vendor/lib/Imager/Matrix2d.pm
vendored
Normal file
@@ -0,0 +1,525 @@
|
||||
package Imager::Matrix2d;
|
||||
use 5.006;
|
||||
use strict;
|
||||
use Scalar::Util qw(reftype looks_like_number);
|
||||
use Carp qw(croak);
|
||||
|
||||
our $VERSION = "1.013";
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Imager::Matrix2d - simple wrapper for matrix construction
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Imager::Matrix2d;
|
||||
$m1 = Imager::Matrix2d->identity;
|
||||
$m2 = Imager::Matrix2d->rotate(radians=>$angle, x=>$cx, y=>$cy);
|
||||
$m3 = Imager::Matrix2d->translate(x=>$dx, y=>$dy);
|
||||
$m4 = Imager::Matrix2d->shear(x=>$sx, y=>$sy);
|
||||
$m5 = Imager::Matrix2d->reflect(axis=>$axis);
|
||||
$m6 = Imager::Matrix2d->scale(x=>$xratio, y=>$yratio);
|
||||
$m8 = Imager::Matric2d->matrix($v11, $v12, $v13,
|
||||
$v21, $v22, $v23,
|
||||
$v31, $v32, $v33);
|
||||
$m6 = $m1 * $m2;
|
||||
$m7 = $m1 + $m2;
|
||||
use Imager::Matrix2d qw(:handy);
|
||||
# various m2d_* functions imported
|
||||
# where m2d_(.*) calls Imager::Matrix2d->$1()
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This class provides a simple wrapper around a reference to an array of
|
||||
9 coefficients, treated as a matrix:
|
||||
|
||||
[ 0, 1, 2,
|
||||
3, 4, 5,
|
||||
6, 7, 8 ]
|
||||
|
||||
Most of the methods in this class are constructors. The others are
|
||||
overloaded operators.
|
||||
|
||||
Note that since Imager represents images with y increasing from top to
|
||||
bottom, rotation angles are clockwise, rather than counter-clockwise.
|
||||
|
||||
=over
|
||||
|
||||
=cut
|
||||
|
||||
our @ISA = 'Exporter';
|
||||
require Exporter;
|
||||
our @EXPORT_OK = qw(m2d_rotate m2d_identity m2d_translate m2d_shear
|
||||
m2d_reflect m2d_scale);
|
||||
our %EXPORT_TAGS =
|
||||
(
|
||||
handy=> [ qw(m2d_rotate m2d_identity m2d_translate m2d_shear
|
||||
m2d_reflect m2d_scale) ],
|
||||
);
|
||||
|
||||
use overload
|
||||
'*' => \&_mult,
|
||||
'+' => \&_add,
|
||||
'""'=>\&_string,
|
||||
"eq" => \&_eq;
|
||||
|
||||
=item identity()
|
||||
|
||||
Returns the identity matrix.
|
||||
|
||||
=cut
|
||||
|
||||
sub identity {
|
||||
return bless [ 1, 0, 0,
|
||||
0, 1, 0,
|
||||
0, 0, 1 ], $_[0];
|
||||
}
|
||||
|
||||
=item rotate(radians=>$angle)
|
||||
|
||||
=item rotate(degrees=>$angle)
|
||||
|
||||
Creates a matrix that rotates around the origin, or around the point
|
||||
(x,y) if the 'x' and 'y' parameters are provided.
|
||||
|
||||
=cut
|
||||
|
||||
sub rotate {
|
||||
my ($class, %opts) = @_;
|
||||
my $angle;
|
||||
|
||||
if (defined $opts{radians}) {
|
||||
$angle = $opts{radians};
|
||||
}
|
||||
elsif (defined $opts{degrees}) {
|
||||
$angle = $opts{degrees} * 3.1415926535 / 180;
|
||||
}
|
||||
else {
|
||||
$Imager::ERRSTR = "degrees or radians parameter required";
|
||||
return undef;
|
||||
}
|
||||
|
||||
if ($opts{'x'} || $opts{'y'}) {
|
||||
$opts{'x'} ||= 0;
|
||||
$opts{'y'} ||= 0;
|
||||
return $class->translate('x'=>$opts{'x'}, 'y'=>$opts{'y'})
|
||||
* $class->rotate(radians=>$angle)
|
||||
* $class->translate('x'=>-$opts{'x'}, 'y'=>-$opts{'y'});
|
||||
}
|
||||
else {
|
||||
my $sin = sin($angle);
|
||||
my $cos = cos($angle);
|
||||
return bless [ $cos, -$sin, 0,
|
||||
$sin, $cos, 0,
|
||||
0, 0, 1 ], $class;
|
||||
}
|
||||
}
|
||||
|
||||
=item translate(x=>$dx, y=>$dy)
|
||||
|
||||
=item translate(x=>$dx)
|
||||
|
||||
=item translate(y=>$dy)
|
||||
|
||||
Translates by the specify amounts.
|
||||
|
||||
=cut
|
||||
|
||||
sub translate {
|
||||
my ($class, %opts) = @_;
|
||||
|
||||
if (defined $opts{'x'} || defined $opts{'y'}) {
|
||||
my $x = $opts{'x'} || 0;
|
||||
my $y = $opts{'y'} || 0;
|
||||
return bless [ 1, 0, $x,
|
||||
0, 1, $y,
|
||||
0, 0, 1 ], $class;
|
||||
}
|
||||
|
||||
$Imager::ERRSTR = 'x or y parameter required';
|
||||
return undef;
|
||||
}
|
||||
|
||||
=item shear(x=>$sx, y=>$sy)
|
||||
|
||||
=item shear(x=>$sx)
|
||||
|
||||
=item shear(y=>$sy)
|
||||
|
||||
Shear by the given amounts.
|
||||
|
||||
=cut
|
||||
sub shear {
|
||||
my ($class, %opts) = @_;
|
||||
|
||||
if (defined $opts{'x'} || defined $opts{'y'}) {
|
||||
return bless [ 1, $opts{'x'}||0, 0,
|
||||
$opts{'y'}||0, 1, 0,
|
||||
0, 0, 1 ], $class;
|
||||
}
|
||||
$Imager::ERRSTR = 'x and y parameters required';
|
||||
return undef;
|
||||
}
|
||||
|
||||
=item reflect(axis=>$axis)
|
||||
|
||||
Reflect around the given axis, either 'x' or 'y'.
|
||||
|
||||
=item reflect(radians=>$angle)
|
||||
|
||||
=item reflect(degrees=>$angle)
|
||||
|
||||
Reflect around a line drawn at the given angle from the origin.
|
||||
|
||||
=cut
|
||||
|
||||
sub reflect {
|
||||
my ($class, %opts) = @_;
|
||||
|
||||
if (defined $opts{axis}) {
|
||||
my $result = $class->identity;
|
||||
if ($opts{axis} eq "y") {
|
||||
$result->[0] = -$result->[0];
|
||||
}
|
||||
elsif ($opts{axis} eq "x") {
|
||||
$result->[4] = -$result->[4];
|
||||
}
|
||||
else {
|
||||
$Imager::ERRSTR = 'axis must be x or y';
|
||||
return undef;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
my $angle;
|
||||
if (defined $opts{radians}) {
|
||||
$angle = $opts{radians};
|
||||
}
|
||||
elsif (defined $opts{degrees}) {
|
||||
$angle = $opts{degrees} * 3.1415926535 / 180;
|
||||
}
|
||||
else {
|
||||
$Imager::ERRSTR = 'axis, degrees or radians parameter required';
|
||||
return undef;
|
||||
}
|
||||
|
||||
# fun with matrices
|
||||
return $class->rotate(radians=>-$angle) * $class->reflect(axis=>'x')
|
||||
* $class->rotate(radians=>$angle);
|
||||
}
|
||||
|
||||
=item scale(x=>$xratio, y=>$yratio)
|
||||
|
||||
Scales at the given ratios.
|
||||
|
||||
You can also specify a center for the scaling with the C<cx> and C<cy>
|
||||
parameters.
|
||||
|
||||
=cut
|
||||
|
||||
sub scale {
|
||||
my ($class, %opts) = @_;
|
||||
|
||||
if (defined $opts{'x'} || defined $opts{'y'}) {
|
||||
$opts{'x'} = 1 unless defined $opts{'x'};
|
||||
$opts{'y'} = 1 unless defined $opts{'y'};
|
||||
if ($opts{cx} || $opts{cy}) {
|
||||
return $class->translate('x'=>-$opts{cx}, 'y'=>-$opts{cy})
|
||||
* $class->scale('x'=>$opts{'x'}, 'y'=>$opts{'y'})
|
||||
* $class->translate('x'=>$opts{cx}, 'y'=>$opts{cy});
|
||||
}
|
||||
else {
|
||||
return bless [ $opts{'x'}, 0, 0,
|
||||
0, $opts{'y'}, 0,
|
||||
0, 0, 1 ], $class;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$Imager::ERRSTR = 'x or y parameter required';
|
||||
return undef;
|
||||
}
|
||||
}
|
||||
|
||||
=item matrix($v11, $v12, $v13, $v21, $v22, $v23, $v31, $v32, $v33)
|
||||
|
||||
Create a matrix with custom coefficients.
|
||||
|
||||
=cut
|
||||
|
||||
sub matrix {
|
||||
my ($class, @self) = @_;
|
||||
|
||||
if (@self == 9) {
|
||||
return bless \@self, $class;
|
||||
}
|
||||
else {
|
||||
$Imager::ERRSTR = "9 coefficients required";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
=item transform($x, $y)
|
||||
|
||||
Transform a point the same way matrix_transform does.
|
||||
|
||||
=cut
|
||||
|
||||
sub transform {
|
||||
my ($self, $x, $y) = @_;
|
||||
|
||||
my $sz = $x * $self->[6] + $y * $self->[7] + $self->[8];
|
||||
my ($sx, $sy);
|
||||
if (abs($sz) > 0.000001) {
|
||||
$sx = ($x * $self->[0] + $y * $self->[1] + $self->[2]) / $sz;
|
||||
$sy = ($x * $self->[3] + $y * $self->[4] + $self->[5]) / $sz;
|
||||
}
|
||||
else {
|
||||
$sx = $sy = 0;
|
||||
}
|
||||
|
||||
return ($sx, $sy);
|
||||
}
|
||||
|
||||
=item compose(matrix...)
|
||||
|
||||
Compose several matrices together for use in transformation.
|
||||
|
||||
For example, for three matrices:
|
||||
|
||||
my $out = Imager::Matrix2d->compose($m1, $m2, $m3);
|
||||
|
||||
is equivalent to:
|
||||
|
||||
my $out = $m3 * $m2 * $m1;
|
||||
|
||||
Returns the identity matrix if no parameters are supplied.
|
||||
|
||||
May return the supplied matrix if only one matrix is supplied.
|
||||
|
||||
=cut
|
||||
|
||||
sub compose {
|
||||
my ($class, @in) = @_;
|
||||
|
||||
@in
|
||||
or return $class->identity;
|
||||
|
||||
my $out = pop @in;
|
||||
for my $m (reverse @in) {
|
||||
$out = $out * $m;
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
=item _mult()
|
||||
|
||||
Implements the overloaded '*' operator. Internal use.
|
||||
|
||||
Currently both the left and right-hand sides of the operator must be
|
||||
an Imager::Matrix2d.
|
||||
|
||||
When composing a matrix for transformation you should multiply the
|
||||
matrices in the reverse order of the transformations:
|
||||
|
||||
my $shear = Imager::Matrix2d->shear(x => 0.1);
|
||||
my $rotate = Imager::Matrix2d->rotate(degrees => 45);
|
||||
my $shear_then_rotate = $rotate * $shear;
|
||||
|
||||
or use the compose method:
|
||||
|
||||
my $shear_then_rotate = Imager::Matrix2d->compose($shear, $rotate);
|
||||
|
||||
=cut
|
||||
|
||||
sub _mult {
|
||||
my ($left, $right, $order) = @_;
|
||||
|
||||
if (ref($right)) {
|
||||
if (reftype($right) eq "ARRAY") {
|
||||
@$right == 9
|
||||
or croak "9 elements required in array ref";
|
||||
if ($order) {
|
||||
($left, $right) = ($right, $left);
|
||||
}
|
||||
my @result;
|
||||
for my $i (0..2) {
|
||||
for my $j (0..2) {
|
||||
my $accum = 0;
|
||||
for my $k (0..2) {
|
||||
$accum += $left->[3*$i + $k] * $right->[3*$k + $j];
|
||||
}
|
||||
$result[3*$i+$j] = $accum;
|
||||
}
|
||||
}
|
||||
return bless \@result, __PACKAGE__;
|
||||
}
|
||||
else {
|
||||
croak "multiply by array ref or number";
|
||||
}
|
||||
}
|
||||
elsif (defined $right && looks_like_number($right)) {
|
||||
my @result = map $_ * $right, @$left;
|
||||
|
||||
return bless \@result, __PACKAGE__;
|
||||
}
|
||||
else {
|
||||
# something we don't handle
|
||||
croak "multiply by array ref or number";
|
||||
}
|
||||
}
|
||||
|
||||
=item _add()
|
||||
|
||||
Implements the overloaded binary '+' operator.
|
||||
|
||||
Currently both the left and right sides of the operator must be
|
||||
Imager::Matrix2d objects.
|
||||
|
||||
=cut
|
||||
sub _add {
|
||||
my ($left, $right, $order) = @_;
|
||||
|
||||
if (ref($right) && UNIVERSAL::isa($right, __PACKAGE__)) {
|
||||
my @result;
|
||||
for (0..8) {
|
||||
push @result, $left->[$_] + $right->[$_];
|
||||
}
|
||||
|
||||
return bless \@result, __PACKAGE__;
|
||||
}
|
||||
else {
|
||||
return undef;
|
||||
}
|
||||
}
|
||||
|
||||
=item _string()
|
||||
|
||||
Implements the overloaded stringification operator.
|
||||
|
||||
This returns a string containing 3 lines of text with no terminating
|
||||
newline.
|
||||
|
||||
I tried to make it fairly nicely formatted. You might disagree :)
|
||||
|
||||
=cut
|
||||
|
||||
sub _string {
|
||||
my ($m) = @_;
|
||||
|
||||
my $maxlen = 0;
|
||||
for (@$m[0..8]) {
|
||||
if (length() > $maxlen) {
|
||||
$maxlen = length;
|
||||
}
|
||||
}
|
||||
$maxlen <= 9 or $maxlen = 9;
|
||||
|
||||
my @left = ('[ ', ' ', ' ');
|
||||
my @right = ("\n", "\n", ']');
|
||||
my $out;
|
||||
my $width = $maxlen+2;
|
||||
for my $i (0..2) {
|
||||
$out .= $left[$i];
|
||||
for my $j (0..2) {
|
||||
my $val = $m->[$i*3+$j];
|
||||
if (length $val > 9) {
|
||||
$val = sprintf("%9f", $val);
|
||||
if ($val =~ /\./ && $val !~ /e/i) {
|
||||
$val =~ s/0+$//;
|
||||
$val =~ s/\.$//;
|
||||
}
|
||||
$val =~ s/^\s//;
|
||||
}
|
||||
$out .= sprintf("%-${width}s", "$val, ");
|
||||
}
|
||||
$out =~ s/ +\Z/ /;
|
||||
$out .= $right[$i];
|
||||
}
|
||||
$out;
|
||||
}
|
||||
|
||||
=item _eq
|
||||
|
||||
Implement the overloaded equality operator.
|
||||
|
||||
Provided for older perls that don't handle magic auto generation of eq
|
||||
from "".
|
||||
|
||||
=cut
|
||||
|
||||
sub _eq {
|
||||
my ($left, $right) = @_;
|
||||
|
||||
return $left . "" eq $right . "";
|
||||
}
|
||||
|
||||
=back
|
||||
|
||||
The following functions are shortcuts to the various constructors.
|
||||
|
||||
These are not methods.
|
||||
|
||||
You can import these methods with:
|
||||
|
||||
use Imager::Matrix2d ':handy';
|
||||
|
||||
=over
|
||||
|
||||
=item m2d_identity
|
||||
|
||||
=item m2d_rotate()
|
||||
|
||||
=item m2d_translate()
|
||||
|
||||
=item m2d_shear()
|
||||
|
||||
=item m2d_reflect()
|
||||
|
||||
=item m2d_scale()
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
sub m2d_identity {
|
||||
return __PACKAGE__->identity;
|
||||
}
|
||||
|
||||
sub m2d_rotate {
|
||||
return __PACKAGE__->rotate(@_);
|
||||
}
|
||||
|
||||
sub m2d_translate {
|
||||
return __PACKAGE__->translate(@_);
|
||||
}
|
||||
|
||||
sub m2d_shear {
|
||||
return __PACKAGE__->shear(@_);
|
||||
}
|
||||
|
||||
sub m2d_reflect {
|
||||
return __PACKAGE__->reflect(@_);
|
||||
}
|
||||
|
||||
sub m2d_scale {
|
||||
return __PACKAGE__->scale(@_);
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
=head1 AUTHOR
|
||||
|
||||
Tony Cook <tony@develop-help.com>
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
Needs a way to invert a matrix.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
Imager(3), Imager::Font(3)
|
||||
|
||||
http://imager.perl.org/
|
||||
|
||||
=cut
|
||||
Reference in New Issue
Block a user