pie -- generates system usage postscript charts

Rodney Peck II rodney at sun.ipl.rpi.edu
Thu Nov 9 14:42:44 AEST 1989


As those of you who saw the last thing I posted here might have guess
already, I'm gathering system statistics for a network of suns in this
lab.  They want some cute graphics so I'm producing them.  I thought
maybe some other people might like this idea and would be able to do
something with it and have some fun.

The best part of this script is the part that figures out what the pie
slice size cutoff will be.  It does a (very) small amount of math that
took me about five minutes to think up. It reduces to a single
division operation and works perfectly.

The program is in perl and it produces postscript.  I hope you like
it.

If you have updates or patches, as always, mail them to me
(rodney at ipl.rpi.edu) and I'll incorporate them in the next version.  I
am maintaining this under RCS so if you use RCS, please make yourself
a branch off from version 1.4 for your changes.  RCS is really nice.

Here's the code:
----
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  pie pie.1 piechart.ps
# Wrapped by rodney at sun.ipl.rpi.edu on Wed Nov  8 22:34:54 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'pie' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'pie'\"
else
echo shar: Extracting \"'pie'\" \(2297 characters\)
sed "s/^X//" >'pie' <<'END_OF_FILE'
X#!/usr/local/bin/perl
X# $Id: pie,v 1.4 89/11/08 22:29:54 rodney Rel $
X
X# setup constants
X$piesize = 150;
X$printheader = 1;
X$xpos = 306;
X$ypos = 396;
X$label = '<<<nolabelhere>>>';
X
X$field{'process'} = 1;
X$units{'process'} = '';
X$field{'cpu'} = 2;
X$units{'cpu'} = 'mins';
X$field{'io'} = 3;
X$units{'io'} = 'k';
X$field{'memory'} = 4;
X$units{'memory'} = 'k*sec';
X$type = 'cpu';
X
X#parse up the arguments
Xwhile ($_=$ARGV[0],/^-/) {
X shift;
X if (/^-l/) { $label = shift;}
X elsif (/^-x/) { $xpos = shift;}
X elsif (/^-y/) { $ypos = shift;}
X elsif (/^-s/) { $piesize = shift;}
X elsif (/^-p/) { $printheader = 0;}
X elsif (/^-f/) { $type = shift;}
X}
X
Xif ($label eq '<<<nolabelhere>>>') {
X   $label = sprintf ("%s %s",$type, $units{$type});}
Xif ($field{$type} == 0) {die "$0: sa -m has no \"$type\" field.\n";}
X
X# copy the postscript program to the output
X$pieprogram = "piechart.ps";
Xopen pieprogram;
Xprint "%!\n";
Xif ($printheader) {
X  while (<pieprogram>) {
X    if (/^%/) {} else 
X    {print;}
X  }
X}
X
X# basically, we want at least 6 points on the edge of a slice.
X# this number was found by guessing and looking at charts that were nice
X# since the side is the percentage times the distance around the whole,
X# which is pi times the diameter, we get the percentage to give 6 points
X# as follows: 6 / pi / diameter = percentage.  since this is just a
X# rough six, we'll pretend that pi is three and come up with 2/diameter.
X#   isn't math fun?!
X$minpercent = 2 / $piesize;
X
X# read in the data
X$total = 0;
Xwhile (<>) {
X  split;
X  $num =  $_[$field{$type}];
X  if ($num < 0) {$num = $num + 4294967294;}
X  $value{$_[0]} = $num;
X  $total = $total + $num;
X}
X#print the label, and font sizes
Xprintf ("(%s %d %s) %.4f %.4f\n[\n",$label,$total,$units{$type},
X	$piesize/3,$piesize/7);
X
X#print the data back out formatted
X$other = 0.0;
Xforeach $key (keys(value)) {
X  $percent = $value{$key}/$total;
X  if ($percent < $minpercent) {
X    $other = $other + $value{$key};
X  }
X  else {
X    printf ("[(%s %d %s) %f ]\n",$key,$value{$key},$units{$type},$percent);
X  }
X}
X
X# print an "other" if we used it
Xif ($other != 0.0) {
X  printf ("[(other %d %s) %f ]\n",$other,$units{$type},$other/$total);
X}
X
X# and finally, the postscript closing
Xprint "] $xpos $ypos $piesize DrawPieChart\n";
Xif ($printheader) {print "showpage\n";}
X
X  
END_OF_FILE
if test 2297 -ne `wc -c <'pie'`; then
    echo shar: \"'pie'\" unpacked with wrong size!
fi
chmod +x 'pie'
# end of 'pie'
fi
if test -f 'pie.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'pie.1'\"
else
echo shar: Extracting \"'pie.1'\" \(1238 characters\)
sed "s/^X//" >'pie.1' <<'END_OF_FILE'
X.\" $Header: /home/rodney/ipl/acct/RCS/pie.1,v 1.1 89/11/08 22:18:22 rodney Rel $
X.TH pie l "November 8, 1989"
X.SH NAME
Xpie \- generate a postscript program to display a pie chart of system usage
X.SH SYNOPSIS
X.B pie
X[
X.B \-p \-x <xpos> \-y <ypos> \-l <label> \-f <field> \-s <size>
X]
X.SH DESCRIPTION
X.LP
X.B w
XDraws a nice postscript pie chart of system activity given the output
Xof \"sa -m\".  The command \"sa -m | pie | lpr -Ppostscript\" will
Xgive you a nice laser print of the cpu usage of your system since you
Xlast removed the accounting files.
X.SH OPTIONS
X.TP
X.B \-p
XSuppress the header file and showpage.  This is used to print multiple
Xpies per page.  (I know that sounds funny.)
X.TP
X.B \-s <size>
XChanges the diameter of the pie.  The default is 150 points.  The text
Xsize and smallest pie slice will be scaled accordingly.
X.TP
X.B \-x <xpos> \-y <ypos>
XThe position of the center of the pie.
X.TP
X.B \-l <label>
XA string for the label at the bottom.
X.TP
X.B \-f <field>
XOne of 'process', 'cpu', 'io', or 'memory': the available fields from
Xthe sa -m command. Cpu is the default.
X.SH EXAMPLE
X.RS
X.ft B
X.nf
Xexample% sa -m | pie | lpr
Xexample%
X.ft R
X.fi
X.RE
X.SH FILES
X.PD 0
X.TP 20
X.B ./piechart.ps
X.SH "SEE ALSO"
X.BR sa (1)
X.SH BUGS
END_OF_FILE
if test 1238 -ne `wc -c <'pie.1'`; then
    echo shar: \"'pie.1'\" unpacked with wrong size!
fi
# end of 'pie.1'
fi
if test -f 'piechart.ps' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'piechart.ps'\"
else
echo shar: Extracting \"'piechart.ps'\" \(6952 characters\)
sed "s/^X//" >'piechart.ps' <<'END_OF_FILE'
X%!
X% $Header: /home/rodney/ipl/acct/RCS/piechart.ps,v 1.3 89/11/07 16:11:52 rodney Rel $
X% $Log:	piechart.ps,v $
X% Revision 1.3  89/11/07  16:11:52  rodney
X% pie chart header file
X% 
X% Revision 1.2  89/11/07  16:10:52  rodney
X% last of the hardwired piecharts.
X% 
X% Revision 1.1  89/11/07  15:37:24  rodney
X% Initial revision
X% 
X% Cookbook Example Program from First Printing, Revised 7 Jan 1985
X% Program: Drawing a Pie Chart     Number: 18
X%-----------------------------------------------------------------------------
X%
X/PieDict 24 dict def                        % Local storage for
X					    % ``DrawPieChart'' and its related
X					    % procedures.
XPieDict begin
X  /DrawSlice                                % DrawSlice draws an outlined and
X    { /grayshade exch def                   % filled-in pie slice. It takes
X      /endangle exch def                    % four operands: the label for
X      /startangle exch def                  % this particular pie slice, the
X      /thelabel exch def                    % starting angle for the slice,
X					    % the ending angle for the slice
X					    % and the shade of gray the slice
X					    % should be.
X							
X      newpath                               % Create a path which will draw a
X					    % pie slice.
X	0 0 moveto
X	0 0 radius startangle endangle arc
X      closepath
X	       
X      1.415 setmiterlimit                   % This guarantees that when we
X					    % outline the pie slices with a
X					    % stroke that we will not get a
X					    % spike on the interior angles.
X      gsave grayshade setgray fill grestore % Fill the pie slice path with the
X      stroke                                % appropriate gray color. By using
X					    % gsave and grestore we don't lose
X					    % the current path. Since
X					    % PostScript paints color onto the
X					    % page, it is very important that
X					    % we fill the pie slice first and
X					    % then outline it with a stroke.
X      gsave                                 % Draw the tick mark and place the
X					    % label:
X	startangle endangle add 2 div       % Find the center of the pie slice
X	  rotate                            % and rotate so that the x-axis
X					    % coincides with this center.
X	radius 0 translate                  % Translate the origin out to the
X					    % circumference.
X	newpath
X	  0 0 moveto labelps .8 mul 0 lineto% Draw the tick-mark.
X	stroke
X	labelps 0 translate                 % Move the origin out a little
X					    % beyond the circumference.
X	0 0 transform                       % Next we wish to place the label
X      grestore                              % at the current origin. If we
X      itransform                            % simply draw the text on the page
X      /y exch def /x exch def               % now, it would come out rotated.
X      x y moveto                            % Since this is not desired we
X					    % avoid it by returning to the
X					    % previous unrotated coordinate
X					    % system. Before returning,
X					    % though, we would like to
X      x 0 lt                                % remember the position of the
X	{ thelabel stringwidth pop neg      % current origin on the printed
X	    0 rmoveto                       % page. We will accomplish this by
X	} if                                % using the transform and
X					    % itransform operators. Performing
X					    % a transform on the origin pushes
X					    % the coordinates of the origin in
X					    % device space onto the operand
X					    % stack. Performing a grestore
X					    % returns us to the previous
X					    % unrotated coordinate system.
X					    % Next we perform an itransform on
X					    % the two device coordinates left
X					    % on the stack to determine where
X					    % we are in the current coordinate
X					    % system.
X      y 0 lt { 0 labelps neg rmoveto } if   % Make some adjustments so that
X      thelabel show                         % the label text won't collide
X    } def                                   % with the pie slice.
X  /findgray                                 % Procedure findgray calculates
X    { /i exch def /n exch def               % the gray value for a slice. It
X      i 2 mod 0 eq                          % takes two arguments: the total
X	{ i 2 div n 2 div round add n div } % number of slices and the current
X	{ i 1 add 2 div n div }             % slice number (Given that there
X	ifelse                              % are n pie slices, the slices are
X    } def                                   % ``numbered'' from 1 to n). The
Xend                                         % gray values for the pie slices
X					    % range evenly from white to black
X					    % (i.e. - the values provided to
X					    % setgray range from (n/n, n-1/n,
X					    % ..., 1/n)). Since we don't want
X					    % similar values of gray next to
X					    % each other, findgray
X					    % ``shuffles'' the possible
X					    % combinations of gray.
X								   
X/DrawPieChart                               % DrawPieChart takes seven
X  { PieDict begin                           % arguments: the title of the pie
X      /radius exch def                      % chart, the point size to print
X      /ycenter exch def /xcenter exch def   % the title in, the point size to
X      /PieArray exch def                    % print the labels for each slice
X      /labelps exch def /titleps exch def   % in, a special array (described
X      /title exch def                       % below where DrawPieChart is
X					    % called), the (x,y) center of the
X					    % pie chart and the radius of the
X					    % pie chart.
X							
X      gsave
X	xcenter ycenter translate           % Translate the coordinate system
X					    % origin to center of pie chart.
X	/Helvetica findfont titleps         % Print the title of the pie chart
X	  scalefont setfont                 % in Helvetica.
X	title stringwidth pop 2 div neg     % Center the title below the pie
X					    % chart.
X	  radius neg titleps 3 mul sub
X	  moveto title show
X	/Helvetica findfont labelps         % Print the individual pie slice
X	  scalefont setfont                 % labels in Helvetica
X	/numslices PieArray length def
X	/slicecnt 0 def
X	/curangle 0 def                     % A ``loop'' variable that keeps
X					    % track of the angle of arc to
X					    % begin each pie slice at.
X	PieArray                            % Repeat the following for each
X					    % element in the PieArray.
X	  { /slicearray exch def
X	    slicearray aload pop            % Push the label and percentage
X					    % onto the stack.
X	    /percent exch def
X	    /label exch def
X	    /perangle percent 360 mul def   % Convert the percentage into
X					    % degrees of angle.
X	    /slicecnt slicecnt 1 add def
X	    label curangle
X	      curangle perangle add
X	      numslices slicecnt findgray
X	      DrawSlice
X	    /curangle curangle perangle add % Update the current starting
X					    % angle.
X	      def
X	  } forall
X      grestore
X    end
X  } def
X       
END_OF_FILE
if test 6952 -ne `wc -c <'piechart.ps'`; then
    echo shar: \"'piechart.ps'\" unpacked with wrong size!
fi
# end of 'piechart.ps'
fi
echo shar: End of shell archive.
exit 0

--
Rodney



More information about the Alt.sources mailing list