File Coverage

blib/lib/Math/Curve/Hilbert.pm
Criterion Covered Total %
statement 179 393 45.5
branch 22 60 36.6
condition 10 55 18.1
subroutine 9 11 81.8
pod 4 8 50.0
total 224 527 42.5


line stmt bran cond sub pod time code
1             # Hibert.pm Perl Implementation of Hilberts space filling Curve
2             package Math::Curve::Hilbert;
3              
4              
5             =head1 NAME
6              
7             Math::Curve::Hilbert - Perl Implementation of Hilberts space filling Curve
8              
9             =head1 SYNOPSIS
10              
11             use Math::Curve::Hilbert;
12              
13             # get object representing 8x8 curve with a step of 10 (i.e. draw 80x80 pixels)
14             my $hilbert = Math::Curve::Hilbert->new( direction=>'up', max=>3, clockwise=>1, step=>10);
15              
16             # get a point from coordinates
17             my $point = $hilbert->PointFromCoordinates(20,60);
18              
19             # get coordinates from a point
20             my ($x,$y) = $hilbert->CoordinatesFromPoint($point);
21              
22              
23             # get range(s) from box
24             my @ranges = $hilbert->RangeFromCoordinates($x1,$y1,$x2,$y2);
25              
26             #
27             # draw image representing curve
28              
29             use GD;
30             # create a new image
31             my $im = new GD::Image(300,300);
32             my $black = $im->colorAllocate(0,0,0);
33             my $blue = $im->colorAllocate(0,0,255);
34              
35             my $count = 0;
36             my ($x1,$y1) = $hilbert->CoordinatesFromPoint($count++);
37             while ( ($hilbert->CoordinatesFromPoint($count))[0] ) {
38             my ($x2,$y2) = $hilbert->CoordinatesFromPoint($count++);
39             $im->line($x1,$y1,$x2,$y2,$black);
40             ($x1,$y1) = ($x2,$y2);
41             }
42              
43             =head1 DESCRIPTION
44              
45             The Hilbert Curve module provides some useful functions using Hilberts Space-filling Curve. This is handy for things like Dithering, Flattening n-dimensional data, fractals - all kind of things really.
46              
47             "A Space Filling Curve is a special fractal curve which has the following basic characteristics:
48             ­ it covers completely an area, a volume or a hyper-volume in a 2-d, 3-d or N-d space respectively,
49             ­ each point is visited once and only once (the curve does not cross itself), and
50             ­ neighbor points in the native space are likely to be neighbors in the space filling curve."
51             definition from Multiple Range Query Optimization in Spatial Databases, Apostolos N. Papadopoulos and Yannis Manolopoulos
52              
53             Other space filling curves include The Peano and Morton or Z-order curves. There is also the Hilbert II curve which has an 'S' shape rather than a 'U' shape. The Hilbert curve can also be applied to 3 dimensions, but this module only supports 2 dimensions.
54              
55             Like most space filling curves, the area must be divided into 2 to the power of N parts, such as 8, 16, 32, etc
56              
57             =head2 EXPORT
58              
59             None by default.
60              
61             =cut
62              
63 1     1   13396 use strict;
  1         2  
  1         40  
64              
65 1     1   1311 use Data::Dumper;
  1         13804  
  1         90  
66              
67 1     1   10 use vars qw(@ISA $VERSION);
  1         7  
  1         5261  
68             $VERSION = '0.04';
69              
70             =head1 METHODS
71              
72             =head2 new
73              
74             # get object representing 8x8 curve with a step of 10 (i.e. draw 80x80 pixels)
75             my $hilbert = Math::Curve::Hilbert->new( direction=>'up', max=>3, clockwise=>1, step=>10);
76              
77             direction specifies which direction the curve follows :
78              
79             up (clockwise) : up, right, down
80             down (clockwise ) : down, right, up
81             left (clockwise) : left, up, right
82             right (clockwise) : right, down, left
83              
84             clockwise specifies if the curve moves clockwise or anti-clockwise, the default is clockwise
85              
86             max specifies the size of the grid to plot in powers of 2 - max=>2 would be a 4x4 grid, max=>4 would be 16 x 16 grid
87              
88             step specifies how large a step should be (used in drawing the curve), the default is 1
89              
90             X and Y allow you to specify a starting X and Y coordinate by passing a reference to a the value
91              
92             =cut
93              
94              
95             sub new {
96 1     1 1 91 my ($class,%options) = @_;
97 1 50       6 $options{clockwise} = 1 unless (defined $options{clockwise});
98 1   50     3 $options{step} ||= 1;
99 1   50     6 $options{level} ||= 0;
100 1   33     12 my $self = bless({%options},ref $class || $class);
101 1         6 my $maxsize = (2 ** $options{max}) * $options{step};
102 1         2 my $minsize = $options{step};
103 1         2 my $X = $options{X};
104 1         3 my $Y = $options{Y};
105             DIRECTION: {
106 1 50       2 if (lc$options{direction} =~ m/up/) {
  1         7  
107 0 0 0     0 $X ||= ( $options{clockwise} ) ? $minsize : $maxsize ;
108 0   0     0 $Y ||= $maxsize;
109 0         0 $options{X} = \$X;
110 0         0 $options{Y} = \$Y ;
111 0         0 $self->{coords} = $self->up(%options), last;
112             }
113 1 50       7 if (lc$options{direction} =~ m/down/) {
114 0 0 0     0 $X ||= ( $options{clockwise} ) ? $maxsize: $minsize ;
115 0   0     0 $Y ||= $minsize ;
116 0         0 $options{X} = \$X;
117 0         0 $options{Y} = \$Y ;
118 0         0 $self->{coords} = $self->down(%options), last;
119             }
120 1 50       5 if (lc$options{direction} =~ m/left/) {
121 0   0     0 $X ||= $maxsize;
122 0 0 0     0 $Y ||= ( $options{clockwise} ) ? $maxsize : $minsize ;
123 0         0 $options{X} = \$X;
124 0         0 $options{Y} = \$Y ;
125 0         0 $self->{coords} = $self->left(%options), last;
126             }
127 1 50       11 if (lc$options{direction} =~ m/right/) {
128 1   33     8 $X ||= $minsize;
129 1 50 33     10 $Y ||= ( $options{clockwise} ) ? $minsize : $maxsize ;
130 1         3 $options{X} = \$X;
131 1         3 $options{Y} = \$Y ;
132 1         7 $self->{coords} = $self->right(%options), last;
133             }
134             }; # end of DIRECTION
135 1         8 return $self;
136             }
137              
138             =head2 PointFromCoordinates
139              
140             my $point = $hilbert->PointFromCoordinates(20,60);
141              
142             =cut
143              
144             sub PointFromCoordinates {
145 4     4 1 64 my ($self,$x,$y) = @_;
146 4         11 my $point = $self->{curve}{"$x:$y"};
147 4         15 return $point;
148             }
149              
150             =head2 CoordinatesFromPoint
151              
152             my ($x1,$y1) = $hilbert->CoordinatesFromPoint($point);
153              
154             =cut
155              
156             sub CoordinatesFromPoint {
157 0     0 1 0 my ($self,$point) = @_;
158 0         0 return ($self->{coords}[$point]{X},$self->{coords}[$point]{Y});
159             }
160              
161             =head2 RangeFromCoordinates
162              
163             # get range(s) from box
164             my @ranges = $hilbert->RangeFromCoordinates($x1,$y1,$x2,$y2);
165              
166             =cut
167              
168             sub RangeFromCoordinates {
169 0     0 1 0 my ($self,$x1,$y1,$x2,$y2) = @_;
170              
171             # get point from top left coordinate
172 0         0 my $startpoint;
173             my $nextpoint;
174 0         0 my %rangepoints;
175 0         0 my @ranges;
176              
177 0         0 my ($xx,$yy) = ($x1,$y1);
178 0   0     0 while ( ($xx <= $x2) && ($yy <= $y2) ) {
179 0         0 $startpoint = $self->{curve}{"$xx:$yy"};
180 0 0       0 unless (defined $rangepoints{$startpoint}) {
181 0         0 push (@ranges,$startpoint);
182 0         0 $rangepoints{$startpoint} = $#ranges;
183 0         0 $nextpoint = $startpoint;
184 0         0 my $ok = 1;
185 0         0 while ( $ok == 1 ) {
186 0         0 $startpoint++;
187 0         0 my ($x,$y) = ($self->{coords}[$startpoint]{X},$self->{coords}[$startpoint]{Y});
188 0 0 0     0 if ($x <= $x2 && $y <= $y2 && $x >= $x1 && $y >= $y1) {
      0        
      0        
189 0 0       0 if ($rangepoints{$startpoint}) {
190 0         0 $ranges[$rangepoints{$startpoint}] = $nextpoint;
191 0         0 pop(@ranges);
192 0         0 last;
193             } else {
194 0         0 $rangepoints{$startpoint} = $#ranges;
195 0         0 $nextpoint = $startpoint;
196             }
197             } else {
198 0         0 push (@ranges,$nextpoint);
199 0         0 $rangepoints{$startpoint} = 0;
200 0         0 $ok = 0;
201             }
202             }
203             }
204 0 0       0 if ($xx == $x2) {
205 0 0       0 if ( $yy < $y2) { $yy++; $xx = $x1; }
  0         0  
  0         0  
206 0         0 else { last; }
207             } else {
208 0         0 $xx++;
209             }
210             }
211 0         0 return @ranges;
212              
213             }
214              
215             ################################################################################
216              
217             sub up {
218 5     5 0 7 my $self = shift;
219 5         20 my %args = @_;
220 5         6 my $coords = [];
221 5         12 my $this_level = $args{level} + 1;
222 5         7 my ($x,$y) = ($args{X}, $args{Y});
223 5   33     24 my $step = $args{step} || $self->{step};
224             # warn "up : x : $$x, y : $$y, step : $step, level : $this_level\n";
225 5 50       12 if ($this_level == 1) {
226 0         0 push (@$coords,{X=>$$x,Y=>$$y});
227 0         0 $self->{curve}{"$$x:$$y"} = $#$coords;
228             }
229 5 50       10 if ($args{clockwise}) {
230 0 0       0 if ($args{max} == $this_level) {
231 0         0 $$y -= $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
232 0         0 $self->{curve}{"$$x:$$y"} = $#$coords;
233 0         0 $$x += $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
234 0         0 $self->{curve}{"$$x:$$y"} = $#$coords;
235 0         0 $$y += $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
236 0         0 $self->{curve}{"$$x:$$y"} = $#$coords;
237             } else {
238 0         0 foreach (@{$self->right(X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  0         0  
239 0         0 push (@$coords,$_);
240 0         0 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
241             }
242 0         0 $$y -= $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
243 0         0 $self->{curve}{"$$x:$$y"} = $#$coords;
244 0         0 foreach (@{$self->up(clockwise=>1,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  0         0  
245 0         0 push (@$coords,$_);
246 0         0 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
247             }
248 0         0 $$x += $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
249 0         0 $self->{curve}{"$$x:$$y"} = $#$coords;
250 0         0 foreach (@{$self->up(clockwise=>1,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  0         0  
251 0         0 push (@$coords,$_);
252 0         0 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
253             }
254 0         0 $$y += $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
255 0         0 $self->{curve}{"$$x:$$y"} = $#$coords;
256 0         0 foreach (@{$self->left(X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  0         0  
257 0         0 push (@$coords,$_);
258 0         0 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
259             }
260             }
261             } else {
262 5 100       12 if ($args{max} == $this_level) {
263 4         5 $$y -= $step; push (@$coords,{X=>$$x,Y=>$$y});
  4         15  
264 4         24 $self->{curve}{"$$x:$$y"} = $#$coords;
265 4         4 $$x -= $step; push (@$coords,{X=>$$x,Y=>$$y});
  4         17  
266 4         588 $self->{curve}{"$$x:$$y"} = $#$coords;
267 4         7 $$y += $step; push (@$coords,{X=>$$x,Y=>$$y});
  4         19  
268 4         16 $self->{curve}{"$$x:$$y"} = $#$coords;
269             } else {
270 1         3 foreach (@{$self->left(clockwise=>1,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  1         4  
271 3         5 push (@$coords,$_);
272 3         11 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
273             }
274 1         2 $$y -= $step; push (@$coords,{X=>$$x,Y=>$$y});
  1         3  
275 1         4 $self->{curve}{"$$x:$$y"} = $#$coords + 1;
276 1         2 foreach (@{$self->up(clockwise=>0,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  1         13  
277 3         5 push (@$coords,$_);
278 3         10 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
279             }
280 1         2 $$x -= $step; push (@$coords,{X=>$$x,Y=>$$y});
  1         3  
281 1         5 $self->{curve}{"$$x:$$y"} = $#$coords + 1;
282 1         2 foreach (@{$self->up(clockwise=>0,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  1         4  
283 3         6 push (@$coords,$_);
284 3         9 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
285             }
286 1         3 $$y += $step; push (@$coords,{X=>$$x,Y=>$$y});
  1         3  
287 1         4 $self->{curve}{"$$x:$$y"} = $#$coords;
288 1         1 foreach (@{$self->right(clockwise=>1,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  1         4  
289 3         4 push (@$coords,$_);
290 3         11 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
291             }
292             }
293             }
294 5         20 return $coords;
295             }
296              
297              
298             sub left {
299 2     2 0 8 my $self = shift;
300 2         8 my %args = @_;
301 2         3 my $coords = [];
302 2         5 my $this_level = $args{level} + 1;
303 2         9 my ($x,$y) = ($args{X}, $args{Y});
304 2   33     13 my $step = $args{step} || $self->{step};
305             # warn "left : x : $$x, y : $$y, step : $step, level : $this_level\n";
306 2 50       6 if ($this_level == 1) {
307 0         0 push (@$coords,{X=>$$x,Y=>$$y});
308 0         0 $self->{curve}{"$$x:$$y"} = $#$coords;
309             }
310 2 50       6 if ($args{clockwise}) {
311 2 50       6 if ($args{max} == $this_level) {
312 2         8 $$x -= $step; push (@$coords,{X=>$$x,Y=>$$y});
  2         7  
313 2         7 $self->{curve}{"$$x:$$y"} = $#$coords;
314 2         4 $$y -= $step; push (@$coords,{X=>$$x,Y=>$$y});
  2         7  
315 2         8 $self->{curve}{"$$x:$$y"} = $#$coords;
316 2         7 $$x += $step; push (@$coords,{X=>$$x,Y=>$$y});
  2         6  
317 2         16 $self->{curve}{"$$x:$$y"} = $#$coords;
318             } else {
319 0         0 foreach (@{$self->up(clockwise=>0,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  0         0  
320 0         0 push (@$coords,$_);
321 0         0 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
322             }
323 0         0 $$x -= $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
324 0         0 $self->{curve}{"$$x:$$y"} = $#$coords;
325 0         0 foreach (@{$self->left(clockwise=>1,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  0         0  
326 0         0 push (@$coords,$_);
327 0         0 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
328             }
329 0         0 $$y -= $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
330 0         0 $self->{curve}{"$$x:$$y"} = $#$coords;
331 0         0 foreach (@{$self->left(clockwise=>1,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  0         0  
332 0         0 push (@$coords,$_);
333 0         0 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
334             }
335 0         0 $$x += $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
336 0         0 $self->{curve}{"$$x:$$y"} = $#$coords;
337 0         0 foreach (@{$self->down(clockwise=>0,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  0         0  
338 0         0 push (@$coords,$_);
339 0         0 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
340             }
341             }
342             } else {
343 0 0       0 if ($args{max} == $this_level) {
344 0         0 $$x -= $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
345 0         0 $self->{curve}{"$$x:$$y"} = $#$coords;
346 0         0 $$y += $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
347 0         0 $self->{curve}{"$$x:$$y"} = $#$coords;
348 0         0 $$x += $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
349 0         0 $self->{curve}{"$$x:$$y"} = $#$coords;
350             } else {
351 0         0 foreach (@{$self->down(clockwise=>1,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  0         0  
352 0         0 push (@$coords,$_);
353 0         0 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
354             }
355 0         0 $$x -= $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
356 0         0 $self->{curve}{"$$x:$$y"} = $#$coords;
357 0         0 foreach (@{$self->left(clockwise=>0,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  0         0  
358 0         0 push (@$coords,$_);
359 0         0 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
360             }
361 0         0 $$y += $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
362 0         0 $self->{curve}{"$$x:$$y"} = $#$coords;
363 0         0 foreach (@{$self->left(clockwise=>0,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  0         0  
364 0         0 push (@$coords,$_);
365 0         0 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
366             }
367 0         0 $$x += $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
368 0         0 $self->{curve}{"$$x:$$y"} = $#$coords;
369 0         0 foreach (@{$self->up(clockwise=>1,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  0         0  
370 0         0 push (@$coords,$_);
371 0         0 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
372             }
373             }
374             }
375 2         8 return $coords;
376             }
377              
378             sub right {
379 9     9 0 17 my $self = shift;
380 9         33 my %args = @_;
381 9         14 my $coords = [];
382 9         19 my $this_level = $args{level} + 1;
383 9         16 my ($x,$y) = ($args{X}, $args{Y});
384 9   66     40 my $step = $args{step} || $self->{step};
385             # warn "right : x : $$x, y : $$y, step : $step, level : $this_level\n";
386 9 100       18 if ($this_level == 1) {
387 1         4 push (@$coords,{X=>$$x,Y=>$$y});
388 1         11 $self->{curve}{"$$x:$$y"} = $#$coords;
389             }
390 9 50       30 if ($args{clockwise}) {
391 9 100       19 if ($args{max} == $this_level) {
392 6         8 $$x += $step; push (@$coords,{X=>$$x,Y=>$$y});
  6         21  
393 6         25 $self->{curve}{"$$x:$$y"} = $#$coords;
394 6         8 $$y += $step; push (@$coords,{X=>$$x,Y=>$$y});
  6         25  
395 6         20 $self->{curve}{"$$x:$$y"} = $#$coords;
396 6         9 $$x -= $step; push (@$coords,{X=>$$x,Y=>$$y});
  6         26  
397 6         21 $self->{curve}{"$$x:$$y"} = $#$coords;
398             } else {
399 3         5 foreach (@{$self->down(clockwise=>0,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  3         15  
400 21         38 push (@$coords,$_);
401 21         69 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
402             }
403 3         7 $$x += $step; push (@$coords,{X=>$$x,Y=>$$y});
  3         11  
404 3         5 foreach (@{$self->right(clockwise=>1,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  3         28  
405 21         27 push (@$coords,$_);
406 21         59 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
407             }
408 3         6 $$y += $step; push (@$coords,{X=>$$x,Y=>$$y});
  3         16  
409 3         5 foreach (@{$self->right(clockwise=>1,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  3         9  
410 21         32 push (@$coords,$_);
411 21         55 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
412             }
413 3         6 $$x -= $step; push (@$coords,{X=>$$x,Y=>$$y});
  3         9  
414 3         6 foreach (@{$self->up(clockwise=>0,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  3         13  
415 21         26 push (@$coords,$_);
416 21         62 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
417             }
418             }
419             } else {
420 0 0       0 if ($args{max} == $this_level) {
421 0         0 $$x += $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
422 0         0 $self->{curve}{"$$x:$$y"} = $#$coords;
423 0         0 $$y -= $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
424 0         0 $self->{curve}{"$$x:$$y"} = $#$coords;
425 0         0 $$x -= $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
426 0         0 $self->{curve}{"$$x:$$y"} = $#$coords;
427             } else {
428 0         0 foreach (@{$self->up(clockwise=>1,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  0         0  
429 0         0 push (@$coords,$_);
430 0         0 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
431             }
432 0         0 $$x += $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
433 0         0 foreach (@{$self->right(clockwise=>0,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  0         0  
434 0         0 push (@$coords,$_);
435 0         0 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
436             }
437 0         0 $$y -= $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
438 0         0 foreach (@{$self->right(clockwise=>0,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  0         0  
439 0         0 push (@$coords,$_);
440 0         0 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
441             }
442 0         0 $$x -= $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
443 0         0 foreach (@{$self->down(clockwise=>1,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  0         0  
444 0         0 push (@$coords,$_);
445 0         0 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
446             }
447             }
448             }
449 9         37 return $coords;
450             }
451              
452             sub down {
453 5     5 0 8 my $self = shift;
454 5         20 my %args = @_;
455 5         7 my $coords = [];
456 5         11 my $this_level = $args{level} + 1;
457 5         9 my ($x,$y) = ($args{X}, $args{Y});
458 5   33     23 my $step = $args{step} || $self->{step};
459             # warn "down : x : $$x, y : $$y, step : $step, level : $this_level\n";
460 5 50       19 if ($this_level == 1) {
461 0         0 push (@$coords,{X=>$$x,Y=>$$y});
462 0         0 $self->{curve}{"$$x:$$y"} = $#$coords;
463             }
464 5 50       10 if ($args{clockwise}) {
465 0 0       0 if ($args{max} == $this_level) {
466 0         0 $$y += $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
467 0         0 $self->{curve}{"$$x:$$y"} = $#$coords;
468 0         0 $$x -= $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
469 0         0 $self->{curve}{"$$x:$$y"} = $#$coords;
470 0         0 $$y -= $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
471 0         0 $self->{curve}{"$$x:$$y"} = $#$coords;
472             } else {
473 0         0 foreach (@{$self->left(clockwise=>0,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  0         0  
474 0         0 push (@$coords,$_);
475 0         0 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
476             }
477 0         0 $$y += $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
478 0         0 foreach (@{$self->down(clockwise=>1,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  0         0  
479 0         0 push (@$coords,$_);
480 0         0 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
481             }
482 0         0 $$x -= $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
483 0         0 foreach (@{$self->down(clockwise=>1,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  0         0  
484 0         0 push (@$coords,$_);
485 0         0 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
486             }
487 0         0 $$y -= $step; push (@$coords,{X=>$$x,Y=>$$y});
  0         0  
488 0         0 foreach (@{$self->right(clockwise=>0,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  0         0  
489 0         0 push (@$coords,$_);
490 0         0 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
491             }
492             }
493             } else {
494 5 100       12 if ($args{max} == $this_level) {
495 4         22 $$y += $step; push (@$coords,{X=>$$x,Y=>$$y});
  4         13  
496 4         17 $self->{curve}{"$$x:$$y"} = $#$coords;
497 4         6 $$x += $step; push (@$coords,{X=>$$x,Y=>$$y});
  4         12  
498 4         13 $self->{curve}{"$$x:$$y"} = $#$coords;
499 4         5 $$y -= $step; push (@$coords,{X=>$$x,Y=>$$y});
  4         12  
500 4         16 $self->{curve}{"$$x:$$y"} = $#$coords;
501             } else {
502 1         2 foreach (@{$self->right(clockwise=>1,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  1         17  
503 3         7 push (@$coords,$_);
504 3         18 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
505             }
506 1         3 $$y += $step; push (@$coords,{X=>$$x,Y=>$$y});
  1         5  
507 1         2 foreach (@{$self->down(clockwise=>0,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  1         16  
508 3         7 push (@$coords,$_);
509 3         11 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
510             }
511 1         3 $$x += $step; push (@$coords,{X=>$$x,Y=>$$y});
  1         6  
512 1         2 foreach (@{$self->down(clockwise=>0,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  1         4  
513 3         5 push (@$coords,$_);
514 3         25 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
515             }
516 1         2 $$y -= $step; push (@$coords,{X=>$$x,Y=>$$y});
  1         4  
517 1         1 foreach (@{$self->left(clockwise=>1,X=>$x,Y=>$y,level=>$this_level,max=>$args{max})}) {
  1         6  
518 3         5 push (@$coords,$_);
519 3         12 $self->{curve}{"$_->{X}:$_->{Y}"} = $#$coords;
520             }
521             }
522             }
523 5         20 return $coords;
524             }
525              
526              
527             1;
528              
529             __END__