File Coverage

blib/lib/Math/Fractal/Noisemaker.pm
Criterion Covered Total %
statement 1456 1979 73.5
branch 341 590 57.8
condition 174 346 50.2
subroutine 88 110 80.0
pod 48 81 59.2
total 2107 3106 67.8


line stmt bran cond sub pod time code
1             package Math::Fractal::Noisemaker;
2              
3             our $VERSION = '0.105';
4              
5 1     1   1245 use strict;
  1         1  
  1         32  
6 1     1   5 use warnings;
  1         1  
  1         29  
7              
8 1     1   1387 use Imager;
  1         51238  
  1         8  
9 1     1   86289 use Math::Trig qw| :radial deg2rad tan pi |;
  1         20658  
  1         207  
10 1     1   1040 use Tie::CArray;
  1         6886  
  1         42  
11              
12 1     1   8 use base qw| Exporter |;
  1         3  
  1         4421  
13              
14             our $COLUMN_CLASS = "Tie::CDoubleArray";
15              
16             our @SIMPLE_TYPES = qw|
17             white wavelet gradient square gel sgel stars spirals dla worley wgel
18             fflame mandel dmandel buddha fern gasket julia djulia newton
19             infile intile moire textile sparkle canvas simplex simplex2
20             |;
21              
22             our @PERLIN_TYPES = qw|
23             multires ridged block pgel fur tesla lumber wormhole flux
24             |;
25              
26             our @NOISE_TYPES = ( @SIMPLE_TYPES, @PERLIN_TYPES, qw| terra | );
27              
28             our @EXPORT_OK = "make";
29              
30             # there used to be more stuff here, but i'll leave the :all tag
31             our %EXPORT_TAGS = (
32             'all' => \@EXPORT_OK,
33             );
34              
35             our $DEFAULT_FORMAT = "bmp";
36             our $DEFAULT_AMP = .5;
37             our $DEFAULT_BIAS = .5;
38             our $DEFAULT_LEN = 256;
39             our $DEFAULT_TYPE = 'multires';
40             our $DEFAULT_SLICE_TYPE = 'white';
41             our $DEFAULT_TERRAIN_BASE = 'multires';
42             our $DEFAULT_TERRAIN_SLICE = 'ridged';
43             our $DEFAULT_GAP = 0;
44             our $DEFAULT_FREQ = 4;
45             our $DEFAULT_OCTAVES = 8;
46             our $DEFAULT_PERSIST = .5;
47             our $DEFAULT_DISPLACEMENT = 1;
48             our $DEFAULT_INTERP = 1;
49             our $DEFAULT_RHO = 1;
50              
51             our $QUIET;
52              
53             my $MAX_COLOR = 255;
54              
55             my $INTERP_FN;
56             my $GROW_FN;
57              
58             #
59             # Persistent gradient values
60             #
61             my @NUMS = ( -255 .. 255 );
62             do {
63             my @r;
64             while (@NUMS) {
65             my $i = rand(@NUMS);
66             push @r, $NUMS[$i];
67             splice( @NUMS, $i, 1 );
68             }
69             @NUMS = @r;
70             };
71              
72             sub showVersion {
73 0     0 0 0 print "Math::Fractal::Noisemaker $VERSION\n";
74             }
75              
76             sub showTypes {
77 0     0 0 0 showVersion();
78              
79 0         0 print "\n";
80 0         0 print "All noise types have optional args, see -h.\n";
81 0         0 print "\n";
82 0         0 print "Noise Types:\n";
83 0         0 print "\n";
84 0         0 print " * white ## pseudo-random values\n";
85 0         0 print " * wavelet ## band-limited ortho\n";
86 0         0 print " * gradient ## persistent gradient noise\n";
87 0         0 print " * simplex ## continuous gradient noise\n";
88 0         0 print " * simplex2 ## interpolated simplex\n";
89 0         0 print " * square ## diamond-square algorithm\n";
90 0         0 print " * gel ## self-displaced smooth\n";
91 0         0 print " * sgel ## self-displaced diamond-square\n";
92 0         0 print " * dmandel ## \"deep\" mandelbrot\n";
93 0         0 print " * djulia ## \"deep\" julia\n";
94 0         0 print " * dla ## diffusion-limited aggregation\n";
95 0         0 print " * worley ## voronoi cell noise\n";
96 0         0 print " * wgel ## self-displaced cell noise\n";
97 0         0 print "\n";
98 0         0 print " ! multires ## multi-resolution\n";
99 0         0 print " ! ridged ## ridged multifractal\n";
100 0         0 print " ! block ## unsmoothed multi-res\n";
101 0         0 print " ! pgel ## self-displaced multi-res\n";
102 0         0 print " ! fur ## inspired by \"Perlin Worms\"\n";
103 0         0 print " ! tesla ## worms/fur variant\n";
104 0         0 print "\n";
105 0         0 print "Legend:";
106 0         0 print "\n";
107 0         0 print " * single-res type\n";
108 0         0 print " ! multi-res type - use 'stype' arg to change basis func\n";
109 0         0 print "\n";
110 0         0 print "For even more types(!), see:\n";
111 0         0 print " $0 -h moretypes\n";
112 0         0 print "\n";
113 0         0 print "perldoc Math::Fractal::Noisemaker for more help, or see:\n";
114 0         0 print
115             " http://search.cpan.org/~aayars/Math-Fractal-Noisemaker/lib/Math/Fractal/Noisemaker.pm\n";
116 0         0 print "\n";
117              
118 0         0 exit 1;
119             }
120              
121             sub showMoreTypes {
122 0     0 0 0 showVersion();
123 0         0 print "\n";
124 0         0 print "Additional types:\n";
125 0         0 print "\n";
126              
127 0         0 print " * mandel ## Mandelbrot (demo)\n";
128 0         0 print " * buddha ## buddhabrot\n";
129 0         0 print " * julia ## Julia set\n";
130 0         0 print " * newton ## Newton fractal (demo)\n";
131 0         0 print " * fflame ## IFS fractal flame\n";
132 0         0 print " * fern ## IFS fern (demo)\n";
133 0         0 print " * gasket ## IFS gasket (demo)\n";
134 0         0 print " * stars ## starfield\n";
135 0         0 print " * spirals ## tiny logspirals\n";
136 0         0 print " * moire ## interference patterns\n";
137 0         0 print " * textile ## random high-freq moire\n";
138 0         0 print " * infile ## image file named by 'in' arg\n";
139 0         0 print " * intile ## infile + blend seams\n";
140 0         0 print " * sparkle ## stylized stars\n";
141 0         0 print " * canvas ## like an old map\n";
142 0         0 print "\n";
143 0         0 print " ! lumber ## vaguely woodlike\n";
144 0         0 print " ! wormhole ## field flow\n";
145 0         0 print " ! flux ## extruded contours\n";
146 0         0 print " ! terra ## terrain recipe (see -h more)\n";
147 0         0 print "\n";
148              
149 0         0 exit 1;
150             }
151              
152             sub usage {
153 0     0 0 0 showVersion();
154              
155 0         0 print "\n";
156 0         0 print "All command line args are optional.\n";
157 0         0 print "\n";
158 0         0 print "Usage:\n";
159 0         0 print "$0 \\\n";
160 0         0 print " [-type ] \\ ## noise type\n";
161 0         0 print " [-stype ]\\ ## multi-res slice type\n";
162 0         0 print " [-amp ] \\ ## base amplitude (eg .5)\n";
163 0         0 print " [-freq ] \\ ## base frequency (eg 2)\n";
164 0         0 print " [-len ] \\ ## side length (eg 256)\n";
165 0         0 print " [-bias ] \\ ## value bias (0..1)\n";
166 0         0 print " [-qual <0|1|2|3>]* \\ ## quality (draft|linear|cosine|gaussian)\n";
167 0         0 print " [-octaves ] \\ ## multi-res octaves (eg 4)\n";
168 0         0 print " [-refract <0|1>] \\ ## refractive grayscale palette\n";
169 0         0 print " [-sphere <0|1>] \\ ## fake spheremap\n";
170 0         0 print " [-displace ] \\ ## self-displacement (eg .25)\n";
171 0         0 print " [-clut ] \\ ## color lookup table (ex.bmp)\n";
172 0         0 print " [-clutdir 0|1|2] \\ ## clut direction diagonal|vertical|fractal\n";
173 0         0 print " [-in ] \\ ## input filename for infile (infile.bmp)\n";
174 0         0 print " [-shadow <0..1>] \\ ## false shadow/highlight amount (rec. .5)\n";
175 0         0 print " [-nth ] \\ ## worley: Nth closest neighbor (0-index)\n";
176 0         0 print " [-dist <0|1|2|3>] \\ ## worley: euclid|manhat|cheby|? (0|1|2|3)\n";
177 0         0 print " [-cell <0|1>] \\ ## worley: render as distance|cell (0|1)\n";
178 0         0 print " [-tile 0|1|2|3] \\ ## force tiling (off|both|horiz|vert)\n";
179 0         0 print " [-format ] \\ ## file type (default bmp)\n";
180 0         0 print " [-outdir ] \\ ## output dir (eg \"mynoise/\")\n";
181 0         0 print " [-quiet <0|1>] \\ ## no STDOUT spam\n";
182 0         0 print " [-out ] ## Output file (foo.bmp)\n";
183 0         0 print "\n";
184 0         0 print "* Add a plus (+) to quality arg to use non-upsampled noise, eg:\n";
185 0         0 print " make-noise -quality 1+\n";
186 0         0 print "\n";
187 0         0 print "For more options, see:\n";
188 0         0 print " $0 -h more\n";
189 0         0 print "\n";
190 0         0 print "For a list of available noise types, see:\n";
191 0         0 print " $0 -h types\n";
192 0         0 print "\n";
193 0         0 print "perldoc Math::Fractal::Noisemaker for more help.\n";
194 0         0 print "\n";
195              
196 0         0 my $warning = shift;
197 0 0       0 print "$warning\n" if $warning;
198              
199 0         0 exit 1;
200             }
201              
202             sub moreUsage {
203 0     0 0 0 showVersion();
204              
205 0         0 print "\n";
206 0         0 print "Additional options:\n";
207 0         0 print "$0 \\\n";
208 0         0 print " [-persist ] \\ ## multi-res persistence (eg .5)\n";
209 0         0 print " [-gap ] \\ ## stars: gappiness (0..1)\n";
210 0         0 print " [-smooth <0|1>] \\ ## resampling off|on (default: on)\n";
211 0         0 print " [-interp <0|1>] \\ ## interp fn linear|cosine\n";
212 0         0 print " [-grow <0|1>] \\ ## growth fn interp|gaussian\n";
213 0         0 print " [-limit 0|1] \\ ## scale|clip pixel values\n";
214 0         0 print " [-zoom ] \\ ## fractals: scale magnitude\n";
215 0         0 print " [-maxiter ] \\ ## fractals: iteration limit\n";
216 0         0 print " [-emboss <0|1>] \\ ## output shadow only (no|yes)\n";
217 0         0 print " [-zshift <-1..1>] \\ ## final z offset for ridged\n";
218 0         0 print " [-delta 0|1] \\ ## output as difference noise\n";
219 0         0 print " [-chiral 0|1] \\ ## output as additive noise\n";
220 0         0 print " [-stereo 0|1] \\ ## output as stereogram\n";
221 0         0 print "\n";
222 0         0 print "'terra' options:\n";
223 0         0 print "\n";
224 0         0 print " [-lbase ] \\ ## terra continent shape\n";
225 0         0 print " [-ltype ] \\ ## terra multi-res type\n";
226 0         0 print " [-feather ] \\ ## terra feather amt (0..255)\n";
227 0         0 print " [-layers ] \\ ## terra layers (eg 3)\n";
228 0         0 print "\n";
229              
230 0         0 my $warning = shift;
231 0 0       0 print "$warning\n" if $warning;
232              
233 0         0 exit 1;
234             }
235              
236             sub make {
237 39     39 1 31668 my %args;
238              
239 39         210 while ( my $arg = shift ) {
240 196 50       724 if ( $arg =~ /(-h$|help)/ ) {
241 0 0 0     0 if ( $_[0] && lc( $_[0] ) eq 'types' ) {
    0 0        
    0 0        
242 0         0 showTypes();
243             } elsif ( $_[0] && lc( $_[0] ) eq 'moretypes' ) {
244 0         0 showMoreTypes();
245             } elsif ( $_[0] && lc( $_[0] ) eq 'more' ) {
246 0         0 moreUsage();
247             } else {
248 0         0 usage();
249             }
250             }
251              
252 196 100       4811 if ( $arg =~ /(^|-)type/ ) { $args{type} = shift; }
  39 50       206  
    50          
    50          
    50          
    50          
    100          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    100          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    100          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    100          
    50          
    50          
253 0         0 elsif ( $arg =~ /stype/ ) { $args{stype} = shift; }
254 0         0 elsif ( $arg =~ /lbase/ ) { $args{lbase} = shift; }
255 0         0 elsif ( $arg =~ /ltype/ ) { $args{ltype} = shift; }
256 0         0 elsif ( $arg =~ /amp/ ) { $args{amp} = shift; }
257 0         0 elsif ( $arg =~ /freq/ ) { $args{freq} = shift; }
258 39         150 elsif ( $arg =~ /len/ ) { $args{len} = shift; }
259 0         0 elsif ( $arg =~ /octaves/ ) { $args{octaves} = shift; }
260 0         0 elsif ( $arg =~ /bias/ ) { $args{bias} = shift; }
261 0         0 elsif ( $arg =~ /persist/ ) { $args{persist} = shift; }
262 0         0 elsif ( $arg =~ /qual/ ) { $args{quality} = shift; }
263 0         0 elsif ( $arg =~ /interp$/ ) { $args{interp} = shift; }
264 0         0 elsif ( $arg =~ /grow$/ ) { $args{grow} = shift; }
265 0         0 elsif ( $arg =~ /gap/ ) { $args{gap} = shift; }
266 0         0 elsif ( $arg =~ /feather/ ) { $args{feather} = shift; }
267 0         0 elsif ( $arg =~ /layers/ ) { $args{layers} = shift; }
268 0         0 elsif ( $arg =~ /smooth/ ) { $args{smooth} = shift; }
269 1         5 elsif ( $arg =~ /(^|-)out$/ ) { $args{out} = shift; }
270 0         0 elsif ( $arg =~ /sphere/ ) { $args{sphere} = shift; }
271 0         0 elsif ( $arg =~ /refract/ ) { $args{refract} = shift; }
272 0         0 elsif ( $arg =~ /displace/ ) { $args{displace} = shift; }
273 0         0 elsif ( $arg =~ /clut$/ ) { $args{clut} = shift; }
274 0         0 elsif ( $arg =~ /clutdir$/ ) { $args{clutdir} = shift; }
275 0 0       0 elsif ( $arg =~ /limit/ ) { $args{auto} = shift() ? 0 : 1; }
276 0         0 elsif ( $arg =~ /zoom/ ) { $args{zoom} = shift; }
277 0         0 elsif ( $arg =~ /maxiter/ ) { $args{maxiter} = shift; }
278 0         0 elsif ( $arg =~ /shadow/ ) { $args{shadow} = shift; }
279 0         0 elsif ( $arg =~ /emboss/ ) { $args{emboss} = shift; }
280 39         180 elsif ( $arg =~ /(^|-)in$/ ) { $args{in} = shift; }
281 0         0 elsif ( $arg =~ /zshift/ ) { $args{zshift} = shift; }
282 0         0 elsif ( $arg =~ /nth/ ) { $args{nth} = shift; }
283 0         0 elsif ( $arg =~ /cell/ ) { $args{cell} = shift; }
284 0         0 elsif ( $arg =~ /dist/ ) { $args{dist} = shift; }
285 0         0 elsif ( $arg =~ /delta/ ) { $args{delta} = shift; }
286 0         0 elsif ( $arg =~ /chiral/ ) { $args{chiral} = shift; }
287 0         0 elsif ( $arg =~ /stereo/ ) { $args{stereo} = shift; }
288 0         0 elsif ( $arg =~ /tile/ ) { $args{tile} = shift; }
289 0         0 elsif ( $arg =~ /xscale/ ) { $args{xscale} = shift; }
290 0         0 elsif ( $arg =~ /yscale/ ) { $args{yscale} = shift; }
291 39         146 elsif ( $arg =~ /quiet/ ) { $QUIET = shift; }
292 0         0 elsif ( $arg =~ /format/ ) { $args{format} = shift; }
293 39         166 elsif ( $arg =~ /outdir/ ) { $args{outdir} = shift; }
294 0         0 else { usage("Unknown argument: $arg") }
295             }
296              
297 39 50 33     168 usage("Specified CLUT file not found") if $args{clut} && !-e $args{clut};
298              
299 39         76 my $q = $args{quality};
300 39         84 $args{upsample} = 1;
301              
302 39 50       113 if ( defined $q ) {
303 0 0       0 $args{upsample} = 0 if $q =~ s/\+$//;
304              
305 0 0       0 if ( $q == 0 ) {
    0          
    0          
    0          
306 0 0       0 $args{smooth} = 0 if !defined $args{smooth};
307 0 0       0 $args{interp} = 0 if !defined $args{interp};
308 0 0       0 $args{grow} = 0 if !defined $args{grow};
309             } elsif ( $q == 1 ) {
310 0 0       0 $args{smooth} = 1 if !defined $args{smooth};
311 0 0       0 $args{interp} = 0 if !defined $args{interp};
312 0 0       0 $args{grow} = 0 if !defined $args{grow};
313             } elsif ( $q == 2 ) {
314 0 0       0 $args{smooth} = 1 if !defined $args{smooth};
315 0 0       0 $args{interp} = 1 if !defined $args{interp};
316 0 0       0 $args{grow} = 0 if !defined $args{grow};
317             } elsif ( $q == 3 ) {
318 0 0       0 $args{smooth} = 1 if !defined $args{smooth};
319 0 0       0 $args{interp} = 1 if !defined $args{interp};
320 0 0       0 $args{grow} = 1 if !defined $args{grow};
321             }
322             }
323              
324             #
325             #
326             #
327 39   33     122 $args{type} ||= $DEFAULT_TYPE;
328 39   33     266 $args{stype} ||= $DEFAULT_SLICE_TYPE;
329 39   33     205 $args{lbase} ||= $DEFAULT_TERRAIN_BASE;
330 39   33     204 $args{ltype} ||= $DEFAULT_TERRAIN_SLICE;
331              
332             #
333             #
334             #
335 39 50       219 if ( !defined $args{interp} ) {
336 39         124 $args{interp} = $DEFAULT_INTERP;
337             }
338              
339 39 50       160 $INTERP_FN = $args{interp} ? \&cosine_interp : \&lerp;
340              
341 39 50       110 if ( $args{grow} ) {
342 0         0 $GROW_FN = \&grow_gaussian;
343             } else {
344 39         114 $GROW_FN = \&grow_interp;
345             }
346              
347 39 50       134 if ( !defined $args{smooth} ) {
348 39         95 $args{smooth} = 1;
349             }
350              
351             #
352             #
353             #
354 39 50 33     145 if ( $args{shadow} && $args{emboss} ) {
355 0         0 delete $args{shadow};
356             }
357              
358 39 50 33     382 if (
    50 66        
      33        
      66        
359             ( $args{type} eq 'terra' )
360             && ( ( $args{lbase} =~ /[prs]gel/ )
361             || ( $args{ltype} =~ /[prs]gel/ )
362             || $args{stype} =~ /[prs]gel/ )
363             )
364             {
365 0   0     0 $args{freq} ||= 2;
366 0   0     0 $args{displace} ||= .125;
367             } elsif (
368             ( $args{type} eq 'terra' )
369             && ( ( $args{lbase} eq 'gel' )
370             || ( $args{ltype} eq 'gel' )
371             || $args{stype} eq 'gel' )
372             )
373             {
374 0   0     0 $args{freq} ||= 4;
375 0   0     0 $args{displace} ||= .5;
376             }
377              
378 39   33     166 my $format = $args{format} || $DEFAULT_FORMAT;
379              
380 39 50       850 if ( !$Imager::formats{$format} ) {
381 0         0 my $formats = join( ",", sort keys %Imager::formats );
382              
383 0         0 usage("Unsupported format: $format (choose: $formats)");
384             }
385              
386 39   66     678 $args{out} ||= join(".", $args{type}, $format);
387              
388 39 50       126 if ( $args{outdir} ) {
389 39 50       1116 usage("outdir does not exist") if !-e $args{outdir};
390              
391 39         159 $args{out} = join( "/", $args{outdir}, $args{out} );
392             }
393              
394 39 50       118 if ( $args{upsample} ) {
395 39   33     111 $args{len} ||= $DEFAULT_LEN;
396 39         111 $args{len} /= 2;
397             }
398              
399 39         56 my $grid;
400              
401 39         111 for my $type (@NOISE_TYPES) {
402 754 100       1461 if ( $args{type} eq $type ) {
403 39         64 my $sub;
404              
405 39         64 do {
406 1     1   8 no strict 'refs';
  1         3  
  1         262  
407 39         78 $sub = \&{"Math::Fractal::Noisemaker::$type"};
  39         329  
408             };
409              
410 39         313 $grid = &$sub(%args);
411 39         393 last;
412             }
413             }
414              
415 39 50       268 if ( !$grid ) {
416 0         0 usage("Unknown noise type '$args{type}' specified");
417             }
418              
419 39 50       173 if ( $args{refract} ) {
420 0         0 $grid = refract( $grid, %args );
421             }
422              
423 39 50 33     300 if ( defined($args{xscale}) || defined($args{yscale}) ) {
424 0         0 $grid = stretch($grid, %args);
425             }
426              
427 39 50       146 if ( $args{sphere} ) {
428 0         0 %args = defaultArgs(%args);
429              
430 0         0 $grid = spheremap( $grid, %args );
431             }
432              
433 39 50 33     315 if ( $args{delta} || $args{chiral} ) {
434 0         0 my $grid2;
435              
436 0         0 for my $type (@NOISE_TYPES) {
437 0 0       0 if ( $args{type} eq $type ) {
438 0         0 my $sub;
439              
440 0         0 do {
441 1     1   5 no strict 'refs';
  1         2  
  1         6267  
442 0         0 $sub = \&{"Math::Fractal::Noisemaker::$type"};
  0         0  
443             };
444              
445 0         0 $grid2 = &$sub(%args);
446 0         0 last;
447             }
448             }
449              
450 0 0       0 if ( $args{delta} ) {
451 0         0 $grid = delta( $grid, $grid2, %args );
452             } else {
453 0         0 $grid = chiral( $grid, $grid2, %args );
454             }
455             }
456              
457 39 50       151 if ( $args{stereo} ) {
458 0         0 $grid = stereo( $grid, %args );
459             }
460              
461 39 50       6097 if ( $args{upsample} ) {
462 39         126 $args{len} *= 2;
463 39         264 $grid = grow($grid, %args);
464             }
465              
466 39         1493 my $img;
467              
468 39         403 $img = img( $grid, %args);
469              
470 39 50       418 $img->write( file => $args{out} ) || die $img->errstr;
471              
472 39 50       543909 print "Saved file to $args{out}\n" if !$QUIET;
473              
474 39         274 return($img, $args{out});
475             }
476              
477             sub defaultArgs {
478 338     338 0 2464 my %args = @_;
479              
480 338 100       1469 $args{bias} = $DEFAULT_BIAS if !defined $args{bias};
481              
482 338   66     1845 $args{gap} ||= $DEFAULT_GAP;
483 338   66     972 $args{type} ||= $DEFAULT_TYPE;
484 338   66     945 $args{stype} ||= $DEFAULT_SLICE_TYPE;
485 338   66     845 $args{lbase} ||= $DEFAULT_TERRAIN_BASE;
486 338   66     749 $args{ltype} ||= $DEFAULT_TERRAIN_SLICE;
487 338   66     1075 $args{freq} ||= $DEFAULT_FREQ;
488 338   33     950 $args{len} ||= $DEFAULT_LEN;
489 338   66     1438 $args{octaves} ||= $DEFAULT_OCTAVES;
490 338   66     1415 $args{persist} ||= $DEFAULT_PERSIST;
491              
492 338 100 100     1725 $args{auto} = 1 if !defined( $args{auto} ) && $args{type} ne 'fern';
493              
494 338 100       1143 $args{amp} = $DEFAULT_AMP if !defined $args{amp};
495              
496 338         7733 return %args;
497             }
498              
499             sub img {
500 39     39 0 96 my $grid = shift;
501 39         162 my %args = defaultArgs(@_);
502              
503 39 50       368 print "Generating image...\n" if !$QUIET;
504              
505 39         53 my $len = scalar( @{$grid} );
  39         260  
506              
507 39 50       152 my $stretch = $args{sphere} ? 2 : 1;
508              
509             ###
510             ### Save the image
511             ###
512 39         223 my %imagerArgs = (
513             xsize => $len*$stretch,
514             ysize => $len,
515             );
516              
517 39 50       148 $imagerArgs{channels} = 1 if !$args{clut};
518              
519 39         711 my $img = Imager->new(%imagerArgs);
520              
521             ###
522             ### Scale pixel values to sane levels
523             ###
524 39         4613 my ( $min, $max, $range );
525              
526 39 100       143 if ( $args{auto} ) {
527 38         320 for ( my $x = 0 ; $x < $len ; $x++ ) {
528 1216         2444 my $column = $grid->[$x];
529              
530 1216         2395 for ( my $y = 0 ; $y < $len ; $y++ ) {
531 38912         88317 my $gray = $column->get($y);
532              
533 38912 100       89125 $min = $gray if !defined $min;
534 38912 100       60358 $max = $gray if !defined $max;
535              
536 38912 100       67287 $min = $gray if $gray < $min;
537 38912 100       120088 $max = $gray if $gray > $max;
538             }
539             }
540              
541 38         102 $range = $max - $min;
542             }
543              
544 39         104 my $scaledGrid = [];
545              
546 39         190 for ( my $x = 0 ; $x < $len*$stretch ; $x++ ) {
547 1248         9294 my $scaledColumn = $COLUMN_CLASS->new($len);
548              
549 1248         3660 for ( my $y = 0 ; $y < $len ; $y++ ) {
550             # my $gray = $column->get($y);
551 39936         82550 my $gray = noise($grid,$x/$stretch,$y);
552              
553 39936         49572 my $scaled;
554              
555 39936 100       82214 if ( $args{auto} ) {
556 38912 50       85450 $scaled = $range ? ( ( $gray - $min ) / $range ) * $MAX_COLOR : 0;
557             } else {
558 1024         1612 $scaled = clamp($gray);
559             }
560              
561 39936         155455 $scaledColumn->set( $y, $scaled );
562             }
563              
564 1248         4603 $scaledGrid->[$x] = $scaledColumn;
565             }
566              
567 39 50 33     236 if ( $args{clut} && $args{clutdir} ) {
    50          
568 0         0 $img = vertclut( $scaledGrid, %args );
569             } elsif ( $args{clut} ) {
570 0         0 $img = hypoclut( $scaledGrid, %args );
571             } else {
572 39 50 33     170 if ( $args{emboss} && !$args{shadow} ) {
573 0         0 $scaledGrid = emboss( $scaledGrid, %args );
574 0         0 $scaledGrid = smooth( $scaledGrid, %args );
575             # $scaledGrid = glow( $scaledGrid, %args );
576 0         0 $scaledGrid = densemap( $scaledGrid );
577             }
578              
579 39         151 for ( my $x = 0 ; $x < $len*$stretch; $x++ ) {
580 1248         2209 my $column = $scaledGrid->[$x];
581              
582 1248         3031 for ( my $y = 0 ; $y < $len ; $y++ ) {
583 39936         2366872 my $gray = $column->get($y);
584 39936         155248 $img->setpixel(
585             x => $x,
586             y => $y,
587             color => [ $gray, $gray, $gray ],
588             );
589             }
590 1248         72293 printRow($column);
591             }
592             }
593              
594 39 50 33     208 if ( $args{shadow} && !$args{emboss} ) {
595 0         0 my $embossed = emboss( $scaledGrid, %args );
596              
597             # $embossed = smooth( $embossed, %args );
598             # $embossed = glow( $embossed, %args );
599 0         0 $embossed = densemap( $embossed );
600              
601 0         0 my $shadow = $args{shadow};
602              
603 0         0 for ( my $x = 0 ; $x < $len*$stretch ; $x++ ) {
604 0         0 for ( my $y = 0 ; $y < $len ; $y++ ) {
605 0         0 my $color = $img->getpixel( x => $x, y => $y );
606 0         0 my ( $r, $g, $b ) = $color->rgba;
607              
608 0         0 my $embColor = noise($embossed,$x/$stretch,$y/$stretch) / $MAX_COLOR;
609              
610 0 0       0 if ( $embColor < .65 ) {
611 0         0 my $amt = ( 1 - ( $embColor / .65 ) ) * $shadow;
612              
613 0         0 $r = interp( $r, 0, $amt );
614 0         0 $g = interp( $g, 0, $amt );
615 0         0 $b = interp( $b, 0, $amt );
616             } else {
617 0         0 my $amt = ( ( ( $embColor - .65 ) / .65 ) ) * $shadow;
618              
619 0         0 $r = interp( $r, $MAX_COLOR, $amt );
620 0         0 $g = interp( $g, $MAX_COLOR, $amt );
621 0         0 $b = interp( $b, $MAX_COLOR, $amt );
622             }
623              
624 0         0 $img->setpixel(
625             x => $x,
626             y => $y,
627             color => [ $r, $g, $b ]
628             );
629             }
630 0 0       0 printRow( $embossed->[$x/2] ) if $x % 2 == 0;
631             }
632             }
633              
634 39         2441 return $img;
635             }
636              
637             sub grow {
638 114   50 114 1 437 $GROW_FN ||= \&grow_interp;
639              
640 114         491 &$GROW_FN(@_);
641             }
642              
643             #
644             # Artificially stretch noise along either axis
645             #
646             sub stretch {
647 0     0 0 0 my $noise = shift;
648 0         0 my %args = defaultArgs(@_);
649              
650 0         0 my $len = $args{len};
651              
652 0         0 my $grid = grid(%args);
653              
654 0   0     0 my $xscale = $args{xscale} || 1;
655 0   0     0 my $yscale = $args{yscale} || 1;
656              
657 0         0 for ( my $x = 0 ; $x < $len ; $x++ ) {
658 0         0 my $column = $grid->[$x];
659              
660 0         0 for ( my $y = 0 ; $y < $len ; $y++ ) {
661 0         0 my $thisX = $x * $xscale;
662 0         0 my $thisY = $y * $yscale;
663              
664 0         0 $column->set( $y, noise( $noise, $thisX, $thisY ) );
665             }
666             }
667              
668 0         0 return $grid;
669             }
670              
671             #
672             # Grow the image using the interpolation function
673             #
674             sub grow_interp {
675 114     114 0 232 my $noise = shift;
676 114         992 my %args = @_;
677              
678 114         293 my $wantLength = $args{len};
679 114         169 my $haveLength = scalar( @{$noise} );
  114         224  
680              
681 114         255 my $scale = $wantLength / $haveLength;
682              
683 114         736 my $grid = grid(%args);
684              
685 114         408 my $smooth = $args{smooth};
686              
687 114         388 for ( my $x = 0 ; $x < $wantLength ; $x++ ) {
688 2520         4409 my $column = $grid->[$x];
689              
690 2520         6273 for ( my $y = 0 ; $y < $wantLength ; $y++ ) {
691 63168         101366 my $thisX = $x / $scale;
692 63168         74812 my $thisY = $y / $scale;
693              
694 63168 100       169876 if ( !$smooth ) {
695 3136         3627 $thisX = int($thisX);
696 3136         3526 $thisY = int($thisY);
697             }
698              
699 63168         146943 $column->set( $y, noise( $noise, $thisX, $thisY ) );
700             }
701             }
702              
703 114         3004 return $grid;
704             }
705              
706             #
707             # Grow the image by resampling neighborhood pixels
708             #
709             sub grow_gaussian {
710 0     0 0 0 my $noise = shift;
711 0         0 my %args = @_;
712              
713 0         0 my $grid = $noise;
714              
715 0         0 my $wantLength = $args{len};
716 0         0 my $haveLength = scalar( @{$noise} );
  0         0  
717              
718 0         0 until ( $haveLength >= $wantLength ) {
719 0         0 my $grown = [];
720              
721 0         0 for ( my $x = 0 ; $x < $haveLength * 2 ; $x++ ) {
722 0         0 my $column = $grid->[ $x / 2 ];
723 0         0 my $grownColumn = $COLUMN_CLASS->new( $haveLength * 2 );
724              
725 0         0 for ( my $y = 0 ; $y < $haveLength * 2 ; $y++ ) {
726 0         0 $grownColumn->set( $y, $column->get( $y / 2 ) );
727             }
728              
729 0         0 $grown->[$x] = $grownColumn;
730             }
731              
732 0 0       0 $grid = $args{smooth} ? smooth( $grown, %args ) : $grown;
733              
734 0         0 $haveLength *= 2;
735             }
736              
737 0         0 return $grid;
738             }
739              
740             sub shrink {
741 15     15 0 38 my $noise = shift;
742 15         130 my %args = @_;
743              
744 15         39 my $grid = $noise;
745              
746 15         39 my $wantLength = $args{len};
747 15         28 my $haveXLen = scalar @$noise;
748 15         83 my $haveYLen = $noise->[0]->len();
749              
750 15         62 until ( $haveXLen <= $wantLength ) {
751 15         40 my $shrunk = [];
752              
753 15         63 for ( my $x = 0 ; $x < $haveXLen / 2 ; $x++ ) {
754 160         1064 my $shrunkColumn = $COLUMN_CLASS->new( $haveYLen / 2 );
755              
756 160         455 for ( my $y = 0 ; $y < $haveYLen / 2 ; $y++ ) {
757 1920         4087 my $value = noise($grid, $x*2, $y*2);
758 1920         4726 $value += noise($grid, ($x*2)+1, $y*2);
759 1920         4756 $value += noise($grid, $x*2, ($y*2)+1);
760 1920         4904 $value += noise($grid, ($x*2)+1, ($y*2)+1);
761              
762 1920         9161 $shrunkColumn->set( $y, $value / 4 );
763             }
764              
765 160         692 $shrunk->[$x] = $shrunkColumn;
766             }
767              
768 15         28 $haveXLen /= 2;
769              
770 15         64 $grid = $shrunk;
771             }
772              
773 15         104 return $grid;
774             }
775              
776             sub grid {
777 197     197 0 726 my %args = defaultArgs(@_);
778              
779 197         925 my $grid = [];
780              
781 197         479 my $len = $args{len};
782              
783 197         741 for ( my $x = 0 ; $x < $len ; $x++ ) {
784 3628         4109 my @row;
785              
786 3628         7617 for ( my $y = 0 ; $y < $len ; $y++ ) {
787 83264         249721 $row[$y] = ( $args{bias} / 1 ) * $MAX_COLOR;
788             }
789              
790 3628         35645 $grid->[$x] = $COLUMN_CLASS->new( $len, \@row );
791             }
792              
793 197         1717 return $grid;
794             }
795              
796             sub infile {
797 3     3 1 13 my %args = defaultArgs(@_);
798              
799 3 50       18 print "Loading image...\n" if !$QUIET;
800              
801 3         8 my $len = $args{len};
802              
803 3         24 my $img = Imager->new;
804              
805 3 50       78 $img->read( file => $args{in} ) || die $img->errstr();
806              
807 3         809 my $width = $img->getwidth();
808 3         57 my $height = $img->getheight();
809              
810 3 50       38 my $tempSize = ( $width > $height ) ? $width : $height;
811 3         23 my $tempGrid = grid(%args, len => $tempSize);
812              
813 3         20 for ( my $x = 0 ; $x < $tempSize ; $x++ ) {
814 96         166 my $column = $tempGrid->[$x];
815              
816 96         192 for ( my $y = 0 ; $y < $tempSize ; $y++ ) {
817 3072         12092 my $color = $img->getpixel(
818             x => ( $x / ( $tempSize / 1 ) ) * ( $width - 1 ),
819             y => ( $y / ( $tempSize - 1 ) ) * ( $height - 1 )
820             );
821              
822 3072         63880 my ( $r, $g, $b ) = $color->rgba;
823              
824 3072         17522 $column->set( $y, ( $r + $g + $b ) / 3 );
825             }
826             }
827              
828 3         37 return grow($tempGrid, %args);
829             }
830              
831             sub intile {
832 1     1 0 5 my $grid = infile(@_);
833              
834 1         47 return tile( $grid, @_ );
835             }
836              
837             sub gradient {
838 1     1 1 7 my %args = @_;
839              
840 1 50       5 print "Generating gradient noise...\n" if !$QUIET;
841              
842 1   33     6 $args{len} ||= $DEFAULT_LEN;
843 1 50       7 $args{freq} = $args{len} if !defined $args{freq};
844              
845 1         7 %args = defaultArgs(%args);
846              
847 1         8 my $freq = $args{freq};
848              
849 1         7 my $grid = grid( %args, len => $freq );
850              
851 1 50       6 $args{amp} = $DEFAULT_AMP if !defined $args{amp};
852              
853 1         4 my $ampVal = $args{amp} * $MAX_COLOR;
854 1         2 my $biasVal = $args{bias} * $MAX_COLOR;
855              
856 1 50       15 spamConsole(%args) if !$QUIET;
857              
858 1         3 my $amp = $args{amp};
859              
860 1         5 for ( my $x = 0 ; $x < $freq ; $x++ ) {
861 16         29 my $column = $grid->[$x];
862              
863 16         21 my $thisX = $x / $freq;
864              
865 16         37 for ( my $y = 0 ; $y < $freq ; $y++ ) {
866              
867             # my $randAmp = rand($ampVal);
868              
869 256         322 my $thisY = $y / $freq;
870              
871 256         445 my $xval = $NUMS[ $thisX * 256 ] + $NUMS[ $thisY * 256 ];
872 256         406 my $yval = $NUMS[ $thisY * 256 ] + $NUMS[ $xval % 256 ];
873 256         365 $xval = ( $NUMS[ $xval % 256 ] / 255 ) * $amp;
874 256         338 $yval = ( $NUMS[ $yval % 256 ] / 255 ) * $amp;
875              
876 256         417 my $randAmp = interp( $xval, $yval, .5 );
877              
878 256         1120 $column->set( $y, $randAmp + $biasVal );
879             }
880              
881 16         33 printRow($column);
882             }
883              
884 1         20 return grow( $grid, %args );
885             }
886              
887             sub worley {
888 2     2 1 15 my %args = @_;
889 2   33     10 $args{len} ||= $DEFAULT_LEN;
890 2 100       8 $args{freq} = 32 if !defined $args{freq};
891              
892 2         13 %args = defaultArgs(%args);
893              
894 2         15 my $freq = $args{freq};
895 2         6 my $len = $args{len};
896 2         4 my $amp = $args{amp};
897 2         4 my $nth = $args{nth};
898 2         3 my $cell = $args{cell};
899 2   100     11 my $distType = $args{dist} || 0;
900              
901 2         15 my $grid = grid(%args);
902              
903 2         8 my @points;
904 2 50       9 if ( $args{points} ) {
905 0         0 @points = @{$args{points}};
  0         0  
906             } else {
907 2         9 for ( my $i = 0 ; $i < $freq ; $i++ ) {
908 40         54 my $x = rand($len);
909 40         46 my $y = rand($len);
910 40         54 my $white = $NUMS[$i];
911              
912 40         138 push @points, [ $x, $y, $white ];
913             }
914             }
915              
916 2 50       9 if ( !defined $nth ) {
917 2         7 $nth = sqrt(scalar @points);
918             }
919              
920             # render as shaded distance or solid cells? normalize the index:
921 2 50       6 $cell = $cell ? 1 : 0;
922              
923 2         11 for ( my $x = 0 ; $x < $len ; $x++ ) {
924 32         48 my $column = $grid->[$x];
925              
926 32         66 for ( my $y = 0 ; $y < $len ; $y++ ) {
927 512         546 my @thisDist;
928 512         830 for my $point (@points) {
929 10240 50 66     23886 if ( $distType == 0 || $distType == 3 ) {
930 10240         14117 my $xdist = abs( $x - $point->[0] );
931 10240         12541 my $ydist = abs( $y - $point->[1] );
932 10240         25347 push @thisDist, [ sqrt( $xdist**2 + $ydist**2 ), $point->[2] ];
933             }
934 10240 50       19985 if ( $distType == 1 ) {
935 0         0 push @thisDist, [
936             abs( $x - $point->[0] ) + abs( $y - $point->[1] ),
937             $point->[2]
938             ];
939             }
940 10240 50       19672 if ( $distType == 2 ) {
941 0         0 my $xdist = abs( $x - $point->[0] );
942 0         0 my $ydist = abs( $y - $point->[1] );
943 0 0       0 my $thisDist = ( $xdist > $ydist ) ? $xdist : $ydist;
944 0         0 push @thisDist, [ $thisDist, $point->[2] ];
945             }
946             }
947 512 100       997 if ( $distType == 3 ) {
948 256         265 my @foo;
949 256         284 my $i = 0;
950 256         398 for (@thisDist) {
951 2048         2050 $i++;
952 2048         5523 push @foo, [
953             abs( $_->[0] - ( $NUMS[$i] / $MAX_COLOR ) * $len ), $_->[1]
954             ];
955             }
956              
957             # push @thisDist, @foo;
958 256         569 @thisDist = sort { $a->[0] <=> $b->[0] } @foo;
  4063         6357  
959             } else {
960 256         613 @thisDist = sort { $a->[0] <=> $b->[0] } @thisDist;
  31365         37357  
961             }
962              
963 512         878 my $val = $thisDist[$nth]->[$cell];
964 512         4030 $column->set( $y, $val);
965             }
966             }
967              
968 2         24 $grid = densemap($grid, $args{invert});
969              
970 2         85 return tile( $grid, %args );
971             }
972              
973             sub white {
974 45     45 1 516 my %args = @_;
975              
976 45 50       146 print "Generating white noise...\n" if !$QUIET;
977              
978 45   33     145 $args{len} ||= $DEFAULT_LEN;
979 45 100       194 $args{freq} = $args{len} if !defined $args{freq};
980              
981 45         279 %args = defaultArgs(%args);
982              
983 45         531 my $freq = $args{freq};
984 45         156 my $gap = $args{gap};
985              
986 45         337 my $grid = grid( %args, len => $freq );
987              
988 45 50       259 $args{amp} = $DEFAULT_AMP if !defined $args{amp};
989              
990 45         111 my $ampVal = $args{amp} * $MAX_COLOR;
991 45         128 my $biasVal = $args{bias} * $MAX_COLOR;
992              
993 45 50       140 spamConsole(%args) if !$QUIET;
994              
995 45         99 my $stars = $args{stars};
996              
997             # my $offX = rand($freq);
998             # my $offY = rand($freq);
999 45         68 my $offX = 0;
1000 45         189 my $offY = 0;
1001              
1002 45         151 for ( my $x = 0 ; $x < $freq ; $x++ ) {
1003 400         566 my $thisX = ( $x + $offX ) % $freq;
1004 400         553 my $column = $grid->[$thisX];
1005              
1006 400         920 for ( my $y = 0 ; $y < $freq ; $y++ ) {
1007 4848         6557 my $thisY = ( $y + $offY ) % $freq;
1008              
1009 4848 100       9931 if ( rand() < $gap ) {
1010 766         1510 $column->set( $thisY, 0 );
1011 766         1582 next;
1012             }
1013              
1014 4082         4794 my $randAmp = rand($ampVal);
1015              
1016 4082 100       6731 if ( !$stars ) {
1017 4080 100       7776 $randAmp *= -1 if rand(1) >= .5;
1018             }
1019              
1020 4082         14307 $column->set( $thisY, $randAmp + $biasVal );
1021             }
1022              
1023 400         795 printRow($column);
1024             }
1025              
1026 45         416 return grow( $grid, %args );
1027             }
1028              
1029             sub stars {
1030 3     3 1 27 my %args = @_;
1031              
1032 3 50       15 print "Generating stars...\n" if !$QUIET;
1033              
1034 3         8 $args{bias} = 0;
1035 3   66     24 $args{amp} ||= $DEFAULT_AMP;
1036 3   50     22 $args{gap} ||= .995;
1037              
1038 3         25 my $grid = white( %args, stars => 1 );
1039              
1040 3         45 %args = defaultArgs(%args);
1041              
1042 3 50       46 return $args{smooth} ? smooth( $grid, %args ) : $grid;
1043             }
1044              
1045             sub gel {
1046 1     1 1 9 my %args = @_;
1047              
1048 1 50       6 print "Generating gel noise...\n" if !$QUIET;
1049              
1050 1 50       5 $args{displace} = $DEFAULT_DISPLACEMENT if !defined $args{displace};
1051              
1052 1         8 %args = defaultArgs(%args);
1053              
1054 1         13 my $grid = white(%args);
1055              
1056 1         20 return displace( $grid, %args );
1057             }
1058              
1059             sub displace {
1060 4     4 1 12 my $grid = shift;
1061 4         121 my %args = @_;
1062              
1063 4 50       22 print "Applying self-displacement...\n" if !$QUIET;
1064              
1065 4         11 my $out = [];
1066              
1067 4         15 my $length = $args{len};
1068 4         8 my $displace = $args{displace};
1069              
1070 4 50       15 $displace = .5 if !defined $displace;
1071              
1072 4         15 $displace =
1073             ( $displace / 1 ) * ( $length / $DEFAULT_LEN )
1074             ; # Same visual offset for diff size imgs
1075              
1076 4         29 $grid = smooth( $grid, %args );
1077              
1078 4         79 for ( my $x = 0 ; $x < $length ; $x++ ) {
1079 64         675 my $column = $COLUMN_CLASS->new($length);
1080              
1081 64         200 for ( my $y = 0 ; $y < $length ; $y++ ) {
1082 1024         3108 my $tmpX = noise($grid, $x + $length/2, $y + $length/2);
1083 1024         2243 my $displaceX = noise($grid, $tmpX, $y) * $displace;
1084              
1085 1024         2755 my $tmpY = noise($grid, $x, $y);
1086 1024         2031 my $displaceY = noise($grid, $x, $tmpY) * $displace;
1087              
1088 1024         1990 $column->set( $y, noise( $grid, $displaceX, $displaceY ) );
1089             }
1090              
1091 64         316 $out->[$x] = $column;
1092             }
1093              
1094 4         267 return $out;
1095             }
1096              
1097             sub square {
1098 3     3 1 15 my %args = defaultArgs(@_);
1099              
1100 3 50       16 print "Generating square noise...\n" if !$QUIET;
1101              
1102 3         10 my $freq = $args{freq};
1103 3         7 my $amp = $args{amp};
1104 3         7 my $bias = $args{bias};
1105 3         7 my $length = $args{len};
1106 3         6 my $persist = $args{persist};
1107              
1108 3 50       12 $amp = $DEFAULT_AMP if !defined $amp;
1109              
1110 3   33     31 my $grid = $args{grid} || white( %args, len => $freq * 2 );
1111 3         17 my $haveLength = scalar @$grid;
1112 3         9 my $baseOffset = $MAX_COLOR * $amp;
1113 3 50       14 $baseOffset = sqrt($baseOffset) if $args{grid};
1114              
1115 3 50       10 spamConsole(%args) if !$QUIET;
1116              
1117 3         11 until ( $haveLength >= $length ) {
1118 3         7 my $grown = [];
1119              
1120 3         13 for ( my $x = 0 ; $x < $haveLength * 2 ; $x++ ) {
1121 48         417 $grown->[$x] = $COLUMN_CLASS->new( $haveLength * 2 );
1122             }
1123              
1124 3         14 for ( my $x = 0 ; $x < $haveLength ; $x++ ) {
1125 24         29 my $thisX = $x * 2;
1126 24         31 my $column = $grid->[$x];
1127 24         38 my $grownColumn = $grown->[$thisX];
1128              
1129 24         55 for ( my $y = 0 ; $y < $haveLength ; $y++ ) {
1130 192         237 my $thisY = $y * 2;
1131              
1132 192         228 my $offset = rand($baseOffset);
1133 192 100       405 $offset *= -1 if ( rand(1) >= .5 );
1134              
1135 192         921 $grownColumn->set( $thisY, $column->get($y) + $offset );
1136             }
1137              
1138 24         68 $grown->[$thisX] = $grownColumn;
1139             }
1140              
1141 3         12 for ( my $x = 0 ; $x < $haveLength ; $x++ ) {
1142 24         30 my $thisX = $x * 2;
1143 24         27 $thisX += 1;
1144              
1145 24         30 my $grownColumn = $grown->[$thisX];
1146              
1147 24         61 for ( my $y = 0 ; $y < $haveLength ; $y++ ) {
1148 192         241 my $thisY = $y * 2;
1149 192         233 $thisY += 1;
1150              
1151 192         423 my $corners =
1152             ( noise( $grid, $x - 1, $y - 1 ) +
1153             noise( $grid, $x + 1, $y - 1 ) +
1154             noise( $grid, $x - 1, $y + 1 ) +
1155             noise( $grid, $x + 1, $y + 1 ) ) / 4;
1156              
1157 192         378 my $offset = rand($baseOffset);
1158 192 100       421 $offset *= -1 if ( rand(1) >= .5 );
1159 192         802 $grownColumn->set( $thisY, $corners + $offset );
1160             }
1161             }
1162              
1163 3         7 $haveLength *= 2;
1164              
1165 3         8 $baseOffset *= $persist;
1166              
1167 3         12 for ( my $x = 0 ; $x < $haveLength ; $x++ ) {
1168 48         189 my $base = ( $x + 1 ) % 2;
1169              
1170 48         74 my $grownColumn = $grown->[$x];
1171              
1172 48         103 for ( my $y = $base ; $y < $haveLength ; $y += 2 ) {
1173 384         772 my $sides =
1174             ( noise( $grown, $x - 1, $y ) +
1175             noise( $grown, $x + 1, $y ) +
1176             noise( $grown, $x, $y - 1 ) +
1177             noise( $grown, $x, $y + 1 ) ) / 4;
1178              
1179 384         719 my $offset = rand($baseOffset);
1180 384 100       858 $offset *= -1 if ( rand(1) >= .5 );
1181 384         1664 $grownColumn->set( $y, $sides + $offset );
1182             }
1183             }
1184              
1185 3 100       58 $grid = $args{smooth} ? smooth( $grown, %args ) : $grown;
1186             }
1187              
1188 3         37 return $grid;
1189             }
1190              
1191             sub sgel {
1192 1     1 1 6 my %args = defaultArgs(@_);
1193              
1194 1 50       9 $args{displace} = $DEFAULT_DISPLACEMENT if !defined $args{displace};
1195              
1196 1 50       5 print "Generating square gel noise...\n" if !$QUIET;
1197              
1198 1         10 my $grid = square(%args);
1199              
1200 1         33 return displace( $grid, %args );
1201             }
1202              
1203             sub multires {
1204 14     14 1 111 my %args = @_;
1205              
1206 14 50       67 print "Generating multi-res noise...\n" if !$QUIET;
1207              
1208 14 100       58 $args{amp} = $DEFAULT_AMP if !defined $args{amp};
1209              
1210 14         99 %args = defaultArgs(%args);
1211              
1212 14         116 $args{amp} *= $args{octaves};
1213              
1214 14         27 my $length = $args{len};
1215 14         30 my $amp = $args{amp};
1216 14         26 my $freq = $args{freq};
1217 14         24 my $bias = $args{bias};
1218 14         30 my $octaves = $args{octaves};
1219              
1220 14         25 my @layers;
1221              
1222 14 50       53 spamConsole(%args) if !$QUIET;
1223              
1224 14         54 for ( my $o = 0 ; $o < $octaves ; $o++ ) {
1225 47 100       153 last if $freq > $length;
1226              
1227 36 50       108 print "Octave " . ( $o + 1 ) . " ... \n" if !$QUIET;
1228              
1229 36         64 my $generator;
1230              
1231 36         123 for my $type (@SIMPLE_TYPES) {
1232 1008 100       2182 if ( $args{stype} eq $type ) {
1233 36         60 do {
1234 1     1   9 no strict 'refs';
  1         3  
  1         2456  
1235 36         58 $generator = \&{"Math::Fractal::Noisemaker::$type"};
  36         354  
1236             };
1237             }
1238             }
1239              
1240 36 50       519 if ( !$generator ) {
1241 0         0 usage("Unknown slice type '$args{stype}' specified");
1242             }
1243              
1244 36         329 push @layers,
1245             &$generator(
1246             %args,
1247             freq => $freq,
1248             amp => $amp,
1249             bias => $bias,
1250             len => $length,
1251             );
1252              
1253 36         407 $amp *= $args{persist};
1254 36         218 $freq *= 2;
1255             }
1256              
1257             #
1258             # Restore orig values
1259             #
1260 14         44 $amp = $args{amp};
1261 14         36 $freq = $args{freq};
1262              
1263 14         66 my $combined = [];
1264              
1265 14         27 my $zshift;
1266 14 100       67 if ( $args{ridged} ) {
1267 5 100       31 $args{zshift} = $amp if !defined $args{zshift};
1268 5         17 $zshift = $args{zshift} * $MAX_COLOR;
1269             }
1270              
1271 14         58 for ( my $x = 0 ; $x < $length ; $x++ ) {
1272              
1273 256         1397 my $combinedColumn = $COLUMN_CLASS->new($length);
1274              
1275 256         703 for ( my $y = 0 ; $y < $length ; $y++ ) {
1276 5120         5463 my $n;
1277             my $t;
1278              
1279 5120         11299 for ( my $z = 0 ; $z < @layers ; $z++ ) {
1280 13824         14860 $n++;
1281              
1282 13824         33428 my $gray = $layers[$z][$x]->get($y);
1283              
1284 13824 100       23621 if ( $args{ridged} ) {
1285 2304         5487 $t += abs($gray);
1286             } else {
1287 11520         28876 $t += $gray;
1288             }
1289             }
1290              
1291 5120 100 100     20993 if ( $n && $args{ridged} ) {
    100          
1292 1024         5096 $combinedColumn->set( $y,
1293             ( $bias * $MAX_COLOR ) + $zshift - ( $t / $n ) );
1294             } elsif ($n) {
1295 3840         14966 $combinedColumn->set( $y, $t / $n );
1296             } else {
1297 256         915 $combinedColumn->set( $y, 0 );
1298             }
1299             }
1300              
1301 256         627 $combined->[$x] = $combinedColumn;
1302              
1303 256         714 printRow($combinedColumn);
1304             }
1305              
1306 14         959 return $combined;
1307             }
1308              
1309             sub block {
1310 1     1 1 7 my %args = @_;
1311              
1312 1 50       5 print "Generating block noise...\n" if !$QUIET;
1313              
1314 1         4 $args{smooth} = 0;
1315              
1316 1         8 return multires(%args);
1317             }
1318              
1319             sub pgel {
1320 1     1 1 9 my %args = @_;
1321              
1322 1 50       5 print "Generating multi-res gel noise...\n" if !$QUIET;
1323              
1324 1         8 my $grid = multires(%args);
1325              
1326 1 50       10 $args{displace} = $DEFAULT_DISPLACEMENT if !defined $args{displace};
1327              
1328 1         9 %args = defaultArgs(%args);
1329              
1330 1         13 return displace( $grid, %args );
1331             }
1332              
1333             sub wgel {
1334 1     1 1 58 my %args = @_;
1335              
1336 1 50       6 print "Generating worley gel noise...\n" if !$QUIET;
1337              
1338 1 50       7 my $dist = defined $args{dist} ? $args{dist} : 3;
1339 1 50       4 my $freq = defined $args{freq} ? $args{freq} : 8;
1340 1 50       4 my $displace = defined $args{displace} ? $args{displace} : 4;
1341              
1342 1         10 %args = defaultArgs(
1343             %args,
1344             dist => $dist,
1345             freq => $freq,
1346             displace => $displace,
1347             );
1348              
1349 1         14 my $grid = worley(%args);
1350              
1351 1         28 return displace( $grid, %args );
1352             }
1353              
1354             sub ridged {
1355 5     5 1 61 my %args = @_;
1356              
1357 5 50       22 print "Generating ridged multifractal noise...\n" if !$QUIET;
1358              
1359 5 100       23 $args{bias} = 0 if !defined $args{bias};
1360 5 100       18 $args{amp} = 1 if !defined $args{amp};
1361              
1362 5         33 return multires( %args, ridged => 1 );
1363             }
1364              
1365             sub refract {
1366 0     0 1 0 my $grid = shift;
1367 0         0 my %args = @_;
1368              
1369 0 0       0 print "Applying fractal Z displacement...\n" if !$QUIET;
1370              
1371 0         0 my $haveLength = scalar( @{$grid} );
  0         0  
1372              
1373 0         0 my $out = [];
1374              
1375 0         0 for ( my $x = 0 ; $x < $haveLength ; $x++ ) {
1376 0         0 $out->[$x] = [];
1377              
1378 0         0 my $inColumn = $grid->[$x];
1379 0         0 my $outColumn = $COLUMN_CLASS->new($haveLength);
1380              
1381 0         0 for ( my $y = 0 ; $y < $haveLength ; $y++ ) {
1382 0   0     0 my $color = $inColumn->get($y) || 0;
1383 0         0 my $srcY = ( $color / $MAX_COLOR ) * $haveLength;
1384              
1385 0         0 $outColumn->set( $y, $inColumn->get($srcY % $haveLength) );
1386             }
1387              
1388 0         0 $out->[$x] = $outColumn;
1389             }
1390              
1391 0         0 return $out;
1392             }
1393              
1394             sub lsmooth {
1395 2     2 0 6 my $grid = shift;
1396 2         26 my %args = @_;
1397              
1398 2         5 my $len = scalar( @{$grid} );
  2         6  
1399              
1400 2         21 my $smooth = grid(%args, len => $args{len}/2);
1401              
1402 2   100     15 my $dirs = $args{dirs} || 6;
1403 2   66     13 my $angle = $args{angle} || rand(360);
1404 2   100     10 my $rad = $args{rad} || 6;
1405              
1406 2         5 my $dirAngle = 360 / $dirs;
1407 2         6 my $angle360 = 360 + $angle;
1408              
1409 2         10 for ( my $x = 0 ; $x < $len/2 ; $x++ ) {
1410 16         25 my $smoothColumn = $smooth->[$x];
1411 16         29 my $column = $grid->[$x*2];
1412              
1413 16         84 for ( my $y = 0 ; $y < $len/2 ; $y++ ) {
1414 128         666 $smoothColumn->set( $y,
1415             $smoothColumn->get($y) + $column->get($y*2) / $dirs );
1416              
1417 128         415 for ( my $a = $angle ; $a < $angle360 ; $a += $dirAngle ) {
1418 640         1482 for ( my $d = 1 ; $d <= $rad ; $d++ ) { # distance
1419 2560         4664 my ( $tx, $ty ) = translate( $x, $y, $a, $d );
1420 2560         4322 $tx = ($tx*2) % $len;
1421 2560         3057 $ty = ($ty*2) % $len;
1422              
1423 2560         23834 $smoothColumn->set( $y,
1424             $smoothColumn->get($y) +
1425             $grid->[$tx]->get($ty) * ( 1 - ( $d / $rad ) ) / $rad );
1426             }
1427             }
1428             }
1429              
1430 16         67 $smooth->[$x] = $smoothColumn;
1431             }
1432              
1433 2         26 return grow($smooth,%args);
1434             }
1435              
1436             sub smooth {
1437 19     19 1 43 my $grid = shift;
1438 19         117 my %args = @_;
1439              
1440 19         34 my $haveLength = scalar( @{$grid} );
  19         42  
1441              
1442 19         48 my $smooth = [];
1443              
1444 19         50 my $amt = $args{smooth};
1445              
1446 19         95 for ( my $x = 0 ; $x < $haveLength ; $x++ ) {
1447 232         1674 my $smoothColumn = $COLUMN_CLASS->new($haveLength);
1448              
1449 232         613 for ( my $y = 0 ; $y < $haveLength ; $y++ ) {
1450 3136         8148 my $corners =
1451             ( noise( $grid, $x - 1, $y - 1 ) +
1452             noise( $grid, $x + 1, $y - 1 ) +
1453             noise( $grid, $x - 1, $y + 1 ) +
1454             noise( $grid, $x + 1, $y + 1 ) ) / 16;
1455              
1456 3136         8294 my $sides =
1457             ( noise( $grid, $x - 1, $y ) +
1458             noise( $grid, $x + 1, $y ) +
1459             noise( $grid, $x, $y - 1 ) +
1460             noise( $grid, $x, $y + 1 ) ) / 8;
1461              
1462 3136         7380 my $pixel = noise( $grid, $x, $y );
1463              
1464 3136         4672 my $center = $pixel / 4;
1465              
1466 3136         9113 my $blended = $corners + $sides + $center;
1467              
1468 3136         5994 my $final = interp( $pixel, $blended, $amt );
1469              
1470 3136         13185 $smoothColumn->set( $y, $final );
1471             }
1472              
1473 232         895 $smooth->[$x] = $smoothColumn;
1474             }
1475              
1476 19         447 return $smooth;
1477             }
1478              
1479             sub terra {
1480 1     1 1 8 my %args = @_;
1481              
1482 1 50       4 print "Generating terra noise...\n" if !$QUIET;
1483              
1484 1 50       6 $args{amp} = .5 if !defined $args{amp};
1485 1 50       6 $args{feather} = 48 if !defined $args{feather};
1486 1   50     7 $args{layers} ||= 4;
1487              
1488 1         9 %args = defaultArgs(%args);
1489              
1490 1         9 my $refGenerator = __generator( $args{lbase} );
1491              
1492 1         24 my $reference = &$refGenerator(
1493             %args,
1494             bias => .4,
1495             amp => .6,
1496             freq => $args{freq},
1497             );
1498              
1499 1         7 my @layers;
1500              
1501 1         3 do {
1502 1         2 my $biasOffset = .5;
1503 1         3 my $bias = .25;
1504 1         3 my $amp = .125;
1505 1         4 my $freq = $args{freq};
1506              
1507 1         6 my $generator = __generator( $args{ltype} );
1508              
1509 1         7 for ( my $i = 0 ; $i < $args{layers} ; $i++ ) {
1510 4 50       18 print "---------------------------------------\n" if !$QUIET;
1511 4 50       14 print "Complex layer $i ...\n" if !$QUIET;
1512              
1513 4         4 my %xargs;
1514              
1515 4 50       91 if ( $args{ltype} eq 'ridged' ) {
1516 4         12 $xargs{zshift} = $bias;
1517 4         11 $xargs{bias} = 0;
1518             } else {
1519 0         0 $xargs{bias} = $bias;
1520             }
1521              
1522 4         39 push @layers,
1523             &$generator(
1524             %args,
1525             %xargs,
1526             freq => $freq,
1527             amp => $amp,
1528             );
1529              
1530 4         30 $bias += $biasOffset;
1531 4         11 $biasOffset *= .5;
1532              
1533 4         25 $freq *= 2;
1534              
1535             # $amp *= $args{persist};
1536             }
1537             };
1538              
1539 1         10 my $out = grid(%args);
1540              
1541 1         4 my $feather = $args{feather};
1542 1         4 my $length = $args{len};
1543              
1544 1         5 for ( my $x = 0 ; $x < $length ; $x++ ) {
1545 16         29 my $referenceColumn = $reference->[$x];
1546 16         91 my $outColumn = $COLUMN_CLASS->new($length);
1547              
1548 16         43 for ( my $y = 0 ; $y < $length ; $y++ ) {
1549 256         705 my $value = $referenceColumn->get($y);
1550              
1551 256         400 my $level = 128;
1552 256         258 my $levelOffset = 64;
1553              
1554 256         972 $outColumn->set( $y, $layers[0][$x]->get($y) );
1555              
1556 256         788 for ( my $z = 1 ; $z < $args{layers} ; $z++ ) {
1557 768         1158 my $diff = $level - $value;
1558              
1559 768 100 66     4839 if ( $value >= $level ) {
    100 33        
      66        
1560             ##
1561             ## Reference pixel value is greater than current level,
1562             ## so use the current level's pixel value
1563             ##
1564 153         712 $outColumn->set( $y, $layers[$z][$x]->get($y) );
1565              
1566             } elsif ( ( ( $feather > 0 ) && $diff <= $feather )
1567             || ( ( $feather < 0 ) && $diff <= $feather * -1 ) )
1568             {
1569 46         69 my $fadeAmt = $diff / abs($feather);
1570              
1571 46 50       83 if ( $feather < 0 ) {
1572 0         0 $fadeAmt = 1 - $fadeAmt;
1573             }
1574              
1575             ##
1576             ## Reference pixel value is less than current level,
1577             ## but within the feather range, so fade it
1578             ##
1579 46         319 my $color =
1580             interp( $layers[$z][$x]->get($y), $outColumn->get($y), $fadeAmt );
1581              
1582 46         223 $outColumn->set( $y, $color );
1583             }
1584              
1585 768         1050 $level += $levelOffset;
1586 768         2177 $levelOffset /= 2;
1587             }
1588              
1589 256         968 $outColumn->set( $y, interp( $outColumn->get($y), $value, .25 ) );
1590             }
1591              
1592 16         33 $out->[$x] = $outColumn;
1593 16         118 printRow($outColumn);
1594             }
1595              
1596 1         175 return $out;
1597              
1598             # return $args{smooth} ? smooth($out, %args) : $out;
1599             }
1600              
1601             sub __generator {
1602 2     2   7 my $type = shift;
1603              
1604 2         3 my $generator;
1605              
1606 2         9 for my $ltype ( @SIMPLE_TYPES, @PERLIN_TYPES ) {
1607 74 100       141 if ( $type eq $ltype ) {
1608 2         3 do {
1609 1     1   7 no strict 'refs';
  1         3  
  1         12260  
1610 2         4 $generator = \&{"Math::Fractal::Noisemaker::$type"};
  2         15  
1611             };
1612             }
1613             }
1614              
1615 2 50       10 if ( !$generator ) {
1616 0         0 usage("Unknown noise type '$type' specified");
1617             }
1618              
1619 2         6 return $generator;
1620             }
1621              
1622             sub clamp {
1623 1024     1024 0 1320 my $val = shift;
1624 1024   33     2990 my $max = shift || $MAX_COLOR;
1625              
1626 1024 50       1904 $val = 0 if $val < 0;
1627 1024 100       1776 $val = $max if $val > $max;
1628              
1629 1024         1707 return $val;
1630             }
1631              
1632             sub noise {
1633 155136     155136 0 202810 my $noise = shift;
1634 155136         185616 my $x = shift;
1635 155136         186113 my $y = shift;
1636              
1637 155136         173529 my $length = shift;
1638 155136   100     370405 my $xlen = $length || scalar @$noise;
1639 155136   66     711963 my $ylen = $length || $noise->[0]->len();
1640              
1641 155136         199520 my $thisX = int($x);
1642 155136         197786 my $thisY = int($y);
1643              
1644             #
1645             # No need to interpolate
1646             #
1647 155136 100 100     627810 if ( ( $thisX == $x ) && ( $thisY == $y ) ) {
1648 110816         554767 return $noise->[ $x % $xlen ]->get( $y % $ylen );
1649             }
1650              
1651 44320         72776 $x = ( ( $x * 1000 ) % ( $xlen * 1000 ) ) / 1000;
1652 44320         68465 $y = ( ( $y * 1000 ) % ( $ylen * 1000 ) ) / 1000;
1653              
1654 44320         64738 my $fractX = $x - $thisX;
1655 44320         64588 my $nextX = ( $x + 1 ) % $xlen;
1656              
1657 44320         64991 my $fractY = $y - $thisY;
1658 44320         58559 my $nextY = ( $y + 1 ) % $ylen;
1659              
1660 44320         55034 $thisX = $thisX % $xlen;
1661 44320         44397 $thisY = $thisY % $ylen;
1662              
1663 44320         64162 my $thisColumn = $noise->[$thisX];
1664 44320         53133 my $nextColumn = $noise->[$nextX];
1665              
1666 44320   100     187357 my $v1 = $thisColumn->get($thisY) || 0;
1667 44320   100     154536 my $v2 = $nextColumn->get($thisY) || 0;
1668 44320   100     163935 my $v3 = $thisColumn->get($nextY) || 0;
1669 44320   100     137849 my $v4 = $nextColumn->get($nextY) || 0;
1670              
1671 44320         83145 my $i1 = interp( $v1, $v2, $fractX );
1672 44320         104189 my $i2 = interp( $v3, $v4, $fractX );
1673              
1674 44320         80348 return interp( $i1, $i2, $fractY );
1675             }
1676              
1677             sub interp {
1678 140750 50   140750 1 300284 die "No interp function defined" if !$INTERP_FN;
1679              
1680 140750         248497 &$INTERP_FN(@_);
1681             }
1682              
1683             sub lerp {
1684 0   0 0 0 0 my $a = shift || 0;
1685 0   0     0 my $b = shift || 0;
1686 0   0     0 my $x = shift || 0;
1687              
1688 0 0       0 if ( $x < 0 ) {
    0          
1689 0         0 $x = 0;
1690             } elsif ( $x > 1 ) {
1691 0         0 $x = 1;
1692             }
1693              
1694 0         0 return ( $a * ( 1 - $x ) + $b * $x );
1695             }
1696              
1697             sub cosine_interp {
1698 140750   100 140750 0 296741 my $a = shift || 0;
1699 140750   100     279416 my $b = shift || 0;
1700 140750   100     347270 my $x = shift || 0;
1701              
1702 140750         196316 my $ft = ( $x * pi );
1703 140750         296839 my $f = ( 1 - cos($ft) ) * .5;
1704              
1705 140750         565053 return ( $a * ( 1 - $f ) + $b * $f );
1706             }
1707              
1708             sub wavelet {
1709 1     1 1 8 my %args = @_;
1710              
1711 1 50       5 print "Generating wavelet noise...\n" if !$QUIET;
1712              
1713 1 50       6 $args{amp} = $DEFAULT_AMP if !defined $args{amp};
1714 1   33     5 $args{len} ||= $DEFAULT_LEN;
1715 1 50       6 $args{freq} = $args{len} if !defined $args{freq};
1716              
1717 1         7 %args = defaultArgs(%args);
1718              
1719 1   33     15 my $source = $args{grid} || white( %args, len => $args{freq} );
1720              
1721 1         20 my $down = shrink( $source, %args, len => $args{freq} / 2 );
1722 1         10 my $up = grow( $down, %args, len => $args{freq} );
1723              
1724 1         9 my $out = [];
1725              
1726 1         6 my $freq = $args{freq};
1727              
1728 1         6 for ( my $x = 0 ; $x < $freq ; $x++ ) {
1729 16         92 my $column = $COLUMN_CLASS->new($freq);
1730 16         29 my $sourceColumn = $source->[$x];
1731 16         22 my $upColumn = $up->[$x];
1732              
1733 16         42 for ( my $y = 0 ; $y < $freq ; $y++ ) {
1734 256         1462 $column->set( $y,
1735             ( $args{bias} * $MAX_COLOR ) +
1736             $sourceColumn->get($y) -
1737             $upColumn->get($y) );
1738             }
1739              
1740 16         35 $out->[$x] = $column;
1741 16         37 printRow($column);
1742             }
1743              
1744 1         27 return grow( $out, %args );
1745             }
1746              
1747             sub gasket {
1748 1     1 1 10 my %args = @_;
1749              
1750 1 50       6 print "Generating gasket...\n" if !$QUIET;
1751              
1752 1   33     5 $args{len} ||= $DEFAULT_LEN;
1753 1 50       7 $args{freq} = $args{len} if !defined $args{freq};
1754 1   50     9 $args{amp} ||= 1;
1755              
1756 1         3 my $freq = $args{freq};
1757 1         3 my $amp = $args{amp} * $MAX_COLOR;
1758              
1759 1         9 %args = defaultArgs(%args);
1760              
1761 1         14 my $grid = grid( %args, len => $args{freq} );
1762              
1763 1         9 for ( my $x = 0 ; $x < $freq ; $x++ ) {
1764 16         99 $grid->[$x] = $COLUMN_CLASS->new($freq);
1765             }
1766              
1767 1     95   7 my $f1 = sub { return ( $_[0] / 2, $_[1] / 2 ) };
  95         478  
1768 1     75   5 my $f2 = sub { return ( ( $_[0] + 1 ) / 2, $_[1] / 2 ) };
  75         355  
1769 1     86   5 my $f3 = sub { return ( $_[0] / 2, ( $_[1] + 1 ) / 2 ) };
  86         12293  
1770              
1771 1   33     16 my $iters = $args{maxiter} || $freq * $freq;
1772              
1773 1         4 my $x = rand(1);
1774 1         3 my $y = rand(1);
1775              
1776 1         6 for ( my $i = 0 ; $i < $iters ; $i++ ) {
1777 256 100       488 if ( $i > 20 ) {
1778 235         354 my $thisX = ( $x * $freq ) % $freq;
1779 235         281 my $thisY = ( $y * $freq ) % $freq;
1780 235         1097 $grid->[$thisX]->set( $thisY, $MAX_COLOR );
1781             }
1782              
1783 256         319 my $rand = rand(3);
1784 256 100       659 if ( $rand < 1 ) {
    100          
1785 95         167 ( $x, $y ) = &$f1( $x, $y );
1786             } elsif ( $rand < 2 ) {
1787 75         205 ( $x, $y ) = &$f2( $x, $y );
1788             } else {
1789 86         142 ( $x, $y ) = &$f3( $x, $y );
1790             }
1791             }
1792              
1793 1         22 return grow( $grid, %args );
1794             }
1795              
1796             #
1797             # Set up IFS flame functions once
1798             #
1799 4250     4250   16880 sub _fflinear { return @_ }
1800              
1801             sub _ffsinusoidal {
1802 2101     2101   2688 my ( $x, $y ) = @_;
1803 2101         7829 return sin($x) * 3, sin($y) * 3;
1804             }
1805              
1806             sub _ffsphere {
1807 4231     4231   5252 my ( $x, $y ) = @_;
1808 4231         5997 my $n = 1 / ( ( $x * $x ) + ( $y + $y ) );
1809 4231         14020 return $x * $n, $y * $n;
1810             }
1811              
1812             sub _ffswirl {
1813 4301     4301   5075 my ( $x, $y ) = @_;
1814 4301         5940 my $rsqrd = ( ( $x * $x ) + ( $y + $y ) );
1815             return (
1816 4301         19114 ( $x * sin($rsqrd) ) - ( $y * cos($rsqrd) ),
1817             ( $x * cos($rsqrd) ) + ( $y * sin($rsqrd) )
1818             );
1819             }
1820              
1821             sub _ffhorseshoe {
1822 4356     4356   5379 my ( $x, $y ) = @_;
1823 4356         5878 my $r = sqrt( ( $x * $x ) + ( $y * $y ) );
1824 4356         5914 my $rf = 1 / ( $r * $r );
1825 4356         17220 return ( $rf * ( $x - $y ) * ( $x + $y ), $rf * 2 * $x * $y );
1826             }
1827              
1828             sub _ffpopcorn {
1829 6361     6361   9108 my ( $x, $y, $c, $f ) = @_;
1830 6361         17039 return ( $x + ( $c * sin( tan( 3 * $y ) ) ),
1831             $y + ( $f * sin( tan( 3 * $x ) ) ), );
1832             }
1833              
1834             my @flameFns;
1835              
1836             do {
1837             push @flameFns, \&_fflinear;
1838             push @flameFns, \&_ffsinusoidal;
1839             push @flameFns, \&_ffsphere;
1840             push @flameFns, \&_ffswirl;
1841             push @flameFns, \&_ffhorseshoe;
1842             push @flameFns, \&_ffpopcorn;
1843             };
1844              
1845             sub fflame {
1846 1     1 1 7 my %args = @_;
1847              
1848 1         3 my @fns;
1849              
1850 1         6 for ( my $i = 0 ; $i < @flameFns * 2 ; $i++ ) {
1851 12         40 push @fns, $flameFns[ rand(@flameFns) ];
1852             }
1853              
1854 1 50       4 print "Generating fractal flame!\n" if !$QUIET;
1855              
1856 1   33     4 $args{len} ||= $DEFAULT_LEN;
1857 1 50       7 $args{freq} = $args{len} if !defined $args{freq};
1858 1   50     8 $args{amp} ||= 1;
1859              
1860 1         2 my $freq = $args{freq};
1861 1         4 my $amp = $args{amp} * $MAX_COLOR;
1862              
1863 1         8 %args = defaultArgs(%args);
1864              
1865 1         14 my $grid = grid( %args, len => $freq );
1866              
1867 1   33     12 my $steps = $args{maxiter} || $freq * $freq * 100;
1868              
1869 1         5 my $A = rand(.125) + .25;
1870 1         4 my $B = rand(.125) + .25;
1871 1         4 my $c = rand(.125) + .25;
1872 1         4 my $d = rand(.125) + .25;
1873 1         3 my $e = rand(.125) + .25;
1874 1         3 my $f = rand(.125) + .25;
1875              
1876 1   50     8 my $scale = $args{zoom} || 1;
1877              
1878 1         3 my $x = 0;
1879 1         2 my $y = 0;
1880              
1881 1         3 my $finalX = rand($freq);
1882 1         2 my $finalY = rand($freq);
1883              
1884 1         6 for ( my $n = 0 ; $n < $steps ; $n++ ) {
1885 25600         151427 do {
1886 25600         40271 my $gx = ( ( $x * $scale * $freq ) + $finalX ) % $freq;
1887 25600         33525 my $gy = ( ( $y * $scale * $freq ) + $finalY ) % $freq;
1888              
1889 25600         31547 my $column = $grid->[$gx];
1890              
1891 25600         85172 $column->set( $gy, $column->get($gy) + 1 );
1892              
1893 25600 100       50299 if ( $n >= 20 ) {
1894 25580         86709 $column->set( $gy, $column->get($gy) + 1 );
1895             }
1896             };
1897              
1898 25600         42408 my $i = rand(@fns);
1899              
1900 25600         26704 do {
1901 25600         32271 my $fn = $fns[$i];
1902              
1903 25600         34446 my $thisX = ( $A * $x ) + ( $B * $y ) + $c;
1904 25600         30836 my $thisY = ( $d * $y ) + ( $e * $y ) + $f;
1905              
1906 25600         42696 ( $x, $y ) = &$fn( $thisX, $thisY, $c, $f );
1907             };
1908              
1909             }
1910              
1911 1         32 $grid = densemap($grid);
1912              
1913 1         53 $grid = glow( $grid, %args );
1914              
1915 1         41 return grow( $grid, %args );
1916             }
1917              
1918             sub densemap {
1919 10     10 0 27 my $grid = shift;
1920 10         27 my $invert = shift;
1921              
1922 10         23 my $xlen = scalar @$grid;
1923 10         66 my $ylen = $grid->[0]->len();
1924              
1925 10         22 my $colors = {};
1926              
1927 10         56 for ( my $x = 0 ; $x < $xlen ; $x++ ) {
1928 160         228 my $column = $grid->[$x];
1929              
1930 160         321 for ( my $y = 0 ; $y < $ylen ; $y++ ) {
1931 2560         13403 $colors->{ $column->get($y) }++;
1932             }
1933             }
1934              
1935 10         20 my @colors = keys %{$colors};
  10         496  
1936              
1937 10         94 my $i = 0;
1938 10         79 for ( sort { $a <=> $b } @colors ) {
  9769         9802  
1939 1506 50       2105 if ( $invert ) {
1940 0         0 $colors->{$_} = $MAX_COLOR - ( $i / @colors ) * $MAX_COLOR;
1941             } else {
1942 1506         2693 $colors->{$_} = ( $i / @colors ) * $MAX_COLOR;
1943             }
1944              
1945 1506         1854 $i++;
1946             }
1947              
1948 10         29 my $out = [];
1949              
1950 10         37 for ( my $x = 0 ; $x < $xlen ; $x++ ) {
1951 160         901 my $outColumn = $COLUMN_CLASS->new($ylen);
1952 160         258 my $column = $grid->[$x];
1953 160         333 for ( my $y = 0 ; $y < $ylen ; $y++ ) {
1954 2560         17829 $outColumn->set( $y, $colors->{ $column->get($y) } );
1955             }
1956 160         478 $out->[$x] = $outColumn;
1957             }
1958              
1959 10         360 return $out;
1960             }
1961              
1962             sub fern {
1963 1     1 1 8 my %args = @_;
1964              
1965 1 50       5 print "Generating fern...\n" if !$QUIET;
1966              
1967 1   33     5 $args{len} ||= $DEFAULT_LEN;
1968 1 50       6 $args{freq} = $args{len} if !defined $args{freq};
1969 1   50     7 $args{amp} ||= 1;
1970              
1971 1         2 my $freq = $args{freq};
1972 1         3 my $amp = $args{amp} * $MAX_COLOR;
1973              
1974 1         9 %args = defaultArgs(%args);
1975              
1976 1         12 my $grid = grid( %args, len => $freq );
1977              
1978 1         5 my $steps = $freq * $freq * 10;
1979              
1980 1         1 my $x = 0;
1981 1         2 my $y = 0;
1982              
1983 1   50     7 my $scale = $args{zoom} || 1;
1984              
1985 1         4 for ( my $n = 0 ; $n < $steps ; $n++ ) {
1986 2560         3954 my $gx =
1987             ( $freq - ( ( ( $x * $scale ) + 2.1818 ) / 4.8374 * $freq ) ) % $freq;
1988 2560         3390 my $gy = ( $freq - ( ( ( $y * $scale ) / 9.95851 ) * $freq ) ) % $freq;
1989              
1990 2560         2802 my $column = $grid->[$gx];
1991              
1992 2560         7949 $column->set( $gy, $column->get($gy) + sqrt( rand() * $amp ) );
1993              
1994 2560         2738 my $rand = rand();
1995              
1996 2560 100       5935 if ( $rand <= .01 ) {
    100          
    100          
1997 23         47 ( $x, $y ) = _fern1( $x, $y );
1998             } elsif ( $rand <= .08 ) {
1999 190         280 ( $x, $y ) = _fern2( $x, $y );
2000             } elsif ( $rand <= .15 ) {
2001 178         266 ( $x, $y ) = _fern3( $x, $y );
2002             } else {
2003 2169         3147 ( $x, $y ) = _fern4( $x, $y );
2004             }
2005             }
2006              
2007 1         22 return grow( $grid, %args );
2008             }
2009              
2010             sub _fern1 {
2011 23     23   28 my $x = shift;
2012 23         26 my $y = shift;
2013              
2014 23         72 return ( 0, .16 * $y );
2015             }
2016              
2017             sub _fern2 {
2018 190     190   202 my $x = shift;
2019 190         173 my $y = shift;
2020              
2021 190         677 return ( ( .2 * $x ) - (.26) * $y, ( .23 * $x ) + ( .22 * $y ) + 1.6 );
2022             }
2023              
2024             sub _fern3 {
2025 178     178   177 my $x = shift;
2026 178         183 my $y = shift;
2027              
2028 178         611 return ( ( -.15 * $x ) + ( .28 * $y ), ( .26 * $x ) + ( .24 * $y ) + .44 );
2029             }
2030              
2031             sub _fern4 {
2032 2169     2169   2215 my $x = shift;
2033 2169         1987 my $y = shift;
2034              
2035 2169         7539 return ( ( .85 * $x ) + ( .04 * $y ), ( -.04 * $x ) + ( .85 * $y ) + 1.6 );
2036             }
2037              
2038             sub mandel {
2039 2     2 1 13 my %args = @_;
2040              
2041 2 50       8 print "Generating Mandelbrot...\n" if !$QUIET;
2042              
2043 2   33     8 $args{len} ||= $DEFAULT_LEN;
2044 2 50       16 $args{freq} = $args{len} if !defined $args{freq};
2045              
2046 2         13 %args = defaultArgs(%args);
2047              
2048 2         14 my $freq = $args{freq};
2049              
2050 2   33     11 my $iters = $args{maxiter} || $freq;
2051              
2052 2   50     11 my $scale = $args{zoom} || 1;
2053              
2054 2         4 $freq *= 2;
2055              
2056 2         14 my $grid = grid( %args, len => $freq );
2057              
2058 2         13 for ( my $x = 0 ; $x < $freq ; $x += 1 ) {
2059 64         113 my $cx = ( $x / $freq ) * 2 - 1;
2060 64         79 $cx -= .5;
2061 64         274 $cx /= $scale;
2062              
2063 64         104 my $column = $grid->[$x];
2064              
2065 64         153 for ( my $y = 0 ; $y < $freq / 2 ; $y += 1 ) {
2066 1024         1675 my $cy = ( $y / $freq ) * 2 - 1;
2067 1024         1230 $cy /= $scale;
2068              
2069 1024         1311 my $zx = 0;
2070 1024         1065 my $zy = 0;
2071 1024         1060 my $n = 0;
2072 1024   100     4324 while ( ( $zx * $zx + $zy * $zy < $freq ) && $n < $iters ) {
2073 11020         15731 my $new_zx = $zx * $zx - $zy * $zy + $cx;
2074 11020         12444 $zy = 2 * $zx * $zy + $cy;
2075 11020         10643 $zx = $new_zx;
2076              
2077 11020         45097 $n++;
2078             }
2079              
2080 1024         3442 $column->set( $y, $MAX_COLOR - ( ( $n / $iters ) * $MAX_COLOR ) );
2081 1024         5482 $column->set( $freq - 1 - $y,
2082             $MAX_COLOR - ( ( $n / $iters ) * $MAX_COLOR ) );
2083             }
2084              
2085 64         140 printRow($column);
2086             }
2087              
2088 2         29 $grid = shrink( $grid, %args );
2089              
2090 2         126 $grid = grow( $grid, %args );
2091              
2092 2         66 return $grid;
2093             }
2094              
2095             sub dmandel {
2096 1     1 1 8 my %args = @_;
2097              
2098 1 50       5 print "Generating Mandelbrot...\n" if !$QUIET;
2099              
2100 1   33     3 $args{len} ||= $DEFAULT_LEN;
2101 1 50       5 $args{freq} = $args{len} if !defined $args{freq};
2102              
2103 1         7 %args = defaultArgs(%args);
2104              
2105 1         6 my $freq = $args{freq};
2106 1   33     6 my $iters = $args{maxiter} || $MAX_COLOR;
2107              
2108 1         2 my @interesting;
2109              
2110 1         2 my $prefreq = 256;
2111              
2112 1         5 for ( my $x = 0 ; $x < $prefreq ; $x += 1 ) {
2113 256         1134 my $cx = ( $x / $prefreq ) * 2 - 1;
2114              
2115 256         890 for ( my $y = 0 ; $y < $prefreq / 2 ; $y += 1 ) {
2116 32768         67265 my $cy = ( $y / $prefreq ) * 2 - 1;
2117              
2118 32768         42503 my $zx = 0;
2119 32768         37928 my $zy = 0;
2120 32768         36477 my $n = 0;
2121 32768   100     173417 while ( ( $zx * $zx + $zy * $zy < $prefreq ) && $n < $prefreq / 2 ) {
2122 1674170         2332334 my $new_zx = $zx * $zx - $zy * $zy + $cx;
2123 1674170         1897903 $zy = 2 * $zx * $zy + $cy;
2124 1674170         1757712 $zx = $new_zx;
2125 1674170         7205453 $n++;
2126             }
2127              
2128 32768         53074 my $pct = ( $n / ( $prefreq / 2 ) );
2129              
2130 32768 100 100     193730 if ( $pct > .99 && $pct < 1 ) {
2131 4         38 push @interesting, [ $cx, $cy ];
2132             }
2133             }
2134             }
2135              
2136 1         7 my $tuple = $interesting[ rand(@interesting) ];
2137              
2138 1   33     14 my $scale = $args{zoom} || 5120 + rand(128);
2139              
2140 1         3 $freq *= 2;
2141              
2142 1         17 my $grid = grid( %args, len => $freq );
2143              
2144 1         69 for ( my $x = 0 ; $x < $freq ; $x += 1 ) {
2145 32         90 my $cx = ( $x / $freq ) * 2 - 1;
2146 32         74 $cx += $tuple->[0] * $scale;
2147 32         56 $cx /= $scale;
2148              
2149 32         84 my $column = $grid->[$x];
2150              
2151 32         96 for ( my $y = 0 ; $y < $freq ; $y += 1 ) {
2152 1024         2049 my $cy = ( $y / $freq ) * 2 - 1;
2153              
2154 1024         1885 $cy += $tuple->[1] * $scale;
2155 1024         2024 $cy /= $scale;
2156 1024         1517 my $cyKey = $cy * $scale;
2157              
2158 1024         1458 my $zx = 0;
2159 1024         1486 my $zy = 0;
2160 1024         2084 my $n = 0;
2161 1024   66     7612 while ( ( $zx * $zx + $zy * $zy < $freq ) && $n < $iters ) {
2162 57714         284624 my $new_zx = $zx * $zx - $zy * $zy + $cx;
2163 57714         161863 $zy = 2 * $zx * $zy + $cy;
2164 57714         86565 $zx = $new_zx;
2165 57714         455189 $n++;
2166             }
2167              
2168 1024         23992 my $color = $MAX_COLOR - ( ( $n / ( $iters - 1 ) ) * $MAX_COLOR );
2169              
2170             # $color = 0 if $color >= $MAX_COLOR;
2171              
2172 1024         8934 $column->set( $y, $color );
2173             }
2174              
2175 32         224 printRow($column);
2176             }
2177              
2178 1         21 $grid = shrink( $grid, %args );
2179              
2180 1         150 $grid = grow( $grid, %args );
2181              
2182 1         826 return tile($grid,%args);
2183             }
2184              
2185             sub buddha {
2186 1     1 1 10 my %args = @_;
2187              
2188 1 50       5 print "Generating Buddhabrot (this will take a while)...\n" if !$QUIET;
2189              
2190 1   33     6 $args{len} ||= $DEFAULT_LEN;
2191 1 50       7 $args{freq} = $args{len} if !defined $args{freq};
2192              
2193 1         8 %args = defaultArgs(%args);
2194              
2195 1         8 my $freq = $args{freq};
2196              
2197 1   50     7 my $iters = $args{maxiter} || 4096;
2198              
2199 1         3 my $gap = $args{gap};
2200              
2201 1         9 my $grid = grid( %args, len => $freq, bias => 0 );
2202              
2203             #
2204             # Zooming in just makes buddhabrots disappear
2205             #
2206 1   50     11 my $scale = $args{zoom} || 1;
2207              
2208 1         6 for ( my $x = 0 ; $x < $freq ; $x++ ) {
2209 16         110 for ( my $y = 0 ; $y < $freq / 2 ; $y++ ) {
2210 128 50       599 next if rand() < $gap;
2211              
2212 128         338 my $cx = ( $x / $freq ) * 2 - 1;
2213 128         630 $cx -= .5;
2214              
2215 128         375 my $cy = ( $y / $freq ) * 2 - 1;
2216              
2217 128         166 $cx /= $scale;
2218 128         159 $cy /= $scale;
2219              
2220 128         414 my $zx = 0;
2221 128         192 my $zy = 0;
2222 128         218 my $n = 0;
2223 128   100     2095 while ( ( $zx * $zx + $zy * $zy < $freq ) && $n < $iters ) {
2224 181050         322905 my $new_zx = $zx * $zx - $zy * $zy + $cx;
2225 181050         253545 $zy = 2 * $zx * $zy + $cy;
2226 181050         256436 $zx = $new_zx;
2227 181050         895783 $n++;
2228             }
2229              
2230 128 100       646 next if $n == $iters;
2231 84 100       431 next if $n <= sqrt($iters);
2232              
2233 1         5 $zx = 0;
2234 1         3 $zy = 0;
2235 1         2 $n = 0;
2236 1   66     13 while ( ( $zx * $zx + $zy * $zy < $freq ) && $n < $iters ) {
2237 155         271 my $new_zx = $zx * $zx - $zy * $zy + $cx;
2238 155         161 $zy = 2 * $zx * $zy + $cy;
2239 155         152 $zx = $new_zx;
2240 155         129 $n++;
2241              
2242 155         239 my $thisX = ( ( ( $zx + 1 ) / 2 ) * $freq + ( $freq * .25 ) ) % $freq;
2243 155         227 my $thisY = ( ( $zy + 1 ) / 2 ) * $freq % $freq;
2244              
2245 155         895 $grid->[$thisY]->set( $thisX, $grid->[$thisY]->get($thisX) + 25 );
2246 155         1159 $grid->[ $freq - 1 - $thisY ]
2247             ->set( $thisX, $grid->[ $freq - 1 - $thisY ]->get($thisX) + 25 );
2248             }
2249             }
2250 16         139 printRow( $grid->[$x] );
2251             }
2252              
2253 1         10 $grid = densemap( $grid );
2254              
2255 1         34 $grid = grow( $grid, %args );
2256              
2257 1         29 return $grid;
2258             }
2259              
2260             # Re-maps pixel values along the north and south edges of the source
2261             # image using polar coordinates, slowly blending back into original
2262             # pixel values towards the middle.
2263             sub spheremap {
2264 0     0 0 0 my $grid = shift;
2265 0         0 my %args = defaultArgs(@_);
2266              
2267 0 0       0 print "Generating spheremap...\n" if !$QUIET;
2268              
2269 0         0 my $len = $args{len};
2270 0         0 my $offset = $len / 2;
2271              
2272 0         0 my $out = [];
2273              
2274 0         0 my $srclen = scalar( @{$grid} );
  0         0  
2275 0         0 my $scale = $srclen / $len;
2276              
2277             #
2278             # Polar regions
2279             #
2280 0         0 my $xOffset = $len / 4;
2281              
2282 0         0 for ( my $x = 0 ; $x < $len ; $x++ ) {
2283 0         0 my $column = $COLUMN_CLASS->new($len);
2284              
2285 0         0 for ( my $y = 0 ; $y < $len ; $y++ ) {
2286 0         0 my ( $cartX, $cartY, $cartZ ) = cartCoords( $x, $y, $len, $scale );
2287              
2288             ### North Pole
2289 0         0 $column->set( $y / 2,
2290             noise( $grid, $xOffset + ( ( $srclen - $cartX ) / 2 ), $cartY / 2 ) );
2291              
2292             ### South Pole
2293 0         0 $column->set(
2294             $len - 1 - ( $y / 2 ),
2295             noise(
2296             $grid,
2297             $xOffset + ( $cartX / 2 ),
2298             ( $offset * $scale ) + ( $cartY / 2 )
2299             )
2300             );
2301             }
2302              
2303 0         0 $out->[$x] = $column;
2304             }
2305              
2306 0         0 $grid = grow( $grid, %args, len => $len * 2 );
2307              
2308             #
2309             # Equator (cover up the unsightly seam left by the above pass)
2310             #
2311 0         0 for ( my $x = 0 ; $x < $len ; $x++ ) {
2312 0         0 my $column = $out->[$x];
2313              
2314 0         0 for ( my $y = 0 ; $y < $len ; $y++ ) {
2315 0         0 my $diff = abs( $offset - $y );
2316 0         0 my $pct = ( $diff / $offset );
2317              
2318 0         0 my $srcY = $scale * $y * 2;
2319 0         0 $srcY += ( $offset / 2 ) * $scale;
2320              
2321 0         0 my $source = noise( $grid, $scale * $x * 2, $srcY / 2 );
2322              
2323 0   0     0 my $target = $column->get($y) || 0;
2324              
2325 0         0 $column->set( $y, interp( $source, $target, $pct ) );
2326             }
2327             }
2328              
2329 0         0 return $out;
2330             }
2331              
2332             sub cartCoords {
2333 0     0 0 0 my $x = shift;
2334 0         0 my $y = shift;
2335 0         0 my $len = shift;
2336 0   0     0 my $scale = shift || 1;
2337              
2338 0         0 my $thisLen = $len * $scale;
2339              
2340 0         0 $x = ( $x * $scale ) % $thisLen;
2341 0         0 $y = ( $y * $scale ) % $thisLen;
2342              
2343 0         0 my $theta = deg2rad( ( $x / $thisLen ) * 360 );
2344 0         0 my $phi = deg2rad( ( $y / $thisLen ) * 90 );
2345              
2346 0         0 my ( $cartX, $cartY, $cartZ ) =
2347             spherical_to_cartesian( $DEFAULT_RHO, $theta, $phi );
2348              
2349 0         0 $cartX = int( ( ( $cartX + 1 ) / 2 ) * $thisLen );
2350 0         0 $cartY = int( ( ( $cartY + 1 ) / 2 ) * $thisLen );
2351 0         0 $cartZ = int( ( ( $cartZ + 1 ) / 2 ) * $thisLen );
2352              
2353 0         0 return ( $cartX, $cartY, $cartZ );
2354             }
2355              
2356             ##
2357             ## Look up color values using vertical offset
2358             ##
2359             sub vertclut {
2360 0     0 0 0 my $grid = shift;
2361 0         0 my %args = @_;
2362              
2363 0 0       0 print "Applying CLUT...\n" if !$QUIET;
2364              
2365 0         0 my $palette = Imager->new;
2366 0 0       0 $palette->read( file => $args{clut} ) || die $palette->errstr;
2367              
2368 0         0 my $srcHeight = $palette->getheight();
2369 0         0 my $srcWidth = $palette->getwidth();
2370              
2371 0         0 my $xlen = scalar @$grid;
2372 0         0 my $ylen = $grid->[0]->len();
2373              
2374 0         0 my $out = Imager->new(
2375             xsize => $xlen,
2376             ysize => $ylen,
2377             );
2378              
2379 0         0 for ( my $x = 0 ; $x < $xlen ; $x++ ) {
2380 0         0 my $column = $grid->[$x];
2381              
2382 0         0 for ( my $y = 0 ; $y < $ylen ; $y++ ) {
2383 0         0 my $gray = $column->get($y);
2384              
2385 0         0 my $srcY = $y / $ylen;
2386              
2387 0         0 $out->setpixel(
2388             x => $x,
2389             y => $y,
2390             color => $palette->getpixel(
2391             x =>
2392             clamp( ( $gray / $MAX_COLOR ) * ( $srcWidth - 1 ), $srcWidth - 1 ),
2393             y => clamp( $srcY * ( $srcHeight - 1 ), $srcHeight - 1 ),
2394             )
2395             );
2396             }
2397             }
2398              
2399 0         0 return $out;
2400             }
2401              
2402             ##
2403             ## Look up color values from bottom left corner to top right corner
2404             ##
2405             sub hypoclut {
2406 0     0 0 0 my $grid = shift;
2407 0         0 my %args = @_;
2408              
2409 0 0       0 print "Applying corner-to-corner CLUT...\n" if !$QUIET;
2410              
2411 0         0 my $palette = Imager->new;
2412 0 0       0 $palette->read( file => $args{clut} ) || die $palette->errstr;
2413              
2414 0         0 my $srcHeight = $palette->getheight();
2415 0         0 my $srcWidth = $palette->getwidth();
2416              
2417 0         0 my $xlen = scalar @$grid;
2418 0         0 my $ylen = $grid->[0]->len();
2419              
2420 0         0 my $out = Imager->new(
2421             xsize => $xlen,
2422             ysize => $ylen,
2423             );
2424              
2425 0         0 for ( my $x = 0 ; $x < $xlen ; $x++ ) {
2426 0         0 my $column = $grid->[$x];
2427              
2428 0         0 for ( my $y = 0 ; $y < $ylen ; $y++ ) {
2429 0         0 my $gray = $column->get($y);
2430              
2431 0         0 my $color = $palette->getpixel(
2432             x => ( clamp($gray) / $MAX_COLOR * ( $srcWidth - 1 ) ),
2433             y => $srcHeight - 1 - ( clamp($gray) / $MAX_COLOR * ( $srcHeight - 1 ) ),
2434             );
2435              
2436 0         0 $out->setpixel(
2437             x => $x,
2438             y => $y,
2439             color => $color
2440             );
2441             }
2442             }
2443              
2444 0         0 return $out;
2445             }
2446              
2447             sub spirals {
2448 1     1 1 7 my %args = @_;
2449              
2450 1   33     4 $args{len} ||= $DEFAULT_LEN;
2451 1 50       7 $args{freq} = $args{len} if !defined $args{freq};
2452              
2453 1         3 my $voronoi = $args{voronoi};
2454              
2455 1         7 %args = defaultArgs(%args);
2456              
2457 1         7 my $len = $args{freq};
2458              
2459 1         8 my $grid = grid( %args, len => $len, bias => 0 );
2460              
2461 1         5 my $half = $len / 2;
2462 1         1 my $radius = $half;
2463 1 0   0   7 my $rand = sub { ( rand() >= .5 ) ? 1 : -1 };
  0         0  
2464              
2465 1 50       4 $args{amp} = $DEFAULT_AMP if !defined $args{amp};
2466              
2467 1         3 my $bias = $args{bias} * $MAX_COLOR;
2468 1         2 my $amp = $args{amp} * $MAX_COLOR;
2469              
2470 1         5 for ( my $n = 0 ; $n < sqrt($len) * 2 ; $n++ ) {
2471 8         19 my ( $coils, $arms, $steps );
2472              
2473 8 50       29 if ($voronoi) {
2474 0         0 $coils = 360;
2475 0         0 $arms = 1;
2476 0         0 $steps = $len * $len * 2;
2477             } else {
2478 8         34 $coils = int( rand(5) );
2479 8         20 $arms = int( rand(7) ) + 1;
2480 8         14 $steps = 180 + rand(180);
2481             }
2482              
2483 8         25 my $aroundStep = ( $coils / $steps );
2484 8         19 my $aroundRads = $aroundStep * 2 * ( 22 / 7 );
2485              
2486 8         20 my $centerX = rand($len);
2487 8         14 my $centerY = rand($len);
2488              
2489 8         15 my $rotation = rand() * 2 * 22 / 7;
2490              
2491 8         21 for ( my $i = 1 ; $i <= $steps ; $i += 1 ) {
2492 1977         3643 my $away = $radius**( $i / $steps );
2493              
2494 1977         4113 for ( my $r = 0 ; $r < $arms ; $r += 1 / $arms ) {
2495 35216         50192 my $around = ( $i * $aroundRads ) + $rotation + ( $r * 2 * ( 22 / 7 ) );
2496              
2497 35216         54940 my $x = ( $centerX + cos($around) * $away ) % $len;
2498 35216         48581 my $y = ( $centerY + sin($around) * $away ) % $len;
2499              
2500 35216         41483 my $column = $grid->[$x];
2501 35216         55766 my $color = $MAX_COLOR - ( ( ( $i - 1 ) / ( $steps - 1 ) ) * $MAX_COLOR );
2502              
2503 35216 100       151252 if ( $column->get($y) < $color ) {
2504 476         2057 $column->set( $y, $color );
2505             }
2506             }
2507             }
2508              
2509 8         72 $grid->[$centerX]->set( $centerY, $MAX_COLOR );
2510             }
2511              
2512 1 50       6 if ($voronoi) {
2513 0         0 $grid = densemap($grid);
2514              
2515 0         0 for ( my $x = 0 ; $x < $len ; $x++ ) {
2516 0         0 my $column = $grid->[$x];
2517              
2518 0         0 for ( my $y = 0 ; $y < $len ; $y++ ) {
2519 0         0 $column->set( $y,
2520             $MAX_COLOR - ( ( $column->get($y) / $MAX_COLOR ) * $MAX_COLOR ) );
2521             }
2522             }
2523             } else {
2524 1         18 $grid = glow( $grid, %args );
2525             }
2526              
2527 1         41 return grow( $grid, %args );
2528             }
2529              
2530             sub dla {
2531 1     1 1 8 my %args = @_;
2532              
2533 1   33     9 $args{bias} ||= $DEFAULT_BIAS;
2534 1   33     7 $args{amp} ||= $DEFAULT_AMP;
2535 1   33     4 $args{len} ||= $DEFAULT_LEN;
2536 1   33     7 $args{freq} ||= $DEFAULT_FREQ;
2537              
2538 1         8 %args = defaultArgs(%args);
2539              
2540 1         6 my $amp = $args{amp};
2541 1         2 my $len = $args{len};
2542 1         2 my $freq = $args{freq};
2543              
2544 1         2 my $grid;
2545              
2546 1 50       4 if ( $args{in} ) {
2547 1         8 $grid = infile( %args, len => $len );
2548             } else {
2549 0         0 $grid = grid( %args, bias => 0 );
2550              
2551 0 0       0 if ( $args{points} ) {
2552 0         0 for ( @{$args{points}} ) {
  0         0  
2553 0         0 $grid->[ $_->[0] % $len ]->set($_->[1] % $len, $amp);
2554             }
2555             } else {
2556 0         0 for ( my $i = 0 ; $i <= $freq; $i++ ) {
2557 0         0 $grid->[ rand($len) ]->set(rand($len), $amp);
2558             }
2559             }
2560             }
2561              
2562 1         24 my @points;
2563              
2564 1         3 my $branches = $len * $len / 4;
2565              
2566 1         6 for ( my $i = 0 ; $i < $branches ; $i++ ) {
2567 64         165 push @points, [ rand($len), rand($len) ];
2568             }
2569              
2570 1         2 my $prev = 0;
2571              
2572 1         4 my $buf = $|;
2573 1         2 $| = 1;
2574              
2575 1         9 while (@points) {
2576 5         9 my $color = ( @points / $branches ) * $MAX_COLOR;
2577              
2578 5 50 33     13 print scalar(@points) . " " if !$QUIET && ( $prev != @points );
2579              
2580 5         6 $prev = scalar(@points);
2581              
2582 5         7 my @newPoints;
2583              
2584 5         12 for ( my $i = 0 ; $i < @points ; $i++ ) {
2585 83         113 my $x = $points[$i]->[0] % $len;
2586 83         82 my $y = $points[$i]->[1] % $len;
2587              
2588 83         79 my $column = $grid->[$x];
2589              
2590 83 100 100     782 if ( ( $column->get($y) )
      100        
      66        
      100        
      100        
      100        
      100        
      66        
2591             || ( $grid->[ ( $x + 1 ) % $len ]->get($y) )
2592             || ( $grid->[ ( $x - 1 ) % $len ]->get($y) )
2593             || ( $column->get( ( $y + 1 ) % $len ) )
2594             || ( $column->get( ( $y - 1 ) % $len ) )
2595             || ( $grid->[ ( $x + 1 ) % $len ]->get( ( $y + 1 ) % $len ) )
2596             || ( $grid->[ ( $x + 1 ) % $len ]->get( ( $y - 1 ) % $len ) )
2597             || ( $grid->[ ( $x - 1 ) % $len ]->get( ( $y - 1 ) % $len ) )
2598             || ( $grid->[ ( $x - 1 ) % $len ]->get( ( $y + 1 ) % $len ) ) )
2599             {
2600 64         183 $column->set( $y, $color );
2601             } else {
2602 19         60 push @newPoints, [ $x, $y ];
2603             }
2604             }
2605              
2606 5         24 @points = @newPoints;
2607              
2608 5 100       8 last if !@points;
2609              
2610 4         10 for ( my $i = 0 ; $i < @points ; $i++ ) {
2611 19         22 my $x = $points[$i]->[0] % $len;
2612 19         20 my $y = $points[$i]->[1] % $len;
2613              
2614 19         20 my $offset = rand(6) - 3;
2615 19         24 $points[$i]->[0] = $x + $offset % $len;
2616              
2617 19         19 $offset = rand(6) - 3;
2618 19         45 $points[$i]->[1] = $y + $offset % $len;
2619             }
2620             }
2621              
2622 1         4 $| = $buf;
2623              
2624 1         9 return grow($grid, %args);
2625             }
2626              
2627              
2628             sub glow {
2629 9     9 0 25 my $grid = shift;
2630 9         108 my %args = @_;
2631              
2632 9   33     42 my $len = $args{len} || $DEFAULT_LEN;
2633              
2634 9         45 my $down = shrink($grid, len => $len/2);
2635 9         60 $down = smooth($down, len => $len/2);
2636 9         179 my $smoothed = grow($down, len => $len);
2637              
2638 9         51 for ( my $x = 0; $x < $len; $x++ ) {
2639 144         223 my $column = $grid->[$x];
2640 144         235 my $smoothedColumn = $smoothed->[$x];
2641              
2642 144         301 for ( my $y = 0 ; $y < $len ; $y++ ) {
2643 2304         11960 $smoothedColumn->set( $y, $smoothedColumn->get($y) + $column->get($y) );
2644             }
2645             }
2646              
2647 9         362 return $smoothed;
2648             }
2649              
2650             sub tesla {
2651 1     1 1 9 my %args = @_;
2652              
2653 1   50     10 $args{freq} ||= 8;
2654              
2655 1         9 return fur( %args, tesla => 1 );
2656             }
2657              
2658             sub fur {
2659 2     2 1 15 my %args = @_;
2660              
2661             # $args{octaves} = 4 if !defined $args{octaves};
2662 2 100       11 $args{freq} = 2 if !defined $args{freq};
2663              
2664 2         15 my $multires = multires( %args, amp => 1, bias => 0 );
2665 2         25 my $grid = grid(%args);
2666              
2667 2   33     15 my $len = $args{len} || $DEFAULT_LEN;
2668              
2669 2         11 %args = defaultArgs(%args);
2670              
2671 2         13 my @worms;
2672              
2673 2         5 my ( $numWorms, $threadLen );
2674              
2675 2 100       12 if ( $args{tesla} ) {
2676 1         2 $numWorms = $len;
2677 1         3 $threadLen = $len;
2678             } else {
2679 1         2 $numWorms = $len * $len;
2680 1         4 $threadLen = sqrt($len);
2681             }
2682              
2683 2         11 for ( my $i = 0 ; $i < $numWorms ; $i++ ) {
2684 272         613 my $worm = [ rand($len), rand($len) ];
2685              
2686 272         651 push @worms, $worm;
2687             }
2688              
2689 2         12 for ( my $i = 0 ; $i < $threadLen ; $i++ ) {
2690 20         27 my $w = 0;
2691              
2692 20         34 for my $worm (@worms) {
2693 1280         1745 my $x = $worm->[0];
2694 1280         1355 my $y = $worm->[1];
2695              
2696 1280         1740 my $multiresColumn = $multires->[$x];
2697 1280         1489 my $column = $grid->[$x];
2698              
2699 1280         3662 my $heading = ( $multiresColumn->get($y) / $MAX_COLOR ) * 360;
2700              
2701 1280 100       2729 if ( $args{tesla} ) {
2702             ### kink it up
2703 256         340 $heading += ( $w / $numWorms ) * 45;
2704             }
2705              
2706 1280         5469 $column->set( $y,
2707             $column->get($y) + 1 -
2708             ( abs( $i - ( $threadLen / 2 ) ) / ( $threadLen / 2 ) ) );
2709              
2710 1280         2459 ( $x, $y ) = translate( $x, $y, $heading, 1 );
2711 1280         2066 $x = ( $x * 100 ) % ( $len * 100 );
2712 1280         1570 $y = ( $y * 100 ) % ( $len * 100 );
2713 1280         1752 $worm->[0] = $x / 100;
2714 1280         1598 $worm->[1] = $y / 100;
2715              
2716 1280         2299 $w++;
2717             }
2718             }
2719              
2720 2         10 $grid = densemap( $grid );
2721              
2722 2 100       49 if ( $args{tesla} ) {
2723 1         12 $grid = glow( $grid, %args );
2724             }
2725              
2726 2         144 return $grid;
2727             }
2728              
2729             sub emboss {
2730 0     0 1 0 my $grid = shift;
2731 0         0 my %args = @_;
2732              
2733 0         0 my $xlen = scalar @$grid;
2734 0         0 my $ylen = $grid->[0]->len();
2735              
2736 0 0       0 print "Generating light map\n" if !$QUIET;
2737              
2738 0         0 my $lightmap = [];
2739              
2740 0         0 my $angle = rand(360);
2741              
2742 0         0 for ( my $x = 0 ; $x < $xlen ; $x += 1 ) {
2743 0         0 $lightmap->[$x] = [];
2744              
2745 0         0 my $lightmapColumn = $COLUMN_CLASS->new($ylen);
2746 0         0 my $column = $grid->[$x];
2747              
2748 0         0 for ( my $y = 0 ; $y < $ylen; $y += 1 ) {
2749 0         0 my $value;
2750              
2751 0         0 my ( $neighborX, $neighborY ) = translate( $x, $y, $angle, 1.5 );
2752              
2753 0         0 my $neighbor = noise( $grid, $neighborX, $neighborY );
2754              
2755 0         0 my $diff = $column->get($y) - $neighbor;
2756              
2757 0         0 $lightmapColumn->set( $y, $MAX_COLOR - $diff );
2758             }
2759              
2760 0         0 $lightmap->[$x] = $lightmapColumn;
2761             }
2762              
2763 0         0 return grow($lightmap,%args);
2764             }
2765              
2766             #
2767             # Make a seamless tile from non-seamless input, such as an infile
2768             #
2769             sub tile {
2770 9     9 1 31 my $grid = shift;
2771 9         98 my %args = @_;
2772              
2773 9 100       46 my $dirs = defined $args{tile} ? $args{tile} : 1;
2774 9 100       42 return $grid if !$dirs;
2775              
2776 8         15 my $len = scalar( @{$grid} );
  8         22  
2777              
2778 8         68 my $out = grid( %args, len => $len );
2779              
2780 8         37 my $border = $len / 2;
2781              
2782 8         48 for ( my $x = 0 ; $x < $len ; $x++ ) {
2783 128         182 my $outColumn = $out->[$x];
2784 128         167 my $column = $grid->[$x];
2785              
2786 128         270 for ( my $y = 0 ; $y < $len ; $y++ ) {
2787 2048         13832 $outColumn->set( $y, $column->get($y) );
2788             }
2789             }
2790              
2791 8 50 33     43 if ( $dirs == 1 || $dirs == 2 ) {
2792 8         39 for ( my $x = 0 ; $x < $len ; $x++ ) {
2793 128         281 my $outColumn = $out->[$x];
2794              
2795 128         357 for ( my $y = 0 ; $y < $len ; $y++ ) {
2796 2048         3348 my $thisX = ( $x - ( $len / 2 ) ) % $len;
2797              
2798 2048         2555 my $blend = 1;
2799 2048 100       5930 if ( $x < $border ) {
    100          
2800 1024         2058 $blend = 1 - ( ( $border - $x ) / $border );
2801             } elsif ( ( $len - $x ) < $border ) {
2802 896         1183 $blend = ( $len - $x ) / $border;
2803             }
2804              
2805 2048         10200 $outColumn->set( $y,
2806             interp( $out->[$thisX]->get($y), $outColumn->get($y), $blend ) );
2807             }
2808             }
2809              
2810 8         36 for ( my $x = 0 ; $x < $len ; $x++ ) {
2811 128         210 my $outColumn = $grid->[$x];
2812 128         156 my $column = $out->[$x];
2813              
2814 128         295 for ( my $y = 0 ; $y < $len ; $y++ ) {
2815 2048         11249 $outColumn->set( $y, $column->get($y) );
2816             }
2817             }
2818             }
2819              
2820 8 50 33     40 if ( $dirs == 1 || $dirs == 3 ) {
2821 8         36 for ( my $x = 0 ; $x < $len ; $x++ ) {
2822 128         196 my $outColumn = $out->[$x];
2823              
2824 128         331 for ( my $y = 0 ; $y < $len ; $y++ ) {
2825 2048         2666 my $thisX = $x;
2826 2048         3570 my $thisY = ( $y - ( $len / 2 ) ) % $len;
2827              
2828 2048         2516 my $blend = 1;
2829 2048 100       4403 if ( $y < $border ) {
    100          
2830 1024         1707 $blend = 1 - ( ( $border - $y ) / $border );
2831             } elsif ( ( $len - $y ) < $border ) {
2832 896         1212 $blend = ( $len - $y ) / $border;
2833             }
2834              
2835 2048         10048 $outColumn->set( $y,
2836             interp( $out->[$thisX]->get($thisY), $outColumn->get($y), $blend ) );
2837             }
2838             }
2839             }
2840              
2841 8         335 return $out;
2842             }
2843              
2844             #
2845             # Translate X and Y coordinates according to heading by N units
2846             #
2847             sub translate {
2848 5888     5888 0 7692 my $x = shift;
2849 5888         6354 my $y = shift;
2850 5888         6256 my $heading = shift; # Euler angle
2851 5888         6468 my $units = shift; # Pixels
2852              
2853             #
2854             # A
2855             # |\
2856             # b | \ c
2857             # | \
2858             # |___\
2859             # C a B
2860             #
2861             #
2862             # 0
2863             # 3/NW | 0/NE
2864             # |
2865             # 270 ----+---- 90
2866             # |
2867             # 2/SW | 1/SE
2868             # 180
2869             #
2870              
2871 5888         5932 my $quadrant = 0; # 0 NE, 1 SE, 2 SW, 3 NW
2872              
2873 5888         8130 my $relativeHeading = $heading % 360;
2874              
2875 5888 100       24670 if ( $relativeHeading == 0 ) {
    100          
    100          
    100          
2876 73         179 return $x, $y - $units;
2877             } elsif ( $relativeHeading == 90 ) {
2878 73         176 return $x + $units, $y;
2879             } elsif ( $relativeHeading == 180 ) {
2880 71         176 return $x, $y + $units;
2881             } elsif ( $relativeHeading == 270 ) {
2882 78         303 return $x - $units, $y;
2883             }
2884              
2885 5593         12062 until ( $relativeHeading < 90 ) {
2886 8996         9347 $relativeHeading -= 90;
2887              
2888 8996         9476 $quadrant += 1;
2889 8996 50       24260 $quadrant = 0 if $quadrant > 3;
2890             }
2891              
2892 5593         6135 my $c = $units;
2893 5593         5742 my ( $b, $a );
2894              
2895 5593         5457 my $A = $relativeHeading;
2896 5593         5515 my $C = 90;
2897 5593         6550 my $B = 180 - 90 - $heading;
2898              
2899 5593         13369 my $rad = deg2rad($A);
2900 5593         44595 $a = sin($rad) * $c;
2901 5593         9573 $b = cos($rad) * $c;
2902              
2903 5593 100       13848 if ( $quadrant == 0 ) {
    100          
    100          
2904 1126         1399 $x += $a;
2905 1126         1639 $y -= $b;
2906             } elsif ( $quadrant == 1 ) {
2907 1556         1868 $x += $b;
2908 1556         2078 $y += $a;
2909             } elsif ( $quadrant == 2 ) {
2910 1293         1576 $x -= $a;
2911 1293         1710 $y += $b;
2912             } else {
2913 1618         2034 $x -= $b;
2914 1618         2405 $y -= $a;
2915             }
2916              
2917 5593         13040 return $x, $y;
2918             }
2919              
2920             sub moire {
2921 2     2 1 16 my %args = @_;
2922              
2923 2   33     11 $args{len} ||= $DEFAULT_LEN;
2924 2         6 my $len = $args{len};
2925              
2926 2   100     11 $args{freq} ||= 64;
2927 2         5 my $freq = $args{freq};
2928              
2929 2         13 %args = defaultArgs(%args);
2930              
2931 2         25 my $grid = grid( %args, len => $len );
2932              
2933             # Magic number is magic
2934 2         13 my $scale = ( .842 * ( $len / $freq ) ) / 4;
2935              
2936 2         11 for ( my $x = 0 ; $x < $len ; $x++ ) {
2937 32         48 my $column = $grid->[$x];
2938              
2939 32         70 for ( my $y = 0 ; $y < $len ; $y++ ) {
2940 512         2255 $column->set( $y,
2941             sin( ( $x / $scale ) * ( $y / $scale ) / 180 * pi ) * $args{bias} );
2942             }
2943             }
2944              
2945 2         17 $grid = tile( $grid, %args );
2946              
2947 2         91 $grid = glow( $grid, %args );
2948              
2949 2         69 return $grid;
2950             }
2951              
2952             sub textile {
2953 1     1 1 6 my %args = defaultArgs(@_);
2954              
2955 1         55 my $grid = moire(
2956             %args,
2957             freq => ( ( 1024 + rand(1024) ) * 2 ) + 1,
2958             square => 1,
2959             );
2960              
2961 1         21 return smooth( $grid, %args );
2962             }
2963              
2964             sub sparkle {
2965 1     1 1 8 my %args = @_;
2966              
2967 1   33     6 $args{len} ||= $DEFAULT_LEN;
2968 1 50       7 $args{freq} = $args{len} if !defined $args{freq};
2969              
2970 1         9 my $stars = stars(%args);
2971 1         20 $stars = lsmooth( $stars, %args );
2972              
2973 1         41 my $stars0 = stars( %args, amp => .25 );
2974              
2975 1         17 %args = defaultArgs(%args);
2976              
2977 1         16 my $out = grid(%args);
2978              
2979 1         5 my $len = $args{len};
2980              
2981 1         6 for ( my $x = 0 ; $x < $len ; $x++ ) {
2982 16         31 my $col0 = $stars0->[$x];
2983 16         20 my $col1 = $stars->[$x];
2984 16         21 my $outColumn = $out->[$x];
2985              
2986 16         37 for ( my $y = 0 ; $y < $len ; $y++ ) {
2987 256         525 my $cv = $col0->get($y);
2988 256         478 my $sv = $col1->get($y);
2989              
2990 256         884 $outColumn->set( $y, $sv + $cv );
2991             }
2992             }
2993              
2994 1         10 return glow( $out, %args );
2995             }
2996              
2997             sub delta {
2998 0     0 1 0 my $noise1 = shift;
2999 0         0 my $noise2 = shift;
3000              
3001 0         0 my %args = defaultArgs(@_);
3002              
3003 0         0 $noise1 = grow($noise1, %args);
3004 0         0 $noise2 = grow($noise2, %args);
3005              
3006 0         0 my $len = $args{len};
3007 0         0 my $grid = grid(%args);
3008              
3009 0         0 for ( my $x = 0 ; $x < $len ; $x++ ) {
3010 0         0 my $column = $grid->[$x];
3011 0         0 my $n1col = $noise1->[$x];
3012 0         0 my $n2col = $noise2->[$x];
3013              
3014 0         0 for ( my $y = 0 ; $y < $len ; $y++ ) {
3015 0         0 $column->set( $y, abs( $n1col->get($y) - $n2col->get($y) ) );
3016             }
3017             }
3018              
3019 0         0 return $grid;
3020             }
3021              
3022             sub chiral {
3023 0     0 1 0 my $noise1 = shift;
3024 0         0 my $noise2 = shift;
3025              
3026 0         0 my %args = defaultArgs(@_);
3027              
3028 0         0 $noise1 = grow($noise1, %args);
3029 0         0 $noise2 = grow($noise2, %args);
3030              
3031 0         0 my $len = $args{len};
3032 0         0 my $grid = grid(%args);
3033              
3034 0         0 for ( my $x = 0 ; $x < $len ; $x++ ) {
3035 0         0 my $column = $grid->[$x];
3036 0         0 my $n1col = $noise1->[$x];
3037 0         0 my $n2col = $noise2->[$x];
3038              
3039 0         0 for ( my $y = 0 ; $y < $len ; $y++ ) {
3040 0 0       0 if ( $n1col->get($y) > $n2col->get($y) ) {
3041 0         0 $column->set( $y, $n1col->get($y) );
3042             } else {
3043 0         0 $column->set( $y, $n2col->get($y) );
3044             }
3045             }
3046             }
3047              
3048 0         0 return $grid;
3049             }
3050              
3051             sub add {
3052 0     0 0 0 my $noise1 = shift;
3053 0         0 my $noise2 = shift;
3054              
3055 0         0 my %args = defaultArgs(@_);
3056              
3057 0         0 $noise1 = grow($noise1, %args);
3058 0         0 $noise2 = grow($noise2, %args);
3059              
3060 0         0 my $len = $args{len};
3061 0         0 my $grid = grid(%args);
3062              
3063 0         0 for ( my $x = 0 ; $x < $len ; $x++ ) {
3064 0         0 my $column = $grid->[$x];
3065 0         0 my $n1col = $noise1->[$x];
3066 0         0 my $n2col = $noise2->[$x];
3067              
3068 0         0 for ( my $y = 0 ; $y < $len ; $y++ ) {
3069 0         0 $column->set( $y, $n1col->get($y) + $n2col->get($y) );
3070             }
3071             }
3072              
3073 0         0 return $grid;
3074             }
3075              
3076             sub avg {
3077 0     0 0 0 my $noise1 = shift;
3078 0         0 my $noise2 = shift;
3079              
3080 0         0 my %args = defaultArgs(@_);
3081              
3082 0         0 $noise1 = grow($noise1, %args);
3083 0         0 $noise2 = grow($noise2, %args);
3084              
3085 0         0 my $len = $args{len};
3086 0         0 my $grid = grid(%args);
3087              
3088 0         0 for ( my $x = 0 ; $x < $len ; $x++ ) {
3089 0         0 my $column = $grid->[$x];
3090 0         0 my $n1col = $noise1->[$x];
3091 0         0 my $n2col = $noise2->[$x];
3092              
3093 0         0 for ( my $y = 0 ; $y < $len ; $y++ ) {
3094 0         0 $column->set( $y, lerp($n1col->get($y), $n2col->get($y), .5) );
3095             }
3096             }
3097              
3098 0         0 return $grid;
3099             }
3100              
3101             sub stereo {
3102 0     0 1 0 my $noise = shift;
3103 0         0 my %args = @_;
3104              
3105 0   0     0 my $len = $args{len} || $DEFAULT_LEN;
3106              
3107 0         0 %args = defaultArgs(%args);
3108              
3109 0         0 my $map = densemap( $noise );
3110 0         0 my $out = grid(%args);
3111              
3112 0         0 for ( my $x = 0 ; $x < $len ; $x++ ) {
3113 0         0 my $outcol = $out->[ $x / 2 ];
3114 0         0 my $outcol2 = $out->[ ( $x + $len ) / 2 ];
3115 0         0 my $mapcol = $map->[$x];
3116              
3117 0         0 for ( my $y = 0 ; $y < $len ; $y++ ) {
3118 0         0 my $offset = ( $mapcol->get($y) / $MAX_COLOR ) * 16;
3119              
3120 0         0 $outcol->set( $y,
3121             $outcol->get($y) + noise( $noise, $x - $offset, $y ) / 2 );
3122 0         0 $outcol2->set( $y,
3123             $outcol2->get($y) + noise( $noise, $x + $offset, $y ) / 2 );
3124             }
3125             }
3126              
3127 0         0 return $out;
3128             }
3129              
3130             #
3131             # Julia distance
3132             #
3133             sub jdist {
3134 512     512 0 659 my $Zx = shift;
3135 512         598 my $Zy = shift;
3136 512         623 my $Cx = shift;
3137 512         651 my $Cy = shift;
3138 512         533 my $iter_max = shift;
3139              
3140 512         604 my $x = $Zx;
3141 512         637 my $y = $Zy;
3142 512         600 my $xp = 1;
3143 512         645 my $yp = 0;
3144 512         545 my $nz = 0;
3145 512         515 my $nzp = 0;
3146              
3147 512         1270 for ( my $i = 0 ; $i < $iter_max ; $i++ ) {
3148 54447         78547 $nz = 2 * ( $x * $xp - $y * $yp ) + 1;
3149 54447         78277 $yp = 2 * ( $x * $yp + $y * $xp );
3150 54447         55909 $xp = $nz;
3151              
3152 54447         65359 $nz = $x * $x - $y * $y + $Cx;
3153 54447         61313 $y = 2 * $x * $y + $Cy;
3154 54447         54015 $x = $nz;
3155              
3156 54447         65304 $nz = $x * $x + $y * $y;
3157 54447         66418 $nzp = $xp * $xp + $yp * $yp;
3158 54447 100       163340 last if $nzp > 1e60;
3159             }
3160              
3161 512         674 my $a = sqrt($nz);
3162              
3163 512         1956 return 2 * $a * log($a) / sqrt($nzp);
3164             }
3165              
3166             sub djulia {
3167 1     1 1 8 my %args = @_;
3168              
3169 1         6 my $xstart = rand(.05) - .05;
3170 1         3 my $ystart = rand(.05) - .05;
3171              
3172 1         3 my $flen = .0125 + rand(.0125);
3173              
3174 1   50     7 $args{maxiter} ||= 4096;
3175              
3176 1         10 return tile( julia(
3177             %args,
3178             ZxMin => $xstart,
3179             ZyMin => $ystart,
3180             ZxMax => $xstart + $flen,
3181             ZyMax => $ystart + $flen,
3182             ), %args );
3183             }
3184              
3185             sub julia {
3186 2     2 1 16 my %args = @_;
3187              
3188 2         5 local $COLUMN_CLASS = "Tie::CDoubleArray";
3189              
3190 2 50       10 print "Generating Julia...\n" if !$QUIET;
3191              
3192 2   33     8 $args{len} ||= $DEFAULT_LEN;
3193 2 50       12 $args{freq} = $args{len} if !defined $args{freq};
3194              
3195 2         13 %args = defaultArgs(%args);
3196              
3197 2         13 my $len = $args{freq};
3198              
3199 2         17 my $grid = grid( %args, len => $len );
3200              
3201 2         13 my @c = (
3202             [ -.74543, .11301 ],
3203              
3204             # [ .285, .01 ],
3205             # [ -.8, .156 ],
3206             );
3207              
3208 2         8 my $c = $c[ rand(@c) ];
3209              
3210 2         5 my $Cx = $c->[0];
3211 2         7 my $Cy = $c->[1];
3212              
3213             # my $Cx = -0.74543;
3214             # my $Cy = 0.11301;
3215              
3216 2         3 my $iX = 0;
3217 2         4 my $iY = 0;
3218              
3219 2         5 my $ZxMin = $args{ZxMin};
3220 2         5 my $ZxMax = $args{ZxMax};
3221 2         5 my $ZyMin = $args{ZyMin};
3222 2         4 my $ZyMax = $args{ZyMax};
3223              
3224 2 100       9 $ZxMin = -2 if !defined $ZxMin;
3225 2 100       8 $ZxMax = 2 if !defined $ZxMax;
3226 2 100       6 $ZyMin = -2 if !defined $ZyMin;
3227 2 100       9 $ZyMax = 2 if !defined $ZyMax;
3228              
3229             # This is really low because this function is really slow
3230 2   66     11 my $iters = $args{maxiter} || ( $MAX_COLOR * .75 );
3231              
3232             # $len *= 2;
3233              
3234 2         6 my $pixelWidth = ( $ZxMax - $ZxMin ) / $len;
3235 2         177 my $pixelHeight = ( $ZyMax - $ZyMin ) / $len;
3236              
3237 2         4 my $Zx = 0;
3238 2         4 my $Zy = 0;
3239 2         4 my $Z0x = 0;
3240 2         3 my $Z0y = 0;
3241 2         3 my $Zx2 = 0;
3242 2         5 my $Zy2 = 0;
3243              
3244 2         2 my $escapeRadius = 400;
3245 2         4 my $ER2 = $escapeRadius * $escapeRadius;
3246              
3247 2         4 my $distanceMax = $pixelWidth / 15;
3248              
3249 2         4 my $i;
3250              
3251 2         8 for ( $iY = 0 ; $iY < $len ; $iY++ ) {
3252 32         59 $Z0y = $ZyMax - $iY * $pixelHeight;
3253 32 100       96 if ( abs($Z0y) < $pixelHeight / 2 ) {
3254 2         4 $Z0y = 0;
3255             }
3256 32         85 for ( $iX = 0 ; $iX < $len ; $iX++ ) {
3257 512         743 $Z0x = $ZxMin + $iX * $pixelWidth;
3258 512         700 $Zx = $Z0x;
3259 512         668 $Zy = $Z0y;
3260 512         749 $Zx2 = $Zx * $Zx;
3261 512         529 $Zy2 = $Zy * $Zy;
3262              
3263 512   100     2486 for ( $i = 1 ; $i <= $iters && ( $Zx2 + $Zy2 ) < $ER2 ; $i++ ) {
3264 52831         59799 $Zy = 2 * $Zx * $Zy + $Cy;
3265 52831         56614 $Zx = $Zx2 - $Zy2 + $Cx;
3266 52831         55020 $Zx2 = $Zx * $Zx;
3267 52831         219062 $Zy2 = $Zy * $Zy;
3268             }
3269              
3270 512         503 my $color;
3271              
3272 512 50       1213 if ( $i == $iters ) {
3273 0         0 $color = 0;
3274             } else {
3275 512         1225 my $distance = jdist( $Z0x, $Z0y, $Cx, $Cy, $iters );
3276 512 100       1098 if ( $distance < $distanceMax ) {
3277 292         1116 $color = $distanceMax - $distance;
3278             } else {
3279 220         347 $color = 0;
3280             }
3281             }
3282              
3283 512         1027 my $column = $grid->[$iX];
3284 512         4063 $column->set( $iY, $column->get($iY) + $color );
3285             }
3286             }
3287              
3288 2         14 $grid = densemap( $grid );
3289              
3290 2         79 return grow( $grid, %args );
3291             }
3292              
3293             my @roots = ( [ 1, 0 ], [ -.5, sqrt(3) / 2 ], [ -.5, sqrt(3) / 2 * -1 ], );
3294              
3295             sub nclass {
3296 256     256 0 335 my $x = shift;
3297 256         378 my $y = shift;
3298 256         339 my $maxiter = shift;
3299              
3300 256         402 my $numRoots = scalar(@roots);
3301              
3302 256         669 my $z = cplx( $x, $y );
3303              
3304 256         12349 my $prev = cplx( 0, 0 );
3305              
3306 256         16314 my $t_numerator = cplx( 0, 0 );
3307 256         10195 my $t_denominator = cplx( 0, 0 );
3308 256         9850 my $t_rootDist = cplx( 0, 0 );
3309 256         9868 my $t_prevRrootDist = cplx( 0, 0 );
3310              
3311 256         9357 my $dist;
3312              
3313 256         696 for ( my $i = 0 ; $i < $maxiter ; $i++ ) {
3314 1098         53585 for ( my $r = 0 ; $r < $numRoots ; $r++ ) {
3315 3066         8288 $t_rootDist->_set_cartesian( $roots[$r] );
3316 3066         20049 $t_rootDist -= $z;
3317              
3318 3066         57829 $dist = abs($t_rootDist);
3319 3066 100       55344 last if $dist == 0;
3320              
3321 3064 100       9738 if ( $dist <= .25 ) {
3322 238         665 $t_prevRrootDist->_set_cartesian( $roots[$r] );
3323 238         1648 $t_rootDist -= $prev;
3324              
3325 238         4540 my $lnPrevRrootDist = log( abs($t_prevRrootDist) );
3326              
3327 238         4333 my $coded =
3328             ( log(.25) - $lnPrevRrootDist ) / ( log($dist) - $lnPrevRrootDist );
3329              
3330 238         408 $coded = $coded - int($coded);
3331 238         341 $coded = $r + $coded;
3332              
3333             # return $coded;
3334 238         2700 return $i / $maxiter + $coded;
3335             }
3336             }
3337              
3338 860 100       2102 if ( $z == $prev ) {
3339 2         47 return -1;
3340             }
3341              
3342 858         12768 $t_numerator = $z;
3343 858         3862 $t_numerator**= 3;
3344 858         217962 $t_numerator *= 2;
3345 858         48790 $t_numerator += 1;
3346              
3347 858         72426 $t_denominator = $z;
3348 858         3435 $t_denominator**= 2;
3349 858         226345 $t_denominator *= 3;
3350              
3351 858         45358 $prev = $z;
3352              
3353 858         3570 $z = $t_numerator / $t_denominator;
3354             }
3355              
3356 16         1098 return -1;
3357             }
3358              
3359             sub newton {
3360 1     1 1 8 my %args = @_;
3361              
3362 1         3 eval {
3363 1     1   15 use Math::Complex;
  1         2  
  1         3623  
3364             };
3365              
3366 1 50       5 print "Generating Newton...\n" if !$QUIET;
3367              
3368 1   33     6 $args{len} ||= $DEFAULT_LEN;
3369 1 50       7 $args{freq} = $args{len} if !defined $args{freq};
3370              
3371 1         9 %args = defaultArgs(%args);
3372              
3373 1         8 my $len = $args{freq};
3374              
3375 1         4 my $ZxMin = $args{ZxMin};
3376 1         2 my $ZxMax = $args{ZxMax};
3377 1         4 my $ZyMin = $args{ZyMin};
3378 1         3 my $ZyMax = $args{ZyMax};
3379              
3380 1 50       5 $ZxMin = -2 if !defined $ZxMin;
3381 1 50       5 $ZxMax = 2 if !defined $ZxMax;
3382 1 50       5 $ZyMin = -2 if !defined $ZyMin;
3383 1 50       5 $ZyMax = 2 if !defined $ZyMax;
3384              
3385 1   50     7 my $iters = $args{maxiter} || 10;
3386              
3387 1         9 my $grid = grid( %args, len => $len );
3388              
3389 1         6 my $pixelWidth = ( $ZxMax - $ZxMin ) / $len;
3390 1         3 my $pixelHeight = ( $ZyMax - $ZyMin ) / $len;
3391              
3392 1         6 for ( my $x = 0 ; $x < $len ; $x++ ) {
3393 16         54 my $zx = $ZxMin + $x * $pixelWidth;
3394              
3395 16         47 my $column = $grid->[$x];
3396              
3397 16         44 for ( my $y = 0 ; $y < $len ; $y++ ) {
3398 256         498 my $zy = $ZyMin + $y * $pixelHeight;
3399              
3400 256         553 my $result = nclass( $zx, $zy, $iters );
3401              
3402 256         1950 $column->set( $y, $result * $MAX_COLOR / 2 );
3403             }
3404              
3405 16         80 printRow( $grid->[$x] );
3406             }
3407              
3408 1         12 $grid = grow( $grid, %args );
3409              
3410 1         25 return $grid;
3411             }
3412              
3413             sub lumber {
3414 1     1 1 8 my %args = defaultArgs(@_);
3415              
3416 1         13 my $multires = multires( %args, octaves => 3, freq => 2, amp => 4 );
3417 1         16 my $grid = grid(%args);
3418              
3419 1         5 my $len = $args{len};
3420              
3421 1         6 for ( my $x = 0 ; $x < $len ; $x++ ) {
3422 16         28 my $column = $grid->[$x];
3423 16         23 my $multiresColumn = $multires->[$x];
3424              
3425 16         36 for ( my $y = 0 ; $y < $len ; $y++ ) {
3426 256         727 my $gray = noise( $multires, $x, 0 ) / 4;
3427              
3428 256         505 $column->set( $y,
3429             ( noise( $multires, $gray, $y ) + $multiresColumn->get($y) )
3430             % $MAX_COLOR );
3431             }
3432             }
3433              
3434 1         17 return glow($grid, %args);
3435             }
3436              
3437             #
3438             # I heartily endorse this event or product
3439             #
3440             sub wormhole {
3441 1     1 1 10 my %args = @_;
3442              
3443 1 50       6 $args{octaves} = 3 if !$args{octaves};
3444 1 50       4 $args{freq} = 2 if !$args{freq};
3445 1 50       5 $args{amp} = 4 if !$args{amp};
3446              
3447 1         7 %args = defaultArgs(%args);
3448              
3449 1         8 my $len = $args{len} * 2;
3450 1         9 my $dist = sqrt($len);
3451              
3452 1         19 my $grid = grid( %args, bias => 0, len => $len );
3453 1         11 my $multires = multires( %args, len => $len );
3454              
3455 1         15 for ( my $x = 0 ; $x < $len ; $x++ ) {
3456 32         80 for ( my $y = 0 ; $y < $len ; $y++ ) {
3457 1024         1842 my $amp = noise( $multires, $x, $y, $len ) / $MAX_COLOR;
3458              
3459 1024         1699 do {
3460 1024         2436 my ( $thisX, $thisY ) = translate( $x, $y, $amp * 360, $amp * $dist, );
3461              
3462 1024         5555 $grid->[ $thisX % $len ]->set( $thisY % $len, abs($amp) );
3463             };
3464             }
3465             }
3466              
3467 1         18 $grid = shrink( $grid, %args );
3468              
3469 1         72 $grid = glow( $grid, %args );
3470              
3471 1         34 $grid = densemap( $grid );
3472              
3473 1         79 return $grid;
3474             }
3475              
3476             sub flux {
3477 1     1 1 9 my %args = @_;
3478              
3479 1 50       5 $args{len} = $DEFAULT_LEN if !$args{len};
3480 1 50       7 $args{octaves} = 3 if !$args{octaves};
3481 1 50       7 $args{freq} = 2 if !$args{freq};
3482              
3483 1         3 my $len = $args{len} * 2;
3484              
3485 1 50       10 $args{amp} = sqrt($len) * 2 if !$args{amp};
3486 1 50       15 $args{bias} = 0 if !$args{bias};
3487              
3488 1         4 my $dist = sqrt($len);
3489              
3490 1         15 %args = defaultArgs(%args);
3491              
3492 1         14 my $grid = grid( %args, bias => 0, len => $len );
3493              
3494 1         11 my $multires = multires( %args, freq => 2, len => $len );
3495              
3496 1         14 for ( my $x = 0 ; $x < $len ; $x++ ) {
3497 32         96 for ( my $y = 0 ; $y < $len ; $y++ ) {
3498 1024         2183 my $amp = noise( $multires, $x, $y, $len ) / $MAX_COLOR;
3499              
3500 1024         1300 do {
3501 1024         2013 my $xAngle = xAngle( $multires, $x, $y );
3502 1024         1909 my $yAngle = yAngle( $multires, $x, $y );
3503              
3504 1024         3986 my $angle = sqrt( ( $xAngle**2 ) + ( $yAngle**2 ) );
3505              
3506 1024         9263 my ( $thisX, $thisY ) =
3507             translate( $x, $y, $angle, ( $amp / $dist ) * $dist, );
3508              
3509 1024         1647 $thisX %= $len;
3510 1024         1168 $thisY %= $len;
3511              
3512 1024         1425 my $column = $grid->[$thisX];
3513              
3514 1024         6429 $column->set( $thisY, $column->get($thisY) + abs($amp) );
3515             };
3516             }
3517             }
3518              
3519 1         17 $grid = shrink( $grid, %args );
3520              
3521 1         66 $grid = glow( $grid, %args );
3522              
3523 1         26 $grid = densemap( $grid );
3524              
3525 1         27 for ( my $x = 0 ; $x < $len / 2 ; $x++ ) {
3526 16         26 my $column = $grid->[$x];
3527              
3528 16         37 for ( my $y = 0 ; $y < $len / 2 ; $y++ ) {
3529 256         961 $column->set( $y, $MAX_COLOR - $column->get($y) );
3530             }
3531             }
3532              
3533 1         47 return $grid;
3534             }
3535              
3536             sub xAngle {
3537 1024     1024 0 1294 my $multires = shift;
3538 1024         1201 my $x = shift;
3539 1024         1072 my $y = shift;
3540              
3541 1024         2449 my $left = noise( $multires, $x - 1, $y );
3542 1024         2047 my $this = noise( $multires, $x, $y );
3543 1024         2299 my $right = noise( $multires, $x + 1, $y );
3544              
3545 1024         1815 my $delta = ( $left - $right ) / $MAX_COLOR;
3546              
3547 1024         2222 return ( $delta * 360 );
3548             }
3549              
3550             sub yAngle {
3551 1024     1024 0 1401 my $multires = shift;
3552 1024         1155 my $x = shift;
3553 1024         1210 my $y = shift;
3554              
3555 1024         2000 my $up = noise( $multires, $x, $y - 1 );
3556 1024         2058 my $this = noise( $multires, $x, $y );
3557 1024         2181 my $down = noise( $multires, $x, $y + 1 );
3558              
3559 1024         1939 my $delta = ( $up - $down ) / $MAX_COLOR;
3560              
3561 1024         2213 return ( $delta * 360 );
3562             }
3563              
3564             sub canvas {
3565 1     1 1 6 my %args = defaultArgs(@_);
3566              
3567 1         12 my $square = square( %args, smooth => 0 );
3568              
3569 1         22 $square =
3570             lsmooth( $square, %args, dirs => 4, angle => 90, rad => $args{len} / 16 );
3571              
3572 1         46 return $square;
3573             }
3574              
3575             #
3576             # Simplex gradient function
3577             #
3578             sub _sgrad {
3579 691     691   759 my $hash = shift;
3580 691         765 my $x = shift;
3581 691         694 my $y = shift;
3582              
3583 691         726 my $h = $hash & 7;
3584              
3585 691 100       1332 my $u = $h < 4 ? $x : $y;
3586 691 100       990 my $v = $h < 4 ? $y : $x;
3587              
3588 691 100       2167 return ( ( $h & 1 ) ? $u * -1 : $u ) + ( ( $h & 2 ) ? $v * -1 : $v );
    100          
3589             }
3590              
3591             my $F2 = 0.366025403;
3592             my $G2 = 0.211324865;
3593              
3594             #
3595             # Simplex noise lookup
3596             #
3597             sub _snoise {
3598 272     272   313 my $x = shift;
3599 272         514 my $y = shift;
3600 272   33     550 my $len = shift || $DEFAULT_LEN;
3601              
3602 272         471 $x = ( ( $x * 1000 ) % ( $len * 1000 ) ) / 1000;
3603 272         360 $y = ( ( $y * 1000 ) % ( $len * 1000 ) ) / 1000;
3604              
3605 272         269 my ( $n0, $n1, $n2 );
3606              
3607 272         369 my $s = ( $x + $y ) * $F2;
3608 272         296 my $xs = $x + $s;
3609 272         280 my $ys = $y + $s;
3610              
3611 272         315 my $i = abs( int($xs) );
3612 272         305 my $j = abs( int($ys) );
3613              
3614 272         330 my $t = ( $i + $j ) * $G2;
3615 272         314 my $X0 = $i - $t;
3616 272         290 my $Y0 = $j - $t;
3617 272         294 my $x0 = $x - $X0;
3618 272         278 my $y0 = $y - $Y0;
3619              
3620 272         266 my ( $i1, $j1 );
3621              
3622 272 100       441 if ( $x0 > $y0 ) { $i1 = 1; $j1 = 0; }
  108         114  
  108         125  
3623 164         178 else { $i1 = 0; $j1 = 1; }
  164         182  
3624              
3625 272         368 my $x1 = $x0 - $i1 + $G2;
3626 272         306 my $y1 = $y0 - $j1 + $G2;
3627 272         337 my $x2 = $x0 - 1 + 2 * $G2;
3628 272         323 my $y2 = $y0 - 1 + 2 * $G2;
3629              
3630 272         289 my $ii = $i % 256;
3631 272         625 my $jj = $j % 256;
3632              
3633 272         375 my $t0 = .5 - $x0 * $x0 - $y0 * $y0;
3634              
3635 272 100       585 if ( $t0 < 0 ) { $n0 = 0; }
  17         22  
3636             else {
3637 255         260 $t0 *= $t0;
3638 255         593 $n0 = $t0 * $t0 * _sgrad( $NUMS[ ( $ii + $NUMS[$jj] ) % 256 ], $x0, $y0 );
3639             }
3640              
3641 272         429 my $t1 = .5 - $x1 * $x1 - $y1 * $y1;
3642              
3643 272 100       439 if ( $t1 < 0 ) { $n1 = 0; }
  80         89  
3644             else {
3645 192         193 $t1 *= $t1;
3646 192         467 $n1 =
3647             $t1 * $t1 *
3648             _sgrad( $NUMS[ ( $ii + $i1 + $NUMS[ ( $jj + $j1 ) % 256 ] ) % 256 ],
3649             $x1, $y1 );
3650             }
3651              
3652 272         700 my $t2 = .5 - $x2 * $x2 - $y2 * $y2;
3653              
3654 272 100       451 if ( $t2 < 0 ) { $n2 = 0; }
  28         31  
3655             else {
3656 244         235 $t2 *= $t2;
3657 244         657 $n2 =
3658             $t2 * $t2 *
3659             _sgrad( $NUMS[ ( $ii + 1 + $NUMS[ ( $jj + 1 ) % 256 ] ) % 256 ],
3660             $x2, $y2 );
3661             }
3662              
3663 272         2524 return ( $n0 + $n1 + $n2 );
3664             }
3665              
3666             sub simplex {
3667 2     2 1 11 my %args = defaultArgs(@_);
3668              
3669 2         10 my $freq = $args{freq};
3670 2         5 my $len = $args{len};
3671 2         5 my $amp = $args{amp};
3672              
3673 2         18 my $grid = grid( %args, len => $len );
3674              
3675 2         14 for ( my $x = 0 ; $x < $len ; $x++ ) {
3676 20         28 my $column = $grid->[$x];
3677              
3678 20         49 for ( my $y = 0 ; $y < $len ; $y++ ) {
3679 272         708 my $thisX = ( $x / $len ) * $freq;
3680 272         344 my $thisY = ( $y / $len ) * $freq;
3681              
3682 272         451 $column->set( $y, _snoise( $thisX, $thisY, $len ) * $amp );
3683             }
3684             }
3685              
3686 2         31 return tile( $grid, %args );
3687             }
3688              
3689             sub simplex2 {
3690 1     1 1 6 my %args = defaultArgs(@_);
3691              
3692 1         12 my $grid = simplex( %args, len => $args{freq}, tile => 0 );
3693              
3694 1         10 return grow( $grid, %args );
3695             }
3696              
3697             sub _test {
3698 0     0   0 my %args = defaultArgs(@_);
3699              
3700 0         0 my $grid = grid(%args);
3701              
3702 0         0 my $len = $args{len};
3703              
3704 0         0 for ( my $x = 0 ; $x < $len ; $x++ ) {
3705 0         0 for ( my $y = 0 ; $y < $len ; $y++ ) {
3706              
3707             }
3708             }
3709              
3710 0         0 return $grid;
3711             }
3712              
3713             our @chars = split( //, ' . .. .....:.::.:::::H:HH:HHHH#H##H#######');
3714              
3715             sub printRow {
3716 2080 50   2080 0 10055 return if $QUIET;
3717              
3718 0           my $row = shift;
3719 0           my $len = $row->len();
3720              
3721 0           my $rows = 80;
3722 0           for my $i ( 0 .. $rows - 1 ) {
3723 0           my $pct = $i / $rows - 1;
3724 0           my $rowI = int( $pct * $len );
3725 0           my $val = $row->get( $rowI % $len );
3726              
3727 0           my $valPct = clamp($val) / $MAX_COLOR;
3728 0           my $char = $chars[ $valPct * ( @chars - 1 ) ];
3729              
3730 0           print $char;
3731             }
3732              
3733 0           print "\n";
3734             }
3735              
3736             sub spamConsole {
3737 0     0 0   my %args = @_;
3738              
3739 0           my $fmtstr = '%-10s %-10s %-10s %-10s %-4s %-4s %-4s %-4s %-4s';
3740              
3741 0           printf( $fmtstr, qw| type lbase ltype stype bias amp freq oct len | );
3742 0           print "\n";
3743              
3744 0           my $type = $args{type};
3745              
3746 0 0         if ( $type eq 'terra' ) {
  0 0          
3747 0           printf( $fmtstr,
3748             "terra", $args{lbase}, $args{ltype},
3749             $args{stype}, $args{bias}, $args{amp},
3750             $args{freq}, $args{octaves}, $args{len},
3751             );
3752             } elsif (
3753             grep {
3754             $_ eq $type
3755             } @PERLIN_TYPES
3756             )
3757             {
3758 0           printf( $fmtstr,
3759             $type, "n/a", "n/a",
3760             $args{stype}, $args{bias}, $args{amp},
3761             $args{freq}, $args{octaves}, $args{len},
3762             );
3763             } else {
3764 0           printf( $fmtstr,
3765             $type, "n/a", "n/a", "n/a", $args{bias},
3766             $args{amp}, $args{freq}, "n/a", $args{len}, );
3767             }
3768              
3769 0           print "\n";
3770             }
3771              
3772             1;
3773             __END__