Skip to content

Commit

Permalink
Add invert() method and tests and perform some cleanup.
Browse files Browse the repository at this point in the history
Thanks to Kevin Ryde for the invert() implementation.
  • Loading branch information
Marc Liyanage committed Jan 19, 2011
1 parent ee42a08 commit 2ac5277
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 9 deletions.
45 changes: 40 additions & 5 deletions Geometry-AffineTransform/lib/Geometry/AffineTransform.pm
@@ -1,6 +1,6 @@
package Geometry::AffineTransform;

our $VERSION = '1.3';
our $VERSION = '1.4';

use strict;
use warnings;
Expand All @@ -9,8 +9,6 @@ use Carp;
use Hash::Util;
use Math::Trig ();

# $Id$

=head1 NAME
Geometry::AffineTransform - Affine Transformation to map 2D coordinates to other 2D coordinates
Expand Down Expand Up @@ -86,7 +84,7 @@ In other words, invoking the constructor without arguments is equivalent to this
=cut

sub new {
my $self = shift @_;
my $self = shift;
my (%args) = @_;

my $class = ref($self) || $self;
Expand All @@ -112,12 +110,39 @@ Returns a clone of the instance.
=cut

sub clone {
my $self = shift @_;
my $self = shift;
return $self->new()->set_matrix_2x3($self->matrix_2x3());
}



=head2 invert
Inverts the state of the transformation.
my $inverted_clone = $t->clone()->invert();
=cut

sub invert {
my $self = shift;

my $det = $self->determinant();

croak "Unable to invert this transform (zero determinant)" unless $det;

return $self->set_matrix_2x3(
$self->{m22} / $det, # 11
-$self->{m12} / $det, # 12
-$self->{m21} / $det, # 21
$self->{m11} / $det, # 22
($self->{m21} * $self->{ty} - $self->{m22} * $self->{tx}) / $det,
($self->{m12} * $self->{tx} - $self->{m11} * $self->{ty}) / $det,
);
}



=head2 transform
Transform one or more coordinate pairs according to the current state.
Expand All @@ -127,6 +152,8 @@ representing the x and y coordinates of a point.
Returns the transformed list of coordinates in the same form as the input list.
my @output = $t->transform(2, 4, 10, 20);
=cut

sub transform {
Expand Down Expand Up @@ -275,6 +302,14 @@ sub matrix_2x3 {
return $self->{m11}, $self->{m12}, $self->{m21}, $self->{m22}, $self->{tx}, $self->{ty};
}


# returns the determinant of the matrix
sub determinant {
my $self = shift;
return $self->{m11} * $self->{m22} - $self->{m12} * $self->{m21};
}


# sets the 6 specifiable parts of the transformation matrix
sub set_matrix_2x3 {
my $self = shift;
Expand Down
51 changes: 47 additions & 4 deletions Geometry-AffineTransform/t/lib/Geometry/AffineTransform/Test.pm
Expand Up @@ -10,7 +10,7 @@ use Test::More;
use List::Util qw(max min);
use Data::Dumper;

sub identity : Test(2) {
sub identity : Test(6) {
my $self = shift;

my $t = Geometry::AffineTransform->new();
Expand All @@ -19,27 +19,70 @@ sub identity : Test(2) {
is_deeply(\@result, [10, 5], "identity transform");
@result = $t->transform(0, 0);
is_deeply(\@result, [0, 0], "identity transform");

my $inverse = $t->clone()->invert();
ok(ref($inverse), "clone()->invert()");
isnt($inverse, $t, "clone()->invert() returns different instance");

@result = $inverse->transform(10, 5);
is_deeply(\@result, [10, 5], "reverse identity transform");
@result = $inverse->transform(0, 0);
is_deeply(\@result, [0, 0], "reverse identity transform");

}


sub inverse : Test(2) {
my $self = shift;

sub rotate : Test(3) {
my $t = Geometry::AffineTransform->new();
my @result;
$t->scale(0, 1)->translate(7, 7);
@result = $t->transform(10, 5, 20, 6);
is_deeply(\@result, [7, 12, 7, 13], "to line");

eval {
$t->invert();
};

like($@, qr/^Unable to invert this transform .zero determinant./, "exception for zero determinant");
}


sub determinant : Test(2) {
my $self = shift;

my $t = Geometry::AffineTransform->new();
is($t->rotate(180), $t);
my @result;
$t->scale(10, 5);
is($t->determinant(), 50, "determinant()");

$t->scale(0, 1);
is($t->determinant(), 0, "determinant()");
}



sub rotate : Test(5) {
my $self = shift;

my $t = Geometry::AffineTransform->new();
is($t->rotate(180), $t);
my $inverse = $t->clone()->invert();

is_deeply([$t->transform(10, 5)], [-10, -5], "rotate 180");
is_deeply([$inverse->transform(-10, -5)], [10, 5], "rotate 180");

$t->rotate(90)->rotate(90);
is_deeply([$t->transform(10, 5)], [10, 5], "rotate 360");

$inverse = $t->clone()->invert();
is_deeply([$inverse->transform(10, 5)], [10, 5], "rotate 360 inverse");

}




sub matrix_2x3 : Test(4) {
my $self = shift @_;
my $t = Geometry::AffineTransform->new();
Expand Down

0 comments on commit 2ac5277

Please sign in to comment.