#!/usr/bin/perl -w
#
# aximate.pm
# (C) 2013 by R. Harlander
#
# version 1.1.2, 03 Mar 2015, rh
#   * if $steps=1, movie contains a single frame now (instead of 2).
#     this required to let $IGLOB start at 1 instead of 0.
# version 1.1.1, 28 May 2014, rh
#   * removed footline from pdf due to beamer style
#
# For rudimentary documentation, see
# http://www.robert-harlander.de/software/aximate
#
#----------------------------------------------------------------------
#- {{{ export:

package aximate;
use Exporter;
use Cwd;
use Math::Trig;
@ISA = ('Exporter');
@EXPORT = ('axocurve',
	   'setparams',
	   'makemovie',
	   'track',
	   'polygon',
	   'point',
	   'ccirc',
	   'text',
	   'arc',
	   'arrowarc',
	   'photon',
	   'gluon',
	   'line',
	   'curve',
	   'printheader',
	   'printfooter'
    );

#- }}}
#----------------------------------------------------------------------
# compile output with latex, then ps2pdf, then
# convert -delay 10 output.pdf output.gif
# and open gif file in a browser, for example.
#----------------------------------------------------------------------
$PI = pi;
#----------------------------------------------------------------------
#- {{{ setparams:

sub setparams {
# set the global parameters:
# steps: number of frames in the movie
# circular: if first=last frame, set circular=1
# loop: if loop=1, movie runs backwards after the last frame
# size: latex picture size
    my($tmp,%options,%myoptions,$key);
    $tmp = shift;
    # default values:
    %options = ("steps" => 50,
		"circular" => 0,
		"loop" => 0,
		"scale" => 1,
		"movielength" => 100,
		"size" => "(300,260)(-150,-130)"
	);
    %myoptions = %{$tmp};
    # override default values by input values:
    foreach $key (keys(%myoptions)) {
	$options{$key} = $myoptions{$key}
    }
    # set global variables:
    $STEPS = $options{"steps"};
    $CIRCULAR = $options{"circular"};
    $LOOP = $options{"loop"};
    $SIZE = $options{"size"};
    $SCALE = $options{"scale"};
    $MOVIELENGTH = $options{"movielength"};
}

#- }}}
#- {{{ makemovie:

sub makemovie {
# 
# usage: makemovie(\&animation)
#
# &animation is a subroutine that calls the objects to appear
# in a movie. See "examples.pl".
# &makemovie() writes a latex file.
#
    my($fun);
    unless ($STEPS) { printexit("You should call setparams()."); }
    $fun = shift;
# $IGLOB is a global variable; it's the frame number    
    foreach $IGLOB (1..($STEPS-$CIRCULAR)) {
	print("\\begin{picture}".$SIZE."\n");
	print("\\SetScale{".$SCALE."}\n");
	print("\\SetWidth{2}\n");
	&{$fun};
	print("\\end{picture}\n");
	print("\\newpage\n");
    }
    if ($LOOP == 1) {
	foreach $IGLOBx (1..$STEPS) {
	    $IGLOB = $STEPS-$IGLOBx;
	    print("\\begin{picture}".$SIZE."\n");
	    &{$fun};
	    print("\\end{picture}\n");
	    print("\\newpage\n");
	}
    }
}

#- }}}
#- {{{ point:

sub point {
    #
    # Produces a point. Useful for connecting other objects to it.
    # Depending on the "type", puts an object at the position of point.
    #
    # Possible values of "type":
    # empty: puts nothing at point
    # ccirc: puts a Ccirc (in AxoDraw notation) at point
    #
    my($tmp,%options,%myoptions,%evolve,%myevolve,
       $key,$bethere,$staythere,$start,$end,
       $mysteps,$iloc,$imin,$progress,$printin,$type,
       $col,$col1,$col2,$evolfun,$evolhash,$text,
       $radius,$height,$width,$angle,
       $greyscale,
       $xcoord,$ycoord,$xcen,$ycen,$rtext,$phitext,$textangle,$textmode,
       $textsize,$font,
       $rpos,$phipos);
    %evolve = ();
    %options = (
	"type" => "ccirc",  # what to draw [ccirc,oval,goval,text]
	"coordinates" => [[0,0],[100,0]], # center for polar coords
	"polar" => [[0,0],[0,0]], # [r,phi] relative to coordinates
	"radius" => [[5]],  # radius of blob
	"width" => [[5]],   # width of oval
	"height" => [[10]], # height of oval
	"angle" => [[0]],     # angle for oval
	"text" => "",     # accompanying text
	"font" => "Helvetica",     # PostScript font
	"textpos" => [[10,90]],  # position of accompanying text (r,phi)
	"textangle" => [[0]], # rotate text
	"textmode" => "c", # how to align text
	"textsize" => [[2]], # size of text
	"greyscale" => [[.5]], # grayscale for goval
	"color" => "Black", # color
	"color1" => "Black", # inner color
	"color2" => "Black", # outer color
	"print" => 1,
	"bethere" => 0,
	"staythere" => 0,
	"interval" => [0,100]);
    # the default is to evolve all coordinates linearly:
    foreach $key ("width","height","angle","greyscale",
		  "textsize","textangle") {
	$evolve{$key} = ['0']
    }
    foreach $key ("textpos","coordinates","polar") {
	$evolve{$key} = ['0','0'];
    }
    foreach $key ("radius") {
	$evolve{$key} = ['0'];
    }
    # override default parameters with the ones specified in the
    # function call:
    $tmp = shift;
    %myoptions = %{$tmp};
    $tmp = shift;
    if ($tmp) {%myevolve = %{$tmp}} else { %myevolve = () }
    foreach $key (keys(%myoptions)) {
	$options{$key} = $myoptions{$key}
    }
    $options{"color1"} = $options{"color"};
    $options{"color2"} = $options{"color"};
    foreach $key (keys(%myoptions)) {
	$options{$key} = $myoptions{$key}
    }
    foreach $key (keys(%myevolve)) {
	$evolve{$key} = $myevolve{$key}
    }
    $bethere = $options{"bethere"};
    $staythere = $options{"staythere"};
    $type = $options{"type"};
    $col = $options{"color"};
    $col1 = $options{"color1"};
    $col2 = $options{"color2"};
    $text = $options{"text"};
    # WARNING: textrotate option seems faulty in axodraw
    $textmode = $options{"textmode"};
    $font = $options{"font"};
    ($start,$end) = @{$options{"interval"}};
    $mysteps = $STEPS*($end-$start)/$MOVIELENGTH;
    ($print,$iloc,$imin) = 
	printyesno($start,$end,$STEPS,$IGLOB,$bethere,$staythere);
    $progress = ($iloc-$imin)/$mysteps;
    ($radius) = evolve($progress,$options{"radius"},
			$evolve{"radius"});
    ($width) = evolve($progress,$options{"width"},$evolve{"width"});
    ($height) = evolve($progress,$options{"height"},$evolve{"height"});
    ($angle) = evolve($progress,$options{"angle"},$evolve{"angle"});
    ($textangle) = evolve($progress,$options{"textangle"},$evolve{"textangle"});
    ($greyscale) = evolve($progress,$options{"greyscale"},
			   $evolve{"greyscale"});
    ($textsize) = evolve($progress,$options{"textsize"},
			   $evolve{"textsize"});
    ($xcen,$ycen) = evolve($progress,$options{"coordinates"},
			    $evolve{"coordinates"});
    ($rpos,$phipos) = evolve($progress,$options{"polar"},$evolve{"polar"});
    ($rtext,$phitext) = evolve($progress,$options{"textpos"},
			       $evolve{"textpos"});
    $xcoord = $xcen + $rpos*cos($phipos/180*$PI);
    $ycoord = $ycen + $rpos*sin($phipos/180*$PI);
    $xtext = $xcoord + $rtext*cos($phitext/180*$PI);
    $ytext = $ycoord + $rtext*sin($phitext/180*$PI);
    if (abs($xtext)<0.0000001) {$xtext=0}
    if (abs($ytext)<0.0000001) {$ytext=0}
    $printin = $options{"print"};
    if ($print*$printin == 1) {
	if (($type ne "text") && ($text ne "")) {
	    print("\\Text(".$xtext.",".$ytext.
		  ")[".$textmode."]{\\scalefont{".$textsize.
		  "}{".$text."}}\n");
	    # print("\\rText(".$xtext.",".$ytext.
		  # ")[".$textmode."][".$textrotate."]{\\scalefont{".$textsize.
		  # "}{".$text."}}\n");
	}
	if ($type eq "ccirc") {
	    print("\\CCirc(".$xcoord.",".$ycoord."){".$radius."}{".
		  $col1."}{".$col2."}\n");
	} elsif ($type eq "oval") {
	    print("\\COval(".$xcoord.",".$ycoord.")(".$height.",".
		  $width.")(".$angle."){".$col1."}{".$col2."}\n");
	} elsif ($type eq "goval") {
	    print("\\GOval(".$xcoord.",".$ycoord.")(".$height.",".
		  $width.")(".$angle."){".$greyscale."}\n");
	} elsif ($type eq "text") {
	    print("\\SetPFont{".$font."}{".$textsize."}\n");
	    print("\\PText(".$xcoord.",".$ycoord.")(".$textangle.")[".
		  $textmode.
		  "]{".$text."}\n");
		  # "]{\\scalefont{".$textsize."}".$text."}\n");
	}
    }
    return({"coordinates" => [$xcoord,$ycoord],
	    "print" => $print*$printin,
	    "progress" => $progress,
	    "polar" => [$rpos,$phipos]});
}

#- }}}
#- {{{ ccirc:

sub ccirc {
    #
    # produces a \Ccirc (in AxoDraw notation)
    #
    my($tmp,$out,%options);
    $tmp = shift;
    %options = %{$tmp};
    $tmp = shift;
    $options{"type"} = "ccirc";
    $out = point(\%options,$tmp);
}

#- }}}
#- {{{ text:

sub text {
    #
    # produces \Text (in AxoDraw notation)
    #
    my($tmp,$out,%options);
    $tmp = shift;
    %options = %{$tmp};
    $tmp = shift;
    $options{"type"} = "text";
    $out = point(\%options,$tmp);
}

#- }}}
#- {{{ arc:

sub arc {
    my($tmp,%options,%myoptions,@dum,$key,$bethere,$staythere,$start,$end,
       $mysteps,$iloc,$imin,$progress,$printin,$type,
       $color,$text,$phi1,$phi2,$orientation,$xcen,$ycen,$radius,
       $wiggles,$nwiggles,$fixedwiggles,
       $xcoord,$ycoord);
    %evolve = ();
    %options = (
	"type" => "line", # what to draw [line,photon,gluon]
	"coordinates" => [[0,0]], # center for 'polar'
	"polar" => [[0,0]], # [r,phi] of arc center relative to "coordinates"
	"phi1" => [[0]], # beginning of arc
	"phi2" => [[170]], # end of arc
	"radius" => [[20]], # radius of arc
	"arrow" => 0,
	"amplitude" => [[5]], # amplitude of photon/gluon wiggles
	"fixedwiggles" => 0, # fixed number of wiggles or fixed wiggle width?
	"wiggles" => 7, # number of wiggles or wiggle width
	"dashlength" => [[0]],
	"orientation" => [[0]], # orientation of arc
	"color" => "Black",
	"print" => 1,
	"staythere" => 0,
	"bethere" => 0,
	"width" => [[2]],
	"interval" => [0,100]);
    foreach $key ("phi1","phi2","radius","amplitude","dashlength","width") {
	$evolve{$key} = ['$t']
    }
    foreach $key ("coordinates","polar") {
	$evolve{$key} = ['$t','$t']
    }
    $tmp = shift;
    %myoptions = %{$tmp};
    $tmp = shift;
    if ($tmp) {%myevolve = %{$tmp}} else { %myevolve = () }
    foreach $key (keys(%myoptions)) {
	$options{$key} = $myoptions{$key}
    }
    foreach $key (keys(%myevolve)) {
	$evolve{$key} = $myevolve{$key}
    }
    $type = $options{"type"};
    $color = $options{"color"};
    $arrow = $options{"arrow"};
    $bethere = $options{"bethere"};
    $staythere = $options{"staythere"};
    $orientation = $options{"orientation"};
    $fixedwiggles = $options{"fixedwiggles"};
    $wiggles = $options{"wiggles"};
    ($start,$end) = @{$options{"interval"}};
    $mysteps = $STEPS*($end-$start)/$MOVIELENGTH;
    ($print,$iloc,$imin) = 
	printyesno($start,$end,$STEPS,$IGLOB,$bethere,$staythere);
    $progress = ($iloc-$imin)/$mysteps;
    ($phi1) = evolve($progress,$options{"phi1"},$evolve{"phi1"});
    ($phi2) = evolve($progress,$options{"phi2"},$evolve{"phi2"});
    ($radius) = evolve($progress,$options{"radius"},$evolve{"radius"});
    ($amplitude) = evolve($progress,$options{"amplitude"},
			   $evolve{"amplitude"});
    ($dashlength) = evolve($progress,$options{"dashlength"},
			    $evolve{"dashlength"});
    ($width) = evolve($progress,$options{"width"},$evolve{"width"});
    ($xcen,$ycen) = evolve($progress,$options{"coordinates"},
			    $evolve{"coordinates"});
    ($rpos,$phipos) = evolve($progress,$options{"polar"},$evolve{"polar"});
    $xcoord = $xcen + $rpos*cos($phipos/180*$PI);
    $ycoord = $ycen + $rpos*sin($phipos/180*$PI);
    if ($phi1==$phi2) {$print = 0}
    if ($orientation==-1) {($phi1,$phi2) = ($phi2,$phi1)}
    $xfoot = $xcoord+$radius*cos($phi1/180*$PI);
    $yfoot = $ycoord+$radius*sin($phi1/180*$PI);
    $xhead = $xcoord+$radius*cos($phi2/180*$PI);
    $yhead = $ycoord+$radius*sin($phi2/180*$PI);
    # only even number of wiggles:
    $arclength = abs($radius*($phi2-$phi1)*$PI/360);
    $length = sqrt( ($xhead-$xfoot)**2 + ($yhead-$yfoot)**2 );
    $angle = asin($length/2/$radius);
    if ($fixedwiggles==1) {
	$nwiggles = $wiggles;
    } else {
	$nwiggles = 2*int(max(1,$arclength/$wiggles));
    }
    $printin = $options{"print"};
    if ($print*$printin == 1) {
	print("\\SetWidth{".$width."}\n");
	print("\\SetColor{".$color."}\n");
	if ($type eq "photon") {
	    print("\\PhotonArc"."(".$xcoord.",".$ycoord.")(".$radius.",".
		  $phi1.",".$phi2."){".($amplitude)."}{".$nwiggles."}\n");
	} elsif ($type eq "gluon") {
	    print("\\GlueArc"."(".$xcoord.",".$ycoord.")(".$radius.",".
		  $phi1.",".$phi2."){".($amplidute)."}{".$nwiggles."}\n");
	} else {
	    if ($dashlength > 0) {
		if ($arrow == 1) {
		    print("\\DashArrowArc"."(".
			  $xcoord.",".$ycoord.")(".$radius.",".
			  $phi1.",".$phi2."){".$dashlength."}\n");
		} elsif ($arrow == -1) {
		    print("\\DashArrowArc"."(".
			  $xcoord.",".$ycoord.")(".$radius.",".
			  $phi2.",".$phi1."){".$dashlength."}\n");
		} else {
		    print("\\DashCArc"."(".$xcoord.",".$ycoord.")(".$radius.",".
			  $phi1.",".$phi2."){".$dashlength."}\n");
		} 
	    } else {
		if ($arrow == 1) {
		    print("\\ArrowArc"."(".$xcoord.",".$ycoord.")(".
			  $radius.",".$phi1.",".$phi2.")\n");
		} elsif ($arrow == -1) {
		    print("\\ArrowArc"."(".$xcoord.",".$ycoord.")(".
			  $radius.",".$phi2.",".$phi1.")\n");
		} else {
		    print("\\CArc"."(".$xcoord.",".$ycoord.")(".$radius.",".
			  $phi1.",".$phi2.")\n");
		}
	    }
	}
    }
    return({"center" => [$xcoord,$ycoord],
	    "radius" => $radius,
	    "foot" => [$xfoot,$yfoot],
	    "head" => [$xhead,$yhead],
	    "angle" => $angle,
	    "phi1" => $phi1,
	    "phi2" => $phi2,
	    "print" => $print*$printin});
}

#- }}}
#- {{{ arrowarc:

sub arrowarc {
    my($tmp,$out,%options);
    $tmp = shift;
    %options = %{$tmp};
    $tmp = shift;
    $options{"arrow"} = 1;
    $out = arc(\%options,$tmp);
}

#- }}}
#- {{{ photon:

sub photon {
    my($tmp,$out,%options);
    $tmp = shift;
    %options = %{$tmp};
    $tmp = shift;
    $options{"type"} = "photon";
    $out = line(\%options,$tmp);
}

#- }}}
#- {{{ gluon:

sub gluon {
    my($tmp,$out,%options);
    $tmp = shift;
    %options = %{$tmp};
    $tmp = shift;
    $options{"type"} = "gluon";
    $out = line(\%options,$tmp);
}

#- }}}
#- {{{ line:

sub line {
    my($tmp,%options,%myoptions,%evolve,%myevolve,
       @dum,$key,$bethere,$staythere,$start,$end,
       $mysteps,$iloc,$imin,$progress,$printin,$angle,$type,
       $color,$text,$dashlength,$textposo,$textposp,$arrow,
       $wiggles,$nwiggles,$fixedwiggles,
       $xfoot,$yfoot,$xhead,$yhead,$fxcen,$fycen,$hxcen,$hycen,
       $rfoot,$rhead,$phifoot,$phihead,$length);
    %evolve = ();
    %options = (
	"type" => "line", # what to draw [line,photon,gluon]
	"foot" => [[-100,0],[0,100]], # center of foot coordinates
	"head" => [[100,100],[200,100]], # center of head coordinates
	"fpolar" => [[0,0]], # [r,phi] relative to foot
	"hpolar" => [[0,0]], # [r,phi] relative to head
	"dashlength" => [[0]],
	"arrow" => 0,
	"color" => "Black",
	"width" => [[2]],
	"amplitude" => [[10]], # amplitude of photon/gluon wiggles
	"fixedwiggles" => 1, # fixed number of wiggles or fixed wiggle width?
	"wiggles" => 3, # number of wiggles or wiggle width
	"text" => "", # accompanying text
	"textpos" => [[.5,10]], # position of accompanying text
	"bethere" => 0,
	"print" => 1,
		"staythere" => 0,
		"interval" => [0,100]);
    foreach $key ("dashlength","width","amplitude") {
	$evolve{$key} = ['$t']
    }
    foreach $key ("foot","head","textpos","fpolar","hpolar") {
	$evolve{$key} = ['$t','$t']
    }
    $tmp = shift;
    %myoptions = %{$tmp};
    $tmp = shift;
    if ($tmp) {%myevolve = %{$tmp}} else { %myevolve = () }
    foreach $key (keys(%myoptions)) {
	$options{$key} = $myoptions{$key}
    }
    foreach $key (keys(%myevolve)) {
	$evolve{$key} = $myevolve{$key}
    }
    $type = $options{"type"};
    $arrow = $options{"arrow"};
    $color = $options{"color"};
    $bethere = $options{"bethere"};
    $staythere = $options{"staythere"};
    $fixedwiggles = $options{"fixedwiggles"};
    $wiggles = $options{"wiggles"};
    $text = $options{"text"};
    ($textposp,$textposo) = @{$options{"textpos"}};
    ($start,$end) = @{$options{"interval"}};
    $mysteps = $STEPS*($end-$start)/$MOVIELENGTH;
    ($print,$iloc,$imin) = 
	printyesno($start,$end,$STEPS,$IGLOB,$bethere,$staythere);
    $progress = ($iloc-$imin)/$mysteps;
    ($amplitude) = evolve($progress,$options{"amplitude"},
			   $evolve{"amplitude"});
    ($dashlength) = evolve($progress,$options{"dashlength"},
			   $evolve{"dashlength"});
    ($width) = evolve($progress,$options{"width"},
			   $evolve{"width"});
    ($fxcen,$fycen) = evolve($progress,$options{"foot"},
			   $evolve{"foot"});
    ($hxcen,$hycen) = evolve($progress,$options{"head"},
			   $evolve{"head"});
    ($rfoot,$phifoot) = evolve($progress,$options{"fpolar"},
			   $evolve{"fpolar"});
    ($rhead,$phihead) = evolve($progress,$options{"hpolar"},
			   $evolve{"hpolar"});
    ($textposp,$textposo) = evolve($progress,$options{"textpos"},
			   $evolve{"textpos"});
    $xfoot = $fxcen + $rfoot*cos($phifoot/180*$PI);
    $yfoot = $fycen + $rfoot*sin($phifoot/180*$PI);
    $xhead = $hxcen + $rhead*cos($phihead/180*$PI);
    $yhead = $hycen + $rhead*sin($phihead/180*$PI);
    $length = sqrt(($xhead-$xfoot)**2 + ($yhead-$yfoot)**2);
    # only even number of wiggles:
    if ($fixedwiggles==1) {
	$nwiggles = $wiggles;
    } else {
	$nwiggles = 2*int(max(1,$length/$wiggles)+0.5)-1;
    }
    $angle = atan2($yhead-$yfoot,$xhead-$xfoot);
    $xtext = $xfoot + $textposp*$length*cos($angle) - $textposo*sin($angle);
    $ytext = $yfoot + $textposp*$length*sin($angle) + $textposo*cos($angle);
    if ( ($xfoot==$xhead) && ($yfoot==$yhead) ) {$print = 0}
    $printin = $options{"print"};
    if ($print*$printin == 1) {
	print("\\SetWidth{".$width."}\n");
	print("\\SetColor{".$color."}\n");
	if ($text ne "") {
	    print("\\Text(".$xtext.",".$ytext.
		  ")[c]{\\scalefont{1.6}{".$text."}}\n");
	}
	if ($type eq "line") {
	    if ($dashlength > 0) {
		if ($arrow == 1) {
		    print("\\DashArrowLine(".$xfoot.",".$yfoot.")(".
			  $xhead.",".$yhead."){".
			  $dashlength."}\n");
		} elsif ($arrow == -1) {
		    print("\\DashArrowLine(".$xhead.",".$yhead.")(".
			  $xfoot.",".$yfoot."){".
			  $dashlength."}\n");
		} else {
		    print("\\DashLine(".$xfoot.",".$yfoot.")(".
			  $xhead.",".$yhead."){".
			  $dashlength."}\n");
		}
	    } else {
		if ($arrow == 1) {
		    print("\\ArrowLine(".$xfoot.",".$yfoot.")(".$xhead.",".
			  $yhead.")\n");
		} elsif ($arrow == -1) {
		    print("\\ArrowLine(".$xhead.",".$yhead.")(".$xfoot.",".
			  $yfoot.")\n");
		} else {
		    print("\\Line(".$xfoot.",".$yfoot.")(".$xhead.",".
			  $yhead.")\n");
		}
	    }
	} elsif ($type eq "photon") {
	    print("\\Photon(".$xfoot.",".$yfoot.")(".$xhead.",".$yhead.
		  "){".$amplitude."}{".$nwiggles."}\n")
	} elsif ($type eq "gluon") {
	    print("\\Gluon(".$xfoot.",".$yfoot.")(".$xhead.",".$yhead.
		  "){".$amplitude."}{".$nwiggles."}\n");
	}
    }
    return({"foot" => [$xfoot,$yfoot],
	    "head" => [$xhead,$yhead],
	    "print" => $print*$printin});
}

#- }}}
#- {{{ track:

sub track {
    #
    # track a point
    #
    my($printin,$length,$nwiggles,$imin,$imax,$iloc,$name,$curve,
       $param,%axohash,@points);
    %myhash = ("print" => 1,
	       "points" => [1],
	       "color" => "Blue",
	       "width" => [[2]],
	       "bethere" => 0,
	       "parameter" => "coordinates",
	       "staythere" => 1,
	       "curve" => "polygon",
	       "interval" => [0,100]);
    $name = shift;
    $tmp = shift;
    %options = %{$tmp};
    foreach $key (keys(%options)) {
	$myhash{$key} = $options{$key}
    }
    $curve = $myhash{"curve"};
    $param = $myhash{"parameter"};
    if (exists($tracks{$name})) {
	@points = (@{$tracks{$name}},[$myhash{$param}]);
    } else {
	@points = [$myhash{$param}]
    }
    $tracks{$name} = [@points];
    if ($curve eq "polygon") {
	polygon({%myhash,"points" => [@points]});
    } elsif (($curve eq "axocurve") or ($curve eq "axicurve")) {
	axocurve({%myhash,"points" => [@points]});
    } else {
	print("curve ".$curve." not known in track.\n");
	exit;
}
}

#- }}}
#- {{{ curve:

sub curve {
    #
    # varying shape
    #
    my($printin,$length,$nwiggles,$imin,$imax,$iloc,$i);
    %evolve = ();
    %myhash = (
	"function" => [['100*sin(2*pi*$t)',
			'100*cos(2*pi*$t)'],
		       ['50*cos(8*pi*$t)',
			'50*sin(8*pi*$t)']],
	"npoints" => 100,
	"color" => "Red",
	"width" => [[2]],
	"print" => 1,
	"bethere" => 0,
	"staythere" => 0,
	"interval" => [0,100]);
    $tmp = shift;
    %options = %{$tmp};
    $tmp = shift;
    if ($tmp) {%myevolve = %{$tmp}} else { %myevolve = () }
    if (exists($myevolve{"function"})) {
	$myevolve{"foot"} = $myevolve{"function"};
	$myevolve{"head"} = $myevolve{"function"}
    }
    foreach $key (keys(%options)) {
	$myhash{$key} = $options{$key}
    }
    $npoints = $myhash{"npoints"};

    @functions = @{$myhash{"function"}};
    # print("@functions\n");exit;
    if ($#functions == 0) {@functions = ($functions[0],$functions[0])}

    $xfun1 = eval("sub { ".'$t = shift;return('.${$functions[0]}[0].') }');
    $yfun1 = eval("sub { ".'$t = shift;return('.${$functions[0]}[1].') }');
    $xfun2 = eval("sub { ".'$t = shift;return('.${$functions[1]}[0].') }');
    $yfun2 = eval("sub { ".'$t = shift;return('.${$functions[1]}[1].') }');
    
    @foot1 = ($xfun1->(0),$yfun1->(0));
    @foot2 = ($xfun2->(0),$yfun2->(0));
    foreach (1..$npoints) {
	$t = $_/$npoints;
	$x1 = $xfun1->($t); $y1 = $yfun1->($t);
	$x2 = $xfun2->($t); $y2 = $yfun2->($t);
	@head1 = ($x1,$y1);
	@head2 = ($x2,$y2);
	%lhash = (%myhash,
		  "foot" => [\@foot1,\@foot2],
		  "head" => [\@head1,\@head2]);
	line(\%lhash,\%myevolve);
	# line({"foot" => [\@foot1,\@foot2],
	      # "head" => [\@head1,\@head2]});
	@foot1 = @head1;
	@foot2 = @head2;
    }
}

#- }}}
#- {{{ curve1:

sub curve1 {
    #
    # varying shape
    #
    my($printin,$length,$nwiggles,$imin,$imax,$iloc,$i);
    %myhash = ("xfunction1" => '100*sin(2*pi*$t)',
	       "yfunction1" => '100*cos(2*pi*$t)',
	       "xfunction2" => '50*cos(8*pi*$t)',
	       "yfunction2" => '50*sin(8*pi*$t)',
	       "npoints" => 100,
	       "print" => 1,
	       "color" => "Red",
	       "width" => [[2]],
	       "bethere" => 0,
	       "staythere" => 0,
	       "interval" => [0,100]);
    $tmp = shift;
    %options = %{$tmp};
    foreach $key (keys(%options)) {
	$myhash{$key} = $options{$key}
    }
    $npoints = $myhash{"npoints"};
    foreach $key ("xfunction1","xfunction2") {
	if (!exists($myhash{$key})) {
	    $myhash{$key} = $myhash{"xfunction"};
	}
    }
    foreach $key ("yfunction1","yfunction2") {
	if (!exists($myhash{$key})) {
	    $myhash{$key} = $myhash{"yfunction"};
	}
    }

    $xfun1 = eval("sub { ".'$t = shift;return('.$myhash{"xfunction1"}.') }');
    $yfun1 = eval("sub { ".'$t = shift;return('.$myhash{"yfunction1"}.') }');
    $xfun2 = eval("sub { ".'$t = shift;return('.$myhash{"xfunction2"}.') }');
    $yfun2 = eval("sub { ".'$t = shift;return('.$myhash{"yfunction2"}.') }');
    
    @foot1 = ($xfun1->(0),$yfun1->(0));
    @foot2 = ($xfun2->(0),$yfun2->(0));
    foreach (1..$npoints) {
	$t = $_/$npoints;
	$x1 = $xfun1->($t); $y1 = $yfun1->($t);
	$x2 = $xfun2->($t); $y2 = $yfun2->($t);
	@head1 = ($x1,$y1);
	@head2 = ($x2,$y2);
	%lhash = (%myhash,
		  "foot" => [\@foot1,\@foot2],
		  "head" => [\@head1,\@head2]);
	line(\%lhash);
	# line({"foot" => [\@foot1,\@foot2],
	      # "head" => [\@head1,\@head2]});
	@foot1 = @head1;
	@foot2 = @head2;
    }
}

#- }}}
#- {{{ polygon:

sub polygon {
    #
    # a polygon through points
    #
    my($printin,$length,$nwiggles,$imin,$imax,$tmp,$key,@points,
       $point,@pointevolve,$foot,$footevolve,$head,$headevolve,
       $foot1,$foot1evolve,$closed,
       $iloc,%evolve,%myhash,%options);
    %evolve = ();
    %myhash = ("points" => [[[-100,0],[-50,50]],
			    [[-20,30],[20,30]]],
	       "print" => 1,
	       "color" => "Red",
	       "closed" => 0,
	       "width" => [[2]],
	       "bethere" => 0,
	       "staythere" => 0,
	       "interval" => [0,100]);
    foreach $key ("width") {
	$evolve{$key} = ['$t']
    }
    $tmp = shift;
    %options = %{$tmp};
    $tmp = shift;
    if ($tmp) {%myevolve = %{$tmp}} else { %myevolve = () }
    foreach $key (keys(%options)) {
	$myhash{$key} = $options{$key}
    }
    @points = @{$myhash{"points"}};
    $evolve{"points"} = ([]);
    foreach $point (@points) {
	$evolve{"points"} = [@{$evolve{"points"}},['$t','$t']];
    }
    foreach $key (keys(%myevolve)) {
	$evolve{$key} = $myevolve{$key}
    }
    @pointevolve = @{$evolve{"points"}};
    $foot = shift(@points);
    $closed = $myhash{"closed"};
    $foot1 = $foot;
    $footevolve = shift(@pointevolve);
    $foot1evolve = $footevolve;
    while (@points) {
	$head = shift(@points);
	$headevolve = shift(@pointevolve);
	line({%myhash,
	      "head" => $head,
	      "foot" => $foot
	     },
	     {%evolve,
	      "foot" => $footevolve,
	      "head" => $headevolve
	      });
	$foot = $head;
    }
    if ($closed == 1) {
	line({%myhash,
	      "head" => $foot1,
	      "foot" => $head
	     },
	     {%evolve,
	      "head" => $foot1evolve,
	      "foot" => $headevolve
	     });
    }
	
}

#- }}}
#- {{{ axocurve:

sub axocurve {
    #
    # a curve as defined by axodraw
    #
    my($printin,$length,$nwiggles,$imin,$imax,$iloc,%evolve,%myhash,%options);
    %evolve = ();
    %myhash = ("points" => [[[-100,0],[-50,50]],
			    [[-20,30],[20,30]]],
	       "print" => 1,
	       "color" => "Red",
	       "width" => [[2]],
	       "bethere" => 0,
	       "staythere" => 0,
	       "interval" => [0,100]);
    foreach $key ("width") {
	$evolve{$key} = ['0']
    }
    $tmp = shift;
    %options = %{$tmp};
    $tmp = shift;
    if ($tmp) {%myevolve = %{$tmp}} else { %myevolve = () }
    foreach $key (keys(%options)) {
	$myhash{$key} = $options{$key}
    }
    foreach $key (keys(%myevolve)) {
	$evolve{$key} = $myevolve{$key}
    }
    ($start,$end) = @{$myhash{"interval"}};
    $color = $myhash{"color"};
    $mysteps = $STEPS*($end-$start)/$MOVIELENGTH;
    ($print,$iloc,$imin) = 
	printyesno($start,$end,$STEPS,$IGLOB,$bethere,$staythere);
    $progress = ($iloc-$imin)/$mysteps;
    ($width) = evolve($progress,$myhash{"width"},$evolve{"width"});
    @points = @{$myhash{"points"}};
    $evolve{"points"} = ([]);
    foreach $point (@points) {
	$evolve{"points"} = [@{$evolve{"points"}},['$t','$t']];
    }
    @pointevolve = @{$evolve{"points"}};
    $string = "";
    while (@points) {
	$point = shift(@points);
	$pointevolve = shift(@pointevolve);
	($xpoint,$ypoint) = evolve($progress,$point,$pointevolve);
	$string .= "($xpoint,$ypoint)";
    }
    $printin = $myhash{"print"};
    if ($print*$printin == 1) {
	print("\\SetWidth{".($width)."}\n");
	print("\\SetColor{".$color."}\n");
	print("\\Curve{".$string."}\n");
    }
}

#- }}}
#- {{{ printheader:

sub printheader {
#    print("\\documentclass[12pt]{article}\n",
    print("\\documentclass{beamer}\n",
	  "\\usepackage{axodraw}\n",
	  "\\usepackage{rotate}\n",
	  "\\usepackage{scalefnt}\n",
	  "\\usepackage{color}\n",
#	  "\\usepackage[text={7in,10in}]{geometry}\n",
	  "\\begin{document}\n",
#	  "\\setlength{\\unitlength}{1pt}\n",
	  "\\pagestyle{empty}\n",
	  "\\setbeamertemplate{footline}[page number]{}\n",
	  "\\setbeamertemplate{navigation symbols}{}\n"
	);
}

#- }}}
#- {{{ printfooter:

sub printfooter {
    print("\\end{document}\n");
}

#- }}}
#- {{{ sub max :

sub max { # max(ARRAY) returns maximum of ARRAY
    my($OUT,@IN);
    $OUT = $_[0];
    @IN = @_;
    for (@IN) {
        if ($_ > $OUT) {
            $OUT = $_}
        }
    return($OUT);
}

#- }}}
#- {{{ sub error:

sub error {
    my($key,$value);
    $key = shift;
    $value = shift;
    print("Input error for ".$value."\n");
    exit;
}

#- }}}
#- {{{ sub printyesno:

sub printyesno {
    #
    # Check we object should be printed ($print=1) or not ($print=0).
    # It will *not* be printed if
    # - time is before the active interval AND $bethere=0
    # - time is after  the active interval AND $staythere=0
    #
    # Returns also 
    # - $iloc, the counting parameter of that particular object
    # - $imin, the value of $IGLOB when active interval starts.
    # 
    my($imin,$imax,$start,$end,$STEPS,$IGLOB,$iloc,$staythere,$bethere);
    ($start,$end,$STEPS,$IGLOB,$bethere,$staythere) = @_;
    $imin = $start/$MOVIELENGTH*$STEPS;
    $imax = $end/$MOVIELENGTH*$STEPS;
    $print = 1;
    if ($IGLOB < $imin) { 
	$iloc = $imin;
	if ($bethere != 1) { $print = 0 }
    } elsif ($IGLOB > $imax) { 
	$iloc = $imax;
	if ($staythere != 1) { $print = 0 }
    }
    else { $iloc = $IGLOB }
    return($print,$iloc,$imin);
}

#- }}}
#- {{{ printexit;

sub printexit {
    $text = shift;
    print("*** AxiMate: ",$text," - EXITING\n");
    exit;
}

#- }}}
#- {{{ evolve:

sub evolve {
    #
    # This function determines the actual value of the parameters $key.
    # It uses the evolution function associated with $key in the hash
    # $evolve.
    #
    my($progress,$input,$num,$key,$options,$evolve,$dum,$step,$evolfun,
       $funprog,$i,$start,$end,@valthen,@evolfun,@linearterm,
       $diffevol,$diffpoints,$val);
    my(@boundary,@val);
    $progress = shift;
    $input = shift;
    $evolve = shift;
    $start = ${$input}[0];
    if ($#{$input} == 0) { ${$input}[1] = ${$input}[0] }
    $end = ${$input}[1];
    @valthen = ();
    @evolfun = ();
    @linearterm = (0,0);
    foreach $i (0..$#{$evolve}) {
	$evolfun =
	    eval("sub { ".'$t = shift;return('.${$evolve}[$i].') }');
	$diffevol = ($evolfun->(1)) - ($evolfun->(0));
	$funprog = ($evolfun->($progress)) - ($evolfun->(0));
	if ($#{$start} > -1) {
	    if ($#{$end} > -1) {
		$diffpoints = ${$end}[$i] - ${$start}[$i];
		if (abs($diffevol) < .0001) {
		    $val = ${$start}[$i] + $funprog + $diffpoints*$progress;
		} else {
		    $val = ${$start}[$i] + $funprog*$diffpoints/$diffevol;
		}
	    } else {
		$val = ${$start}[$i] + $funprog;
	    }
	} else {
	    if ($#{$end} > -1) {
		$val = ${$end}[$i] + $funprog - $diffevol;
	    } else {
		$val = $evolfun->($progress);
	    }
	}
	@valthen = (@valthen,$val);
    }
    return(@valthen);
}

#- }}}
