File Coverage

blib/lib/Math/PlanePath/FibonacciWordFractal.pm
Criterion Covered Total %
statement 169 216 78.2
branch 78 106 73.5
condition 12 18 66.6
subroutine 11 12 91.6
pod 3 3 100.0
total 273 355 76.9


line stmt bran cond sub pod time code
1             # Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 Kevin Ryde
2              
3             # This file is part of Math-PlanePath.
4             #
5             # Math-PlanePath is free software; you can redistribute it and/or modify
6             # it under the terms of the GNU General Public License as published by the
7             # Free Software Foundation; either version 3, or (at your option) any later
8             # version.
9             #
10             # Math-PlanePath is distributed in the hope that it will be useful, but
11             # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12             # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13             # for more details.
14             #
15             # You should have received a copy of the GNU General Public License along
16             # with Math-PlanePath. If not, see .
17              
18              
19             # http://alexis.monnerot-dumaine.neuf.fr/articles/fibonacci%20fractal.pdf
20             # [gone]
21             #
22             # math-image --path=FibonacciWordFractal --output=numbers_dash
23              
24              
25             package Math::PlanePath::FibonacciWordFractal;
26 1     1   8993 use 5.004;
  1         9  
27 1     1   7 use strict;
  1         2  
  1         24  
28              
29 1     1   4 use vars '$VERSION', '@ISA';
  1         2  
  1         64  
30             $VERSION = 127;
31 1     1   695 use Math::PlanePath;
  1         2  
  1         28  
32 1     1   404 use Math::PlanePath::Base::NSEW;
  1         2  
  1         40  
33             @ISA = ('Math::PlanePath::Base::NSEW',
34             'Math::PlanePath');
35              
36             use Math::PlanePath::Base::Generic
37 1         42 'is_infinite',
38 1     1   6 'round_nearest';
  1         2  
39              
40             # uncomment this to run the ### lines
41             #use Smart::Comments;
42              
43              
44 1     1   5 use constant n_start => 0;
  1         2  
  1         42  
45 1     1   5 use constant class_x_negative => 0;
  1         2  
  1         35  
46 1     1   5 use constant class_y_negative => 0;
  1         2  
  1         1355  
47              
48              
49             #------------------------------------------------------------------------------
50              
51             my @dir4_to_dx = (0,-1,0,1);
52             my @dir4_to_dy = (1,0,-1,0);
53              
54             my $moffset = 0;
55              
56             sub n_to_xy {
57 539     539 1 2040 my ($self, $n) = @_;
58             ### FibonacciWordFractal n_to_xy(): $n
59              
60 539 50       971 if ($n < 0) { return; }
  0         0  
61 539 50       1082 if (is_infinite($n)) { return ($n, $n); }
  0         0  
62              
63             # my $frac;
64             # {
65             # my $int = int($n);
66             # $frac = $n - $int; # inherit possible BigFloat
67             # $n = $int; # BigFloat int() gives BigInt, use that
68             # }
69             {
70 539         953 my $int = int($n);
  539         760  
71             ### $int
72             ### $n
73 539 100       1028 if ($n != $int) {
74 90         172 my ($x1,$y1) = $self->n_to_xy($int);
75 90         205 my ($x2,$y2) = $self->n_to_xy($int+1);
76 90         139 my $frac = $n - $int; # inherit possible BigFloat
77 90         146 my $dx = $x2-$x1;
78 90         127 my $dy = $y2-$y1;
79 90         299 return ($frac*$dx + $x1, $frac*$dy + $y1);
80             }
81 449         651 $n = $int; # BigFloat int() gives BigInt, use that
82             }
83              
84 449         632 my $zero = ($n * 0); # inherit bignum 0
85 449         650 my $one = $zero + 1; # inherit bignum 0
86              
87 449         804 my @f = ($one, 2+$zero);
88 449         718 my @xend = ($zero, $zero, $one); # F3 N=2 X=1,Y=1
89 449         705 my @yend = ($zero, $one, $one);
90 449         599 my $level = 2;
91 449         820 while ($f[-1] < $n) {
92 2865         4221 push @f, $f[-1] + $f[-2];
93              
94 2865         3895 my ($x,$y);
95 2865         4177 my $m = (($level+$moffset) % 6);
96 2865 100       6151 if ($m == 1) {
    100          
    100          
    100          
    100          
    50          
97 341         478 $x = $yend[-2]; # T
98 341         445 $y = $xend[-2];
99             } elsif ($m == 2) {
100 712         1023 $x = $yend[-2]; # -90
101 712         1045 $y = - $xend[-2];
102             } elsif ($m == 3) {
103 602         816 $x = $xend[-2]; # T -90
104 602         900 $y = - $yend[-2];
105              
106             } elsif ($m == 4) {
107             ### T ...
108 423         591 $x = $yend[-2]; # T
109 423         592 $y = $xend[-2];
110             } elsif ($m == 5) {
111 406         645 $x = - $yend[-2]; # +90
112 406         556 $y = $xend[-2];
113             } elsif ($m == 0) {
114 381         559 $x = - $xend[-2]; # T +90
115 381         494 $y = $yend[-2];
116             }
117              
118 2865         4217 push @xend, $xend[-1] + $x;
119 2865         4183 push @yend, $yend[-1] + $y;
120 2865         5206 $level++;
121             ### new: ($level%6)." add $x,$y for $xend[-1],$yend[-1] for $f[-1]"
122             }
123              
124 449         601 my $x = $zero;
125 449         597 my $y = $zero;
126 449         628 my $rot = 0;
127 449         601 my $transpose = 0;
128              
129 449         888 while (@xend > 1) {
130             ### at: "$x,$y rot=$rot transpose=$transpose level=$level n=$n consider f=$f[-1]"
131 3763         5286 my $xo = pop @xend;
132 3763         5125 my $yo = pop @yend;
133              
134 3763 100       6437 if ($n >= $f[-1]) {
135 1183         1782 my $m = (($level+$moffset) % 6);
136 1183         1605 $n -= $f[-1];
137             ### offset: "$xo, $yo for levelmod=$m"
138              
139 1183 100       2006 if ($transpose) {
140 412         676 ($xo,$yo) = ($yo,$xo);
141             }
142 1183 100       2087 if ($rot & 2) {
143 475         648 $xo = -$xo;
144 475         651 $yo = -$yo;
145             }
146 1183 100       2023 if ($rot & 1) {
147 475         784 ($xo,$yo) = (-$yo,$xo);
148             }
149             ### apply rot to offset: "$xo, $yo"
150              
151 1183         1990 $x += $xo;
152 1183         1549 $y += $yo;
153 1183 100       2435 if ($m == 1) { # F7 N=13 etc
    100          
    100          
    100          
    100          
154 302         464 $transpose ^= 3; # T
155             } elsif ($m == 2) { # F8 N=21 etc
156             # -90
157 210 100       334 if ($transpose) {
158 50         73 $rot++;
159             } else {
160 160         241 $rot--; # -90
161             }
162             } elsif ($m == 3) { # F3 N=2 etc
163             # T -90
164 300 100       513 if ($transpose) {
165 60         105 $rot++;
166             } else {
167 240         320 $rot--; # -90
168             }
169 300         438 $transpose ^= 3;
170              
171             } elsif ($m == 4) { # F4 N=3 etc
172 126         213 $transpose ^= 3; # T
173             } elsif ($m == 5) { # F5 N=5 etc
174             # +90
175 125 100       207 if ($transpose) {
176 50         68 $rot--;
177             } else {
178 75         102 $rot++; # +90
179             }
180             } else { # ($m == 0) # F6 N=8 etc
181             # T +90
182 120 100       192 if ($transpose) {
183 40         72 $rot--;
184             } else {
185 80         107 $rot++; # +90
186             }
187 120         176 $transpose ^= 3;
188             }
189             }
190 3763         4931 pop @f;
191 3763         6607 $level--;
192             }
193              
194             # mod 6 twist ?
195             # ### final rot: "$rot transpose=$transpose gives ".(($rot^$transpose)&3)
196             # $rot = ($rot ^ $transpose) & 3;
197             # $x = $frac * $dir4_to_dx[$rot] + $x;
198             # $y = $frac * $dir4_to_dy[$rot] + $y;
199              
200             ### final with frac: "$x,$y"
201 449         1026 return ($x,$y);
202             }
203              
204             sub xy_to_n {
205 625     625 1 2891 my ($self, $x, $y) = @_;
206             ### FibonacciWordFractal xy_to_n(): "$x, $y"
207              
208 625         1242 $x = round_nearest($x);
209 625 50       1239 if (is_infinite($x)) {
210 0         0 return $x;
211             }
212              
213 625         1362 $y = round_nearest($y);
214 625 50       1152 if (is_infinite($y)) {
215 0         0 return $y;
216             }
217              
218 625         1103 my $zero = ($x * 0 * $y); # inherit bignum 0
219 625         953 my $one = $zero + 1; # inherit bignum 0
220              
221 625         1081 my @f = ($one, $zero+2);
222 625         980 my @xend = ($zero, $one); # F3 N=2 X=1,Y=1
223 625         906 my @yend = ($one, $one);
224 625         862 my $level = 3;
225              
226 625         857 for (;;) {
227 4067         5504 my ($xo,$yo);
228 4067         5820 my $m = ($level % 6);
229 4067 100       8756 if ($m == 2) {
    100          
    100          
    100          
    100          
    50          
230 453         650 $xo = $yend[-2]; # T
231 453         598 $yo = $xend[-2];
232             } elsif ($m == 3) {
233 1003         1412 $xo = $yend[-2]; # -90
234 1003         1437 $yo = - $xend[-2];
235             } elsif ($m == 4) {
236 780         1016 $xo = $xend[-2]; # T -90
237 780         1157 $yo = - $yend[-2];
238              
239             } elsif ($m == 5) {
240             ### T
241 732         998 $xo = $yend[-2]; # T
242 732         1048 $yo = $xend[-2];
243             } elsif ($m == 0) {
244 630         898 $xo = - $yend[-2]; # +90
245 630         889 $yo = $xend[-2];
246             } elsif ($m == 1) {
247 469         672 $xo = - $xend[-2]; # T +90
248 469         626 $yo = $yend[-2];
249             }
250              
251 4067         5451 $xo += $xend[-1];
252 4067         5273 $yo += $yend[-1];
253 4067 100 100     9455 last if ($xo > $x && $yo > $y);
254              
255 3442         5062 push @f, $f[-1] + $f[-2];
256 3442         4696 push @xend, $xo;
257 3442         4685 push @yend, $yo;
258 3442         4807 $level++;
259             ### new: "level=$level $xend[-1],$yend[-1] for N=$f[-1]"
260             }
261              
262 625         839 my $n = 0;
263 625         1123 while ($level >= 2) {
264             ### at: "$x,$y n=$n level=$level consider $xend[-1],$yend[-1] for $f[-1]"
265              
266 3553         5038 my $m = (($level+$moffset) % 6);
267 3553 100       5535 if ($m >= 3) {
268             ### 3,4,5 X ...
269 1818 100       3237 if ($x >= $xend[-1]) {
270 586         819 $n += $f[-1];
271 586         798 $x -= $xend[-1];
272 586         774 $y -= $yend[-1];
273             ### shift to: "$x,$y levelmod ".$m
274              
275 586 100       1093 if ($m == 3) { # F3 N=2 etc
    100          
    50          
276 269         439 ($x,$y) = (-$y,$x); # +90
277             } elsif ($m == 4) { # F4 N=3 etc
278 157         234 $y = -$y; # +90 T
279             } elsif ($m == 5) { # F5 N=5 etc
280 160         260 ($x,$y) = ($y,$x); # T
281             }
282             ### rot to: "$x,$y"
283 586 100 100     1623 if ($x < 0 || $y < 0) {
284 118         296 return undef;
285             }
286             }
287             } else {
288             ### 0,1,2 Y ...
289 1735 100       3064 if ($y >= $yend[-1]) {
290 828         1075 $n += $f[-1];
291 828         1165 $x -= $xend[-1];
292 828         1158 $y -= $yend[-1];
293             ### shift to: "$x,$y levelmod ".$m
294              
295 828 100       1661 if ($m == 0) { # F6 N=8 etc
    100          
    50          
296 368         604 ($x,$y) = ($y,-$x); # -90
297             } elsif ($m == 1) { # F7 N=13 etc
298 76         110 $x = -$x; # -90 T
299             } elsif ($m == 2) { # F8 N=21 etc, incl F2 N=1
300 384         654 ($x,$y) = ($y,$x); # T
301             }
302             ### rot to: "$x,$y"
303 828 100 100     2253 if ($x < 0 || $y < 0) {
304 222         570 return undef;
305             }
306             }
307             }
308              
309 3213         4165 pop @f;
310 3213         4322 pop @xend;
311 3213         4177 pop @yend;
312 3213         5641 $level--;
313             }
314              
315 285 100 100     650 if ($x != 0 || $y != 0) {
316 196         439 return undef;
317             }
318 89         193 return $n;
319             }
320              
321             # not exact
322             sub rect_to_n_range {
323 0     0 1   my ($self, $x1,$y1, $x2,$y2) = @_;
324             ### FibonacciWordFractal rect_to_n_range(): "$x1,$y1 $x2,$y2"
325              
326 0           $x1 = round_nearest ($x1);
327 0           $y1 = round_nearest ($y1);
328 0           $x2 = round_nearest ($x2);
329 0           $y2 = round_nearest ($y2);
330 0 0         ($x1,$x2) = ($x2,$x1) if $x1 > $x2;
331 0 0         ($y1,$y2) = ($y2,$y1) if $y1 > $y2;
332             ### rect_to_n_range(): "$x1,$y1 to $x2,$y2"
333              
334 0 0 0       if ($x2 < 0 || $y2 < 0) {
335 0           return (1, 0);
336             }
337 0           foreach ($x1,$x2,$y1,$y2) {
338 0 0         if (is_infinite($_)) { return (0, $_); }
  0            
339             }
340              
341 0           my $zero = ($x1 * 0 * $y1 * $x2 * $y2); # inherit bignum 0
342 0           my $one = $zero + 1; # inherit bignum 0
343              
344 0           my $f0 = 1;
345 0           my $f1 = 2;
346 0           my $xend0 = $zero;
347 0           my $xend1 = $one;
348 0           my $yend0 = $one;
349 0           my $yend1 = $one;
350 0           my $level = 3;
351              
352 0           for (;;) {
353 0           my ($xo,$yo);
354 0           my $m = (($level+$moffset) % 6);
355 0 0         if ($m == 3) { # at F3 N=2 etc
    0          
    0          
    0          
    0          
356 0           $xo = $yend0; # -90
357 0           $yo = - $xend0;
358             } elsif ($m == 4) { # at F4 N=3 etc
359 0           $xo = $xend0; # T -90
360 0           $yo = - $yend0;
361              
362             } elsif ($m == 5) { # at F5 N=5 etc
363 0           $xo = $yend0; # T
364 0           $yo = $xend0;
365             } elsif ($m == 0) { # at F6 N=8 etc
366 0           $xo = - $yend0; # +90
367 0           $yo = $xend0;
368             } elsif ($m == 1) { # at F7 N=13 etc
369 0           $xo = - $xend0; # T +90
370 0           $yo = $yend0;
371             } else { # if ($m == 2) { # at F8 N=21 etc
372 0           $xo = $yend0; # T
373 0           $yo = $xend0;
374             }
375              
376 0           ($f1,$f0) = ($f1+$f0,$f1);
377 0           ($xend1,$xend0) = ($xend1+$xo,$xend1);
378 0           ($yend1,$yend0) = ($yend1+$yo,$yend1);
379 0           $level++;
380              
381             ### consider: "f1=$f1 xy end $xend1,$yend1"
382 0 0 0       if ($xend1 > $x2 && $yend1 > $y2) {
383 0           return (0, $f1 - 1);
384             }
385             }
386             }
387              
388             #------------------------------------------------------------------------------
389              
390             # ENHANCE-ME: is this good?
391             #
392             # Points which are on alternate X and Y axes
393             # n=0
394             # 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144
395             # ^ ^ ^ ^
396             # 0 4 20 88
397             # F(2+3*level)
398             # or F(2+6*level) to go X axis each time
399             #
400             # sub level_to_n_range {
401             # my ($self, $level) = @_;
402             # return (0, F(2+3*$level)-1);
403             # }
404              
405             1;
406             __END__