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