#!/usr/bin/perl # # Copyright 2011-2012 Thomas A. Alspaugh. # # This work is licensed under the # Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. # To view a copy of this license, visit # http://creativecommons.org/licenses/by-nc-sa/3.0/ # or send a letter to Creative Commons, 444 Castro Street, Suite 900, # Mountain View, California, 94041, USA. # # Attribution requirements for this file are listed at # http://www.thomasalspaugh.org/pub/attribution-by-nc-sa.html # # Timestamp 2013Jul10We14:29 use strict; use warnings; my $cmdName = $0; $cmdName =~ s/^.*\///; sub help { print < comment is placed at the end of the HTML file summarizing the options that produced it. HEREDOCUMENT ; } sub css { print < comment is placed at the end of the SVG file summarizing the options that produced it. HEREDOCUMENT ; # svgXStep } my $verbose = ""; my @changes = (); my $changesJ = 0; my $changeStr = ""; my $changeStrJ = 0; my $changesStr = ""; my $count = ""; my $html = ""; my $indent0 = ""; my $indent = $indent0; my $j = 0; my $leadEnd = ""; my %leadEnds = (); my $leadEndsOpts = ""; my %leadNonEnds = (); my $leadTrebleBack = ""; my $leadTrebleHand = ""; my %note = (); my $noteOpts = ""; my $numBells = 0; my $numCovers = 0; my $numWorking = -1; my $repeat = ""; my $Repeat = ""; my $repetitions = 1; my $rounds = ""; my $rn = 0; my $rn0 = 0; my $row = ""; my $row0Opt = ""; my $rowJ = 0; my $rowHtml = ""; my $stroke = "back"; my $svgBellNums = ""; my $svgBellNums0 = ""; my $svgBellNumsStr = ""; my $svgNotes = ""; my %svgPath = (); $svgPath{"1"} = ""; $svgPath{"2"} = ""; $svgPath{"3"} = ""; $svgPath{"4"} = ""; $svgPath{"5"} = ""; $svgPath{"6"} = ""; $svgPath{"7"} = ""; $svgPath{"8"} = ""; $svgPath{"9"} = ""; $svgPath{"0"} = ""; my $svgRowBars = "\n"; my $svgWidth = ""; my $svgWorkBell = 0; if (0 == scalar @ARGV) { &help; exit; } my $sOpts = ""; my $arg; foreach $arg (@ARGV) { if ($arg =~ /^-+help$/) { &help; exit; } elsif ($arg =~ /^-+(Phelp|x)$/) { &examples; exit; } elsif ($arg =~ /^-+v$/) { $verbose = "y"; } elsif ($arg =~ /^-+0=([0-9]+)$/) { $row = $arg; $row =~ s/^-+0=([0-9]+)$/$1/; if ($row !~ /1/) { die "Initial row must contain 1 ($row).\n"; } if ($row !~ /2/) { die "Initial row must contain 2 ($row).\n"; } my $ck = $row; $ck =~ s/1//; $ck =~ s/2//; $ck =~ s/3//; $ck =~ s/4//; $ck =~ s/5//; $ck =~ s/6//; $ck =~ s/7//; $ck =~ s/8//; $ck =~ s/9//; $ck =~ s/0//; if ($ck) { die "Initial row contains duplicates for $ck ($row).\n"; } $ck = $row; $ck =~ s/1//; $ck =~ s/2//; if ($ck =~ /3/) { $ck =~ s/3//; if ($ck =~ /4/) { $ck =~ s/4//; if ($ck =~ /5/) { $ck =~ s/5//; if ($ck =~ /6/) { $ck =~ s/6//; if ($ck =~ /7/) { $ck =~ s/7//; if ($ck =~ /8/) { $ck =~ s/8//; if ($ck =~ /9/) { $ck =~ s/9//; if ($ck =~ /0/) { $ck =~ s/0//; if ($ck) { die "Assertion failed \"$ck\" ($arg).\n"; } $numWorking = 10; } else { $numWorking = 9; if ($ck) { die "Assertion failed \"$ck\" ($arg).\n"; } } } else { $numWorking = 8; if ($ck) { die "Initial row is missing the 9 ($row).\n"; } } } else { $numWorking = 7; if ($ck) { die "Initial row is missing the 8 ($row).\n"; } } } else { $numWorking = 6; if ($ck) { die "Initial row is missing the 7 ($row).\n"; } } } else { $numWorking = 5; if ($ck) { die "Initial row is missing the 6 ($row).\n"; } } } else { $numWorking = 4; if ($ck) { die "Initial row is missing the 5 ($row).\n"; } } } else { $numWorking = 3; if ($ck) { die "Initial row is missing the 4 ($row).\n"; } } } else { $numWorking = 2; if ($ck) { die "Initial row is missing the 3 ($row).\n"; } } if (10 < $numWorking - -$numCovers) { die "Only up to ten bells are allowed ($numWorking + $numCovers).\n" } $row0Opt = "$arg "; } elsif ($arg =~ /^-+(beginAtHand|bh)$/) { $rn = 1; $rn0 = 1; } elsif ($arg =~ /^-+[bn]=([0-9]+)$/) { my $nn = $arg; $nn =~ s/^-+[bn]=([0-9]+)$/$1/; if ($verbose) { print STDERR "Number of bells: $nn\n"; } if ($nn < 2) { die "Minimum number of bells allowed is 2 ($nn).\n" } if (-1 < $numWorking && $nn != $numWorking) { die "Number of bells $numWorking already inferred from other options ($nn).\n"; } $numWorking = $nn; if (10 < $numWorking - -$numCovers) { die "Only up to ten bells are allowed ($numWorking + $numCovers).\n" } } elsif ($arg =~ /^-+c(ount)?$/) { $count = "y"; } elsif ($arg =~ /^-+c(ount)?=([0-9]+)$/) { my $nn = $arg; $nn =~ s/^-+c(ount)?=([0-9]+)$/$2/; if ($verbose) { print STDERR "Beginning at row $nn\n"; } $rn = "$nn"; $rn0 = "$nn"; $count = "y"; } elsif ($arg =~ /^-+css$/) { &css(); exit; } elsif ($arg =~ /^-+C=([0-9]+)$/) { my $nn = $arg; $nn =~ s/^-+C=([0-9]+)$/$1/; if ($verbose) { print STDERR "Number of covers: $nn\n"; } if ($nn < 0) { die "Minimum number of bells allowed is 0 ($nn).\n" } $numCovers = $nn; if (10 < $numWorking - -$numCovers) { die "Only up to ten bells are allowed ($numWorking + $numCovers).\n" } } elsif ($arg =~ /^-+h(tml)?$/) { $html = "y"; $svg = ""; } elsif ($arg =~ /^-+H(TML)?$/) { $html = "Y"; $svg = ""; } elsif ($arg =~ /^-+h(tml)?[hH]elp$/) { &htmlHelp(); exit; } elsif ($arg =~ /^-+l(ead)?e(nd)?=([0-9]+)$/) { my $nn = $arg; $nn =~ s/^-+l(ead)?e(nd)?=([0-9]+)$/$3/; if ($nn%2) { die "Lead ends only allowed for backstrokes (even-numbered rows): $arg\n"; } $leadEnds{$nn} = "$nn"; $leadEndsOpts = "$leadEndsOpts$arg "; } elsif ($arg =~ /^-+LE=([0-9]+)$/) { my $nn = $arg; $nn =~ s/^-+LE=([0-9]+)$/$1/; $leadNonEnds{$nn} = "$nn"; $leadEndsOpts = "$leadEndsOpts$arg "; } elsif ($arg =~ /^-+[nN]ote([0-9]+)=(.+)$/) { my $nn = $arg; my $note = $arg; $nn =~ s/^-+[nN]ote([0-9]+)=(.+)$/$1/; $note =~ s/^-+[nN]ote([0-9]+)=(.+)$/$2/; $note =~ s/_/ /g; if ($arg =~ /^-+N/) { $note = "“$note”"; } $note{$nn} = $note; $noteOpts = "$noteOpts$arg "; } elsif ($arg =~ /^-+p=([.0-9x]+)$/) { $changesStr = $arg; $changesStr =~ s/^-+p=([.0-9x]+)$/$1/; if ($numWorking < 0) { for ($j = 0; $j < length($changesStr); ++$j) { my $cc = substr($changesStr, $j, 1); if ($cc =~ /[0-9]/) { if ("0" eq $cc && $numWorking < 10) { $numWorking = 10; if ($verbose) { print STDERR "Inferred number of bells is at least $numWorking\n"; } } elsif ($numWorking < $cc) { $numWorking = $cc; if ($verbose) { print STDERR "Inferred number of bells is at least $numWorking\n"; } } } } } if ($verbose) { print STDERR "Changes: $changesStr\n"; } my $str = $changesStr; while (-1 < ($j = index($str, "."))) { $changeStr = substr($str, 0, $j); if ($verbose) { print STDERR " change $changeStr\n"; } push @changes, $changeStr; $str = substr($str, 1+$j); } if ("" ne $str) { push @changes, $str; if ($verbose) { print STDERR " change $str\n"; } } if ($verbose) { print STDERR "Changes: " . scalar @changes . "\n"; } } elsif ($arg =~ /^-+r(epeat)?$/) { $repeat = "y"; } elsif ($arg =~ /^-+R(epeat)?$/) { $Repeat = "y"; $repeat = "y"; $indent = "$indent0 "; } elsif ($arg =~ /^-+s(vg)?$/) { $svg = "y"; $html = ""; } elsif ($arg =~ /^-+s(vg)?B(ellNums)?0?$/) { if ($arg =~ /^-+s(vg)?B(ellNums)?0$/) { $svgBellNums0 = "y"; $svgDotRadius0 = $svgFontSize*.5; } else { $svgBellNums = "y"; $svgDotOpacity = .85; $svgDotRadius = $svgFontSize*.5; } $svgBellNumsStr = "\n"; } elsif ($arg =~ /^-+s(vg)?(Color)?Backstroke=(.+)$/) { $svgColorBackstroke = $arg; $svgColorBackstroke =~ s/^-+s(vg)?(Color)?Backstroke=(.+)$/$3/; } elsif ($arg =~ /^-+s(vg)?DotRadius=([0-9.]+)$/) { $svgDotRadius = $arg; $svgDotRadius =~ s/^-+s(vg)?DotRadius=([0-9.]+)$/$2/; } elsif ($arg =~ /^-+s(vg)?[hH]elp$/ || $arg =~ /^-+helpSvg/) { &svgHelp(); exit; } elsif ($arg =~ /^-+s(vg)?(Color)?LeadEnd=(.+)$/) { $svgColorLeadEnd = $arg; $svgColorLeadEnd =~ s/^-+s(vg)?(Color)?LeadEnd=(.+)$/$3/; $sOpts = "$sOpts-sLeadEnd=$svgColorLeadEnd "; } elsif ($arg =~ /^-+s(vg)?R(ows)?Ex(pected)?=([0-9]+)$/) { $arg =~ s/^-+s(vg)?R(ows)?Ex(pected)?=([0-9]+)$/$4/; $sOpts = "$sOpts-sREx=$arg "; if ($arg < 10) { $svgX0 = 1.50*$svgUnit; #print STDERR "### svgX0 set to $svgX0\n"; } elsif ($arg < 100) { $svgX0 = 1.84*$svgUnit; #print STDERR "### svgX0 set to $svgX0\n"; } else { $svgX0 = 2.25*$svgUnit; } } elsif ($arg =~ /^-+s(vg)?Stroke(Width)?=([0-9.]+)$/) { $svgStrokeWidth = $arg; $svgStrokeWidth =~ s/^-+s(vg)?Stroke(Width)?=([0-9.]+)$/$3/; $sOpts = "$sOpts-sStroke=$svgStrokeWidth "; } elsif ($arg =~ /^-+s(vg)?U(nit)?=([0-9.]+)$/) { $svgUnit = $arg; $svgUnit =~ s/^-+s(vg)?U(nit)?=([0-9.]+)$/$3/; $svgBellNumYOffset *= $svgUnit/$svgUnit0; $svgCrossbarStroke *= $svgUnit/$svgUnit0; $svgDotRadius *= $svgUnit/$svgUnit0; $svgFontSize *= $svgUnit/$svgUnit0; $svgStrokeWidth *= $svgUnit/$svgUnit0; $svgX0 *= $svgUnit/$svgUnit0; $svgXStep *= $svgUnit/$svgUnit0; $svgY0 *= $svgUnit/$svgUnit0; $svgYStep *= $svgUnit/$svgUnit0; $sOpts = "$sOpts-sUnit=$svgUnit "; } elsif ($arg =~ /^-+s(vg)?Width=([0-9.]+)$/) { $svgWidth = $arg; $svgWidth =~ s/^-+s(vg)?Width=([0-9.]+)$/$2/; $sOpts = "$sOpts-sWidth=$svgWidth "; } elsif ($arg =~ /^-+s(vg)?X(Step)?=([0-9.]+)$/) { $svgXStep = $arg; $svgXStep =~ s/^-+s(vg)?X(Step)?=([0-9.]+)$/$3/; $sOpts = "$sOpts-sX=$svgXStep "; } elsif ($arg =~ /^-+s(vg)?X0=([0-9.]+)$/) { $svgX0 = $arg; $svgX0 =~ s/^-+s(vg)?X0=([0-9.]+)$/$2/; $sOpts = "$sOpts-sX0=$svgX0 "; } elsif ($arg =~ /^-+s(vg)?Y(Step)?=([0-9.]+)$/) { $svgYStep = $arg; $svgYStep =~ s/^-+s(vg)?Y(Step)?=([0-9.]+)$/$3/; $sOpts = "$sOpts-sY=$svgYStep "; } elsif ($arg =~ /^-+s(vg)?Y0=([0-9.]+)$/) { $svgY0 = $arg; $svgY0 =~ s/^-+s(vg)?Y0=([0-9.]+)$/$2/; $sOpts = "$sOpts-sY0=$svgY0 "; } elsif ($arg =~ /^-+s(vg)?([0-9])=(.+)$/) { my $bell = $arg; my $clr = $arg; $bell =~ s/^-+s(vg)?([0-9])=(.+)$/$2/; $clr =~ s/^-+s(vg)?([0-9])=(.+)$/$3/; if ($clr eq "X") { $clr = "transparent"; } $svgColors{$bell} = $clr; $sOpts = "$sOpts-s$bell=$clr "; } elsif ($arg =~ /^-+s(vg)?C([0-9])=(.+)$/) { my $bell = $arg; my $clr = $arg; $bell =~ s/^-+s(vg)?C([0-9])=(.+)$/$2/; $clr =~ s/^-+s(vg)?C([0-9])=(.+)$/$3/; if ($clr eq "C") { $clr = $svgColorCrs; $svgOverTrb = "C"; } elsif ($clr eq "A") { $clr = $svgColorAft; $svgOverTrb = "A"; } $svgColors{$bell} = "transparent"; $svgCircleColors{$bell} = $clr; $sOpts = "$sOpts-sC$bell=$clr "; } elsif ($arg =~ /^-+s(vg)?Work$/) { $svgWorkBell = 2; $sOpts = "$sOpts-sWork "; } elsif ($arg =~ /^-+s(vg)?Work=(.+)$/) { my $bell = $arg; $bell =~ s/^-+s(vg)?Work=(.+)$/$2/; $svgWorkBell = $bell; $sOpts = "$sOpts-sWork=$bell "; } else { die "Unexpected argument \"$arg\".\n"; } } if ($numWorking < 0) { $numWorking = 6; if (10 < $numWorking - -$numCovers) { die "Only up to ten bells are allowed ($numWorking + $numCovers).\n" } } $numBells = $numWorking - -$numCovers; if ($html eq "Y") { print <
HEREDOCUMENT ; } if ($html) { print "$indent0\n"; } # Initialize to rounds for ($j = 0; $j < $numBells; ++$j) { if ($j < 9) { $rounds = $rounds . (1+$j); } elsif (9 == $j) { $rounds = $rounds . "0"; } else { die "Only up to ten bells ($numBells).\n" } } if (!$row) { $row = $rounds; } if (!$count) { $svgX0 = $svgXStep/2; } my $rectXx = $svgX0 - $svgXStep/2; my $barWidth = $svgXStep*$numBells; sub handleRow { my $leadEndClass = ($leadEnd ? " leadEnd" : ""); my $leadEndSpace = ($leadEnd ? "" : " "); my $roundsClass = ($row eq $rounds ? " rounds" : ""); my $roundsSpace = ($row eq $rounds ? "" : " "); # # HTML # if ($html) { $rowHtml = $row; for ($j = 1; $j <= $numBells; ++$j) { my $bell = (10 == $j ? "0" : $j); $rowHtml =~ s/$bell/$bell<\/span>/; } print "$indent
"; print ""; if (0 == $rn) { print ""; } else { my $rN = ($rn < 10 ? " $rn" : $rn); print "$rN"; } print ""; print "$rowHtml"; if (exists $note{$rn}) { print " " . $note{$rn}; } print "
\n"; } # # SVG # elsif ($svg) { my $placeOfWorkBell = index($row, $svgWorkBell); for ($j = 1; $j <= $numBells; ++$j) { my $bell = (10 == $j ? "0" : $j); my $cu = index($row, $bell); my $xx = $cu*$svgXStep + $svgX0; my $yy = $svgYStep*($rn - $rn0) + $svgY0; if ($rn0 == $rn) { $svgPath{$bell} = " $bell\n"; } if ($svgCircleColors{$bell}) { if (($svgColorCrs eq $svgCircleColors{$bell} || $svgColorAft eq $svgCircleColors{$bell}) && $placeOfWorkBell - 1 == $cu) { $svgCirclesStr = $svgCirclesStr . " \n" . " " . ($svgColorCrs eq $svgCircleColors{$bell} ? "C" : "A") . "\n"; } elsif ($svgColorCrs eq $svgCircleColors{$bell} && $placeOfWorkBell - 1 == $cu) { } else { $svgCirclesStr = $svgCirclesStr . " \n"; } } if ($svgOverTrb && 1 == $bell && $placeOfWorkBell - 1 == $cu) { $svgCirclesStr = $svgCirclesStr . " \n"; } } if ($svgRowBars && (0 == $rn%2)) { # && $rn my $yy = $svgYStep*(($rn - $rn0) - .5) + $svgY0; $leadEnd = ($leadEndsOpts ? exists $leadEnds{$rn} : (0 == $rn%(scalar @changes)) && ($row =~ /^1/)); if ($leadEnd && exists $leadNonEnds{$rn}) { $leadEnd = ""; if ($verbose) { print "Would have been a lead end at row $rn but -LE option blocked it.\n"; } } my $color = ($leadEnd ? $svgColorLeadEnd : $svgColorBackstroke); if ($leadEnd && $rn) { $svgRowBars = $svgRowBars . " \n"; } if ($leadEnd && $svgWorkBell) { my $workPlace = index($row, $svgWorkBell) + 1; my $workXx = $rectXx + $barWidth + 0.75*$svgYStep; my $workYy = $yy + $svgYStep/2; my $workRr = 0.42*$svgYStep; $svgWorkStr = $svgWorkStr . " \n" . " $workPlace\n"; } $svgRowBars = $svgRowBars . " \n"; } if (exists $note{$rn}) { my $xx = ($numBells)*$svgXStep + $svgX0; my $yy = $svgYStep*(($rn - $rn0) + .167) + $svgY0; $svgNotes = $svgNotes . " " . $note{$rn} . "\n"; } } # # Text # else { my $note = (exists $note{$rn} ? " " . $note{$rn} : ""); if ($count) { if ($rn < 10) { print "[ $rn] $row$note\n"; } else { print "[$rn] $row$note\n"; } } else { print "$row$note\n"; } } } # if ($Repeat && $html) { # print "$indent0
\n"; # ### Lead # } &handleRow; if (0 < scalar @changes) { if ($verbose) { print STDERR "Beginning the changes\n"; } $changesJ = 0; while (1) { if ($#changes < $changesJ) { # $# last index, not size if ($repeat) { $changesJ = 0; ++$repetitions; } else { last; } } $stroke = ("back" eq $stroke ? "hand" : "back"); ++$rn; if ($verbose) { print STDERR "Row number $rn\n"; } $changeStr = $changes[$changesJ]; if ($verbose) { print STDERR "change: $changeStr\n"; } if ($changeStr eq "x") { if ($numWorking != 2*($numWorking/2)) { die "Change x, but odd number of bells ($numWorking).\n"; } for ($rowJ = 0; $rowJ < $numWorking; $rowJ += 2) { substr($row, $rowJ, 2) = substr($row, 1+$rowJ, 1) . substr($row, $rowJ, 1); } } else { $changeStrJ = 0; for ($rowJ = 0; $rowJ < $numWorking; ++$rowJ) { my $skip = substr($changeStr, $changeStrJ, 1); if ($verbose) { print STDERR "'skip' bell is '$skip'\n"; } if ("0" eq $skip) { $skip = 10; } if (length($changeStr) <= $changeStrJ || 1+$rowJ < $skip) { if ($changeStrJ < length($changeStr) && 2+$rowJ == $skip) { die "Impossible change \"$changeStr\" requires " . (1+$rowJ) . " to change with non-adjacent bell.\n"; } if ($verbose) { print STDERR ":: Exchanging " . (1+$rowJ) . " and " . (2+$rowJ) . "...\n"; } substr($row, $rowJ, 2) = substr($row, 1+$rowJ, 1) . substr($row, $rowJ, 1); ++$rowJ; } elsif (1+$rowJ == $skip) { ++$changeStrJ; } else { die "\n### Whoops: $rowJ $skip\n\n"; } } } if ($stroke eq "hand") { $leadTrebleHand = ($row =~ /^1/); $leadEnd = ""; } else { $leadEnd = ($leadEndsOpts ? exists $leadEnds{$rn} : $leadTrebleHand && ($row =~ /^1/)); } &handleRow; if ($Repeat && $leadEnd && $html && $row ne $rounds) { # print "$indent0
\n"; # ### Lead # print "$indent0
\n"; # ### Lead &handleRow; } if ($row eq $rounds && $#changes == $changesJ) { last; } ++$changesJ; } } # if ($Repeat && $html) { # print "$indent0
\n"; # ### Lead # } if ($html) { print "$indent0\n"; } if ($html eq "Y") { print < HEREDOCUMENT ; } if ($svg) { if ($verbose) { print STDERR "Starting svg-only work:\n"; } if ($verbose) { print STDERR "Rows: " . ($rn - $rn0) . "\n"; } my $xx0 = $svgX0 + ($numBells - .5)*$svgXStep + ($svgWorkBell ? 1.25*$svgYStep + 1 : 0); my $yy = $svgY0 + (($rn - $rn0) + 0.5)*$svgYStep; my $xx = ($svgWidth ? $svgWidth : $xx0); if ($verbose) { print STDERR "Width $xx, height $yy\n"; } print < HEREDOCUMENT ; if ($svgWidth) { print " \n"; } if ( $svgRowBars) { print $svgRowBars; } my $x0 = $svgX0 - .5*$svgXStep; my $x1 = $svgX0 + ($numBells - .5)*$svgXStep; $yy = $svgY0 + $svgYStep/2; $xx = $svgX0 + ($numBells - .5)*$svgXStep; $yy = $svgYStep*(0 - .5) + $svgY0; $yy = $svgY0 + .5*$svgYStep; my $nXx = $svgX0 - 1*$svgFontSize; my $k = 0; if ($count) { for ($k = $rn0 - -1; $k <= ($rn - $rn0); ++$k) { $yy = $svgYStep*$k + $svgY0 + $svgFontSize/4; print " [$k]\n"; } } print "\n"; for ($j = 1; $j <= $numBells; ++$j) { my $bell = (10 == $j ? "0" : $j); $xx = $svgX0 + $svgXStep*($j - 1); $yy = $svgY0 - .4*$svgFontSize; print $svgPath{$bell} . "' />\n"; } print "\n"; #print "\n"; if ($svgDotRadius) { for ($k = 0; $k <= ($rn - $rn0); ++$k) { my $opacity = (0 == $k ? 1.0 : $svgDotOpacity); my $radius = (0 == $k && $svgBellNums0 ? $svgDotRadius0 : $svgDotRadius); $yy = $svgY0 + $svgYStep*$k; for ($j = 1; $j <= $numBells; ++$j) { $xx = $svgX0 + ($j - 1)*$svgXStep; print " \n"; } } } if ( $svgCirclesStr) { print " \n"; print $svgCirclesStr; } if ( $svgBellNumsStr) { print " \n"; print $svgBellNumsStr; } if ( $svgWorkStr) { print " \n"; print $svgWorkStr; } if ( $svgNotes) { print " \n"; print "\n" . $svgNotes . "\n"; } my $rr = ($repeat ? ($Repeat ? "-R": "-r") : ""); print < HEREDOCUMENT ; }