xref: /plan9/sys/src/cmd/gview.c (revision 162f803d803e7af3469d29bde78fb975a68a2888)
196e091f8SDavid du Colombier #include	<u.h>
296e091f8SDavid du Colombier #include	<libc.h>
396e091f8SDavid du Colombier #include	<ctype.h>
496e091f8SDavid du Colombier #include	<draw.h>
596e091f8SDavid du Colombier #include	<event.h>
696e091f8SDavid du Colombier #include	<cursor.h>
796e091f8SDavid du Colombier #include	<stdio.h>
896e091f8SDavid du Colombier 
996e091f8SDavid du Colombier #define Never	0xffffffff	/* Maximum ulong */
1096e091f8SDavid du Colombier #define LOG2  0.301029995664
1196e091f8SDavid du Colombier #define Button_bit(b)	(1 << ((b)-1))
1296e091f8SDavid du Colombier 
1396e091f8SDavid du Colombier enum {
1496e091f8SDavid du Colombier 	But1	= Button_bit(1),/* mouse buttons for events */
1596e091f8SDavid du Colombier 	But2	= Button_bit(2),
1696e091f8SDavid du Colombier 	But3	= Button_bit(3),
1796e091f8SDavid du Colombier };
1896e091f8SDavid du Colombier int cantmv = 1;			/* disallow rotate and move? 0..1 */
198db68488SDavid du Colombier int plotdots;			/* plot dots instead of lines */
2096e091f8SDavid du Colombier int top_border, bot_border, lft_border, rt_border;
2196e091f8SDavid du Colombier int lft_border0;		/* lft_border for y-axis labels >0 */
2296e091f8SDavid du Colombier int top_left, top_right;	/* edges of top line free space */
2396e091f8SDavid du Colombier int Mv_delay = 400;		/* msec for button click vs. button hold down */
2496e091f8SDavid du Colombier int Dotrad = 2;			/* dot radius in pixels */
2596e091f8SDavid du Colombier int framewd=1;			/* line thickness for frame (pixels) */
2696e091f8SDavid du Colombier int framesep=1;			/* distance between frame and surrounding text */
2796e091f8SDavid du Colombier int outersep=1;			/* distance: surrounding text to screen edge */
2896e091f8SDavid du Colombier Point sdigit;			/* size of a digit in the font */
2996e091f8SDavid du Colombier Point smaxch;			/* assume any character in font fits in this */
3096e091f8SDavid du Colombier double underscan = .05;		/* fraction of frame initially unused per side */
3196e091f8SDavid du Colombier double fuzz = 6;		/* selection tolerance in pixels */
3296e091f8SDavid du Colombier int tick_len = 15;		/* length of axis label tick mark in pixels */
3396e091f8SDavid du Colombier FILE* logfil = 0;		/* dump selected points here if nonzero */
3496e091f8SDavid du Colombier 
3596e091f8SDavid du Colombier #define labdigs  3		/* allow this many sig digits in axis labels */
3696e091f8SDavid du Colombier #define digs10pow 1000		/* pow(10,labdigs) */
3796e091f8SDavid du Colombier #define axis_color  clr_im(DLtblue)
3896e091f8SDavid du Colombier 
3996e091f8SDavid du Colombier 
4096e091f8SDavid du Colombier 
4196e091f8SDavid du Colombier 
4296e091f8SDavid du Colombier /********************************* Utilities  *********************************/
4396e091f8SDavid du Colombier 
4496e091f8SDavid du Colombier /* Prepend string s to null-terminated string in n-byte buffer buf[], truncating if
4596e091f8SDavid du Colombier    necessary and using a space to separate s from the rest of buf[].
4696e091f8SDavid du Colombier */
str_insert(char * buf,char * s,int n)4796e091f8SDavid du Colombier char* str_insert(char* buf, char* s, int n)
4896e091f8SDavid du Colombier {
4996e091f8SDavid du Colombier 	int blen, slen = strlen(s) + 1;
5096e091f8SDavid du Colombier 	if (slen >= n)
5196e091f8SDavid du Colombier 		{strncpy(buf,s,n); buf[n-1]='\0'; return buf;}
5296e091f8SDavid du Colombier 	blen = strlen(buf);
5396e091f8SDavid du Colombier 	if (blen >= n-slen)
5496e091f8SDavid du Colombier 		buf[blen=n-slen-1] = '\0';
5596e091f8SDavid du Colombier 	memmove(buf+slen, buf, slen+blen+1);
5696e091f8SDavid du Colombier 	memcpy(buf, s, slen-1);
5796e091f8SDavid du Colombier 	buf[slen-1] = ' ';
5896e091f8SDavid du Colombier 	return buf;
5996e091f8SDavid du Colombier }
6096e091f8SDavid du Colombier 
6196e091f8SDavid du Colombier /* Alter string smain (without lengthening it) so as to remove the first occurrence of
6296e091f8SDavid du Colombier    ssub, assuming ssub is ASCII.  Return nonzero (true) if string smain had to be changed.
6396e091f8SDavid du Colombier    In spite of the ASCII-centric appearance, I think this can handle UTF in smain.
6496e091f8SDavid du Colombier */
remove_substr(char * smain,char * ssub)6596e091f8SDavid du Colombier int remove_substr(char* smain, char* ssub)
6696e091f8SDavid du Colombier {
6796e091f8SDavid du Colombier 	char *ss, *s = strstr(smain, ssub);
6896e091f8SDavid du Colombier 	int n = strlen(ssub);
6996e091f8SDavid du Colombier 	if (s==0)
7096e091f8SDavid du Colombier 		return 0;
7196e091f8SDavid du Colombier 	if (islower(s[n]))
7296e091f8SDavid du Colombier 		s[0] ^= 32;			/* probably tolower(s[0]) or toupper(s[0]) */
7396e091f8SDavid du Colombier 	else {
7496e091f8SDavid du Colombier 		for (ss=s+n; *ss!=0; s++, ss++)
7596e091f8SDavid du Colombier 			*s = *ss;
7696e091f8SDavid du Colombier 		*s = '\0';
7796e091f8SDavid du Colombier 	}
7896e091f8SDavid du Colombier 	return 1;
7996e091f8SDavid du Colombier }
8096e091f8SDavid du Colombier 
adjust_border(Font * f)8196e091f8SDavid du Colombier void adjust_border(Font* f)
8296e091f8SDavid du Colombier {
8396e091f8SDavid du Colombier 	int sep = framesep + outersep;
8496e091f8SDavid du Colombier 	sdigit = stringsize(f, "8");
8596e091f8SDavid du Colombier 	smaxch = stringsize(f, "MMMg");
8696e091f8SDavid du Colombier 	smaxch.x = (smaxch.x + 3)/4;
8796e091f8SDavid du Colombier 	lft_border0 = (1+labdigs)*sdigit.x + framewd + sep;
8896e091f8SDavid du Colombier 	rt_border = (lft_border0 - sep)/2 + outersep;
8996e091f8SDavid du Colombier 	bot_border = sdigit.y + framewd + sep;
9096e091f8SDavid du Colombier 	top_border = smaxch.y + framewd + sep;
9196e091f8SDavid du Colombier 	lft_border = lft_border0;		/* this gets reset later */
9296e091f8SDavid du Colombier }
9396e091f8SDavid du Colombier 
9496e091f8SDavid du Colombier 
is_off_screen(Point p)9596e091f8SDavid du Colombier int is_off_screen(Point p)
9696e091f8SDavid du Colombier {
9796e091f8SDavid du Colombier 	const Rectangle* r = &(screen->r);
9896e091f8SDavid du Colombier 	return p.x-r->min.x<lft_border || r->max.x-p.x<rt_border
9996e091f8SDavid du Colombier 		|| p.y-r->min.y<=top_border || r->max.y-p.y<=bot_border;
10096e091f8SDavid du Colombier }
10196e091f8SDavid du Colombier 
10296e091f8SDavid du Colombier 
10396e091f8SDavid du Colombier Cursor	bullseye =
10496e091f8SDavid du Colombier {
10596e091f8SDavid du Colombier 	{-7, -7},
10696e091f8SDavid du Colombier 	{
10796e091f8SDavid du Colombier 		0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF,
10896e091f8SDavid du Colombier 	 	0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF,
10996e091f8SDavid du Colombier 	 	0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF,
11096e091f8SDavid du Colombier 	 	0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8,
11196e091f8SDavid du Colombier 	},
11296e091f8SDavid du Colombier 	{
11396e091f8SDavid du Colombier 		0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84,
11496e091f8SDavid du Colombier 		0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE,
11596e091f8SDavid du Colombier 		0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82,
11696e091f8SDavid du Colombier 		0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00,
11796e091f8SDavid du Colombier 	}
11896e091f8SDavid du Colombier };
11996e091f8SDavid du Colombier 
1200c6300e7SDavid du Colombier /* Wait for a mouse click and return 0 for failue if not button but (curs can be 0) */
get_1click(int but,Mouse * m,Cursor * curs)12196e091f8SDavid du Colombier int get_1click(int but, Mouse* m, Cursor* curs)
12296e091f8SDavid du Colombier {
12396e091f8SDavid du Colombier 	if (curs)
12496e091f8SDavid du Colombier 		esetcursor(curs);
12596e091f8SDavid du Colombier 	while (m->buttons==0)
12696e091f8SDavid du Colombier 		*m = emouse();
12796e091f8SDavid du Colombier 	if (curs)
12896e091f8SDavid du Colombier 		esetcursor(0);
12996e091f8SDavid du Colombier 	return (m->buttons==Button_bit(but));
13096e091f8SDavid du Colombier }
13196e091f8SDavid du Colombier 
13296e091f8SDavid du Colombier 
1330c6300e7SDavid du Colombier /* Wait for a mouse click or keyboard event from the string of expected characters.  Return
1340c6300e7SDavid du Colombier    the character code or -1 for a button-but mouse event or 0 for wrong button.
1350c6300e7SDavid du Colombier */
get_click_or_kbd(int but,Mouse * m,const char * expected)1360c6300e7SDavid du Colombier int get_click_or_kbd(int but, Mouse* m, const char* expected)
1370c6300e7SDavid du Colombier {
1380c6300e7SDavid du Colombier 	Event ev;
1390c6300e7SDavid du Colombier 	ulong expbits[4], ty;
1400c6300e7SDavid du Colombier 	expbits[0] = expbits[1] = expbits[2] = expbits[3];
1410c6300e7SDavid du Colombier 	for (; *expected!=0; expected++)
1420c6300e7SDavid du Colombier 		expbits[((*expected)>>5)&3] |= 1 << (*expected&31);
1430c6300e7SDavid du Colombier 	do ty = eread(Emouse|Ekeyboard, &ev);
1440c6300e7SDavid du Colombier 	while ((ty&Emouse) ? ev.mouse.buttons==0
1450c6300e7SDavid du Colombier 		: (ev.kbdc&~127) || !(expbits[(ev.kbdc>>5)&3] & (1<<(ev.kbdc&31))) );
1460c6300e7SDavid du Colombier 	if (ty&Ekeyboard)
1470c6300e7SDavid du Colombier 		return ev.kbdc;
1480c6300e7SDavid du Colombier 	*m = ev.mouse;
1490c6300e7SDavid du Colombier 	return (ev.mouse.buttons==Button_bit(but)) ? -1 : 0;
1500c6300e7SDavid du Colombier }
1510c6300e7SDavid du Colombier 
1520c6300e7SDavid du Colombier 
15396e091f8SDavid du Colombier /* Wait until but goes up or until a mouse event's msec passes tlimit.
15496e091f8SDavid du Colombier    Return a boolean result that tells whether the button went up.
15596e091f8SDavid du Colombier */
lift_button(int but,Mouse * m,int tlimit)15696e091f8SDavid du Colombier int lift_button(int but, Mouse* m, int tlimit)
15796e091f8SDavid du Colombier {
15896e091f8SDavid du Colombier 	do {	*m = emouse();
15996e091f8SDavid du Colombier 		if (m->msec >= tlimit)
16096e091f8SDavid du Colombier 			return 0;
16196e091f8SDavid du Colombier 	} while (m->buttons & Button_bit(but));
16296e091f8SDavid du Colombier 	return 1;
16396e091f8SDavid du Colombier }
16496e091f8SDavid du Colombier 
16596e091f8SDavid du Colombier 
16696e091f8SDavid du Colombier /* Set *m to the last pending mouse event, or the first one where but is up.
16796e091f8SDavid du Colombier    If no mouse events are pending, wait for the next one.
16896e091f8SDavid du Colombier */
latest_mouse(int but,Mouse * m)16996e091f8SDavid du Colombier void latest_mouse(int but, Mouse* m)
17096e091f8SDavid du Colombier {
17196e091f8SDavid du Colombier 	int bbit = Button_bit(but);
17296e091f8SDavid du Colombier 	do {	*m = emouse();
17396e091f8SDavid du Colombier 	} while ((m->buttons & bbit) && ecanmouse());
17496e091f8SDavid du Colombier }
17596e091f8SDavid du Colombier 
17696e091f8SDavid du Colombier 
17796e091f8SDavid du Colombier 
17896e091f8SDavid du Colombier /*********************************** Colors ***********************************/
17996e091f8SDavid du Colombier 
18096e091f8SDavid du Colombier enum {	DOrange=0xffaa00FF, Dgray=0xbbbbbbFF, DDkgreen=0x009900FF,
18196e091f8SDavid du Colombier 	DDkred=0xcc0000FF, DViolet=0x990099FF, DDkyellow=0xaaaa00FF,
18296e091f8SDavid du Colombier 	DLtblue=0xaaaaffFF, DPink=0xffaaaaFF,
18396e091f8SDavid du Colombier 	/* ndraw.h sets DBlack, DBlue, DRed, DYellow, DGreen,
18496e091f8SDavid du Colombier 		DCyan, DMagenta, DWhite */
18596e091f8SDavid du Colombier };
18696e091f8SDavid du Colombier 
1870c6300e7SDavid du Colombier 
1880c6300e7SDavid du Colombier typedef struct thick_color {
1890c6300e7SDavid du Colombier 	int thick;			/* use 1+2*thick pixel wide lines */
1900c6300e7SDavid du Colombier 	Image* clr;			/* Color to use when drawing this */
1910c6300e7SDavid du Colombier } thick_color;
1920c6300e7SDavid du Colombier 
1930c6300e7SDavid du Colombier 
19496e091f8SDavid du Colombier typedef struct color_ref {
19596e091f8SDavid du Colombier 	ulong c;			/* RGBA pixel color */
19696e091f8SDavid du Colombier 	char* nam;			/* ASCII name (matched to input, used in output)*/
1970c6300e7SDavid du Colombier 	int nam1;			/* single-letter version of color name */
19896e091f8SDavid du Colombier 	Image* im;			/* replicated solid-color image */
19996e091f8SDavid du Colombier } color_ref;
20096e091f8SDavid du Colombier 
20196e091f8SDavid du Colombier color_ref clrtab[] = {
2020c6300e7SDavid du Colombier 	DRed,		"Red",		'R', 0,
2030c6300e7SDavid du Colombier 	DPink,		"Pink",		'P', 0,
2040c6300e7SDavid du Colombier 	DDkred,		"Dkred",	'r', 0,
2050c6300e7SDavid du Colombier 	DOrange,	"Orange",	'O', 0,
2060c6300e7SDavid du Colombier 	DYellow,	"Yellow",	'Y', 0,
2070c6300e7SDavid du Colombier 	DDkyellow,	"Dkyellow",	'y', 0,
2080c6300e7SDavid du Colombier 	DGreen,		"Green",	'G', 0,
2090c6300e7SDavid du Colombier 	DDkgreen,	"Dkgreen",	'g', 0,
2100c6300e7SDavid du Colombier 	DCyan,		"Cyan",		'C', 0,
2110c6300e7SDavid du Colombier 	DBlue,		"Blue",		'B', 0,
2120c6300e7SDavid du Colombier 	DLtblue,	"Ltblue",	'b', 0,
2130c6300e7SDavid du Colombier 	DMagenta,	"Magenta",	'M', 0,
2140c6300e7SDavid du Colombier 	DViolet,	"Violet",	'V', 0,
2150c6300e7SDavid du Colombier 	Dgray,		"Gray",		'A', 0,
2160c6300e7SDavid du Colombier 	DBlack,		"Black",	'K', 0,
2170c6300e7SDavid du Colombier 	DWhite,		"White",	'W', 0,
2180c6300e7SDavid du Colombier 	DNofill,	0,		0,   0	/* DNofill means "end of data" */
21996e091f8SDavid du Colombier };
22096e091f8SDavid du Colombier 
2210c6300e7SDavid du Colombier short nam1_idx[128];			/* the clrtab[] index for each nam1, else -1 */
2220c6300e7SDavid du Colombier 
22396e091f8SDavid du Colombier 
init_clrtab(void)22496e091f8SDavid du Colombier void  init_clrtab(void)
22596e091f8SDavid du Colombier {
22696e091f8SDavid du Colombier 	int i;
22796e091f8SDavid du Colombier 	Rectangle r = Rect(0,0,1,1);
2280c6300e7SDavid du Colombier 	memset(&nam1_idx[0], -1, sizeof(nam1_idx));
2290c6300e7SDavid du Colombier 	for (i=0; clrtab[i].c!=DNofill; i++) {
23096e091f8SDavid du Colombier 		clrtab[i].im = allocimage(display, r, CMAP8, 1, clrtab[i].c);
23196e091f8SDavid du Colombier 		/* should check for 0 result? */
2320c6300e7SDavid du Colombier 		nam1_idx[clrtab[i].nam1] = i;
2330c6300e7SDavid du Colombier 	}
23496e091f8SDavid du Colombier }
23596e091f8SDavid du Colombier 
23696e091f8SDavid du Colombier 
clrim_id(Image * clr)23796e091f8SDavid du Colombier int clrim_id(Image* clr)
23896e091f8SDavid du Colombier {
23996e091f8SDavid du Colombier 	int i;
24096e091f8SDavid du Colombier 	for (i=0; clrtab[i].im!=clr; i++)
24196e091f8SDavid du Colombier 		if (clrtab[i].c==DNofill)
24296e091f8SDavid du Colombier 			exits("bad image color");
24396e091f8SDavid du Colombier 	return i;
24496e091f8SDavid du Colombier }
24596e091f8SDavid du Colombier 
clr_id(int clr)24696e091f8SDavid du Colombier int clr_id(int clr)
24796e091f8SDavid du Colombier {
24896e091f8SDavid du Colombier 	int i;
24996e091f8SDavid du Colombier 	for (i=0; clrtab[i].c!=clr; i++)
25096e091f8SDavid du Colombier 		if (clrtab[i].c==DNofill)
25196e091f8SDavid du Colombier 			exits("bad color");
25296e091f8SDavid du Colombier 	return i;
25396e091f8SDavid du Colombier }
25496e091f8SDavid du Colombier 
2550c6300e7SDavid du Colombier 
25696e091f8SDavid du Colombier #define clr_im(clr)	clrtab[clr_id(clr)].im
2570c6300e7SDavid du Colombier #define is_Multi  -2			/* dummy clrtab[] less than -1 */
25896e091f8SDavid du Colombier 
25996e091f8SDavid du Colombier 
tc_default(thick_color * buf)2600c6300e7SDavid du Colombier thick_color* tc_default(thick_color *buf)
26196e091f8SDavid du Colombier {
2620c6300e7SDavid du Colombier 	buf[0].thick = 1;
2630c6300e7SDavid du Colombier 	buf[1].clr = clr_im(DBlack);
2640c6300e7SDavid du Colombier 	buf[1].thick = 0;
2650c6300e7SDavid du Colombier 	return buf;
2660c6300e7SDavid du Colombier }
2670c6300e7SDavid du Colombier 
2680c6300e7SDavid du Colombier 
2690c6300e7SDavid du Colombier /* Return an allocated array that describes the color letters (clrtab[].nam1 values,
2700c6300e7SDavid du Colombier    optionally prefixed by 'T') in the string that starts at c0 and ends just before
2710c6300e7SDavid du Colombier    fin.  The first entry is a dummy whose thick field tells how many data entries follow.
2720c6300e7SDavid du Colombier    If buf!=0, it should point to an array of length 2 that is to hold the output
2730c6300e7SDavid du Colombier    (truncated to a dummy and one data entry).  The error indication is 1 data entry
2740c6300e7SDavid du Colombier    of default color and thickness; e.g., "Multi(xxbadxx)" in a label prevents gview
2750c6300e7SDavid du Colombier    from recognizing anything that follows.
2760c6300e7SDavid du Colombier */
parse_color_chars(const char * c0,const char * fin,thick_color * buf)2770c6300e7SDavid du Colombier thick_color* parse_color_chars(const char* c0, const char* fin, thick_color *buf)
2780c6300e7SDavid du Colombier {
2790c6300e7SDavid du Colombier 	thick_color* tc;		/* Pending return value */
2800c6300e7SDavid du Colombier 	int i, j, n=fin-c0;		/* n is an upper bound on how many data members */
2810c6300e7SDavid du Colombier 	const char* c;
2820c6300e7SDavid du Colombier 	for (c=c0; c<fin-1; c++)
2830c6300e7SDavid du Colombier 		if (*c=='T')
2840c6300e7SDavid du Colombier 			n--;
2850c6300e7SDavid du Colombier 	if (buf==0)
2860c6300e7SDavid du Colombier 		tc = (thick_color*) malloc((n+1)*sizeof(thick_color));
2870c6300e7SDavid du Colombier 	else {tc=buf; n=1;}
2880c6300e7SDavid du Colombier 	i = 0;
2890c6300e7SDavid du Colombier 	for (c=c0; c<fin && i<n; c++) {
2900c6300e7SDavid du Colombier 		tc[++i].thick = 0;
2910c6300e7SDavid du Colombier 		if (*c=='T')
2920c6300e7SDavid du Colombier 			if (++c==fin)
2930c6300e7SDavid du Colombier 				return tc_default(tc);
2940c6300e7SDavid du Colombier 			else tc[i].thick=1;
2950c6300e7SDavid du Colombier 		j = (*c&~127) ? -1 : nam1_idx[*c];
2960c6300e7SDavid du Colombier 		if (j < 0)
2970c6300e7SDavid du Colombier 			return tc_default(tc);
2980c6300e7SDavid du Colombier 		tc[i].clr = clrtab[j].im;
2990c6300e7SDavid du Colombier 	}
3000c6300e7SDavid du Colombier 	tc[0].thick = i;
3010c6300e7SDavid du Colombier 	return tc;
3020c6300e7SDavid du Colombier }
3030c6300e7SDavid du Colombier 
3040c6300e7SDavid du Colombier 
3050c6300e7SDavid du Colombier /* Decide what color and thickness to use for a polyline based on the label it has in the
3060c6300e7SDavid du Colombier    input file.  The winner is whichever color name comes first, or otherwise black; and
3070c6300e7SDavid du Colombier    thickness is determined by the presence of "Thick" in the string.  Place the result
3080c6300e7SDavid du Colombier    in *r1 and return 0 unless a Multi(...) spec is found, in which case the result is
3090c6300e7SDavid du Colombier    an allocated array of alternative color and thickness values.  A nonzero idxdest
3100c6300e7SDavid du Colombier    requests the clrtab[] index in *idxdest and no allocated array.
3110c6300e7SDavid du Colombier */
nam2thclr(const char * nam,thick_color * r1,int * idxdest)3120c6300e7SDavid du Colombier thick_color* nam2thclr(const char* nam, thick_color *r1, int *idxdest)
3130c6300e7SDavid du Colombier {
3140c6300e7SDavid du Colombier 	char *c, *cbest=0, *rp=0;
31596e091f8SDavid du Colombier 	int i, ibest=-1;
3160c6300e7SDavid du Colombier 	thick_color* tc = 0;
3170c6300e7SDavid du Colombier 	thick_color buf[2];
3180c6300e7SDavid du Colombier 	if (*nam!=0) {
3190c6300e7SDavid du Colombier 		c = strstr(nam, "Multi(");
3200c6300e7SDavid du Colombier 		if (c!=0 && (rp=strchr(c+6,')'))!=0)
3210c6300e7SDavid du Colombier 			{ibest=is_Multi; cbest=c;}
32296e091f8SDavid du Colombier 		for (i=0; clrtab[i].nam!=0; i++) {
32396e091f8SDavid du Colombier 			c = strstr(nam,clrtab[i].nam);
3240c6300e7SDavid du Colombier 			if (c!=0 && (ibest==-1 || c<cbest))
32596e091f8SDavid du Colombier 				{ibest=i; cbest=c;}
32696e091f8SDavid du Colombier 		}
3270c6300e7SDavid du Colombier 	}
3280c6300e7SDavid du Colombier 	if (ibest==is_Multi) {
3290c6300e7SDavid du Colombier 		tc = parse_color_chars(cbest+6, rp, (idxdest==0 ? 0 : &buf[0]));
3300c6300e7SDavid du Colombier 		ibest = clrim_id(tc[1].clr);
3310c6300e7SDavid du Colombier 	}
33296e091f8SDavid du Colombier 	if (idxdest!=0)
33396e091f8SDavid du Colombier 		*idxdest = (ibest<0) ? clr_id(DBlack) : ibest;
3340c6300e7SDavid du Colombier 	r1->clr = (ibest<0) ? clr_im(DBlack) : clrtab[ibest].im;
3350c6300e7SDavid du Colombier 	r1->thick = (tc!=0) ? tc[1].thick : (strstr(nam,"Thick")==0 ? 0 : 1);
3360c6300e7SDavid du Colombier 	return tc;
33796e091f8SDavid du Colombier }
33896e091f8SDavid du Colombier 
33996e091f8SDavid du Colombier 
3400c6300e7SDavid du Colombier /* Alter string nam so that nam2thick() and nam2clr() agree with *tc, using
34196e091f8SDavid du Colombier    buf[] (a buffer of length bufn) to store the result if it differs from nam.
34296e091f8SDavid du Colombier    We go to great pains to perform this alteration in a manner that will seem natural
34396e091f8SDavid du Colombier    to the user, i.e., we try removing a suitably isolated color name before inserting
34496e091f8SDavid du Colombier    a new one.
34596e091f8SDavid du Colombier */
nam_with_thclr(char * nam,const thick_color * tc,char * buf,int bufn)3460c6300e7SDavid du Colombier char* nam_with_thclr(char* nam, const thick_color *tc, char* buf, int bufn)
34796e091f8SDavid du Colombier {
3480c6300e7SDavid du Colombier 	thick_color c0;
3490c6300e7SDavid du Colombier 	int clr0i;
3500c6300e7SDavid du Colombier 	nam2thclr(nam, &c0, &clr0i);
35196e091f8SDavid du Colombier 	char *clr0s;
3520c6300e7SDavid du Colombier 	if (c0.thick==tc->thick && c0.clr==tc->clr)
35396e091f8SDavid du Colombier 		return nam;
35496e091f8SDavid du Colombier 	clr0s = clrtab[clr0i].nam;
35596e091f8SDavid du Colombier 	if (strlen(nam)<bufn) strcpy(buf,nam);
35696e091f8SDavid du Colombier 	else {strncpy(buf,nam,bufn); buf[bufn-1]='\0';}
3570c6300e7SDavid du Colombier 	if (c0.clr != tc->clr)
35896e091f8SDavid du Colombier 		remove_substr(buf, clr0s);
3590c6300e7SDavid du Colombier 	if (c0.thick > tc->thick)
36096e091f8SDavid du Colombier 		while (remove_substr(buf, "Thick"))
36196e091f8SDavid du Colombier 			/* do nothing */;
3620c6300e7SDavid du Colombier 	nam2thclr(nam, &c0, &clr0i);
3630c6300e7SDavid du Colombier 	if (c0.clr != tc->clr)
3640c6300e7SDavid du Colombier 		str_insert(buf, clrtab[clrim_id(tc->clr)].nam, bufn);
3650c6300e7SDavid du Colombier 	if (c0.thick < tc->thick)
36696e091f8SDavid du Colombier 		str_insert(buf, "Thick", bufn);
36796e091f8SDavid du Colombier 	return buf;
36896e091f8SDavid du Colombier }
36996e091f8SDavid du Colombier 
37096e091f8SDavid du Colombier 
37196e091f8SDavid du Colombier 
37296e091f8SDavid du Colombier /****************************** Data structures  ******************************/
37396e091f8SDavid du Colombier 
37496e091f8SDavid du Colombier Image* mv_bkgd;				/* Background image (usually 0) */
37596e091f8SDavid du Colombier 
37696e091f8SDavid du Colombier typedef struct fpoint {
37796e091f8SDavid du Colombier 	double x, y;
37896e091f8SDavid du Colombier } fpoint;
37996e091f8SDavid du Colombier 
38096e091f8SDavid du Colombier typedef struct frectangle {
38196e091f8SDavid du Colombier 	fpoint min, max;
38296e091f8SDavid du Colombier } frectangle;
38396e091f8SDavid du Colombier 
38496e091f8SDavid du Colombier frectangle empty_frect = {1e30, 1e30, -1e30, -1e30};
38596e091f8SDavid du Colombier 
38696e091f8SDavid du Colombier 
38796e091f8SDavid du Colombier /* When *r2 is transformed by y=y-x*slant, might it intersect *r1 ?
38896e091f8SDavid du Colombier */
fintersects(const frectangle * r1,const frectangle * r2,double slant)38996e091f8SDavid du Colombier int fintersects(const frectangle* r1, const frectangle* r2, double slant)
39096e091f8SDavid du Colombier {
39196e091f8SDavid du Colombier 	double x2min=r2->min.x, x2max=r2->max.x;
39296e091f8SDavid du Colombier 	if (r1->max.x <= x2min || x2max <= r1->min.x)
39396e091f8SDavid du Colombier 		return 0;
39496e091f8SDavid du Colombier 	if (slant >=0)
39596e091f8SDavid du Colombier 		{x2min*=slant; x2max*=slant;}
39696e091f8SDavid du Colombier 	else	{double t=x2min*slant; x2min=x2max*slant; x2max=t;}
39796e091f8SDavid du Colombier 	return r1->max.y > r2->min.y-x2max && r2->max.y-x2min > r1->min.y;
39896e091f8SDavid du Colombier }
39996e091f8SDavid du Colombier 
fcontains(const frectangle * r,fpoint p)40096e091f8SDavid du Colombier int fcontains(const frectangle* r, fpoint p)
40196e091f8SDavid du Colombier {
40296e091f8SDavid du Colombier 	return r->min.x <=p.x && p.x<= r->max.x && r->min.y <=p.y && p.y<= r->max.y;
40396e091f8SDavid du Colombier }
40496e091f8SDavid du Colombier 
40596e091f8SDavid du Colombier 
grow_bb(frectangle * dest,const frectangle * r)40696e091f8SDavid du Colombier void grow_bb(frectangle* dest, const frectangle* r)
40796e091f8SDavid du Colombier {
40896e091f8SDavid du Colombier 	if (r->min.x < dest->min.x) dest->min.x=r->min.x;
40996e091f8SDavid du Colombier 	if (r->min.y < dest->min.y) dest->min.y=r->min.y;
41096e091f8SDavid du Colombier 	if (r->max.x > dest->max.x) dest->max.x=r->max.x;
41196e091f8SDavid du Colombier 	if (r->max.y > dest->max.y) dest->max.y=r->max.y;
41296e091f8SDavid du Colombier }
41396e091f8SDavid du Colombier 
41496e091f8SDavid du Colombier 
slant_frect(frectangle * r,double sl)41596e091f8SDavid du Colombier void slant_frect(frectangle *r, double sl)
41696e091f8SDavid du Colombier {
41796e091f8SDavid du Colombier 	r->min.y += sl*r->min.x;
41896e091f8SDavid du Colombier 	r->max.y += sl*r->max.x;
41996e091f8SDavid du Colombier }
42096e091f8SDavid du Colombier 
42196e091f8SDavid du Colombier 
fcenter(const frectangle * r)42296e091f8SDavid du Colombier fpoint fcenter(const frectangle* r)
42396e091f8SDavid du Colombier {
42496e091f8SDavid du Colombier 	fpoint c;
42596e091f8SDavid du Colombier 	c.x = .5*(r->max.x + r->min.x);
42696e091f8SDavid du Colombier 	c.y = .5*(r->max.y + r->min.y);
42796e091f8SDavid du Colombier 	return c;
42896e091f8SDavid du Colombier }
42996e091f8SDavid du Colombier 
43096e091f8SDavid du Colombier 
43196e091f8SDavid du Colombier typedef struct fpolygon {
43296e091f8SDavid du Colombier 	fpoint* p;			/* a malloc'ed array */
43396e091f8SDavid du Colombier 	int n;				/* p[] has n elements: p[0..n] */
43496e091f8SDavid du Colombier 	frectangle bb;			/* bounding box */
43596e091f8SDavid du Colombier 	char* nam;			/* name of this polygon (malloc'ed) */
4360c6300e7SDavid du Colombier 	thick_color c;			/* the current color and line thickness */
4370c6300e7SDavid du Colombier 	thick_color* ct;		/* 0 or malloc'ed color schemes, ct[1..ct->thick] */
43896e091f8SDavid du Colombier 	struct fpolygon* link;
43996e091f8SDavid du Colombier } fpolygon;
44096e091f8SDavid du Colombier 
44196e091f8SDavid du Colombier typedef struct fpolygons {
44296e091f8SDavid du Colombier 	fpolygon* p;			/* the head of a linked list */
44396e091f8SDavid du Colombier 	frectangle bb;			/* overall bounding box */
44496e091f8SDavid du Colombier 	frectangle disp;		/* part being mapped onto screen->r */
44596e091f8SDavid du Colombier 	double slant_ht;		/* controls how disp is slanted */
44696e091f8SDavid du Colombier } fpolygons;
44796e091f8SDavid du Colombier 
44896e091f8SDavid du Colombier 
44996e091f8SDavid du Colombier fpolygons univ = {			/* everything there is to display */
45096e091f8SDavid du Colombier 	0,
45196e091f8SDavid du Colombier 	1e30, 1e30, -1e30, -1e30,
45296e091f8SDavid du Colombier 	0, 0, 0, 0,
45396e091f8SDavid du Colombier 	2*1e30
45496e091f8SDavid du Colombier };
45596e091f8SDavid du Colombier 
45696e091f8SDavid du Colombier 
free_fp_etc(fpolygon * fp)4570c6300e7SDavid du Colombier void free_fp_etc(fpolygon* fp)
4580c6300e7SDavid du Colombier {
4590c6300e7SDavid du Colombier 	if (fp->ct != 0)
4600c6300e7SDavid du Colombier 		free(fp->ct);
4610c6300e7SDavid du Colombier 	free(fp->p);
4620c6300e7SDavid du Colombier 	free(fp);
4630c6300e7SDavid du Colombier }
4640c6300e7SDavid du Colombier 
4650c6300e7SDavid du Colombier 
set_default_clrs(fpolygons * fps,fpolygon * fpstop)46696e091f8SDavid du Colombier void set_default_clrs(fpolygons* fps, fpolygon* fpstop)
46796e091f8SDavid du Colombier {
46896e091f8SDavid du Colombier 	fpolygon* fp;
4690c6300e7SDavid du Colombier 	for (fp=fps->p; fp!=0 && fp!=fpstop; fp=fp->link)
4700c6300e7SDavid du Colombier 		fp->ct = nam2thclr(fp->nam, &fp->c, 0);
47196e091f8SDavid du Colombier }
47296e091f8SDavid du Colombier 
47396e091f8SDavid du Colombier 
fps_invert(fpolygons * fps)47496e091f8SDavid du Colombier void fps_invert(fpolygons* fps)
47596e091f8SDavid du Colombier {
47696e091f8SDavid du Colombier 	fpolygon *p, *r=0;
47796e091f8SDavid du Colombier 	for (p=fps->p; p!=0;) {
47896e091f8SDavid du Colombier 		fpolygon* q = p;
47996e091f8SDavid du Colombier 		p = p->link;
48096e091f8SDavid du Colombier 		q->link = r;
48196e091f8SDavid du Colombier 		r = q;
48296e091f8SDavid du Colombier 	}
48396e091f8SDavid du Colombier 	fps->p = r;
48496e091f8SDavid du Colombier }
48596e091f8SDavid du Colombier 
48696e091f8SDavid du Colombier 
fp_remove(fpolygons * fps,fpolygon * fp)48796e091f8SDavid du Colombier void fp_remove(fpolygons* fps, fpolygon* fp)
48896e091f8SDavid du Colombier {
48996e091f8SDavid du Colombier 	fpolygon *q, **p = &fps->p;
49096e091f8SDavid du Colombier 	while (*p!=fp)
49196e091f8SDavid du Colombier 		if (*p==0)
49296e091f8SDavid du Colombier 			return;
49396e091f8SDavid du Colombier 		else	p = &(*p)->link;
49496e091f8SDavid du Colombier 	*p = fp->link;
49596e091f8SDavid du Colombier 	fps->bb = empty_frect;
49696e091f8SDavid du Colombier 	for (q=fps->p; q!=0; q=q->link)
49796e091f8SDavid du Colombier 		grow_bb(&fps->bb, &q->bb);
49896e091f8SDavid du Colombier }
49996e091f8SDavid du Colombier 
50096e091f8SDavid du Colombier 
50196e091f8SDavid du Colombier /* The transform maps abstract fpoint coordinates (the ones used in the input)
50296e091f8SDavid du Colombier    to the current screen coordinates.  The do_untransform() macros reverses this.
50396e091f8SDavid du Colombier    If univ.slant_ht is not the height of univ.disp, the actual region in the
50496e091f8SDavid du Colombier    abstract coordinates is a parallelogram inscribed in univ.disp with two
50596e091f8SDavid du Colombier    vertical edges and two slanted slanted edges: slant_ht>0 means that the
50696e091f8SDavid du Colombier    vertical edges have height slant_ht and the parallelogram touches the lower
50796e091f8SDavid du Colombier    left and upper right corners of univ.disp; slant_ht<0 refers to a parallelogram
50896e091f8SDavid du Colombier    of height -slant_ht that touches the other two corners of univ.disp.
50996e091f8SDavid du Colombier    NOTE: the ytransform macro assumes that tr->sl times the x coordinate has
51096e091f8SDavid du Colombier    already been subtracted from yy.
51196e091f8SDavid du Colombier */
51296e091f8SDavid du Colombier typedef struct transform {
51396e091f8SDavid du Colombier 	double sl;
51496e091f8SDavid du Colombier 	fpoint o, sc;		/* (x,y):->(o.x+sc.x*x, o.y+sc.y*y+sl*x) */
51596e091f8SDavid du Colombier } transform;
51696e091f8SDavid du Colombier 
51796e091f8SDavid du Colombier #define do_transform(d,tr,s)	((d)->x = (tr)->o.x + (tr)->sc.x*(s)->x,  \
51896e091f8SDavid du Colombier 				(d)->y = (tr)->o.y + (tr)->sc.y*(s)->y    \
51996e091f8SDavid du Colombier 					+ (tr)->sl*(s)->x)
52096e091f8SDavid du Colombier #define do_untransform(d,tr,s)	((d)->x = (.5+(s)->x-(tr)->o.x)/(tr)->sc.x,    \
52196e091f8SDavid du Colombier 				(d)->y = (.5+(s)->y-(tr)->sl*(d)->x-(tr)->o.y) \
52296e091f8SDavid du Colombier 					/(tr)->sc.y)
52396e091f8SDavid du Colombier #define xtransform(tr,xx)	((tr)->o.x + (tr)->sc.x*(xx))
52496e091f8SDavid du Colombier #define ytransform(tr,yy)	((tr)->o.y + (tr)->sc.y*(yy))
52596e091f8SDavid du Colombier #define dxuntransform(tr,xx)	((xx)/(tr)->sc.x)
52696e091f8SDavid du Colombier #define dyuntransform(tr,yy)	((yy)/(tr)->sc.y)
52796e091f8SDavid du Colombier 
52896e091f8SDavid du Colombier 
cur_trans(void)52996e091f8SDavid du Colombier transform cur_trans(void)
53096e091f8SDavid du Colombier {
53196e091f8SDavid du Colombier 	transform t;
53296e091f8SDavid du Colombier 	Rectangle d = screen->r;
53396e091f8SDavid du Colombier 	const frectangle* s = &univ.disp;
53496e091f8SDavid du Colombier 	double sh = univ.slant_ht;
53596e091f8SDavid du Colombier 	d.min.x += lft_border;
53696e091f8SDavid du Colombier 	d.min.y += top_border;
53796e091f8SDavid du Colombier 	d.max.x -= rt_border;
53896e091f8SDavid du Colombier 	d.max.y -= bot_border;
53996e091f8SDavid du Colombier 	t.sc.x = (d.max.x - d.min.x)/(s->max.x - s->min.x);
54096e091f8SDavid du Colombier 	t.sc.y = -(d.max.y - d.min.y)/fabs(sh);
54196e091f8SDavid du Colombier 	if (sh > 0) {
54296e091f8SDavid du Colombier 		t.sl = -t.sc.y*(s->max.y-s->min.y-sh)/(s->max.x - s->min.x);
54396e091f8SDavid du Colombier 		t.o.y = d.min.y - t.sc.y*s->max.y - t.sl*s->max.x;
54496e091f8SDavid du Colombier 	} else {
54596e091f8SDavid du Colombier 		t.sl = t.sc.y*(s->max.y-s->min.y+sh)/(s->max.x - s->min.x);
54696e091f8SDavid du Colombier 		t.o.y = d.min.y - t.sc.y*s->max.y - t.sl*s->min.x;
54796e091f8SDavid du Colombier 	}
54896e091f8SDavid du Colombier 	t.o.x = d.min.x - t.sc.x*s->min.x;
54996e091f8SDavid du Colombier 	return t;
55096e091f8SDavid du Colombier }
55196e091f8SDavid du Colombier 
55296e091f8SDavid du Colombier 
u_slant_amt(fpolygons * u)55396e091f8SDavid du Colombier double u_slant_amt(fpolygons *u)
55496e091f8SDavid du Colombier {
55596e091f8SDavid du Colombier 	double sh=u->slant_ht, dy=u->disp.max.y - u->disp.min.y;
55696e091f8SDavid du Colombier 	double dx = u->disp.max.x - u->disp.min.x;
55796e091f8SDavid du Colombier 	return (sh>0) ? (dy-sh)/dx : -(dy+sh)/dx;
55896e091f8SDavid du Colombier }
55996e091f8SDavid du Colombier 
56096e091f8SDavid du Colombier 
56196e091f8SDavid du Colombier /* Set *y0 and *y1 to the lower and upper bounds of the set of y-sl*x values that
56296e091f8SDavid du Colombier    *u says to display, where sl is the amount of slant.
56396e091f8SDavid du Colombier */
set_unslanted_y(fpolygons * u,double * y0,double * y1)56496e091f8SDavid du Colombier double set_unslanted_y(fpolygons *u, double *y0, double *y1)
56596e091f8SDavid du Colombier {
56696e091f8SDavid du Colombier 	double yy1, sl=u_slant_amt(u);
56796e091f8SDavid du Colombier 	if (u->slant_ht > 0) {
56896e091f8SDavid du Colombier 		*y0 = u->disp.min.y - sl*u->disp.min.x;
56996e091f8SDavid du Colombier 		yy1 = *y0 + u->slant_ht;
57096e091f8SDavid du Colombier 	} else {
57196e091f8SDavid du Colombier 		yy1 = u->disp.max.y - sl*u->disp.min.x;
57296e091f8SDavid du Colombier 		*y0 = yy1 + u->slant_ht;
57396e091f8SDavid du Colombier 	}
57496e091f8SDavid du Colombier 	if (y1 != 0)
57596e091f8SDavid du Colombier 		*y1 = yy1;
57696e091f8SDavid du Colombier 	return sl;
57796e091f8SDavid du Colombier }
57896e091f8SDavid du Colombier 
57996e091f8SDavid du Colombier 
58096e091f8SDavid du Colombier 
58196e091f8SDavid du Colombier 
58296e091f8SDavid du Colombier /*************************** The region to display ****************************/
58396e091f8SDavid du Colombier 
nontrivial_interval(double * lo,double * hi)58496e091f8SDavid du Colombier void nontrivial_interval(double *lo, double *hi)
58596e091f8SDavid du Colombier {
58696e091f8SDavid du Colombier 	if (*lo >= *hi) {
58796e091f8SDavid du Colombier 		double mid = .5*(*lo + *hi);
58896e091f8SDavid du Colombier 		double tweak = 1e-6 + 1e-6*fabs(mid);
58996e091f8SDavid du Colombier 		*lo = mid - tweak;
59096e091f8SDavid du Colombier 		*hi = mid + tweak;
59196e091f8SDavid du Colombier 	}
59296e091f8SDavid du Colombier }
59396e091f8SDavid du Colombier 
59496e091f8SDavid du Colombier 
init_disp(void)59596e091f8SDavid du Colombier void init_disp(void)
59696e091f8SDavid du Colombier {
59796e091f8SDavid du Colombier 	double dw = (univ.bb.max.x - univ.bb.min.x)*underscan;
59896e091f8SDavid du Colombier 	double dh = (univ.bb.max.y - univ.bb.min.y)*underscan;
59996e091f8SDavid du Colombier 	univ.disp.min.x = univ.bb.min.x - dw;
60096e091f8SDavid du Colombier 	univ.disp.min.y = univ.bb.min.y - dh;
60196e091f8SDavid du Colombier 	univ.disp.max.x = univ.bb.max.x + dw;
60296e091f8SDavid du Colombier 	univ.disp.max.y = univ.bb.max.y + dh;
60396e091f8SDavid du Colombier 	nontrivial_interval(&univ.disp.min.x, &univ.disp.max.x);
60496e091f8SDavid du Colombier 	nontrivial_interval(&univ.disp.min.y, &univ.disp.max.y);
60596e091f8SDavid du Colombier 	univ.slant_ht = univ.disp.max.y - univ.disp.min.y;	/* means no slant */
60696e091f8SDavid du Colombier }
60796e091f8SDavid du Colombier 
60896e091f8SDavid du Colombier 
recenter_disp(Point c)60996e091f8SDavid du Colombier void recenter_disp(Point c)
61096e091f8SDavid du Colombier {
61196e091f8SDavid du Colombier 	transform tr = cur_trans();
61296e091f8SDavid du Colombier 	fpoint cc, off;
61396e091f8SDavid du Colombier 	do_untransform(&cc, &tr, &c);
61496e091f8SDavid du Colombier 	off.x = cc.x - .5*(univ.disp.min.x + univ.disp.max.x);
61596e091f8SDavid du Colombier 	off.y = cc.y - .5*(univ.disp.min.y + univ.disp.max.y);
61696e091f8SDavid du Colombier 	univ.disp.min.x += off.x;
61796e091f8SDavid du Colombier 	univ.disp.min.y += off.y;
61896e091f8SDavid du Colombier 	univ.disp.max.x += off.x;
61996e091f8SDavid du Colombier 	univ.disp.max.y += off.y;
62096e091f8SDavid du Colombier }
62196e091f8SDavid du Colombier 
62296e091f8SDavid du Colombier 
62396e091f8SDavid du Colombier /* Find the upper-left and lower-right corners of the bounding box of the
62496e091f8SDavid du Colombier    parallelogram formed by untransforming the rectangle rminx, rminy, ... (given
62596e091f8SDavid du Colombier    in screen coordinates), and return the height of the parallelogram (negated
62696e091f8SDavid du Colombier    if it slopes downward).
62796e091f8SDavid du Colombier */
untransform_corners(double rminx,double rminy,double rmaxx,double rmaxy,fpoint * ul,fpoint * lr)62896e091f8SDavid du Colombier double untransform_corners(double rminx, double rminy, double rmaxx, double rmaxy,
62996e091f8SDavid du Colombier 		fpoint *ul, fpoint *lr)
63096e091f8SDavid du Colombier {
63196e091f8SDavid du Colombier 	fpoint r_ur, r_ul, r_ll, r_lr;	/* corners of the given recangle */
63296e091f8SDavid du Colombier 	fpoint ur, ll;			/* untransformed versions of r_ur, r_ll */
63396e091f8SDavid du Colombier 	transform tr = cur_trans();
63496e091f8SDavid du Colombier 	double ht;
63596e091f8SDavid du Colombier 	r_ur.x=rmaxx;  r_ur.y=rminy;
63696e091f8SDavid du Colombier 	r_ul.x=rminx;  r_ul.y=rminy;
63796e091f8SDavid du Colombier 	r_ll.x=rminx;  r_ll.y=rmaxy;
63896e091f8SDavid du Colombier 	r_lr.x=rmaxx;  r_lr.y=rmaxy;
63996e091f8SDavid du Colombier 	do_untransform(ul, &tr, &r_ul);
64096e091f8SDavid du Colombier 	do_untransform(lr, &tr, &r_lr);
64196e091f8SDavid du Colombier 	do_untransform(&ur, &tr, &r_ur);
64296e091f8SDavid du Colombier 	do_untransform(&ll, &tr, &r_ll);
64396e091f8SDavid du Colombier 	ht = ur.y - lr->y;
64496e091f8SDavid du Colombier 	if (ll.x < ul->x)
64596e091f8SDavid du Colombier 		ul->x = ll.x;
64696e091f8SDavid du Colombier 	if (ur.y > ul->y)
64796e091f8SDavid du Colombier 		ul->y = ur.y;
64896e091f8SDavid du Colombier 	else	ht = -ht;
64996e091f8SDavid du Colombier 	if (ur.x > lr->x)
65096e091f8SDavid du Colombier 		lr->x = ur.x;
65196e091f8SDavid du Colombier 	if (ll.y < lr->y)
65296e091f8SDavid du Colombier 		lr->y = ll.y;
65396e091f8SDavid du Colombier 	return ht;
65496e091f8SDavid du Colombier }
65596e091f8SDavid du Colombier 
65696e091f8SDavid du Colombier 
disp_dozoom(double rminx,double rminy,double rmaxx,double rmaxy)65796e091f8SDavid du Colombier void disp_dozoom(double rminx, double rminy, double rmaxx, double rmaxy)
65896e091f8SDavid du Colombier {
65996e091f8SDavid du Colombier 	fpoint ul, lr;
66096e091f8SDavid du Colombier 	double sh = untransform_corners(rminx, rminy, rmaxx, rmaxy, &ul, &lr);
66196e091f8SDavid du Colombier 	if (ul.x==lr.x || ul.y==lr.y)
66296e091f8SDavid du Colombier 		return;
66396e091f8SDavid du Colombier 	univ.slant_ht = sh;
66496e091f8SDavid du Colombier 	univ.disp.min.x = ul.x;
66596e091f8SDavid du Colombier 	univ.disp.max.y = ul.y;
66696e091f8SDavid du Colombier 	univ.disp.max.x = lr.x;
66796e091f8SDavid du Colombier 	univ.disp.min.y = lr.y;
66896e091f8SDavid du Colombier 	nontrivial_interval(&univ.disp.min.x, &univ.disp.max.x);
66996e091f8SDavid du Colombier 	nontrivial_interval(&univ.disp.min.y, &univ.disp.max.y);
67096e091f8SDavid du Colombier }
67196e091f8SDavid du Colombier 
67296e091f8SDavid du Colombier 
disp_zoomin(Rectangle r)67396e091f8SDavid du Colombier void disp_zoomin(Rectangle r)
67496e091f8SDavid du Colombier {
67596e091f8SDavid du Colombier 	disp_dozoom(r.min.x, r.min.y, r.max.x, r.max.y);
67696e091f8SDavid du Colombier }
67796e091f8SDavid du Colombier 
67896e091f8SDavid du Colombier 
disp_zoomout(Rectangle r)67996e091f8SDavid du Colombier void disp_zoomout(Rectangle r)
68096e091f8SDavid du Colombier {
68196e091f8SDavid du Colombier 	double qminx, qminy, qmaxx, qmaxy;
68296e091f8SDavid du Colombier 	double scx, scy;
68396e091f8SDavid du Colombier 	Rectangle s = screen->r;
68496e091f8SDavid du Colombier 	if (r.min.x==r.max.x || r.min.y==r.max.y)
68596e091f8SDavid du Colombier 		return;
68696e091f8SDavid du Colombier 	s.min.x += lft_border;
68796e091f8SDavid du Colombier 	s.min.y += top_border;
68896e091f8SDavid du Colombier 	s.max.x -= rt_border;
68996e091f8SDavid du Colombier 	s.max.y -= bot_border;
69096e091f8SDavid du Colombier 	scx = (s.max.x - s.min.x)/(r.max.x - r.min.x);
69196e091f8SDavid du Colombier 	scy = (s.max.y - s.min.y)/(r.max.y - r.min.y);
69296e091f8SDavid du Colombier 	qminx = s.min.x + scx*(s.min.x - r.min.x);
69396e091f8SDavid du Colombier 	qmaxx = s.max.x + scx*(s.max.x - r.max.x);
69496e091f8SDavid du Colombier 	qminy = s.min.y + scy*(s.min.y - r.min.y);
69596e091f8SDavid du Colombier 	qmaxy = s.max.y + scy*(s.max.y - r.max.y);
69696e091f8SDavid du Colombier 	disp_dozoom(qminx, qminy, qmaxx, qmaxy);
69796e091f8SDavid du Colombier }
69896e091f8SDavid du Colombier 
69996e091f8SDavid du Colombier 
expand2(double * a,double * b,double f)70096e091f8SDavid du Colombier void expand2(double* a, double* b, double f)
70196e091f8SDavid du Colombier {
70296e091f8SDavid du Colombier 	double mid = .5*(*a + *b);
70396e091f8SDavid du Colombier 	*a = mid + f*(*a - mid);
70496e091f8SDavid du Colombier 	*b = mid + f*(*b - mid);
70596e091f8SDavid du Colombier }
70696e091f8SDavid du Colombier 
disp_squareup(void)70796e091f8SDavid du Colombier void disp_squareup(void)
70896e091f8SDavid du Colombier {
70996e091f8SDavid du Colombier 	double dx = univ.disp.max.x - univ.disp.min.x;
71096e091f8SDavid du Colombier 	double dy = univ.disp.max.y - univ.disp.min.y;
71196e091f8SDavid du Colombier 	dx /= screen->r.max.x - lft_border - screen->r.min.x - rt_border;
71296e091f8SDavid du Colombier 	dy /= screen->r.max.y - bot_border - screen->r.min.y - top_border;
71396e091f8SDavid du Colombier 	if (dx > dy)
71496e091f8SDavid du Colombier 		expand2(&univ.disp.min.y, &univ.disp.max.y, dx/dy);
71596e091f8SDavid du Colombier 	else	expand2(&univ.disp.min.x, &univ.disp.max.x, dy/dx);
71696e091f8SDavid du Colombier 	univ.slant_ht = univ.disp.max.y - univ.disp.min.y;
71796e091f8SDavid du Colombier }
71896e091f8SDavid du Colombier 
71996e091f8SDavid du Colombier 
72096e091f8SDavid du Colombier /* Slant so that p and q appear at the same height on the screen and the
72196e091f8SDavid du Colombier    screen contains the smallest possible superset of what its previous contents.
72296e091f8SDavid du Colombier */
slant_disp(fpoint p,fpoint q)72396e091f8SDavid du Colombier void slant_disp(fpoint p, fpoint q)
72496e091f8SDavid du Colombier {
72596e091f8SDavid du Colombier 	double yll, ylr, yul, yur;	/* corner y coords of displayed parallelogram */
72696e091f8SDavid du Colombier 	double sh, dy;
72796e091f8SDavid du Colombier 	if (p.x == q.x)
72896e091f8SDavid du Colombier 		return;
72996e091f8SDavid du Colombier 	sh = univ.slant_ht;
73096e091f8SDavid du Colombier 	if (sh > 0) {
73196e091f8SDavid du Colombier 		yll=yul=univ.disp.min.y;  yul+=sh;
73296e091f8SDavid du Colombier 		ylr=yur=univ.disp.max.y;  ylr-=sh;
73396e091f8SDavid du Colombier 	} else {
73496e091f8SDavid du Colombier 		yll=yul=univ.disp.max.y;  yll+=sh;
73596e091f8SDavid du Colombier 		ylr=yur=univ.disp.min.y;  yur-=sh;
73696e091f8SDavid du Colombier 	}
73796e091f8SDavid du Colombier 	dy = (univ.disp.max.x-univ.disp.min.x)*(q.y - p.y)/(q.x - p.x);
73896e091f8SDavid du Colombier 	dy -= ylr - yll;
73996e091f8SDavid du Colombier 	if (dy > 0)
74096e091f8SDavid du Colombier 		{yll-=dy; yur+=dy;}
74196e091f8SDavid du Colombier 	else	{yul-=dy; ylr+=dy;}
74296e091f8SDavid du Colombier 	if (ylr > yll) {
74396e091f8SDavid du Colombier 		univ.disp.min.y = yll;
74496e091f8SDavid du Colombier 		univ.disp.max.y = yur;
74596e091f8SDavid du Colombier 		univ.slant_ht = yur - ylr;
74696e091f8SDavid du Colombier 	} else {
74796e091f8SDavid du Colombier 		univ.disp.max.y = yul;
74896e091f8SDavid du Colombier 		univ.disp.min.y = ylr;
74996e091f8SDavid du Colombier 		univ.slant_ht = ylr - yur;
75096e091f8SDavid du Colombier 	}
75196e091f8SDavid du Colombier }
75296e091f8SDavid du Colombier 
75396e091f8SDavid du Colombier 
75496e091f8SDavid du Colombier 
75596e091f8SDavid du Colombier 
75696e091f8SDavid du Colombier /******************************** Ascii input  ********************************/
75796e091f8SDavid du Colombier 
set_fbb(fpolygon * fp)75896e091f8SDavid du Colombier void set_fbb(fpolygon* fp)
75996e091f8SDavid du Colombier {
76096e091f8SDavid du Colombier 	fpoint lo=fp->p[0], hi=fp->p[0];
76196e091f8SDavid du Colombier 	const fpoint *q, *qtop;
76296e091f8SDavid du Colombier 	for (qtop=(q=fp->p)+fp->n; ++q<=qtop;) {
76396e091f8SDavid du Colombier 		if (q->x < lo.x) lo.x=q->x;
76496e091f8SDavid du Colombier 		if (q->y < lo.y) lo.y=q->y;
76596e091f8SDavid du Colombier 		if (q->x > hi.x) hi.x=q->x;
76696e091f8SDavid du Colombier 		if (q->y > hi.y) hi.y=q->y;
76796e091f8SDavid du Colombier 	}
76896e091f8SDavid du Colombier 	fp->bb.min = lo;
76996e091f8SDavid du Colombier 	fp->bb.max = hi;
77096e091f8SDavid du Colombier }
77196e091f8SDavid du Colombier 
mystrdup(char * s)77296e091f8SDavid du Colombier char* mystrdup(char* s)
77396e091f8SDavid du Colombier {
77496e091f8SDavid du Colombier 	char *r, *t = strrchr(s,'"');
77596e091f8SDavid du Colombier 	if (t==0) {
77696e091f8SDavid du Colombier 		t = s + strlen(s);
77796e091f8SDavid du Colombier 		while (t>s && (t[-1]=='\n' || t[-1]=='\r'))
77896e091f8SDavid du Colombier 			t--;
77996e091f8SDavid du Colombier 	}
78096e091f8SDavid du Colombier 	r = malloc(1+(t-s));
78196e091f8SDavid du Colombier 	memcpy(r, s, t-s);
78296e091f8SDavid du Colombier 	r[t-s] = 0;
78396e091f8SDavid du Colombier 	return r;
78496e091f8SDavid du Colombier }
78596e091f8SDavid du Colombier 
is_valid_label(char * lab)78696e091f8SDavid du Colombier int is_valid_label(char* lab)
78796e091f8SDavid du Colombier {
78896e091f8SDavid du Colombier 	char* t;
78996e091f8SDavid du Colombier 	if (lab[0]=='"')
79096e091f8SDavid du Colombier 		return (t=strrchr(lab,'"'))!=0 && t!=lab && strspn(t+1," \t\r\n")==strlen(t+1);
79196e091f8SDavid du Colombier 	return strcspn(lab," \t")==strlen(lab);
79296e091f8SDavid du Colombier }
79396e091f8SDavid du Colombier 
79496e091f8SDavid du Colombier /* Read a polyline and update the number of lines read.  A zero result indicates bad
79596e091f8SDavid du Colombier    syntax if *lineno increases; otherwise it indicates end of file.
79696e091f8SDavid du Colombier */
rd_fpoly(FILE * fin,int * lineno)79796e091f8SDavid du Colombier fpolygon* rd_fpoly(FILE* fin, int *lineno)
79896e091f8SDavid du Colombier {
7990c6300e7SDavid du Colombier 	char buf[4096], junk[2];
80096e091f8SDavid du Colombier 	fpoint q;
80196e091f8SDavid du Colombier 	fpolygon* fp;
80296e091f8SDavid du Colombier 	int allocn;
8030c6300e7SDavid du Colombier 	if (!fgets(buf,4096,fin))
80496e091f8SDavid du Colombier 		return 0;
80596e091f8SDavid du Colombier 	(*lineno)++;
80696e091f8SDavid du Colombier 	if (sscanf(buf,"%lg%lg%1s",&q.x,&q.y,junk) != 2)
80796e091f8SDavid du Colombier 		return 0;
80896e091f8SDavid du Colombier 	fp = malloc(sizeof(fpolygon));
8090c6300e7SDavid du Colombier 	allocn = 4;
81096e091f8SDavid du Colombier 	fp->p = malloc(allocn*sizeof(fpoint));
81196e091f8SDavid du Colombier 	fp->p[0] = q;
81296e091f8SDavid du Colombier 	fp->n = 0;
81396e091f8SDavid du Colombier 	fp->nam = "";
8140c6300e7SDavid du Colombier 	fp->c.thick = 0;
8150c6300e7SDavid du Colombier 	fp->c.clr = clr_im(DBlack);
8160c6300e7SDavid du Colombier 	fp->ct = 0;
8170c6300e7SDavid du Colombier 	while (fgets(buf,4096,fin)) {
81896e091f8SDavid du Colombier 		(*lineno)++;
81996e091f8SDavid du Colombier 		if (sscanf(buf,"%lg%lg%1s",&q.x,&q.y,junk) != 2) {
82096e091f8SDavid du Colombier 			if (!is_valid_label(buf))
8210c6300e7SDavid du Colombier 				{free_fp_etc(fp); return 0;}
82296e091f8SDavid du Colombier 			fp->nam = (buf[0]=='"') ? buf+1 : buf;
82396e091f8SDavid du Colombier 			break;
82496e091f8SDavid du Colombier 		}
82596e091f8SDavid du Colombier 		if (++(fp->n) == allocn)
82696e091f8SDavid du Colombier 			fp->p = realloc(fp->p, (allocn<<=1)*sizeof(fpoint));
82796e091f8SDavid du Colombier 		fp->p[fp->n] = q;
82896e091f8SDavid du Colombier 	}
82996e091f8SDavid du Colombier 	fp->nam = mystrdup(fp->nam);
83096e091f8SDavid du Colombier 	set_fbb(fp);
83196e091f8SDavid du Colombier 	fp->link = 0;
83296e091f8SDavid du Colombier 	return fp;
83396e091f8SDavid du Colombier }
83496e091f8SDavid du Colombier 
83596e091f8SDavid du Colombier 
83696e091f8SDavid du Colombier /* Read input into *fps and return 0 or a line number where there's a syntax error */
rd_fpolys(FILE * fin,fpolygons * fps)83796e091f8SDavid du Colombier int rd_fpolys(FILE* fin, fpolygons* fps)
83896e091f8SDavid du Colombier {
83996e091f8SDavid du Colombier 	fpolygon *fp, *fp0=fps->p;
84096e091f8SDavid du Colombier 	int lineno=0, ok_upto=0;
84196e091f8SDavid du Colombier 	while ((fp=rd_fpoly(fin,&lineno)) != 0) {
84296e091f8SDavid du Colombier 		ok_upto = lineno;
84396e091f8SDavid du Colombier 		fp->link = fps->p;
84496e091f8SDavid du Colombier 		fps->p = fp;
84596e091f8SDavid du Colombier 		grow_bb(&fps->bb, &fp->bb);
84696e091f8SDavid du Colombier 	}
84796e091f8SDavid du Colombier 	set_default_clrs(fps, fp0);
84896e091f8SDavid du Colombier 	return (ok_upto==lineno) ? 0 : lineno;
84996e091f8SDavid du Colombier }
85096e091f8SDavid du Colombier 
85196e091f8SDavid du Colombier 
85296e091f8SDavid du Colombier /* Read input from file fnam and return an error line no., -1 for "can't open"
85396e091f8SDavid du Colombier    or 0 for success.
85496e091f8SDavid du Colombier */
doinput(char * fnam)85596e091f8SDavid du Colombier int doinput(char* fnam)
85696e091f8SDavid du Colombier {
85796e091f8SDavid du Colombier 	FILE* fin = strcmp(fnam,"-")==0 ? stdin : fopen(fnam, "r");
85896e091f8SDavid du Colombier 	int errline_or0;
85996e091f8SDavid du Colombier 	if (fin==0)
86096e091f8SDavid du Colombier 		return -1;
86196e091f8SDavid du Colombier 	errline_or0 = rd_fpolys(fin, &univ);
86296e091f8SDavid du Colombier 	fclose(fin);
86396e091f8SDavid du Colombier 	return errline_or0;
86496e091f8SDavid du Colombier }
86596e091f8SDavid du Colombier 
86696e091f8SDavid du Colombier 
86796e091f8SDavid du Colombier 
86896e091f8SDavid du Colombier /******************************** Ascii output ********************************/
86996e091f8SDavid du Colombier 
fp_reverse(fpolygon * fp)87096e091f8SDavid du Colombier fpolygon* fp_reverse(fpolygon* fp)
87196e091f8SDavid du Colombier {
87296e091f8SDavid du Colombier 	fpolygon* r = 0;
87396e091f8SDavid du Colombier 	while (fp!=0) {
87496e091f8SDavid du Colombier 		fpolygon* q = fp->link;
87596e091f8SDavid du Colombier 		fp->link = r;
87696e091f8SDavid du Colombier 		r = fp;
87796e091f8SDavid du Colombier 		fp = q;
87896e091f8SDavid du Colombier 	}
87996e091f8SDavid du Colombier 	return r;
88096e091f8SDavid du Colombier }
88196e091f8SDavid du Colombier 
wr_fpoly(FILE * fout,const fpolygon * fp)88296e091f8SDavid du Colombier void wr_fpoly(FILE* fout, const fpolygon* fp)
88396e091f8SDavid du Colombier {
88496e091f8SDavid du Colombier 	char buf[256];
88596e091f8SDavid du Colombier 	int i;
88696e091f8SDavid du Colombier 	for (i=0; i<=fp->n; i++)
88796e091f8SDavid du Colombier 		fprintf(fout,"%.12g\t%.12g\n", fp->p[i].x, fp->p[i].y);
8880c6300e7SDavid du Colombier 	fprintf(fout,"\"%s\"\n", nam_with_thclr(fp->nam, &fp->c, buf, 256));
88996e091f8SDavid du Colombier }
89096e091f8SDavid du Colombier 
wr_fpolys(FILE * fout,fpolygons * fps)89196e091f8SDavid du Colombier void wr_fpolys(FILE* fout, fpolygons* fps)
89296e091f8SDavid du Colombier {
89396e091f8SDavid du Colombier 	fpolygon* fp;
89496e091f8SDavid du Colombier 	fps->p = fp_reverse(fps->p);
89596e091f8SDavid du Colombier 	for (fp=fps->p; fp!=0; fp=fp->link)
89696e091f8SDavid du Colombier 		wr_fpoly(fout, fp);
89796e091f8SDavid du Colombier 	fps->p = fp_reverse(fps->p);
89896e091f8SDavid du Colombier }
89996e091f8SDavid du Colombier 
90096e091f8SDavid du Colombier 
dooutput(char * fnam)90196e091f8SDavid du Colombier int dooutput(char* fnam)
90296e091f8SDavid du Colombier {
90396e091f8SDavid du Colombier 	FILE* fout = fopen(fnam, "w");
90496e091f8SDavid du Colombier 	if (fout==0)
90596e091f8SDavid du Colombier 		return 0;
90696e091f8SDavid du Colombier 	wr_fpolys(fout, &univ);
90796e091f8SDavid du Colombier 	fclose(fout);
90896e091f8SDavid du Colombier 	return 1;
90996e091f8SDavid du Colombier }
91096e091f8SDavid du Colombier 
91196e091f8SDavid du Colombier 
91296e091f8SDavid du Colombier 
91396e091f8SDavid du Colombier 
91496e091f8SDavid du Colombier /************************ Clipping to screen rectangle ************************/
91596e091f8SDavid du Colombier 
91696e091f8SDavid du Colombier /* Find the t values, 0<=t<=1 for which x0+t*(x1-x0) is between xlo and xhi,
91796e091f8SDavid du Colombier    or return 0 to indicate no such t values exist.  If returning 1, set *t0 and
91896e091f8SDavid du Colombier    *t1 to delimit the t interval.
91996e091f8SDavid du Colombier */
do_xory(double x0,double x1,double xlo,double xhi,double * t0,double * t1)92096e091f8SDavid du Colombier int do_xory(double x0, double x1, double xlo, double xhi, double* t0, double* t1)
92196e091f8SDavid du Colombier {
92296e091f8SDavid du Colombier 	*t1 = 1.0;
92396e091f8SDavid du Colombier 	if (x0<xlo) {
92496e091f8SDavid du Colombier 		if (x1<xlo) return 0;
92596e091f8SDavid du Colombier 		*t0 = (xlo-x0)/(x1-x0);
92696e091f8SDavid du Colombier 		if (x1>xhi) *t1 = (xhi-x0)/(x1-x0);
92796e091f8SDavid du Colombier 	} else if (x0>xhi) {
92896e091f8SDavid du Colombier 		if (x1>xhi) return 0;
92996e091f8SDavid du Colombier 		*t0 = (xhi-x0)/(x1-x0);
93096e091f8SDavid du Colombier 		if (x1<xlo) *t1 = (xlo-x0)/(x1-x0);
93196e091f8SDavid du Colombier 	} else {
93296e091f8SDavid du Colombier 		*t0 = 0.0;
93396e091f8SDavid du Colombier 		if (x1>xhi) *t1 = (xhi-x0)/(x1-x0);
93496e091f8SDavid du Colombier 		else if (x1<xlo) *t1 = (xlo-x0)/(x1-x0);
93596e091f8SDavid du Colombier 		else *t1 = 1.0;
93696e091f8SDavid du Colombier 	}
93796e091f8SDavid du Colombier 	return 1;
93896e091f8SDavid du Colombier }
93996e091f8SDavid du Colombier 
94096e091f8SDavid du Colombier 
94196e091f8SDavid du Colombier /* After mapping y to y-slope*x, what initial fraction of the *p to *q edge is
94296e091f8SDavid du Colombier    outside of *r?  Note that the edge could start outside *r, pass through *r,
94396e091f8SDavid du Colombier    and wind up outside again.
94496e091f8SDavid du Colombier */
frac_outside(const fpoint * p,const fpoint * q,const frectangle * r,double slope)94596e091f8SDavid du Colombier double frac_outside(const fpoint* p, const fpoint* q, const frectangle* r,
94696e091f8SDavid du Colombier 		double slope)
94796e091f8SDavid du Colombier {
94896e091f8SDavid du Colombier 	double t0, t1, tt0, tt1;
94996e091f8SDavid du Colombier 	double px=p->x, qx=q->x;
95096e091f8SDavid du Colombier 	if (!do_xory(px, qx, r->min.x, r->max.x, &t0, &t1))
95196e091f8SDavid du Colombier 		return 1;
95296e091f8SDavid du Colombier 	if (!do_xory(p->y-slope*px, q->y-slope*qx, r->min.y, r->max.y, &tt0, &tt1))
95396e091f8SDavid du Colombier 		return 1;
95496e091f8SDavid du Colombier 	if (tt0 > t0)
95596e091f8SDavid du Colombier 		t0 = tt0;
95696e091f8SDavid du Colombier 	if (t1<=t0 || tt1<=t0)
95796e091f8SDavid du Colombier 		return 1;
95896e091f8SDavid du Colombier 	return t0;
95996e091f8SDavid du Colombier }
96096e091f8SDavid du Colombier 
96196e091f8SDavid du Colombier 
96296e091f8SDavid du Colombier /* Think of p0..pn as piecewise-linear function F(t) for t=0..pn-p0, and find
96396e091f8SDavid du Colombier    the maximum tt such that F(0..tt) is all inside of r, assuming p0 is inside.
96496e091f8SDavid du Colombier    Coordinates are transformed by y=y-x*slope before testing against r.
96596e091f8SDavid du Colombier */
in_length(const fpoint * p0,const fpoint * pn,frectangle r,double slope)96696e091f8SDavid du Colombier double in_length(const fpoint* p0, const fpoint* pn, frectangle r, double slope)
96796e091f8SDavid du Colombier {
96896e091f8SDavid du Colombier 	const fpoint* p = p0;
96996e091f8SDavid du Colombier 	double px, py;
97096e091f8SDavid du Colombier 	do if (++p > pn)
97196e091f8SDavid du Colombier 		return pn - p0;
97296e091f8SDavid du Colombier 	while (r.min.x<=(px=p->x) && px<=r.max.x
97396e091f8SDavid du Colombier 			&& r.min.y<=(py=p->y-slope*px) && py<=r.max.y);
97496e091f8SDavid du Colombier 	return (p - p0) - frac_outside(p, p-1, &r, slope);
97596e091f8SDavid du Colombier }
97696e091f8SDavid du Colombier 
97796e091f8SDavid du Colombier 
97896e091f8SDavid du Colombier /* Think of p0..pn as piecewise-linear function F(t) for t=0..pn-p0, and find
97996e091f8SDavid du Colombier    the maximum tt such that F(0..tt) is all outside of *r.  Coordinates are
98096e091f8SDavid du Colombier    transformed by y=y-x*slope before testing against r.
98196e091f8SDavid du Colombier */
out_length(const fpoint * p0,const fpoint * pn,frectangle r,double slope)98296e091f8SDavid du Colombier double out_length(const fpoint* p0, const fpoint* pn, frectangle r, double slope)
98396e091f8SDavid du Colombier {
98496e091f8SDavid du Colombier 	const fpoint* p = p0;
98596e091f8SDavid du Colombier 	double fr;
98696e091f8SDavid du Colombier 	do {	if (p->x < r.min.x)
98796e091f8SDavid du Colombier 			do if (++p>pn) return pn-p0;
98896e091f8SDavid du Colombier 			while (p->x <= r.min.x);
98996e091f8SDavid du Colombier 		else if (p->x > r.max.x)
99096e091f8SDavid du Colombier 			do if (++p>pn) return pn-p0;
99196e091f8SDavid du Colombier 			while (p->x >= r.max.x);
99296e091f8SDavid du Colombier 		else if (p->y-slope*p->x < r.min.y)
99396e091f8SDavid du Colombier 			do if (++p>pn) return pn-p0;
99496e091f8SDavid du Colombier 			while (p->y-slope*p->x <= r.min.y);
99596e091f8SDavid du Colombier 		else if (p->y-slope*p->x > r.max.y)
99696e091f8SDavid du Colombier 			do if (++p>pn) return pn-p0;
99796e091f8SDavid du Colombier 			while (p->y-slope*p->x >= r.max.y);
99896e091f8SDavid du Colombier 		else return p - p0;
99996e091f8SDavid du Colombier 	} while ((fr=frac_outside(p-1,p,&r,slope)) == 1);
100096e091f8SDavid du Colombier 	return (p - p0) + fr-1;
100196e091f8SDavid du Colombier }
100296e091f8SDavid du Colombier 
100396e091f8SDavid du Colombier 
100496e091f8SDavid du Colombier 
100596e091f8SDavid du Colombier /*********************** Drawing frame and axis labels  ***********************/
100696e091f8SDavid du Colombier 
100796e091f8SDavid du Colombier #define Nthous  7
100896e091f8SDavid du Colombier #define Len_thous  30			/* bound on strlen(thous_nam[i]) */
100996e091f8SDavid du Colombier char* thous_nam[Nthous] = {
101096e091f8SDavid du Colombier 	"one", "thousand", "million", "billion",
101196e091f8SDavid du Colombier 	"trillion", "quadrillion", "quintillion",
101296e091f8SDavid du Colombier };
101396e091f8SDavid du Colombier 
101496e091f8SDavid du Colombier 
101596e091f8SDavid du Colombier typedef struct lab_interval {
101696e091f8SDavid du Colombier 	double sep;			/* separation between tick marks */
101796e091f8SDavid du Colombier 	double unit;		/* power of 1000 divisor */
101896e091f8SDavid du Colombier 	int logunit;		/* log base 1000 of of this divisor */
101996e091f8SDavid du Colombier 	double off;			/* offset to subtract before dividing */
102096e091f8SDavid du Colombier } lab_interval;
102196e091f8SDavid du Colombier 
102296e091f8SDavid du Colombier 
abbrev_num(double x,const lab_interval * iv)102396e091f8SDavid du Colombier char* abbrev_num(double x, const lab_interval* iv)
102496e091f8SDavid du Colombier {
102596e091f8SDavid du Colombier 	static char buf[16];
102696e091f8SDavid du Colombier 	double dx = x - iv->off;
102796e091f8SDavid du Colombier 	dx = iv->sep * floor(dx/iv->sep + .5);
102896e091f8SDavid du Colombier 	sprintf(buf,"%g", dx/iv->unit);
102996e091f8SDavid du Colombier 	return buf;
103096e091f8SDavid du Colombier }
103196e091f8SDavid du Colombier 
103296e091f8SDavid du Colombier 
lead_digits(double n,double r)103396e091f8SDavid du Colombier double lead_digits(double n, double r)	/* n truncated to power of 10 above r */
103496e091f8SDavid du Colombier {
103596e091f8SDavid du Colombier 	double rr = pow(10, ceil(log10(r)));
103696e091f8SDavid du Colombier 	double nn = (n<rr) ? 0.0 : rr*floor(n/rr);
103796e091f8SDavid du Colombier 	if (n+r-nn >= digs10pow) {
103896e091f8SDavid du Colombier 		rr /= 10;
103996e091f8SDavid du Colombier 		nn = (n<rr) ? 0.0 : rr*floor(n/rr);
104096e091f8SDavid du Colombier 	}
104196e091f8SDavid du Colombier 	return nn;
104296e091f8SDavid du Colombier }
104396e091f8SDavid du Colombier 
104496e091f8SDavid du Colombier 
next_larger(double s0,double xlo,double xhi)104596e091f8SDavid du Colombier lab_interval next_larger(double s0, double xlo, double xhi)
104696e091f8SDavid du Colombier {
104796e091f8SDavid du Colombier 	double nlo, nhi;
104896e091f8SDavid du Colombier 	lab_interval r;
104996e091f8SDavid du Colombier 	r.logunit = (int) floor(log10(s0) + LOG2);
105096e091f8SDavid du Colombier 	r.unit = pow(10, r.logunit);
105196e091f8SDavid du Colombier 	nlo = xlo/r.unit;
105296e091f8SDavid du Colombier 	nhi = xhi/r.unit;
105396e091f8SDavid du Colombier 	if (nhi >= digs10pow)
105496e091f8SDavid du Colombier 		r.off = r.unit*lead_digits(nlo, nhi-nlo);
105596e091f8SDavid du Colombier 	else if (nlo <= -digs10pow)
105696e091f8SDavid du Colombier 		r.off = -r.unit*lead_digits(-nhi, nhi-nlo);
105796e091f8SDavid du Colombier 	else	r.off = 0;
105896e091f8SDavid du Colombier 	r.sep = (s0<=r.unit) ? r.unit : (s0<2*r.unit ? 2*r.unit : 5*r.unit);
105996e091f8SDavid du Colombier 	switch (r.logunit%3) {
106096e091f8SDavid du Colombier 	case 1:	r.unit*=.1; r.logunit--;
106196e091f8SDavid du Colombier 		break;
106296e091f8SDavid du Colombier 	case -1: case 2:
106396e091f8SDavid du Colombier 		r.unit*=10; r.logunit++;
106496e091f8SDavid du Colombier 		break;
106596e091f8SDavid du Colombier 	case -2: r.unit*=100; r.logunit+=2;
106696e091f8SDavid du Colombier 	}
106796e091f8SDavid du Colombier 	r.logunit /= 3;
106896e091f8SDavid du Colombier 	return r;
106996e091f8SDavid du Colombier }
107096e091f8SDavid du Colombier 
107196e091f8SDavid du Colombier 
min_hsep(const transform * tr)107296e091f8SDavid du Colombier double min_hsep(const transform* tr)
107396e091f8SDavid du Colombier {
107496e091f8SDavid du Colombier 	double s = (2+labdigs)*sdigit.x;
107596e091f8SDavid du Colombier 	double ss = (univ.disp.min.x<0) ? s+sdigit.x : s;
107696e091f8SDavid du Colombier 	return dxuntransform(tr, ss);
107796e091f8SDavid du Colombier }
107896e091f8SDavid du Colombier 
107996e091f8SDavid du Colombier 
mark_x_axis(const transform * tr)108096e091f8SDavid du Colombier lab_interval mark_x_axis(const transform* tr)
108196e091f8SDavid du Colombier {
108296e091f8SDavid du Colombier 	fpoint p = univ.disp.min;
108396e091f8SDavid du Colombier 	Point q, qtop, qbot, tmp;
108496e091f8SDavid du Colombier 	double x0=univ.disp.min.x, x1=univ.disp.max.x;
108596e091f8SDavid du Colombier 	double seps0, nseps, seps;
108696e091f8SDavid du Colombier 	lab_interval iv = next_larger(min_hsep(tr), x0, x1);
108796e091f8SDavid du Colombier 	set_unslanted_y(&univ, &p.y, 0);
108896e091f8SDavid du Colombier 	q.y = ytransform(tr, p.y) + .5;
108996e091f8SDavid du Colombier 	qtop.y = q.y - tick_len;
109096e091f8SDavid du Colombier 	qbot.y = q.y + framewd + framesep;
109196e091f8SDavid du Colombier 	seps0 = ceil(x0/iv.sep);
109296e091f8SDavid du Colombier 	for (seps=0, nseps=floor(x1/iv.sep)-seps0; seps<=nseps; seps+=1) {
109396e091f8SDavid du Colombier 		char* num = abbrev_num((p.x=iv.sep*(seps0+seps)), &iv);
109496e091f8SDavid du Colombier 		Font* f = display->defaultfont;
109596e091f8SDavid du Colombier 		q.x = qtop.x = qbot.x = xtransform(tr, p.x);
109696e091f8SDavid du Colombier 		line(screen, qtop, q, Enddisc, Enddisc, 0, axis_color, q);
109796e091f8SDavid du Colombier 		tmp = stringsize(f, num);
109896e091f8SDavid du Colombier 		qbot.x -= tmp.x/2;
109996e091f8SDavid du Colombier 		string(screen, qbot, display->black, qbot, f, num);
110096e091f8SDavid du Colombier 	}
110196e091f8SDavid du Colombier 	return iv;
110296e091f8SDavid du Colombier }
110396e091f8SDavid du Colombier 
110496e091f8SDavid du Colombier 
mark_y_axis(const transform * tr)110596e091f8SDavid du Colombier lab_interval mark_y_axis(const transform* tr)
110696e091f8SDavid du Colombier {
110796e091f8SDavid du Colombier 	Font* f = display->defaultfont;
110896e091f8SDavid du Colombier 	fpoint p = univ.disp.min;
110996e091f8SDavid du Colombier 	Point q, qrt, qlft;
111096e091f8SDavid du Colombier 	double y0, y1, seps0, nseps, seps;
111196e091f8SDavid du Colombier 	lab_interval iv;
111296e091f8SDavid du Colombier 	set_unslanted_y(&univ, &y0, &y1);
111396e091f8SDavid du Colombier 	iv = next_larger(dyuntransform(tr,-f->height), y0, y1);
111496e091f8SDavid du Colombier 	q.x = xtransform(tr, p.x) - .5;
111596e091f8SDavid du Colombier 	qrt.x = q.x + tick_len;
111696e091f8SDavid du Colombier 	qlft.x = q.x - (framewd + framesep);
111796e091f8SDavid du Colombier 	seps0 = ceil(y0/iv.sep);
111896e091f8SDavid du Colombier 	for (seps=0, nseps=floor(y1/iv.sep)-seps0; seps<=nseps; seps+=1) {
111996e091f8SDavid du Colombier 		char* num = abbrev_num((p.y=iv.sep*(seps0+seps)), &iv);
112096e091f8SDavid du Colombier 		Point qq = stringsize(f, num);
112196e091f8SDavid du Colombier 		q.y = qrt.y = qlft.y = ytransform(tr, p.y);
112296e091f8SDavid du Colombier 		line(screen, qrt, q, Enddisc, Enddisc, 0, axis_color, q);
112396e091f8SDavid du Colombier 		qq.x = qlft.x - qq.x;
112496e091f8SDavid du Colombier 		qq.y = qlft.y - qq.y/2;
112596e091f8SDavid du Colombier 		string(screen, qq, display->black, qq, f, num);
112696e091f8SDavid du Colombier 	}
112796e091f8SDavid du Colombier 	return iv;
112896e091f8SDavid du Colombier }
112996e091f8SDavid du Colombier 
113096e091f8SDavid du Colombier 
lab_iv_info(const lab_interval * iv,double slant,char * buf,int * n)113196e091f8SDavid du Colombier void lab_iv_info(const lab_interval *iv, double slant, char* buf, int *n)
113296e091f8SDavid du Colombier {
113396e091f8SDavid du Colombier 	if (iv->off > 0)
113496e091f8SDavid du Colombier 		(*n) += sprintf(buf+*n,"-%.12g",iv->off);
113596e091f8SDavid du Colombier 	else if (iv->off < 0)
113696e091f8SDavid du Colombier 		(*n) += sprintf(buf+*n,"+%.12g",-iv->off);
113796e091f8SDavid du Colombier 	if (slant>0)
113896e091f8SDavid du Colombier 		(*n) += sprintf(buf+*n,"-%.6gx", slant);
113996e091f8SDavid du Colombier 	else if (slant<0)
114096e091f8SDavid du Colombier 		(*n) += sprintf(buf+*n,"+%.6gx", -slant);
114196e091f8SDavid du Colombier 	if (abs(iv->logunit) >= Nthous)
114296e091f8SDavid du Colombier 		(*n) += sprintf(buf+*n," in 1e%d units", 3*iv->logunit);
114396e091f8SDavid du Colombier 	else if (iv->logunit > 0)
114496e091f8SDavid du Colombier 		(*n) += sprintf(buf+*n," in %ss", thous_nam[iv->logunit]);
114596e091f8SDavid du Colombier 	else if (iv->logunit < 0)
114696e091f8SDavid du Colombier 		(*n) += sprintf(buf+*n," in %sths", thous_nam[-iv->logunit]);
114796e091f8SDavid du Colombier }
114896e091f8SDavid du Colombier 
114996e091f8SDavid du Colombier 
draw_xy_ranges(const lab_interval * xiv,const lab_interval * yiv)115096e091f8SDavid du Colombier void draw_xy_ranges(const lab_interval *xiv, const lab_interval *yiv)
115196e091f8SDavid du Colombier {
115296e091f8SDavid du Colombier 	Point p;
115396e091f8SDavid du Colombier 	char buf[2*(19+Len_thous+8)+50];
115496e091f8SDavid du Colombier 	int bufn = 0;
115596e091f8SDavid du Colombier 	buf[bufn++] = 'x';
115696e091f8SDavid du Colombier 	lab_iv_info(xiv, 0, buf, &bufn);
115796e091f8SDavid du Colombier 	bufn += sprintf(buf+bufn, "; y");
115896e091f8SDavid du Colombier 	lab_iv_info(yiv, u_slant_amt(&univ), buf, &bufn);
115996e091f8SDavid du Colombier 	buf[bufn] = '\0';
116096e091f8SDavid du Colombier 	p = stringsize(display->defaultfont, buf);
116196e091f8SDavid du Colombier 	top_left = screen->r.min.x + lft_border;
116296e091f8SDavid du Colombier 	p.x = top_right = screen->r.max.x - rt_border - p.x;
116396e091f8SDavid du Colombier 	p.y = screen->r.min.y + outersep;
116496e091f8SDavid du Colombier 	string(screen, p, display->black, p, display->defaultfont, buf);
116596e091f8SDavid du Colombier }
116696e091f8SDavid du Colombier 
116796e091f8SDavid du Colombier 
draw_frame(void)116896e091f8SDavid du Colombier transform draw_frame(void)
116996e091f8SDavid du Colombier {
117096e091f8SDavid du Colombier 	lab_interval x_iv, y_iv;
117196e091f8SDavid du Colombier 	transform tr;
117296e091f8SDavid du Colombier 	Rectangle r = screen->r;
117396e091f8SDavid du Colombier 	lft_border = (univ.disp.min.y<0) ? lft_border0+sdigit.x : lft_border0;
117496e091f8SDavid du Colombier 	tr = cur_trans();
117596e091f8SDavid du Colombier 	r.min.x += lft_border;
117696e091f8SDavid du Colombier 	r.min.y += top_border;
117796e091f8SDavid du Colombier 	r.max.x -= rt_border;
117896e091f8SDavid du Colombier 	r.max.y -= bot_border;
117996e091f8SDavid du Colombier 	border(screen, r, -framewd, axis_color, r.min);
118096e091f8SDavid du Colombier 	x_iv = mark_x_axis(&tr);
118196e091f8SDavid du Colombier 	y_iv = mark_y_axis(&tr);
118296e091f8SDavid du Colombier 	draw_xy_ranges(&x_iv, &y_iv);
118396e091f8SDavid du Colombier 	return tr;
118496e091f8SDavid du Colombier }
118596e091f8SDavid du Colombier 
118696e091f8SDavid du Colombier 
118796e091f8SDavid du Colombier 
118896e091f8SDavid du Colombier /*************************** Finding the selection  ***************************/
118996e091f8SDavid du Colombier 
119096e091f8SDavid du Colombier typedef struct pt_on_fpoly {
119196e091f8SDavid du Colombier 	fpoint p;			/* the point */
119296e091f8SDavid du Colombier 	fpolygon* fp;			/* the fpolygon it lies on */
119396e091f8SDavid du Colombier 	double t;			/* how many knots from the beginning */
119496e091f8SDavid du Colombier } pt_on_fpoly;
119596e091f8SDavid du Colombier 
119696e091f8SDavid du Colombier 
119796e091f8SDavid du Colombier static double myx, myy;
119896e091f8SDavid du Colombier #define mydist(p,o,sl,xwt,ywt)	(myx=(p).x-(o).x, myy=(p).y-sl*(p).x-(o).y,	\
119996e091f8SDavid du Colombier 					xwt*myx*myx + ywt*myy*myy)
120096e091f8SDavid du Colombier 
120196e091f8SDavid du Colombier /* At what fraction of the way from p0[0] to p0[1] is mydist(p,ctr,slant,xwt,ywt)
120296e091f8SDavid du Colombier    minimized?
120396e091f8SDavid du Colombier */
closest_time(const fpoint * p0,const fpoint * ctr,double slant,double xwt,double ywt)120496e091f8SDavid du Colombier double closest_time(const fpoint* p0, const fpoint* ctr, double slant,
120596e091f8SDavid du Colombier 		double xwt, double ywt)
120696e091f8SDavid du Colombier {
120796e091f8SDavid du Colombier 	double p00y=p0[0].y-slant*p0[0].x, p01y=p0[1].y-slant*p0[1].x;
120896e091f8SDavid du Colombier 	double dx=p0[1].x-p0[0].x, dy=p01y-p00y;
120996e091f8SDavid du Colombier 	double x0=p0[0].x-ctr->x, y0=p00y-ctr->y;
121096e091f8SDavid du Colombier 	double bot = xwt*dx*dx + ywt*dy*dy;
121196e091f8SDavid du Colombier 	if (bot==0)
121296e091f8SDavid du Colombier 		return 0;
121396e091f8SDavid du Colombier 	return -(xwt*x0*dx + ywt*y0*dy)/bot;
121496e091f8SDavid du Colombier }
121596e091f8SDavid du Colombier 
121696e091f8SDavid du Colombier 
121796e091f8SDavid du Colombier /* Scan the polygonal path of length len knots starting at p0, and find the
121896e091f8SDavid du Colombier    point that the transformation y=y-x*slant makes closest to the center of *r,
121996e091f8SDavid du Colombier    where *r itself defines the distance metric.  Knots get higher priority than
122096e091f8SDavid du Colombier    points between knots.  If psel->t is negative, always update *psel; otherwise
122196e091f8SDavid du Colombier    update *psel only if the scan can improve it.  Return a boolean that says
122296e091f8SDavid du Colombier    whether *psel was updated.
122396e091f8SDavid du Colombier      Note that *r is a very tiny rectangle (tiny when converted screen pixels)
122496e091f8SDavid du Colombier    such that anything in *r is considered close enough to match the mouse click.
122596e091f8SDavid du Colombier    The purpose of this routine is to be careful in case there is a lot of hidden
122696e091f8SDavid du Colombier    detail in the tiny rectangle *r.
122796e091f8SDavid du Colombier */
improve_pt(fpoint * p0,double len,const frectangle * r,double slant,pt_on_fpoly * psel)122896e091f8SDavid du Colombier int improve_pt(fpoint* p0, double len, const frectangle* r, double slant,
122996e091f8SDavid du Colombier 		pt_on_fpoly* psel)
123096e091f8SDavid du Colombier {
123196e091f8SDavid du Colombier 	fpoint ctr = fcenter(r);
123296e091f8SDavid du Colombier 	double x_wt=2/(r->max.x-r->min.x), y_wt=2/(r->max.y-r->min.y);
123396e091f8SDavid du Colombier 	double xwt=x_wt*x_wt, ywt=y_wt*y_wt;
123496e091f8SDavid du Colombier 	double d, dbest = (psel->t <0) ? 1e30 : mydist(psel->p,ctr,slant,xwt,ywt);
123596e091f8SDavid du Colombier 	double tt, dbest0 = dbest;
123696e091f8SDavid du Colombier 	fpoint pp;
123796e091f8SDavid du Colombier 	int ilen = (int) len;
123896e091f8SDavid du Colombier 	if (len==0 || ilen>0) {
123996e091f8SDavid du Colombier 		int i;
124096e091f8SDavid du Colombier 		for (i=(len==0 ? 0 : 1); i<=ilen; i++) {
124196e091f8SDavid du Colombier 			d = mydist(p0[i], ctr, slant, xwt, ywt);
124296e091f8SDavid du Colombier 			if (d < dbest)
124396e091f8SDavid du Colombier 				{psel->p=p0[i]; psel->t=i; dbest=d;}
124496e091f8SDavid du Colombier 		}
124596e091f8SDavid du Colombier 		return (dbest < dbest0);
124696e091f8SDavid du Colombier 	}
124796e091f8SDavid du Colombier 	tt = closest_time(p0, &ctr, slant, xwt, ywt);
124896e091f8SDavid du Colombier 	if (tt > len)
124996e091f8SDavid du Colombier 		tt = len;
125096e091f8SDavid du Colombier 	pp.x = p0[0].x + tt*(p0[1].x - p0[0].x);
125196e091f8SDavid du Colombier 	pp.y = p0[0].y + tt*(p0[1].y - p0[0].y);
125296e091f8SDavid du Colombier 	if (mydist(pp, ctr, slant, xwt, ywt) < dbest) {
125396e091f8SDavid du Colombier 		psel->p = pp;
125496e091f8SDavid du Colombier 		psel->t = tt;
125596e091f8SDavid du Colombier 		return 1;
125696e091f8SDavid du Colombier 	}
125796e091f8SDavid du Colombier 	return 0;
125896e091f8SDavid du Colombier }
125996e091f8SDavid du Colombier 
126096e091f8SDavid du Colombier 
126196e091f8SDavid du Colombier /* Test *fp against *r after transforming by y=y-x*slope, and set *psel accordingly.
126296e091f8SDavid du Colombier */
select_in_fpoly(fpolygon * fp,const frectangle * r,double slant,pt_on_fpoly * psel)126396e091f8SDavid du Colombier void select_in_fpoly(fpolygon* fp, const frectangle* r, double slant,
126496e091f8SDavid du Colombier 		pt_on_fpoly* psel)
126596e091f8SDavid du Colombier {
126696e091f8SDavid du Colombier 	fpoint *p0=fp->p, *pn=fp->p+fp->n;
126796e091f8SDavid du Colombier 	double l1, l2;
126896e091f8SDavid du Colombier 	if (p0==pn)
126996e091f8SDavid du Colombier 		{improve_pt(p0, 0, r, slant, psel); psel->fp=fp; return;}
127096e091f8SDavid du Colombier 	while ((l1=out_length(p0,pn,*r,slant)) < pn-p0) {
127196e091f8SDavid du Colombier 		fpoint p0sav;
127296e091f8SDavid du Colombier 		int i1 = (int) l1;
127396e091f8SDavid du Colombier 		p0+=i1; l1-=i1;
127496e091f8SDavid du Colombier 		p0sav = *p0;
127596e091f8SDavid du Colombier 		p0[0].x += l1*(p0[1].x - p0[0].x);
127696e091f8SDavid du Colombier 		p0[0].y += l1*(p0[1].y - p0[0].y);
127796e091f8SDavid du Colombier 		l2 = in_length(p0, pn, *r, slant);
127896e091f8SDavid du Colombier 		if (improve_pt(p0, l2, r, slant, psel)) {
127996e091f8SDavid du Colombier 			if (l1==0 && psel->t!=((int) psel->t)) {
128096e091f8SDavid du Colombier 				psel->t = 0;
128196e091f8SDavid du Colombier 				psel->p = *p0;
128296e091f8SDavid du Colombier 			} else if (psel->t < 1)
128396e091f8SDavid du Colombier 				psel->t += l1*(1 - psel->t);
128496e091f8SDavid du Colombier 			psel->t += p0 - fp->p;
128596e091f8SDavid du Colombier 			psel->fp = fp;
128696e091f8SDavid du Colombier 		}
128796e091f8SDavid du Colombier 		*p0 = p0sav;
128896e091f8SDavid du Colombier 		p0 += (l2>0) ? ((int) ceil(l2)) : 1;
128996e091f8SDavid du Colombier 	}
129096e091f8SDavid du Colombier }
129196e091f8SDavid du Colombier 
129296e091f8SDavid du Colombier 
129396e091f8SDavid du Colombier /* Test all the fpolygons against *r after transforming by y=y-x*slope, and return
129496e091f8SDavid du Colombier    the resulting selection, if any.
129596e091f8SDavid du Colombier */
select_in_univ(const frectangle * r,double slant)129696e091f8SDavid du Colombier pt_on_fpoly* select_in_univ(const frectangle* r, double slant)
129796e091f8SDavid du Colombier {
129896e091f8SDavid du Colombier 	static pt_on_fpoly answ;
129996e091f8SDavid du Colombier 	fpolygon* fp;
130096e091f8SDavid du Colombier 	answ.t = -1;
130196e091f8SDavid du Colombier 	for (fp=univ.p; fp!=0; fp=fp->link)
130296e091f8SDavid du Colombier 		if (fintersects(r, &fp->bb, slant))
130396e091f8SDavid du Colombier 			select_in_fpoly(fp, r, slant, &answ);
130496e091f8SDavid du Colombier 	if (answ.t < 0)
130596e091f8SDavid du Colombier 		return 0;
130696e091f8SDavid du Colombier 	return &answ;
130796e091f8SDavid du Colombier }
130896e091f8SDavid du Colombier 
130996e091f8SDavid du Colombier 
131096e091f8SDavid du Colombier 
131196e091f8SDavid du Colombier /**************************** Using the selection  ****************************/
131296e091f8SDavid du Colombier 
131396e091f8SDavid du Colombier pt_on_fpoly cur_sel;			/* current selection if cur_sel.t>=0 */
131496e091f8SDavid du Colombier pt_on_fpoly prev_sel;			/* previous selection if prev_sel.t>=0 (for slant) */
131596e091f8SDavid du Colombier Image* sel_bkg = 0;			/* what's behind the red dot */
131696e091f8SDavid du Colombier 
131796e091f8SDavid du Colombier 
clear_txt(void)131896e091f8SDavid du Colombier void clear_txt(void)
131996e091f8SDavid du Colombier {
132096e091f8SDavid du Colombier 	Rectangle r;
132196e091f8SDavid du Colombier 	r.min = screen->r.min;
132296e091f8SDavid du Colombier 	r.min.x += lft_border;
132396e091f8SDavid du Colombier 	r.min.y += outersep;
132496e091f8SDavid du Colombier 	r.max.x = top_left;
132596e091f8SDavid du Colombier 	r.max.y = r.min.y + smaxch.y;
132696e091f8SDavid du Colombier 	draw(screen, r, display->white, display->opaque, r.min);
132796e091f8SDavid du Colombier 	top_left = r.min.x;
132896e091f8SDavid du Colombier }
132996e091f8SDavid du Colombier 
133096e091f8SDavid du Colombier 
sel_dot_box(const transform * tr)133196e091f8SDavid du Colombier Rectangle sel_dot_box(const transform* tr)
133296e091f8SDavid du Colombier {
133396e091f8SDavid du Colombier 	Point ctr;
133496e091f8SDavid du Colombier 	Rectangle r;
133596e091f8SDavid du Colombier 	if (tr==0)
133696e091f8SDavid du Colombier 		ctr.x = ctr.y = Dotrad;
133796e091f8SDavid du Colombier 	else	do_transform(&ctr, tr, &cur_sel.p);
133896e091f8SDavid du Colombier 	r.min.x=ctr.x-Dotrad;  r.max.x=ctr.x+Dotrad+1;
133996e091f8SDavid du Colombier 	r.min.y=ctr.y-Dotrad;  r.max.y=ctr.y+Dotrad+1;
134096e091f8SDavid du Colombier 	return r;
134196e091f8SDavid du Colombier }
134296e091f8SDavid du Colombier 
134396e091f8SDavid du Colombier 
unselect(const transform * tr)134496e091f8SDavid du Colombier void unselect(const transform* tr)
134596e091f8SDavid du Colombier {
134696e091f8SDavid du Colombier 	transform tra;
134796e091f8SDavid du Colombier 	if (sel_bkg==0)
134896e091f8SDavid du Colombier 		sel_bkg = allocimage(display, sel_dot_box(0), CMAP8, 0, DWhite);
134996e091f8SDavid du Colombier 	clear_txt();
135096e091f8SDavid du Colombier 	if (cur_sel.t < 0)
135196e091f8SDavid du Colombier 		return;
135296e091f8SDavid du Colombier 	prev_sel = cur_sel;
135396e091f8SDavid du Colombier 	if (tr==0)
135496e091f8SDavid du Colombier 		{tra=cur_trans(); tr=&tra;}
135596e091f8SDavid du Colombier 	draw(screen, sel_dot_box(tr), sel_bkg, display->opaque, ZP);
135696e091f8SDavid du Colombier 	cur_sel.t = -1;
135796e091f8SDavid du Colombier }
135896e091f8SDavid du Colombier 
135996e091f8SDavid du Colombier 
136096e091f8SDavid du Colombier /* Text at top right is written first and this low-level routine clobbers it if
136196e091f8SDavid du Colombier    the new top-left text would overwrite it.  However, users of this routine should
136296e091f8SDavid du Colombier    try to keep the new text short enough to avoid this.
136396e091f8SDavid du Colombier */
show_mytext(char * msg)136496e091f8SDavid du Colombier void show_mytext(char* msg)
136596e091f8SDavid du Colombier {
136696e091f8SDavid du Colombier 	Point tmp, pt = screen->r.min;
136796e091f8SDavid du Colombier 	int siz;
136896e091f8SDavid du Colombier 	tmp = stringsize(display->defaultfont, msg);
136996e091f8SDavid du Colombier 	siz = tmp.x;
137096e091f8SDavid du Colombier 	pt.x=top_left;  pt.y+=outersep;
137196e091f8SDavid du Colombier 	if (top_left+siz > top_right) {
137296e091f8SDavid du Colombier 		Rectangle r;
137396e091f8SDavid du Colombier 		r.min.y = pt.y;
137496e091f8SDavid du Colombier 		r.min.x = top_right;
137596e091f8SDavid du Colombier 		r.max.y = r.min.y + smaxch.y;
137696e091f8SDavid du Colombier 		r.max.x = top_left+siz;
137796e091f8SDavid du Colombier 		draw(screen, r, display->white, display->opaque, r.min);
137896e091f8SDavid du Colombier 		top_right = top_left+siz;
137996e091f8SDavid du Colombier 	}
138096e091f8SDavid du Colombier 	string(screen, pt, display->black, ZP, display->defaultfont, msg);
138196e091f8SDavid du Colombier 	top_left += siz;
138296e091f8SDavid du Colombier }
138396e091f8SDavid du Colombier 
138496e091f8SDavid du Colombier 
rnd(double x,double tol)138596e091f8SDavid du Colombier double rnd(double x, double tol)	/* round to enough digits for accuracy tol */
138696e091f8SDavid du Colombier {
138796e091f8SDavid du Colombier 	double t = pow(10, floor(log10(tol)));
138896e091f8SDavid du Colombier 	return t * floor(x/t + .5);
138996e091f8SDavid du Colombier }
139096e091f8SDavid du Colombier 
t_tol(double xtol,double ytol)139196e091f8SDavid du Colombier double t_tol(double xtol, double ytol)
139296e091f8SDavid du Colombier {
139396e091f8SDavid du Colombier 	int t = (int) floor(cur_sel.t);
139496e091f8SDavid du Colombier 	fpoint* p = cur_sel.fp->p;
139596e091f8SDavid du Colombier 	double dx, dy;
139696e091f8SDavid du Colombier 	if (t==cur_sel.t)
139796e091f8SDavid du Colombier 		return 1;
139896e091f8SDavid du Colombier 	dx = fabs(p[t+1].x - p[t].x);
139996e091f8SDavid du Colombier 	dy = fabs(p[t+1].y - p[t].y);
140096e091f8SDavid du Colombier 	xtol /= (xtol>dx) ? xtol : dx;
140196e091f8SDavid du Colombier 	ytol /= (ytol>dy) ? ytol : dy;
140296e091f8SDavid du Colombier 	return (xtol<ytol) ? xtol : ytol;
140396e091f8SDavid du Colombier }
140496e091f8SDavid du Colombier 
say_where(const transform * tr)140596e091f8SDavid du Colombier void say_where(const transform* tr)
140696e091f8SDavid du Colombier {
140796e091f8SDavid du Colombier 	double xtol=dxuntransform(tr,1), ytol=dyuntransform(tr,-1);
140896e091f8SDavid du Colombier 	char buf[100];
140996e091f8SDavid du Colombier 	int n, nmax = (top_right - top_left)/smaxch.x;
141096e091f8SDavid du Colombier 	if (nmax >= 100)
141196e091f8SDavid du Colombier 		nmax = 100-1;
141296e091f8SDavid du Colombier 	n = sprintf(buf,"(%.14g,%.14g) at t=%.14g",
141396e091f8SDavid du Colombier 			rnd(cur_sel.p.x,xtol), rnd(cur_sel.p.y,ytol),
141496e091f8SDavid du Colombier 			rnd(cur_sel.t, t_tol(xtol,ytol)));
141596e091f8SDavid du Colombier 	if (cur_sel.fp->nam[0] != 0)
141696e091f8SDavid du Colombier 		sprintf(buf+n," %.*s", nmax-n-1, cur_sel.fp->nam);
141796e091f8SDavid du Colombier 	show_mytext(buf);
141896e091f8SDavid du Colombier }
141996e091f8SDavid du Colombier 
142096e091f8SDavid du Colombier 
reselect(const transform * tr)142196e091f8SDavid du Colombier void reselect(const transform* tr)	/* uselect(); set cur_sel; call this */
142296e091f8SDavid du Colombier {
142396e091f8SDavid du Colombier 	Point pt2, pt3;
142496e091f8SDavid du Colombier 	fpoint p2;
142596e091f8SDavid du Colombier 	transform tra;
142696e091f8SDavid du Colombier 	if (cur_sel.t < 0)
142796e091f8SDavid du Colombier 		return;
142896e091f8SDavid du Colombier 	if (tr==0)
142996e091f8SDavid du Colombier 		{tra=cur_trans(); tr=&tra;}
143096e091f8SDavid du Colombier 	do_transform(&p2, tr, &cur_sel.p);
143196e091f8SDavid du Colombier 	if (fabs(p2.x)+fabs(p2.y)>1e8 || (pt2.x=p2.x, pt2.y=p2.y, is_off_screen(pt2)))
143296e091f8SDavid du Colombier 		{cur_sel.t= -1; return;}
143396e091f8SDavid du Colombier 	pt3.x=pt2.x-Dotrad;  pt3.y=pt2.y-Dotrad;
143496e091f8SDavid du Colombier 	draw(sel_bkg, sel_dot_box(0), screen, display->opaque, pt3);
143596e091f8SDavid du Colombier 	fillellipse(screen, pt2, Dotrad, Dotrad, clr_im(DRed), pt2);
143696e091f8SDavid du Colombier 	say_where(tr);
143796e091f8SDavid du Colombier }
143896e091f8SDavid du Colombier 
143996e091f8SDavid du Colombier 
do_select(Point pt)144096e091f8SDavid du Colombier void do_select(Point pt)
144196e091f8SDavid du Colombier {
144296e091f8SDavid du Colombier 	transform tr = cur_trans();
144396e091f8SDavid du Colombier 	fpoint pt1, pt2, ctr;
144496e091f8SDavid du Colombier 	frectangle r;
144596e091f8SDavid du Colombier 	double slant;
144696e091f8SDavid du Colombier 	pt_on_fpoly* psel;
144796e091f8SDavid du Colombier 	unselect(&tr);
144896e091f8SDavid du Colombier 	do_untransform(&ctr, &tr, &pt);
144996e091f8SDavid du Colombier 	pt1.x=pt.x-fuzz;  pt1.y=pt.y+fuzz;
145096e091f8SDavid du Colombier 	pt2.x=pt.x+fuzz;  pt2.y=pt.y-fuzz;
145196e091f8SDavid du Colombier 	do_untransform(&r.min, &tr, &pt1);
145296e091f8SDavid du Colombier 	do_untransform(&r.max, &tr, &pt2);
145396e091f8SDavid du Colombier 	slant = u_slant_amt(&univ);
145496e091f8SDavid du Colombier 	slant_frect(&r, -slant);
145596e091f8SDavid du Colombier 	psel = select_in_univ(&r, slant);
145696e091f8SDavid du Colombier 	if (psel==0)
145796e091f8SDavid du Colombier 		return;
145896e091f8SDavid du Colombier 	if (logfil!=0) {
145996e091f8SDavid du Colombier 		fprintf(logfil,"%.14g\t%.14g\n", psel->p.x, psel->p.y);
146096e091f8SDavid du Colombier 		fflush(logfil);
146196e091f8SDavid du Colombier 	}
146296e091f8SDavid du Colombier 	cur_sel = *psel;
146396e091f8SDavid du Colombier 	reselect(&tr);
146496e091f8SDavid du Colombier }
146596e091f8SDavid du Colombier 
146696e091f8SDavid du Colombier 
146796e091f8SDavid du Colombier /***************************** Prompting for text *****************************/
146896e091f8SDavid du Colombier 
unshow_mytext(char * msg)146996e091f8SDavid du Colombier void unshow_mytext(char* msg)
147096e091f8SDavid du Colombier {
147196e091f8SDavid du Colombier 	Rectangle r;
147296e091f8SDavid du Colombier 	Point siz = stringsize(display->defaultfont, msg);
147396e091f8SDavid du Colombier 	top_left -= siz.x;
147496e091f8SDavid du Colombier 	r.min.y = screen->r.min.y + outersep;
147596e091f8SDavid du Colombier 	r.min.x = top_left;
147696e091f8SDavid du Colombier 	r.max.y = r.min.y + siz.y;
147796e091f8SDavid du Colombier 	r.max.x = r.min.x + siz.x;
147896e091f8SDavid du Colombier 	draw(screen, r, display->white, display->opaque, r.min);
147996e091f8SDavid du Colombier }
148096e091f8SDavid du Colombier 
148196e091f8SDavid du Colombier 
148296e091f8SDavid du Colombier /* Show the given prompt and read a line of user input.  The text appears at the
148396e091f8SDavid du Colombier    top left.  If it runs into the top right text, we stop echoing but let the user
148496e091f8SDavid du Colombier    continue typing blind if he wants to.
148596e091f8SDavid du Colombier */
prompt_text(char * prompt)148696e091f8SDavid du Colombier char* prompt_text(char* prompt)
148796e091f8SDavid du Colombier {
148896e091f8SDavid du Colombier 	static char buf[200];
148996e091f8SDavid du Colombier 	int n0, n=0, nshown=0;
149096e091f8SDavid du Colombier 	Rune c;
149196e091f8SDavid du Colombier 	unselect(0);
149296e091f8SDavid du Colombier 	show_mytext(prompt);
149396e091f8SDavid du Colombier 	while (n<200-1-UTFmax && (c=ekbd())!='\n') {
149496e091f8SDavid du Colombier 		if (c=='\b') {
149596e091f8SDavid du Colombier 			buf[n] = 0;
149696e091f8SDavid du Colombier 			if (n > 0)
149796e091f8SDavid du Colombier 				do n--;
149896e091f8SDavid du Colombier 				while (n>0 && (buf[n-1]&0xc0)==0x80);
149996e091f8SDavid du Colombier 			if (n < nshown)
150096e091f8SDavid du Colombier 				{unshow_mytext(buf+n); nshown=n;}
150196e091f8SDavid du Colombier 		} else {
150296e091f8SDavid du Colombier 			n0 = n;
150396e091f8SDavid du Colombier 			n += runetochar(buf+n, &c);
150496e091f8SDavid du Colombier 			buf[n] = 0;
150596e091f8SDavid du Colombier 			if (nshown==n0 && top_right-top_left >= smaxch.x)
150696e091f8SDavid du Colombier 				{show_mytext(buf+n0); nshown=n;}
150796e091f8SDavid du Colombier 		}
150896e091f8SDavid du Colombier 	}
150996e091f8SDavid du Colombier 	buf[n] = 0;
151096e091f8SDavid du Colombier 	while (ecanmouse())
151196e091f8SDavid du Colombier 		emouse();
151296e091f8SDavid du Colombier 	return buf;
151396e091f8SDavid du Colombier }
151496e091f8SDavid du Colombier 
151596e091f8SDavid du Colombier 
151696e091f8SDavid du Colombier /**************************** Redrawing the screen ****************************/
151796e091f8SDavid du Colombier 
151896e091f8SDavid du Colombier /* Let p0 and its successors define a piecewise-linear function of a paramter t,
151996e091f8SDavid du Colombier    and draw the 0<=t<=n1 portion using transform *tr.
152096e091f8SDavid du Colombier */
draw_fpts(const fpoint * p0,double n1,const transform * tr,int thick,Image * clr)152196e091f8SDavid du Colombier void draw_fpts(const fpoint* p0, double n1, const transform* tr, int thick,
152296e091f8SDavid du Colombier 		Image* clr)
152396e091f8SDavid du Colombier {
152496e091f8SDavid du Colombier 	int n = (int) n1;
152596e091f8SDavid du Colombier 	const fpoint* p = p0 + n;
152696e091f8SDavid du Colombier 	fpoint pp;
152796e091f8SDavid du Colombier 	Point qq, q;
152896e091f8SDavid du Colombier 	if (n1 > n) {
152996e091f8SDavid du Colombier 		pp.x = p[0].x + (n1-n)*(p[1].x - p[0].x);
153096e091f8SDavid du Colombier 		pp.y = p[0].y + (n1-n)*(p[1].y - p[0].y);
153196e091f8SDavid du Colombier 	} else	pp = *p--;
153296e091f8SDavid du Colombier 	do_transform(&qq, tr, &pp);
153396e091f8SDavid du Colombier 	if (n1==0)
153496e091f8SDavid du Colombier 		fillellipse(screen, qq, 1+thick, 1+thick, clr, qq);
153596e091f8SDavid du Colombier 	for (; p>=p0; p--) {
153696e091f8SDavid du Colombier 		do_transform(&q, tr, p);
15378db68488SDavid du Colombier 		if(plotdots)
15388db68488SDavid du Colombier 			fillellipse(screen, q, Dotrad, Dotrad, clr, q);
15398db68488SDavid du Colombier 		else
154096e091f8SDavid du Colombier 			line(screen, qq, q, Enddisc, Enddisc, thick, clr, qq);
154196e091f8SDavid du Colombier 		qq = q;
154296e091f8SDavid du Colombier 	}
154396e091f8SDavid du Colombier }
154496e091f8SDavid du Colombier 
draw_1fpoly(const fpolygon * fp,const transform * tr,Image * clr,const frectangle * udisp,double slant)154596e091f8SDavid du Colombier void draw_1fpoly(const fpolygon* fp, const transform* tr, Image* clr,
154696e091f8SDavid du Colombier 		const frectangle *udisp, double slant)
154796e091f8SDavid du Colombier {
154896e091f8SDavid du Colombier 	fpoint *p0=fp->p, *pn=fp->p+fp->n;
154996e091f8SDavid du Colombier 	double l1, l2;
155096e091f8SDavid du Colombier 	if (p0==pn && fcontains(udisp,*p0))
15510c6300e7SDavid du Colombier 		{draw_fpts(p0, 0, tr, fp->c.thick, clr); return;}
155296e091f8SDavid du Colombier 	while ((l1=out_length(p0,pn,*udisp,slant)) < pn-p0) {
155396e091f8SDavid du Colombier 		fpoint p0sav;
155496e091f8SDavid du Colombier 		int i1 = (int) l1;
155596e091f8SDavid du Colombier 		p0+=i1; l1-=i1;
155696e091f8SDavid du Colombier 		p0sav = *p0;
155796e091f8SDavid du Colombier 		p0[0].x += l1*(p0[1].x - p0[0].x);
155896e091f8SDavid du Colombier 		p0[0].y += l1*(p0[1].y - p0[0].y);
155996e091f8SDavid du Colombier 		l2 = in_length(p0, pn, *udisp, slant);
15600c6300e7SDavid du Colombier 		draw_fpts(p0, l2, tr, fp->c.thick, clr);
156196e091f8SDavid du Colombier 		*p0 = p0sav;
156296e091f8SDavid du Colombier 		p0 += (l2>0) ? ((int) ceil(l2)) : 1;
156396e091f8SDavid du Colombier 	}
156496e091f8SDavid du Colombier }
156596e091f8SDavid du Colombier 
156696e091f8SDavid du Colombier 
get_clip_data(const fpolygons * u,frectangle * r)156796e091f8SDavid du Colombier double get_clip_data(const fpolygons *u, frectangle *r)
156896e091f8SDavid du Colombier {
156996e091f8SDavid du Colombier 	double slant = set_unslanted_y(u, &r->min.y, &r->max.y);
157096e091f8SDavid du Colombier 	r->min.x = u->disp.min.x;
157196e091f8SDavid du Colombier 	r->max.x = u->disp.max.x;
157296e091f8SDavid du Colombier 	return slant;
157396e091f8SDavid du Colombier }
157496e091f8SDavid du Colombier 
157596e091f8SDavid du Colombier 
draw_fpoly(const fpolygon * fp,const transform * tr,Image * clr)157696e091f8SDavid du Colombier void draw_fpoly(const fpolygon* fp, const transform* tr, Image* clr)
157796e091f8SDavid du Colombier {
157896e091f8SDavid du Colombier 	frectangle r;
157996e091f8SDavid du Colombier 	double slant = get_clip_data(&univ, &r);
158096e091f8SDavid du Colombier 	draw_1fpoly(fp, tr, clr, &r, slant);
158196e091f8SDavid du Colombier }
158296e091f8SDavid du Colombier 
158396e091f8SDavid du Colombier 
eresized(int new)158496e091f8SDavid du Colombier void eresized(int new)
158596e091f8SDavid du Colombier {
158696e091f8SDavid du Colombier 	transform tr;
158796e091f8SDavid du Colombier 	fpolygon* fp;
158896e091f8SDavid du Colombier 	frectangle clipr;
158996e091f8SDavid du Colombier 	double slant;
159096e091f8SDavid du Colombier 	if(new && getwindow(display, Refmesg) < 0) {
159196e091f8SDavid du Colombier 		fprintf(stderr,"can't reattach to window\n");
159296e091f8SDavid du Colombier 		exits("reshap");
159396e091f8SDavid du Colombier 	}
159496e091f8SDavid du Colombier 	draw(screen, screen->r, display->white, display->opaque, screen->r.min);
159596e091f8SDavid du Colombier 	tr = draw_frame();
159696e091f8SDavid du Colombier 	slant = get_clip_data(&univ, &clipr);
159796e091f8SDavid du Colombier 	for (fp=univ.p; fp!=0; fp=fp->link)
159896e091f8SDavid du Colombier 		if (fintersects(&clipr, &fp->bb, slant))
15990c6300e7SDavid du Colombier 			draw_1fpoly(fp, &tr, fp->c.clr, &clipr, slant);
160096e091f8SDavid du Colombier 	reselect(0);
160196e091f8SDavid du Colombier 	if (mv_bkgd!=0 && mv_bkgd->repl==0) {
160296e091f8SDavid du Colombier 		freeimage(mv_bkgd);
160396e091f8SDavid du Colombier 		mv_bkgd = display->white;
160496e091f8SDavid du Colombier 	}
160596e091f8SDavid du Colombier 	flushimage(display, 1);
160696e091f8SDavid du Colombier }
160796e091f8SDavid du Colombier 
160896e091f8SDavid du Colombier 
160996e091f8SDavid du Colombier 
161096e091f8SDavid du Colombier 
161196e091f8SDavid du Colombier /********************************* Recoloring *********************************/
161296e091f8SDavid du Colombier 
draw_palette(int n)161396e091f8SDavid du Colombier int draw_palette(int n)		/* n is number of colors; returns patch dy */
161496e091f8SDavid du Colombier {
161596e091f8SDavid du Colombier 	int y0 = screen->r.min.y + top_border;
161696e091f8SDavid du Colombier 	int dy = (screen->r.max.y - bot_border - y0)/n;
161796e091f8SDavid du Colombier 	Rectangle r;
161896e091f8SDavid du Colombier 	int i;
161996e091f8SDavid du Colombier 	r.min.y = y0;
162096e091f8SDavid du Colombier 	r.min.x = screen->r.max.x - rt_border + framewd;
162196e091f8SDavid du Colombier 	r.max.y = y0 + dy;
162296e091f8SDavid du Colombier 	r.max.x = screen->r.max.x;
162396e091f8SDavid du Colombier 	for (i=0; i<n; i++) {
162496e091f8SDavid du Colombier 		draw(screen, r, clrtab[i].im, display->opaque, r.min);
162596e091f8SDavid du Colombier 		r.min.y = r.max.y;
162696e091f8SDavid du Colombier 		r.max.y += dy;
162796e091f8SDavid du Colombier 	}
162896e091f8SDavid du Colombier 	return dy;
162996e091f8SDavid du Colombier }
163096e091f8SDavid du Colombier 
163196e091f8SDavid du Colombier 
palette_color(Point pt,int dy,int n)163296e091f8SDavid du Colombier Image* palette_color(Point pt, int dy, int n)
163396e091f8SDavid du Colombier {				/* mouse at pt, patch size dy, n colors */
163496e091f8SDavid du Colombier 	int yy;
163596e091f8SDavid du Colombier 	if (screen->r.max.x - pt.x > rt_border - framewd)
163696e091f8SDavid du Colombier 		return 0;
163796e091f8SDavid du Colombier 	yy = pt.y - (screen->r.min.y + top_border);
163896e091f8SDavid du Colombier 	if (yy<0 || yy>=n*dy)
163996e091f8SDavid du Colombier 		return 0;
164096e091f8SDavid du Colombier 	return clrtab[yy/dy].im;
164196e091f8SDavid du Colombier }
164296e091f8SDavid du Colombier 
164396e091f8SDavid du Colombier 
all_set_clr(fpolygons * fps,Image * clr)164496e091f8SDavid du Colombier void all_set_clr(fpolygons* fps, Image* clr)
164596e091f8SDavid du Colombier {
164696e091f8SDavid du Colombier 	fpolygon* p;
164796e091f8SDavid du Colombier 	for (p=fps->p; p!=0; p=p->link)
16480c6300e7SDavid du Colombier 		p->c.clr = clr;
16490c6300e7SDavid du Colombier }
16500c6300e7SDavid du Colombier 
16510c6300e7SDavid du Colombier 
all_set_scheme(fpolygons * fps,int scheme)16520c6300e7SDavid du Colombier void all_set_scheme(fpolygons* fps, int scheme)
16530c6300e7SDavid du Colombier {
16540c6300e7SDavid du Colombier 	fpolygon* p;
16550c6300e7SDavid du Colombier 	for (p=fps->p; p!=0; p=p->link)
16560c6300e7SDavid du Colombier 		if (p->ct!=0 && scheme <= p->ct[0].thick)
16570c6300e7SDavid du Colombier 			p->c = p->ct[scheme];
165896e091f8SDavid du Colombier }
165996e091f8SDavid du Colombier 
166096e091f8SDavid du Colombier 
do_recolor(int but,Mouse * m,int alluniv)166196e091f8SDavid du Colombier void do_recolor(int but, Mouse* m, int alluniv)
166296e091f8SDavid du Colombier {
16630c6300e7SDavid du Colombier 	int sel, clkk, nclr = clr_id(DWhite);
166496e091f8SDavid du Colombier 	int dy = draw_palette(nclr);
166596e091f8SDavid du Colombier 	Image* clr;
16660c6300e7SDavid du Colombier 	clkk = get_click_or_kbd(but, m, "123456789abcdefghijklmnopqrstuvwxyz");
16670c6300e7SDavid du Colombier 	if (clkk < 0) {
166896e091f8SDavid du Colombier 		clr = palette_color(m->xy, dy, nclr);
166996e091f8SDavid du Colombier 		if (clr != 0) {
167096e091f8SDavid du Colombier 			if (alluniv)
167196e091f8SDavid du Colombier 				all_set_clr(&univ, clr);
16720c6300e7SDavid du Colombier 			else cur_sel.fp->c.clr = clr;
167396e091f8SDavid du Colombier 		}
167496e091f8SDavid du Colombier 		eresized(0);
167596e091f8SDavid du Colombier 		lift_button(but, m, Never);
16760c6300e7SDavid du Colombier 	} else if (clkk > 0) {
16770c6300e7SDavid du Colombier 		sel = ('0'<clkk&&clkk<='9') ? 0 : 10+(clkk-'a')*10;
16780c6300e7SDavid du Colombier 		while (!('0'<=clkk&&clkk<='9'))
16790c6300e7SDavid du Colombier 			clkk = ekbd();
16800c6300e7SDavid du Colombier 		sel += clkk-'0';
16810c6300e7SDavid du Colombier 		if (alluniv)
16820c6300e7SDavid du Colombier 			all_set_scheme(&univ, sel);
16830c6300e7SDavid du Colombier 		else if (sel <= cur_sel.fp->ct[0].thick)
16840c6300e7SDavid du Colombier 			cur_sel.fp->c = cur_sel.fp->ct[sel];
16850c6300e7SDavid du Colombier 	}
16860c6300e7SDavid du Colombier 	eresized(0);
168796e091f8SDavid du Colombier }
168896e091f8SDavid du Colombier 
168996e091f8SDavid du Colombier 
169096e091f8SDavid du Colombier /****************************** Move and rotate  ******************************/
169196e091f8SDavid du Colombier 
prepare_mv(const fpolygon * fp)169296e091f8SDavid du Colombier void prepare_mv(const fpolygon* fp)
169396e091f8SDavid du Colombier {
169496e091f8SDavid du Colombier 	Rectangle r = screen->r;
169596e091f8SDavid du Colombier 	Image* scr0;
16960c6300e7SDavid du Colombier 	int dt = 1 + fp->c.thick;
169796e091f8SDavid du Colombier 	r.min.x+=lft_border-dt;  r.min.y+=top_border-dt;
169896e091f8SDavid du Colombier 	r.max.x-=rt_border-dt;   r.max.y-=bot_border-dt;
169996e091f8SDavid du Colombier 	if (mv_bkgd!=0 && mv_bkgd->repl==0)
170096e091f8SDavid du Colombier 		freeimage(mv_bkgd);
170196e091f8SDavid du Colombier 	mv_bkgd = allocimage(display, r, CMAP8, 0, DNofill);
170296e091f8SDavid du Colombier 	if (mv_bkgd==0)
170396e091f8SDavid du Colombier 		mv_bkgd = display->white;
170496e091f8SDavid du Colombier 	else {	transform tr = cur_trans();
170596e091f8SDavid du Colombier 		draw(mv_bkgd, r, screen, display->opaque, r.min);
170696e091f8SDavid du Colombier 		draw(mv_bkgd, sel_dot_box(&tr), sel_bkg, display->opaque, ZP);
170796e091f8SDavid du Colombier 		scr0 = screen;
170896e091f8SDavid du Colombier 		screen = mv_bkgd;
170996e091f8SDavid du Colombier 		draw_fpoly(fp, &tr, display->white);
171096e091f8SDavid du Colombier 		screen = scr0;
171196e091f8SDavid du Colombier 	}
171296e091f8SDavid du Colombier }
171396e091f8SDavid du Colombier 
171496e091f8SDavid du Colombier 
move_fp(fpolygon * fp,double dx,double dy)171596e091f8SDavid du Colombier void move_fp(fpolygon* fp, double dx, double dy)
171696e091f8SDavid du Colombier {
171796e091f8SDavid du Colombier 	fpoint *p, *pn=fp->p+fp->n;
171896e091f8SDavid du Colombier 	for (p=fp->p; p<=pn; p++) {
171996e091f8SDavid du Colombier 		(p->x) += dx;
172096e091f8SDavid du Colombier 		(p->y) += dy;
172196e091f8SDavid du Colombier 	}
172296e091f8SDavid du Colombier 	(fp->bb.min.x)+=dx;  (fp->bb.min.y)+=dy;
172396e091f8SDavid du Colombier 	(fp->bb.max.x)+=dx;  (fp->bb.max.y)+=dy;
172496e091f8SDavid du Colombier }
172596e091f8SDavid du Colombier 
172696e091f8SDavid du Colombier 
rotate_fp(fpolygon * fp,fpoint o,double theta)172796e091f8SDavid du Colombier void rotate_fp(fpolygon* fp, fpoint o, double theta)
172896e091f8SDavid du Colombier {
172996e091f8SDavid du Colombier 	double s=sin(theta), c=cos(theta);
173096e091f8SDavid du Colombier 	fpoint *p, *pn=fp->p+fp->n;
173196e091f8SDavid du Colombier 	for (p=fp->p; p<=pn; p++) {
173296e091f8SDavid du Colombier 		double x=p->x-o.x, y=p->y-o.y;
173396e091f8SDavid du Colombier 		(p->x) = o.x + c*x - s*y;
173496e091f8SDavid du Colombier 		(p->y) = o.y + s*x + c*y;
173596e091f8SDavid du Colombier 	}
173696e091f8SDavid du Colombier 	set_fbb(fp);
173796e091f8SDavid du Colombier }
173896e091f8SDavid du Colombier 
173996e091f8SDavid du Colombier 
174096e091f8SDavid du Colombier /* Move the selected fpolygon so the selected point tracks the mouse, and return
174196e091f8SDavid du Colombier    the total amount of movement.  Button but has already been held down for at
174296e091f8SDavid du Colombier    least Mv_delay milliseconds and the mouse might have moved some distance.
174396e091f8SDavid du Colombier */
do_move(int but,Mouse * m)174496e091f8SDavid du Colombier fpoint do_move(int but, Mouse* m)
174596e091f8SDavid du Colombier {
174696e091f8SDavid du Colombier 	transform tr = cur_trans();
174796e091f8SDavid du Colombier 	int bbit = Button_bit(but);
174896e091f8SDavid du Colombier 	fpolygon* fp = cur_sel.fp;
174996e091f8SDavid du Colombier 	fpoint loc, loc0=cur_sel.p;
175096e091f8SDavid du Colombier 	double tsav = cur_sel.t;
175196e091f8SDavid du Colombier 	unselect(&tr);
175296e091f8SDavid du Colombier 	do {	latest_mouse(but, m);
17530c6300e7SDavid du Colombier 		(fp->c.thick)++;		/* line() DISAGREES WITH ITSELF */
175496e091f8SDavid du Colombier 		draw_fpoly(fp, &tr, mv_bkgd);
17550c6300e7SDavid du Colombier 		(fp->c.thick)--;
175696e091f8SDavid du Colombier 		do_untransform(&loc, &tr, &m->xy);
175796e091f8SDavid du Colombier 		move_fp(fp, loc.x-cur_sel.p.x, loc.y-cur_sel.p.y);
175896e091f8SDavid du Colombier 		cur_sel.p = loc;
17590c6300e7SDavid du Colombier 		draw_fpoly(fp, &tr, fp->c.clr);
176096e091f8SDavid du Colombier 	} while (m->buttons & bbit);
176196e091f8SDavid du Colombier 	cur_sel.t = tsav;
176296e091f8SDavid du Colombier 	reselect(&tr);
176396e091f8SDavid du Colombier 	loc.x -= loc0.x;
176496e091f8SDavid du Colombier 	loc.y -= loc0.y;
176596e091f8SDavid du Colombier 	return loc;
176696e091f8SDavid du Colombier }
176796e091f8SDavid du Colombier 
176896e091f8SDavid du Colombier 
dir_angle(const Point * pt,const transform * tr)176996e091f8SDavid du Colombier double dir_angle(const Point* pt, const transform* tr)
177096e091f8SDavid du Colombier {
177196e091f8SDavid du Colombier 	fpoint p;
177296e091f8SDavid du Colombier 	double dy, dx;
177396e091f8SDavid du Colombier 	do_untransform(&p, tr, pt);
177496e091f8SDavid du Colombier 	dy=p.y-cur_sel.p.y;  dx=p.x-cur_sel.p.x;
177596e091f8SDavid du Colombier 	return (dx==0 && dy==0) ? 0.0 : atan2(dy, dx);
177696e091f8SDavid du Colombier }
177796e091f8SDavid du Colombier 
177896e091f8SDavid du Colombier 
177996e091f8SDavid du Colombier /* Rotate the selected fpolygon around the selection point so as to track the
178096e091f8SDavid du Colombier    direction angle from the selected point to m->xy.  Stop when button but goes
178196e091f8SDavid du Colombier    up and return the total amount of rotation in radians.
178296e091f8SDavid du Colombier */
do_rotate(int but,Mouse * m)178396e091f8SDavid du Colombier double do_rotate(int but, Mouse* m)
178496e091f8SDavid du Colombier {
178596e091f8SDavid du Colombier 	transform tr = cur_trans();
178696e091f8SDavid du Colombier 	int bbit = Button_bit(but);
178796e091f8SDavid du Colombier 	fpolygon* fp = cur_sel.fp;
178896e091f8SDavid du Colombier 	double theta0 = dir_angle(&m->xy, &tr);
178996e091f8SDavid du Colombier 	double th, theta = theta0;
179096e091f8SDavid du Colombier 	do {	latest_mouse(but, m);
17910c6300e7SDavid du Colombier 		(fp->c.thick)++;		/* line() DISAGREES WITH ITSELF */
179296e091f8SDavid du Colombier 		draw_fpoly(fp, &tr, mv_bkgd);
17930c6300e7SDavid du Colombier 		(fp->c.thick)--;
179496e091f8SDavid du Colombier 		th = dir_angle(&m->xy, &tr);
179596e091f8SDavid du Colombier 		rotate_fp(fp, cur_sel.p, th-theta);
179696e091f8SDavid du Colombier 		theta = th;
17970c6300e7SDavid du Colombier 		draw_fpoly(fp, &tr, fp->c.clr);
179896e091f8SDavid du Colombier 	} while (m->buttons & bbit);
179996e091f8SDavid du Colombier 	unselect(&tr);
180096e091f8SDavid du Colombier 	cur_sel = prev_sel;
180196e091f8SDavid du Colombier 	reselect(&tr);
180296e091f8SDavid du Colombier 	return theta - theta0;
180396e091f8SDavid du Colombier }
180496e091f8SDavid du Colombier 
180596e091f8SDavid du Colombier 
180696e091f8SDavid du Colombier 
180796e091f8SDavid du Colombier /********************************* Edit menu  *********************************/
180896e091f8SDavid du Colombier 
180996e091f8SDavid du Colombier typedef enum e_index {
181096e091f8SDavid du Colombier 		Erecolor, Ethick, Edelete, Eundo, Erotate, Eoptions,
181196e091f8SDavid du Colombier 		Emove
181296e091f8SDavid du Colombier } e_index;
181396e091f8SDavid du Colombier 
181496e091f8SDavid du Colombier char* e_items[Eoptions+1];
181596e091f8SDavid du Colombier 
181696e091f8SDavid du Colombier Menu e_menu = {e_items, 0, 0};
181796e091f8SDavid du Colombier 
181896e091f8SDavid du Colombier 
181996e091f8SDavid du Colombier typedef struct e_action {
182096e091f8SDavid du Colombier 	e_index typ;			/* What type of action */
182196e091f8SDavid du Colombier 	fpolygon* fp;			/* fpolygon the action applies to */
182296e091f8SDavid du Colombier 	Image* clr;			/* color to use if typ==Erecolor */
182396e091f8SDavid du Colombier 	double amt;			/* rotation angle or line thickness */
182496e091f8SDavid du Colombier 	fpoint pt;			/* movement vector or rotation center */
182596e091f8SDavid du Colombier 	struct e_action* link;		/* next in a stack */
182696e091f8SDavid du Colombier } e_action;
182796e091f8SDavid du Colombier 
182896e091f8SDavid du Colombier e_action* unact = 0;			/* heads a linked list of actions */
182996e091f8SDavid du Colombier e_action* do_undo(e_action*);		/* pop off an e_action and (un)do it */
183096e091f8SDavid du Colombier e_action* save_act(e_action*,e_index);	/* append new e_action for status quo */
183196e091f8SDavid du Colombier 
183296e091f8SDavid du Colombier 
save_mv(fpoint movement)183396e091f8SDavid du Colombier void save_mv(fpoint movement)
183496e091f8SDavid du Colombier {
183596e091f8SDavid du Colombier 	unact = save_act(unact, Emove);
183696e091f8SDavid du Colombier 	unact->pt = movement;
183796e091f8SDavid du Colombier }
183896e091f8SDavid du Colombier 
183996e091f8SDavid du Colombier 
init_e_menu(void)184096e091f8SDavid du Colombier void init_e_menu(void)
184196e091f8SDavid du Colombier {
184296e091f8SDavid du Colombier 	char* u = "can't undo";
184396e091f8SDavid du Colombier 	e_items[Erecolor] = "recolor";
184496e091f8SDavid du Colombier 	e_items[Edelete] = "delete";
184596e091f8SDavid du Colombier 	e_items[Erotate] = "rotate";
184696e091f8SDavid du Colombier 	e_items[Eoptions-cantmv] = 0;
18470c6300e7SDavid du Colombier 	e_items[Ethick] = (cur_sel.fp->c.thick >0) ? "thin" : "thick";
184896e091f8SDavid du Colombier 	if (unact!=0)
184996e091f8SDavid du Colombier 		switch (unact->typ) {
185096e091f8SDavid du Colombier 		case Erecolor: u="uncolor"; break;
18510c6300e7SDavid du Colombier 		case Ethick: u=(unact->fp->c.thick==0) ? "unthin" : "unthicken";
185296e091f8SDavid du Colombier 			break;
185396e091f8SDavid du Colombier 		case Edelete: u="undelete"; break;
185496e091f8SDavid du Colombier 		case Emove: u="unmove"; break;
185596e091f8SDavid du Colombier 		case Erotate: u="unrotate"; break;
185696e091f8SDavid du Colombier 		}
185796e091f8SDavid du Colombier 	e_items[Eundo] = u;
185896e091f8SDavid du Colombier }
185996e091f8SDavid du Colombier 
186096e091f8SDavid du Colombier 
do_emenu(int but,Mouse * m)186196e091f8SDavid du Colombier void do_emenu(int but, Mouse* m)
186296e091f8SDavid du Colombier {
186396e091f8SDavid du Colombier 	int h;
186496e091f8SDavid du Colombier 	if (cur_sel.t < 0)
186596e091f8SDavid du Colombier 		return;
186696e091f8SDavid du Colombier 	init_e_menu();
186796e091f8SDavid du Colombier 	h = emenuhit(but, m, &e_menu);
186896e091f8SDavid du Colombier 	switch(h) {
186996e091f8SDavid du Colombier 	case Ethick: unact = save_act(unact, h);
18700c6300e7SDavid du Colombier 		cur_sel.fp->c.thick ^= 1;
187196e091f8SDavid du Colombier 		eresized(0);
187296e091f8SDavid du Colombier 		break;
187396e091f8SDavid du Colombier 	case Edelete: unact = save_act(unact, h);
187496e091f8SDavid du Colombier 		fp_remove(&univ, cur_sel.fp);
187596e091f8SDavid du Colombier 		unselect(0);
187696e091f8SDavid du Colombier 		eresized(0);
187796e091f8SDavid du Colombier 		break;
187896e091f8SDavid du Colombier 	case Erecolor: unact = save_act(unact, h);
187996e091f8SDavid du Colombier 		do_recolor(but, m, 0);
188096e091f8SDavid du Colombier 		break;
188196e091f8SDavid du Colombier 	case Erotate: unact = save_act(unact, h);
188296e091f8SDavid du Colombier 		prepare_mv(cur_sel.fp);
188396e091f8SDavid du Colombier 		if (get_1click(but, m, 0)) {
188496e091f8SDavid du Colombier 			unact->pt = cur_sel.p;
188596e091f8SDavid du Colombier 			unact->amt = do_rotate(but, m);
188696e091f8SDavid du Colombier 		}
188796e091f8SDavid du Colombier 		break;
188896e091f8SDavid du Colombier 	case Eundo: unact = do_undo(unact);
188996e091f8SDavid du Colombier 		break;
189096e091f8SDavid du Colombier 	}
189196e091f8SDavid du Colombier }
189296e091f8SDavid du Colombier 
189396e091f8SDavid du Colombier 
189496e091f8SDavid du Colombier 
189596e091f8SDavid du Colombier /******************************* Undoing edits  *******************************/
189696e091f8SDavid du Colombier 
save_act(e_action * a0,e_index typ)189796e091f8SDavid du Colombier e_action* save_act(e_action* a0, e_index typ)
189896e091f8SDavid du Colombier {					/* append new e_action for status quo */
189996e091f8SDavid du Colombier 	e_action* a = malloc(sizeof(e_action));
190096e091f8SDavid du Colombier 	a->link = a0;
190196e091f8SDavid du Colombier 	a->pt.x = a->pt.y = 0.0;
19020c6300e7SDavid du Colombier 	a->amt = cur_sel.fp->c.thick;
19030c6300e7SDavid du Colombier 	a->clr = cur_sel.fp->c.clr;
190496e091f8SDavid du Colombier 	a->fp = cur_sel.fp;
190596e091f8SDavid du Colombier 	a->typ = typ;
190696e091f8SDavid du Colombier 	return a;
190796e091f8SDavid du Colombier }
190896e091f8SDavid du Colombier 
190996e091f8SDavid du Colombier 
191096e091f8SDavid du Colombier /* This would be trivial except it's nice to preserve the selection in order to make
191196e091f8SDavid du Colombier    it easy to undo a series of moves.  (There's no do_unrotate() because it's harder
191296e091f8SDavid du Colombier    and less important to preserve the selection in that case.)
191396e091f8SDavid du Colombier */
do_unmove(e_action * a)191496e091f8SDavid du Colombier void do_unmove(e_action* a)
191596e091f8SDavid du Colombier {
191696e091f8SDavid du Colombier 	double tsav = cur_sel.t;
191796e091f8SDavid du Colombier 	unselect(0);
191896e091f8SDavid du Colombier 	move_fp(a->fp, -a->pt.x, -a->pt.y);
191996e091f8SDavid du Colombier 	if (a->fp == cur_sel.fp) {
192096e091f8SDavid du Colombier 		cur_sel.p.x -= a->pt.x;
192196e091f8SDavid du Colombier 		cur_sel.p.y -= a->pt.y;
192296e091f8SDavid du Colombier 	}
192396e091f8SDavid du Colombier 	cur_sel.t = tsav;
192496e091f8SDavid du Colombier 	reselect(0);
192596e091f8SDavid du Colombier }
192696e091f8SDavid du Colombier 
192796e091f8SDavid du Colombier 
do_undo(e_action * a0)192896e091f8SDavid du Colombier e_action* do_undo(e_action* a0)		/* pop off an e_action and (un)do it */
192996e091f8SDavid du Colombier {
193096e091f8SDavid du Colombier 	e_action* a = a0;
193196e091f8SDavid du Colombier 	if (a==0)
193296e091f8SDavid du Colombier 		return 0;
193396e091f8SDavid du Colombier 	switch(a->typ) {
19340c6300e7SDavid du Colombier 	case Ethick: a->fp->c.thick = a->amt;
193596e091f8SDavid du Colombier 		eresized(0);
193696e091f8SDavid du Colombier 		break;
19370c6300e7SDavid du Colombier 	case Erecolor: a->fp->c.clr = a->clr;
193896e091f8SDavid du Colombier 		eresized(0);
193996e091f8SDavid du Colombier 		break;
194096e091f8SDavid du Colombier 	case Edelete:
194196e091f8SDavid du Colombier 		a->fp->link = univ.p;
194296e091f8SDavid du Colombier 		univ.p = a->fp;
194396e091f8SDavid du Colombier 		grow_bb(&univ.bb, &a->fp->bb);
194496e091f8SDavid du Colombier 		eresized(0);
194596e091f8SDavid du Colombier 		break;
194696e091f8SDavid du Colombier 	case Emove:
194796e091f8SDavid du Colombier 		do_unmove(a);
194896e091f8SDavid du Colombier 		eresized(0);
194996e091f8SDavid du Colombier 		break;
195096e091f8SDavid du Colombier 	case Erotate:
195196e091f8SDavid du Colombier 		unselect(0);
195296e091f8SDavid du Colombier 		rotate_fp(a->fp, a->pt, -a->amt);
195396e091f8SDavid du Colombier 		eresized(0);
195496e091f8SDavid du Colombier 		break;
195596e091f8SDavid du Colombier 	}
195696e091f8SDavid du Colombier 	a0 = a->link;
195796e091f8SDavid du Colombier 	free(a);
195896e091f8SDavid du Colombier 	return a0;
195996e091f8SDavid du Colombier }
196096e091f8SDavid du Colombier 
196196e091f8SDavid du Colombier 
196296e091f8SDavid du Colombier 
196396e091f8SDavid du Colombier /********************************* Main menu  *********************************/
196496e091f8SDavid du Colombier 
196596e091f8SDavid du Colombier enum m_index {     Mzoom_in,  Mzoom_out,  Munzoom,  Mslant,    Munslant,
196696e091f8SDavid du Colombier 		Msquare_up,  Mrecenter,  Mrecolor,  Mrestack,  Mread,
196796e091f8SDavid du Colombier 		Mwrite,      Mexit};
196896e091f8SDavid du Colombier char* m_items[] = {"zoom in", "zoom out", "unzoom", "slant",   "unslant",
196996e091f8SDavid du Colombier 		"square up", "recenter", "recolor", "restack", "read",
197096e091f8SDavid du Colombier 		"write",     "exit", 0};
197196e091f8SDavid du Colombier 
197296e091f8SDavid du Colombier Menu m_menu = {m_items, 0, 0};
197396e091f8SDavid du Colombier 
197496e091f8SDavid du Colombier 
do_mmenu(int but,Mouse * m)197596e091f8SDavid du Colombier void do_mmenu(int but, Mouse* m)
197696e091f8SDavid du Colombier {
197796e091f8SDavid du Colombier 	int e, h = emenuhit(but, m, &m_menu);
197896e091f8SDavid du Colombier 	switch (h) {
197996e091f8SDavid du Colombier 	case Mzoom_in:
198096e091f8SDavid du Colombier 		disp_zoomin(egetrect(but,m));
198196e091f8SDavid du Colombier 		eresized(0);
198296e091f8SDavid du Colombier 		break;
198396e091f8SDavid du Colombier 	case Mzoom_out:
198496e091f8SDavid du Colombier 		disp_zoomout(egetrect(but,m));
198596e091f8SDavid du Colombier 		eresized(0);
198696e091f8SDavid du Colombier 		break;
198796e091f8SDavid du Colombier 	case Msquare_up:
198896e091f8SDavid du Colombier 		disp_squareup();
198996e091f8SDavid du Colombier 		eresized(0);
199096e091f8SDavid du Colombier 		break;
199196e091f8SDavid du Colombier 	case Munzoom:
199296e091f8SDavid du Colombier 		init_disp();
199396e091f8SDavid du Colombier 		eresized(0);
199496e091f8SDavid du Colombier 		break;
199596e091f8SDavid du Colombier 	case Mrecenter:
199696e091f8SDavid du Colombier 		if (get_1click(but, m, &bullseye)) {
199796e091f8SDavid du Colombier 			recenter_disp(m->xy);
199896e091f8SDavid du Colombier 			eresized(0);
199996e091f8SDavid du Colombier 			lift_button(but, m, Never);
200096e091f8SDavid du Colombier 		}
200196e091f8SDavid du Colombier 		break;
200296e091f8SDavid du Colombier 	case Mslant:
200396e091f8SDavid du Colombier 		if (cur_sel.t>=0 && prev_sel.t>=0) {
200496e091f8SDavid du Colombier 			slant_disp(prev_sel.p, cur_sel.p);
200596e091f8SDavid du Colombier 			eresized(0);
200696e091f8SDavid du Colombier 		}
200796e091f8SDavid du Colombier 		break;
200896e091f8SDavid du Colombier 	case Munslant:
200996e091f8SDavid du Colombier 		univ.slant_ht = univ.disp.max.y - univ.disp.min.y;
201096e091f8SDavid du Colombier 		eresized(0);
201196e091f8SDavid du Colombier 		break;
201296e091f8SDavid du Colombier 	case Mrecolor:
201396e091f8SDavid du Colombier 		do_recolor(but, m, 1);
201496e091f8SDavid du Colombier 		break;
201596e091f8SDavid du Colombier 	case Mrestack:
201696e091f8SDavid du Colombier 		fps_invert(&univ);
201796e091f8SDavid du Colombier 		eresized(0);
201896e091f8SDavid du Colombier 		break;
201996e091f8SDavid du Colombier 	case Mread:
202096e091f8SDavid du Colombier 		e = doinput(prompt_text("File:"));
202196e091f8SDavid du Colombier 		if (e==0)
202296e091f8SDavid du Colombier 			eresized(0);
202396e091f8SDavid du Colombier 		else if (e<0)
202496e091f8SDavid du Colombier 			show_mytext(" - can't read");
202596e091f8SDavid du Colombier 		else {
202696e091f8SDavid du Colombier 			char ebuf[80];
202796e091f8SDavid du Colombier 			snprintf(ebuf, 80, " - error line %d", e);
202896e091f8SDavid du Colombier 			show_mytext(ebuf);
202996e091f8SDavid du Colombier 		}
203096e091f8SDavid du Colombier 		break;
203196e091f8SDavid du Colombier 	case Mwrite:
203296e091f8SDavid du Colombier 		if (!dooutput(prompt_text("File:")))
203396e091f8SDavid du Colombier 			show_mytext(" - can't write");
203496e091f8SDavid du Colombier 		break;
203596e091f8SDavid du Colombier 	case Mexit:
203696e091f8SDavid du Colombier 		exits("");
203796e091f8SDavid du Colombier 	}
203896e091f8SDavid du Colombier }
203996e091f8SDavid du Colombier 
204096e091f8SDavid du Colombier 
204196e091f8SDavid du Colombier 
204296e091f8SDavid du Colombier /****************************** Handling events  ******************************/
204396e091f8SDavid du Colombier 
doevent(void)204496e091f8SDavid du Colombier void doevent(void)
204596e091f8SDavid du Colombier {
204696e091f8SDavid du Colombier 	ulong etype;
204796e091f8SDavid du Colombier 	int mobile;
204896e091f8SDavid du Colombier 	ulong mvtime;
204996e091f8SDavid du Colombier 	Event	ev;
205096e091f8SDavid du Colombier 
205196e091f8SDavid du Colombier 	etype = eread(Emouse|Ekeyboard, &ev);
205296e091f8SDavid du Colombier 	if(etype & Emouse) {
205396e091f8SDavid du Colombier 		if (ev.mouse.buttons & But1) {
205496e091f8SDavid du Colombier 			do_select(ev.mouse.xy);
205596e091f8SDavid du Colombier 			mvtime = Never;
205696e091f8SDavid du Colombier 			mobile = !cantmv && cur_sel.t>=0;
205796e091f8SDavid du Colombier 			if (mobile) {
205896e091f8SDavid du Colombier 				mvtime = ev.mouse.msec + Mv_delay;
205996e091f8SDavid du Colombier 				prepare_mv(cur_sel.fp);
206096e091f8SDavid du Colombier 			}
206196e091f8SDavid du Colombier 			if (!lift_button(1, &ev.mouse, mvtime) && mobile)
206296e091f8SDavid du Colombier 				save_mv(do_move(1, &ev.mouse));
206396e091f8SDavid du Colombier 		} else if (ev.mouse.buttons & But2)
206496e091f8SDavid du Colombier 			do_emenu(2, &ev.mouse);
206596e091f8SDavid du Colombier 		else if (ev.mouse.buttons & But3)
206696e091f8SDavid du Colombier 			do_mmenu(3, &ev.mouse);
20670c6300e7SDavid du Colombier 	} else if (etype & Ekeyboard) {
20680c6300e7SDavid du Colombier 		if (ev.kbdc=='\n' && cur_sel.t>=0 && logfil!=0) {
20690c6300e7SDavid du Colombier 			fprintf(logfil,"%s\n", cur_sel.fp->nam);
20700c6300e7SDavid du Colombier 			fflush(logfil);
207196e091f8SDavid du Colombier 		}
20720c6300e7SDavid du Colombier 	}
207396e091f8SDavid du Colombier }
207496e091f8SDavid du Colombier 
207596e091f8SDavid du Colombier 
207696e091f8SDavid du Colombier 
207796e091f8SDavid du Colombier /******************************** Main program ********************************/
207896e091f8SDavid du Colombier 
207996e091f8SDavid du Colombier extern char* argv0;
208096e091f8SDavid du Colombier 
usage(void)208196e091f8SDavid du Colombier void usage(void)
208296e091f8SDavid du Colombier {
208396e091f8SDavid du Colombier 	int i;
208496e091f8SDavid du Colombier 	fprintf(stderr,"Usage %s [options] [infile]\n", argv0);
208596e091f8SDavid du Colombier 	fprintf(stderr,
2086a7529a1dSDavid du Colombier "option ::= -l logfile | -m | -p\n"
208796e091f8SDavid du Colombier "\n"
208896e091f8SDavid du Colombier "Read a polygonal line graph in an ASCII format (one x y pair per line, delimited\n"
208996e091f8SDavid du Colombier "by spaces with a label after each polyline), and view it interactively.  Use\n"
209096e091f8SDavid du Colombier "standard input if no infile is specified.\n"
209196e091f8SDavid du Colombier "Option -l specifies a file in which to log the coordinates of each point selected.\n"
209296e091f8SDavid du Colombier "(Clicking a point with button one selects it and displays its coordinates and\n"
209396e091f8SDavid du Colombier "the label of its polylone.)  Option -m allows polylines to be moved and rotated.\n"
2094a7529a1dSDavid du Colombier "The -p option plots only the vertices of the polygons.\n"
209596e091f8SDavid du Colombier "The polyline labels can use the following color names:"
209696e091f8SDavid du Colombier 	);
209796e091f8SDavid du Colombier 	for (i=0; clrtab[i].c!=DNofill; i++)
209896e091f8SDavid du Colombier 		fprintf(stderr,"%s%8s", (i%8==0 ? "\n" : "  "), clrtab[i].nam);
209996e091f8SDavid du Colombier 	fputc('\n', stderr);
210096e091f8SDavid du Colombier 	exits("usage");
210196e091f8SDavid du Colombier }
210296e091f8SDavid du Colombier 
main(int argc,char * argv[])210396e091f8SDavid du Colombier void main(int argc, char *argv[])
210496e091f8SDavid du Colombier {
210596e091f8SDavid du Colombier 	int e;
2106*162f803dSDavid du Colombier 	char err[ERRMAX];
210796e091f8SDavid du Colombier 
210896e091f8SDavid du Colombier 	ARGBEGIN {
2109a7529a1dSDavid du Colombier 	case 'm':
2110a7529a1dSDavid du Colombier 		cantmv=0;
211196e091f8SDavid du Colombier 		break;
2112a7529a1dSDavid du Colombier 	case 'l':
2113a7529a1dSDavid du Colombier 		logfil = fopen(ARGF(),"w");
211496e091f8SDavid du Colombier 		break;
21158db68488SDavid du Colombier 	case 'p':
21168db68488SDavid du Colombier 		plotdots++;
21178db68488SDavid du Colombier 		break;
2118a7529a1dSDavid du Colombier 	default:
2119a7529a1dSDavid du Colombier 		usage();
2120a7529a1dSDavid du Colombier 	} ARGEND;
212196e091f8SDavid du Colombier 
212296e091f8SDavid du Colombier 	if(initdraw(0, 0, "gview") < 0)
212396e091f8SDavid du Colombier 		exits("initdraw");
212496e091f8SDavid du Colombier 	einit(Emouse|Ekeyboard);
212596e091f8SDavid du Colombier 
21265f6b17bbSDavid du Colombier 	do {
212796e091f8SDavid du Colombier 		e = doinput(*argv ? *argv : "-");
212896e091f8SDavid du Colombier 		if (e < 0) {
2129*162f803dSDavid du Colombier 			rerrstr(err, sizeof err);
2130*162f803dSDavid du Colombier 			fprintf(stderr, "%s: cannot read %s: %s\n",
2131*162f803dSDavid du Colombier 				argv0, *argv, err);
213296e091f8SDavid du Colombier 			exits("no valid input file");
213396e091f8SDavid du Colombier 		} else if (e > 0) {
2134*162f803dSDavid du Colombier 			fprintf(stderr, "%s: %s:%d: bad data syntax\n",
2135*162f803dSDavid du Colombier 				argv0, (*argv ? *argv : "-"), e);
213696e091f8SDavid du Colombier 			exits("bad syntax in input");
213796e091f8SDavid du Colombier 		}
21385f6b17bbSDavid du Colombier 	} while (*argv && *++argv);
213996e091f8SDavid du Colombier 	init_disp();
214096e091f8SDavid du Colombier 	init_clrtab();
214196e091f8SDavid du Colombier 	set_default_clrs(&univ, 0);
214296e091f8SDavid du Colombier 	adjust_border(display->defaultfont);
214396e091f8SDavid du Colombier 	cur_sel.t = prev_sel.t = -1;
214496e091f8SDavid du Colombier 	eresized(0);
214596e091f8SDavid du Colombier 	for(;;)
214696e091f8SDavid du Colombier 		doevent();
214796e091f8SDavid du Colombier }
2148