[net.sources] Tidy tree plotting.

gillam@aero.ARPA (April Gillam) (11/10/86)

The following programs draw a series of nodes with connecting ports
between them. It was written for the Apollo DN300. The graphics is
specific to that, however the tidy_tree() algorithm is general.
Tidy_tree() is expanded from the following 2 papers which only deal
with binary trees. Mine works with multiple parents and children, but
can only have one node at the top.

Tidy Drawing of Trees by C. Wetherell & A. Shannon IEEE Vol. SE-5 #5
    Sept 79 pp. 514-520
Tidier Drawings of Trees by E. Reingold & J. Tilford IEEE Vol. SE-7 #2
    Mar '81 pp. 223-228

I tried to separate out all the Apollo routines into "ifdef APOLLO", 
but I haven't run it on anything else yet, so I'm not sure how well that
goes. I will be getting this up on the Sun eventually. In 'trlib.c', the
library routines, there are many Apollo calls, which I haven't put into
ifdef's. However to run elsewhere, you'll probably need to rewrite these
anyway.

There are 3 input files: 
   acglimits.data has window sizes, labels, spacing of nodes & the name
      of the files containing node and port data
   acgnode.data in this eg. has the node data
   acgport.data in this eg. has the port data

I hope the code is readable & understandable. It works as is. 
To run type: tr
On the Apollo you can scroll using the regular boxed arrow keys & type q to 
quit. If you make any enhancements, please post them or let me know.

acgnode.data format:
   node# . 0 . children node#'s . 0 . name of node . level . nth on that lvl . total # on that lvl . BOX or CIRCLE .

acgport.data format:
   port name . from node# . to node# .

-----Cut Here-----Cut Here-----Cut Here-----Cut Here-----
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	acgnode.data
#	acgport.data
#	acglimits.data
#	maketr
#	tr.incl.h
#	tr.c
#	trlib.c
sed 's/^X//' << 'SHAR_EOF' > acgnode.data
X500 . 0 . 400 402 . 0 . the event is valid [false high] . 1 . 1 . 1 . BOX .
X400 . 0 . 705 . 0 . mission processing is credible [true high] . 7 . 1 . 2 . BOX .
X402 . 0 . 700 . 0 . the technical evaluation of the event is good [false high] . 2 . 1 . 1 . BOX .
X705 . 0 . 0 . 0 . system and display are normal [true high] . 5 . 1 . 1 . BOX .
X700 . 0 . 410 424 . 0 . the pattern is a good signature [false high] . 3 . 1 . 1 . BOX .
X410 . 0 . 707 . 0 . attitude is the only suspected system error [false high] . 6 . 1 . 2 . BOX .
X424 . 0 . 451 120 . 0 . there are enough clean points in the event [false high] . 4 . 1 . 1 . BOX .
X707 . 0 . 705 . 0 . defaults are set for identified anomalies which affect processing [true high] . 7 . 2 . 2 . BOX .
X451 . 0 . 707 . 0 . some of the points in the event are lost [false high] . 6 . 2 . 2 . BOX .
X120 . 0 . 118 . 0 . event is free of transient noise bursts [false high] . 8 . 1 . 1 . BOX .
X118 . 0 . 0 . 0 . number of points passing transient noise test [2] . 9 . 1 . 1 . BOX .
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > acgport.data
Xrule250 . 400 . 500 .
Xrule250 . 402 . 500 .
Xrule490 . 705 . 400 .
Xrule320 . 700 . 402 .
Xrule450 . 410 . 700 .
Xrule450 . 424 . 700 .
Xrule412 . 707 . 410 .
Xrule470 . 451 . 424 .
Xrule470 . 120 . 424 .
Xrule447 . 705 . 707 .
Xrule706 . 707 . 451 .
Xrule131 . 118 . 120 .
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > acglimits.data
X,GRAPH_TITLE Dynamic Logic Trace
X,NODE_FILE acgnode.data
X,PORT_FILE acgport.data
X,BOX_HALF_WIDTH 45 
X,BOX_HALF_HEIGHT 25
X,RADIUS 30
X,NODE_LABEL 1	
X,MAXX_WINDOW 1000
X,MAXY_WINDOW 750
X,MINX_WINDOW 0
X,MINY_WINDOW 0
X,MIN_X_SPACE 30
X,MIN_Y_SPACE 40
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > maketr
Xtr: tr.o trlib.o tr.incl.h
X	cc -o tr tr.o trlib.o
Xtr.o: tr.c trlib.o tr.incl.h
X	cc tr.c trlib.o -c -g -DAPOLLO
Xtrlib.o: trlib.c tr.incl.h
X	cc trlib.c -c -DAPOLLO
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > tr.incl.h
X#include <stdio.h>
X#include <ctype.h>
X#include <math.h>
X#ifdef APOLLO
X#include </sys/ins/base.ins.c>
X#include </sys/ins/pad.ins.c>
X#include </sys/ins/gpr.ins.c>
X#include </sys/ins/kbd.ins.c>
X#endif APOLLO
X
X#define MAXNODE 300
X#define MAXPORT 600
X#define	PI 3.1415926536
X
X#define TOP 1
X#define RHS 2
X#define BOT 3
X#define LHS 4
X#define MID 5
X
X#ifdef APOLLO
Xgpr_$keyset_t buttons = {0,0,0,0};
Xgpr_$keyset_t btn     = {0,0,0,0};
Xgpr_$keyset_t keys     = {0,0,0,0};
Xchar keystroke;
X
Xgpr_$bitmap_desc_t mem_bm,init_bitmap;
Xgpr_$event_t event_type;
X
Xstatus_$t st;
X#endif APOLLO
X
Xchar GRAPH_TITLE[128],NODE_FILE[128],PORT_FILE[128];
X
Xfloat mnx,mny,scl;
Xfloat xx[1000], yy[1000];
Xfloat char_size, char_sz_sml; /* char_size is half character width */
X
Xshort x[1000],y[1000]; 
Xshort len, font_id, font_id_small;
Xshort delta_x, delta_y; /* distance between nodes */
Xshort char_width;
X
Xint node_max, port_max;
Xint char_height_text,char_width_text;
Xint MAXX_WINDOW[5], MAXY_WINDOW[5], MINX_WINDOW[5], MINY_WINDOW[5], 
X	MIN_Y_SPACE, MIN_X_SPACE,BOXWIDTH,BOXHT,RADIUS,NODE_LABEL;
X
Xstruct node_data
X	{
X	int node;
X	char name[128];
X	int target[5];
X	int source[40];
X	struct node_data *src[40];
X	int cohort[5];
X	int lvl;	/* level of node 0=highest at top of graph 
X					1=lower 
X					... 
X					n=lowest at bottom of graph */
X	int nth;	/* order 1=first 2=second ... */
X	int of;		/* total # on this lvl */
X	short x0;	/* x0.y0 - center of node */
X	short y0;
X	int shape;	/* 0=circle 1=box 2=rect ... */
X	short wide;	/* size of shape in x - radius or width */
X	short ht;		/* size of shape in y - radius or height */
X/*	int inport[20]; */	/* ports into this node */
X/*	int outport[20]; */ /* ports leaving this node */
X	int pop;        /* parent position */
X	int flg; 	/* flag whether to write name or blank it out */
X	} nodes[MAXNODE];
X
Xstruct node_data *nod[MAXNODE],*tmpnod;
X
Xstruct port_data
X	{
X	char name[128];
X	int from_node;	/* node# of source */
X	int to_node;	/* node# of target */
X	int nth_from;	/* nodes[nth_from].node = ports[].from_node */
X	int nth_to;	/* nodes[nth_to].node = ports[].to_node */
X	short x0;	/* x0,y0 - from port location */
X	short y0;
X	short xf;	/* xf,yf - to port location */
X	short yf;
X	int side_from;	/* TOP RHS BOT LHS MID */
X	int side_to;
X	int angle;	/* angle of arrow from side, +angle=cw -angle=ccw */
X	} ports[MAXPORT];
X
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > tr.c
X#include "tr.incl.h"
X/*
X	tidy tree drawing routines expanded from :
XTidy Drawing of Trees by C. Wetherell & A. Shannon IEEE Vol. SE-5 #5
X    Sept 79 pp. 514-520
XTidier Drawings of Trees by E. Reingold & J. Tilford IEEE Vol. SE-7 #2
X    Mar '81 pp. 223-228
X
X	program written by April Gillam (gillam@aerospace.arpa)
X			   Aerospace Corp.
X			   2350 El Segundo Blvd. M1-102
X			   El Segundo, Ca. 90245
X*/
Xint tidy_tree(),text(),check(),setbutton(),clearbutton(),closest(),edge(),
X	circle(),set_node_locs(),prnt_in_box(),
X	where_port(),set_port(),rectangle(),arrow2(),init();
Xint askacg,lvlstart[20];
X
Xmain(argc,argv)
Xchar *argv[];
Xint argc;
X{
Xint loop, j;
Xshort position[2];
X#ifdef APOLLO
Xgpr_$window_t src_wind;
Xgpr_$position_t dest_orig,cursor_pos;
X#endif APOLLO
X
X	read_limits();
X
X	init();		 /* initialize graphics */
X
X/*
X		askacg == 1 ask user for acgnode.data & acgport.data file names
X			< 0 use small boxes & don't print words in box
X*/
X	if(argc > 1) askacg = atoi(argv[1]);
X	else askacg = 0;
X	if(askacg < 0){
X		BOXWIDTH = 20; BOXHT = 15; RADIUS = 15; MIN_X_SPACE = 10;
X		MIN_Y_SPACE = 10;
X		printf("enter BOXWIDTH BOXHT: ");
X		scanf("%d %d",&BOXWIDTH,&BOXHT);
X		}
X
X	node_max = read_node_data(askacg);
X
X	len = node_max;
X
X	tidy_tree(node_max);
X
X	port_max = read_port_data(askacg);
X
X#ifdef APOLLO
X	gpr_$set_bitmap(mem_bm,st); check(st);
X
X	set_node_locs();
X
X	src_wind.x_coord = (short)0;
X	src_wind.y_coord = (short)0;
X	src_wind.x_size = MAXX_WINDOW[0] - MINX_WINDOW[0];
X	src_wind.y_size = MAXY_WINDOW[0] - MINY_WINDOW[0];
X
X	dest_orig.x_coord = (short) 0;
X	dest_orig.y_coord = (short) 0;
X
X	gpr_$set_bitmap(init_bitmap,st); check(st);
Xgpr_$acquire_display(st); check(st);
X	gpr_$pixel_blt(mem_bm,src_wind,dest_orig,st); check(st); 
X	gpr_$set_cursor_active(true,st); check(st);
X	for(loop=1;loop;) {
X		for(loop=1;loop==1;){
X			gpr_$event_wait(event_type,keystroke,position,st);check(st);
X			switch(event_type){
X			    case  gpr_$locator:  gpr_$set_cursor_position(position,st);break;
X			    case  gpr_$keystroke: 
X/*			    case  gpr_$buttons: */
X			    switch(255&keystroke){
X/*			      case 'c': closest(position);loop=2;break;
X			      case 'b': edge(position);   loop=2;break;
X*/
X		case 'q':	loop=0;break;
X              case KBD_$L4: 
X			src_wind.x_coord = (short) 0;
X			cursor_pos.x_coord = 0;
X			cursor_pos.y_coord = position[1];
X			gpr_$set_cursor_position(cursor_pos,st);check(st);
X			break;
X              case KBD_$L6: 
X			src_wind.x_coord = MAXX_WINDOW[1] - src_wind.x_size;
X			cursor_pos.x_coord = src_wind.x_coord;
X			cursor_pos.y_coord = position[1];
X			gpr_$set_cursor_position(cursor_pos,st);check(st);
X			break;
X		case KBD_$L7: 
X			src_wind.x_coord = src_wind.x_coord - 100;
X			if(src_wind.x_coord < 0)
X				src_wind.x_coord = 0;
X			break;
X              case KBD_$L8S:
X			src_wind.y_coord = src_wind.y_coord - 10;
X			if(src_wind.y_coord < 0)
X				src_wind.y_coord = 0;
X			break;
X              case KBD_$L9:
X			src_wind.x_coord = src_wind.x_coord + 100;
X			if(src_wind.x_coord > (MAXX_WINDOW[1] - src_wind.x_size))
X				src_wind.x_coord = MAXX_WINDOW[1] - src_wind.x_size;
X			break;
X              case KBD_$LAS:
X			src_wind.x_coord = src_wind.x_coord - 10;
X			if(src_wind.x_coord < 0)
X				src_wind.x_coord = 0;
X			break;
X              case KBD_$LCS:
X			src_wind.x_coord = src_wind.x_coord + 10;
X			if(src_wind.x_coord > (MAXX_WINDOW[1] - src_wind.x_size))
X				src_wind.x_coord = MAXX_WINDOW[1] - src_wind.x_size;
X			break;
X              case KBD_$LD:
X			src_wind.y_coord = src_wind.y_coord - 100;
X			if(src_wind.y_coord < 0)
X				src_wind.y_coord = 0;
X			break;
X              case KBD_$LES:
X			src_wind.y_coord = src_wind.y_coord + 10;
X			if(src_wind.y_coord > MAXY_WINDOW[1] - src_wind.y_size)
X				src_wind.y_coord = MAXY_WINDOW[1] - src_wind.y_size;
X			break;
X              case KBD_$LF:
X			src_wind.y_coord = src_wind.y_coord + 100;
X			if(src_wind.y_coord > MAXY_WINDOW[1] - src_wind.y_size)
X				src_wind.y_coord = MAXY_WINDOW[1] - src_wind.y_size;
X			break;
X		default:
X			break;
X			      } /* switch keystroke */
X		gpr_$pixel_blt(mem_bm,src_wind,dest_orig,st); check(st); 
X			break;
X			    } /* switch event_type */
X			} /* loop */
X		} /* loop */
X
Xgpr_$release_display(st);check(st);
X	gpr_$terminate(true,st);check(st);
X#endif APOLLO
X}
X/* ************************************************************ */
X/*	nmax = # of nodes
X*/
Xtidy_tree(nmax)
Xint nmax;
X{
Xint j,k,m,nlvl,lvl,oldlvl,maxlvl,xdelta,ydelta,pop,jth,j0,jf;
Xint n_per_lvl[20],next_pos,xmin,xmax,xchild,xparent,mostx,cnt,l,jj;
X
Xxdelta=120; ydelta = 100;
Xfor(j=0;j<20;j++)
X	n_per_lvl[j]=0;
Xmaxlvl = 0;
Xfor(j=0;j<nmax;j++){
X	nod[j] = &nodes[j];
X	if(nodes[j].lvl > maxlvl) maxlvl = nodes[j].lvl;
X	}
X/*
X	set lvls
X*/
X/* find top assertion assuming there is only one at the top */
Xfor(j=0;j<nmax;j++){
X	if(nodes[j].lvl > 1) nodes[j].lvl = 0;
X	nodes[j].pop = 0;
X	}
Xfor(lvl = 1; lvl <= maxlvl; lvl++) {
X	for(j = 0; j<nmax; j++){
X		if(nodes[j].lvl != lvl) continue;
X		k = 0;
X		while(nodes[j].source[k] != 0){
X			for(m=0; m<nmax; m++)
X				if(nodes[m].node == nodes[j].source[k]) {
X					nodes[m].lvl = lvl + 1;
X					break;
X					}
X			k++;
X			}
X		}
X	}
X/* 
X	sort by lvl 
X*/
Xfor(k=0;k<nmax-1;k++){
Xfor(j=k;j<nmax-1;j++){
X	if(nod[j]->lvl > nod[j+1]->lvl){
X		tmpnod = nod[j];
X		nod[j] = nod[j+1];
X		nod[j+1] = tmpnod;
X		}
X        }
X	}
X/*
X	find where each lvl begins
X*/
Xfor(j=1; j<=maxlvl; j++) lvlstart[j] = nmax;
Xlvl = 1; 
Xfor(j=0; j<nmax; j++){
X	lvl = nod[j]->lvl;
X	if(j < lvlstart[lvl]) lvlstart[lvl] = j;
X	}
Xwhile(lvlstart[maxlvl] == nmax) maxlvl--;
X/*
X	set src ptr to nod source
X*/
Xfor(j=nmax-1;j>=0;j--){
X	m = 0;
X        if(nod[j]->source[0] == 0) nod[j]->src[0] = NULL;
X	else while(nod[j]->source[m] != 0){
X		for(k=j+1; k<nmax;k++)
X			if(nod[k]->node == nod[j]->source[m]){
X          			nod[j]->src[m] = nod[k];
X				break;
X          			}
X		m++;
X		}
X	}
Xfor(j=0;j<nmax;j++){
X	lvl = nodes[j].lvl;
X	++n_per_lvl[lvl];
X	}
X/*
X	make sure children of same parents are next to each other
X	pop only keeps track of one of parents, what if multiple parents???
X*/
Xfor(lvl = 1; lvl < maxlvl; lvl++) {
X	pop = 0;
X	printf("lvl=%d: ",lvl);
X	j0 = lvlstart[lvl]; jf = lvlstart[lvl+1];
X	for(j=j0; j<jf; j++){
X		if(nod[j]->source[0] == 0) continue;
X		pop++;
X		m = 0;
X		while(nod[j]->source[m] != 0){
X			(nod[j]->src[m])->pop = pop;
X			printf("src[%d]pop(%d) ",nod[j]->source[m],pop);
X			m++;
X      			}
X		}
Xprintf("\n");
X/* for each lvl exchange children positions so siblings are together */
X	j0 = lvlstart[lvl+1]; jf = lvlstart[lvl+2];
X	for(jj=j0; jj<jf-1; jj++){
X		if(nod[jj]->pop == 0) continue;
X		k = jj;
X		while(k+1 < nmax && nod[++k]->lvl == lvl+1){
X			if(nod[jj]->pop <= nod[k]->pop) continue;
X			tmpnod = nod[jj];
X			nod[jj] = nod[k];
X			nod[k] = tmpnod;
X			}
X		}
Xprintf("\t\t");
X	for(jj = j0; jj < jf; jj++)
X		printf("%d(%d) ",nod[jj]->node,nod[jj]->pop);
X	printf("\n");
X	}
X/* x spacing - initial values */
Xfor(lvl = 1; lvl <= maxlvl; lvl++) {
X	printf("lvl %d) ",lvl);
X	next_pos = xdelta;
X	j0 = lvlstart[lvl]; jf = lvlstart[lvl+1];
X	for(j = j0; j<jf; j++){
X		nod[j]->x0 = next_pos;
X		next_pos = next_pos + xdelta;		
Xprintf("%d<%d> ",nod[j]->node,nod[j]->x0);
X		}
Xprintf("\n");
X	}
X/*
X	assign ycoord
X*/
X	ydelta = (MAXY_WINDOW[0] - MINY_WINDOW[0]) / maxlvl;
X	if(ydelta < 3.5*BOXHT)
X		ydelta = 3.5*BOXHT;
X	if(ydelta > (MAXY_WINDOW[1] - MINY_WINDOW[1]) / maxlvl)
X		ydelta = (MAXY_WINDOW[1] - MINY_WINDOW[1]) / maxlvl; 
Xfor(j=0;j<nmax;j++){
X	lvl = nodes[j].lvl;
X	nodes[j].y0 = lvl * ydelta;
X	}
X/*
X	 algorithm to assign xcoord
X*/
X		x_space(maxlvl,nmax,xdelta);
X
Xprintf("node    x0    y0  lvl\n");
Xfor(j=0;j<nmax;j++){
X	printf("%d  %5d  %5d  %3d\n",nod[j]->node,nod[j]->x0,nod[j]->y0,nod[j]->lvl);
X	}
X
X}
X/* ************************************************************ */
Xx_space(maxlvl,nmax,xdelta)
Xint maxlvl,nmax,xdelta;
X{
Xint m,oldlvl,next_pos,jj,j,k,imin,xmin,xmax,lvl,new,xshift,j0,jf;
X/*
X	 algorithm to assign xcoord
X*/
Xnext_pos=xdelta;
Xm=0; oldlvl=maxlvl; 
Xfor(j=nmax-1;j>=0;j--){
X	lvl = nod[j]->lvl;	
X	if(lvl == oldlvl) m++;
X	else{
X/*	make sure nodes are not on top of one another */
X		next_pos=xdelta;
X		j0 = lvlstart[oldlvl]; jf = lvlstart[oldlvl+1];
X		for(jj = jf-1; jj>=j0; jj--){
X			for(k = jf-1; k >=j0; k--){
X				if(k == jj) continue;
X				if(abs(nod[jj]->x0 - nod[k]->x0) < xdelta)
X					nod[k]->x0 = nod[jj]->x0 + xdelta;
X				}
X			}
X		m=1;
X		oldlvl = lvl;
X		} /* else */
X/* leaf */
X	if(nod[j]->source[0] == 0){
X		nod[j]->x0 = next_pos;
X		same_spot(xdelta,nmax,lvl,nod[j]->x0,nod[j]->node);
X		next_pos = next_pos+xdelta;
X		}
X/* children */
X	else{
X		k = 0; xmax=0; xmin=4096;
X		while(nod[j]->source[k] != 0){
X			if(xmin > (nod[j]->src[k])->x0)
X				xmin = (nod[j]->src[k])->x0;
X			if(xmax < (nod[j]->src[k])->x0)
X				xmax = (nod[j]->src[k])->x0;
X			k++;
X			}
X		new = (xmax+xmin)/2;
X		if(new < next_pos) new = next_pos;
X		nod[j]->x0 = new;
X		same_spot(xdelta,nmax,lvl,nod[j]->x0,nod[j]->node);
X		next_pos = new + xdelta;
X		} /* else */
X	} /* for j=nmax-1 */
Xprintf("Phase I\n");
Xfor(lvl = 1; lvl <= maxlvl; lvl++) {
X	printf("lvl %d) ",lvl);
X	j0 = lvlstart[lvl]; jf = lvlstart[lvl+1];
X	for(j = j0; j<jf; j++)
X		printf("%d<%d> ",nod[j]->node,nod[j]->x0);
X	printf("\n");
X	}
X/* put children beneath parents:
X	one parent
X		leaf:	directly beneath
X		bros:	equidistant
X	multi parents
X		leaf:	equidistant
X		bros:	???
X*/
X/* lvl 1 only has 1 node, so don't need to check if one
X	node obscures another
X*/
Xfor(j=1;j<nmax;j++)
X	same_spot(xdelta,nmax,nod[j]->lvl,nod[j]->x0,nod[j]->node);
X
Xfor(lvl=1; lvl<maxlvl; lvl++){
X	j0 = lvlstart[lvl]; jf = lvlstart[lvl+1];
X	for(j = jf-1; j>=j0; j--){
X		m = 0; k = 0;
X		while(nod[j]->source[k++] != 0) m++;
X/* one parent leaf */
X		if(m == 1){
X			xshift = nod[j]->x0 - nod[j]->src[0]->x0;
X			if(xshift > xdelta/2) {
X				printf("parent %d at %d xshift=%d xdelta=%d\n",nod[j]->node,nod[j]->x0,xshift,xdelta);
X				printf("\tchild=%d at %d\n",nod[j]->source[0],nod[j]->src[0]->x0);
X				shift_rt(nod[j]->src[0]->lvl,nod[j]->source[0],xshift,nmax,xdelta);
X				}
X	        } /* m = 1 */
X/* one parent >1 child */
X	else if(m > 1){
X		k = 0; xmax=0; xmin=4096;
X		while(nod[j]->source[k] != 0){
X			if(xmin > (nod[j]->src[k])->x0) {
X				xmin = (nod[j]->src[k])->x0; imin = k; }
X			if(xmax < (nod[j]->src[k])->x0)
X				xmax = (nod[j]->src[k])->x0;
X			k++;
X			}
X		if((nod[j]->x0 - xmin) - (xmax - nod[j]->x0) <= xdelta) continue;
X		xshift = nod[j]->x0 - (xmax+xmin)/2;
X		if(xshift > xdelta/2) {
X			printf("parent %d at %d xshift=%d xdelta=%d\n",nod[j]->node,nod[j]->x0,xshift,xdelta);
X			for(jj=0;jj<m;jj++)
X			printf("\tchildren=%d at %d\n",nod[j]->source[jj],nod[j]->src[jj]->x0);
X			shift_rt(nod[j]->src[0]->lvl,nod[j]->source[imin],xshift,nmax,xdelta);
X			}
X		} /* else m>1 */
X		} /* for j=jf-1 ... */
X
X	} /* lvl */
X}
X/* ************************************************************ */
X/* shift lvl nodes, starting with nbro, to the right by xshift
X*/
Xshift_rt(lvl,nbro,xshift,nmax,xdelta)
Xint lvl,nbro,xshift,nmax,xdelta;
X{
Xint i,flag,old_x,j0,jf,xdif;
X	flag = 0;
X	printf("lvl=%d\n",lvl);
X	j0 = lvlstart[lvl]; jf = lvlstart[lvl+1];
X	for(i=j0; i<jf; i++) 
X		if(nod[i]->node == nbro) old_x = nod[i]->x0;
X
X	for(i=j0; i<jf; i++){
X		printf("\t(%d)x=%d->",nod[i]->node,nod[i]->x0);
X		if(nod[i]->x0 >= old_x) nod[i]->x0 += xshift;
X		printf("%d\n",nod[i]->x0);
X		}
X	old_x += xshift;
X	for(i=j0; i<jf; i++){
X		if(nod[i]->node == nbro) continue;
X		if(abs(xdif = nod[i]->x0 - old_x) < xdelta) {
X			printf("\t**** %d is at %d oops! (%d at %d)\n",nod[i]->node,nod[i]->x0,nbro,old_x);
X			
X			}
X		}
X}
X/* ************************************************************ */
X/*	
X		check that node=nmbr at xspot is the only one there
X*/
Xsame_spot(xdelta,nmax,lvl,xspot,nmbr)
Xint xdelta,nmax,lvl,xspot,nmbr;
X{
Xint jj,k,xshift,j0,jf,j;
X	j0 = lvlstart[lvl]; jf = lvlstart[lvl+1];
X	for(j=j0; j<jf; j++){
X		if(nod[j]->node == nmbr) continue;
X		if(abs(nod[j]->x0 - xspot) >= xdelta) continue;
X
X		xshift = nod[j]->x0 - xspot;
X
X		if(xshift < xdelta && xshift > 0)
X			shift_rt(lvl,nod[j]->node,xdelta-xshift,nmax,xdelta);
X
X		else if(abs((double)xshift) < xdelta && xshift < 0)
X			shift_rt(lvl,nod[k]->node,xdelta+xshift,nmax);
X		}
X}
X/* 
X************************* node locations ********************************
X*/
Xset_node_locs()
X{
Xint nlvl, i, j, k, n_per_lvl[20], label_node;
Xchar str[128];
Xshort del_x,del_y,xcenter, ycenter;
Xshort xpt, ypt,xxx[5],yyy[5],xmid,ymid;
Xshort box_w,box_h,box_w_fr,box_w_to;
X	/* read from acglimits.data file */
Xshort MIN_BOX_W,MIN_BOX_H,MAX_BOX_W,MAX_BOX_H; 
X
XFILE *datafile, *fopen();
Xint nn,nx,ny;
X	MIN_BOX_W = 20;
X	MIN_BOX_H = 20;
X	MAX_BOX_W = 120;
X	MAX_BOX_H = 120;
X
X	for(j = 0; j < 20; j++) n_per_lvl[j] = 0;
X	nlvl = 0;
X	for(i = 0; i < node_max; i++){
X		j = nodes[i].lvl;
X		if(j > nlvl) nlvl = j;
X		n_per_lvl[j]++;
X		}
X	nlvl++;
X	label_node = NODE_LABEL;
X/* y spacing */
X	del_y = (MAXY_WINDOW[0] - MINY_WINDOW[0]) / nlvl;
X/* assumes have a box & not a circle -- fix so handles circles too */
X	if( del_y < 2*BOXHT) 
X		del_y = (MAXY_WINDOW[1] - MINY_WINDOW[1]) / nlvl;
X/* title for graph */
X/* fix so center of memory_bm ??? */
X/*	xpt = (MAXX_WINDOW[0] - MINX_WINDOW[0]) / 2 - char_size*strlen(GRAPH_TITLE); */
X/* assuming only one node at the top */
X	xpt = nodes[0].x0 - char_size*strlen(GRAPH_TITLE);
X	ypt = del_y/3;
X	text(xpt,ypt,GRAPH_TITLE);
X/* x spacing */
X	for(i = 1; i < nlvl; i++){	 /* # of levels */
X		for(k = 0; k < node_max; k++){ /* actual node data */
X			if(nodes[k].lvl == i) {/* weed out those not on this lvl */
X
X		del_x = (MAXX_WINDOW[0] - MINX_WINDOW[0]) / (n_per_lvl[i] + 1); 
X/* assumes have a box & not a circle -- fix so handles circles too */
X		if( del_x < 2*BOXWIDTH)
X			del_x = (MAXX_WINDOW[1] - MINX_WINDOW[1]) / (n_per_lvl[i] + 1); 
X/*		if(RADIUS >= del_x && BOXWIDTH >= del_x){ */
X		if(nodes[k].shape == 0) { box_h = 2*RADIUS;
X                        box_w = 2*RADIUS; }
X		else { box_h = 2*BOXHT;
X                        box_w = 2*BOXWIDTH; }
X		if(box_h > MAX_BOX_H) box_h = MAX_BOX_H;
X		if(box_h < MIN_BOX_H){
X			printf("Box height( = %ld) is too small\n\tEither increase window size in acglimits.data\n\tor decrease the # of node levels!\n",(long) box_h);
X			exit(0);
X			}
X		if(box_w > MAX_BOX_W) box_w = MAX_BOX_W;
X		if(box_w < MIN_BOX_W){
X			printf("Box width( = %ld) is too small\n\tEither increase window size in acgdefs\n\tor decrease the # of node levels!\n",(long) box_w);
X			exit(0);
X			}
X/*		} */ /* comparison to del_x */
X/*		else {
X			box_w = del_x;
X			box_h = 0.5*del_y;
X			}
X*/
X				nodes[k].wide = box_w;
X				nodes[k].ht = box_h;
X/* from tidy_tree */
X				xcenter = nodes[k].x0;
X				ycenter = nodes[k].y0;
X/* offset should be set here
X	xcenter += xoffset;
X*/
X
X/* shape 0 = circle 1 = box */
X			if(nodes[k].shape == 0) circle(xcenter,ycenter,RADIUS);
X			else rectangle(xcenter,ycenter,box_w,box_h);
X
X/* print node# */
X#ifdef APOLLO
X	gpr_$set_text_font(font_id,st);check(st);
X#endif APOLLO
X		sprintf(str,"%d",nodes[k].node);
X		xpt = xcenter - char_size*strlen(str);
X		ypt = ycenter - nodes[k].ht/2 + 2*char_size;
X		text(xpt, ypt,str);
X
X#ifdef APOLLO
X      gpr_$set_text_font(font_id_small,st);check(st);
X#endif APOLLO
X/* print name */
X        ypt = ycenter - nodes[k].ht/2 + 4*char_size;
X	if(label_node == 1 && askacg >= 0)
X		prnt_in_box(k,xcenter,ypt,del_x);
X
X        	} /* nodes.lvl ok */
X	} /* for k < node_max */   
X	} /* for nlvl */
X/* arrows */
X	where_port(port_max);
X	for(i = 0; i < port_max; i++){
X		box_w_fr = nodes[ports[i].nth_from].wide;
X		box_w_to = nodes[ports[i].nth_to].wide;
X                if(ports[i].side_from == BOT && ports[i].side_to == BOT) {
X	                xmid = ports[i].x0 + (ports[i].xf - ports[i].x0)/2;
X        	        ymid = ports[i].y0 + (del_y - box_h)/4;
X#ifdef APOLLO
X		      gpr_$set_text_font(font_id,st);check(st);
X			arrow(ports[i].x0,ports[i].y0,ports[i].xf,ports[i].yf,xmid,ymid,ports[i].name);
X		      gpr_$set_text_font(font_id_small,st);check(st);
X#endif APOLLO
X			} 
X		else {
X#ifdef APOLLO
X		      gpr_$set_text_font(font_id,st);check(st);
X		      arrow(ports[i].x0,ports[i].y0,ports[i].xf,ports[i].yf,0,0,ports[i].name);
X		      gpr_$set_text_font(font_id_small,st);check(st);
X#endif APOLLO
X                	}
X
X		}
X}
X/* ************************** set port sides *********************** */
Xwhere_port(pmax)
Xint pmax;
X{
Xint i, j, port_n, n, nthfr, nthto, jfr, jto;
X/*
X
X   set from_node & to_node values to the subscript, nth, for nodes[nth]
X	this is slow & cumbersome - find a faster way
X
X   set x0,y0 xf,yf 
X*/		
X	for(i = 0; i < pmax; i++){	/* ports */
X		nthfr = -1;
X		nthto = -1;
X		for(j = 0; j < node_max; j++){	/* nodes */
X			if(ports[i].from_node == nodes[j].node)	{
X				nthfr = ports[i].nth_from = j;
X				}
X			if(ports[i].to_node == nodes[j].node) {
X				nthto = ports[i].nth_to = j;
X				}
X			} /* nodes */
Xif(nthfr < 0 || nthto < 0) printf("read_port_data: %d to %d missing node\n",
X	ports[i].from_node,ports[i].to_node);
X/*
X	check level to decide where to put port 
X*/
X		if(nodes[nthfr].lvl > nodes[nthto].lvl) {
X			jfr = ports[i].side_from = TOP;
X			jto = ports[i].side_to = BOT; 
X			}
X		else if(nodes[nthfr].lvl < nodes[nthto].lvl) {
X			jfr = ports[i].side_from = BOT;
X			jto = ports[i].side_to = TOP; }
X		else { /* same lvl so see if next to each other */
X			if(nodes[nthfr].nth == nodes[nthto].nth + 1) {
X				jfr = ports[i].side_from = LHS;
X				jto = ports[i].side_to = RHS;
X				}
X			else if(nodes[nthfr].nth == nodes[nthto].nth - 1) {
X				jfr = ports[i].side_from = RHS;
X				jto = ports[i].side_to = LHS;
X				}
X			else { /* not next to each other -- oh no! */
X/*
X	don't know how to deal with if not next to each other - do later
X		for now put port on bottom
X*/
X				jfr = ports[i].side_from = BOT;
X				jto = ports[i].side_to = BOT;
X				}
X			} /* else same lvl */
X/*
X	now find coordinates of the port 
X*/
X		set_port(jfr,nthfr,jto,nthto,i);
X
X /* later decide on angle depending on # of ports & other criteria */
X		ports[nthto].angle = 0;
X		} /* for i -- ports */
X}
X/* ************************** set port coordinates *********************** */
X/*
X	nside1,nthnode1 - from
X	     2        2 - to
X*/
Xset_port(nside1,nthnode1,nside2,nthnode2,ithport)
Xint nside1,nthnode1,nside2,nthnode2,ithport;
X{
Xint i,nside,nthnode;
Xshort xfr,yfr;
X/*
X	now find coordinates of the port 
X*/
X	for(i=0; i < 2; i++){ /* i=0:from  i=1:to */
X		if(i == 0){
X			nside = nside1;
X			nthnode = nthnode1;
X			}
X		else{
X			nside = nside2;
X                        nthnode = nthnode2;
X			}
X		if(nside == TOP){
X			xfr = 0;
X			yfr = -nodes[nthnode].ht/2;
X			}
X		else if(nside == BOT){
X			xfr = 0;
X			yfr = nodes[nthnode].ht/2;
X			}                
X		else if(nside == RHS){
X                        xfr = nodes[nthnode].wide/2;
X			yfr = 0;
X			}
X		else {	/* LHS */
X                        xfr = -nodes[nthnode].wide/2;
X			yfr = 0;
X			}
X		if(i == 0) {
X			ports[ithport].x0 = nodes[nthnode].x0 + xfr;
X			ports[ithport].y0 = nodes[nthnode].y0 + yfr;
X/* clipping */
X/*			if(ports[ithport].x0 < MINX_WINDOW) 
X				ports[ithport].x0 = MINX_WINDOW;
X			else if(ports[ithport].x0 > MAXX_WINDOW)
X				ports[ithport].x0 = MAXX_WINDOW;
X			if(ports[ithport].y0 < MINY_WINDOW) 
X				ports[ithport].y0 = MINY_WINDOW;
X			else if(ports[ithport].y0 > MAXY_WINDOW)
X				ports[ithport].y0 = MAXY_WINDOW;
X*/
X                        }
X		else {
X			ports[ithport].xf = nodes[nthnode].x0 + xfr;
X			ports[ithport].yf = nodes[nthnode].y0 + yfr;
X/* clipping */
X/*			if(ports[ithport].xf < MINX_WINDOW) 
X				ports[ithport].xf = MINX_WINDOW;
X			else if(ports[ithport].xf > MAXX_WINDOW)
X				ports[ithport].xf = MAXX_WINDOW;
X			if(ports[ithport].yf < MINY_WINDOW) 
X				ports[ithport].yf = MINY_WINDOW;
X			else if(ports[ithport].yf > MAXY_WINDOW)
X				ports[ithport].yf = MAXY_WINDOW;
X*/
X			}
X		}
X}
X
SHAR_EOF
sed 's/^X//' << 'SHAR_EOF' > trlib.c
X#include "tr.incl.h"
X/*
Xprnt_in_box(k,xcenter,ycenter,del_x)
Xrectangle(xcenter,ycenter,horiz_side_length,vert_side_length)
Xarrow2(x0,y0,xf,yf,xmid,ymid,label)
Xinit()
Xtext(x0,y0,titles)
Xcheck(st)
Xsetbutton(c,buttons)
Xclearbutton(buttons)
Xclosest(position)
Xedge(position)
Xcircle(xcenter,ycenter,radius)
Xarrow(x0,y0,xf,yf,xmid,ymid,label)
Xint read_port_data(askacg)
Xint read_node_data(askacg)
Xint read_limits()
X*/
X/* ************************************************************ */
Xprnt_in_box(k,xcenter,ycenter,del_x)
Xint k;
Xshort xcenter,ycenter,del_x;
X{
Xchar str[128],strip[128];
Xshort xpt,ypt,xspacing;
Xint nchars,j,j0,line,nlines,l,i,iflg;
Xfloat frac_nlines,length;
X/* print name */
X		sprintf(str,"%s",nodes[k].name);
X		length = strlen(str) - 1;
X                i = 0;
X                while(i< length && ispunct(str[i]) == 0) i++; 
X/*                while(i< length && strncmp(str[i],"[",1) != 0) i++; */
X		nchars = nodes[k].wide/char_width_text; 
X                frac_nlines = i/nchars;
X                nlines = frac_nlines;
X                if (nlines < frac_nlines)
X                   nlines++;
X                if(i < length) nlines++;
X                iflg = 0;
X		line = 0;
X		j0 = 0;
X/*	while(j0 < length){ */
X	while(j0 < i){
X		for(l=0;l<nchars+1;l++) strip[l] = '\0';
X/*		if(j0+nchars > length) nchars = length - j0; */
X		if(j0+nchars > i) nchars = i - j0;
X		for(j=j0;j<j0+nchars; j++)
X			strip[j-j0] = str[j];
X                xpt = xcenter - (char_width_text*strlen(strip) - 2)/2;    
X                if(iflg == 1) line++;
X		if (nodes[k].node < 100)
X                 ypt = ycenter + char_height_text/2 - ((nlines-1)*char_height_text)/2 + line*char_height_text; 
X                else
X                 ypt = ycenter + line*char_height_text;
X		if(xpt < 0) xpt = 20;
X		if(xpt > MAXX_WINDOW[1])
X			printf("xpt = %ld is out of window\nCannot print: %s\n",(long) xpt,str);
X		else {
X		      gpr_$move(xpt,ypt,st);check(st);
X		      gpr_$text(strip,(short) nchars,st);check(st);
X			}
X		line++;
X		j0 += nchars;
X                if(j0 >= i){ 
X			nchars = length - i;
X			i = length;
X                        iflg = 1;
X                        }
X		} /* while */
X}
X/* ************************************************************ */
Xrectangle(xcenter,ycenter,horiz_side_length,vert_side_length)
Xshort xcenter, ycenter, horiz_side_length,vert_side_length;
X{
Xshort lenx, leny, xxx[10],yyy[10];
X	lenx = horiz_side_length;
X	leny = vert_side_length;
X
X	xxx[3] = xxx[4] = xxx[0] = xcenter - lenx/2;
X	yyy[4] = yyy[1] = yyy[0] = ycenter - leny/2;
X	xxx[2] = xxx[1] = xcenter + lenx/2;
X	yyy[3] = yyy[2] = ycenter + leny/2;
X
X	gpr_$move(xxx[0],yyy[0],st); check(st);
X	gpr_$polyline(xxx,yyy,(short)5,st); check(st);
X}
X
X/* ************************************************************ */
X/* 
X	arrow2 double lined arrow from x0,y0 to xf,yf 
X		angle is half-angle of arrowhead
X
X*/
Xarrow2(x0,y0,xf,yf,xmid,ymid,label)
Xshort x0,y0,xf,yf,xmid,ymid;
Xchar label[128];
X{
Xint quadrant, isignx, isigny, flgmidpt;
Xfloat angle,phi,r,tmp,tmpx,tmpy,rtip;
Xshort xxx[100], yyy[100], xpt, ypt, npt, savex0, savey0;
Xint length,i,j;
Xchar strip[40];
X
X	angle = PI/12.0; /* head of arrow = 2 * angle = 30 degrees */
X/*		phi = angle betw line & x-axis */
X	flgmidpt = 0; /* flag = 1 if there's a midpoint */
X/* if there's a midpoint arrows has direction of xmid,ymid to xf,yf
X		instead of x0,y0 to xf,yf
X*/
X	if(xmid > 0 && ymid > 0){
X		flgmidpt = 1;
X		savex0 = x0;
X		savey0 = y0;
X		x0 = xmid;
X		y0 = ymid;
X		}
X	tmpy = yf-y0;
X	tmpx = xf-x0;
X	if(fabs(tmpx) > 0.001) phi = atan(fabs(tmpy/tmpx));
X	else phi = PI/2.0;
X	quadrant = 1; /* xf < x0 & yf > y0 */
X	if(xf < x0 && yf < y0)  quadrant = 2;
X	else if(xf > x0 && yf < y0)  quadrant = 3;
X	else if(xf > x0 && yf > y0)  quadrant = 4;
X	else if(xf > x0 && y0 == yf) quadrant = 3;
X	else if(xf < x0 && y0 == yf) quadrant = 1;
X	else if(xf == x0 && y0 < yf) quadrant = 1;
X	else if(xf == x0 && y0 > yf) quadrant = 3;
X
X	r = 10;
X	rtip = 14;
X	if(quadrant == 1) phi = - phi;
X	else if(quadrant == 3){
X		phi = -phi;
X		r = -r; rtip = -rtip;
X		}
X	else if(quadrant == 4){
X		 r = -r; rtip = -rtip; }
X
X	if(flgmidpt == 1){
X		x0 = savex0;
X		y0 = savey0;
X		}
Xif(flgmidpt == 0){
X	xxx[2] = xf + r*cos(phi+angle);
X	yyy[2] = yf + r*sin(phi+angle);
X	xxx[3] = xf + r*cos(phi-angle);
X	yyy[3] = yf + r*sin(phi-angle);
X	xxx[4] = xxx[0] = xxx[3] + x0 - xf;
X	ypt = yyy[3] + y0 - yf;
Xif(yf == y0 || (yf < y0 && ypt <= y0) || (yf > y0 && ypt >= y0))
X	yyy[4] = yyy[0] = ypt;
X/* else if(yf > y0 && ypt >= y0)
X	yyy[4] = yyy[0] = ypt;
X*/
Xelse yyy[4] = yyy[0] = y0;
X	xxx[1] = xxx[2] + x0 - xf;
X	ypt = yyy[2] + y0 - yf;
Xif(yf == y0 || (yf < y0 && ypt <= y0) || (yf > y0 && ypt >= y0))
X	yyy[1] = ypt;
X/* else if(yf > y0 && ypt >= y0)
X	yyy[1] = ypt;
X*/
Xelse yyy[1] = y0;
X	npt = 5;  
X        }
Xelse {
X        xxx[2] = xmid + r*cos(phi+angle);
X        yyy[2] = ymid + r*sin(phi+angle);
X	xxx[3] = xf + r*cos(phi+angle);
X	yyy[3] = yf + r*sin(phi+angle);
X	xxx[4] = xf + r*cos(phi-angle);
X	yyy[4] = yf + r*sin(phi-angle);
X	xxx[6] = xxx[0] = xxx[4] + x0 - xf;
X	yyy[6] = yyy[0] = yyy[4] + y0 - yf;
X	xxx[1] = xxx[3] + x0 - xf;
X	yyy[1] = yyy[3] + y0 - yf;
X        xxx[5] = xmid + r*cos(phi-angle);
X	yyy[5] = ymid + r*sin(phi-angle);
X	npt = 7;  
X	}
X	gpr_$move(xxx[0],yyy[0],st); check(st);
X	gpr_$polyline(xxx,yyy,npt,st); check(st);
X
X	angle = PI/4.0; 
X/* arrowhead */
X	if(flgmidpt == 0){
X		xxx[3] = xxx[0] = xf; yyy[3] = yyy[0] = yf;
X		xxx[1] = xf + rtip*cos(phi-angle);
X		yyy[1] = yf + rtip*sin(phi-angle);
X		xxx[2] = xf + rtip*cos(phi+angle);
X		yyy[2] = yf + rtip*sin(phi+angle);
X		npt = 4;
X		}
X	else{
X	 	xxx[5] = xxx[2] =xf; yyy[5] = yyy[2] = yf;
X		xxx[1] = xmid; yyy[1] = ymid;
X		xxx[3] = xf + r*cos(phi-angle);
X		yyy[3] = yf + r*sin(phi-angle);
X		xxx[4] = xf + r*cos(phi+angle);
X		yyy[4] = yf + r*sin(phi+angle);
X		npt = 6;
X		}
X
X	gpr_$move(xxx[0],yyy[0],st); check(st);
X	gpr_$polyline(xxx,yyy,npt,st); check(st);
X/* 2 lines of port label if there's a [...] phrase */
X	if(yf == y0){
X		if(xf <= x0){
X			xpt = x0 - 2 * (strlen(label) - 1) * char_size;
X			ypt = y0 + char_size;
X			}
X		else {
X			xpt = x0 + 3 * char_size;
X			ypt = y0 + char_size;
X			}
X		}
X	else {
X		ypt = y0 - char_size;   /*  BOXHT/2; */
X		xpt = x0 - (strlen(label) - 1) * char_size;
X                }
X        text(xpt,ypt,label);
X/*
X			for(j=0;j<i; j++)
X			strip                i = 0;
X                while(ispunct(label[i]) == 0 && i< length) i++;
X		for(j=0;j<length;j++) strip[j] = '\0';
X		for(j=0;j<i; j++)
X			strip[j] = label[j];
X*/
X/*
X	if(flgmidpt == 0){ 
X		xpt = x0 + (xf-x0)/5 - (strlen(strip) - 1) * char_size;
X		if(yf > y0) ypt = y0 + 3*char_size; 
X                else ypt = y0 - BOXHT/2 - 3*char_size;
X                }
X	else{
X		xpt = xmid - (strlen(strip) -1) * char_size;
X		ypt = ymid -3;
X		}
X	        text(xpt,ypt,strip);
X	if(i < length - 1){
X		for(j=0;j<length;j++) strip[j] = '\0';
X		for(j=i;j<length; j++)
X			strip[j-i] = label[j];
X	if(flgmidpt == 0){ 
X		xpt = x0 + (xf-x0)/5 - (strlen(strip) - 1) * char_size;
X		if(yf > y0) ypt = y0 + 6*char_size;
X                else ypt = y0 - BOXHT/2;
X                }
X	else{
X		xpt = xmid - (strlen(strip) - 1) * char_size;
X		ypt = ymid -3 + 3*char_size;
X		}
X	        text(xpt,ypt,strip);
X                }
X*/
X/*
X	if(flgmidpt == 0){ */ /* no midpoint */
X/*		xpt = x0 + (xf-x0)/2 - strlen(label-1) * char_size;
X		ypt = y0 + 4*(yf-y0)/5 -3;
X		}
X	else{
X		xpt = xmid - strlen(label) * char_size;
X		ypt = ymid -3;
X		}
X	text(xpt,ypt,label);
X*/
X}
X/* ************************************************************ */
Xinit()
X{
X gpr_$offset_t disp_bm_size,mem_size;
X/* gpr_$bitmap_desc_t init_bitmap,mem_bm; */
X gpr_$attribute_desc_t attrib_bl_desc;
X short position[2];
X pad_$window_desc_t local_graphic_window_desc;
X stream_$id_t local_stream;
X
X	local_graphic_window_desc.top    = MINY_WINDOW[0];
X	local_graphic_window_desc.left   = MINX_WINDOW[0];
X	local_graphic_window_desc.width  = MAXX_WINDOW[0];
X	local_graphic_window_desc.height = MAXY_WINDOW[0];
X	disp_bm_size.x_size = MAXX_WINDOW[0] - MINX_WINDOW[0];
X	disp_bm_size.y_size = MAXY_WINDOW[0] - MINY_WINDOW[0];
X
X	scl = 1.0; mnx = MINX_WINDOW[0]; mny = MINY_WINDOW[0];
X
X	pad_$create_window("",0,pad_$transcript,1,
X        	local_graphic_window_desc,local_stream,st);
X        
X        pad_$set_auto_close(local_stream,1,true,st); check(st); 
X/* memory bitmap size */
X	MINX_WINDOW[1] = 0;
X	MINY_WINDOW[1] = 0;
X	MAXX_WINDOW[1] = mem_size.x_size = (short) 4096;
X	MAXY_WINDOW[1] = mem_size.y_size = (short) 2048;
X
X/*        setbutton('q',btn); 
X        setbutton('c',buttons);
X        setbutton('b',buttons);
X        setbutton('B',buttons); 
X*/
X        setbutton('q',keys);
X        setbutton(KBD_$L4,keys);
X        setbutton(KBD_$L6,keys);
X        setbutton(KBD_$L7,keys);
X        setbutton(KBD_$L8S,keys);
X        setbutton(KBD_$L9,keys);
X        setbutton(KBD_$LAS,keys);
X        setbutton(KBD_$LCS,keys);
X        setbutton(KBD_$LD,keys);
X        setbutton(KBD_$LES,keys);
X        setbutton(KBD_$LF,keys); 
X
X      gpr_$init(gpr_$direct,local_stream,disp_bm_size,0,init_bitmap,st);check(st);
X      gpr_$set_obscured_opt(gpr_$block_if_obs,st);
X      gpr_$set_auto_refresh(true,st);
X
X      gpr_$enable_input(gpr_$keystroke,keys,st); check(st);
X/*      gpr_$enable_input(gpr_$buttons,buttons,st);check(st); */
X      gpr_$enable_input(gpr_$locator,0,st);check(st);
X      gpr_$set_cursor_active(true,st); check(st);
X
X	gpr_$allocate_attribute_block(attrib_bl_desc,st); check(st);
X	gpr_$allocate_bitmap(mem_size,(short) 0,attrib_bl_desc,mem_bm,st); check(st);
X	gpr_$set_bitmap(mem_bm,st); check(st);
X
X/* fonts */
X      gpr_$load_font_file("/sys/dm/fonts/f5x7",18,font_id_small,st); check(st);
X      gpr_$set_text_font(font_id_small,st);check(st);
X      gpr_$inq_character_width(font_id_small,"R",char_width,st); check(st);
X	char_sz_sml = (long) char_width/2.0;
X      gpr_$load_font_file("/sys/dm/fonts/scvc5x10.r.b",26,font_id,st); check(st);
X      gpr_$set_text_font(font_id,st);check(st);
X      gpr_$inq_character_width(font_id,"u",char_width,st); check(st);
X	char_size = (long) char_width/2.0;
X
X      char_width_text = 5 + 2;
X      char_height_text = 7 + 1;
X}
X/* ************************************************************ */
Xtext(x0,y0,titles)
Xshort x0,y0;
Xgpr_$string_t titles;
X{
X      gpr_$move(x0,y0,st);check(st);
X      gpr_$text(titles,(short) strlen(titles),st);check(st);
X}
X/* ************************************************************ */
Xcheck(st)
Xstatus_$t st;
X{ if(st.all != status_$ok) error_$print(st);
X}
X/* ************************************************************ */
Xsetbutton(c,buttons)
Xchar c; int *buttons;
X{int j;
X j=c&255; buttons[7-j/32] |= 1<<(j%32);
X}
X/* ************************************************************ */
Xclearbutton(buttons)
Xint *buttons;
X{int i; 
X for(i=0;i<4;i++) buttons[i]=0;
X};
X/* ************************************************************ */
Xclosest(position)
Xshort *position;
X{
Xint i, imin, dmin, d, s, t, namesz;
Xchar str[128];
Xshort where[2];
X
X	dmin=4000000; /*arbitrary large number*/
X	for(i=0;i<node_max;i++){
X		s=position[0]-x[i];
X		t=position[1]-y[i];
X		d=s*s+t*t;
X		if(dmin>d) {imin=i;dmin=d;};
X		}
X	sprintf(str,"%s",nodes[imin].name);
X
X/* see if name can fit on one line, else go to 2 or more lines */
X	namesz = strlen(nodes[imin].name);
X	delta_x = (MAXX_WINDOW[0] - MINX_WINDOW[0])/(nodes[imin].of + 1);	
X	if(char_size *namesz <= delta_x){
X		s = x[imin] - char_size * namesz;
X		if (s < 0) s = 0;                     
X		}
X	else{	/* split line up */
X		s = x[imin] - char_size * namesz;
X		if (s < 0) s = 0;                     
X		}
X	t = y[imin] + 2*RADIUS/3;
X	if(t > MAXY_WINDOW[0]) t = y[imin];
X
X	gpr_$set_cursor_active(false,st); check(st);
X
X	if(nodes[imin].flg == 0) {
X		gpr_$set_draw_value(gpr_$black,st);
X		nodes[imin].flg = 1;
X		}
X	else {
X		gpr_$set_draw_value(gpr_$white,st);
X		nodes[imin].flg = 0;
X		}
X	where[0] = s; where[1] = t;
X	gpr_$set_cursor_position(where,st);
X
X	gpr_$move((short) s,(short) t,st);check(st);
X	gpr_$text(str,(short) strlen(str),st);check(st);
X
X	gpr_$set_cursor_active(true,st); check(st);
X
X	gpr_$event_wait(event_type,keystroke,position,st);check(st);
X}       
X/* ************************************************************ */
Xedge(position)
Xshort position[2];
X{int i,ip; float s,t,u,v,dd,d,e,dmin;
X short x1[3],y1[3],l;
X ip= -1;
X dmin=40000000.; /*arbitrary large number*/
X for(i=0;i<len;i++)
X { s=x[i+1]-x[i]; t=y[i+1]-y[i]; dd=sqrt(s*s+t*t);
X   if(dd<=0) fprintf(stderr,"distance=0\n");
X   s/=dd; t/=dd;
X   u=position[0]-x[i]; v=position[1]-y[i];
X   d=u*t-v*s; d=d*d;  e=u*s+v*t;
X  if((dmin>d)&&(e>0)&&(e<dd)) {dmin=d; ip=i+1;};
X };
X if(ip<0) return;
X
X len++;
X for(i=len;i>ip;i--)
X {  x[i]=x[i-1];
X    y[i]=y[i-1];
X }
X/* {  x[i]=x[i-1];  xx[i]=xx[i-1]; 
X    y[i]=y[i-1];  yy[i]=yy[i-1];
X }
X*/
Xgpr_$set_cursor_active(false,st); check(st);
Xi=ip-1; x1[0]=x[i];x1[2]=x[i+2];y1[0]=y[i];y1[2]=y[i+2];
Xx1[1]=(x1[0]+x1[2])/2;y1[1]=(y1[0]+y1[2])/2;
Xl=3;
Xfor(;;)
X{
X gpr_$set_draw_value(gpr_$black,st);
X gpr_$move(x[i],y[i],st);check(st);
X gpr_$polyline(x1,y1,l,st); 
X x[ip] =x1[1]  =position[0];  /* xx[ip] = (position[0]-10)/scl + mnx; */
X y[ip] =y1[1]  =position[1];  /* yy[ip] = (position[1]-10)/scl + mny; */
X gpr_$set_draw_value(gpr_$white,st);
X gpr_$move(x[i],y[i],st);check(st);
X gpr_$polyline(x1,y1,l,st); 
X                
X gpr_$event_wait(event_type,keystroke,position,st);check(st);
X    switch(event_type)
X    {case  gpr_$locator: break;
X     case  gpr_$buttons: 
X      if(keystroke=='B'){ gpr_$set_cursor_active(true,st); check(st);
X                          gpr_$set_cursor_position(position,st);
X                          return;}
X    } 
X}
X}
X
X/* ************************************************************ */
Xcircle(xcenter,ycenter,radius)
Xshort xcenter,ycenter,radius;
X{
Xgpr_$position_t center;
X
X	center.x_coord = xcenter; center.y_coord = ycenter;
X	gpr_$circle(center,radius,st);
X	check(st);
X}
X/* ************************************************************ */
X/* 
X	arrow from x0,y0 to xf,yf ; angle is half-angle of arrowhead
X*/
Xarrow(x0,y0,xf,yf,xmid,ymid,label)
Xshort x0,y0,xf,yf,xmid,ymid;
Xchar label[128];
X{
Xint quadrant, isignx, isigny, flgmidpt;
Xfloat angle,phi,r,tmp,tmpx,tmpy;
Xshort xxx[100], yyy[100], xpt, ypt, npt, savex0, savey0;
X
X	angle = PI/12.0; /* head of arrow = 2 * angle = 30 degrees */
X/*		phi = angle betw line & x-axis */
X	flgmidpt = 0; /* flag = 1 if there's a midpoint */
X/* if there's a midpoint arrows has direction of xmid,ymid to xf,yf
X		instead of x0,y0 to xf,yf
X*/
X	if(xmid > 0 && ymid > 0){
X		flgmidpt = 1;
X		savex0 = x0;
X		savey0 = y0;
X		x0 = xmid;
X		y0 = ymid;
X		}
X	tmpy = yf-y0;
X	tmpx = xf-x0;
X	if(fabs(tmpx) > 0.001) phi = atan(fabs(tmpy/tmpx));
X	else phi = PI/2.0;
X	quadrant = 1; /* xf < x0 & yf > y0 */
X	if(xf < x0 && yf < y0)  quadrant = 2;
X	else if(xf > x0 && yf < y0)  quadrant = 3;
X	else if(xf > x0 && yf > y0)  quadrant = 4;
X	else if(xf > x0 && y0 == yf) quadrant = 3;
X	else if(xf < x0 && y0 == yf) quadrant = 1;
X	else if(xf == x0 && y0 < yf) quadrant = 1;
X	else if(xf == x0 && y0 > yf) quadrant = 3;
X
X	r = 10;
X	if(quadrant == 1) phi = - phi;
X	else if(quadrant == 3){
X		phi = -phi;
X		r = -r;
X		}
X	else if(quadrant == 4) r = -r;
X
X	if(flgmidpt == 1){
X		x0 = savex0;
X		y0 = savey0;
X		}
X
X	xxx[0] = x0; yyy[0] = y0;
X	if(flgmidpt == 0){
X		xxx[4] = xxx[1] =xf; yyy[4] = yyy[1] = yf;
X		xxx[2] = xf + r*cos(phi-angle);
X		yyy[2] = yf + r*sin(phi-angle);
X		xxx[3] = xf + r*cos(phi+angle);
X		yyy[3] = yf + r*sin(phi+angle);
X		npt = 5;
X		}
X	else{
X		xxx[5] = xxx[2] =xf; yyy[5] = yyy[2] = yf;
X		xxx[1] = xmid; yyy[1] = ymid;
X		xxx[3] = xf + r*cos(phi-angle);
X		yyy[3] = yf + r*sin(phi-angle);
X		xxx[4] = xf + r*cos(phi+angle);
X		yyy[4] = yf + r*sin(phi+angle);
X		npt = 6;
X		}
X
X	gpr_$move(xxx[0],yyy[0],st); check(st);
X	gpr_$polyline(xxx,yyy,npt,st); check(st);
X
X	if(yf == y0){
X		if(xf <= x0){
X			xpt = x0 - 2 * (strlen(label) - 1) * char_size;
X			ypt = y0 + char_size;
X			}
X		else {
X			xpt = x0 + 3 * char_size;
X			ypt = y0 + char_size;
X			}
X		}
X	else {
X		ypt = y0 - char_size;   /*  BOXHT/2; */
X		xpt = x0 - (strlen(label) - 1) * char_size;
X                }
X        text(xpt,ypt,label);
X
X
X
X/*	if(flgmidpt == 0){ */ /* no midpoint */
X/*		xpt = x0 + (xf-x0)/2 - strlen(label) * char_size;
X		ypt = y0 + (yf-y0)/2 + 3;
X		}
X	else{
X		xpt = xmid - strlen(label) * char_size;
X		ypt = ymid + 3;
X		}
X*/
X/*	printf("\t\txpt=%ld ypt=%ld name=%s\n",(long)xpt,(long)ypt,label); */
X	text(xpt,ypt,label);
X}
X/* 
X ************************* read port data *********************************** 
X*/
Xint read_port_data(askacg)
Xint askacg;
X{
XFILE *datafile, *fopen();
Xchar str[128],file[40];
Xint i, j, port_n, n, pmax, nthfr, nthto, jfr, jto;
X/* start with node already in correct order, later do sorting */
X
X	i = 0;
X	port_n = 0;
X	pmax = 0;
X	if(askacg > 0 || askacg == -1){
X		printf("enter port file name: ");
X		scanf("%s",file);
X		if((datafile = fopen(file,"r")) == NULL)
X			printf("can't open port file: %s\n",file);
X		}
X	else if((datafile = fopen(PORT_FILE,"r")) == NULL)
X		printf("can't open %s\n",PORT_FILE);
X/*	else if((datafile = fopen("acgport.data","r")) == NULL)
X		printf("can't open acgport.data\n");
X*/
X	while ( fscanf(datafile,"%s",str) !=  EOF) {
X		if(strncmp(str,".",1) != 0){
X			switch(i){
X			case 0:	
X				strcat(ports[port_n].name,str); 
X				strcat(ports[port_n].name," ");
X				break;
X			case 1:	ports[port_n].from_node = atoi(str);
X				break;
X			case 2:	ports[port_n].to_node = atoi(str);
X				pmax++;
X				break;
X			default:
X	printf("error in reading ports - hit default\n");
X			break;
X			} /*switch */
X		}  /* if strncmp */
X		else i++; /* incr i when find a period */
X		if(i > 2){
X			n = port_n;
X			port_n++;
X			i = 0;
X			}
X	}  /* while fscanf */
X	fclose(datafile);
Xreturn(pmax);
X}
X/*
X ************************ read node data ****************************
X*/
Xint read_node_data(askacg)
Xint askacg;
X{
XFILE *datafile, *fopen();
Xchar str[128],file[40];
Xint i, j, node_n, n, nmax, l, m;
X/* start with node already in correct order, later do sorting */
X
X	i = 0; j = 0; nmax = 0;
X	node_n = 0;
X	if(askacg > 0 || askacg == -1){
X		printf("enter node file name: ");
X		scanf("%s",file);
X		if((datafile = fopen(file,"r")) == NULL)
X			printf("can't open node file: %s\n",file);
X		}
X	else if((datafile = fopen(NODE_FILE,"r")) == NULL)
X		printf("can't open %s\n",NODE_FILE);
X/*	else if((datafile = fopen("acgnode.data","r")) == NULL)
X		printf("can't open acgnode.data\n");
X*/
X	while ( fscanf(datafile,"%s",str) !=  EOF) {
X		if(strncmp(str,".",1) == 0) {
X			j = 0;
X			i++;
X			}
X		else {
X			switch(i){
X			case 0:	
X				for(m=0;m<5;m++)
X					nodes[node_n].target[m] = 0;
X				nodes[node_n].node = atoi(str);	break;
X			case 1:	nodes[node_n].target[j] = atoi(str);
X				j++;		break;
X			case 2:	nodes[node_n].source[j] = atoi(str);
X				j++;	break;
X			case 3:	nodes[node_n].cohort[j] = atoi(str);
X				j++;	break;
X			case 4:	strcat(nodes[node_n].name,str); 
X				strcat(nodes[node_n].name," ");
X				break;
X			case 5: nodes[node_n].lvl = atoi(str);
X				break;
X			case 6: nodes[node_n].nth = atoi(str);
X				break;
X			case 7: nodes[node_n].of = atoi(str);
X				break;
X			case 8: if(strncmp(str,"B",1) == 0) { /* box */
X					nodes[node_n].shape = 1;
X					nodes[node_n].wide = (short) BOXWIDTH;
X					nodes[node_n].ht = (short) BOXHT;
X					}
X				else {	/* circle */
X					nodes[node_n].shape = 0;
X					nodes[node_n].wide = (short) RADIUS;
X					nodes[node_n].ht = (short) RADIUS;
X					}
X				nmax++;
X				break;
X			default:
X			nodes[node_n].flg = 0;
X			node_n++;
X			nodes[node_n].node = atoi(str);
X			i = 0; j = 0;
X				break;
X			} /*switch */
X		}  /* else switch */
X	}  /* while fscanf */
Xn = node_n;
X	fclose(datafile);
X/* set target */
Xfor(j=0;j<nmax;j++){
X	i=0;
X	while(nodes[j].source[i] != 0){
X		m = 0; /* find subscript(m) of node which is the source */
X		while(nodes[j].source[i] != nodes[m].node && m < nmax) m++;
X/* printf("node=%d source=%d node[%d]=%d\n",nodes[j].node, nodes[j].source[i],m,nodes[m].node); */
X		l = 0; /* find first free target */
X		while(nodes[m].target[l] != 0) l++;
X		nodes[m].target[l] = nodes[j].node;
X/*
Xprintf("child nodes[%d]=%d target[%d]=%d parent=%d\n",m,nodes[m].node,l,
X	nodes[m].target[l],nodes[j].node);
X*/
X		i++;
X		}
X	}
X	return(nmax);
X}
X/* ************************************************************ */
X/* 
X		read limits from data file acglimits
X*/
Xint read_limits()
X{
XFILE *datafile, *fopen();
Xchar str[128];
Xint i,j,j0;
X	i = -1;
X	if((datafile = fopen("acglimits.data","r")) == NULL)
X		printf("can't open acglimits\n");
X	while ( fscanf(datafile,"%s",str) !=  EOF) {
X		if(ispunct(str[0]) == 0) { /* ==0 false    !=0 true */
X/*		if(isalpha(str[0]) == 0) { */
X			i++;
X			switch(i){
X			case 0: 
X                                j = 0;
X				while((GRAPH_TITLE[j]=str[j]) != '\0')
X                                	j++;
X                                j0 = j+1;
X                                strncat(GRAPH_TITLE," ",1);
X				while(fscanf(datafile,"%s",str) != 0 && ispunct(str[0]) == 0) { 
X        	                        j = 0;
X					while((GRAPH_TITLE[j0+j]=str[j]) != '\0')
X        	                        	j++;
X        	                        j0 = j0+j+1;
X        	                        strncat(GRAPH_TITLE," ",1);
X        	                        }
X				break;
X			case 1: 
X                                j = 0;
X				while((NODE_FILE[j]=str[j]) != '\0')
X                                	j++;
X				break;
X			case 2: 
X                                j = 0;
X				while((PORT_FILE[j]=str[j]) != '\0')
X                                	j++;
X				break;
X			case 3:	BOXWIDTH = atoi(str);	
X				break;
X			case 4:	BOXHT = atoi(str);
X				break;
X			case 5:	RADIUS = atoi(str);
X				break;
X			case 6:	NODE_LABEL = atoi(str);
X				break;
X			case 7: MAXX_WINDOW[0] = atoi(str);
X				break;
X			case 8: MAXY_WINDOW[0] = atoi(str);
X				break;
X			case 9: MINX_WINDOW[0] = atoi(str);
X				break;
X			case 10: MINY_WINDOW[0] = atoi(str);
X				break;
X			case 11: MIN_X_SPACE = atoi(str);
X				break;
X			case 12: MIN_Y_SPACE = atoi(str);
X				break;
X			default:
X				printf("read_limits: reached default. bah!\n");
X				break;
X			} /*switch */
X		}  /* if isalpha */
X	}  /* while fscanf */
X
X	fclose(datafile);
X	return;
X}
X
SHAR_EOF
exit