1 package Imager::Matrix2d;
4 use Scalar::Util qw(reftype looks_like_number);
11 Imager::Matrix2d - simple wrapper for matrix construction
16 $m1 = Imager::Matrix2d->identity;
17 $m2 = Imager::Matrix2d->rotate(radians=>$angle, x=>$cx, y=>$cy);
18 $m3 = Imager::Matrix2d->translate(x=>$dx, y=>$dy);
19 $m4 = Imager::Matrix2d->shear(x=>$sx, y=>$sy);
20 $m5 = Imager::Matrix2d->reflect(axis=>$axis);
21 $m6 = Imager::Matrix2d->scale(x=>$xratio, y=>$yratio);
22 $m8 = Imager::Matric2d->matrix($v11, $v12, $v13,
27 use Imager::Matrix2d qw(:handy);
28 # various m2d_* functions imported
29 # where m2d_(.*) calls Imager::Matrix2d->$1()
33 This class provides a simple wrapper around a reference to an array of
34 9 coefficients, treated as a matrix:
40 Most of the methods in this class are constructors. The others are
43 Note that since Imager represents images with y increasing from top to
44 bottom, rotation angles are clockwise, rather than counter-clockwise.
50 use vars qw(@EXPORT_OK %EXPORT_TAGS @ISA);
52 require 'Exporter.pm';
53 @EXPORT_OK = qw(m2d_rotate m2d_identity m2d_translate m2d_shear
54 m2d_reflect m2d_scale);
57 handy=> [ qw(m2d_rotate m2d_identity m2d_translate m2d_shear
58 m2d_reflect m2d_scale) ],
69 Returns the identity matrix.
74 return bless [ 1, 0, 0,
79 =item rotate(radians=>$angle)
81 =item rotate(degrees=>$angle)
83 Creates a matrix that rotates around the origin, or around the point
84 (x,y) if the 'x' and 'y' parameters are provided.
89 my ($class, %opts) = @_;
92 if (defined $opts{radians}) {
93 $angle = $opts{radians};
95 elsif (defined $opts{degrees}) {
96 $angle = $opts{degrees} * 3.1415926535 / 180;
99 $Imager::ERRSTR = "degrees or radians parameter required";
103 if ($opts{'x'} || $opts{'y'}) {
106 return $class->translate('x'=>$opts{'x'}, 'y'=>$opts{'y'})
107 * $class->rotate(radians=>$angle)
108 * $class->translate('x'=>-$opts{'x'}, 'y'=>-$opts{'y'});
111 my $sin = sin($angle);
112 my $cos = cos($angle);
113 return bless [ $cos, -$sin, 0,
119 =item translate(x=>$dx, y=>$dy)
121 =item translate(x=>$dx)
123 =item translate(y=>$dy)
125 Translates by the specify amounts.
130 my ($class, %opts) = @_;
132 if (defined $opts{'x'} || defined $opts{'y'}) {
133 my $x = $opts{'x'} || 0;
134 my $y = $opts{'y'} || 0;
135 return bless [ 1, 0, $x,
140 $Imager::ERRSTR = 'x or y parameter required';
144 =item shear(x=>$sx, y=>$sy)
150 Shear by the given amounts.
154 my ($class, %opts) = @_;
156 if (defined $opts{'x'} || defined $opts{'y'}) {
157 return bless [ 1, $opts{'x'}||0, 0,
161 $Imager::ERRSTR = 'x and y parameters required';
165 =item reflect(axis=>$axis)
167 Reflect around the given axis, either 'x' or 'y'.
169 =item reflect(radians=>$angle)
171 =item reflect(degrees=>$angle)
173 Reflect around a line drawn at the given angle from the origin.
178 my ($class, %opts) = @_;
180 if (defined $opts{axis}) {
181 my $result = $class->identity;
182 if ($opts{axis} eq "y") {
183 $result->[0] = -$result->[0];
185 elsif ($opts{axis} eq "x") {
186 $result->[4] = -$result->[4];
189 $Imager::ERRSTR = 'axis must be x or y';
196 if (defined $opts{radians}) {
197 $angle = $opts{radians};
199 elsif (defined $opts{degrees}) {
200 $angle = $opts{degrees} * 3.1415926535 / 180;
203 $Imager::ERRSTR = 'axis, degrees or radians parameter required';
208 return $class->rotate(radians=>-$angle) * $class->reflect(axis=>'x')
209 * $class->rotate(radians=>$angle);
212 =item scale(x=>$xratio, y=>$yratio)
214 Scales at the given ratios.
216 You can also specify a center for the scaling with the C<cx> and C<cy>
222 my ($class, %opts) = @_;
224 if (defined $opts{'x'} || defined $opts{'y'}) {
225 $opts{'x'} = 1 unless defined $opts{'x'};
226 $opts{'y'} = 1 unless defined $opts{'y'};
227 if ($opts{cx} || $opts{cy}) {
228 return $class->translate('x'=>-$opts{cx}, 'y'=>-$opts{cy})
229 * $class->scale('x'=>$opts{'x'}, 'y'=>$opts{'y'})
230 * $class->translate('x'=>$opts{cx}, 'y'=>$opts{cy});
233 return bless [ $opts{'x'}, 0, 0,
239 $Imager::ERRSTR = 'x or y parameter required';
244 =item matrix($v11, $v12, $v13, $v21, $v22, $v23, $v31, $v32, $v33)
246 Create a matrix with custom coefficients.
251 my ($class, @self) = @_;
254 return bless \@self, $class;
257 $Imager::ERRSTR = "9 coefficients required";
262 =item transform($x, $y)
264 Transform a point the same way matrix_transform does.
269 my ($self, $x, $y) = @_;
271 my $sz = $x * $self->[6] + $y * $self->[7] + $self->[8];
273 if (abs($sz) > 0.000001) {
274 $sx = ($x * $self->[0] + $y * $self->[1] + $self->[2]) / $sz;
275 $sy = ($x * $self->[3] + $y * $self->[4] + $self->[5]) / $sz;
284 =item compose(matrix...)
286 Compose several matrices together for use in transformation.
288 For example, for three matrices:
290 my $out = Imager::Matrix2d->compose($m1, $m2, $m3);
294 my $out = $m3 * $m2 * $m1;
296 Returns the identity matrix if no parameters are supplied.
298 May return the supplied matrix if only one matrix is supplied.
303 my ($class, @in) = @_;
306 or return $class->identity;
309 for my $m (reverse @in) {
318 Implements the overloaded '*' operator. Internal use.
320 Currently both the left and right-hand sides of the operator must be
323 When composing a matrix for transformation you should multiply the
324 matrices in the reverse order of the transformations:
326 my $shear = Imager::Matrix2d->shear(x => 0.1);
327 my $rotate = Imager::Matrix2d->rotate(degrees => 45);
328 my $shear_then_rotate = $rotate * $shear;
330 or use the compose method:
332 my $shear_then_rotate = Imager::Matrix2d->compose($shear, $rotate);
337 my ($left, $right, $order) = @_;
340 if (reftype($right) eq "ARRAY") {
342 or croak "9 elements required in array ref";
344 ($left, $right) = ($right, $left);
351 $accum += $left->[3*$i + $k] * $right->[3*$k + $j];
353 $result[3*$i+$j] = $accum;
356 return bless \@result, __PACKAGE__;
359 croak "multiply by array ref or number";
362 elsif (defined $right && looks_like_number($right)) {
363 my @result = map $_ * $right, @$left;
365 return bless \@result, __PACKAGE__;
368 # something we don't handle
369 croak "multiply by array ref or number";
375 Implements the overloaded binary '+' operator.
377 Currently both the left and right sides of the operator must be
378 Imager::Matrix2d objects.
382 my ($left, $right, $order) = @_;
384 if (ref($right) && UNIVERSAL::isa($right, __PACKAGE__)) {
387 push @result, $left->[$_] + $right->[$_];
390 return bless \@result, __PACKAGE__;
399 Implements the overloaded stringification operator.
401 This returns a string containing 3 lines of text with no terminating
404 I tried to make it fairly nicely formatted. You might disagree :)
413 if (length() > $maxlen) {
417 $maxlen <= 9 or $maxlen = 9;
419 my @left = ('[ ', ' ', ' ');
420 my @right = ("\n", "\n", ']');
422 my $width = $maxlen+2;
426 my $val = $m->[$i*3+$j];
427 if (length $val > 9) {
428 $val = sprintf("%9f", $val);
429 if ($val =~ /\./ && $val !~ /e/i) {
435 $out .= sprintf("%-${width}s", "$val, ");
445 Implement the overloaded equality operator.
447 Provided for older perls that don't handle magic auto generation of eq
453 my ($left, $right) = @_;
455 return $left . "" eq $right . "";
460 The following functions are shortcuts to the various constructors.
462 These are not methods.
464 You can import these methods with:
466 use Imager::Matrix2d ':handy';
474 =item m2d_translate()
487 return __PACKAGE__->identity;
491 return __PACKAGE__->rotate(@_);
495 return __PACKAGE__->translate(@_);
499 return __PACKAGE__->shear(@_);
503 return __PACKAGE__->reflect(@_);
507 return __PACKAGE__->scale(@_);
514 Tony Cook <tony@develop-help.com>
518 Needs a way to invert a matrix.
522 Imager(3), Imager::Font(3)
524 http://imager.perl.org/