/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h>#include <libc.h>#include <ctype.h>#include <draw.h>#include <event.h>#include <cursor.h>#include <stdio.h> #define Never 0xffffffff /* Maximum ulong */#define LOG2 0.301029995664#define Button_bit(b) (1 << ((b)-1)) enum { But1 = Button_bit(1),/* mouse buttons for events */ But2 = Button_bit(2), But3 = Button_bit(3),};int cantmv = 1; /* disallow rotate and move? 0..1 */int plotdots; /* plot dots instead of lines */int top_border, bot_border, lft_border, rt_border;int lft_border0; /* lft_border for y-axis labels >0 */int top_left, top_right; /* edges of top line free space */int Mv_delay = 400; /* msec for button click vs. button hold down */int Dotrad = 2; /* dot radius in pixels */int framewd=1; /* line thickness for frame (pixels) */int framesep=1; /* distance between frame and surrounding text */int outersep=1; /* distance: surrounding text to screen edge */Point sdigit; /* size of a digit in the font */Point smaxch; /* assume any character in font fits in this */double underscan = .05; /* fraction of frame initially unused per side */double fuzz = 6; /* selection tolerance in pixels */int tick_len = 15; /* length of axis label tick mark in pixels */FILE* logfil = 0; /* dump selected points here if nonzero */ #define labdigs 3 /* allow this many sig digits in axis labels */#define digs10pow 1000 /* pow(10,labdigs) */#define axis_color clr_im(DLtblue) /********************************* Utilities *********************************/ /* Prepend string s to null-terminated string in n-byte buffer buf[], truncating if necessary and using a space to separate s from the rest of buf[].*/char* str_insert(char* buf, char* s, int n){ int blen, slen = strlen(s) + 1; if (slen >= n) {strncpy(buf,s,n); buf[n-1]='\0'; return buf;} blen = strlen(buf); if (blen >= n-slen) buf[blen=n-slen-1] = '\0'; memmove(buf+slen, buf, slen+blen+1); memcpy(buf, s, slen-1); buf[slen-1] = ' '; return buf;} /* Alter string smain (without lengthening it) so as to remove the first occurrence of ssub, assuming ssub is ASCII. Return nonzero (true) if string smain had to be changed. In spite of the ASCII-centric appearance, I think this can handle UTF in smain.*/int remove_substr(char* smain, char* ssub){ char *ss, *s = strstr(smain, ssub); int n = strlen(ssub); if (s==0) return 0; if (islower(s[n])) s[0] ^= 32; /* probably tolower(s[0]) or toupper(s[0]) */ else { for (ss=s+n; *ss!=0; s++, ss++) *s = *ss; *s = '\0'; } return 1;} void adjust_border(Font* f){ int sep = framesep + outersep; sdigit = stringsize(f, "8"); smaxch = stringsize(f, "MMMg"); smaxch.x = (smaxch.x + 3)/4; lft_border0 = (1+labdigs)*sdigit.x + framewd + sep; rt_border = (lft_border0 - sep)/2 + outersep; bot_border = sdigit.y + framewd + sep; top_border = smaxch.y + framewd + sep; lft_border = lft_border0; /* this gets reset later */} int is_off_screen(Point p){ const Rectangle* r = &(screen->r); return p.x-r->min.x<lft_border || r->max.x-p.x<rt_border || p.y-r->min.y<=top_border || r->max.y-p.y<=bot_border;} Cursor bullseye ={ {-7, -7}, { 0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF, 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF, 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8, }, { 0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE, 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00, }}; /* Wait for a mouse click and return 0 for failue if not button but (curs can be 0) */int get_1click(int but, Mouse* m, Cursor* curs){ if (curs) esetcursor(curs); while (m->buttons==0) *m = emouse(); if (curs) esetcursor(0); return (m->buttons==Button_bit(but));} /* Wait for a mouse click or keyboard event from the string of expected characters. Return the character code or -1 for a button-but mouse event or 0 for wrong button.*/int get_click_or_kbd(int but, Mouse* m, const char* expected){ Event ev; ulong expbits[4], ty; expbits[0] = expbits[1] = expbits[2] = expbits[3]; for (; *expected!=0; expected++) expbits[((*expected)>>5)&3] |= 1 << (*expected&31); do ty = eread(Emouse|Ekeyboard, &ev); while ((ty&Emouse) ? ev.mouse.buttons==0 : (ev.kbdc&~127) || !(expbits[(ev.kbdc>>5)&3] & (1<<(ev.kbdc&31))) ); if (ty&Ekeyboard) return ev.kbdc; *m = ev.mouse; return (ev.mouse.buttons==Button_bit(but)) ? -1 : 0;} /* Wait until but goes up or until a mouse event's msec passes tlimit. Return a boolean result that tells whether the button went up.*/int lift_button(int but, Mouse* m, int tlimit){ do { *m = emouse(); if (m->msec >= tlimit) return 0; } while (m->buttons & Button_bit(but)); return 1;} /* Set *m to the last pending mouse event, or the first one where but is up. If no mouse events are pending, wait for the next one.*/void latest_mouse(int but, Mouse* m){ int bbit = Button_bit(but); do { *m = emouse(); } while ((m->buttons & bbit) && ecanmouse());} /*********************************** Colors ***********************************/ enum { DOrange=0xffaa00FF, Dgray=0xbbbbbbFF, DDkgreen=0x009900FF, DDkred=0xcc0000FF, DViolet=0x990099FF, DDkyellow=0xaaaa00FF, DLtblue=0xaaaaffFF, DPink=0xffaaaaFF, /* ndraw.h sets DBlack, DBlue, DRed, DYellow, DGreen, DCyan, DMagenta, DWhite */}; typedef struct thick_color { int thick; /* use 1+2*thick pixel wide lines */ Image* clr; /* Color to use when drawing this */} thick_color; typedef struct color_ref { ulong c; /* RGBA pixel color */ char* nam; /* ASCII name (matched to input, used in output)*/ int nam1; /* single-letter version of color name */ Image* im; /* replicated solid-color image */} color_ref; color_ref clrtab[] = { DRed, "Red", 'R', 0, DPink, "Pink", 'P', 0, DDkred, "Dkred", 'r', 0, DOrange, "Orange", 'O', 0, DYellow, "Yellow", 'Y', 0, DDkyellow, "Dkyellow", 'y', 0, DGreen, "Green", 'G', 0, DDkgreen, "Dkgreen", 'g', 0, DCyan, "Cyan", 'C', 0, DBlue, "Blue", 'B', 0, DLtblue, "Ltblue", 'b', 0, DMagenta, "Magenta", 'M', 0, DViolet, "Violet", 'V', 0, Dgray, "Gray", 'A', 0, DBlack, "Black", 'K', 0, DWhite, "White", 'W', 0, DNofill, 0, 0, 0 /* DNofill means "end of data" */}; short nam1_idx[128]; /* the clrtab[] index for each nam1, else -1 */ void init_clrtab(void){ int i; Rectangle r = Rect(0,0,1,1); memset(&nam1_idx[0], -1, sizeof(nam1_idx)); for (i=0; clrtab[i].c!=DNofill; i++) { clrtab[i].im = allocimage(display, r, CMAP8, 1, clrtab[i].c); /* should check for 0 result? */ nam1_idx[clrtab[i].nam1] = i; }} int clrim_id(Image* clr){ int i; for (i=0; clrtab[i].im!=clr; i++) if (clrtab[i].c==DNofill) exits("bad image color"); return i;} int clr_id(int clr){ int i; for (i=0; clrtab[i].c!=clr; i++) if (clrtab[i].c==DNofill) exits("bad color"); return i;} #define clr_im(clr) clrtab[clr_id(clr)].im#define is_Multi -2 /* dummy clrtab[] less than -1 */ thick_color* tc_default(thick_color *buf){ buf[0].thick = 1; buf[1].clr = clr_im(DBlack); buf[1].thick = 0; return buf;} /* Return an allocated array that describes the color letters (clrtab[].nam1 values, optionally prefixed by 'T') in the string that starts at c0 and ends just before fin. The first entry is a dummy whose thick field tells how many data entries follow. If buf!=0, it should point to an array of length 2 that is to hold the output (truncated to a dummy and one data entry). The error indication is 1 data entry of default color and thickness; e.g., "Multi(xxbadxx)" in a label prevents gview from recognizing anything that follows.*/thick_color* parse_color_chars(const char* c0, const char* fin, thick_color *buf){ thick_color* tc; /* Pending return value */ int i, j, n=fin-c0; /* n is an upper bound on how many data members */ const char* c; for (c=c0; c<fin-1; c++) if (*c=='T') n--; if (buf==0) tc = (thick_color*) malloc((n+1)*sizeof(thick_color)); else {tc=buf; n=1;} i = 0; for (c=c0; c<fin && i<n; c++) { tc[++i].thick = 0; if (*c=='T') if (++c==fin) return tc_default(tc); else tc[i].thick=1; j = (*c&~127) ? -1 : nam1_idx[*c]; if (j < 0) return tc_default(tc); tc[i].clr = clrtab[j].im; } tc[0].thick = i; return tc;} /* Decide what color and thickness to use for a polyline based on the label it has in the input file. The winner is whichever color name comes first, or otherwise black; and thickness is determined by the presence of "Thick" in the string. Place the result in *r1 and return 0 unless a Multi(...) spec is found, in which case the result is an allocated array of alternative color and thickness values. A nonzero idxdest requests the clrtab[] index in *idxdest and no allocated array.*/thick_color* nam2thclr(const char* nam, thick_color *r1, int *idxdest){ char *c, *cbest=0, *rp=0; int i, ibest=-1; thick_color* tc = 0; thick_color buf[2]; if (*nam!=0) { c = strstr(nam, "Multi("); if (c!=0 && (rp=strchr(c+6,')'))!=0) {ibest=is_Multi; cbest=c;} for (i=0; clrtab[i].nam!=0; i++) { c = strstr(nam,clrtab[i].nam); if (c!=0 && (ibest==-1 || c<cbest)) {ibest=i; cbest=c;} } } if (ibest==is_Multi) { tc = parse_color_chars(cbest+6, rp, (idxdest==0 ? 0 : &buf[0])); ibest = clrim_id(tc[1].clr); } if (idxdest!=0) *idxdest = (ibest<0) ? clr_id(DBlack) : ibest; r1->clr = (ibest<0) ? clr_im(DBlack) : clrtab[ibest].im; r1->thick = (tc!=0) ? tc[1].thick : (strstr(nam,"Thick")==0 ? 0 : 1); return tc;} /* Alter string nam so that nam2thick() and nam2clr() agree with *tc, using buf[] (a buffer of length bufn) to store the result if it differs from nam. We go to great pains to perform this alteration in a manner that will seem natural to the user, i.e., we try removing a suitably isolated color name before inserting a new one.*/char* nam_with_thclr(char* nam, const thick_color *tc, char* buf, int bufn){ thick_color c0; int clr0i; nam2thclr(nam, &c0, &clr0i); char *clr0s; if (c0.thick==tc->thick && c0.clr==tc->clr) return nam; clr0s = clrtab[clr0i].nam; if (strlen(nam)<bufn) strcpy(buf,nam); else {strncpy(buf,nam,bufn); buf[bufn-1]='\0';} if (c0.clr != tc->clr) remove_substr(buf, clr0s); if (c0.thick > tc->thick) while (remove_substr(buf, "Thick")) /* do nothing */; nam2thclr(nam, &c0, &clr0i); if (c0.clr != tc->clr) str_insert(buf, clrtab[clrim_id(tc->clr)].nam, bufn); if (c0.thick < tc->thick) str_insert(buf, "Thick", bufn); return buf;} /****************************** Data structures ******************************/ Image* mv_bkgd; /* Background image (usually 0) */ typedef struct fpoint { double x, y;} fpoint; typedef struct frectangle { fpoint min, max;} frectangle; frectangle empty_frect = {1e30, 1e30, -1e30, -1e30}; /* When *r2 is transformed by y=y-x*slant, might it intersect *r1 ?*/int fintersects(const frectangle* r1, const frectangle* r2, double slant){ double x2min=r2->min.x, x2max=r2->max.x; if (r1->max.x <= x2min || x2max <= r1->min.x) return 0; if (slant >=0) {x2min*=slant; x2max*=slant;} else {double t=x2min*slant; x2min=x2max*slant; x2max=t;} return r1->max.y > r2->min.y-x2max && r2->max.y-x2min > r1->min.y;} int fcontains(const frectangle* r, fpoint p){ return r->min.x <=p.x && p.x<= r->max.x && r->min.y <=p.y && p.y<= r->max.y;} void grow_bb(frectangle* dest, const frectangle* r){ if (r->min.x < dest->min.x) dest->min.x=r->min.x; if (r->min.y < dest->min.y) dest->min.y=r->min.y; if (r->max.x > dest->max.x) dest->max.x=r->max.x; if (r->max.y > dest->max.y) dest->max.y=r->max.y;} void slant_frect(frectangle *r, double sl){ r->min.y += sl*r->min.x; r->max.y += sl*r->max.x;} fpoint fcenter(const frectangle* r){ fpoint c; c.x = .5*(r->max.x + r->min.x); c.y = .5*(r->max.y + r->min.y); return c;} typedef struct fpolygon { fpoint* p; /* a malloc'ed array */ int n; /* p[] has n elements: p[0..n] */ frectangle bb; /* bounding box */ char* nam; /* name of this polygon (malloc'ed) */ thick_color c; /* the current color and line thickness */ thick_color* ct; /* 0 or malloc'ed color schemes, ct[1..ct->thick] */ struct fpolygon* link;} fpolygon; typedef struct fpolygons { fpolygon* p; /* the head of a linked list */ frectangle bb; /* overall bounding box */ frectangle disp; /* part being mapped onto screen->r */ double slant_ht; /* controls how disp is slanted */} fpolygons; fpolygons univ = { /* everything there is to display */ 0, 1e30, 1e30, -1e30, -1e30, 0, 0, 0, 0, 2*1e30}; void free_fp_etc(fpolygon* fp){ if (fp->ct != 0) free(fp->ct); free(fp->p); free(fp);} void set_default_clrs(fpolygons* fps, fpolygon* fpstop){ fpolygon* fp; for (fp=fps->p; fp!=0 && fp!=fpstop; fp=fp->link) fp->ct = nam2thclr(fp->nam, &fp->c, 0);} void fps_invert(fpolygons* fps){ fpolygon *p, *r=0; for (p=fps->p; p!=0;) { fpolygon* q = p; p = p->link; q->link = r; r = q; } fps->p = r;} void fp_remove(fpolygons* fps, fpolygon* fp){ fpolygon *q, **p = &fps->p; while (*p!=fp) if (*p==0)Lines 1–500 of 2156. Load more
/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h> #include <libc.h> #include <ctype.h> #include <draw.h> #include <event.h> #include <cursor.h> #include <stdio.h> #define Never 0xffffffff /* Maximum ulong */ #define LOG2 0.301029995664 #define Button_bit(b) (1 << ((b)-1)) enum { But1 = Button_bit(1),/* mouse buttons for events */ But2 = Button_bit(2), But3 = Button_bit(3), }; int cantmv = 1; /* disallow rotate and move? 0..1 */ int plotdots; /* plot dots instead of lines */ int top_border, bot_border, lft_border, rt_border; int lft_border0; /* lft_border for y-axis labels >0 */ int top_left, top_right; /* edges of top line free space */ int Mv_delay = 400; /* msec for button click vs. button hold down */ int Dotrad = 2; /* dot radius in pixels */ int framewd=1; /* line thickness for frame (pixels) */ int framesep=1; /* distance between frame and surrounding text */ int outersep=1; /* distance: surrounding text to screen edge */ Point sdigit; /* size of a digit in the font */ Point smaxch; /* assume any character in font fits in this */ double underscan = .05; /* fraction of frame initially unused per side */ double fuzz = 6; /* selection tolerance in pixels */ int tick_len = 15; /* length of axis label tick mark in pixels */ FILE* logfil = 0; /* dump selected points here if nonzero */ #define labdigs 3 /* allow this many sig digits in axis labels */ #define digs10pow 1000 /* pow(10,labdigs) */ #define axis_color clr_im(DLtblue) /********************************* Utilities *********************************/ /* Prepend string s to null-terminated string in n-byte buffer buf[], truncating if necessary and using a space to separate s from the rest of buf[]. */ char* str_insert(char* buf, char* s, int n) { int blen, slen = strlen(s) + 1; if (slen >= n) {strncpy(buf,s,n); buf[n-1]='\0'; return buf;} blen = strlen(buf); if (blen >= n-slen) buf[blen=n-slen-1] = '\0'; memmove(buf+slen, buf, slen+blen+1); memcpy(buf, s, slen-1); buf[slen-1] = ' '; return buf; } /* Alter string smain (without lengthening it) so as to remove the first occurrence of ssub, assuming ssub is ASCII. Return nonzero (true) if string smain had to be changed. In spite of the ASCII-centric appearance, I think this can handle UTF in smain. */ int remove_substr(char* smain, char* ssub) { char *ss, *s = strstr(smain, ssub); int n = strlen(ssub); if (s==0) return 0; if (islower(s[n])) s[0] ^= 32; /* probably tolower(s[0]) or toupper(s[0]) */ else { for (ss=s+n; *ss!=0; s++, ss++) *s = *ss; *s = '\0'; } return 1; } void adjust_border(Font* f) { int sep = framesep + outersep; sdigit = stringsize(f, "8"); smaxch = stringsize(f, "MMMg"); smaxch.x = (smaxch.x + 3)/4; lft_border0 = (1+labdigs)*sdigit.x + framewd + sep; rt_border = (lft_border0 - sep)/2 + outersep; bot_border = sdigit.y + framewd + sep; top_border = smaxch.y + framewd + sep; lft_border = lft_border0; /* this gets reset later */ } int is_off_screen(Point p) { const Rectangle* r = &(screen->r); return p.x-r->min.x<lft_border || r->max.x-p.x<rt_border || p.y-r->min.y<=top_border || r->max.y-p.y<=bot_border; } Cursor bullseye = { {-7, -7}, { 0x1F, 0xF8, 0x3F, 0xFC, 0x7F, 0xFE, 0xFB, 0xDF, 0xF3, 0xCF, 0xE3, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC7, 0xF3, 0xCF, 0x7B, 0xDF, 0x7F, 0xFE, 0x3F, 0xFC, 0x1F, 0xF8, }, { 0x00, 0x00, 0x0F, 0xF0, 0x31, 0x8C, 0x21, 0x84, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x7F, 0xFE, 0x7F, 0xFE, 0x41, 0x82, 0x41, 0x82, 0x41, 0x82, 0x21, 0x84, 0x31, 0x8C, 0x0F, 0xF0, 0x00, 0x00, } }; /* Wait for a mouse click and return 0 for failue if not button but (curs can be 0) */ int get_1click(int but, Mouse* m, Cursor* curs) { if (curs) esetcursor(curs); while (m->buttons==0) *m = emouse(); if (curs) esetcursor(0); return (m->buttons==Button_bit(but)); } /* Wait for a mouse click or keyboard event from the string of expected characters. Return the character code or -1 for a button-but mouse event or 0 for wrong button. */ int get_click_or_kbd(int but, Mouse* m, const char* expected) { Event ev; ulong expbits[4], ty; expbits[0] = expbits[1] = expbits[2] = expbits[3]; for (; *expected!=0; expected++) expbits[((*expected)>>5)&3] |= 1 << (*expected&31); do ty = eread(Emouse|Ekeyboard, &ev); while ((ty&Emouse) ? ev.mouse.buttons==0 : (ev.kbdc&~127) || !(expbits[(ev.kbdc>>5)&3] & (1<<(ev.kbdc&31))) ); if (ty&Ekeyboard) return ev.kbdc; *m = ev.mouse; return (ev.mouse.buttons==Button_bit(but)) ? -1 : 0; } /* Wait until but goes up or until a mouse event's msec passes tlimit. Return a boolean result that tells whether the button went up. */ int lift_button(int but, Mouse* m, int tlimit) { do { *m = emouse(); if (m->msec >= tlimit) return 0; } while (m->buttons & Button_bit(but)); return 1; } /* Set *m to the last pending mouse event, or the first one where but is up. If no mouse events are pending, wait for the next one. */ void latest_mouse(int but, Mouse* m) { int bbit = Button_bit(but); do { *m = emouse(); } while ((m->buttons & bbit) && ecanmouse()); } /*********************************** Colors ***********************************/ enum { DOrange=0xffaa00FF, Dgray=0xbbbbbbFF, DDkgreen=0x009900FF, DDkred=0xcc0000FF, DViolet=0x990099FF, DDkyellow=0xaaaa00FF, DLtblue=0xaaaaffFF, DPink=0xffaaaaFF, /* ndraw.h sets DBlack, DBlue, DRed, DYellow, DGreen, DCyan, DMagenta, DWhite */ }; typedef struct thick_color { int thick; /* use 1+2*thick pixel wide lines */ Image* clr; /* Color to use when drawing this */ } thick_color; typedef struct color_ref { ulong c; /* RGBA pixel color */ char* nam; /* ASCII name (matched to input, used in output)*/ int nam1; /* single-letter version of color name */ Image* im; /* replicated solid-color image */ } color_ref; color_ref clrtab[] = { DRed, "Red", 'R', 0, DPink, "Pink", 'P', 0, DDkred, "Dkred", 'r', 0, DOrange, "Orange", 'O', 0, DYellow, "Yellow", 'Y', 0, DDkyellow, "Dkyellow", 'y', 0, DGreen, "Green", 'G', 0, DDkgreen, "Dkgreen", 'g', 0, DCyan, "Cyan", 'C', 0, DBlue, "Blue", 'B', 0, DLtblue, "Ltblue", 'b', 0, DMagenta, "Magenta", 'M', 0, DViolet, "Violet", 'V', 0, Dgray, "Gray", 'A', 0, DBlack, "Black", 'K', 0, DWhite, "White", 'W', 0, DNofill, 0, 0, 0 /* DNofill means "end of data" */ }; short nam1_idx[128]; /* the clrtab[] index for each nam1, else -1 */ void init_clrtab(void) { int i; Rectangle r = Rect(0,0,1,1); memset(&nam1_idx[0], -1, sizeof(nam1_idx)); for (i=0; clrtab[i].c!=DNofill; i++) { clrtab[i].im = allocimage(display, r, CMAP8, 1, clrtab[i].c); /* should check for 0 result? */ nam1_idx[clrtab[i].nam1] = i; } } int clrim_id(Image* clr) { int i; for (i=0; clrtab[i].im!=clr; i++) if (clrtab[i].c==DNofill) exits("bad image color"); return i; } int clr_id(int clr) { int i; for (i=0; clrtab[i].c!=clr; i++) if (clrtab[i].c==DNofill) exits("bad color"); return i; } #define clr_im(clr) clrtab[clr_id(clr)].im #define is_Multi -2 /* dummy clrtab[] less than -1 */ thick_color* tc_default(thick_color *buf) { buf[0].thick = 1; buf[1].clr = clr_im(DBlack); buf[1].thick = 0; return buf; } /* Return an allocated array that describes the color letters (clrtab[].nam1 values, optionally prefixed by 'T') in the string that starts at c0 and ends just before fin. The first entry is a dummy whose thick field tells how many data entries follow. If buf!=0, it should point to an array of length 2 that is to hold the output (truncated to a dummy and one data entry). The error indication is 1 data entry of default color and thickness; e.g., "Multi(xxbadxx)" in a label prevents gview from recognizing anything that follows. */ thick_color* parse_color_chars(const char* c0, const char* fin, thick_color *buf) { thick_color* tc; /* Pending return value */ int i, j, n=fin-c0; /* n is an upper bound on how many data members */ const char* c; for (c=c0; c<fin-1; c++) if (*c=='T') n--; if (buf==0) tc = (thick_color*) malloc((n+1)*sizeof(thick_color)); else {tc=buf; n=1;} i = 0; for (c=c0; c<fin && i<n; c++) { tc[++i].thick = 0; if (*c=='T') if (++c==fin) return tc_default(tc); else tc[i].thick=1; j = (*c&~127) ? -1 : nam1_idx[*c]; if (j < 0) return tc_default(tc); tc[i].clr = clrtab[j].im; } tc[0].thick = i; return tc; } /* Decide what color and thickness to use for a polyline based on the label it has in the input file. The winner is whichever color name comes first, or otherwise black; and thickness is determined by the presence of "Thick" in the string. Place the result in *r1 and return 0 unless a Multi(...) spec is found, in which case the result is an allocated array of alternative color and thickness values. A nonzero idxdest requests the clrtab[] index in *idxdest and no allocated array. */ thick_color* nam2thclr(const char* nam, thick_color *r1, int *idxdest) { char *c, *cbest=0, *rp=0; int i, ibest=-1; thick_color* tc = 0; thick_color buf[2]; if (*nam!=0) { c = strstr(nam, "Multi("); if (c!=0 && (rp=strchr(c+6,')'))!=0) {ibest=is_Multi; cbest=c;} for (i=0; clrtab[i].nam!=0; i++) { c = strstr(nam,clrtab[i].nam); if (c!=0 && (ibest==-1 || c<cbest)) {ibest=i; cbest=c;} } } if (ibest==is_Multi) { tc = parse_color_chars(cbest+6, rp, (idxdest==0 ? 0 : &buf[0])); ibest = clrim_id(tc[1].clr); } if (idxdest!=0) *idxdest = (ibest<0) ? clr_id(DBlack) : ibest; r1->clr = (ibest<0) ? clr_im(DBlack) : clrtab[ibest].im; r1->thick = (tc!=0) ? tc[1].thick : (strstr(nam,"Thick")==0 ? 0 : 1); return tc; } /* Alter string nam so that nam2thick() and nam2clr() agree with *tc, using buf[] (a buffer of length bufn) to store the result if it differs from nam. We go to great pains to perform this alteration in a manner that will seem natural to the user, i.e., we try removing a suitably isolated color name before inserting a new one. */ char* nam_with_thclr(char* nam, const thick_color *tc, char* buf, int bufn) { thick_color c0; int clr0i; nam2thclr(nam, &c0, &clr0i); char *clr0s; if (c0.thick==tc->thick && c0.clr==tc->clr) return nam; clr0s = clrtab[clr0i].nam; if (strlen(nam)<bufn) strcpy(buf,nam); else {strncpy(buf,nam,bufn); buf[bufn-1]='\0';} if (c0.clr != tc->clr) remove_substr(buf, clr0s); if (c0.thick > tc->thick) while (remove_substr(buf, "Thick")) /* do nothing */; nam2thclr(nam, &c0, &clr0i); if (c0.clr != tc->clr) str_insert(buf, clrtab[clrim_id(tc->clr)].nam, bufn); if (c0.thick < tc->thick) str_insert(buf, "Thick", bufn); return buf; } /****************************** Data structures ******************************/ Image* mv_bkgd; /* Background image (usually 0) */ typedef struct fpoint { double x, y; } fpoint; typedef struct frectangle { fpoint min, max; } frectangle; frectangle empty_frect = {1e30, 1e30, -1e30, -1e30}; /* When *r2 is transformed by y=y-x*slant, might it intersect *r1 ? */ int fintersects(const frectangle* r1, const frectangle* r2, double slant) { double x2min=r2->min.x, x2max=r2->max.x; if (r1->max.x <= x2min || x2max <= r1->min.x) return 0; if (slant >=0) {x2min*=slant; x2max*=slant;} else {double t=x2min*slant; x2min=x2max*slant; x2max=t;} return r1->max.y > r2->min.y-x2max && r2->max.y-x2min > r1->min.y; } int fcontains(const frectangle* r, fpoint p) { return r->min.x <=p.x && p.x<= r->max.x && r->min.y <=p.y && p.y<= r->max.y; } void grow_bb(frectangle* dest, const frectangle* r) { if (r->min.x < dest->min.x) dest->min.x=r->min.x; if (r->min.y < dest->min.y) dest->min.y=r->min.y; if (r->max.x > dest->max.x) dest->max.x=r->max.x; if (r->max.y > dest->max.y) dest->max.y=r->max.y; } void slant_frect(frectangle *r, double sl) { r->min.y += sl*r->min.x; r->max.y += sl*r->max.x; } fpoint fcenter(const frectangle* r) { fpoint c; c.x = .5*(r->max.x + r->min.x); c.y = .5*(r->max.y + r->min.y); return c; } typedef struct fpolygon { fpoint* p; /* a malloc'ed array */ int n; /* p[] has n elements: p[0..n] */ frectangle bb; /* bounding box */ char* nam; /* name of this polygon (malloc'ed) */ thick_color c; /* the current color and line thickness */ thick_color* ct; /* 0 or malloc'ed color schemes, ct[1..ct->thick] */ struct fpolygon* link; } fpolygon; typedef struct fpolygons { fpolygon* p; /* the head of a linked list */ frectangle bb; /* overall bounding box */ frectangle disp; /* part being mapped onto screen->r */ double slant_ht; /* controls how disp is slanted */ } fpolygons; fpolygons univ = { /* everything there is to display */ 0, 1e30, 1e30, -1e30, -1e30, 0, 0, 0, 0, 2*1e30 }; void free_fp_etc(fpolygon* fp) { if (fp->ct != 0) free(fp->ct); free(fp->p); free(fp); } void set_default_clrs(fpolygons* fps, fpolygon* fpstop) { fpolygon* fp; for (fp=fps->p; fp!=0 && fp!=fpstop; fp=fp->link) fp->ct = nam2thclr(fp->nam, &fp->c, 0); } void fps_invert(fpolygons* fps) { fpolygon *p, *r=0; for (p=fps->p; p!=0;) { fpolygon* q = p; p = p->link; q->link = r; r = q; } fps->p = r; } void fp_remove(fpolygons* fps, fpolygon* fp) { fpolygon *q, **p = &fps->p; while (*p!=fp) if (*p==0)