/* file: xw.c Paul Albrecht May 1989 Last revised: 3 September 2008 X11 driver for plt Copyright (C) Paul Albrecht 1988 Recent changes (by George Moody, george@mit.edu): 10 October 2002: fixed solid line style 23 February 2001: added color support 20 March 2001: added grey, fill, line mode, line width 23 March 2001: added font scaling, X resource support 26 March 2001: added font rotation 30 March 2001: added LN, CN, RN, label concatenation 11 April 2001: general cleanup 24 April 2001: added progressive display in quickplot mode 10 May 2001: reduced size of exponents in elabel 19 May 2001: added symbol scaling based on point size for fontgroup f 3 September 2008: disabled font substitution warnings unless DEBUG is defined _______________________________________________________________________________ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. You may contact the maintainer by e-mail (george@mit.edu) or postal mail (MIT Room E25-505A, Cambridge, MA 02139 USA). For updates to this software, please visit PhysioNet (http://www.physionet.org/). _______________________________________________________________________________ */ #include "plt.h" #include #include #include #include #include #define X(N) (int)(xScl*((N)-xMin)) #define Y(N) (int)(yDim - (int)(yScl*((N)-yMin))) #define CBGFILL (Const)15 /* outline polygon with opaque white center */ #define QPFREQ 50 /* maximum refresh rate (Hz) in quickplot mode; QPFREQ must never be less than 1 or greater than 1000000, but reasonable values are between 10 and the lower of your monitor's refresh rate and 100. */ static Display *dpy; static int screen; static Window window; static Pixmap pixmap; static GC gc; static XFontStruct *font; static XWindowAttributes wa; static Ptype xLast, yLast; /* Ptype is int or short */ static Ptype xMin, yMin, xDim, yDim; static double xScl=1, yScl=1; static XColor fgcolor; /* current foreground color (background is always white) */ /* Prototypes of functions defined in this module */ void openpl(void); void closepl(void); void erase(void); void space(Ptype x0, Ptype y0, Ptype x1, Ptype y1); void label(char *lbl); void move(Ptype x, Ptype y); void line(Ptype x0, Ptype y0, Ptype x1, Ptype y1); void cont(Ptype x, Ptype y); void plabel(char *lbl, Ptype x, Ptype y, char *just, double angle); void alabel(char *what, char *lbl, Ptype x, Ptype y); void elabel(char *what, char *base, char *exp, Ptype x, Ptype y); void strsize(char *str, Ptype *xsize, Ptype *ysize, double angle); void scatterplot(PltPtr plt, Ptype x, Ptype y, Ptype yneg, Ptype ypos); void PolyDef(Ptype x, Ptype y, Const what); void special(int what, PtrUnion arg0, PtrUnion arg1); static void update_display(int ignored); static void point(Ptype x, Ptype y); static void linewidth(double lw); static void setgray(double grey); static void setcolor(char *color); static void linemod(char *lineMode); static void OpenPixelWindow(void); static char *OpenPixelFont(void); static void setfont(char *name, double ps); static void CreatePltWindow(char *windowName); static Boolean PltWindowFind(char *windowName); static void label0(char *lbl, Ptype x, Ptype y, char *just); static void label90(char *lbl, Ptype x, Ptype y, char *just); static void pcont(Ptype x, Ptype y); static int ErrorHandler() { return (0); } #define DEF_FONT_H "-*-helvetica-medium-r-*-*-%d-*-*-*-*-*-*-*" #define DEF_FONT_HB "-*-helvetica-bold-r-*-*-%d-*-*-*-*-*-*-*" #define DEF_FONT_HO "-*-helvetica-medium-o-*-*-%d-*-*-*-*-*-*-*" #define DEF_FONT_HBO "-*-helvetica-bold-o-*-*-%d-*-*-*-*-*-*-*" #define DEF_FONT_C "-*-courier-medium-r-*-*-%d-*-*-*-*-*-*-*" #define DEF_FONT_CB "-*-courier-bold-r-*-*-%d-*-*-*-*-*-*-*" #define DEF_FONT_CO "-*-courier-medium-o-*-*-%d-*-*-*-*-*-*-*" #define DEF_FONT_CBO "-*-courier-bold-o-*-*-%d-*-*-*-*-*-*-*" #define DEF_FONT_T "-*-times-medium-r-*-*-%d-*-*-*-*-*-*-*" #define DEF_FONT_TB "-*-times-bold-r-*-*-%d-*-*-*-*-*-*-*" #define DEF_FONT_TI "-*-times-medium-i-*-*-%d-*-*-*-*-*-*-*" #define DEF_FONT_TBI "-*-times-bold-i-*-*-%d-*-*-*-*-*-*-*" #define DEF_FONT_S "-*-symbol-medium-r-*-*-%d-*-*-*-*-*-*-*" static struct { char *name; char *code; } *f, fonts[] = { DEF_FONT_H, "h", DEF_FONT_HB, "hb", DEF_FONT_HO, "ho", DEF_FONT_HBO, "hbo", DEF_FONT_HBO, "hob", DEF_FONT_C, "c", DEF_FONT_CB, "cb", DEF_FONT_CO, "co", DEF_FONT_CBO, "cbo", DEF_FONT_CBO, "cob", DEF_FONT_T, "t", DEF_FONT_T, "tr", DEF_FONT_TI, "ti", DEF_FONT_TB, "tb", DEF_FONT_TBI, "tbi", DEF_FONT_TBI, "tib", DEF_FONT_S, "s", NULL, NULL }; void openpl(void) { if (!dpy) { OpenPixelWindow(); /* read X resource database and set defaults */ for (f = fonts; f->code; f++) { char *p, resource_name[10]; sprintf(resource_name, "font_%s", f->code); if (p = XGetDefault(dpy, "xplt", resource_name)) f->name = p; } } setfont(DEFAULT_FONT, (double)DEFAULT_PS); linemod("solid"); linewidth(0.); setcolor("Black"); if (Plot.quickPlot) update_display(0); /* enable progressive display */ } /* update_display is the signal handler for SIGVTALRM (set in quickplot mode only to permit progressive display updating). This signal handler can be installed by invoking it with signum = 0. */ static void update_display(int signum) { #ifdef SIGVTALRM /* defined under BSD 4.2+, SVR4, Solaris, Linux ... */ struct itimerval timer; signal(SIGVTALRM, SIG_IGN); if (signum == SIGVTALRM) closepl(); /* update display (force X server to render the pixmap) */ timer.it_interval.tv_sec = timer.it_value.tv_sec = 0; timer.it_interval.tv_usec = timer.it_value.tv_usec = (long)(1e6/QPFREQ); setitimer(ITIMER_VIRTUAL, &timer, NULL); signal(SIGVTALRM, update_display); #endif } void closepl(void) { XExposeEvent *e; XEvent event; if (wa.map_state == IsViewable) { e = (XExposeEvent *)(&event); /* Send message to window to redraw */ e->type = Expose; e->send_event = True; e->display = dpy; e->window = window; e->x = e->y = 0; e->width = wa.width; e->height = wa.height; XSendEvent(dpy, window, True, ExposureMask, &event); } XFlush(dpy); } void erase(void) { if (pixmap != window) { XSetForeground(dpy, gc, WhitePixel(dpy,screen)); XFillRectangle(dpy, pixmap, gc, 0, 0, wa.width, wa.height); XSetForeground(dpy, gc, fgcolor.pixel=BlackPixel(dpy,screen)); } if(wa.map_state == IsViewable) XClearWindow(dpy, window); } void space(Ptype x0, Ptype y0, Ptype x1, Ptype y1) { xMin = x0; yMin = y0; xScl = (double)xDim/(x1-x0+1); yScl = (double)yDim/(y1-y0+1); } void move(Ptype x, Ptype y) { xLast = x; yLast = y; } void cont(Ptype x, Ptype y) { XDrawLine(dpy, pixmap, gc, X(xLast), Y(yLast), X(x), Y(y)); xLast = x; yLast = y; } void line(Ptype x0, Ptype y0, Ptype x1, Ptype y1) { XDrawLine(dpy, pixmap, gc, X(x0), Y(y0), X(x1), Y(y1)); xLast = x1; yLast = y1; } static void point(Ptype x, Ptype y) { XDrawPoint(dpy, pixmap, gc, X(x), Y(y)); xLast = x; yLast = y; } void label(char *str) { XDrawString(dpy, pixmap, gc, X(xLast), Y(yLast), str, strlen(str)); } static void linewidth(double lw) { static double prev_lw = -1; XGCValues gcvalues; if (lw < 0) { err(NO, "Bad line width: %.5lg", lw); lw = 0.3; } if (prev_lw != lw) { gcvalues.line_width = (int)(lw/5.0 + 0.5); gcvalues.join_style = JoinMiter; (void)XChangeGC(dpy, gc, GCLineWidth | GCJoinStyle, &gcvalues); } } static void setgray(double grey) { char greystring[8]; if (grey < 0 || grey > 1) { err(NO, "Bad grey value: %.5lg", grey); grey = 0; } sprintf(greystring, "grey%02d", (int)(grey*100.0)); setcolor(greystring); } static void setcolor(char *color) { Colormap default_cmap = DefaultColormap(dpy, screen); if (DefaultDepth(dpy, screen) > 1 && XParseColor(dpy, default_cmap, color, &fgcolor) && XAllocColor(dpy, default_cmap, &fgcolor)) XSetForeground(dpy, gc, fgcolor.pixel); else XSetForeground(dpy, gc, fgcolor.pixel = BlackPixel(dpy, screen)); } static void linemod(char *lineMode) { static char solid[] = { 1 }, dotted[] = { 2, 4 }, shortdashed[] = { 4, 4 }, longdashed[] = { 8, 8 }, dotdashed[] = { 2, 4, 6, 4 }; static char oldLineMode[16]; XGCValues gcvalues; char *dash_list = NULL; int n = 0; if (strcmp(lineMode,oldLineMode) == 0) return; if (strcmp(lineMode,"dotted") == 0) { dash_list = dotted; n = sizeof(dotted); } else if (strcmp(lineMode,"dotdashed") == 0) { dash_list = dotdashed; n = sizeof(dotdashed); } else if (strcmp(lineMode,"shortdashed") == 0) { dash_list = shortdashed; n = sizeof(shortdashed); } else if (strcmp(lineMode,"longdashed") == 0) { dash_list = longdashed; n = sizeof(longdashed); } else { lineMode = "solid"; dash_list = solid; n = sizeof(solid); } if (n > 1) gcvalues.line_style = LineOnOffDash; else gcvalues.line_style = LineSolid; (void)XChangeGC(dpy, gc, GCLineStyle, &gcvalues); if (n > 1) XSetDashes(dpy, gc, 1, dash_list, n); strncpy(oldLineMode, lineMode, sizeof(oldLineMode)-1); } void special(int what, PtrUnion arg0, PtrUnion arg1) { switch (what) { case SETCOLOR: setcolor(arg0.c); break; case SETFONT: setfont(arg0.c, *arg1.d); break; case SETGRAY: case SETGRAYD: setgray(*arg0.d); break; case SETLINEMODE: linemod(arg0.c); break; case SETLINEWIDTH: linewidth(*arg0.d); break; case SETPTERM: openpl(); /* defines xDim, yDim and font size information */ PTERMLookup(arg0.c, "xw"); p->xfull = p->xsquare = xDim*10; p->yfull = yDim*10; chwd = p->cw = font->max_bounds.width*10; chht = p->ch = (font->max_bounds.ascent+font->max_bounds.descent)*10; p->xinches = xDim/75.0; p->yinches = yDim/75.0; xinch = p->xfull/p->xinches; yinch = p->yfull/p->yinches; break; } } static void OpenPixelWindow(void) { char *windowName, *getenv(); if ((dpy = XOpenDisplay(NULL)) == NULL) { fprintf(stderr, "Can't open X server display\n"); exit(1); } screen = DefaultScreen(dpy); if ((windowName = getenv("XPLTWIN")) == NULL || *windowName == 0) windowName = "Plt Window"; if (!PltWindowFind(windowName)) { CreatePltWindow(windowName); if (!PltWindowFind(windowName)) sleep(3); /* Time sharing, wait for window to show up */ if (!PltWindowFind(windowName)) { fprintf(stderr, "Missing xpltwin or something is botched!\n"); exit(1); } } XGetWindowAttributes(dpy, window, &wa); xDim = wa.width; yDim = wa.height; gc = XCreateGC(dpy, pixmap, 0L, (XGCValues *)0); XSetForeground(dpy, gc, BlackPixel(dpy,screen)); (void)OpenPixelFont(); } typedef struct { short xMinDim; short yMinDim; char *preferredFontName; char *acceptableFontName; char *pleaseNotThisFontName; } SizeSpec; static SizeSpec pointSizes[] = { 250, 350, "lucidasanstypewriter-8", "6x10", "fixed", 300, 400, "lucidasanstypewriter-10", "6x12", "fixed", 550, 450, "lucidasanstypewriter-bold-10","lucidasanstypewriter-10","8x13", 650, 550, "lucidasanstypewriter-bold-12","lucidasanstypewriter-12","9x15", 725, 625, "lucidasanstypewriter-bold-14", "10x20", "9x15", 800, 700, "lucidasanstypewriter-bold-18", "12x24", "9x15", 9999,9999, "" /* dummy, never used */ }; static char *OpenPixelFont(void) { SizeSpec *pt; char *fn; for (pt = pointSizes; pt < ENDP(pointSizes)-1; pt++) { if(pt[1].xMinDim > xDim || pt[1].yMinDim > yDim) break; } if ((font = XLoadQueryFont(dpy, fn = pt->preferredFontName)) == 0 && (font = XLoadQueryFont(dpy, fn = pt->acceptableFontName)) == 0 && (font = XLoadQueryFont(dpy, fn = pt->pleaseNotThisFontName)) == 0) { fprintf(stderr, "%s font not found\n", pt->pleaseNotThisFontName); exit (1); } XSetFont(dpy, gc, font->fid); return (fn); } static double prev_ps = -1; static char *prev_name; static char fontcode[4] = "t"; static void setfont(char *name, double ps) { short n = 0; char *fontname = name; while (*fontname && n < 3) { fontcode[n++] = *(fontname++) | 040; /* save as lower-case */ while (*fontname && *(fontname++) != '-') ; } fontcode[n] = 0; for (f = fonts; f->code != NULL && strcmp(fontcode, f->code) != 0; f++) ; if (f->code == NULL) { err( NO, "Unknown font: %s -- Using: %s", name, DEFAULT_FONT); f->name = DEFAULT_FONT; } if (ps < 0) { err( NO, "Bad font size: %.5lg -- Using %g", ps, DEFAULT_PS); ps = DEFAULT_PS; } if (prev_ps != ps || prev_name != f->name) { char fullfontname[64]; int i, points = (int)((prev_ps = ps)*wa.width/670.0+0.5); sprintf(fullfontname, prev_name = f->name, points); font = XLoadQueryFont(dpy, fullfontname); /* If the desired font isn't available in the size we need, try varying the size by up to 5 points in either direction. */ for (i = 1; !font && i <= 5; i++) { char subfontname[64]; sprintf(subfontname, prev_name, points+i); if (!(font = XLoadQueryFont(dpy, subfontname)) && points > i) { sprintf(subfontname, prev_name, points-i); font = XLoadQueryFont(dpy, subfontname); } #ifdef DEBUG if (font) fprintf(stderr, "substituting %s for %s\n", subfontname, fullfontname); #endif } if (!font) { char *p; p = OpenPixelFont(); #ifdef DEBUG fprintf(stderr, "substituting %s for %s\n", p, fullfontname); #endif } else XSetFont(dpy, gc, font->fid); chwd = font->max_bounds.width*10; chht = (font->max_bounds.ascent+font->max_bounds.descent)*10; } } static void CreatePltWindow(char *windowName) { char systemCall[128]; sprintf(systemCall, "xpltwin '%s'", windowName); system(systemCall); sleep(1); } static Boolean PltWindowFind(char *windowName) { Drawable *drawables; Atom pltWindowAtom, actualType; long nItems, bytesAfter; int actualFormat; char atomName[100]; sprintf(atomName, "_xpltwin_%d_%s", screen, windowName); pltWindowAtom = XInternAtom(dpy, atomName, True); if (pltWindowAtom == None) return(NO); XGetWindowProperty(dpy, RootWindow(dpy,screen), pltWindowAtom, 0, 2, False, XA_DRAWABLE, &actualType, &actualFormat, &nItems, &bytesAfter, (unsigned char **)&drawables); if (actualType != XA_DRAWABLE || actualFormat != 32 || nItems != 2) return(NO); window = drawables[0]; pixmap = drawables[1]; XFree(drawables); actualType = actualFormat = nItems = 0; XSetErrorHandler(ErrorHandler); XGetWindowProperty(dpy, window, pltWindowAtom, 0, 2, False, XA_DRAWABLE, &actualType, &actualFormat, &nItems, &bytesAfter, (unsigned char **)&drawables); XSetErrorHandler((void *)0); if (actualType != XA_DRAWABLE || actualFormat != 32 || nItems != 2 || window != drawables[0]) return(NO); XFree(drawables); return(YES); } static Ptype xax_off, yax_off, xax_roff, yax_roff, xsize, ysize, lastx, lasty; static void label0(char *lbl, Ptype x, Ptype y, char *just) /* horizontal */ { short len; Boolean errflag; len = strlen(lbl); errflag = NO; strsize(lbl, &xsize, &ysize, 0.0); switch (just[0]) { case 'L': break; case 'R': x -= xsize; break; case 'C': x -= xsize/2; break; default: errflag = YES; break; } switch (just[1]) { case 'T': y -= chht; break; case 'N': y -= 2*chht/3; break; case 'C': y -= chht/3; break; case 'B': break; default: errflag = YES; break; } if (errflag) err(NO, "Unknown justification mode `%s'", just); else { move(x, y); label(lbl); lastx = x + xsize; lasty = y + chht/3; } } static void label90(char *lbl, Ptype x, Ptype y, char *just) /* vertical */ { Pixmap temppixmap; int xx, yy, lwidth, lheight, errflag = NO; /* First figure out how large an area is needed for the label. */ strsize(lbl, &xsize, &ysize, 0.0); lwidth = xsize/10; lheight = ysize/6; switch (just[0]) { case 'L': y += xsize; break; case 'C': y += xsize/2; break; case 'R': break; default: errflag = YES; break; } switch (just[1]) { case 'T': x += chht/3; break; case 'N': break; case 'C': x -= chht/3; break; case 'B': x -= 2*chht/3; break; default: errflag = YES; break; } if (errflag) err(NO, "Unknown justification mode '%s'", just); lastx = x + ysize/3; lasty = y; xx = X(x); yy = Y(y); if (xx < 0) { lheight += xx; xx = 0; } if (yy < 0) { lwidth += yy; yy = 0; } /* Crop out anything that would fall outside the window. */ if (lheight > wa.width - xx) lheight = wa.width - xx; if (lwidth > wa.height - yy) lwidth = wa.height - yy; /* Do nothing if the label would be entirely outside the window. */ if (lheight <= 0 || lwidth <= 0) return; /* Make a pixmap (off-screen drawing area) of the requisite size. */ if (temppixmap = XCreatePixmap(dpy, window, lwidth, lheight, DefaultDepth(dpy, screen))) { int ix, iy; XImage *xi, *xo; unsigned long whitepixel = WhitePixel(dpy, screen); /* Clear the temporary pixmap. */ XSetForeground(dpy, gc, whitepixel); XFillRectangle(dpy, temppixmap, gc, 0, 0, lwidth, lheight); XSetForeground(dpy, gc, fgcolor.pixel); /* Draw the label into the temporary pixmap (horizontally). */ XDrawString(dpy, temppixmap, gc, 0, lheight/2, lbl, strlen(lbl)); /* Create two images (client-side pixmap equivalents). The first one, xi, gets a copy of the temporary pixmap, with the label drawn into it. The second one, xo, gets a copy of the contents of the drawing window in the (vertically oriented) region where the label will be drawn. */ xi = XGetImage(dpy, temppixmap, 0, 0, lwidth, lheight, AllPlanes, XYPixmap); xo = XGetImage(dpy, pixmap, xx, yy, lheight, lwidth, AllPlanes, XYPixmap); /* Now we copy the non-background pixels from xi into the corresponding locations in xo. This has the effect of overlaying the label on anything that was already in the specified location. */ for (ix = 0; ix < lwidth; ix++) for (iy = 0; iy < lheight; iy++) { unsigned long pixelvalue; if ((pixelvalue = XGetPixel(xi, ix, iy)) != whitepixel) XPutPixel(xo, iy, lwidth-(ix+1), pixelvalue); } /* Copy the image back to the main window's pixmap. */ XPutImage(dpy, pixmap, gc, xo, 0, 0, xx, yy, lheight, lwidth); /* Release memory used for the images and for the temporary pixmap. */ XDestroyImage(xo); XDestroyImage(xi); XFreePixmap(dpy, temppixmap); } } void plabel(char *lbl, Ptype x, Ptype y, char *just, double angle) { if (lbl[0] == 0) return; if (x == DEFAULT) x = lastx; if (y == DEFAULT) y = lasty; if (fabs(angle) < 1) label0(lbl, x, y, just); else if (fabs(angle-90) < 1) label90(lbl, x, y, just); else err(NO, "Bad angle %g in for plabel(%s,...)", just, lbl); } /********* Output the tick labels and axis label for the axes *************/ void alabel(char *what, char *lbl, Ptype x, Ptype y) { short n; if (strcmp(what,"XN") == 0) { n = (lbl[0] == '-') ? 2*chwd/3 : 0; label0(lbl, x-n, y, "CT"); xax_off = chht; } else if (strcmp(what,"YN") == 0) { label0(lbl, x-chwd/3, y, "RC"); strsize(lbl, &xsize, &ysize, 0.0); n = xsize + chwd/3; if (n > yax_off) yax_off = n; } else if(strcmp(what,"XNR") == 0) { n = (lbl[0] == '-') ? 2*chwd/3 : 0; label0(lbl, x+n, y+chht/4, "CB"); xax_roff = chht; } else if(strcmp(what,"YNR") == 0) { label0(lbl, x+chwd/3, y, "LC"); strsize(lbl, &xsize, &ysize, 0.0); n = xsize + chwd/3; if (n > yax_roff) yax_roff = n; } else if (strcmp(what,"XT") == 0) label0(lbl, x, y-xax_off, "CT"); else if (strcmp(what,"YT") == 0) label90(lbl, x-yax_off, y, "CB"); else if(strcmp(what,"XTR") == 0) label0(lbl, x, y+xax_roff, "CB"); else if (strcmp(what,"YTR") == 0) label90(lbl, x+yax_roff, y, "CT"); else err(YES, "Bad call: alabel(%s,...)", what); } /****************** Output log tick labels for the axes ********************/ void elabel(char *what, char *base, char *exponent, Ptype x, Ptype y) { Ptype off; char *font_name = StringSave(fontcode); double base_ps = prev_ps, exp_ps = prev_ps*0.8; strsize(base, &xsize, &ysize, 0.0); off = xsize; setfont(font_name, exp_ps); strsize(exponent, &xsize, &ysize, 0.0); off += xsize; if (strcmp(what,"XE") == 0) { x += chwd/3; label0(exponent, x+off/2, y, "RT"); setfont(font_name, base_ps); label0(base, x-off/2, y-chht/3, "LT"); xax_off = 3*chht/2; } else if (strcmp(what,"YE") == 0) { x -= chwd/3; setfont(font_name, base_ps); label0(base, x-off, y-chht/3, "LB"); setfont(font_name, exp_ps); label0(exponent, x, y+(chht+3)/6, "RB"); if (off > yax_off) yax_off = off; setfont(font_name, base_ps); } else if (strcmp(what,"XER") == 0) { x += chwd/3; label0(exponent, x+off/2, y+3*chht/4, "RB"); setfont(font_name, base_ps); label0(base, x-off/2, y+chht/4, "LB"); xax_roff = 3*chht/2; } else if (strcmp(what,"YER") == 0) { x += chwd/2; label0(exponent, x+off, y+(chht+3)/6, "RB"); setfont(font_name, base_ps); label0(base, x, y-chht/3, "LB"); if (off > yax_roff) yax_roff = off; } else err(YES, "Bad call: elabel(%s,...)", what); } /****** Scatterplot characters or symbols with or without error bars *******/ static char circle[] = {100, 0, 89, 46, 57, 82, 12, 99, -34, 94, -74, 66, -96, 24, -96, -23, -74, -65, -34, -93, 12, -98, 57, -81, 89, -45, 100, 0}; static char diamond[] = {0, -15, 12, 0, 0, 15, -12, 0, 0, -15 }; static char triangle[] = {0, 1, -1, -1, 1, -1, 0, 1}; static char utriangle[] = {0, -1, -1, 1, 1, 1, 0, -1}; static char square[] = {-1, -1, -1, 1, 1, 1, 1, -1, -1, -1}; static struct syminf { char *pts; /* x,y points specifying the symbol contour */ short npts; double scl; /* factor for converting *pts to inches */ } *sym, syms[] = { circle, sizeof(circle), 0.11, utriangle, sizeof(utriangle), 12, diamond, sizeof(diamond), 1, square, sizeof(square), 10, triangle, sizeof(triangle), 12, circle, sizeof(circle), 0.11, utriangle, sizeof(utriangle), 12, diamond, sizeof(diamond), 1, square, sizeof(square), 10, triangle, sizeof(triangle), 12 }; void scatterplot(PltPtr plt, Ptype x, Ptype y, Ptype yneg, Ptype ypos) { static char lbl[2] = {0, 0}; double xscl, yscl; Ptype n, xd, yd, xdmin, xdmax, ydmin, ydmax; int nsyms = sizeof(syms)/sizeof(syms[0]); if (plt->symbol) { int symno = (plt->pc - '0') % nsyms; sym = &syms[symno]; xdmin = ydmin = 30000; xdmax = ydmax = -30000; xscl = xinch*sym->scl*xDim*prev_ps/(DEFAULT_PS*100000.0); yscl = yinch*sym->scl*xDim*prev_ps/(DEFAULT_PS*100000.0); for (n=0; n < sym->npts; n+=2) { xd = sym->pts[n]; yd = sym->pts[n+1]; xd = xscl*xd + (xd < 0 ? -0.5 : 0.5); yd = yscl*yd + (yd < 0 ? -0.5 : 0.5); if (n == 0) PolyDef(x+xd, y+yd, CMOVE); else if (n+2 < sym->npts) PolyDef(x+xd, y+yd, CCONT); else if (symno < nsyms/2) PolyDef(x+xd, y+yd, CBGFILL); else PolyDef(x+xd, y+yd, CFILL); if(xd < xdmin) xdmin = xd; if(xd > xdmax) xdmax = xd; if(yd < ydmin) ydmin = yd; if(yd > ydmax) ydmax = yd; } } else { if (plt->pc != '.') { lbl[0] = plt->pc; label0(lbl, x, y, "CC"); } else point(x, y); xdmax = chwd/2; xdmin = -xdmax; ydmax = chht/3; ydmin = -ydmax; } if (ypos-y >= ydmax) { move(x+xdmin, ypos); cont(x+xdmax, ypos); move(x, ypos); cont(x, y+ydmax); } if (yneg-y <= ydmin) { move(x+xdmin, yneg); cont(x+xdmax, yneg); move(x, yneg); cont(x, y+ydmin); } } int max_vertices = 0, nvertices = 0; XPoint *vertices = NULL; static void pcont(Ptype x, Ptype y) { if (nvertices >= max_vertices) { XPoint *nv = (XPoint *)realloc(vertices, (max_vertices + 1000)*sizeof(XPoint)); if (nv) { vertices = nv; max_vertices += 1000; } } if (nvertices < max_vertices) { vertices[nvertices].x = X(x); vertices[nvertices++].y = Y(y); } } void PolyDef(Ptype x, Ptype y, Const what) { static int x0, y0; XColor savecolor; switch (what) { case CMOVE: /* start a new polygon with first vertex at (x,y) */ if (nvertices > 0) { /* flush any unfinished polygon */ pcont(x0, y0); /* close the polygon first */ XDrawLines(dpy, pixmap, gc, vertices, nvertices, CoordModeOrigin); nvertices = 0; } move(x, y); pcont(x, y); x0 = x; y0 = y; break; case CCONT: /* add a vertex to the current polygon */ if (oldGrayLevel != PS_WHITE) pcont(x, y); break; case CBBCONT: pcont(x, y); break; case CFILL: case CFILLI: /* fill polygon with current color */ pcont(x, y); XFillPolygon(dpy, pixmap, gc, vertices, nvertices, Complex, CoordModeOrigin); nvertices = 0; break; case CBGFILL: /* outline polygon in current color, fill with white */ pcont(x, y); savecolor = fgcolor; setcolor("White"); XFillPolygon(dpy, pixmap, gc, vertices, nvertices, Complex, CoordModeOrigin); fgcolor = savecolor; XSetForeground(dpy, gc,fgcolor.pixel); XDrawLines(dpy, pixmap, gc, vertices, nvertices, CoordModeOrigin); nvertices = 0; break; case CSTROKE: /* draw the (outline of the) polygon */ if (oldGrayLevel != PS_WHITE) { pcont(x, y); XDrawLines(dpy, pixmap, gc, vertices, nvertices, CoordModeOrigin); nvertices = 0; } break; case COSTROKE: XDrawLines(dpy, pixmap, gc, vertices, nvertices, CoordModeOrigin); nvertices = 0; break; case CBBFILL: case CBBFILLI: /* fill polygon with current color, outline in black */ pcont(x, y); XFillPolygon(dpy, pixmap, gc, vertices, nvertices, Complex, CoordModeOrigin); savecolor = fgcolor; setcolor("Black"); pcont(x0, y0); XDrawLines(dpy, pixmap, gc, vertices, nvertices, CoordModeOrigin); fgcolor = savecolor; XSetForeground(dpy, gc, fgcolor.pixel); nvertices = 0; break; default: err(YES, "Bad PolyDef() code %d", what); } } /******* Give the x and y dimension of a string in device units ******/ void strsize(char *str, Ptype *xsize, Ptype *ysize, double angle) { Ptype width = XTextWidth(font, str, strlen(str))*10, height = chht; if (fabs(angle) < 1) { *xsize = width; *ysize = height; } else if (fabs(angle-90) < 1) { *xsize = height; *ysize = width; } else err(NO, "Bad angle %lg in for strsize(%s,...)", angle, str); }