[comp.sources.misc] v06i075: cpic --- yet another troff preprocessor

allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (03/22/89)

Posting-number: Volume 6, Issue 75
Submitted-by: tcjones@watdragon.waterloo.edu (speedboat jones)
Archive-name: cpic



cpic --- troff preprocessor producing pic(1) output to draw some simple graphs
         and diagrams.


This is a little language that lets you draw simple graphs (the nodes
and edges type) in pic. There's very little to it and it produces nice
pictures. The best way to see what happens is to make it and run the
Example file through cpic and pic and eqn and troff -ms and compare what
comes out to what went in.  Good luck.

Terry Jones

    Department Of Computer Science,  University Of Waterloo
    Waterloo Ontario Canada N2L 3G1. Phone: 1-519-8884674
    UUCP:                    ...!watmath!watdragon!tcjones
    CSNET, Internet, CDNnet: tcjones@dragon.waterloo.{cdn,edu}
    BITNET:                  tcjones@WATER.bitnet
    Canadian domain:         tcjones@dragon.uwaterloo.ca



#! /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 archive 1 (of 1)."
# Contents:  cpic cpic/Example cpic/README cpic/cpic.c cpic/cpic.h
# Wrapped by tcjones@watdragon on Wed Mar 15 14:03:58 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test ! -d 'cpic' ; then
    echo shar: Creating directory \"'cpic'\"
    mkdir 'cpic'
fi
if test -f 'cpic/Example' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cpic/Example'\"
else
echo shar: Extracting \"'cpic/Example'\" \(4965 characters\)
sed "s/^X//" >'cpic/Example' <<'END_OF_FILE'
X.LP
X.EQ
delim $$
X.EN
X
X.TL
CPIC
X
X.NH 1
Pictures vs Figures
X
X.PP
XFor the purposes of this discussion a picture will be thought of as containing
some positive number of figures. The figures are drawn side by side.
X
X.NH 1
Delimiters and Arguments
X
X.PP
Delimit the picture with .CPS and .CPE (Charlie Pic Start/End).  The
X\&.CPS line can take two real arguments, they are the radius of the
circle on which the vertices of the figures are put, and the radius of
the actual vertices. If only one argument is given it is taken as the
radius of the figures. You can change the default values to suit
yourself by doing obvious things to cpic.h. The default values are 0.4
for the figure radius and 0.02 for the vertex radius.
X
X.NH 1
How Many Figures to a Picture
X
X.PP
You can have as many as you like (in theory). You will need to adjust 
the default figure radius if you require alot of figures or if you have
wide labels on your figures.
The first number after the .CPS line tells how many individual figures 
there will be in this picture.
X
X.NH 1
Defining a Figure
X
X.PP
XEach figure in the picture is specified as follows:
firstly, give the number of vertices that will be in this figure,
and then give its edge list.
X
X.NH 2
The Figure's Vertex Count
X
X.PP
Typically you will just want to simply say how many vertices are to be in the
next figure of this picture. This is done as one would expect, by giving the 
count alone on a line. It is also possible to define a label for each figure.
The label should be given enclosed in double quotes (") and will be placed to
the left of the figure in question. Cpic will make a primitive attempt to
estimate the width of the label. If the spacing is not correct the the width
may also be given as a third parameter.
X
An example is
X.br
X8 "Octahedron" 1.0
X.br
which specifies a figure on eight vertices, labelled with the string 
X"Octahedron" centred in a field one inch wide. Cpic should be able to
guesstimate the appropriate width for simple labels such as this.
X
X.NH 2
A Figure's Edge List
X
X.PP
XEdges are specified as a b c, meaning vertex a should be joined
to vertex b with a multiplicity of c. If c is omitted it defaults to 1.
c must be less than or equal to 3. The edge list should immediately 
follow the vertex count.
X
The vertices will be numbered from 1 upwards. Vertex 1 will always be 
located on the same horizontal level as the centre of the figure, and
to the right of the centre. The numbering continues consecutively
anti-clockwise around the circumference. I should have added a feature
to allow for an initial rotation instead of insisting that the first 
vertex be at 0 degrees. This numbering is internal and should not be confused
with labels that might one day appear on vertices when the page is printed.
X
X.NH 1
Labelling the Entire Picture
X
X.PP
This is most comfortably accomplished by giving the label after the .CPE.
X
X.NH 1
Comments etc.
X
X.PP
Comments are allowed - a comment line is one that starts with a #. 
Blank lines are also ignored. The entire cpic specification will be
output (commented out) within the produced pic.
X
X
X.NH 1
Invocation
X
X.PP
Cpic can be run as a filter or may take file name arguments (but not
both simultaneously). Output may be sent to a file or (by default) to stdout.
Use -o <filename> to write output to a file. This will not overwrite an
existing file though, for that use -O <filename>.
The input specification will be given (commented) in the output pic.
X.br
The following are valid.  
X
X.br
X.I
cpic fred.in > fred.out
X.br
cpic -o fred.out fred.in 
X.br
cat fred.in | cpic > fred.out
X.br
cat fred.in | cpic | pic | tbl etc etc etc...
X.br
X.R
X
X.NH 1
Notes
X
X.PP
All measurements should be given in inches and as floating point
numbers, there is no need to specify the trailing "i" for inches \- the
world will probably end if you do.  The generated pic could be tidied
considerably, and I should add the ability to label vertices and an
option to give an initial rotation for each figure.  Let me know if you
want any of this done.
X
X.NH 1
XExample
X
X.PP
You get the idea. Here is an example.
X
X.CPS 0.3
X# use figure radius of 0.3 inches.
X
X# six figures in this diagram (six things on this line).
X6 
X
X# the first has 4 vertices.
X4 "A:"
X1 2 3
X2 3 2
X3 4
X
X# one with 16 vertices...
X16 "B:"
X2 3 2
X4 7 1
X8 11 3
X5 9
X1 7
X4 12
X13 14
X
X# the third.
X5 "C:"
X1 3
X3 5
X5 2
X2 4
X4 1
X
X# the fourth.
X2 "D:"
X1 2 3
X
X# the fifth.
X8 "E:"
X1 2 2
X2 3
X5 2 3
X6 1 1
X2 4 2
X3 4 
X4 5 2
X5 6 2
X6 7
X7 8
X
X# the sixth.
X6 "F:"
X1 2 1
X4 5 2
X5 6 3
X.CPE
X
X.ce
XFig 3: Six pretty little figures are shown above.
X
X
X
The vortex...
X
X.CPS
X1
X16 "$ sigma pi 2x sub i$" 1.0
X# tell cpic how long the label is as it doesn't understand eqn.
X
X1 8
X8 15
X15 6
X6 13
X13 4
X4 11
X11 2
X2 9
X9 16
X16 7
X7 14
X14 5
X5 12
X12 3
X3 10
X10 1
X.CPE
X.ce 
XFig 900: The vortex.
X
X.PP
Here are some that are not in the default size.
X.CPS 0.5 .05
X3
X
X3 "Three pictures:"
X2 3 2
X2 1 2
X
X3
X1 2 2
X1 3 2
X
X3
X3 1 2
X3 2 2
X.CPE
X.ce
Three lovely little babies.
X
X.br
Goodbye
END_OF_FILE
if test 4965 -ne `wc -c <'cpic/Example'`; then
    echo shar: \"'cpic/Example'\" unpacked with wrong size!
fi
# end of 'cpic/Example'
fi
if test -f 'cpic/README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cpic/README'\"
else
echo shar: Extracting \"'cpic/README'\" \(788 characters\)
sed "s/^X//" >'cpic/README' <<'END_OF_FILE'
X
cpic --- troff preprocessor producing pic(1) output to draw some simple graphs
X         and diagrams.
X
X
This is a little language that lets you draw simple graphs (the nodes and 
edges type) in pic. There's very little to it and it produces nice 
pictures. The best way to see what happens is to compile it and
run the Example file through cpic and then pic and eqn and troff -ms and
have a look at what comes out. Good luck.
X
Terry Jones
X
X    Department Of Computer Science,  University Of Waterloo
X    Waterloo Ontario Canada N2L 3G1. Phone: 1-519-8884674
X    UUCP:                    ...!watmath!watdragon!tcjones
X    CSNET, Internet, CDNnet: tcjones@dragon.waterloo.{cdn,edu}
X    BITNET:                  tcjones@WATER.bitnet
X    Canadian domain:         tcjones@dragon.uwaterloo.ca
END_OF_FILE
if test 788 -ne `wc -c <'cpic/README'`; then
    echo shar: \"'cpic/README'\" unpacked with wrong size!
fi
# end of 'cpic/README'
fi
if test -f 'cpic/cpic.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cpic/cpic.c'\"
else
echo shar: Extracting \"'cpic/cpic.c'\" \(17631 characters\)
sed "s/^X//" >'cpic/cpic.c' <<'END_OF_FILE'
X/*
X *  Cpic.c --- produce pic output for graphs in which all vertices lie on a
X *  circle and edge multiplicity is <= 3.
X *
X *  Terry Jones  (tcjones@watdragon)
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <errno.h>
X#include <signal.h>
X#include <math.h>
X#include "cpic.h"
X
char *this_file;
XFILE *in_fp;
XFILE *out_fp;
char *myname;
int line_number = 0;
int found_line = 0;
edge_list *avail_edges = NULL;
picture *avail_pics = NULL;
X
main(argc, argv)
int argc;
char **argv;
X{
X	extern char *getenv();
X	FILE *open_file();
X	extern void clean_up();
X	int std_in = 0;
X
X	myname=*argv;
X	handle_signals();
X
X	set_io(argv, &argc, &std_in);
X
X	if (std_in){
X		in_fp = stdin;
X		this_file = "(standard input)";
X		do_file(stdin, out_fp);
X	}
X	else{
X		if (out_fp != stdout){
X			argc -= 2;
X			argv += 2;
X		}
X		while (--argc){
X			this_file = *++argv;
X			in_fp = open_file(this_file,"r",0);
X			if (in_fp == NULL) continue;
X			do_file(in_fp, out_fp);
X		}
X	}
X	fclose(out_fp);
X	exit(0);
X}
X
X
set_io(c, n, std_in)
char **c;
int *n;
int *std_in;
X{
X	switch (*n - 1){
X
X		case 0: {
X			*std_in = 1;
X			out_fp = stdout;
X			break;
X		}
X
X		case 1: {
X			if (!strcmp(c[1],"-h") || !strcmp(c[1],"-help")) help();
X			out_fp = stdout;
X			break;
X		}
X
X		default: {
X			if (!strcmp(c[1],"-o")){
X				out_fp = open_file(c[2], "w", 0);  /* open w/o clobbering. */
X
X				if (*n == 3){
X					*std_in = 1;
X				}
X			}
X
X			else if (!strcmp(c[1],"-O")){
X				out_fp = open_file(c[2], "w", 1); /* open & clobber. */
X
X				if (*n == 3){
X					*std_in = 1; /* All arguments are used up. */
X				}
X			}
X
X			else out_fp = stdout;
X			break;
X		}
X	}
X}
X		
X
help()
X{
X	fprintf(stderr,"Usage: %s [-[oO] outfile] [infiles...]\n", myname);
X	exit(0);
X}
X
X
handle_signals()
X{
X    if (signal(SIGINT,SIG_IGN)!=SIG_IGN){
X		signal(SIGINT,clean_up);
X	}
X}
X
void
clean_up()
X{
X	fprintf(stderr,"\nInterrupt...\n");
X	goodbye();
X}
X
do_file(in_f, out_f)
XFILE *in_f;
XFILE *out_f;
X{
X	extern picture *begin_picture();
X	extern char *get_quoted_name();
X	char *getline();
X	char line[MAX];
X	picture_list pics;
X	picture *curr_pic = NULL;
X	char *pic_title = NULL;
X	int pics_this_list = 0;
X	double picr;
X	double ptr;
X
X	pics.pic = NULL;
X
X	while ( GETLINE ){
X
X		line_number++;
X
X		/* A line we are not interested in, print it and go on. */
X		if (strlen(line) < 4 || strncmp(line, ".CPS", 4) != 0){
X			fprintf(out_f, "%s\n", line);
X			continue;
X		}
X
X
X		/* A line we are interested in. */
X		/* This line will have format ".CPS [pic_radius] [point_radius]" */
X
X		switch (sscanf(line,".CPS %f %f", &picr, &ptr)){
X
X			case EOF: {
X				/* Could not match, but tried. */
X				picr = PIC_RADIUS;
X				ptr = POINT_RADIUS;
X				break;
X			}
X
X			case 1: {
X				/* Got picr from the sscanf */
X				ptr = POINT_RADIUS;
X				break;
X			}
X
X			case 2: {
X				break;
X			}
X
X			default: {
X				fprintf(stderr,
X					"%s: .CPS line syntax is \".CPS [picture radius [", myname);
X				fprintf(stderr,
X					"point radius]]\". Both are taken to be in inches and\n");
X				fprintf(stderr,
X					"default to %.2f and %.2f if omitted. (Line=%d, File=%s)\n",
X					PIC_RADIUS, POINT_RADIUS, line_number, this_file);
X
X				goodbye();
X				break;
X			}
X
X		}
X
X		/* Start pic processing. */
X		/* fprintf(out_f, ".PS\n#%s\n", line); */
X		fprintf(out_f, ".PS\n");
X
X
X		if ( ! GETLINE ) goodbye();
X		line_number++;
X
X		while (allwhite(line) || iscomment(line)){
X			/* fprintf(out_f,"%s\n",line); */
X			if ( ! GETLINE ) goodbye();
X			line_number++;
X		}
X
X		/* Get the number of pictures that will be on this line. */
X		if (sscanf(line, "%d", &pics_this_list) != 1) goodbye();
X		found_line = line_number;
X
X		pic_title = get_quoted_name(line);
X		begin_picture_list(&pics, pics_this_list, pic_title);
X
X		/* Used to print out the definition commented out, but don't worry. */
X		/* fprintf(out_f, "#%s\n", line); */
X
X		while ( GETLINE ){
X
X			int n1, n2, n3;
X			int pictures_done = 0;
X
X			line_number++;
X
X			/* skip blank and comment lines */
X			while (allwhite(line) || iscomment(line)){
X				/* fprintf(out_f,"%s\n", line); */
X				if ( ! GETLINE ) goodbye();
X				line_number++;
X			}
X
X			/* Now we have a non-blank, non-comment line. */
X			/* Don't worry about echoing it. */
X			/* fprintf(out_f, "#%s\n", line); */
X
X			switch(sscanf(line, "%d %d %d", &n1, &n2, &n3)){
X
X				/* The number of vertices that this picture will be on. */
X				case 1: {
X
X					extern char *get_quoted_name();
X					extern double get_width();
X
X					double width = get_width(line);
X					char *pic_name = get_quoted_name(line);
X
X					curr_pic = 
X						begin_picture(&pics, curr_pic, n1, pic_name, width);
X					break;
X				}
X
X
X				/* Two vertices which are to be joined (once). */
X				case 2: {
X					add_edges(curr_pic, n1, n2, 1);
X					break;
X				}
X
X				/* Two vertices to be joined and the edge multiplicity */
X				case 3: {
X					add_edges(curr_pic, n1, n2, n3);
X					break;
X				}
X
X				default: {
X					if (strlen(line) < 4 || strncmp(line, ".CPE", 4) != 0){
X						fprintf(stderr,"Whoops! bad input \n");
X						
X						goodbye();
X					}
X
X					end_picture(&pics, picr, ptr, out_f);
X					pictures_done = 1;
X					break;
X				}
X			}
X
X		if (pictures_done) break;
X		}
X	}
X	fclose(in_f);
X}
X
begin_picture_list(pl, max_vertices, pic_title)
picture_list *pl;
int max_vertices;
char *pic_title;
X{
X	if (pl->pic != NULL) free_picture_list(pl);
X	pl->pic_limit = max_vertices;
X	pl->pics_in_list = 0;
X	pl->total_label_width = 0.0;
X	strcpy(pl->pic_title, pic_title);
X	pl->pic = NULL;
X}
X
X
picture *
begin_picture(pl, cp, vertices, pic_name, width)
picture_list *pl;
picture *cp;
int vertices;
char *pic_name;
double width;
X{
X	extern picture *new_picture();
X
X	if (pl->pic_limit == pl->pics_in_list){
X		fprintf(stderr,"%s: %d pictures promised on line %d of file %s,\n", 
X			myname, pl->pic_limit, found_line, this_file);
X		
X		fprintf(stderr,"but the picture beginning line %d exceeds this!\n",
X			line_number);
X		goodbye();
X	}
X
X	if (vertices == 0){
X		fprintf(stderr,
X			"%s: A graph with 0 vertices? Interesting. Line=%d, File=%s.\n",
X			myname, line_number, this_file);
X		goodbye();
X	}
X
X	if (width < 0.0){
X		fprintf(stderr,
X			"%s: A label with negative width? Interesting. Line=%d, File=%s.\n",
X			myname, line_number, this_file);
X		goodbye();
X	}
X
X
X	pl->pics_in_list++;
X
X	if (pl->pic == NULL){
X		pl->pic = cp = new_picture();
X	}
X	else{
X		cp->next_pic = new_picture();
X		cp = cp->next_pic;
X	}
X
X
X	/* Try to estimate the label's width. */
X	/* Actual string to be printed does not include the 2 "'s */
X	if (pic_name && width == 0.0) 
X		width = CHAR_WIDTH * (double)(strlen(pic_name) - 2);
X
X	/* Add to the total amount of label space we have seen for this pic. */
X	if (pic_name) 
X		pl->total_label_width += width;
X
X	cp->vertices = vertices;
X	strcpy(cp->fig_name, pic_name);
X	cp->label_width = width;
X	return(cp);
X}
X
X
add_edges(cp, v1, v2, multiplicity)
picture *cp;
int v1;
int v2;
int multiplicity;
X{
X	extern edge_list *new_edge_list();
X	edge_list *temp_e_list;
X
X	if (cp == NULL){
X		fprintf(stderr,"%s: Oops! you tried to define edges before saying\n",
X			myname);
X		fprintf(stderr,
X			"how many vertices there were going to be. Line=%d, File=%s\n",
X			line_number, this_file);
X		goodbye();
X	}
X
X	if (v1 < 1 || v2 < 1 || v1 > cp->vertices || v2 > cp->vertices){
X		fprintf(stderr,
X			"%s: Vertex index outside legal range on line %d, in file %s.\n", 
X			myname, line_number, this_file);
X		goodbye();
X	}
X
X	if (multiplicity < 1 || multiplicity > 3){
X		fprintf(stderr,
X			"%s: Illegal edge multiplicity of %d give on line %d in file %s.\n",
X			myname, multiplicity, line_number, this_file);
X		goodbye();
X	}
X
X	if (v1 == v2){
X		fprintf(stderr,
X"%s: Self loops not supported - edge %d,%d ignored on line %d in file %s.\n",
X			myname, v1, v1, line_number, this_file);
X		return;
X	}
X
X	temp_e_list = new_edge_list();
X
X	temp_e_list->v1 = v1;
X	temp_e_list->v2 = v2;
X	temp_e_list->times = multiplicity;
X
X	temp_e_list->next_edge = cp->e_list;
X	cp->e_list = temp_e_list;
X}
X
XFILE *
open_file(s,mode,flag)
char *s;
char *mode;
int flag;
X{
X	/* 
X	 * fopen() the file whose name is "s", exit on error.
X	 * flag is clobber mode for "w". 1 = clobber existing file.
X	 *
X	 */
X
X
X	FILE *fp, *fopen();
X	struct stat buf;
X
X	if (strcmp(mode,"w") == 0 && flag != 1 && stat(s, &buf) != -1){
X		fprintf(stderr,
X			"%s: file \"%s\" already exists! (Use -O to overwrite.)\n", 
X			myname, s);
X		exit(1);
X	}
X
X	if (!(fp=fopen(s,mode))){
X		fprintf(stderr,"%s: could not open %s (ignored).\n", myname, s);
X	}
X
X	return(fp);
X}
X
X
X
int
file_exists(s)
char *s;
X{
X	/* 
X	 * return 1 if the file s exists, 0 if not.
X	 * This is silly - I should have used access(). Didn't know about it.
X	 *
X	 */
X
X
X	extern int errno;
X	struct stat buf;
X	int status;
X
X	status = stat(s, &buf);
X
X	if (status == 0){
X		return(1);
X	}
X	else if (status == -1 && errno == ENOENT){
X		return(0);
X	}
X	else{
X		fprintf(stderr,"file_exists: stat failed, errno=%d\n", errno);
X		perror("stat");
X		exit(1);
X	}
X}
X
char *
getline(fp,max,where)
XFILE *fp;
int max;
char *where;
X{
X	/* returns null terminated line in "where" and NULL on EOF or error */
X	/* essentially does what gets() does, except for any open FILE * */
X
X	extern char* index();
X	char *fred;
X	
X	if ((fred=fgets(where,max,fp))){
X		char *tmp;
X		if (!(tmp=index(where,'\n'))){
X			printf("getline: Line with no newline!\n");
X			exit(1);
X		}
X		*tmp='\0';
X	}
X	return(fred);
X}
X
X
int 
allwhite(s)
char *s;
X{
X	/*
X	 * Return 1 if the char array s is all white space, or empty.
X	 *
X	 */
X
X	if (!strlen(s)) return(1);
X
X	while (*s){
X		if (*s != ' ' && *s != '\t'){
X			return(0);
X		}
X		s++;
X	}
X	return(1);
X}
X
X
picture *
new_picture()
X{
X	picture *temp;
X
X	if (avail_pics == NULL){
X		if ((temp = (picture *)malloc((unsigned long)sizeof(picture))) == NULL){
X			fprintf(stderr,"Could not malloc! giving up.\n");
X			exit(1);
X		}
X	}
X	else{
X		temp = avail_pics;
X		avail_pics = avail_pics->next_pic;
X	}
X
X	temp->vertices = 0;
X	temp->fig_name[0] = '\0';
X	temp->label_width = 0.0;
X	temp->e_list = NULL;
X	temp->next_pic = NULL;
X	return(temp);
X}
X
edge_list *
new_edge_list()
X{
X	edge_list *temp;
X
X	if (avail_edges == NULL){
X		if ((temp = 
X			(edge_list *)malloc((unsigned long)sizeof(edge_list))) == NULL){
X
X			fprintf(stderr,"Could not malloc! giving up.\n");
X			exit(1);
X		}
X	}
X	else{
X		temp = avail_edges;
X		avail_edges = avail_edges->next_edge;
X	}
X
X	temp->v1 = temp->v2 = temp->times = 0;
X	temp->next_edge = NULL;
X	return(temp);
X}
X
goodbye()
X{
X	fflush(stderr);
X	fclose(in_fp);
X	fclose(out_fp);
X	fprintf(stderr,"%s: Execution aborted on line %d in file %s.\n",
X		myname, line_number, this_file);
X	exit(1);
X}
X
X
X
free_picture_list(pl)
picture_list *pl;
X/* This does not seem to free anything! Is it wrong?  TERRY! */
X{
X	picture *this_pic = pl->pic;
X	edge_list *this_e_list;
X
X	if (this_pic == NULL) return;
X
X	while (this_pic->next_pic != NULL){
X		if (this_pic->e_list != NULL){
X			this_e_list = this_pic->e_list;
X			while (this_e_list->next_edge != NULL){
X				this_e_list = this_e_list->next_edge;
X			}
X
X			this_e_list->next_edge = avail_edges;
X			avail_edges = this_pic->e_list;
X		}
X
X		this_pic = this_pic->next_pic;
X	}
X	this_pic->next_pic = avail_pics;
X	avail_pics = pl->pic;
X}
X
X
end_picture(pl, picr, ptr, f)
picture_list *pl;
double picr;
double ptr;
XFILE *f;
X{
X	/* Produce pic output for each of the pictures in the picture list. */
X
X	/* 
X	 * Will need to do some looking at the list to determine the width
X	 * and other things like that - saving on recomputation of sins etc.
X	 * but for now just be dumb so we can get some output!
X	 */
X
X	picture *p = pl->pic;
X	int rad_count = 1;
X	int sep_count = 1;
X	double sep;
X	double pic_y;
X	double arc_rad;
X	double x_adjust = BOUNDARY;
X
X	if (pl->pic_limit != pl->pics_in_list){
X		fprintf(stderr,
X			"%s: You promised %d graphs but gave %d. (Line %d, File %s)\n",
X			myname, pl->pic_limit, pl->pics_in_list, line_number, this_file);
X		goodbye();
X	}
X
X	/* Calculate inter-picture separator. */
X	sep = (PAGE_WIDTH - 
X		   (2.0 * (double)(pl->pic_limit) * picr) - pl->total_label_width
X		  ) / (double)(pl->pic_limit + 1.0);
X
X	/* Calculate height of figure centres. */
X	pic_y = picr + BOUNDARY + (pl->pic_title[0] ? TITLE_SEP : 0.0);
X
X	/* 
X	 * debug - point of reference.
X	 * fprintf(f,"circle rad 0.1i at (0i,%.2fi) #invisible\n", picr + sep);
X	 */
X
X	while (p != NULL){
X		/* process each picture in turn. */
X
X		char centre[15];
X
X		x_adjust += p->label_width; /* Will be 0.0 if there is no label. */
X		sprintf(centre,"(%.2fi,%.2fi)", 
X			rad_count*picr + sep_count*sep + x_adjust, pic_y);
X		
X		produce_pic(p, centre, picr, ptr, f);
X
X		rad_count += 2;
X		sep_count++;
X		p = p->next_pic;
X	}
X
X	/* 
X	 * DISABLED - use .ce in plain troff to get the title for the picture
X	 * Do the title for the whole picture.
X	 * if (pl->pic_title[0]){
X	 *	fprintf(f, "%s at (%.2fi,%2fi)\n", pl->pic_title, 
X	 *
X	 *		either -- BOUNDARY + sep * (double)(pl->pic_limit + 1) / 2.0 +
X	 *		          picr * 2.0 * (double)(pl->pic_limit) / 2.0,
X	 *		-- or --  PAGE_WIDTH / 2.0,
X	 *
X	 *		TITLE_SEP);
X	 * }
X	 */
X
X	fprintf(f,".PE\n");
X}
X
X
produce_pic(p, centre, picr, ptr, f)
picture *p;
char *centre;
double picr;
double ptr;
XFILE *f;
X{
X	extern double cos();
X	extern double sin();
X
X	int i;
X	static int vertex_count = 0;
X	static point *points;
X
X	/* See if there is any need to recompute the vertex coordinates. */
X
X	if (p->vertices != vertex_count){
X
X		double inc = 360.0 / (double) p->vertices; /* p->vertices != zero */
X
X		/* Need more space than there is - free the old and calloc again. */
X
X		if (p->vertices > vertex_count){ 
X			free(points);
X			if ((points = 
X				(point *)calloc(p->vertices+1, sizeof(point))) == NULL){
X				fprintf(stderr,"%s: Could not calloc! Goodbye.\n",myname);
X				goodbye();
X			}
X		}
X
X		vertex_count = p->vertices;
X
X		for (i=1; i<=p->vertices; i++){
X			double fred = (double)(i-1) * inc * RAD_ADJ;
X			points[i].x = picr * cos( fred );
X			points[i].y = picr * sin( fred );
X		}
X	}
X
X	plot_label(centre, picr, p->fig_name, p->label_width, f);
X	define_vertices(points, p->vertices, centre, ptr, f);
X	plot_vertices(p->vertices, f);
X	plot_edges(points, p->e_list, centre, f);
X}
X
X
plot_label(centre, picr, name, width, f)
char *centre;
double picr;
char *name;
double width;
XFILE *f;
X{
X	if (*name){
X		fprintf(f,"# Picture title.\n");
X		fprintf(f, "%s at %s - (%2fi, 0i)\n", name, centre,(width/2.0) + picr);
X		fprintf(f,"#\n");
X	}
X}
X
X
define_vertices(pts, n, centre, ptr, f)
point *pts;
int n;
char *centre;
double ptr;
XFILE *f;
X{
X	/*
X	 * Output the defined positions of the vertices.
X	 */
X
X	register int i;
X
X	fprintf(f,"# Vertice definitions.\n");
X	for (i=1; i<=n; i++){
X		fprintf(f,"define V_%d X (%.2f,%.2f) + %s X\n", i, pts[i].x, pts[i].y,
X			centre);
X	}
X	fprintf(f,"define Pt_Radius X %.2fi X\n", ptr);
X	fprintf(f,"#\n");
X}
X
X
plot_vertices(n, f)
int n;
XFILE *f;
X{
X	int i;
X
X	fprintf(f,"# Set default circle radius\n");
X	fprintf(f,"circlerad = Pt_Radius\n");
X	fprintf(f,"#\n");
X
X	fprintf(f, "# Do the vertices.\n");
X
X	for (i=1; i<=n; i++){
X
X		/* 
X		 * This will produce labelled vertices from 1,2,...n - useful in debug.
X		 * fprintf(f,"circle \"%d\" at V_%d\n", i, i);
X		 */
X
X		fprintf(f,"circle at V_%d\n", i);
X	}
X	fprintf(f,"#\n");
X}
X
X
plot_edges(pts, edges, centre, f)
point *pts;
edge_list *edges;
char *centre;
XFILE *f;
X{
X	/* No need to check that vertex numbers are in range - done in add_edges. */
X
X	extern double get_arc_radius();
X	extern double distance();
X	char arc_radius[15];
X
X	fprintf(f,"# Draw the edges.\n");
X
X	while (edges != NULL){
X
X		int one = edges->v1;
X		int two = edges->v2;
X
X		if (edges->times != 1){
X			fprintf(f,"arcrad = %.2f\n",
X				get_arc_radius(distance(pts[one].x, pts[one].y, 
X					pts[two].x, pts[two].y)));
X		}
X
X		/* No need to check multiplicities - done in add_edges. */
X		switch(edges->times){
X
X			case 1: {
X				fprintf(f,"line from V_%d to V_%d\n", one, two);
X				break;
X			}
X
X			case 2: {
X				fprintf(f,"arc from V_%d to V_%d\n", one, two);
X				fprintf(f,"arc from V_%d to V_%d\n", two, one);
X				break;
X			}
X
X			case 3: {
X
X				fprintf(f,"line from V_%d to V_%d\n", one, two);
X				fprintf(f,"arc from V_%d to V_%d\n", one, two);
X				fprintf(f,"arc from V_%d to V_%d\n", two, one);
X				break;
X			}
X		}
X
X		edges = edges->next_edge;
X	}
X	fprintf(f,"#\n");
X}
X
X
double
get_arc_radius(d)
double d;
X{
X		/*
X		 *  Some empirical arc radius values depending on the inter pt dist.
X		 *  IT'S PROBABLY NOT A GOOD IDEA TO FIDDLE WITH THESE...
X		 *  of course i should now use all the least squares stuff i learned
X		 *  for the comps :-)
X		 */
X
X		if (d < 0.2) return(0.15);
X		if (d < 0.3) return(0.32);
X		if (d < 0.35) return(0.45);
X		if (d < 0.4) return(0.6);
X		/*if (d < 0.45) return(0.65);*/
X		if (d < 0.5) return(0.8);
X		if (d < 0.6) return(1.05);
X		if (d < 0.7) return(1.22);
X		if (d < 0.77) return(1.32);
X		if (d < 0.85) return(1.75);
X		if (d < 1.0) return(2.0);
X		if (d < 1.5) return(2.5);
X		return(3.0);
X}
X
X
double 
distance(x1, why1, x2, y2)
double x1;
double why1;
double x2;
double y2;
X{
X	return( sqrt ((x1-x2)*(x1-x2) + (why1-y2)*(why1-y2)) );
X}
X
X
char *
get_quoted_name(s)
char *s;
X{
X	/* 
X	 * Try to find a quoted "" string in the array s. If one exists
X	 * then return a pointer to the first quote, having zeroed the
X	 * character after the closing quote.
X	 */
X
X	char *start = index(s, '"');
X
X	if (start){
X		char *quotes = index(start + 1, '"');;
X		if (quotes == NULL){
X			fprintf(stderr, "Closing quote omitted on figure name. Line");
X			fprintf(stderr, " %d, file %s.\n", line_number, this_file);
X			goodbye();
X		}
X		*(quotes + 1) = '\0';
X	}
X	return(start);
X}
X
X
double
get_width(s)
char *s;
X{
X	char *quote1, *quote2;
X	double w = 0.0;
X
X	if ( (quote1 = index(s,'"')) == NULL ||
X		 (quote2 = index(quote1 + 1, '"')) == NULL || 
X		 sscanf(quote2 + 1, "%f", &w) != 1 )
X
X		 return(0.0);
X
X	return(w);
X}
END_OF_FILE
if test 17631 -ne `wc -c <'cpic/cpic.c'`; then
    echo shar: \"'cpic/cpic.c'\" unpacked with wrong size!
fi
# end of 'cpic/cpic.c'
fi
if test -f 'cpic/cpic.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cpic/cpic.h'\"
else
echo shar: Extracting \"'cpic/cpic.h'\" \(949 characters\)
sed "s/^X//" >'cpic/cpic.h' <<'END_OF_FILE'
X/* cpic.h */
X
X#define POINT_RADIUS         0.02
X#define PIC_RADIUS           0.4
X#define PAGE_WIDTH           6.5
X#define TITLE_SEP            0.25
X#define BOUNDARY             0.2
X#define CHAR_WIDTH           0.12
X#define PIC_NAME_LIMIT       512
X#define PIC_TITLE_LIMIT      512
X
X#define RAD_ADJ ( 3.14159265 / 180.0 )
X#define MAX 1024
X
X#define GETLINE getline(in_f, MAX, line)
X#define iscomment(s) (*s == '#')
X
X
typedef struct {
X	double x;
X	double y;
X} point;
X
typedef struct edge_list_entry {
X	int v1;
X	int v2;
X	int times;
X	struct edge_list_entry *next_edge;
X} edge_list;
X
typedef struct pict {
X	int vertices;
X	char fig_name[PIC_NAME_LIMIT];
X	double label_width;
X	edge_list *e_list;
X	struct pict *next_pic;
X} picture;
X
typedef struct plist_entry {
X	int pic_limit;
X	int pics_in_list;
X	double total_label_width;
X	char pic_title[PIC_TITLE_LIMIT];
X	picture *pic;
X} picture_list;
X
X
extern char *malloc();
extern char *calloc();
extern char *index();
END_OF_FILE
if test 949 -ne `wc -c <'cpic/cpic.h'`; then
    echo shar: \"'cpic/cpic.h'\" unpacked with wrong size!
fi
# end of 'cpic/cpic.h'
fi
echo shar: End of archive 1 \(of 1\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have the archive.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0