Cheap Draw - Now Freeware

Robert L. Platt rlp at ceres.UUCP
Sat Feb 10 11:39:25 AEST 1990


Cheap Draw is a drawing package for the AT&T UNIX-PC/3B1/7300.
It was originally released (version 1.0) as shareware about
3 years ago.  A later version (2.0) with many new features was
released (as careware) about a year ago.  I had plans for
many new features, which I was going to incorporate into 3.0.
It's been a year, and it doesn't look like 3.0 is going to
happen.  So, I've decided to release "Cheap Draw" into the
public domain.  Maybe someone else, with the time, and the
interest can add a few useful features.

If you have any questions, feel free to contact me via E-mail.
In earlier releases several people indicated that they were
missing libcvdi.a.  You should have the library in /usr/lib.
It IS included with the 3.0, 3.5, and 3.51 development sets.
It is on a separate floppy in your development set.  If it's
not installed, install it before compiling "Cheap Draw".
I am willing to supply UUENCODED binaries of "Cheap Draw"
on a limited basis (as long as I don't get too many requests!).

Bob Platt
osu-cis!n8emr!ceres!rlp

Note: Cheap Draw is object based and uses an ASCII data format.
Perhaps we can have an informal "Cheap Draw" art contest, by posting
drawings to the appropriate unix-pc group.  ???
------------------------ Cut Here ----------------------------------
: run sh on this file to unbundle
echo x - Makefile
cat >Makefile <<'!Funky!Stuff!'
# Cdraw Makefile -  written by R. L. Platt 

include $(MAKEINC)/Makepre.h
CFLAGS=-g

all: cdraw shrink reminder

cdraw:	cdraw.h cdraw.o menu.o
	$(LD) -G $(LDFLAGS) $(SHAREDLIB) -o cdraw cdraw.o menu.o \
		$(LIBM) /usr/lib/libcvdi.a -lPW

shrink:	shrink.sh
	cp shrink.sh shrink
	chmod +x shrink

reminder:
	@echo "Make completed!"
	@echo "Be sure to add the following lines"
	@echo "to your .profile:"
	@echo
	@echo "\tDISPLAY=pc7300"
	@echo "\texport DISPLAY"
	@echo "\t. /usr/lib/GSS_Drivers/Environment"

clean:
	/bin/rm -f *.o core

clobber: clean
	/bin/rm -f cdraw shrink

include $(MAKEINC)/Makepost.h
!Funky!Stuff!
echo x - README
cat >README <<'!Funky!Stuff!'
	"Cheap Draw 2.0" - Drawing package for the UNIX-PC

You should have nine files:

	README - this one!

	README.2 - changes since last release.
	
	Makefile - to compile cdraw

	cdraw.c - Source file - most of the code is here.

	cdraw.h - Source file - definitions, macros, etc.

	menu.c - Source file - menu handling code.

	cdraw.man - Cheap Draw manual pages

	example - sample "Cheap Draw" file.

	shrink.sh - shrink an image to 1/2 size.

Very briefly, "Cheap Draw" is a drawing package for UNIX-PC.  It
lets you create pictures consisting of lines, circles, boxes,
polygons, text, etc.

To use "Cheap Draw", you need the following -

	AT&T UNIX-PC 
	UNIX Release 3.5 
	UNIX Development set including C compiler, GSS drivers,
	and 'C' VDI binding.
	Printer (C. Itoh 8510 works for sure, others supported
		by VDI should work).

"Cheap draw" can be compiled by placing all the above files in an
otherwise empty directory, and typing:

	make

Before using "Cheap Draw" set up your environment by including the 
following lines in your .profile:

	DISPLAY=pc7300
	export DISPLAY
	. /usr/lib/GSS_Drivers/Environment

The manual can be printed by typing:

	nroff -man cdraw.man | col | lp

You will note that the load step includes a "-G" flag.  This is
required in version 3.5, because the 3.5 compiler uses flexnames,
but the VDI library was compiled without flexnames.  The -G
option truncates any unresolved external references, and searches
the library again.  "Grumble, %$!-+![%, gnash!!!"  Remove this
flag if you have 3.0.

Good luck, and enjoy!

Bob Platt
osu-cis!n8emr!ceres!rlp
!Funky!Stuff!
echo x - README.2
cat >README.2 <<'!Funky!Stuff!'
Changes for Cheap Draw Version 2.0:

[1] Added coarse / fine grid option for snap grid.
[2] Added six types of markers.
[3] Added "preview" mode to read data from std. input
    and display on screen.  Useful with data filter
    programs.
[4] Trapped user interrupts - if user hits delete key,
    cdraw cleans up and exits.  It used to leave the
    tty line in a weird state.
[5] Ellipses.
[6] Load files.
[7] Pop-up error messages.
[8] Confirmation required for some operations.
!Funky!Stuff!
echo x - cdraw.c
cat >cdraw.c <<'!Funky!Stuff!'
/*
 * Written by R. L. Platt
 */
#include <stdio.h>
#include <tam.h>
#include <message.h>
#include <menu.h>
#include <fcntl.h>
#include <math.h>
#include <sys/signal.h>
#include "cdraw.h"

/* Globals */
static short wn;
static struct Pict pict[PICTSIZ];
static int pictptr = 0;
static char *textlst[TEXTLSTSIZ];
static int textptr = 0;
static short pixop = 4;
static short dev_handle;
static short prt_handle;
static short tmp_handle;
static short work_out[66];
static short prt_out[66];
static short mode;
static short prev_mode;
static short work_in[19] = {
	1,	/* ndc index */
	1,	/* polyline line type */
	WHITE,	/* polyline color index */
	3,	/* polymarker type */
	WHITE,	/* polymarker color index */
	1,	/* graphics text font */
	WHITE,	/* graphics text color index */
	SOLID,	/* fill interior style */
	1,	/* fill style index */
	WHITE,	/* fill color index */
	1,	/* display prompts */
	'D', 'I', 'S', 'P', 'L', 'A', 'Y', ' ',  /* device name */
};

static short prt_in[] = {
	1,	/* equal axis ratios */
	1,	/* solid line type */
	WHITE,	/* line color index to use - white */
	3,	/* marker type - star */
	WHITE,	/* marker color index - white */
	1,	/* text font to use - normal */
	WHITE,	/* text color - white */
	SOLID,	/* fill interior style - hollow */
	1,	/* fill style index */
	WHITE,	/* fill color index */
	1,	/* send device messages to screen */
	'P', 'R', 'I', 'N', 'T', 'E', 'R', ' '	/* device name */
};
static short attrib[] = {
	/* Font attributes */
	1,	/* attrib[0] - size		*/
	1,	/* attrib[1] - virt=0 horz=1	*/
	/* Line attributes */
	1,	/* attrib[2] - line type	*/
	/* Fill Attributes			*/
	0,	/* attrib[3] - interior		*/
	1,	/* attrib[4] - style		*/
	0,	/* attrib[5] - grid off=0 on=1  */
	/* Grid size attribute (coarse or fine) */
	0,	/* attrib[6] - coarse=0 fine=1	*/
	/* Marker type attribute		*/
	1,	/* attrib[7]			*/
};
/* Nearest point in snap grid */
/* Note: GRIDSIZ is size of   */
/* displayed snap grid        */
static short pickgrid = COARSE_GRID;

/* Trig table */
static float sin_tbl[180];

/* Flag to indicate save state */
static int saved = 1;

/* Error Messages */
static char *msgs[] = { "Cannot load file",			/* 0  */
			"Overwrite drawing on screen?",		/* 1  */
			"Erase drawing on screen?",		/* 2  */
			"Save failed - cannot open file",	/* 3  */
			"Exit without saving?",			/* 4  */
			0 };

main(argc,argv)
int argc;
char **argv;
{
	char *s;
	short i,xy_in[2],xy_out[2];
	short d1,d2,d3;
	short preview;
	unsigned char button;
	char *get_text();
	void wrapup();
	void do_line();
	void do_cline();
	void do_rays();
	void smode();
	void do_box();
	void do_circle();
	void do_marker();
	void do_ellipse();
	void do_text();
	void do_poly();
	void redraw();
	void pictsave();
	int pictload();
	void undo();
	void freetext();
	int prt_open();
	void prt_close();
	void delete();
	void mark_pts();
	void select_options();
	void draw_grid();
	void draw_marker();
	unsigned char locator();
	char *stamp();
	int sigtrap();
	void init_trig();
	void error_msg();
	void exit();

	if (argc != 2) {
		fprintf(stderr,"FORMAT: %s [<file>|-p]\n",argv[0]);
		exit(0);
	}

	/* Check if we are in preview mode */
	preview = 0;
	if (strcmp(argv[1],"-p") == 0) preview = 1;
	/* Check if file exists - if not create it */
	else if (access(argv[1],0) == -1) {
		if (creat(argv[1],0666) == -1) {
			fprintf(stderr,"Cannot create '%s'\n",argv[1]);
			exit(0);
		}
	}
	/* Check if file is read/write */
	else if (access(argv[1],6) == -1) {
		fprintf(stderr,"Cannot access '%s'\n",argv[1]);
		exit(0);
	}
	else {
		if (pictload(argv[1]) == 0) {
			fprintf(stderr,"Cannot load '%s'\n",argv[1]);
			exit(0);
		}
	}

	/* initialize 7300 graphics window */
	winit();
	wn = wcreate(1,0,24,80,NBORDER);
	wselect(wn);
	keypad(0,1);
	wlabel(wn,DRLABEL);

	/* initialize VDI */
	if (v_opnwk(work_in,&dev_handle,work_out) != 0)
		fatal("Initialization error");

	/* Set SRC drawing mode */
	if (vswr_mode(dev_handle,pixop) < 0) fatal("Initialization error");

	/* Trap user generated interrupt signals */
	signal(SIGINT,sigtrap);

	/* Se up trig tables */
	init_trig();

	/* Preview Mode */
	if (preview) {
		(void)pictload(0);			/* Read from stdin */
		redraw(0);				/* Draw picture    */
		wcmd(wn,"PREVIEW MODE");
		wprompt(wn,"Press ENTER to continue");	/* Prompt for <CR> */
		while (wgetc(wn) != '\n');		/* Wait for it     */
		wrapup();				/* Clean-up and	   */
		wexit(0);				/* exit		   */
	}

	redraw(0);

	xy_in[0] = XCENT;
	xy_in[1] = YCENT;

	mode = LINE;

	while(1) {
		switch(mode) {
			case CLINE:	wprompt(wn,"Pick start of line");
					smode("Pick","Menu","Undo","C-line");
					break;
			case LINE:	wprompt(wn,"Pick start of line");
					smode("Pick","Menu","Undo","Line");
					break;
			case RAYS:	wprompt(wn,"Pick center point");
					smode("Pick","Menu","Undo","Rays");
					break;
			case BOX:	wprompt(wn,"Pick start of box");
					smode("Pick","Menu","Undo","Box");
					break;
			case CIRCLE:	wprompt(wn,"Pick center of circle");
					smode("Pick","Menu","Undo","Circle");
					break;
			case ELLIPSE:	wprompt(wn,"Pick first point");
					smode("Pick","Menu","Undo","Ellipse");
					break;
			case MARKER:	wprompt(wn,"Position marker");
					smode("Pick","Menu","Undo","Marker");
					break;
			case POLYGON:	wprompt(wn,"Pick first point");
					smode("Pick","Menu","Undo","Polygon");
					break;
			case TEXT:	wprompt(wn,"Position text");
					smode("Pick","Menu","Undo","Text");
					break;
			default:	wprompt(wn,"Not implemented");
					smode("N/A","Menu","Undo",
						"Not implemented");
					break;
		}
		button = locator(xy_in,0,0,xy_out);
		if (button == B1) {
			switch(mode) {
				case CLINE:	do_cline(xy_out);
						break;
				case POLYGON:	do_poly(xy_out);
						break;
				case LINE:	do_line(xy_out);
						break;
				case RAYS:	do_rays(xy_out);
						break;
				case BOX:	do_box(xy_out);
						break;
				case MARKER:	do_marker(xy_out);
						break;
				case CIRCLE:	do_circle(xy_out);
						break;
				case ELLIPSE:	do_ellipse(xy_out);
						break;
				case TEXT:	do_text(xy_out,textptr-1);
						break;
				default:	break;
			}
			saved = 0;
		}
		else if (button == B3) {
			undo();
			saved = 0;
		}
		else if (button == B2) {
			prev_mode = mode;
			switch(mode=select_mode()) {
				case CLINE:	break;
				case LINE:	break;
				case BOX:	break;
				case RAYS:	break;
				case CIRCLE:	break;
				case POLYGON:	break;
				case ELLIPSE:	break;
				case MARKER:	break;
				case CLEAR:	/* Clear picture */
						if (pictptr && confirm(2)==0) {
							mode = prev_mode;
							break;
						}
						v_clrwk(dev_handle);
						if (attrib[5]) draw_grid();
						pictptr = 0;
						freetext();
						mode = LINE;
						saved = 0;
						break;
				case DELETE:	/* Delete object */
						delete(xy_out);
						mode = prev_mode;
						saved = 0;
						break;
				case OPTIONS:	/* Drawing options */
						i = attrib[5];
						select_options(&attrib[0]);
						if (i==0 && attrib[5])
							draw_grid();
						if (i && attrib[5]==0) {
							v_clrwk(dev_handle);
							redraw(0);
						}
						mode = prev_mode;
						break;
				case TEXT:	/* Draw text */
						if ((s=get_text()) == NULL) {
							mode = prev_mode;
							break;
						}
						textlst[textptr++] = s;
						if (textptr >= TEXTLSTSIZ)
						  fatal("Text table overflow");
						saved = 0;
						break;
				case REFRESH:	/* Refresh screen */
						v_clrwk(dev_handle);
						redraw(0);
						if (attrib[5]) draw_grid();
						mode = prev_mode;
						break;
				case PRINT:	/* Print screen */
						if (prt_open() == 0) break;
						tmp_handle = dev_handle;
						dev_handle = prt_handle;
						vst_rotation(dev_handle,0);
						vst_height(dev_handle,PTEXSIZ,
							&d1,&d2,&d3);
						v_gtext(dev_handle,1000,
							32000,stamp());
						redraw(1);
						dev_handle = tmp_handle;
						prt_close();
						mode = prev_mode;
						break;
				case LOAD:	/* Load saved file */
						if (pictptr && confirm(1)==0) {
							mode = prev_mode;
							break;
						}
						v_clrwk(dev_handle);
						pictptr = 0;
						freetext();
						if (pictload(argv[1]) == 0)
							error_msg(0);
						else redraw(0);
						if (attrib[5]) draw_grid();
						mode = LINE;
						saved = 1;
						break;
				case SAVE:	/* Save file */
						pictsave(argv[1]);
						mode = prev_mode;
						break;
				case EXIT:	/* Cleanup and exit */
						if (saved == 0 && 
							confirm(4) == 0) {
							mode = prev_mode;
							break;
						}
						wrapup();
						wexit(0);
				default:	mode = prev_mode;
						break;
			}
		}
		xy_in[0] = xy_out[0];
		xy_in[1] = xy_out[1];
	}
}

void do_line(xy_in)
short xy_in[2];
{
	short xy_out[2];
	short p;
	void do_pict();
	unsigned char locator();

	wprompt(wn,"Pick end point");
	smode("Pick","Cancel","Cancel","Line");
	vsl_type(dev_handle,attrib[2]);
	if (locator(xy_in,0,1,xy_out) != B1) return;

	p = pictptr;
	pict[pictptr].mode = LINE;
	pict[pictptr].x = xy_in[0];
	pict[pictptr].y = xy_in[1];
	pict[pictptr++].arg[0] = attrib[2];
	if (pictptr >= PICTSIZ) fatal("Point table overflow");

	pict[pictptr].mode = 0;
	pict[pictptr].x = xy_out[0];
	pict[pictptr].y = xy_out[1];
	pict[pictptr++].arg[0] = attrib[2];
	if (pictptr >= PICTSIZ) fatal("Point table overflow");
	do_pict(p,0);

	return;
}

void do_ellipse(xy_in)
short xy_in[2];
{
	short xy_out[2];
	unsigned char locator();
	short p;

	wprompt(wn, "Pick second point");
	smode("Pick","Cancel","Cancel","Ellipse");
	vsl_type(dev_handle,1);
	if (locator(xy_in,0,2,xy_out) != B1) return;
	p = pictptr;
	pict[pictptr].mode = ELLIPSE;
	pict[pictptr].x = xy_in[0];
	pict[pictptr].y = xy_in[1];
	pict[pictptr].arg[0] = attrib[3];
	pict[pictptr++].arg[1] = attrib[4];
	if (pictptr >= PICTSIZ) fatal("Point table overflow");

	pict[pictptr].mode = 0;
	pict[pictptr].x = xy_out[0];
	pict[pictptr].y = xy_out[1];
	pict[pictptr].arg[0] = attrib[3];
	pict[pictptr++].arg[1] = attrib[4];
	if (pictptr >= PICTSIZ) fatal("Point table overflow");

	do_pict(p,0);
	return;
}

void do_marker(xy_in)
short xy_in[2];
{
	pict[pictptr].mode = MARKER;
	pict[pictptr].x = xy_in[0];
	pict[pictptr].y = xy_in[1];
	pict[pictptr++].arg[0] =  attrib[7];
	if (pictptr >= PICTSIZ) fatal("Point table overflow");
	do_pict(pictptr-1,0);
	return;
}

void do_circle(xy_in)
short xy_in[2];
{
	short xy_out[2];
	unsigned char locator();
	short p;

	wprompt(wn, "Pick edge of circle");
	smode("Pick","Cancel","Cancel","Circle");
	vsl_type(dev_handle,1);
	if (locator(xy_in,0,1,xy_out) != B1) return;
	p = pictptr;
	pict[pictptr].mode = CIRCLE;
	pict[pictptr].x = xy_in[0];
	pict[pictptr].y = xy_in[1];
	pict[pictptr].arg[0] =  attrib[3];
	pict[pictptr++].arg[1] =  attrib[4];
	if (pictptr >= PICTSIZ) fatal("Point table overflow");

	pict[pictptr].mode = 0;
	pict[pictptr].x = xy_out[0];
	pict[pictptr].y = xy_out[1];
	pict[pictptr].arg[0] =  attrib[3];
	pict[pictptr++].arg[1] =  attrib[4];
	if (pictptr >= PICTSIZ) fatal("Point table overflow");

	do_pict(p,0);
	return;
}

void do_box(xy_in)
short xy_in[2];
{
	short xy_out[2];
	unsigned char locator();
	short p;

	wprompt(wn, "Pick opposite corner");
	smode("Pick","Cancel","Cancel","Box");
	vsl_type(dev_handle,1);
	if (locator(xy_in,0,2,xy_out) != B1) return;
	p = pictptr;
	pict[pictptr].mode = BOX;
	pict[pictptr].x = xy_in[0];
	pict[pictptr].y = xy_in[1];
	pict[pictptr].arg[0] = attrib[3];
	pict[pictptr++].arg[1] = attrib[4];
	if (pictptr >= PICTSIZ) fatal("Point table overflow");

	pict[pictptr].mode = 0;
	pict[pictptr].x = xy_out[0];
	pict[pictptr].y = xy_out[1];
	pict[pictptr].arg[0] = attrib[3];
	pict[pictptr++].arg[1] = attrib[4];
	if (pictptr >= PICTSIZ) fatal("Point table overflow");

	do_pict(p,0);
	return;
}

void do_cline(xy_in)
short xy_in[2];
{
	short xy_out[2];
	unsigned char locator();
	void draw_line();
	int flag;

	flag = 1;
	wprompt(wn,"Pick next point");
	smode("Pick","End","End","C-line");

	vsl_type(dev_handle,attrib[2]);
	while(1) {
		if (locator(xy_in,0,1,xy_out) != B1) return;
		draw_line(xy_in, xy_out,attrib[2]);
		if (flag) {
			pict[pictptr].mode = CLINE;
			pict[pictptr].x = xy_in[0];
			pict[pictptr].y = xy_in[1];
			pict[pictptr++].arg[0] = attrib[2];
			if (pictptr >= PICTSIZ) 
				fatal("Point table overflow");
			flag = 0;
		}
		pict[pictptr].mode = 0;
		xy_in[0] = pict[pictptr].x = xy_out[0];
		xy_in[1] = pict[pictptr].y = xy_out[1];
		pict[pictptr++].arg[0] = attrib[2];
		if (pictptr >= PICTSIZ) fatal("Point table overflow");
	}
}

void do_poly(xy_in)
short xy_in[2];
{
	short xy_out[2];
	unsigned char locator();
	void do_pict();
	int flag;
	short p, xyfirst[2];

	flag = 1;
	xyfirst[0] = xy_in[0];	xyfirst[1] = xy_in[1];
	wprompt(wn,"Pick next point");
	smode("Pick","Connect","Connect","Polygon");

	vsl_type(dev_handle,1);
	while(1) {
		if (locator(xy_in,0,1,xy_out) != B1) break;
		else draw_line(xy_in,xy_out,1);
		if (flag) {
			p = pictptr;
			pict[pictptr].mode = POLYGON;
			pict[pictptr].x = xy_in[0];
			pict[pictptr].y = xy_in[1];
			pict[pictptr].arg[0] = attrib[3];
			pict[pictptr++].arg[1] = attrib[4];
			if (pictptr >= PICTSIZ) 
				fatal("Point table overflow");
			flag = 0;
		}
		pict[pictptr].mode = 0;
		xy_in[0] = pict[pictptr].x = xy_out[0];
		xy_in[1] = pict[pictptr].y = xy_out[1];
		pict[pictptr].arg[0] = attrib[3];
		pict[pictptr++].arg[1] = attrib[4];
		if (pictptr >= PICTSIZ) fatal("Point table overflow");
	}
	if (flag) return;
	pict[pictptr].mode = 0;
	pict[pictptr].x = xyfirst[0];
	pict[pictptr].y = xyfirst[1];
	pict[pictptr].arg[0] = attrib[3];
	pict[pictptr++].arg[1] = attrib[4];
	if (pictptr >= PICTSIZ) fatal("Point table overflow");

	do_pict(p,0);
	return;
}

void do_rays(xy_in)
short xy_in[2];
{
	short xy_out[2];
	unsigned char locator();
	void draw_line();
	int flag;

	flag = 1;
	wprompt(wn,"Pick end point");
	smode("Pick","End","End","Rays");

	vsl_type(dev_handle,attrib[2]);
	while(1) {
		if (locator(xy_in,0,1,xy_out) != B1) return;
		draw_line(xy_in, xy_out,attrib[2]);
		if (flag) {
			pict[pictptr].mode = RAYS;
			pict[pictptr].x = xy_in[0];
			pict[pictptr].y = xy_in[1];
			pict[pictptr++].arg[0] = attrib[2];
			if (pictptr >= PICTSIZ) 
				fatal("Point table overflow");
			flag = 0;
		}
		pict[pictptr].mode = 0;
		pict[pictptr].x = xy_out[0];
		pict[pictptr].y = xy_out[1];
		pict[pictptr++].arg[0] = attrib[2];
		if (pictptr >= PICTSIZ) fatal("Point table overflow");
	}
}

void do_text(xy,tptr)
short xy[2],tptr;
{
	void do_pict();

	if (tptr < 0) return;
	pict[pictptr].mode = TEXT;
	pict[pictptr].x = xy[0]; 
	pict[pictptr].y = xy[1];
	pict[pictptr].arg[0] = tptr;
	pict[pictptr].arg[1] = attrib[0];
	pict[pictptr++].arg[2] = attrib[1];
	if (pictptr >= PICTSIZ) fatal("Point table overflow");
	do_pict(pictptr-1,0);
	
	return;
}

void draw_text(xy,str,size,rot)
short xy[2];
char *str;
short size;
short rot;
{
	short d1,d2,d3;
	short tquanta;

	if (prt_handle == dev_handle) tquanta = PTEXSIZ;
	else tquanta = STEXSIZ;

	vst_rotation(dev_handle,rot*10);
	vst_height(dev_handle,size*tquanta,&d1,&d2,&d3);
	v_gtext(dev_handle,xy[0],xy[1],str);
	return;
}

/* Clean-up */
void wrapup()
{
	v_clrwk(dev_handle);
	v_clswk(dev_handle);
	wlabel(wn,"");
	wprompt(wn,"");
	wcmd(wn,"");
}

/* User interrupt */
int sigtrap()
{
	void wrapup();

	signal(SIGINT,SIG_IGN);
	wrapup();
	wexit(0);
}

void smode(s1,s2,s3,s4)
char *s1, *s2, *s3, *s4;
{
	static buf[128];

	sprintf(buf,"MOUSE: < %s >  < %s >  < %s >   MODE: %s",s1,s2,s3,s4);
	wcmd(wn,buf);
	return;
}

void draw_line(xy_a,xy_b,ltype)
short xy_a[2], xy_b[2],ltype;
{
	short xy[4];

	xy[0] = xy_a[0];	xy[1] = xy_a[1];
	xy[2] = xy_b[0];	xy[3] = xy_b[1];
	vsl_type(dev_handle,ltype);
	v_pline(dev_handle,2,xy);
	return;
}

void draw_box(xy_a,xy_b,interior,fstyle)
short xy_a[2], xy_b[2];
short interior,fstyle;
{
	short xy[10];

	xy[0] = xy_a[0];	xy[1] = xy_a[1];
	xy[2] = xy_b[0];	xy[3] = xy_a[1];
	xy[4] = xy_b[0];	xy[5] = xy_b[1];
	xy[6] = xy_a[0];	xy[7] = xy_b[1];
	xy[8] = xy_a[0];	xy[9] = xy_a[1];
	vsl_type(dev_handle,1);
	vsf_interior(dev_handle,interior);
	vsf_style(dev_handle,fstyle);
	if (interior) v_fillarea(dev_handle,5,xy);
	else v_pline(dev_handle,5,xy);
	return;
}

void draw_ellipse(xy_a,xy_b,interior,fstyle)
short xy_a[2],xy_b[2];
short interior,fstyle;
{
	short hwid, hht, cent[2],i,j;
	short pts[(ELL_PTS+1)*2];
	float dx,dy;
	float mysin(), mycos();
	void draw_poly();

	hwid = ABS(xy_a[0]-xy_b[0])/2;
	hht = ABS(xy_a[1]-xy_b[1])/2;
	cent[0] = (xy_a[0] + xy_b[0])/2;
	cent[1] = (xy_a[1] + xy_b[1])/2;
	for (i=j=0; i<360; i+=(360/ELL_PTS)) {
		dx = (float)hwid * mycos(i);
		dy = (float)hht * mysin(i);
		pts[j++] = (int)dx + cent[0];
		pts[j++] = (int)dy + cent[1];
	}
	pts[j++] = pts[0];
	pts[j] = pts[1];
	draw_poly(pts,ELL_PTS+1,interior,fstyle);
	return;
}

void draw_circle(xy_a,xy_b,interior,fstyle)
short xy_a[2],xy_b[2];
short interior,fstyle;
{
	int dx, dy, radius;
	double sqrt();
	
	dx = xy_a[0] - xy_b[0];
	dy = xy_a[1] - xy_b[1];
	radius = (int)(sqrt((double)(dx*dx) + (double)(dy*dy)));
	vsl_type(dev_handle,1);
	vsf_interior(dev_handle,interior);
	vsf_style(dev_handle,fstyle);
	v_circle(dev_handle,xy_a[0],xy_a[1],radius);
	return;
}

void draw_marker(xy,mtype)
short xy[2];
short mtype;
{
	vsm_color(dev_handle,WHITE);
	vsm_height(dev_handle,250);
	vsm_type(dev_handle,mtype);
	v_pmarker(dev_handle,1,xy);
	return;
}

void undo()
{
	int p;
	void do_pict();

	for (p=pictptr-1; p>=0; p--) {
		if (pict[p].mode) {
			vswr_mode(dev_handle,7);
			do_pict(p,0);
			vswr_mode(dev_handle,pixop);
			pictptr = p;
			break;
		}
	}
	return;
}

void redraw(trans)
int trans;
{
	int p;
	void do_pict();

	for (p=0; p<pictptr; ++p) if (pict[p].mode) do_pict(p,trans);
	return;
}

void pictsave(fname)
char *fname;
{
	FILE *fp;
	int p;
	void gcollect();
	void error_msg();

	if ((fp=fopen(fname,"w")) == NULL) {
		error_msg(3);
		return;
	}
	gcollect();
	fprintf(fp,DRAWHDR);
	for (p=0; p<textptr; ++p) {
		fprintf(fp,"%s\n",textlst[p]);
	}
	fprintf(fp,"\n");
	for (p=0; p<pictptr; ++p) {
		fprintf(fp,"%d %d %d %d %d %d\n",pict[p].mode,pict[p].x,
			pict[p].y, pict[p].arg[0], pict[p].arg[1],
			pict[p].arg[2]);
	}
	fclose(fp);
	saved = 1;
	return;
}

int pictload(fname)
char *fname;
{
	FILE *fp;
	char buf[128];
	char *p;
	int strlen();
	char *strcpy();
	char *strtok();
	char *malloc();

	if (fname == 0) fp = stdin;
	else if ((fp=fopen(fname,"r")) == NULL) return(0);
	fgets(buf,sizeof(buf),fp);
	if (strcmp(buf,DRAWHDR) != 0) return(0);
	for (textptr=0; textptr<TEXTLSTSIZ; ++textptr) {
		fgets(buf,sizeof(buf),fp);
		if (buf[0] == '\n') break;
		buf[strlen(buf)-1] = 0;
		if ((p=malloc(strlen(buf)+1)) == NULL) {
			fclose(fp);
			return(0);
		}
		strcpy(p,buf);
		textlst[textptr] = p;
	}
	for (pictptr=0; pictptr<PICTSIZ; ++pictptr) {
		if (fgets(buf,sizeof(buf),fp) == NULL) break;
		if (buf[0] != '#') {
			if ((p=strtok(buf," \t\n")) == NULL) break;
			pict[pictptr].mode = atoi(p);
			if ((p=strtok(NULL," \t\n")) == NULL) break;
			pict[pictptr].x = atoi(p);
			if ((p=strtok(NULL," \t\n")) == NULL) break;
			pict[pictptr].y = atoi(p);
			if ((p=strtok(NULL," \t\n")) == NULL) break;
			pict[pictptr].arg[0] = atoi(p);
			if ((p=strtok(NULL," \t\n")) == NULL) break;
			pict[pictptr].arg[1] = atoi(p);
			if ((p=strtok(NULL," \t\n")) == NULL) break;
			pict[pictptr].arg[2] = atoi(p);
		}
	}
	fclose(fp);
	return(1);
}

void do_pict(p,trans)
int p,trans;
{
	short a[2],b[2];
	short i, rot, ltype, interior, fstyle, mtype;
	short *xy;
	int xycnt;
	short *getpts();
	void transxy();
	void draw_poly();

	switch(pict[p].mode) {
		case CLINE:	ltype = pict[p].arg[0];
				a[0] = pict[p].x; a[1] = pict[p++].y;
				if (trans) transxy(a);
				do {
					b[0] = pict[p].x; b[1] = pict[p++].y;
					if (trans) transxy(b);
					draw_line(a,b,ltype);
					a[0] = b[0];	a[1] = b[1];
				} while(pict[p].mode == 0 && p < pictptr);
				break;
		case RAYS:	ltype = pict[p].arg[0];
				a[0] = pict[p].x; a[1] = pict[p++].y;
				if (trans) transxy(a);
				do {
					b[0] = pict[p].x; b[1] = pict[p++].y;
					if (trans) transxy(b);
					draw_line(a,b,ltype);
				} while(pict[p].mode == 0 && p < pictptr);
				break;
		case LINE:	ltype = pict[p].arg[0];
				a[0] = pict[p].x; a[1] = pict[p++].y;
				b[0] = pict[p].x; b[1] = pict[p++].y;
				if (trans) {
					transxy(a); transxy(b);
				}
				draw_line(a,b,ltype);
				break;
		case BOX:	interior = pict[p].arg[0];
				fstyle = pict[p].arg[1];
				a[0] = pict[p].x; a[1] = pict[p++].y;
				b[0] = pict[p].x; b[1] = pict[p++].y;
				if (trans) {
					transxy(a); transxy(b);
				}
				draw_box(a,b,interior,fstyle);
				break;
		case CIRCLE:	interior = pict[p].arg[0];
				fstyle = pict[p].arg[1];
				a[0] = pict[p].x; a[1] = pict[p++].y;
				b[0] = pict[p].x; b[1] = pict[p++].y;
				if (trans) {
					transxy(a); transxy(b);
				}
				draw_circle(a,b,interior,fstyle);
				break;
		case ELLIPSE:	interior = pict[p].arg[0];
				fstyle = pict[p].arg[1];
				a[0] = pict[p].x; a[1] = pict[p++].y;
				b[0] = pict[p].x; b[1] = pict[p++].y;
				if (trans) {
					transxy(a); transxy(b);
				}
				draw_ellipse(a,b,interior,fstyle);
				break;
		case TEXT:	a[0] = pict[p].x; a[1] = pict[p].y;
				if (trans) {
					transxy(a);
					if (pict[p].arg[2]) rot = 270;
					else rot = 0;
				}
				else {
					if (pict[p].arg[2]) rot = 0;
					else rot = 90;
				}
				draw_text(a,textlst[pict[p].arg[0]],
					pict[p].arg[1],rot);
				break;
		case POLYGON:	interior = pict[p].arg[0];
				fstyle = pict[p].arg[1];
				xy = getpts(p,&xycnt);
				if (trans) {
					for (i=0; i<(2*xycnt); i+=2)
						transxy(&xy[i]);
				}
				draw_poly(xy,(short)xycnt,interior,fstyle);
				free(xy);
				break;
		case MARKER:	a[0] = pict[p].x; a[1] = pict[p].y;
				mtype = pict[p++].arg[0];
				if (trans) transxy(a);
				draw_marker(a,mtype);
				break;
		default:	break;
	}
	return;
}

void freetext()
{
	int i;

	for (i=0; i<textptr; ++i) free(textlst[i]);
	textptr = 0;
	return;
}

int prt_open()
{
	/* initialize printer */
	if (v_opnwk(prt_in,&prt_handle,prt_out) != 0) return(0);
	else return(1);
}

void prt_close()
{
	v_clswk(prt_handle);
	return;
}

void transxy(a)
short a[2];
{
	/* Scale to 90% */
	a[0] = (a[0] * 9) / 10;
	a[1] = (a[1] * 9) / 10;
	/* Shift X & Y by 2000 */
	a[0] = a[0] + 2000;
	a[1] = a[1] + 2000;
	/* Swap X & Y */
	SWAP(a);
	/* Reflect about center line (x = center) */
	a[1] = XMAX - a[1];
	a[1] = (a[1]<0) ? - a[1] : a[1];
	return;
}

char *stamp()
{
	char buf[128];
	char *logname();
	char *ctime();
	long time();
	long tloc;

	time(&tloc);
	sprintf(buf,"%s %s",logname(),ctime(&tloc));
	return(buf);
}

void mark_pts(start,end)
short start, end;
{
	short p,xy[2];

	vsm_color(dev_handle,WHITE);
	vsm_height(dev_handle,100);
	vsm_type(dev_handle,4);
	for (p=start; p<end; ++p) {
		xy[0] = pict[p].x;	xy[1] = pict[p].y;
		v_pmarker(dev_handle,1,xy);
	}
	return;
}

void delete(xy_in)
short xy_in[2];
{
	short flag, p, end;
	short xy_out[2];
	long dx, dy, dist;
	short xy[2];
	unsigned char button;
	void remove();
	void smode();

	wprompt(wn,"Select delete point");
	smode("Pick","Cancel","Cancel","Delete");
	mark_pts(0,pictptr);
	vrq_locator(dev_handle,xy_in,0,0,dev_handle,xy_out,&button);
	if (button != B1) {
		v_clrwk(dev_handle);
		redraw(0);
		if (attrib[5]) draw_grid();
		return;
	}
	flag = 0;
	for (p=0; p<pictptr; ++p) {
		dx = (xy_out[0] - pict[p].x)/10;
		dy = (xy_out[1] - pict[p].y)/10;
		dist = (dx*dx) + (dy*dy);
		if (dist < 1000) {
			flag = 1;
			break;
		}
	}

	if (flag) {
		xy[0] = pict[p].x;
		xy[1] = pict[p].y;
		/* Find start of object */
		while(pict[p].mode == 0) p--;
		/* Find end of object */
		for (end=p+1; pict[end].mode==0 && end<pictptr; end++);
		remove(p,end);
	}
	v_clrwk(dev_handle);
	redraw(0);
	if (attrib[5]) draw_grid();

	return;
}

void remove(p,end)
short p,end;
{
	short i, delta;

	/* Delete item from list */
	if (end == pictptr) pictptr = p;
	else {
		delta = end - p;
		for (i=end; i<pictptr; ++i) pict[i-delta] = pict[i];
		pictptr = pictptr - delta;
	}
	return;
}

void gcollect()
{
	int i,j,flag;

	/* Search through all strings */
	for (i=0; i<textptr; ++i) {
		flag = 1;
		for (j=0; j<pictptr; ++j) {
			/* If string is used set flag & break */
			if (pict[j].mode == TEXT && pict[j].arg[0] == i) {
				flag = 0;
				break;
			}
		}
		/* If string is not used - deallocate it */
		if (flag) {
			free(textlst[i]);
			textlst[i] = NULL;
		}
	}
	/* Search through all strings */
	for (i=0; i<textptr; ++i) {
		/* If it is empty */
		if (textlst[i] == NULL) {
			/* Compress list */
			for (j=i; j<textptr; ++j) textlst[j] = textlst[j+1];
			/* Adjust pointers to list */
			for (j=0; j<pictptr; ++j) {
				if (pict[j].mode == TEXT && pict[j].arg[0] > i)
					pict[j].arg[0]--;
			}
			/* Examine next element, reduce list size */
			textptr--;
			i--;
		}
	}
	return;
}

short *getpts(p,cnt)
int p, *cnt;
{
	short i,j,end,*ptr;
	char *malloc();

	for (end=p+1; pict[end].mode==0 && end<pictptr; end++);
	*cnt = end - p;
	
	ptr = (short*)malloc((*cnt) * 2 * sizeof(short));
	for (i=0,j=p; i<(*cnt * 2); j++,i+=2) {
		ptr[i] = pict[j].x;
		ptr[i+1] = pict[j].y;
	}

	return(ptr);
}

void draw_poly(xy,xycnt,interior,fstyle)
short xy[], xycnt, interior, fstyle;
{
	vsl_type(dev_handle,1);
	vsf_interior(dev_handle,interior);
	vsf_style(dev_handle,fstyle);
	if (interior) v_fillarea(dev_handle,xycnt,xy);
	else v_pline(dev_handle,xycnt,xy);
	return;
}

void draw_grid()
{
	register short i,j,k;
	short *xy;
	short pts;
	char *malloc();

	pts = (XMAX/GRIDSIZ) * (YMAX/GRIDSIZ);
	xy = (short *)malloc(sizeof(short)*2*pts);
	k = 0;
	for (i=1; i<=(XMAX/GRIDSIZ); i++) {
		for (j=1; j<=(YMAX/GRIDSIZ); j++) {
			xy[k++] = i*GRIDSIZ;	xy[k++] = j*GRIDSIZ;
		}
	}
	vsm_color(dev_handle,WHITE);
	vsm_height(dev_handle,50);
	vsm_type(dev_handle,1);
	v_pmarker(dev_handle,pts,xy);
	free(xy);
	return;
}

unsigned char locator(xy_in,ink,rubber,xy_out)
short xy_in[2], rubber, ink, xy_out[2];
{
	unsigned char button;
	long x, y;

	vrq_locator(dev_handle,xy_in,ink,rubber,dev_handle,xy_out,&button);

	if (attrib[5]) {	/* If grid enabled */
		pickgrid = ((attrib[6] == 0) ? COARSE_GRID : FINE_GRID);
		x = xy_out[0];	y = xy_out[1];

		x = ((x+pickgrid/2) / pickgrid) * pickgrid;
		x = (x<0) ? 0 : x;
		x = (x>XMAX) ? ((XMAX/pickgrid)*pickgrid) : x;

		y = ((y+pickgrid/2) / pickgrid) * pickgrid;
		y = (y<0) ? 0 : y;
		y = (y>YMAX) ? ((YMAX/pickgrid)*pickgrid) : y;

		xy_out[0] = x;	xy_out[1] = y;
	}

	return(button);
}

int fatal(s)
char *s;
{
	void wrapup();

	wrapup();
	fprintf(stderr,"ERROR: %s\n",s);
	wexit(0);
}

void init_trig()
{
	int i;
	double theta;
	double sin(), cos();

	for (i=0; i<180; ++i) {
		theta = (double)i * PI / 180.;
		sin_tbl[i] = (float)sin(theta);
	}
}

float mysin(deg)
int deg;
{
	int theta,sgn;

	theta = deg % 180;
	sgn = ((deg / 180 % 2 == 0) ? 1 : -1);
	return((float)sgn * sin_tbl[theta]);
}

float mycos(deg)
int deg;
{
	float mysin();
	return(mysin(deg+90));
}

void error_msg(msgnum)
int msgnum;
{
	message(MT_POPUP,0,0,"%s",msgs[msgnum]);
}

int confirm(msgnum)
int msgnum;
{
	int rc;

	rc = message(MT_CONFIRM,0,0,"%s",msgs[msgnum]);
	if (rc == 10) return(1);
	else return(0);
}
!Funky!Stuff!
echo x - cdraw.h
cat >cdraw.h <<'!Funky!Stuff!'
/*
 * Written by R. L. Platt
 */
#ifndef CDRAW_H
#define CDRAW_H

#define PICTSIZ		2048
#define TEXTLSTSIZ	128

#define HOLLOW	0
#define SOLID	1
#define HATCH	3
#define BLACK 	0
#define WHITE 	1

#define XMAX	32767		/* Max X on UNIX PC */
#define YMAX	17704		/* Max Y on UNIX PC */
#define XCENT	(XMAX/2)	/* X Center */
#define YCENT	(YMAX/2)	/* Y Center */
#define STEXSIZ	572		/* Screen text size quanta */
#define PTEXSIZ	342		/* Printer text size quanta */
				/* For ATT-470 printer	    */
#define GRIDSIZ	1000		/* Grid spacing */
#define COARSE_GRID GRIDSIZ	/* Grid size - for picking */
#define FINE_GRID GRIDSIZ/2	/* Grid size - for picking */

/* ASCII Codes for Mouse Buttons */
#define B1	' '	/* Left Button */
#define B2	'!'	/* Middle Button */
#define B3	'"'	/* Right Button */

/* Draw Package Modes */
#define CLINE	1
#define LINE	2
#define RAYS	3
#define BOX	5
#define CIRCLE	6
#define TEXT	7
#define POLYGON	8
#define ELLIPSE 9
#define MARKER	10
#define CLEAR	100
#define REFRESH	101
#define EXIT	102
#define LOAD	103
#define SAVE	104
#define PRINT	105
#define DELETE	106
#define OPTIONS	107

#define MAX(x,y) 	(((x)>(y))?(x):(y))
#define MIN(x,y) 	(((x)>(y))?(y):(x))
#define SWAP(xy)	{ int t; t = xy[0]; xy[0] = xy[1]; xy[1] = t; }
#define ABS(a)		(((a)>0)?(a): -(a))
#define PI		3.14159265

/* Pict structure */
struct Pict {
	unsigned char mode;
	short x;
	short y;
	unsigned char arg[3];
};

/* # pts in ellipse - Note: must have (360 % ELL_PTS) = 0 */
#define ELL_PTS	90

/* Drawing file header */
#define DRAWHDR "Cheap Draw\n"

/* Screen label */
#define DRLABEL "Cheap Draw 2.0 - R. L. Platt"

#define PROXIMITY (100*100)

#endif
!Funky!Stuff!
echo x - cdraw.man
cat >cdraw.man <<'!Funky!Stuff!'
.TH "CDRAW 2.0" 1
.SH "NAME"
cdraw - "Cheap Draw" is a drawing package for the UNIX PC.
.SH "SYNOPSIS"
.B cdraw
file
.SH "RIGHTS & LIMITATIONS"
[1] "Cheap Draw" is distributed "as is".  You assume the risk of any and
all damage or loss from use, or inability to use the software.
The author does not warrant that the functions of the software will
meet your needs or that the software will be error-free or uninterrupted.
.sp
[2] You may do anything you want with it.  I don't care.
.SH "DESCRIPTION"
(So much for the legal junk). "Cheap Draw" is a point / segment 
based (rather than pixel oriented)
drawing package for the UNIX-PC.  It takes a single argument,
a file name that can be used to load / store "Cheap Draw" pictures.
Upon invocation, "Cheap Draw" will bring up a full screen window.
The window label at the top of the screen will contain the
program identification.  The window will be either blank, or will contain
a picture loaded from the specified file.  The bottom of the screen
will contain two lines of information.  The first line will be
prompting information, such as "Pick start of line".  The second
line contains instructions for using the mouse, and will identify the
current drawing mode (such as "Line").
The mouse instructions denote the current meaning of the left, middle, and
right mouse buttons.
.SH "MODES"
Cheap Draw has lots of modes: so many that it may be a little confusing at
first.  Take heart, things are not quite as chaotic as they seem.  At the
start of any of the drawing operation, you may select from any of the
three mouse buttons.  The left button is used to select an initial point,
such as the start of a line or the center of a circle.
.sp
The middle button invokes a menu.  The menu is used to change the current mode,
select different drawing options, etc.  Choose an item in the menu by
placing the cursor over that item, and pressing the left mouse button.
For instance, if you're currently drawing lines, but you want to draw
circles, press the middle mouse button.  This will bring up a menu.
Move the cursor until the word "Circle" is highlighted.  Then press
the left button.  The menu will disappear.  The prompt on the bottom of the
screen should indicate that you are now in "Circle" mode.  Each mode is
described in further detail below.
.sp
The right button is used to undo the previous operation.  Suppose you drew
a line, a box, and a circle.  Pressing the right mouse button will make
the circle disappear.  Pressing the button again will make the box disappear.
Press it again, and the line will vanish.  Additional "undo's" will have no
further effect.  Occasionally, the undo operation will leave debris on the
screen.  To fix this, select "Refresh" from the menu.  This will completely
redraw the image on the screen without the debris.  You may wish to remove
a line or circle drawn much earlier.  To remove a single object, see the
"Delete" operation below.
.sp
Most of the draw functions require two, or more selections to complete.
For instance a line requires two selections: one for the starting
point, and one for the end point.  As soon as you select the first point,
the mode (and meaning of the mouse buttons change).  As before, the
left button is used to select the 2nd, or even Nth point.  However,
the middle, or right buttons are now used to cancel the current operation,
or in the case of multi-point operations (like polygon) terminate the
current operation.  Upon completion of the operation, the middle, and right
buttons return to their original meaning.
.sp
When you first use "Cheap Draw" - pay careful attention to the prompts
on the bottom two lines of the screen.  These will inform you of the 
current drawing mode, and meaning of each mouse button.  The drawing
modes are summarized below:
.sp
LINE - Draw a line by picking the two end points.  After choosing the
first end point, a "rubberband" line will be drawn to help you choose
the proper line length.
.sp
C-LINE - Connected line, similar to line, except the starting point of
new segment is the end point of the previous segment.  Select the middle,
or right mouse button to terminate line.
.sp
RAYS - Select a center point, subsequent selections will result in lines
radiating out from common center.  The middle, and right mouse buttons are
used to terminate the current ray.
.sp
BOX - Select one corner - a rubberband box (actually a rectangle)
is drawn to the cursor, and changes size and shape as the cursor
moves.  Select the opposite corner by pressing the left mouse
button.  Cancel the current box by selecting either of the other
two buttons.
.sp
POLYGON - similar to C-LINE, except pressing the middle, or right buttons
will cause "Cheap Draw" to close the polygon by drawing a line between the
first and last points. 
.sp
CIRCLE - select the center, and then any point on the perimeter of the
circle.  Cancel with the middle, or right buttons.
.sp
ELLIPSE - select the top left corner of a bounding box around the
oval.  Then select the bottom right corner.  Cancel with the
middle, or right buttons.
.sp
TEXT - Upon selecting TEXT mode, you will be requested to enter a
text string via a pop-up form.  After entering the string, use the
mouse to choose a starting location for the text on the screen.
Press the left button to actually place the text on the screen.
To repeat the text elsewhere on the screen, reposition the cursor,
and press the left button again.
.sp
MARKER - Use the left button to place a DOT, CROSS, STAR, BOX,
X, or DIAMOND at a specific location.  Cancel with the middle,
or right button.
.sp
LOAD - re-load the file from disk.
.sp
SAVE - save the picture on disk.
.sp
PRINT - print the picture on your printer.  (Further details below).
.sp
OPTIONS - assorted display options. (Further details below).
.sp
REFRESH - redraw the picture on the screen.
.sp
DELETE - upon selecting delete, "Cheap Draw" will draw small boxes
around each stored point in your picture.  To delete an object,
place the cursor within one of these boxes, and depress the left
button.  "Cheap Draw" will search through the set of stored points,
and will delete the first object it finds that includes the
indicated point.  The picture will then be redrawn (sans the
delete object), and "Cheap Draw" will enter "LINE" mode.  Please
note if you choose a point that is shared by two objects, or is
very close to another point, "Cheap Draw" may delete the wrong
object.  Circles are stored as two points, a center point,
and a point on the perimeter.  Boxes are stored as a pair of
two opposite corners.  Ellipses are stored as the top left
and bottom right corners of a bounding box around the ellipse.
.sp
CLEAR - UTTERLY DESTROY CURRENT PICTURE, AND START FROM SCRATCH!
.sp
EXIT - terminate "Cheap Draw", be sure and SAVE your picture first!
.sp
.SH "OPTIONS"
The options menu allows you to change the way some of the above
operations work.  Selecting OPTIONS from the main menu
will invoke the options sub-menu.  You may select an option to
modify by choosing it with the left mouse button.  The middle
button will produce a menu of all possible choices for the
current option.  You may choose from this list with the left mouse
button.  Alternatively, pressing the right mouse button will change
the current option to the next available choice.  By repeatedly
pressing the right button, you can cycle through all the
possible choices.  You may change as many options as you
wish.  When you're finished select OK, and the new options
will take effect.  The list of options is shown below.  Following
each option, in brackets is the set of operations the option
effects.
.sp
TEXT SIZE [ TEXT ] - Change the size of displayed text.  The
range is 1 to 5.
.sp
TEXT ROTATION [ TEXT ] - Display text vertically or horizontally.
.sp
LINE TYPE [ LINE, C-LINE, RAYS ] - Seven line options are available
enabling you to draw solid lines, dotted lines, dashed lines, etc.
.sp
FILL TYPE [ BOX, POLYGON, CIRCLE ] - Draw HOLLOW, SOLID, or HATCH'ed
objects.
.sp
FILL STYLE [ BOX, POLYGON, CIRCLE ] - If the fill type is HATCH, then
the object is filled with a pattern defined by the current fill style.
Six patterns are available.
.sp
GRID [ LINE, C-LINE, RAYS, BOX, POLYGON, CIRCLE, TEXT ] - Two choices
are available for GRID; ON and OFF.  OFF is normal drawing mode.  Turning
GRID ON will cause "Cheap Draw" to display a 32 by 17 array of points
on the screen.  Thereafter (or until GRID is turned off), whenever you
place an object, the selected point will gravitate to the nearest
grid point.  This will enable you to line up objects, draw true 90
degree angles, and ensure that multiple lines come together in a
single point.  REDRAW will redisplay the grid, PRINT will NOT print
the grid.
.sp
GRID SIZE [ GRID ] - COARSE or FINE.  The fine grid 
gives you twice the resolution
of the coarse grid.  However, it draws the same number of grid points 
(otherwise the screen would be too crowded).  The extra (unseen) grid
points are half-way between the drawn points.
.sp
MARKER TYPE [ MARKER ] - Six types of markers are available:
DOT, CROSS, STAR, BOX, X, and DIAMOND.
.SH "PRINTING"
The PRINT operation outputs your drawing to the printer.  The drawing
will be rotated 90 degrees (landscape mode), scaled, and translated
a bit to fit on an 8.5 by 11 page.  Text may appear a little smaller
(proportionately) than on the screen.  I have only tested printing
with a C. ITOH 8510 printer.  This printer is supposed to be identical
to an AT&T 470, so that should work, too.  Other printers are supported
the VDI interface.  For details check your manual entitled "AT&T UNIX
PC Virtual Device Interface".  Additionally, you can print any screen
by typing the <SHIFT> and <Print> keys.  This will produce a small
bit-mapped image of your screen on your printer, thus the PRINT
command will be preferable.
.SH "FILE FORMAT"
"Cheap Draw" pictures are stored as ASCII files.  The first line of
the file contains a two word header - "Cheap Draw".  Following the
header are lines of text created with the TEXT operation.  The
last line of text is followed by a blank line.  Following this
are one or more sets of six numbers:
.sp
	opcode x y arg0 arg1 arg2
.sp
The first number is an opcode indicating some drawing operation (such
as LINE, CIRCLE, etc.).  The last three numbers are arguments used
by the operation.  These are used to store things such as line style,
fill style, etc.  The x, and y  values are the X & Y coordinates
of a particular point.  You can use this information to write
your own programs to perform various transformations on your
pictures.  For example, by modifying only the X & Y values, 
you can rotate, scale, or translate your drawings.  Since this
format is relatively compact, you can also send picture files to
other machines via regular UNIX mail.
.SH "CAVEATS"
The VDI software makes use of certain environmental variables.
For details see the "AT&T UNIX PC Virtual Device Interface" manual,
page 4-10.  I have found that the following lines in my .profile
are sufficient:
.sp
	DISPLAY=pc7300
.br
	export DISPLAY
.br
	. /usr/lib/GSS_Drivers/Environment
.sp
.SH "BUGS"
Currently, "Cheap Draw" limits you to a maximum of 2048 points, and
128 different text strings.  DELETE as described above is a little
quirky.  And then, there's always the bugs, I haven't
found yet.
.SH "AUTHOR"
If you wish to contact me about Cheap Draw:
.sp
.nf
E-mail:		osu-cis!n8emr!ceres!rlp
Snail Mail:	Bob Platt
		738 Whirlaway Court
		Gahanna, Ohio   43230
.fi
!Funky!Stuff!
echo x - example
cat >example <<'!Funky!Stuff!'
Cheap Draw
National
Space
Society
Join the

6 5000 14000 0 1 0
0 5000 11000 0 1 0
3 5000 7000 1 0 0
0 5000 14000 1 0 0
0 6000 14000 1 0 0
0 7000 14000 1 0 0
0 8000 14000 1 0 0
0 4000 14000 1 0 0
0 3000 14000 1 0 0
0 2000 14000 1 0 0
2 2000 14000 1 0 0
0 8000 14000 1 0 0
8 22000 4000 0 1 0
0 22000 12000 0 1 0
0 23000 14000 0 1 0
0 24000 12000 0 1 0
0 24000 4000 0 1 0
0 25000 2000 0 1 0
0 21000 2000 0 1 0
0 22000 4000 0 1 0
2 22000 4000 1 0 0
0 24000 4000 1 0 0
2 22000 11000 1 0 0
0 24000 11000 1 0 0
2 22000 9000 1 0 0
0 24000 9000 1 0 0
2 22000 7000 1 0 0
0 24000 7000 1 0 0
2 22000 5000 1 0 0
0 24000 5000 1 0 0
2 23000 4000 1 0 0
0 23000 11000 1 0 0
1 21000 2000 1 0 0
0 18000 3000 1 0 0
0 19000 5000 1 0 0
0 21000 6000 1 0 0
0 22000 11000 1 0 0
1 25000 2000 1 0 0
0 28000 3000 1 0 0
0 27000 5000 1 0 0
0 25000 6000 1 0 0
0 24000 11000 1 0 0
8 22962 3998 3 4 0
0 22962 1000 3 4 0
0 23153 1999 3 4 0
0 22962 3998 3 4 0
8 22962 3998 3 4 0
0 22771 1999 3 4 0
0 22962 1071 3 4 0
0 22962 3998 3 4 0
8 22914 1999 3 4 0
0 22484 1428 3 4 0
0 23297 1428 3 4 0
0 22914 1999 3 4 0
8 22053 1999 3 4 0
0 21862 1428 3 4 0
0 22531 1428 3 4 0
0 22292 1928 3 4 0
0 22053 1999 3 4 0
8 23488 1928 3 4 0
0 23393 1428 3 4 0
0 23871 1428 3 4 0
0 23727 1928 3 4 0
0 23488 1928 3 4 0
1 22531 1999 1 0 0
0 22531 3356 1 0 0
0 22292 3998 1 0 0
1 23345 1999 1 0 0
0 23345 3284 1 0 0
0 23536 3927 1 0 0
2 18322 3570 1 0 0
0 21670 3570 1 0 0
2 24206 3570 1 0 0
0 27602 3570 1 0 0
2 19518 2571 1 0 0
0 19518 3570 1 0 0
2 26741 2642 1 0 0
0 26741 3570 1 0 0
5 22484 11566 3 4 0
0 22723 11352 3 4 0
5 23153 11566 3 4 0
0 23345 11352 3 4 0
5 3827 7996 1 5 1
0 6219 6783 1 5 1
7 10000 12000 0 2 1
7 10000 10000 1 2 1
7 10000 8000 2 2 1
7 10000 14000 3 1 1
!Funky!Stuff!
echo x - menu.c
cat >menu.c <<'!Funky!Stuff!'
/*
 * Written by R. L. Platt
 */
#include <stdio.h>
#include <tam.h>
#include <message.h>
#include <menu.h>
#include <form.h>
#include "cdraw.h"

#define TEXTSIZ	32
#define OPTSIZ	20
#define OPTNUM	8

static char textbuf[TEXTSIZ+1] = { "" };
static char opt_buf[OPTNUM][OPTSIZ+1] = { "", "", "", "", "", "", "", ""};

mitem_t mode_item[] = {	"Line",		M_MARKED,LINE,
			"C-line",	M_MARKED,CLINE,
			"Rays",		M_MARKED,RAYS,
			"Box",		M_MARKED,BOX,
			"Polygon",	M_MARKED,POLYGON,
			"Circle",	M_MARKED,CIRCLE,
			"Ellipse",	M_MARKED,ELLIPSE,
			"Text",		M_MARKED,TEXT,
			"Marker",	M_MARKED,MARKER,
			"Load",		M_MARKED,LOAD,
			"Save",		M_MARKED,SAVE,
			"Print",	M_MARKED,PRINT,
			"Options",	M_MARKED,OPTIONS,
			"Refresh",	M_MARKED,REFRESH,
			"Delete",	M_MARKED,DELETE,
			"Clear",	M_MARKED,CLEAR,
			"Exit",		M_MARKED,EXIT,
			0,		0,0,
};

menu_t mode_menu = {	"Select:",
			NULL,
			"Choose a mode",
			0, 2, 0, 0,
			M_SINGLE|M_NOMOVE|M_NOHELP|M_NORESIZE,
			{0},
			0,0,0,0,0,
			mode_item,
			mode_item,
			0,
};

field_t textfield[] = { "Text:",	0,
			0,		6,
			TEXTSIZ,	0,
			textbuf,	0,
			"Enter text string",
			0,0,0,0,0,0,0,0,0 
};

form_t textform = { 	"Enter text",
			NULL,
			F_WINSON|F_NOMOVE|F_NOHELP,
			0,
			0,
			textfield,
			textfield
};

mitem_t tsiz_item[] = {	"1",	M_MARKED,1,
			"2",	M_MARKED,2,
			"3",	M_MARKED,3,
			"4",	M_MARKED,4,
			"5",	M_MARKED,5,
			0,	0,0,
};

mitem_t trot_item[] = {	"Horizontal",	M_MARKED,1,
			"Vertical",	M_MARKED,0,
			0,	0,0,
};

mitem_t ltyp_item[] = {	"Solid",	M_MARKED,1,
			"Long dash",	M_MARKED,2,
			"Dotted",	M_MARKED,3,
			"Dash Dotted",	M_MARKED,4,
			"Medium dash",	M_MARKED,5,
			"Dash two dots",M_MARKED,6,
			"Short dash",	M_MARKED,7,
			0,	0,0,
};

mitem_t ftyp_item[] = {	"Hollow",	M_MARKED,0,
			"Solid",	M_MARKED,1,
			"Hatch",	M_MARKED,3,
			0,	0,0,
};

mitem_t fsty_item[] = {	"Narrow diagonal",	M_MARKED,1,
			"Medium diagonal",	M_MARKED,2,
			"Wide diagonal",	M_MARKED,3,
			"Narrow crosshatch",	M_MARKED,4,
			"Medium crosshatch",	M_MARKED,5,
			"Wide crosshatch",	M_MARKED,6,
			0,	0,0,
};

mitem_t grid_item[] = {	"Off",	M_MARKED,0,
			"On",	M_MARKED,1,
			0,	0,0,
};

mitem_t gsiz_item[] = {	"Coarse",	M_MARKED,0,
			"Fine",		M_MARKED,1,
			0,		0,0,
};

mitem_t mtyp_item[] = { "Dot",		M_MARKED,1,
			"Cross",	M_MARKED,2,
			"Star",		M_MARKED,3,
			"Box",		M_MARKED,4,
			"X",		M_MARKED,5,
			"Diamond",	M_MARKED,6,
			0,		0,0,
};

menu_t tsiz_menu = {	"Text size:",
			NULL,
			"Select text size",
			0, 0, 0, 0,
			M_SINGLE|M_NOMOVE|M_NOHELP|M_NORESIZE,
			{0},
			0,0,0,0,0,
			tsiz_item,
			tsiz_item,
			0,
};

menu_t trot_menu = {	"Text rotation:",
			NULL,
			"Select text rotation",
			0, 0, 0, 0,
			M_SINGLE|M_NOMOVE|M_NOHELP|M_NORESIZE,
			{0},
			0,0,0,0,0,
			trot_item,
			trot_item,
			0,
};

menu_t ltyp_menu = {	"Line type:",
			NULL,
			"Select line type",
			0, 0, 0, 0,
			M_SINGLE|M_NOMOVE|M_NOHELP|M_NORESIZE,
			{0},
			0,0,0,0,0,
			ltyp_item,
			ltyp_item,
			0,
};

menu_t ftyp_menu = {	"Fill type:",
			NULL,
			"Select fill type",
			0, 0, 0, 0,
			M_SINGLE|M_NOMOVE|M_NOHELP|M_NORESIZE,
			{0},
			0,0,0,0,0,
			ftyp_item,
			ftyp_item,
			0,
};

menu_t fsty_menu = {	"Fill style:",
			NULL,
			"Select fill style",
			0, 0, 0, 0,
			M_SINGLE|M_NOMOVE|M_NOHELP|M_NORESIZE,
			{0},
			0,0,0,0,0,
			fsty_item,
			fsty_item,
			0,
};

menu_t grid_menu = {	"Grid:",
			NULL,
			"Enable / disable grid",
			0, 0, 0, 0,
			M_SINGLE|M_NOMOVE|M_NOHELP|M_NORESIZE,
			{0},
			0,0,0,0,0,
			grid_item,
			grid_item,
			0,
};

menu_t gsiz_menu = {	"Grid size:",
			NULL,
			"Select grid size",
			0, 0, 0, 0,
			M_SINGLE|M_NOMOVE|M_NOHELP|M_NORESIZE,
			{0},
			0,0,0,0,0,
			gsiz_item,
			gsiz_item,
			0,
};

menu_t mtyp_menu = {	"Marker type:",
			NULL,
			"Select marker type",
			0, 0, 0, 0,
			M_SINGLE|M_NOMOVE|M_NOHELP|M_NORESIZE,
			{0},
			0,0,0,0,0,
			mtyp_item,
			mtyp_item,
			0,
};

field_t opt_fields[] = { "Text size:",	0,
			0,		15,
			OPTSIZ,		F_MONLY,
			opt_buf[0],	&tsiz_menu,
			"Select text size",

			"Text rotation:",1,
			0,		15,
			OPTSIZ,		F_MONLY,
			opt_buf[1],	&trot_menu,
			"Select text rotation",

			"Line type:",	2,
			0,		15,
			OPTSIZ,		F_MONLY,
			opt_buf[2],	&ltyp_menu,
			"Select line type",
			
			"Fill type:",	3,
			0,		15,
			OPTSIZ,		F_MONLY,
			opt_buf[3],	&ftyp_menu,
			"Select fill type",

			"Fill style:",	4,
			0,		15,
			OPTSIZ,		F_MONLY,
			opt_buf[4],	&fsty_menu,
			"Select fill style",

			"Grid:",	5,
			0,		15,
			OPTSIZ,		F_MONLY,
			opt_buf[5],	&grid_menu,
			"Enable / Disable grid",

			"Grid Size:",	6,
			0,		15,
			OPTSIZ,		F_MONLY,
			opt_buf[6],	&gsiz_menu,
			"Select grid size",

			"Marker Type:",	7,
			0,		15,
			OPTSIZ,		F_MONLY,
			opt_buf[7],	&mtyp_menu,
			"Select marker type",

			0,0,0,0,0,0,0,0,0 
};

form_t opt_form = {	"Select option",
			NULL,
			F_WINSON|F_NOMOVE|F_NOHELP,
			0,
			0,
			opt_fields,
			opt_fields,
};

/*
 * make a selection from a menu
 */
int select_mode()
{
	int err, choice;
	menu_t *menup;

	menup = &mode_menu;
	err = menu(menup,(M_BEGIN|M_INPUT|M_DESEL));
	choice = menup->m_curi->mi_val;
	menu(menup,M_END);
	if (err == Enter && choice > 0) return(choice);
	else return(-1);
}

void select_options(attrib)
short *attrib;
{
	int err;

	err = form(&opt_form,(F_BEGIN|F_INPUT|F_END));
	attrib[0] = tsiz_menu.m_curi->mi_val;
	attrib[1] = trot_menu.m_curi->mi_val;
	attrib[2] = ltyp_menu.m_curi->mi_val;
	attrib[3] = ftyp_menu.m_curi->mi_val;
	attrib[4] = fsty_menu.m_curi->mi_val;
	attrib[5] = grid_menu.m_curi->mi_val;
	attrib[6] = gsiz_menu.m_curi->mi_val;
	attrib[7] = mtyp_menu.m_curi->mi_val;
	return;
}

char *get_text()
{
	int err;
	int strlen();
	char *strcpy();
	char *malloc();
	char *p;

	textbuf[0] = 0;
	err = form(&textform,(F_BEGIN|F_INPUT|F_END));
	if (strlen(textbuf) == 0) return(NULL);
	if (err == Enter) {
		if ((p=malloc(strlen(textbuf)+1)) == NULL) return(NULL);
		strcpy(p,textbuf);
		return(p);
	}
	else return(NULL);
}
!Funky!Stuff!
echo x - shrink.sh
cat >shrink.sh <<'!Funky!Stuff!'
# AWK program to scale a "Cheap Draw" image down to 1/2
# size.  To use, type:
#	shrink some_cdraw_file | cdraw -p
# Using a scaling factor greater than 1.0 may cause cdraw
# to drop a core.  (VDI cannot handle coordinates larger
# than 32767).  Also, note the "-p" option is for preview
# mode.  Shrink will not reduce the size of text.
#
# Bob Platt 4/28/89

awk '
BEGIN	{
	label_flag = 1
	xmax = 32767
	ymax = 17704
	sfact = 0.5
}

/^$/	{ label_flag = 0 ; print}

	{ if (label_flag) print
	  else if ($0 != "") {
		x = $2 * sfact
		y = $3 * sfact
		printf("%d %d %d %d %d %d\n",$1,x,y,$4,$5,$6)
	  }
	}
END {
	# Print bounding box
	# printf("%d %d %d %d %d %d\n",8,0,ymax,0,0,0)
	# printf("%d %d %d %d %d %d\n",0,xmax/2,ymax,0,0,0)
	# printf("%d %d %d %d %d %d\n",0,xmax/2,ymax/2,0,0,0)
	# printf("%d %d %d %d %d %d\n",0,0,ymax/2,0,0,0)
	# printf("%d %d %d %d %d %d\n",0,0,ymax,0,0,0)
}
' $*
!Funky!Stuff!
: End of shell archive 



More information about the Unix-pc.sources mailing list