I found this old toy while cleaning out the closet and thought some
might find it interesting. It is an experiment to see what kinds of
images can be made from Moire-like patterns. Output is a png image,
so redirect to a file, or uncomment the Content-Type header line and run
as a CGI program.
#!/usr/bin/perl
use strict;
use GD;
use POSIX;
#---------------------------------------
# conf
my $conf = {
image => {
x_size => 320,
y_size => 320,
},
palette => {
num_colors => int(rand(56) + 8),
set_points => [
[0, 0, 0],
[int(rand(255)), int(rand(255)), int(rand(255))],
[0, 0, 0],
[255, 255, 255],
[0, 0, 0],
[int(rand(255)), int(rand(255)), int(rand(255))],
],
repeat => rand(1.5) + .5,
},
moire => {
num_lines => int(rand(7)) + 2,
repeat_min => 4,
repeat_max => 8,
wrap_prob => rand(),
turn_prob => rand(),
plot => int(rand(2)),
},
};
#---------------------------------------
# init
my $palette = setup_palette($conf->{palette});
my $moire = setup_moire($conf->{moire});
my $image = new GD::Image($conf->{image}{x_size}, $conf->{image}{y
+_size});
for my $col (@$palette) {
$image->colorAllocate($col->[0], $col->[1], $col->[2]);
}
#---------------------------------------
# go
make_image($conf, $moire, $image);
#print "Content-Type: image/png \n\n";
print $image->png();
#-----------------------------------------------------------
sub make_image {
my $conf = shift;
my $moire = shift;
my $image = shift;
my $x_pix = 1 / $conf->{image}{x_size};
my $y_pix = 1 / $conf->{image}{y_size};
my $max;
for my $line (@$moire) {
if ($line->{wrap}) { $max += 1; }
else { $max += .5; }
}
for my $xi (0..$conf->{image}{x_size}-1) {
my $x = $xi * $x_pix;
for my $yi (0..$conf->{image}{y_size}-1) {
my $y = $yi * $y_pix;
my $tot;
for my $line (@$moire) {
my $val = $x + $y * $line->{grad};
$val = $val * $line->{mult};
$val = $val - floor($val);
if ($line->{wrap}) {
if ($val > .5) { $val = 1 - $val; }
}
if ($line->{turn}) { $val = 1 - $val; }
$tot += $val;
}
$tot = $conf->{moire}{plot}
? ($tot / $max) * $conf->{palette}{repeat}
: $tot / @$moire;
my $ci = $tot * $conf->{palette}{num_colors};
$image->setPixel($xi, $yi, $ci % $conf->{palette}{num_colors});
}
}
}
#-----------------------------------------------------------
sub setup_moire {
my $conf = shift;
my @lines;
for (1..$conf->{num_lines}) {
my $line = {};
push (@lines, $line);
$line->{mult} = rand($conf->{repeat_max} - $conf->{repeat_min});
$line->{mult} = int($line->{mult} + $conf->{repeat_min});
$line->{wrap} = (rand() < $conf->{wrap_prob}) ? 1 : 0;
$line->{turn} = (rand() < $conf->{turn_prob}) ? 1 : 0;
$line->{grad} = rand(2);
if ($line->{grad} > 1) {
$line->{grad} = 1 / ($line->{grad} - 1);
}
if (int(rand(2))) { $line->{grad} *= -1; }
}
return \@lines;
}
#-----------------------------------------------------------
sub setup_palette {
my $conf = shift;
my @palette_colors;
my @palette_indexs;
push @{$conf->{set_points}}, $conf->{set_points}[0];
my $rate = $conf->{num_colors} / $#{$conf->{set_points}};
for my $i (0 .. $#{$conf->{set_points}}) {
my $j = int($i * $rate);
push @palette_indexs, $j;
$palette_colors[$j] = $conf->{set_points}[$i];
}
for my $i (0 .. $#palette_indexs - 1) {
my $beg = $palette_indexs[$i];
my $end = $palette_indexs[$i+1];
interpolate(\@palette_colors, $beg, $end);
}
pop @palette_colors;
return \@palette_colors;
}
#-----------------------------------------------------------
sub interpolate {
my $colors = shift;
my $beg = shift;
my $end = shift;
my $steps = $end - $beg;
my $r_start = $colors->[$beg][0];
my $r_range = $colors->[$end][0] - $colors->[$beg][0];
my $r_slope = $r_range / $steps;
my $g_start = $colors->[$beg][1];
my $g_range = $colors->[$end][1] - $colors->[$beg][1];
my $g_slope = $g_range / $steps;
my $b_start = $colors->[$beg][2];
my $b_range = $colors->[$end][2] - $colors->[$beg][2];
my $b_slope = $b_range / $steps;
for my $i (1..$steps-1) {
my $j = $i + $beg;
$colors->[$j][0] = int($r_start + ($i * $r_slope));
$colors->[$j][1] = int($g_start + ($i * $g_slope));
$colors->[$j][2] = int($b_start + ($i * $b_slope));
}
}