Skip to content

Commit

Permalink
[rt #99959] fix Imager::Matrix2d::rotate()'s centre point hanling
Browse files Browse the repository at this point in the history
also add a compose() method and add notes on the order of multiplcation
vs composition of transformations.
  • Loading branch information
tonycoz committed Nov 15, 2014
1 parent f17a0a6 commit f480f88
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 5 deletions.
71 changes: 68 additions & 3 deletions lib/Imager/Matrix2d.pm
Expand Up @@ -4,7 +4,7 @@ use vars qw($VERSION);
use Scalar::Util qw(reftype looks_like_number);
use Carp qw(croak);

$VERSION = "1.011";
$VERSION = "1.012";

=head1 NAME
Expand Down Expand Up @@ -103,9 +103,9 @@ sub rotate {
if ($opts{'x'} || $opts{'y'}) {
$opts{'x'} ||= 0;
$opts{'y'} ||= 0;
return $class->translate('x'=>-$opts{'x'}, 'y'=>-$opts{'y'})
return $class->translate('x'=>$opts{'x'}, 'y'=>$opts{'y'})
* $class->rotate(radians=>$angle)
* $class->translate('x'=>$opts{'x'}, 'y'=>$opts{'y'});
* $class->translate('x'=>-$opts{'x'}, 'y'=>-$opts{'y'});
}
else {
my $sin = sin($angle);
Expand Down Expand Up @@ -259,13 +259,78 @@ sub matrix {
}
}

=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 {
Expand Down
13 changes: 11 additions & 2 deletions t/900-util/050-matrix.t
@@ -1,7 +1,8 @@
#!perl -w
use strict;
use Test::More tests => 23;
use Test::More tests => 25;
use Imager;
use constant EPSILON => 0.000001;

BEGIN { use_ok('Imager::Matrix2d', ':handy') }

Expand Down Expand Up @@ -105,9 +106,17 @@ is(Imager->errstr, "9 coefficients required", "check error");
ok($died, "mult by bad scalar died");
like($@, qr/multiply by array ref or number/, "check message");
}

}

{ # rt #99959 Imager::Matrix2d->rotate about (x, y) bug
my $rm = Imager::Matrix2d->rotate(degrees => 180, x => 10, y => 5);
my ($rx, $ry) = $rm->transform(0, 0);
ok(abs($rx - 20) < EPSILON, "x from rotate (0,0) around (10, 5)")
or print "# x = $rx\n";
ok(abs($ry - 10) < EPSILON, "y from rotate (0,0) around (10, 5)")
or print "# y = $ry\n";

}

sub almost_equal {
my ($m1, $m2) = @_;
Expand Down

0 comments on commit f480f88

Please sign in to comment.