#!/usr/bin/perl
# draw line and bar graphs
# Brian Ward http://www.o--o.net/

$aspect_ratio = 4/3;
$graph_type = "bar";		# default graph type
$spacing = 10;			# default bar graph spacing
$datapoint = 0;			# show data points
$inversedots = 0;		# inverse dots

$line_nlabels = 6;		# number of labels on a line graph

# font stuff
$font_size = 20;		# label font size (bottom)
$lfont_size = 25;		# label font size (left side)
$tfont_size = 40;		# title font size

$font = "Helvetica-Bold";
# uncomment the next two for an external font file
# $font = "LucidaSans-Bold";
# $font_file = "/home/bri/lsb.pfa";

# -------
require POSIX;

while ($ARGV[0] =~ /^-/) {
    if ($ARGV[0] eq "-line") {
	$graph_type = "line";
    } elsif ($ARGV[0] eq "-bar") {
	$graph_type = "bar";
    } elsif ($ARGV[0] eq "-dp") {
	$datapoint = 1;
    } elsif ($ARGV[0] eq "-idot") {
	$inversedots = 1;
    } elsif ($ARGV[0] eq "-font") {
	shift;
	($tfn, $tff) = split(/:/, $ARGV[0], 2);
	if (-r $tff) {
	    ($font, $font_file) = ($tfn, $tff);
	} else {
	    print stderr "$0: Warning: cannot find font file $tff," .
		" using default\n";
	}
    }
    shift;
}

# -----

$vsize = int(1000/$aspect_ratio);

$title = <>;
chop $title;

$prev_point = "none";
$i = 0;
$max = 0; $min = 9999999999;

while (<>) {
    chop;
    ($label[$i], $value[$i]) = split(/\s+/);
    if ($max < $value[$i]) { $max = $value[$i]; }
    if ($min > $value[$i]) { $min = $value[$i]; }
    $i++;
}

if ($datapoint) {
    @ta = sort(@label);
    $min_dp = $label[0];
    $max_dp = $label[$#label];
}

print<<"HEADER";
%!

/cshow { dup stringwidth pop 2 div neg 0 rmoveto show } bind def

50 100 translate
.5 .5 scale

2 setlinewidth
0   0    moveto
1000 0    lineto
1000 $vsize  lineto
0    $vsize  lineto
closepath stroke
HEADER

if ($font_file) {
    system("cat $font_file");
}

print<<"TITLE";
 /$font findfont $tfont_size scalefont setfont
 500 $vsize $tfont_size .5 mul add moveto
 ($title) cshow
TITLE

$ncols = $#label + 1;
$width = (1000)/($ncols - 1);
if ($graph_type eq "bar") {
    $width = (1000 - $spacing)/($ncols);
} elsif ($graph_type eq "line") {
    $label_interval = int($ncols/$line_nlabels);
    if (!$label_interval) {
	$label_interval = 1;
    }
}

$bwidth = $width - $spacing;

$maxheight = (POSIX::ceil($max/10))*10;
if (($maxheight - $max)/$maxheight < .01) {
    $maxheight = (POSIX::ceil($max/10) + 1)*10;
}

$topexp = POSIX::ceil(log($max)/log(10));
$init_div = (10**($topexp-1));

if (($maxheight/$init_div) < 5) {
    $init_div = $init_div / 2;
    if (($maxheight/$init_div) < 5) {
	$init_div = $init_div / 2;
    }
}

# print stderr $maxheight/$init_div . " : $init_div\n";

# print stderr "$maxheight\n";

$colmul = $vsize / $maxheight;

print "/$font findfont $lfont_size scalefont setfont\n";
$i = 0;
while ($i < $maxheight/$init_div) {
    print " -8 $colmul $i $init_div mul mul moveto\n";
    print " 8 0 rlineto stroke\n";
    print " (" . $i*$init_div . ") dup\n";
    print " stringwidth pop neg -12 add\n";	# x-coord
    print " $i $init_div $colmul mul mul $lfont_size 3.5 div sub moveto\n"; # y
    print " show\n";

    $i++;
}

print "/$font findfont $font_size scalefont setfont\n";

if ($graph_type eq "line") {
    # draw the labels
    $i = 0;
    while ($i < $ncols) {
	plot_linelabel($i, $label[$i]);
	$i++;
    }

    # figure out the continuations and clip the path accordingly
    $k = $ncols - 1;
    $first_y = ($value[0] - ($value[1] - $value[0])) * $colmul;
    $last_y = ($value[$k] - ($value[$k - 1] - $value[$k])) * $colmul;
    print "0 0 moveto 1000 0 rlineto 0 $vsize rlineto -1000 0 rlineto\n";
    print "closepath clip newpath\n";
}

$i = 0;
while ($i < $ncols) {
    $vs = $colmul * $value[$i];
    if ($graph_type eq "line") {
	plot_line($i, $vs, $label[$i]);
    } elsif ($graph_type eq "bar") {
	plot_bar($i, $vs, $label[$i]);
    }
    $i++;
}

if ($inversedots) {
    print "1 setgray 1 setlinewidth\n";
    $i = 0;
    while ($i < $ncols) {
	$vs = $colmul * $value[$i];
	plot_dot($i, $vs, $label[$i]);
	$i++;
    }
}

print "showpage\n";

sub plot_bar {
    local($bar_num, $val, $label) = @_;
    print<<"COL";
	newpath
	$bar_num $width mul $spacing add 0 moveto
	$bwidth 0 rlineto
	0 $val rlineto
	$bwidth neg 0 rlineto
	closepath fill

	$bar_num $width mul $spacing add 0 moveto

	0 $font_size neg rmoveto
	$width 4 div 0 rmoveto
	gsave
	-40 rotate
	($label) show
	grestore

COL
}

sub plot_line {
    local($line_num, $val, $label) = @_;

    if ($line_num == 0) {
	print "8 setlinewidth  % Start of line stroke\n";
	print "$width neg $first_y moveto\n";
    }

    if (!$datapoint) {
	print "$line_num $width mul $val lineto\n";
    } else {
	$gx = 1000 * ($label - $min_dp) / ($max_dp - $min_dp);
	print "$gx $val lineto\n";
    }

    if ($line_num == ($ncols - 1)) {
	print "1000 $width add $last_y lineto\n";
	print "stroke  % End of line stroke\n";
    }
	# $line_num 1 sub $width mul $prev_point lineto stroke

}

sub plot_linelabel {
    local($line_num, $label) = @_;
    if (($line_num % $label_interval) == 0) {
	print <<"LLABEL";
	    gsave newpath
	     $line_num $width mul 0 moveto
	     0 $font_size neg rmoveto
	     $width .5 mul neg 0 rmoveto
	     -40 rotate
	     ($label) show
	    grestore
LLABEL
	if ($line_num && ($line_num != $ncols - 1)) {
	    print "gsave newpath 2 setlinewidth\n";
	    print " $line_num $width mul 0 moveto\n";
	    print " 0 $vsize rlineto stroke\n";
	    print "grestore\n";
	}
    }
}

sub plot_dot {
    local($line_num, $val, $label) = @_;

    if (!$datapoint) {
	# print "$line_num $width mul $val moveto\n";
	$gx = $line_num * $width;
    } else {
	$gx = 1000 * ($label - $min_dp) / ($max_dp - $min_dp);
    }
    print "newpath $gx $val moveto $gx $val 2 0 360 arc closepath fill\n";
}
