#!perl -w use strict; # docu: ~/docs/azteccode-patent.pdf # sample data: see ~/private/reizen/ns /002.txt # 83x83 : 16 layers, 588 bytes my $lines= readazteclines(); my $middle= @$lines >> 1; my %syms; $syms{substr($lines->[$middle], $middle, 1)}= 1; $syms{substr($lines->[$middle], $middle+1, 1)}= 0; my $acode= lines2darray($lines, \%syms); if (!verifycenter($acode)) { die "concentric squares missing\n"; } if (!verifygrid($acode)) { die "missing grid\n"; } my $r= findorientation($acode); $acode= rotatearray($acode, $r); my @l; my $nl= 7*(@$acode>>5)-3; for (my $il= 1 ; $il<$nl ; $il++) { push @l, readlayer($acode, $il); } sub readazteclines { my @lines; my $len; while(<>) { if (/\S+/) { push @lines, $&; $len= length($&) if !defined $len; if ($len != length($&)) { die "not a fixed line length\n"; } if (($len%2)==0) { die "even linelength\n"; } } } if (@lines != $len) { die "not a square\n"; } return \@lines; } sub lines2darray { my ($alines, $s)= @_; my $n= @$alines; my @acode; for (my $x=0 ; $x<$n; $x++) { for (my $y=0 ; $y<$n; $y++) { $acode[$x][$y]= $s->{substr($alines->[$y], $x, 1)}; } } return \@acode; } sub verifycenter { my ($acode)= @_; for (my $i=0 ; $i<6 ; $i++) { if (!verifysquare($acode, $i+1, $i&1)) { printf("verifysquare failed ( i=%d, val=%d )\n", $i, $i&1); return undef; } } return 1; } sub verifysquare { my ($acode, $radius, $value)= @_; my $n= @$acode; my ($min, $max)= (($n>>1)-$radius, ($n>>1)+$radius); #printf("n=%d r=%d, %d .. %d\n", $n, $radius, $min, $max); my ($x,$y)=($min, $min); my $dir= 0; while ($dir<4) { if ($acode->[$x][$y]!=$value) { printf("verifysquare: (%d,%d) != %d\n", $x,$y, $value); return undef; } if ($dir==0) { $x++; $dir++ if ($x==$max); } elsif ($dir==1) { $y++; $dir++ if ($y==$max); } elsif ($dir==2) { $x--; $dir++ if ($x==$min); } elsif ($dir==3) { $y--; $dir++ if ($y==$min); } } return 1; } sub verifygrid { my ($acode)= @_; my $n= @$acode; my $m= $n>>1; my $ng= $middle>>4; #printf("ng=%d\n", $ng); for (my $i= -$ng ; $i<=$ng ; $i++) { if (!checkhgridline($acode, $m+16*$i)) { printf("hgrid %d error\n", $i); return undef; } if (!checkvgridline($acode, $m+16*$i)) { printf("vgrid %d error\n", $i); return undef; } } return 1; } sub checkvgridline { my ($acode, $ypos)= @_; my $n= @$acode; my $val=($ypos&1)^1; for (my $x= 0 ; $x<$n ; $x++) { if ($acode->[$x][$ypos]!=$val) { printf("vgrid fail (%d,%d) != %d\n", $x, $ypos, $val); return undef; } $val = $val^1; } return 1; } sub checkhgridline { my ($acode, $xpos)= @_; my $n= @$acode; my $val=($xpos&1)^1; for (my $y= 0 ; $y<$n ; $y++) { if ($acode->[$xpos][$y]!=$val) { printf("hgrid fail (%d,%d) != %d\n", $xpos, $y, $val); return undef; } $val = $val^1; } return 1; } sub countsquarebits { my ($acode, $x,$y)= @_; #printf("counting (%d,%d) .. (%d,%d)\n", $x,$y,$x+1,$y+1); return $acode->[$x][$y]+$acode->[$x][$y+1]+$acode->[$x+1][$y]+$acode->[$x+1][$y+1]; } # todo: also find mirror transforms # -> represent those by negative orientation value sub findorientation { my ($acode)=@_; my $n= @$acode; my $middle= $n>>1; my @counts; push @counts, countsquarebits($acode, $middle-7, $middle+6); push @counts, countsquarebits($acode, $middle+6, $middle+6); push @counts, countsquarebits($acode, $middle+6, $middle-7); push @counts, countsquarebits($acode, $middle-7, $middle-7); my $ref; $ref |= (1<<$_) for @counts; if ($ref!=30) { printf("orientation squares incorrect\n"); return undef; } #printf("o: %s\n", join(", ", @counts)); return $counts[0]-1; } sub rotatearray { my ($acode, $r)=@_; my $n= @$acode; my $middle=$n>>1; if ($r==0) { return $acode; } else { my @bcode; for (my $x=-$middle ; $x<=$middle ; $x++) { for (my $y=-$middle ; $y<=$middle ; $y++) { my ($dx, $dy)= ($r==1) ? ($y,-$x) : ($r==2) ? (-$x,-$y) : ($r==3) ? (-$y,$x) : ($x,$y); $bcode[$middle+$x][$middle+$y]= $acode->[$middle+$dx][$middle+$dy]; } } return \@bcode; } } # (x,y) point to bit 10 sub addbits { my ($acode, $value, $x, $y, $dir)= @_; my ($dx0,$dy0,$dx1,$dy1)= ($dir==0) ? (0,0,0,-1) :($dir==1) ? (0,0,1,0) :($dir==2) ? (0,0,0,1) :($dir==3) ? (0,0,-1,0) : (0,0,0,0); $value<<=1; $value |= $acode[$x+$dx0][$y+$dy0]; $value<<=1; $value |= $acode[$x+$dx1][$y+$dy1]; return $value; } # todo: finish this sub readlayer { my ($acode, $il)= @_; $il+=3; $il= int($il/7)*8 + ($il%7); my $dir=0; my $bits=0; my $value=0; while ($dir<4) { $value= addbits($acode, $value, $x,$y, $dir); $bits++; if ($bits==5) { push @data, $value; $bits= 0; $value= 0; } if ($dir==0) { $x++; $dir++ if ($x==$max); } elsif ($dir==1) { $y++; $dir++ if ($y==$max); } elsif ($dir==2) { $x--; $dir++ if ($x==$min); } elsif ($dir==3) { $y--; $dir++ if ($y==$min); } } }