File Coverage

blib/lib/Array/Tour/Serpentine.pm
Criterion Covered Total %
statement 70 81 86.4
branch 28 30 93.3
condition 6 9 66.6
subroutine 8 10 80.0
pod 3 3 100.0
total 115 133 86.4


line stmt bran cond sub pod time code
1             package Array::Tour::Serpentine;
2              
3 2     2   37431 use 5.008;
  2         7  
  2         81  
4 2     2   17 use strict;
  2         3  
  2         71  
5 2     2   10 use warnings;
  2         4  
  2         100  
6 2     2   2113 use integer;
  2         24  
  2         11  
7 2     2   72 use base q(Array::Tour);
  2         3  
  2         1492  
8 2     2   11 use Array::Tour qw(:directions :status);
  2         6  
  2         2630  
9              
10             our $VERSION = '0.06';
11              
12             =head1 NAME
13              
14             Array::Tour::Serpentine - Return coordinates to take a serpentine path.
15              
16             =head1 SYNOPSIS
17              
18             use Array::Tour::Serpentine qw(:directions);
19              
20             my $tour = Array::Tour::Serpentine->new(
21             dimensions => [5, 5],
22             vertical => $vertical,
23             corner_right => $corner_right,
24             corner_bottom => $corner_bottom);
25              
26             Creates the object with its attributes. The attributes B,
27             B, B, and B are inherited from L.
28             This package adds more attributes of its own, which are:
29              
30             =over 4
31              
32             =item counterclock, corner_bottom, corner_right, vertical
33              
34             I All are boolean values that affect the starting
35             point and the direction of the tour. By default, the tour is
36             generated the upper left corner in a horizontal back-and-forth path.
37              
38             See the Examples section
39             to see what effects the different combinations produce.
40              
41             =back
42              
43             =head1 PREREQUISITES
44              
45             Perl 5.8 or later. This is the version of perl under which this module
46             was developed.
47              
48             =head1 DESCRIPTION
49              
50             A simple iterator that will return the coordinates of the next cell if
51             one were to tour an array's cells in a serpentine path.
52              
53             =head2 Serpentine Object Methods
54              
55              
56             =head3 direction()
57              
58             $dir = $tour->direction()
59              
60             Return the direction we just walked.
61              
62             Overrides Array::Tour's direction() method.
63             =cut
64              
65             sub direction()
66             {
67 0     0 1 0 my $self = shift;
68 0 0       0 return ($self->{status} == STOP)? undef: ${$self->{direction}}[0];
  0         0  
69             }
70              
71             =head3 next()
72              
73             Returns a reference to an array of coordinates. Returns undef
74             if there is no next cell to visit.
75              
76             Overrides Array::Tour's next() method.
77              
78             =cut
79              
80             sub next
81             {
82 536     536 1 4011 my $self = shift;
83              
84 536 100       1153 return undef unless ($self->has_next());
85              
86             #
87             # Set up the conditions for the pacing.
88             #
89 520 100       952 if ($self->{tourstatus} == START)
90             {
91 16         24 $self->{tourstatus} = TOURING;
92 16         18 $self->{pacer} = ${$self->{pacing}}[0];
  16         77  
93             }
94             else
95             {
96             #
97             # Pace off in the current direction.
98             #
99 504         524 my $direction = ${$self->{direction}}[0];
  504         782  
100 504 100       553 ${$self->{position}}[(($direction & (North | South)) == 0)? 0: 1] +=
  504 100       1526  
101             (($direction & (North | West)) == 0)? 1: -1;
102              
103             #
104             # Will the next pace be in a different direction?
105             #
106 504 100       1455 if (--$self->{pacer} == 0)
107             {
108 168         159 $self->{pacer} = ${$self->{pacing}}[1];
  168         266  
109 168         206 ${$self->{pacing}}[0] += $self->{pacechange};
  168         256  
110            
111             #
112             # Rotate to the next pacing length and the next direction.
113             #
114 168         158 push @{$self->{pacing}}, shift @{$self->{pacing}};
  168         219  
  168         269  
115 168         183 push @{$self->{direction}}, shift @{$self->{direction}};
  168         201  
  168         316  
116             }
117             }
118              
119 520 100       1166 $self->{tourstatus} = STOP if (++$self->{odometer} == $self->{tourlength});
120 520         1347 return $self->adjusted_position();
121             }
122              
123             =head3 opposite()
124              
125             $ruot = $tour->opposite();
126              
127             Return a new object that follows the same path as the original object,
128             reversing the inward/outward direction.
129              
130              
131             =cut
132              
133             sub opposite()
134             {
135 0     0 1 0 my $self = shift;
136 0         0 my %anti_self;
137 0         0 my @dimensions = @{ $self->{dimensions} };
  0         0  
138              
139 0         0 $anti_self{dimensions} = $self->{dimensions};
140              
141 0         0 $anti_self{corner_right} ^= 1;
142 0         0 $anti_self{corner_bottom} ^= 1;
143              
144 0         0 return Array::Tour::Serpentine->new(%anti_self);
145             }
146              
147             =head3 _set()
148              
149             $self->_set(%parameters);
150              
151             Override Array::Tour's _set() method for one that can handle
152             our parameters.
153             =cut
154              
155             sub _set()
156             {
157 16     16   21 my $self = shift;
158 16         46 my(%params) = @_;
159 16         27 my($pace_x, $pace_y) = (1, 1);
160 16         20 my($start_x, $start_y) = (0, 0);
161 16         35 my @dirlist = (East, South, West, North);
162 16         20 my @dimensions = @{$self->{dimensions}};
  16         71  
163 16         21 my @direction;
164              
165 16         35 warn "Unknown paramter $_" foreach (grep{$_ !~ /vertical|corner_right|corner_bottom/} (keys %params));
  48         232  
166              
167             #
168             # Parameter checks.
169             #
170             # Set corner_right, corner_bottom, and vertical to 0/1 values.
171             #
172 16 100 66     97 my $vertical = (defined $params{vertical} and $params{vertical} != 0)? 1: 0;
173 16 100 66     69 my $corner_right = (defined $params{corner_right} and $params{corner_right} != 0)? 1: 0;
174 16 100 66     78 my $corner_bottom = (defined $params{corner_bottom} and $params{corner_bottom} != 0)? 1: 0;
175              
176 16 100       35 $pace_x = $dimensions[0] - 1 unless ($vertical);
177 16 100       35 $pace_y = $dimensions[1] - 1 if ($vertical);
178 16 100       36 $start_x = $dimensions[0] - 1 if ($corner_right);
179 16 100       27 $start_y = $dimensions[1] - 1 if ($corner_bottom);
180              
181 16         35 my $idx0 = ((($corner_bottom & $vertical)|
182             ($corner_right & ($vertical^1))) << 1) | $vertical;
183 16         62 my $idx1 = ((($corner_bottom & ($vertical^1))|
184             ($corner_right & $vertical)) << 1) | ($vertical ^ 1);
185 16         38 push @direction, @dirlist[$idx0, $idx1, $idx0 ^ 2, $idx1];
186              
187 16         25 $self->{corner_right} = $corner_right;
188 16         77 $self->{corner_bottom} = $corner_bottom;
189 16         21 $self->{vertical} = $vertical;
190 16         29 $self->{direction} = \@direction;
191 16         26 $self->{pacechange} = 0;
192 16 100       53 $self->{pacing} = (($direction[0] & (West | East)) == 0)? [$pace_y, $pace_x]: [$pace_x, $pace_y];
193 16         58 $self->{start} = [$start_x, $start_y];
194 16         41 $self->{position} = [$start_x, $start_y];
195              
196 16         98 return $self;
197             }
198              
199             1;
200             __END__