File Coverage

blib/lib/Transform/Canvas.pm
Criterion Covered Total %
statement 100 117 85.4
branch 30 70 42.8
condition 1 3 33.3
subroutine 18 20 90.0
pod 15 15 100.0
total 164 225 72.8


line stmt bran cond sub pod time code
1             package Transform::Canvas;
2              
3 1     1   20929 use 5.006;
  1         4  
  1         33  
4 1     1   4 use strict;
  1         3  
  1         31  
5 1     1   4 use warnings;
  1         6  
  1         27  
6 1     1   5 use Carp;
  1         1  
  1         1556  
7              
8             # Items to export into callers namespace by default. Note: do not export
9             # names by default without a very good reason. Use EXPORT_OK instead.
10             # Do not simply export all your public functions/methods/constants.
11              
12             # This allows declaration use Transform::Canvas ':all';
13             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
14             # will save memory.
15              
16              
17             =head1 NAME
18              
19             Transform::Canvas - Perl extension for performing Coordinate transformation
20             operations from the cartesion to the traditional drawing-model canvas coordinate systems.
21              
22             =head2 VERSION
23              
24             VERSION 0.14 15 Febuary, 2009
25              
26             =cut
27              
28             our $VERSION = 0.15;
29              
30             =head1 SYNOPSIS
31              
32             use Transform::Canvas;
33             # create a mapping transform for data from
34             #x=-100,y=-100,x=100,y=100 to x=10,y=10,x=100,y=100
35             $t = Transform::Canvas->new(canvas=>[10,10,100,100],data=>[-100,-100,100,100]);
36             # create a arrays of x and y values
37             $r_x = [-100,-10, 0, 20, 40, 60, 80, 100];
38             $r_y = [-100,-10, 0, 20, 40, 60, 80, 100];
39             #map the two arrays into the canvas data space
40             ($pr_x,$pr_y) = $t->map($r_x,$r_y);
41              
42             =head1 DESCRIPTION
43              
44             Transform::Canvas is a module which automates reference-frame transformations beween two cartesian coordinate systems. it is specifically intended to be used as a facilitator for coordinate-system transformation procedures between the traditional, right-hand-rule coordinate system used in mathematics graphing and the visual-arts coordinate system with a y-axis pointing down.
45              
46             The module allows for arbitrary 2-D transform mappings.
47              
48             =head1 Methods
49              
50             =head2 new
51              
52             Module constructor.
53              
54             #there are two ways to invoke this module
55             #one-step constructo
56             $t = Transform::Canvas->new (canvas => [x0 y0 x1 y1], data=>[x0 y0 x1 y1])
57             # or two-step connstructor
58             $t-> Transform::Canvas->new ();
59             $t->prepareMap (canvas => [x0 y0 x1 y1], data=>[x0 y0 x1 y1])
60              
61             generate the conversion object through which all data points will be passed.
62             NB: svg drawings use the painter's model and use a coordinate system which
63             starts at the top, left corner of the document and has x-axis increasing to
64             the right and y-axis increasing down.
65              
66             In certain drawings, the y-axis is inverted compared to mathematical
67             representation systems which prefer y to increase in the upwards direction.
68              
69             canvas (target):
70             x0 = paper-space minimum x value
71             y0 = paper-space maximum x value
72             x1 = paper-space minimum y value
73             y1 = paper-space maximum y value
74             data (source):
75             x0 = data-space minimum x value
76             y0 = data--space maximum x value
77             x1 = data-space minimum y value
78             y1 = data-space maximum y value
79              
80             =cut
81              
82             sub new ($;@) {
83 1     1 1 19 my ( $proto, %attrs ) = @_;
84 1   33     10 my $class = ref $proto || $proto;
85 1         2 my $self;
86 1         4 $self->{_config_} = {};
87              
88             #define the mappings
89 1 50       5 if (%attrs) {
90 1         2 $self->{_config_} = \%attrs;
91              
92 1         5 confess("Mising canvas data")
93 1 50       3 unless scalar( @{ $self->{_config_}->{canvas} } ) == 4;
94 1         5 confess("Mising data data")
95 1 50       3 unless scalar( @{ $self->{_config_}->{data} } ) == 4;
96              
97             # establish defaults for unspecified attributes
98 1         3 bless $self, $class;
99 1 50       4 $self->_initialize()
100             || croak("Failed to initialize Transform::Canvas object");
101 1 50       6 $self->prepareMap() || croak("Failed to prepare transformation map");
102             }
103 1         9 return $self;
104             }
105              
106             sub _initialize ($) {
107 1     1   8 my $self = shift;
108             }
109              
110             =head2 prepareMap hash %args
111              
112             Prepare the transformation space for the conversions;
113             Currently only handles linear transformations, but this is a perfect candidate
114             for non-spacial, non-cartesian transforms...
115              
116             =cut
117              
118             sub prepareMap ($;@) {
119 1     1 1 3 my $self = shift;
120 1         3 my %args = @_;
121              
122 1 50       4 if (%args) {
123 0         0 $self->{_config_} = \%args;
124             }
125              
126             #scale factors
127              
128             #flip
129             #scale
130             #translate (?)
131 1         5 my $sy = ( $self->cy1 - $self->cy0 ) / ( $self->dy1 - $self->dy0 ); #ok
132 1         4 my $sx = ( $self->cx1 - $self->cx0 ) / ( $self->dx1 - $self->dx0 ); #ok
133              
134             #translation factors
135 1         3 my $tx = $self->cx0;
136 1         4 my $ty = $self->cy0;
137              
138 1         11 $self->{map} = {
139             x => {
140             s => $sx,
141             t => $tx,
142             },
143             y => {
144             s => $sy,
145             t => $ty,
146             },
147             };
148              
149             }
150              
151             # helper methods which return or set the corners of the canvas and data windows
152              
153             =head2 sub cx0 [string $value]
154              
155             set and/or return the canvas x min value
156              
157             =head2 sub cx1 [string $value]
158              
159             set and/or return the canvas x max value
160              
161             =head2 sub cy0 [string $value]
162              
163             set and/or return return the canvas y min value
164              
165             =head2 sub cy1 [string $value]
166              
167             set and/or return the canvas y max value
168              
169             =head2 sub dx0 [string $value]
170              
171             set and/or return the data space x min value
172              
173             =head2 sub dx1 [string $value]
174              
175             set and/or return the data space x max value
176              
177             =head2 sub dy0 [string $value]
178              
179             set and/or return the data space y min value
180              
181             =head2 sub dy1 [string $value]
182              
183             set and/or return the data space y max value.
184              
185             =cut
186              
187             sub cx0 ($;$) {
188 2     2 1 4 my $self = shift;
189 2         3 my $val = shift;
190 2 50       7 $self->{_config_}->{canvas}->[0] = $val if defined $val;
191 2 50       14 confess("canvas min x value not set")
192             unless defined $self->{_config_}->{canvas}->[0];
193              
194 2         9 return $self->{_config_}->{canvas}->[0];
195             }
196              
197             sub cx1 ($;$) {
198 1     1 1 3 my $self = shift;
199 1         2 my $val = shift;
200 1 50       13 $self->{_config_}->{canvas}->[2] = $val if defined $val;
201 1 50       5 confess("canvas max x value not set")
202             unless defined $self->{_config_}->{canvas}->[2];
203 1         6 return $self->{_config_}->{canvas}->[2];
204             }
205              
206             sub dx0 ($;$) {
207 12     12 1 15 my $self = shift;
208 12         16 my $val = shift;
209 12 50       50 $self->{_config_}->{data}->[0] = $val if defined $val;
210 12 50       34 confess("data min x value not set")
211             unless defined $self->{_config_}->{data}->[0];
212 12         53 return $self->{_config_}->{data}->[0];
213             }
214              
215             sub dx1 ($;$) {
216 1     1 1 3 my $self = shift;
217 1         3 my $val = shift;
218 1 50       4 $self->{_config_}->{data}->[2] = $val if defined $val;
219 1 50       5 confess("data max x value not set")
220             unless defined $self->{_config_}->{data}->[2];
221 1         6 return $self->{_config_}->{data}->[2];
222             }
223              
224             sub cy0 ($;$) {
225 2     2 1 3 my $self = shift;
226 2         3 my $val = shift;
227 2 50       7 $self->{_config_}->{canvas}->[1] = $val if defined $val;
228 2 50       7 confess("canvas min y value not set")
229             unless defined $self->{_config_}->{canvas}->[1];
230 2         10 return $self->{_config_}->{canvas}->[1];
231             }
232              
233             sub cy1 ($;$) {
234 1     1 1 2 my $self = shift;
235 1         2 my $val = shift;
236 1 50       4 $self->{_config_}->{canvas}->[3] = $val if defined $val;
237 1 50       8 confess("canvas max y value not set")
238             unless defined $self->{_config_}->{canvas}->[3];
239 1         7 return $self->{_config_}->{canvas}->[3];
240             }
241              
242             sub dy0 ($;$) {
243 1     1 1 3 my $self = shift;
244 1         1 my $val = shift;
245 1 50       4 $self->{_config_}->{data}->[1] = $val if defined $val;
246 1 50       5 confess("datamin y value not set")
247             unless defined $self->{_config_}->{data}->[1];
248 1         5 return $self->{_config_}->{data}->[1];
249             }
250              
251             sub dy1 ($;$) {
252 12     12 1 16 my $self = shift;
253 12         15 my $val = shift;
254 12 50       25 $self->{_config_}->{data}->[3] = $val if defined $val;
255 12 50       36 confess("data max y value not set")
256             unless defined $self->{_config_}->{data}->[3];
257 12         47 return $self->{_config_}->{data}->[3];
258             }
259              
260             =head2 map($x,$y)
261              
262             Map an array or a value from the (x,y) data axes to the (x,y) canvas axes
263              
264             =cut
265              
266             sub map ($$$) {
267 1     1 1 2 my $self = shift;
268 1         3 my $x = shift;
269 1         1 my $y = shift;
270 1 50       8 croak "map error: x is undefined" unless defined $x;
271 1 50       5 croak "map error: y is undefined" unless defined $y;
272              
273             #be flexible about single values or array refs
274 1 50       19 $x = [$x] unless ref($x) eq 'ARRAY';
275 1 50       5 $y = [$y] unless ref($y) eq 'ARRAY';
276 1 50       4 croak "Error: x and y arrays different lengths"
277             unless ( scalar @$x == scalar @$y );
278              
279 11         25 my @p_x = map {
280 1         3 ( ( $_ - $self->dx0 ) * $self->{map}->{x}->{s} ) +
281             $self->{map}->{x}->{t}
282             } @$x;
283 11         22 my @p_y = map {
284 1         3 ( ( $self->dy1 - $_ ) * $self->{map}->{y}->{s} ) +
285             $self->{map}->{y}->{t}
286             } @$y;
287              
288 1         7 return ( \@p_x, \@p_y );
289             }
290              
291             =head2 mapX
292              
293             Map an array or a value of the x data axis to the x canvas axis
294              
295             =cut
296              
297             sub mapX ($$) {
298 0     0 1 0 my $self = shift;
299 0         0 my $x = shift;
300 0 0       0 croak "x is undefined" unless defined $x;
301              
302             #be flexible about single values or array refs
303 0 0       0 $x = [$x] unless ref($x) eq 'ARRAY';
304              
305 0         0 my @p_x = map {
306 0         0 ( ( $_ - $self->dx0 ) * $self->{map}->{x}->{s} ) +
307             $self->{map}->{x}->{t}
308             } @$x;
309 0 0       0 return $p_x[0] if scalar @p_x == 1;
310 0         0 return ( \@p_x );
311             }
312              
313             =head2 mapY
314              
315             Map an array or a value of the y data axis to the y canvas axis
316              
317             =cut
318              
319             sub mapY ($$) {
320 0     0 1 0 my $self = shift;
321 0         0 my $y = shift;
322 0 0       0 croak "y is undefined" unless defined $y;
323              
324             #be flexible about single values or array refs
325 0 0       0 $y = [$y] unless ref($y) eq 'ARRAY';
326 0         0 my @p_y = map {
327 0         0 ( ( $self->dy1 - $_ ) * $self->{map}->{y}->{s} ) +
328             $self->{map}->{y}->{t}
329             } @$y;
330 0 0       0 return $p_y[0] if scalar @p_y == 1;
331 0         0 return ( \@p_y );
332             }
333              
334             =head2 Max
335              
336             Find th of an array
337              
338             my $x = $t->Max([1,2,3,4,5]);
339              
340             This utility needed a home and this seems like a convenient place to stick it
341              
342             =cut
343              
344             #subs Max, Min from:
345             #https://lists.dulug.duke.edu/pipermail/dulug/2001-March/009326.html
346              
347             sub Max {
348              
349 1     1 1 1239 my $self = shift;
350              
351             # takes an array ref - returns the max
352              
353 1         2 my $list = shift;
354 1         2 my $max = $list->[0];
355              
356             #foreach (@$list) {
357 1 100       3 map { $max = $_ if ( $_ > $max ) } @$list;
  11         38  
358              
359             #}
360              
361 1         6 return ($max);
362             }
363              
364             =head2 Min
365              
366             Find th of an array
367              
368             my $x = $t->Max([1,2,3,4,5]);
369              
370              
371             =cut
372              
373             sub Min {
374              
375             # takes an array ref - returns the min
376 1     1 1 2 my $self = shift;
377 1         3 my $list = shift;
378 1         2 my $min = $list->[0];
379              
380             #foreach (@$list) {
381 1 50       2 map { $min = $_ if ( $_ < $min ) } @$list;
  6         16  
382              
383             #$min = $_ if ( $_ < $min );
384             #}
385              
386 1         4 return ($min);
387             }
388              
389             =head1 SEE ALSO
390              
391             SVG SVG::Parser SVG::DOM SVG::Element SVG::Graph SVG::Extension
392              
393             =head1 AUTHOR
394              
395             Ronan Oger, Eronan@cpan.comE
396              
397             =head1 COPYRIGHT AND LICENSE
398              
399             Copyright (C) 2004-2009 by Ronan Oger
400              
401             This library is free software; you can redistribute it and/or modify
402             it under the same terms as Perl itself, either Perl version 5.8.3 or,
403             at your option, any later version of Perl 5 you may have available.
404              
405              
406             =cut
407              
408             1;
409             __END__