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 */ 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 */ 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 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 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 120*0c6300e7SDavid du Colombier /* Wait for a mouse click and return 0 for failue if not button but (curs can be 0) */ 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 133*0c6300e7SDavid du Colombier /* Wait for a mouse click or keyboard event from the string of expected characters. Return 134*0c6300e7SDavid du Colombier the character code or -1 for a button-but mouse event or 0 for wrong button. 135*0c6300e7SDavid du Colombier */ 136*0c6300e7SDavid du Colombier int get_click_or_kbd(int but, Mouse* m, const char* expected) 137*0c6300e7SDavid du Colombier { 138*0c6300e7SDavid du Colombier Event ev; 139*0c6300e7SDavid du Colombier ulong expbits[4], ty; 140*0c6300e7SDavid du Colombier expbits[0] = expbits[1] = expbits[2] = expbits[3]; 141*0c6300e7SDavid du Colombier for (; *expected!=0; expected++) 142*0c6300e7SDavid du Colombier expbits[((*expected)>>5)&3] |= 1 << (*expected&31); 143*0c6300e7SDavid du Colombier do ty = eread(Emouse|Ekeyboard, &ev); 144*0c6300e7SDavid du Colombier while ((ty&Emouse) ? ev.mouse.buttons==0 145*0c6300e7SDavid du Colombier : (ev.kbdc&~127) || !(expbits[(ev.kbdc>>5)&3] & (1<<(ev.kbdc&31))) ); 146*0c6300e7SDavid du Colombier if (ty&Ekeyboard) 147*0c6300e7SDavid du Colombier return ev.kbdc; 148*0c6300e7SDavid du Colombier *m = ev.mouse; 149*0c6300e7SDavid du Colombier return (ev.mouse.buttons==Button_bit(but)) ? -1 : 0; 150*0c6300e7SDavid du Colombier } 151*0c6300e7SDavid du Colombier 152*0c6300e7SDavid 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 */ 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 */ 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 187*0c6300e7SDavid du Colombier 188*0c6300e7SDavid du Colombier typedef struct thick_color { 189*0c6300e7SDavid du Colombier int thick; /* use 1+2*thick pixel wide lines */ 190*0c6300e7SDavid du Colombier Image* clr; /* Color to use when drawing this */ 191*0c6300e7SDavid du Colombier } thick_color; 192*0c6300e7SDavid du Colombier 193*0c6300e7SDavid 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)*/ 197*0c6300e7SDavid 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[] = { 202*0c6300e7SDavid du Colombier DRed, "Red", 'R', 0, 203*0c6300e7SDavid du Colombier DPink, "Pink", 'P', 0, 204*0c6300e7SDavid du Colombier DDkred, "Dkred", 'r', 0, 205*0c6300e7SDavid du Colombier DOrange, "Orange", 'O', 0, 206*0c6300e7SDavid du Colombier DYellow, "Yellow", 'Y', 0, 207*0c6300e7SDavid du Colombier DDkyellow, "Dkyellow", 'y', 0, 208*0c6300e7SDavid du Colombier DGreen, "Green", 'G', 0, 209*0c6300e7SDavid du Colombier DDkgreen, "Dkgreen", 'g', 0, 210*0c6300e7SDavid du Colombier DCyan, "Cyan", 'C', 0, 211*0c6300e7SDavid du Colombier DBlue, "Blue", 'B', 0, 212*0c6300e7SDavid du Colombier DLtblue, "Ltblue", 'b', 0, 213*0c6300e7SDavid du Colombier DMagenta, "Magenta", 'M', 0, 214*0c6300e7SDavid du Colombier DViolet, "Violet", 'V', 0, 215*0c6300e7SDavid du Colombier Dgray, "Gray", 'A', 0, 216*0c6300e7SDavid du Colombier DBlack, "Black", 'K', 0, 217*0c6300e7SDavid du Colombier DWhite, "White", 'W', 0, 218*0c6300e7SDavid du Colombier DNofill, 0, 0, 0 /* DNofill means "end of data" */ 21996e091f8SDavid du Colombier }; 22096e091f8SDavid du Colombier 221*0c6300e7SDavid du Colombier short nam1_idx[128]; /* the clrtab[] index for each nam1, else -1 */ 222*0c6300e7SDavid du Colombier 22396e091f8SDavid du Colombier 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); 228*0c6300e7SDavid du Colombier memset(&nam1_idx[0], -1, sizeof(nam1_idx)); 229*0c6300e7SDavid 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? */ 232*0c6300e7SDavid du Colombier nam1_idx[clrtab[i].nam1] = i; 233*0c6300e7SDavid du Colombier } 23496e091f8SDavid du Colombier } 23596e091f8SDavid du Colombier 23696e091f8SDavid du Colombier 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 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 255*0c6300e7SDavid du Colombier 25696e091f8SDavid du Colombier #define clr_im(clr) clrtab[clr_id(clr)].im 257*0c6300e7SDavid du Colombier #define is_Multi -2 /* dummy clrtab[] less than -1 */ 25896e091f8SDavid du Colombier 25996e091f8SDavid du Colombier 260*0c6300e7SDavid du Colombier thick_color* tc_default(thick_color *buf) 26196e091f8SDavid du Colombier { 262*0c6300e7SDavid du Colombier buf[0].thick = 1; 263*0c6300e7SDavid du Colombier buf[1].clr = clr_im(DBlack); 264*0c6300e7SDavid du Colombier buf[1].thick = 0; 265*0c6300e7SDavid du Colombier return buf; 266*0c6300e7SDavid du Colombier } 267*0c6300e7SDavid du Colombier 268*0c6300e7SDavid du Colombier 269*0c6300e7SDavid du Colombier /* Return an allocated array that describes the color letters (clrtab[].nam1 values, 270*0c6300e7SDavid du Colombier optionally prefixed by 'T') in the string that starts at c0 and ends just before 271*0c6300e7SDavid du Colombier fin. The first entry is a dummy whose thick field tells how many data entries follow. 272*0c6300e7SDavid du Colombier If buf!=0, it should point to an array of length 2 that is to hold the output 273*0c6300e7SDavid du Colombier (truncated to a dummy and one data entry). The error indication is 1 data entry 274*0c6300e7SDavid du Colombier of default color and thickness; e.g., "Multi(xxbadxx)" in a label prevents gview 275*0c6300e7SDavid du Colombier from recognizing anything that follows. 276*0c6300e7SDavid du Colombier */ 277*0c6300e7SDavid du Colombier thick_color* parse_color_chars(const char* c0, const char* fin, thick_color *buf) 278*0c6300e7SDavid du Colombier { 279*0c6300e7SDavid du Colombier thick_color* tc; /* Pending return value */ 280*0c6300e7SDavid du Colombier int i, j, n=fin-c0; /* n is an upper bound on how many data members */ 281*0c6300e7SDavid du Colombier const char* c; 282*0c6300e7SDavid du Colombier for (c=c0; c<fin-1; c++) 283*0c6300e7SDavid du Colombier if (*c=='T') 284*0c6300e7SDavid du Colombier n--; 285*0c6300e7SDavid du Colombier if (buf==0) 286*0c6300e7SDavid du Colombier tc = (thick_color*) malloc((n+1)*sizeof(thick_color)); 287*0c6300e7SDavid du Colombier else {tc=buf; n=1;} 288*0c6300e7SDavid du Colombier i = 0; 289*0c6300e7SDavid du Colombier for (c=c0; c<fin && i<n; c++) { 290*0c6300e7SDavid du Colombier tc[++i].thick = 0; 291*0c6300e7SDavid du Colombier if (*c=='T') 292*0c6300e7SDavid du Colombier if (++c==fin) 293*0c6300e7SDavid du Colombier return tc_default(tc); 294*0c6300e7SDavid du Colombier else tc[i].thick=1; 295*0c6300e7SDavid du Colombier j = (*c&~127) ? -1 : nam1_idx[*c]; 296*0c6300e7SDavid du Colombier if (j < 0) 297*0c6300e7SDavid du Colombier return tc_default(tc); 298*0c6300e7SDavid du Colombier tc[i].clr = clrtab[j].im; 299*0c6300e7SDavid du Colombier } 300*0c6300e7SDavid du Colombier tc[0].thick = i; 301*0c6300e7SDavid du Colombier return tc; 302*0c6300e7SDavid du Colombier } 303*0c6300e7SDavid du Colombier 304*0c6300e7SDavid du Colombier 305*0c6300e7SDavid du Colombier /* Decide what color and thickness to use for a polyline based on the label it has in the 306*0c6300e7SDavid du Colombier input file. The winner is whichever color name comes first, or otherwise black; and 307*0c6300e7SDavid du Colombier thickness is determined by the presence of "Thick" in the string. Place the result 308*0c6300e7SDavid du Colombier in *r1 and return 0 unless a Multi(...) spec is found, in which case the result is 309*0c6300e7SDavid du Colombier an allocated array of alternative color and thickness values. A nonzero idxdest 310*0c6300e7SDavid du Colombier requests the clrtab[] index in *idxdest and no allocated array. 311*0c6300e7SDavid du Colombier */ 312*0c6300e7SDavid du Colombier thick_color* nam2thclr(const char* nam, thick_color *r1, int *idxdest) 313*0c6300e7SDavid du Colombier { 314*0c6300e7SDavid du Colombier char *c, *cbest=0, *rp=0; 31596e091f8SDavid du Colombier int i, ibest=-1; 316*0c6300e7SDavid du Colombier thick_color* tc = 0; 317*0c6300e7SDavid du Colombier thick_color buf[2]; 318*0c6300e7SDavid du Colombier if (*nam!=0) { 319*0c6300e7SDavid du Colombier c = strstr(nam, "Multi("); 320*0c6300e7SDavid du Colombier if (c!=0 && (rp=strchr(c+6,')'))!=0) 321*0c6300e7SDavid 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); 324*0c6300e7SDavid du Colombier if (c!=0 && (ibest==-1 || c<cbest)) 32596e091f8SDavid du Colombier {ibest=i; cbest=c;} 32696e091f8SDavid du Colombier } 327*0c6300e7SDavid du Colombier } 328*0c6300e7SDavid du Colombier if (ibest==is_Multi) { 329*0c6300e7SDavid du Colombier tc = parse_color_chars(cbest+6, rp, (idxdest==0 ? 0 : &buf[0])); 330*0c6300e7SDavid du Colombier ibest = clrim_id(tc[1].clr); 331*0c6300e7SDavid du Colombier } 33296e091f8SDavid du Colombier if (idxdest!=0) 33396e091f8SDavid du Colombier *idxdest = (ibest<0) ? clr_id(DBlack) : ibest; 334*0c6300e7SDavid du Colombier r1->clr = (ibest<0) ? clr_im(DBlack) : clrtab[ibest].im; 335*0c6300e7SDavid du Colombier r1->thick = (tc!=0) ? tc[1].thick : (strstr(nam,"Thick")==0 ? 0 : 1); 336*0c6300e7SDavid du Colombier return tc; 33796e091f8SDavid du Colombier } 33896e091f8SDavid du Colombier 33996e091f8SDavid du Colombier 340*0c6300e7SDavid 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 */ 346*0c6300e7SDavid du Colombier char* nam_with_thclr(char* nam, const thick_color *tc, char* buf, int bufn) 34796e091f8SDavid du Colombier { 348*0c6300e7SDavid du Colombier thick_color c0; 349*0c6300e7SDavid du Colombier int clr0i; 350*0c6300e7SDavid du Colombier nam2thclr(nam, &c0, &clr0i); 35196e091f8SDavid du Colombier char *clr0s; 352*0c6300e7SDavid 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';} 357*0c6300e7SDavid du Colombier if (c0.clr != tc->clr) 35896e091f8SDavid du Colombier remove_substr(buf, clr0s); 359*0c6300e7SDavid du Colombier if (c0.thick > tc->thick) 36096e091f8SDavid du Colombier while (remove_substr(buf, "Thick")) 36196e091f8SDavid du Colombier /* do nothing */; 362*0c6300e7SDavid du Colombier nam2thclr(nam, &c0, &clr0i); 363*0c6300e7SDavid du Colombier if (c0.clr != tc->clr) 364*0c6300e7SDavid du Colombier str_insert(buf, clrtab[clrim_id(tc->clr)].nam, bufn); 365*0c6300e7SDavid 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 */ 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 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 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 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 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) */ 436*0c6300e7SDavid du Colombier thick_color c; /* the current color and line thickness */ 437*0c6300e7SDavid 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 457*0c6300e7SDavid du Colombier void free_fp_etc(fpolygon* fp) 458*0c6300e7SDavid du Colombier { 459*0c6300e7SDavid du Colombier if (fp->ct != 0) 460*0c6300e7SDavid du Colombier free(fp->ct); 461*0c6300e7SDavid du Colombier free(fp->p); 462*0c6300e7SDavid du Colombier free(fp); 463*0c6300e7SDavid du Colombier } 464*0c6300e7SDavid du Colombier 465*0c6300e7SDavid du Colombier 46696e091f8SDavid du Colombier void set_default_clrs(fpolygons* fps, fpolygon* fpstop) 46796e091f8SDavid du Colombier { 46896e091f8SDavid du Colombier fpolygon* fp; 469*0c6300e7SDavid du Colombier for (fp=fps->p; fp!=0 && fp!=fpstop; fp=fp->link) 470*0c6300e7SDavid du Colombier fp->ct = nam2thclr(fp->nam, &fp->c, 0); 47196e091f8SDavid du Colombier } 47296e091f8SDavid du Colombier 47396e091f8SDavid du Colombier 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 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 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 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 */ 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 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 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 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 */ 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 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 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 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 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 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 */ 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 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 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 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 */ 79796e091f8SDavid du Colombier fpolygon* rd_fpoly(FILE* fin, int *lineno) 79896e091f8SDavid du Colombier { 799*0c6300e7SDavid du Colombier char buf[4096], junk[2]; 80096e091f8SDavid du Colombier fpoint q; 80196e091f8SDavid du Colombier fpolygon* fp; 80296e091f8SDavid du Colombier int allocn; 803*0c6300e7SDavid 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)); 809*0c6300e7SDavid 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 = ""; 814*0c6300e7SDavid du Colombier fp->c.thick = 0; 815*0c6300e7SDavid du Colombier fp->c.clr = clr_im(DBlack); 816*0c6300e7SDavid du Colombier fp->ct = 0; 817*0c6300e7SDavid 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)) 821*0c6300e7SDavid 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 */ 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 */ 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 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 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); 888*0c6300e7SDavid du Colombier fprintf(fout,"\"%s\"\n", nam_with_thclr(fp->nam, &fp->c, buf, 256)); 88996e091f8SDavid du Colombier } 89096e091f8SDavid du Colombier 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 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 */ 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 */ 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 */ 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 */ 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 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 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 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 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 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 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 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 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 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 */ 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 */ 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 */ 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 */ 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 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 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 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 */ 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 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 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 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 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 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 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 */ 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 */ 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 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)) 1551*0c6300e7SDavid 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); 1560*0c6300e7SDavid 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 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 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 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)) 1599*0c6300e7SDavid 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 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 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 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) 1648*0c6300e7SDavid du Colombier p->c.clr = clr; 1649*0c6300e7SDavid du Colombier } 1650*0c6300e7SDavid du Colombier 1651*0c6300e7SDavid du Colombier 1652*0c6300e7SDavid du Colombier void all_set_scheme(fpolygons* fps, int scheme) 1653*0c6300e7SDavid du Colombier { 1654*0c6300e7SDavid du Colombier fpolygon* p; 1655*0c6300e7SDavid du Colombier for (p=fps->p; p!=0; p=p->link) 1656*0c6300e7SDavid du Colombier if (p->ct!=0 && scheme <= p->ct[0].thick) 1657*0c6300e7SDavid du Colombier p->c = p->ct[scheme]; 165896e091f8SDavid du Colombier } 165996e091f8SDavid du Colombier 166096e091f8SDavid du Colombier 166196e091f8SDavid du Colombier void do_recolor(int but, Mouse* m, int alluniv) 166296e091f8SDavid du Colombier { 1663*0c6300e7SDavid du Colombier int sel, clkk, nclr = clr_id(DWhite); 166496e091f8SDavid du Colombier int dy = draw_palette(nclr); 166596e091f8SDavid du Colombier Image* clr; 1666*0c6300e7SDavid du Colombier clkk = get_click_or_kbd(but, m, "123456789abcdefghijklmnopqrstuvwxyz"); 1667*0c6300e7SDavid 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); 1672*0c6300e7SDavid 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); 1676*0c6300e7SDavid du Colombier } else if (clkk > 0) { 1677*0c6300e7SDavid du Colombier sel = ('0'<clkk&&clkk<='9') ? 0 : 10+(clkk-'a')*10; 1678*0c6300e7SDavid du Colombier while (!('0'<=clkk&&clkk<='9')) 1679*0c6300e7SDavid du Colombier clkk = ekbd(); 1680*0c6300e7SDavid du Colombier sel += clkk-'0'; 1681*0c6300e7SDavid du Colombier if (alluniv) 1682*0c6300e7SDavid du Colombier all_set_scheme(&univ, sel); 1683*0c6300e7SDavid du Colombier else if (sel <= cur_sel.fp->ct[0].thick) 1684*0c6300e7SDavid du Colombier cur_sel.fp->c = cur_sel.fp->ct[sel]; 1685*0c6300e7SDavid du Colombier } 1686*0c6300e7SDavid du Colombier eresized(0); 168796e091f8SDavid du Colombier } 168896e091f8SDavid du Colombier 168996e091f8SDavid du Colombier 169096e091f8SDavid du Colombier /****************************** Move and rotate ******************************/ 169196e091f8SDavid du Colombier 169296e091f8SDavid du Colombier void prepare_mv(const fpolygon* fp) 169396e091f8SDavid du Colombier { 169496e091f8SDavid du Colombier Rectangle r = screen->r; 169596e091f8SDavid du Colombier Image* scr0; 1696*0c6300e7SDavid 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 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 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 */ 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); 1753*0c6300e7SDavid du Colombier (fp->c.thick)++; /* line() DISAGREES WITH ITSELF */ 175496e091f8SDavid du Colombier draw_fpoly(fp, &tr, mv_bkgd); 1755*0c6300e7SDavid 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; 1759*0c6300e7SDavid 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 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 */ 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); 1791*0c6300e7SDavid du Colombier (fp->c.thick)++; /* line() DISAGREES WITH ITSELF */ 179296e091f8SDavid du Colombier draw_fpoly(fp, &tr, mv_bkgd); 1793*0c6300e7SDavid 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; 1797*0c6300e7SDavid 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 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 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; 1847*0c6300e7SDavid 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; 1851*0c6300e7SDavid 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 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); 1870*0c6300e7SDavid 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 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; 1902*0c6300e7SDavid du Colombier a->amt = cur_sel.fp->c.thick; 1903*0c6300e7SDavid 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 */ 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 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) { 1934*0c6300e7SDavid du Colombier case Ethick: a->fp->c.thick = a->amt; 193596e091f8SDavid du Colombier eresized(0); 193696e091f8SDavid du Colombier break; 1937*0c6300e7SDavid 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 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 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); 2067*0c6300e7SDavid du Colombier } else if (etype & Ekeyboard) { 2068*0c6300e7SDavid du Colombier if (ev.kbdc=='\n' && cur_sel.t>=0 && logfil!=0) { 2069*0c6300e7SDavid du Colombier fprintf(logfil,"%s\n", cur_sel.fp->nam); 2070*0c6300e7SDavid du Colombier fflush(logfil); 207196e091f8SDavid du Colombier } 2072*0c6300e7SDavid 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 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 210396e091f8SDavid du Colombier void main(int argc, char *argv[]) 210496e091f8SDavid du Colombier { 210596e091f8SDavid du Colombier int e; 210696e091f8SDavid du Colombier 210796e091f8SDavid du Colombier ARGBEGIN { 2108a7529a1dSDavid du Colombier case 'm': 2109a7529a1dSDavid du Colombier cantmv=0; 211096e091f8SDavid du Colombier break; 2111a7529a1dSDavid du Colombier case 'l': 2112a7529a1dSDavid du Colombier logfil = fopen(ARGF(),"w"); 211396e091f8SDavid du Colombier break; 21148db68488SDavid du Colombier case 'p': 21158db68488SDavid du Colombier plotdots++; 21168db68488SDavid du Colombier break; 2117a7529a1dSDavid du Colombier default: 2118a7529a1dSDavid du Colombier usage(); 2119a7529a1dSDavid du Colombier } ARGEND; 212096e091f8SDavid du Colombier 212196e091f8SDavid du Colombier if(initdraw(0, 0, "gview") < 0) 212296e091f8SDavid du Colombier exits("initdraw"); 212396e091f8SDavid du Colombier einit(Emouse|Ekeyboard); 212496e091f8SDavid du Colombier 21255f6b17bbSDavid du Colombier do { 212696e091f8SDavid du Colombier e = doinput(*argv ? *argv : "-"); 212796e091f8SDavid du Colombier if (e < 0) { 212896e091f8SDavid du Colombier fprintf(stderr,"Cannot read input file %s\n", *argv); 212996e091f8SDavid du Colombier exits("no valid input file"); 213096e091f8SDavid du Colombier } else if (e > 0) { 213115174232SDavid du Colombier fprintf(stderr,"Bad syntax at line %d of file %s\n", e, *argv ? *argv : "-"); 213296e091f8SDavid du Colombier exits("bad syntax in input"); 213396e091f8SDavid du Colombier } 21345f6b17bbSDavid du Colombier } while (*argv && *++argv); 213596e091f8SDavid du Colombier init_disp(); 213696e091f8SDavid du Colombier init_clrtab(); 213796e091f8SDavid du Colombier set_default_clrs(&univ, 0); 213896e091f8SDavid du Colombier adjust_border(display->defaultfont); 213996e091f8SDavid du Colombier cur_sel.t = prev_sel.t = -1; 214096e091f8SDavid du Colombier eresized(0); 214196e091f8SDavid du Colombier for(;;) 214296e091f8SDavid du Colombier doevent(); 214396e091f8SDavid du Colombier } 2144