argv@island.uu.net (Dan Heller) (04/11/89)
Submitted-by: David Harrison <davidh@ic.berkeley.edu> Posting-number: Volume 3, Issue 73 Archive-name: xgraph/part04 #!/bin/sh # to extract, remove the header and type "sh filename" if `test ! -d ./xgraph-11` then mkdir ./xgraph-11 echo "mkdir ./xgraph-11" fi if `test ! -s ./xgraph-11/xgraph.c` then echo "writing ./xgraph-11/xgraph.c" cat > ./xgraph-11/xgraph.c << '\End\Of\Shar\' /* * xgraph - A Simple Plotter for X * * David Harrison * University of California, Berkeley * 1986, 1987, 1988, 1989 * * Please see copyright.h concerning the formal reproduction rights * of this software. */ #include "copyright.h" #include <stdio.h> #include <math.h> #include <pwd.h> #include <ctype.h> #include "xgout.h" #include "xgraph.h" #include "xtb.h" #include "hard_devices.h" #include <X11/Xutil.h> #define ZOOM #define TOOLBOX #ifndef MAXFLOAT #define MAXFLOAT HUGE #endif #define BIGINT 0xfffffff #define GRIDPOWER 10 #define INITSIZE 128 #define CONTROL_D '\004' #define CONTROL_C '\003' #define TILDE '~' #define BTNPAD 1 #define BTNINTER 3 #define MAX(a,b) ((a) > (b) ? (a) : (b)) #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define ABS(x) ((x) < 0 ? -(x) : (x)) #define ZERO_THRES 1.0E-07 /* To get around an inaccurate log */ #define nlog10(x) (x == 0.0 ? 0.0 : log10(x) + 1e-15) #define ISCOLOR (wi->dev_info.dev_flags & D_COLOR) #define PIXVALUE(set) ((set) % MAXATTR) #define LINESTYLE(set) \ (ISCOLOR ? ((set)/MAXATTR) : ((set) % MAXATTR)) #define MARKSTYLE(set) \ (colorMark ? COLMARK(set) : BWMARK(set)) #define COLMARK(set) \ ((set) / MAXATTR) #define BWMARK(set) \ ((set) % MAXATTR) extern void init_X(); #ifdef TOOLBOX extern void do_error(); #endif static char *tildeExpand(); AttrSet AllAttrs[MAXATTR]; typedef struct data_set { char *setName; /* Name of set */ int numPoints; /* How many points */ int allocSize; /* Allocated size */ double *xvec; /* X values */ double *yvec; /* Y values */ } DataSet; static DataSet AllSets[MAXSETS]; static XSegment *Xsegs; /* Point space for X */ /* Basic transformation stuff */ static double llx, lly, urx, ury; /* Bounding box of all data */ typedef struct local_win { double loX, loY, hiX, hiY; /* Local bounding box of window */ int XOrgX, XOrgY; /* Origin of bounding box on screen */ int XOppX, XOppY; /* Other point defining bounding box */ double UsrOrgX, UsrOrgY; /* Origin of bounding box in user space */ double UsrOppX, UsrOppY; /* Other point of bounding box */ double XUnitsPerPixel; /* X Axis scale factor */ double YUnitsPerPixel; /* Y Axis scale factor */ xgOut dev_info; /* Device information */ Window close, hardcopy; /* Buttons for closing and hardcopy */ } LocalWin; #define SCREENX(ws, userX) \ (((int) (((userX) - ws->UsrOrgX)/ws->XUnitsPerPixel + 0.5)) + ws->XOrgX) #define SCREENY(ws, userY) \ (ws->XOppY - ((int) (((userY) - ws->UsrOrgY)/ws->YUnitsPerPixel + 0.5))) static XContext win_context = (XContext) 0; /* Other globally set defaults */ Display *disp; /* Open display */ Visual *vis; /* Standard visual */ Colormap cmap; /* Standard colormap */ int screen; /* Screen number */ int depth; /* Depth of screen */ int install_flag; /* Install colormaps */ Pixel black_pixel; /* Actual black pixel */ Pixel white_pixel; /* Actual white pixel */ Pixel bgPixel; /* Background color */ int bdrSize; /* Width of border */ Pixel bdrPixel; /* Border color */ Pixel zeroPixel; /* Zero grid color */ int zeroWidth; /* Width of zero line */ char zeroLS[MAXLS]; /* Line style spec */ int zeroLSLen; /* Length of zero LS spec */ Pixel normPixel; /* Norm grid color */ int axisWidth; /* Width of axis line */ char axisLS[MAXLS]; /* Axis line style spec */ int axisLSLen; /* Length of axis line style */ Pixel echoPix; /* Echo pixel value */ XFontStruct *axisFont; /* Font for axis labels */ XFontStruct *titleFont; /* Font for title labels */ char titleText[MAXBUFSIZE]; /* Plot title */ char XUnits[MAXBUFSIZE]; /* X Unit string */ char YUnits[MAXBUFSIZE]; /* Y Unit string */ int bwFlag; /* Black and white flag */ int tickFlag; /* Don't draw full grid */ int bbFlag; /* Whether to draw bb */ int noLines; /* Don't draw lines */ int markFlag; /* Draw marks at points */ int pixelMarks; /* Draw pixel markers */ int bigPixel; /* Draw big pixels */ int colorMark; /* Normal markers track color */ int logXFlag; /* Logarithmic X axis */ int logYFlag; /* Logarithmic Y axis */ int barFlag; /* Draw bar graph */ double barBase, barWidth; /* Base and width of bars */ int lineWidth; /* Width of data lines */ char *geoSpec = ""; /* Geometry specification */ int numFiles = 0; /* Number of input files */ char *inFileNames[MAXSETS]; /* File names */ char *Odevice = ""; /* Output device */ char *Odisp = "To Device"; /* Output disposition */ char *OfileDev = ""; /* Output file or device */ int debugFlag = 0; /* Whether debugging is on */ /* Possible user specified bounding box */ static double UsrLX, UsrLY, UsrRX, UsrRY; /* Total number of active windows */ static int Num_Windows = 0; static char *Prog_Name; /* * Marker bitmaps */ #include "dot.11" #include "mark1.11" #include "mark2.11" #include "mark3.11" #include "mark4.11" #include "mark5.11" #include "mark6.11" #include "mark7.11" #include "mark8.11" /* Sizes exported for marker drawing */ unsigned int dot_w = dot_width; unsigned int dot_h = dot_height; unsigned int mark_w = mark1_width; unsigned int mark_h = mark1_height; int mark_cx = mark1_x_hot; /* Contrary to what lint says, these are used in xgX.c */ int mark_cy = mark1_y_hot; Pixmap dotMap = (Pixmap) 0; static int XErrHandler(); /* Handles error messages */ main(argc, argv) int argc; char *argv[]; /* * This sets up the hard-wired defaults and reads the X defaults. * The command line format is: xgraph [host:display]. */ { Window primary, NewWindow(); XEvent theEvent; LocalWin *win_info; Cursor zoomCursor; FILE *strm; XColor fg_color, bg_color; char keys[MAXKEYS]; int nbytes, idx, maxitems; /* Open up new display */ Prog_Name = argv[0]; disp = ux11_open_display(argc, argv); XSetErrorHandler(XErrHandler); /* Set up hard-wired defaults and allocate spaces */ InitSets(); /* Read X defaults and override hard-coded defaults */ ReadDefaults(); /* Parse the argument list */ ParseArgs(argc, argv); /* Read the data into the data sets */ llx = lly = MAXFLOAT; urx = ury = -MAXFLOAT; for (idx = 0; idx < numFiles; idx++) { strm = fopen(inFileNames[idx], "r"); if (!strm) { (void) fprintf(stderr, "Warning: cannot open file `%s'\n", inFileNames[idx]); } else { if ((maxitems = ReadData(strm, inFileNames[idx])) < 0) { (void) fprintf(stderr, "data formatting error\n"); exit(1); } (void) fclose(strm); } } if (!numFiles) { if ((maxitems = ReadData(stdin, (char *) 0)) < 0) { (void) fprintf(stderr, "data formatting error\n"); exit(1); } } Xsegs = (XSegment *) malloc((unsigned) (maxitems * sizeof(XSegment))); /* Nasty hack here for bar graphs */ if (barFlag) { llx -= barWidth; urx += barWidth; } /* Create initial window */ #ifdef TOOLBOX xtb_init(disp, screen, normPixel, bgPixel, axisFont); #endif primary = NewWindow(Prog_Name, UsrLX, UsrLY, UsrRX, UsrRY, 1.0); if (!primary) { (void) fprintf(stderr, "Main window would not open\n"); exit(1); } zoomCursor = XCreateFontCursor(disp, XC_sizing); fg_color.pixel = normPixel; XQueryColor(disp, cmap, &fg_color); bg_color.pixel = bgPixel; XQueryColor(disp, cmap, &bg_color); XRecolorCursor(disp, zoomCursor, &fg_color, &bg_color); Num_Windows = 1; while (Num_Windows > 0) { XNextEvent(disp, &theEvent); #ifdef TOOLBOX if (xtb_dispatch(&theEvent) != XTB_NOTDEF) continue; #endif if (XFindContext(theEvent.xany.display, theEvent.xany.window, win_context, (caddr_t *) &win_info)) { /* Nothing found */ continue; } switch (theEvent.type) { case Expose: #ifdef PEXPOSE printf("expose: "); if (theEvent.xexpose.send_event) { printf(" sendevent"); } printf(" win=0x%x x=%d y=%d width=%d height=%d count=%d\n", theEvent.xexpose.window, theEvent.xexpose.x, theEvent.xexpose.y, theEvent.xexpose.width, theEvent.xexpose.height, theEvent.xexpose.count); #endif if (theEvent.xexpose.count <= 0) { XWindowAttributes win_attr; XGetWindowAttributes(disp, theEvent.xany.window, &win_attr); win_info->dev_info.area_w = win_attr.width; win_info->dev_info.area_h = win_attr.height; init_X(win_info->dev_info.user_state); DrawWindow(win_info); } break; case KeyPress: nbytes = XLookupString(&theEvent.xkey, keys, MAXKEYS, (KeySym *) 0, (XComposeStatus *) 0); for (idx = 0; idx < nbytes; idx++) { if (keys[idx] == CONTROL_D) { /* Delete this window */ DelWindow(theEvent.xkey.window, win_info); } else if (keys[idx] == CONTROL_C) { /* Exit program */ Num_Windows = 0; } else if (keys[idx] == 'h') { #ifdef TOOLBOX PrintWindow(theEvent.xany.window, win_info); #endif } } break; case ButtonPress: /* Handle creating a new window */ #ifdef ZOOM Num_Windows += HandleZoom(Prog_Name, &theEvent.xbutton, win_info, zoomCursor); #endif break; default: (void) fprintf(stderr, "Unknown event type: %x\n", theEvent.type); break; } } exit(0); } #ifdef TOOLBOX /* * Button handling functions */ /*ARGSUSED*/ xtb_hret del_func(win, bval, info) Window win; /* Button window */ int bval; /* Button value */ char *info; /* User information */ /* * This routine is called when the `Close' button is pressed in * an xgraph window. It causes the window to go away. */ { Window the_win = (Window) info; LocalWin *win_info; xtb_bt_set(win, 1, (char *) 0); if (!XFindContext(disp, the_win, win_context, (caddr_t *) &win_info)) { DelWindow(the_win, win_info); } return XTB_HANDLED; } /*ARGSUSED*/ xtb_hret hcpy_func(win, bval, info) Window win; /* Button Window */ int bval; /* Button value */ char *info; /* User Information */ /* * This routine is called when the hardcopy button is pressed * in an xgraph window. It causes the output dialog to be * posted. */ { Window the_win = (Window) info; LocalWin *win_info; xtb_bt_set(win, 1, (char *) 0); if (!XFindContext(disp, the_win, win_context, (caddr_t *) &win_info)) { PrintWindow(the_win, win_info); } xtb_bt_set(win, 0, (char *) 0); return XTB_HANDLED; } #endif #define NORMSIZE 600 #define MINDIM 100 Window NewWindow(progname, lowX, lowY, upX, upY, asp) char *progname; /* Name of program */ double lowX, lowY; /* Lower left corner */ double upX, upY; /* Upper right corner */ double asp; /* Aspect ratio */ /* * Creates and maps a new window. This includes allocating its * local structure and associating it with the XId for the window. * The aspect ratio is specified as the ratio of width over height. */ { Window new_window; LocalWin *new_info; static Cursor theCursor = (Cursor) 0; XSizeHints sizehints; XSetWindowAttributes wattr; XWMHints wmhints; XColor fg_color, bg_color; int geo_mask; int width, height; unsigned long wamask; char defSpec[120]; double pad; new_info = (LocalWin *) malloc(sizeof(LocalWin)); if (upX > lowX) { new_info->loX = lowX; new_info->hiX = upX; } else { new_info->loX = llx; new_info->hiX = urx; } if (upY > lowY) { new_info->loY = lowY; new_info->hiY = upY; } else { new_info->loY = lly; new_info->hiY = ury; } /* Increase the padding for aesthetics */ if (new_info->hiX - new_info->loX == 0.0) { pad = MAX(0.5, fabs(new_info->hiX/2.0)); new_info->hiX += pad; new_info->loX -= pad; } if (new_info->hiY - new_info->loY == 0) { pad = MAX(0.5, fabs(ury/2.0)); new_info->hiY += pad; new_info->loY -= pad; } /* Add 10% padding to bounding box (div by 20 yeilds 5%) */ pad = (new_info->hiX - new_info->loX) / 20.0; new_info->loX -= pad; new_info->hiX += pad; pad = (new_info->hiY - new_info->loY) / 20.0; new_info->loY -= pad; new_info->hiY += pad; /* Aspect ratio computation */ if (asp < 1.0) { height = NORMSIZE; width = ((int) (((double) NORMSIZE) * asp)); } else { width = NORMSIZE; height = ((int) (((double) NORMSIZE) / asp)); } height = MAX(MINDIM, height); width = MAX(MINDIM, width); (void) sprintf(defSpec, "%dx%d+100+100", width, height); wamask = ux11_fill_wattr(&wattr, CWBackPixel, bgPixel, CWBorderPixel, bdrPixel, CWColormap, cmap, UX11_END); sizehints.flags = PPosition|PSize; sizehints.x = sizehints.y = 100; sizehints.width = width; sizehints.height = height; new_window = XCreateWindow(disp, RootWindow(disp, screen), sizehints.x, sizehints.y, (unsigned int) sizehints.width, (unsigned int) sizehints.height, (unsigned int) bdrSize, depth, InputOutput, vis, wamask, &wattr); if (new_window) { xtb_frame cl_frame, hd_frame; XStoreName(disp, new_window, progname); XSetIconName(disp, new_window, progname); wmhints.flags = InputHint | StateHint; wmhints.input = True; wmhints.initial_state = NormalState; XSetWMHints(disp, new_window, &wmhints); geo_mask = XParseGeometry(geoSpec, &sizehints.x, &sizehints.y, (unsigned int *) &sizehints.width, (unsigned int *) &sizehints.height); if (geo_mask & (XValue | YValue)) { sizehints.flags = (sizehints.flags & ~PPosition) | USPosition; } if (geo_mask & (WidthValue | HeightValue)) { sizehints.flags = (sizehints.flags & ~PSize) | USSize; } XSetNormalHints(disp, new_window, &sizehints); /* Set device info */ set_X(new_window, &(new_info->dev_info)); #ifdef TOOLBOX /* Make buttons */ xtb_bt_new(new_window, "Close", del_func, (xtb_data) new_window, &cl_frame); new_info->close = cl_frame.win; XMoveWindow(disp, new_info->close, (int) BTNPAD, (int) BTNPAD); xtb_bt_new(new_window, "Hardcopy", hcpy_func, (xtb_data) new_window, &hd_frame); new_info->hardcopy = hd_frame.win; XMoveWindow(disp, new_info->hardcopy, (int) (BTNPAD + cl_frame.width + BTNINTER), BTNPAD); #endif XSelectInput(disp, new_window, ExposureMask|KeyPressMask|ButtonPressMask); if (!theCursor) { theCursor = XCreateFontCursor(disp, XC_top_left_arrow); fg_color.pixel = normPixel; XQueryColor(disp, cmap, &fg_color); bg_color.pixel = bgPixel; XQueryColor(disp, cmap, &bg_color); XRecolorCursor(disp, theCursor, &fg_color, &bg_color); } XDefineCursor(disp, new_window, theCursor); if (!win_context) { win_context = XUniqueContext(); } XSaveContext(disp, new_window, win_context, (caddr_t) new_info); XMapWindow(disp, new_window); return new_window; } else { return (Window) 0; } } DelWindow(win, win_info) Window win; /* Window */ LocalWin *win_info; /* Local Info */ /* * This routine actually deletes the specified window and * decrements the window count. */ { xtb_data info; XDeleteContext(disp, win, win_context); #ifdef TOOLBOX xtb_bt_del(win_info->close, &info); xtb_bt_del(win_info->hardcopy, &info); #endif free((char *) win_info); XDestroyWindow(disp, win); Num_Windows -= 1; } #ifdef TOOLBOX PrintWindow(win, win_info) Window win; /* Window */ LocalWin *win_info; /* Local Info */ /* * This routine posts a dialog asking about the hardcopy * options desired. If the user hits `OK', the hard * copy is performed. */ { ho_dialog(win, Prog_Name, (char *) win_info); } #endif #ifdef ZOOM static XRectangle boxEcho; static GC echoGC = (GC) 0; #define DRAWBOX \ if (startX < curX) { \ boxEcho.x = startX; \ boxEcho.width = curX - startX; \ } else { \ boxEcho.x = curX; \ boxEcho.width = startX - curX; \ } \ if (startY < curY) { \ boxEcho.y = startY; \ boxEcho.height = curY - startY; \ } else { \ boxEcho.y = curY; \ boxEcho.height = startY - curY; \ } \ XDrawRectangles(disp, win, echoGC, &boxEcho, 1); #endif #define TRANX(xval) \ (((double) ((xval) - wi->XOrgX)) * wi->XUnitsPerPixel + wi->UsrOrgX) #define TRANY(yval) \ (wi->UsrOppY - (((double) ((yval) - wi->XOrgY)) * wi->YUnitsPerPixel)) #ifdef ZOOM int HandleZoom(progname, evt, wi, cur) char *progname; XButtonPressedEvent *evt; LocalWin *wi; Cursor cur; { Window win, new_win; Window root_rtn, child_rtn; XEvent theEvent; int startX, startY, curX, curY, newX, newY, stopFlag, numwin; int root_x, root_y; unsigned int mask_rtn; double loX, loY, hiX, hiY, asp; win = evt->window; if (XGrabPointer(disp, win, True, (unsigned int) (ButtonPressMask|ButtonReleaseMask| PointerMotionMask|PointerMotionHintMask), GrabModeAsync, GrabModeAsync, win, cur, CurrentTime) != GrabSuccess) { XBell(disp, 0); return 0; } if (echoGC == (GC) 0) { unsigned long gcmask; XGCValues gcvals; gcmask = ux11_fill_gcvals(&gcvals, GCForeground, zeroPixel ^ bgPixel, GCFunction, GXxor, UX11_END); echoGC = XCreateGC(disp, win, gcmask, &gcvals); } startX = evt->x; startY = evt->y; XQueryPointer(disp, win, &root_rtn, &child_rtn, &root_x, &root_y, &curX, &curY, &mask_rtn); /* Draw first box */ DRAWBOX; stopFlag = 0; while (!stopFlag) { XNextEvent(disp, &theEvent); switch (theEvent.xany.type) { case MotionNotify: XQueryPointer(disp, win, &root_rtn, &child_rtn, &root_x, &root_y, &newX, &newY, &mask_rtn); /* Undraw the old one */ DRAWBOX; /* Draw the new one */ curX = newX; curY = newY; DRAWBOX; break; case ButtonRelease: DRAWBOX; XUngrabPointer(disp, CurrentTime); stopFlag = 1; if ((startX-curX != 0) && (startY-curY != 0)) { /* Figure out relative bounding box */ loX = TRANX(startX); loY = TRANY(startY); hiX = TRANX(curX); hiY = TRANY(curY); if (loX > hiX) { double temp; temp = hiX; hiX = loX; loX = temp; } if (loY > hiY) { double temp; temp = hiY; hiY = loY; loY = temp; } /* physical aspect ratio */ asp = ((double) ABS(startX-curX))/((double) ABS(startY-curY)); new_win = NewWindow(progname, loX, loY, hiX, hiY, asp); if (new_win) { numwin = 1; } else { numwin = 0; } } else { numwin = 0; } break; default: printf("unknown event: %d\n", theEvent.xany.type); break; } } return numwin; } #endif unsigned long GetColor(name) char *name; /* * Given a standard color name, this routine fetches the associated * pixel value from the global `cmap'. The name may be specified * using the standard specification format parsed by XParseColor. * Some sort of parsing error causes the program to exit. */ { XColor def; if (XParseColor(disp, cmap, name, &def)) { if (XAllocColor(disp, cmap, &def)) { return def.pixel; } else { (void) fprintf(stderr, "xgraph: could not allocate color: `%s'\n", name); exit(1); } } else { (void) fprintf(stderr, "xgraph: cannot parse color specification: `%s'\n", name); exit(1); } return 0; } /* Default line styles */ static char *defStyle[MAXATTR] = { "11", "44", "1142", "31", "88", "113111", "2231", "224" }; /* Default color names */ static char *defColors[MAXATTR] = { "red", "SpringGreen", "blue", "yellow", "cyan", "plum", "orange", "coral" }; int InitSets() /* * Initializes the data sets with default information. */ { int idx; char setname[40]; Window temp_win; XSetWindowAttributes wattr; char *font_name; if (ux11_std_vismap(disp, &vis, &cmap, &screen, &depth) == UX11_ALTERNATE) { install_flag = 1; } else { install_flag = 0; } black_pixel = GetColor("black"); white_pixel = GetColor("white"); /* Create a temporary window for creating graphics contexts */ temp_win = XCreateWindow(disp, RootWindow(disp, screen), 0, 0, 10, 10, 0, depth, InputOutput, vis, (unsigned long) 0, &wattr); bwFlag = (depth < 4); bdrSize = 2; bdrPixel = black_pixel; (void) strcpy(titleText, "X Graph"); (void) strcpy(XUnits, "X"); (void) strcpy(YUnits, "Y"); tickFlag = 0; markFlag = 0; pixelMarks = 0; bigPixel = 0; colorMark = 0; bbFlag = 0; noLines = 0; logXFlag = 0; logYFlag = 0; barFlag = 0; barBase = 0.0; barWidth = -1.0; /* Holder */ lineWidth = 1; echoPix = BIGINT; /* Set the user bounding box */ UsrLX = UsrLY = MAXFLOAT; UsrRX = UsrRY = -MAXFLOAT; /* Depends critically on whether the display has color */ if (bwFlag) { /* Its black and white */ bgPixel = white_pixel; zeroPixel = black_pixel; zeroWidth = 3; normPixel = black_pixel; /* Initialize set defaults */ for (idx = 0; idx < MAXATTR; idx++) { /* Needs work! */ AllAttrs[idx].lineStyleLen = ProcessStyle(defStyle[idx], AllAttrs[idx].lineStyle, MAXLS); AllAttrs[idx].pixelValue = black_pixel; } } else { /* Its color */ bgPixel = GetColor("LightGray"); zeroPixel = white_pixel; zeroWidth = 1; normPixel = black_pixel; /* Initalize attribute colors defaults */ AllAttrs[0].lineStyle[0] = '\0'; AllAttrs[0].lineStyleLen = 0; AllAttrs[0].pixelValue = GetColor(defColors[0]); for (idx = 1; idx < MAXATTR; idx++) { AllAttrs[idx].lineStyleLen = ProcessStyle(defStyle[idx-1], AllAttrs[idx].lineStyle, MAXLS); AllAttrs[idx].pixelValue = GetColor(defColors[idx]); } } if (!ux11_size_font(disp, screen, 4000, &axisFont, &font_name)) { fprintf(stderr, "Can't find an appropriate axis font\n"); abort(); } if (!ux11_size_font(disp, screen, 6000, &titleFont, &font_name)) { fprintf(stderr, "Can't find an appropriate title font\n"); abort(); } /* Initialize the data sets */ for (idx = 0; idx < MAXSETS; idx++) { (void) sprintf(setname, "Set %d", idx); AllSets[idx].setName = STRDUP(setname); AllSets[idx].numPoints = 0; AllSets[idx].allocSize = 0; AllSets[idx].xvec = AllSets[idx].yvec = (double *) 0; } /* Store bitmaps for dots and markers */ dotMap = XCreateBitmapFromData(disp, temp_win, dot_bits, dot_w, dot_h); AllAttrs[0].markStyle = XCreateBitmapFromData(disp, temp_win, mark1_bits, mark_w, mark_h); AllAttrs[1].markStyle = XCreateBitmapFromData(disp, temp_win, mark2_bits, mark_w, mark_h); AllAttrs[2].markStyle = XCreateBitmapFromData(disp, temp_win, mark3_bits, mark_w, mark_h); AllAttrs[3].markStyle = XCreateBitmapFromData(disp, temp_win, mark4_bits, mark_w, mark_h); AllAttrs[4].markStyle = XCreateBitmapFromData(disp, temp_win, mark5_bits, mark_w, mark_h); AllAttrs[5].markStyle = XCreateBitmapFromData(disp, temp_win, mark6_bits, mark_w, mark_h); AllAttrs[6].markStyle = XCreateBitmapFromData(disp, temp_win, mark7_bits, mark_w, mark_h); AllAttrs[7].markStyle = XCreateBitmapFromData(disp, temp_win, mark8_bits, mark_w, mark_h); XDestroyWindow(disp, temp_win); } static char *def_str; static Pixel def_pixel; static int def_int; static XFontStruct *def_font; static double def_dbl; int rd_pix(name) char *name; /* Result in def_pixel */ { if (def_str = XGetDefault(disp, Prog_Name, name)) { def_pixel = GetColor(def_str); if (debugFlag) printf("%s (pixel) = %s\n", name, def_str); return 1; } else { return 0; } } int rd_int(name) char *name; /* Result in def_int */ { if (def_str = XGetDefault(disp, Prog_Name, name)) { if (sscanf(def_str, "%ld", &def_int) == 1) { if (debugFlag) printf("%s (int) = %s\n", name, def_str); return 1; } else { fprintf(stderr, "warning: could not read integer value for %s\n", name); return 0; } } else { return 0; } } int rd_str(name) char *name; /* Result in def_str */ { if (def_str = XGetDefault(disp, Prog_Name, name)) { if (debugFlag) printf("%s (str) = %s\n", name, def_str); return 1; } else { return 0; } } int rd_font(name) char *name; /* Result in def_font */ { if (def_str = XGetDefault(disp, Prog_Name, name)) { if (def_font = XLoadQueryFont(disp, def_str)) { if (debugFlag) printf("%s (font) = %s\n", name, def_str); return 1; } else { fprintf(stderr, "warning: could not load font for %s\n", name); return 0; } } else { return 0; } } int rd_flag(name) char *name; /* Result in def_int */ { if (def_str = XGetDefault(disp, Prog_Name, name)) { def_int = (stricmp(def_str, "on") == 0) || (stricmp(def_str, "1") == 0); if (debugFlag) printf("%s (flag) = %s\n", name, def_str); return 1; } else { return 0; } } int rd_dbl(name) char *name; /* Result in def_dbl */ { if (def_str = XGetDefault(disp, Prog_Name, name)) { if (sscanf(def_str, "%lg", &def_dbl) == 1) { if (debugFlag) printf("%s (dbl) = %s\n", name, def_str); return 1; } else { fprintf(stderr, "warning: could not read value of %s\n", name); return 0; } } else { return 0; } } int ReversePixel(pixValue) Pixel *pixValue; /* Pixel value to reverse */ { if (*pixValue == white_pixel) *pixValue = black_pixel; else if (*pixValue == black_pixel) *pixValue = white_pixel; } int ProcessStyle(style, buf, maxbuf) char *style; /* Textual line style spec */ char *buf; /* Returned buf */ int maxbuf; /* Maximum size of buffer */ /* * Translates a textual specification for a line style into * an appropriate dash list for X11. Returns the length. */ { int len, i; strncpy(buf, style, maxbuf-1); buf[maxbuf-1] = '\0'; len = strlen(buf); for (i = 0; i < len; i++) { if ((buf[i] >= '0') && (buf[i] <= '9')) { buf[i] = buf[i] - '0'; } else if ((buf[i] >= 'a') && (buf[i] <= 'f')) { buf[i] = buf[i] - 'a' + 10; } else if ((buf[i] >= 'A') && (buf[i] <= 'F')) { buf[i] = buf[i] - 'A' + 10; } else { return 0; } } return len; } int ReadDefaults() /* * Reads X default values which override the hard-coded defaults * set up by InitSets. */ { char newname[100]; int idx; if (rd_pix("Background")) { bgPixel = def_pixel; } if (rd_int("BorderSize")) { bdrSize = def_int; } if (rd_pix("Border")) { bdrPixel = def_pixel; } if (rd_int("GridSize")) { axisWidth = def_int; } if (rd_str("GridStyle")) { axisLSLen = ProcessStyle(def_str, axisLS, MAXLS); } if (rd_pix("Foreground")) { normPixel = def_pixel; } if (rd_pix("ZeroColor")) zeroPixel = def_pixel; if (rd_str("ZeroStyle")) { zeroLSLen = ProcessStyle(def_str, zeroLS, MAXLS); } if (rd_int("ZeroSize")) zeroWidth = def_int; if (rd_font("LabelFont")) { axisFont = def_font; } if (rd_font("TitleFont")) { titleFont = def_font; } if (rd_flag("Ticks")) tickFlag = def_int; if (rd_str("Device")) Odevice = def_str; if (rd_str("Disposition")) Odisp = def_str; if (rd_str("FileOrDev")) OfileDev = def_str; /* Read device specific parameters */ for (idx = 0; idx < hard_count; idx++) { sprintf(newname, "%s.Dimension", hard_devices[idx].dev_name); if (rd_dbl(newname)) hard_devices[idx].dev_max_dim = def_dbl; sprintf(newname, "%s.OutputTitleFont", hard_devices[idx].dev_name); if (rd_str(newname)) { (void) strncpy(hard_devices[idx].dev_title_font, def_str, MFNAME-1); } sprintf(newname, "%s.OutputTitleSize", hard_devices[idx].dev_name); if (rd_dbl(newname)) hard_devices[idx].dev_title_size = def_dbl; sprintf(newname, "%s.OutputAxisFont", hard_devices[idx].dev_name); if (rd_str(newname)) { (void) strncpy(hard_devices[idx].dev_axis_font, def_str, MFNAME-1); } sprintf(newname, "%s.OutputAxisSize", hard_devices[idx].dev_name); if (rd_dbl(newname)) hard_devices[idx].dev_axis_size = def_dbl; } if (rd_flag("SmallPixels")) { if (def_int) { noLines = markFlag = 1; pixelMarks = 1; bigPixel = 0; } } if (rd_flag("LargePixels")) { if (def_int) { markFlag = 1; pixelMarks = 1; bigPixel = 1; } } if (rd_flag("Markers")) { if (def_int) { markFlag = 1; pixelMarks = 0; colorMark = 0; } } if (rd_flag("StyleMarkers")) { if (def_int) { markFlag = 1; pixelMarks = 0; colorMark = 1; } } if (rd_flag("BoundBox")) bbFlag = def_int; if (rd_flag("NoLines")) noLines = def_int; if (rd_flag("PixelMarkers")) pixelMarks = def_int; if (rd_int("LineWidth")) lineWidth = def_int; /* Read the default line and color attributes */ for (idx = 0; idx < MAXATTR; idx++) { (void) sprintf(newname, "%d.Style", idx); if (rd_str(newname)) { AllAttrs[idx].lineStyleLen = ProcessStyle(def_str, AllAttrs[idx].lineStyle, MAXLS); } (void) sprintf(newname, "%d.Color", idx); if (rd_pix(newname)) { AllAttrs[idx].pixelValue = def_pixel; } } if (bwFlag) { /* Black and white - check for reverse video */ if (rd_flag("ReverseVideo")) { if (def_int) { ReversePixel(&bgPixel); ReversePixel(&bdrPixel); ReversePixel(&zeroPixel); ReversePixel(&normPixel); for (idx = 0; idx < MAXATTR; idx++) { ReversePixel(&(AllAttrs[idx].pixelValue)); } } } } } int argerror(err, val) char *err, *val; { (void) fprintf(stderr, "Error: %s: %s\n\n", val, err); (void) fprintf(stderr, "format: xgraph [-bd color] [-bg color] [-fg color] [-zg color]\n"); (void) fprintf(stderr, " [-bw bdr_width] [-lf label_font] [-tf title_font]\n"); (void) fprintf(stderr, " [-<digit> set_name] [-t title] [-x unitname] [-y unitname]\n"); (void) fprintf(stderr, " [-lx x1,x2] [-ly y1,y2] [-bar] [-brb base] [-brw width]\n"); (void) fprintf(stderr, " [-bb] [-db] [-lnx] [-lny] [-nl] [-m] [-M] [-p] [-P] [-rv]\n"); (void) fprintf(stderr, " [-tk] [-lw linewidth] [-display host:display.screen] [=geospec]\n"); (void) fprintf(stderr, "\nFonts must be fixed width\n"); (void) fprintf(stderr, "-bar Draw bar graph with base -brb and width -brw\n"); (void) fprintf(stderr, "-bb Draws bounding box around data\n"); (void) fprintf(stderr, "-db Turns on debugging mode\n"); (void) fprintf(stderr, "-lnx Logarithmic scale for X axis\n"); (void) fprintf(stderr, "-lny Logarithmic scale for Y axis\n"); (void) fprintf(stderr, "-nl Don't draw lines (scatter plot)\n"); (void) fprintf(stderr, "-m -M Mark points distinctively (M varies with color)\n"); (void) fprintf(stderr, "-p -P Mark points with dot (P means big dot)\n"); (void) fprintf(stderr, "-rv Reverse video on black and white displays\n"); (void) fprintf(stderr, "-tk Draw tick marks instead of full grid\n"); exit(1); } int ParseArgs(argc, argv) int argc; char *argv[]; /* * This routine parses the argument list for xgraph. There are too * many to mention here so I won't. */ { int idx, set, tempPixel; XFontStruct *tempFont; idx = 1; while (idx < argc) { if (argv[idx][0] == '-') { /* Check to see if its a data set name */ if (sscanf(argv[idx], "-%d", &set) == 1) { /* The next string is a set name */ if (idx+1 >= argc) argerror("missing set name", argv[idx]); AllSets[set].setName = argv[idx+1]; idx += 2; } else { /* Some non-dataset option */ if (strcmp(argv[idx], "-x") == 0) { /* Units for X axis */ if (idx+1 >= argc) argerror("missing axis name", argv[idx]); (void) strcpy(XUnits, argv[idx+1]); idx += 2; } else if (strcmp(argv[idx], "-y") == 0) { /* Units for Y axis */ if (idx+1 >= argc) argerror("missing axis name", argv[idx]); (void) strcpy(YUnits, argv[idx+1]); idx += 2; } else if (strcmp(argv[idx], "-t") == 0) { /* Title of plot */ if (idx+1 >= argc) argerror("missing plot title", argv[idx]); (void) strcpy(titleText, argv[idx+1]); idx += 2; } else if (strcmp(argv[idx], "-fg") == 0) { /* Foreground color */ if (idx+1 >= argc) argerror("missing color", argv[idx]); tempPixel = GetColor(argv[idx+1]); normPixel = tempPixel; idx += 2; } else if (strcmp(argv[idx], "-bg") == 0) { /* Background color */ if (idx+1 >= argc) argerror("missing color", argv[idx]); tempPixel = GetColor(argv[idx+1]); bgPixel = tempPixel; idx += 2; } else if (strcmp(argv[idx], "-bd") == 0) { /* Border color */ if (idx+1 >= argc) argerror("missing color", argv[idx]); tempPixel = GetColor(argv[idx+1]); bdrPixel = tempPixel; idx += 2; } else if (strcmp(argv[idx], "-bw") == 0) { /* Border width */ if (idx+1 >= argc) argerror("missing border size", argv[idx]); bdrSize = atoi(argv[idx+1]); idx += 2; } else if (strcmp(argv[idx], "-zg") == 0) { /* Zero grid color */ if (idx+1 >= argc) argerror("missing color", argv[idx]); tempPixel = GetColor(argv[idx+1]); zeroPixel = tempPixel; idx += 2; } else if (strcmp(argv[idx], "-tf") == 0) { /* Title Font */ if (idx+1 >= argc) argerror("missing font", argv[idx]); tempFont = XLoadQueryFont(disp, argv[idx+1]); if (!tempFont) argerror("can't get font", argv[idx+1]); titleFont = tempFont; idx += 2; } else if (strcmp(argv[idx], "-lf") == 0) { /* Label Font */ if (idx+1 >= argc) argerror("missing font", argv[idx]); tempFont = XLoadQueryFont(disp, argv[idx+1]); if (!tempFont) argerror("can't get font", argv[idx+1]); axisFont = tempFont; idx += 2; } else if (strcmp(argv[idx], "-rv") == 0) { /* Reverse video option */ ReversePixel(&bgPixel); ReversePixel(&bdrPixel); ReversePixel(&zeroPixel); ReversePixel(&normPixel); for (set = 0; set < MAXATTR; set++) { ReversePixel(&(AllAttrs[set].pixelValue)); } idx++; } else if (strcmp(argv[idx], "-tk") == 0) { /* Draw tick marks instead of full grid */ tickFlag = 1; idx++; } else if (strcmp(argv[idx], "-bb") == 0) { /* Draw bounding box around graph region */ bbFlag = 1; idx++; } else if (strcmp(argv[idx], "-lx") == 0) { /* Limit the X coordinates */ if (idx+1 >= argc) argerror("missing coordinate(s)", argv[idx]); (void) sscanf(argv[idx+1], "%lf,%lf", &UsrLX, &UsrRX); idx += 2; } else if (strcmp(argv[idx], "-ly") == 0) { /* Limit the Y coordinates */ if (idx+1 >= argc) argerror("missing coordinate(s)", argv[idx]); (void) sscanf(argv[idx+1], "%lf,%lf", &UsrLY, &UsrRY); idx += 2; } else if (strcmp(argv[idx], "-lw") == 0) { /* Set the line width */ if (idx+1 >= argc) argerror("missing line width", argv[idx]); lineWidth = atoi(argv[idx+1]); idx += 2; } else if (strcmp(argv[idx], "-nl") == 0) { noLines = 1; idx++; } else if (strcmp(argv[idx], "-m") == 0) { /* * Mark each point with a individual marker * varying with linestyle */ markFlag = 1; pixelMarks = colorMark = 0; idx++; } else if (strcmp(argv[idx], "-M") == 0) { /* * Mark each point with an individual marker * varying with color. */ markFlag = colorMark = 1; pixelMarks = 0; idx++; } else if (strcmp(argv[idx], "-p") == 0) { /* Draw small pixel sized markers */ noLines = markFlag = pixelMarks = 1; bigPixel = 0; idx++; } else if (strcmp(argv[idx], "-P") == 0) { /* Draw large pixel sized markers */ markFlag = pixelMarks = bigPixel = 1; idx++; } else if (strcmp(argv[idx], "-lnx") == 0) { /* The X axis is logarithmic */ logXFlag = 1; idx++; } else if (strcmp(argv[idx], "-lny") == 0) { /* The Y axis is logarithmic */ logYFlag = 1; idx++; } else if (strcmp(argv[idx], "-bar") == 0) { /* Draw bar graph */ barFlag = 1; idx++; } else if (strcmp(argv[idx], "-brw") == 0) { /* Set width of bar */ if (idx+1 >= argc) argerror("missing width", argv[idx]); (void) sscanf(argv[idx+1], "%lf", &barWidth); idx += 2; } else if (strcmp(argv[idx], "-brb") == 0) { if (idx+1 >= argc) argerror("missing base", argv[idx]); (void) sscanf(argv[idx+1], "%lf", &barBase); idx += 2; } else if (strcmp(argv[idx], "-db") == 0) { /* Debug on */ debugFlag = 1; XSynchronize(disp, 1); XFlush(disp); idx++; } else if (strcmp(argv[idx], "-display") == 0) { /* Harmless display specification */ idx += 2; } else { argerror("unknown option", argv[idx]); } } } else if (argv[idx][0] == '=') { /* Its a geometry specification */ geoSpec = &(argv[idx][1]); idx++; } else { /* It might be the host:display string */ if (rindex(argv[idx], ':') == (char *) 0) { /* Should be an input file */ inFileNames[numFiles] = argv[idx]; numFiles++; } idx++; } } } /* For reading in the data */ static int setNumber = 0; static maxSize = 0; int ReadData(stream, filename) FILE *stream; char *filename; /* * Reads in the data sets from the supplied stream. If the format * is correct, it returns the current maximum number of points across * all data sets. If there is an error, it returns -1. */ { char buffer[MAXBUFSIZE]; char setname[40]; int spot = 0; int line_count = 0; /* Set name of set to file name if appropriate */ (void) sprintf(setname, "Set %d", setNumber); if ((strcmp(AllSets[setNumber].setName, setname) == 0) && filename) { AllSets[setNumber].setName = filename; } /* Eliminate over-zealous set increments */ if (setNumber > 0) { if (AllSets[setNumber-1].numPoints == 0) { setNumber--; return ReadData(stream, filename); } } if (!filename) filename = "stdin"; while (setNumber < MAXSETS) { if (!fgets(buffer, MAXBUFSIZE, stream)) break; line_count++; if (buffer[0] == '#') continue; if (buffer[0] == '\n') { /* Empty line - increment data set */ setNumber++; if (spot > maxSize) maxSize = spot; spot = 0; } else if (buffer[0] == '"') { buffer[strlen(buffer)-1] = '\0'; AllSets[setNumber].setName = STRDUP(&(buffer[1])); } else { if (spot >= AllSets[setNumber].allocSize) { /* Time to make the arrays bigger (or initialize them) */ if (AllSets[setNumber].allocSize == 0) { AllSets[setNumber].allocSize = INITSIZE; AllSets[setNumber].xvec = (double *) malloc((unsigned) (INITSIZE * sizeof(double))); AllSets[setNumber].yvec = (double *) malloc((unsigned) (INITSIZE * sizeof(double))); } else { AllSets[setNumber].allocSize *= 2; AllSets[setNumber].xvec = (double *) realloc((char *) AllSets[setNumber].xvec, (unsigned) (AllSets[setNumber].allocSize * sizeof(double))); AllSets[setNumber].yvec = (double *) realloc((char *) AllSets[setNumber].yvec, (unsigned) (AllSets[setNumber].allocSize * sizeof(double))); } } if (sscanf(buffer, "%F %F", &(AllSets[setNumber].xvec[spot]), &(AllSets[setNumber].yvec[spot])) != 2) { (void) fprintf(stderr, "file: `%s', line: %d\n", filename, line_count); (void) fprintf(stderr, "Exactly two coordinates per line\n"); return -1; } if (logXFlag) { if (AllSets[setNumber].xvec[spot] <= 0.0) { (void) fprintf(stderr, "file: `%s', line: %d\n", filename, line_count); (void) fprintf(stderr, "Non-positive X value in logarithmic mode\n"); return -1; } AllSets[setNumber].xvec[spot] = log10(AllSets[setNumber].xvec[spot]); } if (logYFlag) { if (AllSets[setNumber].yvec[spot] <= 0.0) { (void) fprintf(stderr, "file: `%s', line: %d\n", filename, line_count); (void) fprintf(stderr, "Non-positive Y value in logarithmic mode\n"); return -1; } AllSets[setNumber].yvec[spot] = log10(AllSets[setNumber].yvec[spot]); } /* Update bounding box */ if (AllSets[setNumber].xvec[spot] < llx) llx = AllSets[setNumber].xvec[spot]; if (AllSets[setNumber].xvec[spot] > urx) urx = AllSets[setNumber].xvec[spot]; if (AllSets[setNumber].yvec[spot] < lly) lly = AllSets[setNumber].yvec[spot]; if (AllSets[setNumber].yvec[spot] > ury) ury = AllSets[setNumber].yvec[spot]; spot++; AllSets[setNumber].numPoints += 1; } } if (spot > maxSize) maxSize = spot; if (line_count <= 0) { (void) fprintf(stderr, "No data found\n"); return -1; } if (setNumber >= MAXSETS) { (void) fprintf(stderr, "Too many data sets\n"); return -1; } else { setNumber++; return maxSize; } } int DrawWindow(win_info) LocalWin *win_info; /* Window information */ /* * Draws the data in the window. Does not clear the window. * The data is scaled so that all of the data will fit. * Grid lines are drawn at the nearest power of 10 in engineering * notation.. Draws axis numbers along bottom and left hand edges. * Centers title at top of window. */ { /* Figure out the transformation constants */ if (TransformCompute(win_info)) { /* Draw the title */ DrawTitle(win_info); /* Draw the legend */ DrawLegend(win_info); /* Draw the axis unit labels, grid lines, and grid labels */ DrawGridAndAxis(win_info); /* Draw the data sets themselves */ DrawData(win_info); } } DrawTitle(wi) LocalWin *wi; /* Window information */ /* * This routine draws the title of the graph centered in * the window. It is spaced down from the top by an amount * specified by the constant PADDING. The font must be * fixed width. The routine returns the height of the * title in pixels. */ { wi->dev_info.xg_text(wi->dev_info.user_state, wi->dev_info.area_w/2, wi->dev_info.axis_pad, titleText, T_TOP, T_TITLE); } int TransformCompute(wi) LocalWin *wi; /* Window information */ /* * This routine figures out how to draw the axis labels and grid lines. * Both linear and logarithmic axes are supported. Axis labels are * drawn in engineering notation. The power of the axes are labeled * in the normal axis labeling spots. The routine also figures * out the necessary transformation information for the display * of the points (it touches XOrgX, XOrgY, UsrOrgX, UsrOrgY, and * UnitsPerPixel). */ { double bbCenX, bbCenY, bbHalfWidth, bbHalfHeight; int idx, maxName, leftWidth; char err[MAXBUFSIZE]; /* * First, we figure out the origin in the X window. Above * the space we have the title and the Y axis unit label. * To the left of the space we have the Y axis grid labels. */ wi->XOrgX = wi->dev_info.bdr_pad + (7 * wi->dev_info.axis_width) + wi->dev_info.bdr_pad; wi->XOrgY = wi->dev_info.bdr_pad + wi->dev_info.title_height + wi->dev_info.bdr_pad + wi->dev_info.axis_height + wi->dev_info.axis_height/2 + wi->dev_info.bdr_pad; /* * Now we find the lower right corner.. Below the space we * have the X axis grid labels. To the right of the space we * have the X axis unit label and the legend. We assume the * worst case size for the unit label. */ maxName = 0; for (idx = 0; idx < MAXSETS; idx++) { if (AllSets[idx].numPoints > 0) { int tempSize; tempSize = strlen(AllSets[idx].setName); if (tempSize > maxName) maxName = tempSize; } } /* Worst case size of the X axis label: */ leftWidth = (strlen(XUnits)) * wi->dev_info.axis_width; if ((maxName*wi->dev_info.axis_width)+wi->dev_info.bdr_pad > leftWidth) leftWidth = maxName * wi->dev_info.axis_width + wi->dev_info.bdr_pad; wi->XOppX = wi->dev_info.area_w - wi->dev_info.bdr_pad - leftWidth; wi->XOppY = wi->dev_info.area_h - wi->dev_info.bdr_pad - wi->dev_info.axis_height - wi->dev_info.bdr_pad; if ((wi->XOrgX >= wi->XOppX) || (wi->XOrgY >= wi->XOppY)) { #ifdef TOOLBOX do_error(strcpy(err, "Drawing area is too small\n")); #else (void) fprintf(stderr, "Drawing area is too small\n"); #endif return 0; } /* * We now have a bounding box for the drawing region. * Figure out the units per pixel using the data set bounding box. */ wi->XUnitsPerPixel = (wi->hiX - wi->loX)/((double) (wi->XOppX - wi->XOrgX)); wi->YUnitsPerPixel = (wi->hiY - wi->loY)/((double) (wi->XOppY - wi->XOrgY)); /* * Find origin in user coordinate space. We keep the center of * the original bounding box in the same place. */ bbCenX = (wi->loX + wi->hiX) / 2.0; bbCenY = (wi->loY + wi->hiY) / 2.0; bbHalfWidth = ((double) (wi->XOppX - wi->XOrgX))/2.0 * wi->XUnitsPerPixel; bbHalfHeight = ((double) (wi->XOppY - wi->XOrgY))/2.0 * wi->YUnitsPerPixel; wi->UsrOrgX = bbCenX - bbHalfWidth; wi->UsrOrgY = bbCenY - bbHalfHeight; wi->UsrOppX = bbCenX + bbHalfWidth; wi->UsrOppY = bbCenY + bbHalfHeight; /* * Everything is defined so we can now use the SCREENX and SCREENY * transformations. */ return 1; } int DrawGridAndAxis(wi) LocalWin *wi; /* Window information */ /* * This routine draws grid line labels in engineering notation, * the grid lines themselves, and unit labels on the axes. */ { int expX, expY; /* Engineering powers */ int startX; int Yspot, Xspot; char power[10], value[10], final[MAXBUFSIZE+10]; double Xincr, Yincr, Xstart, Ystart, Yindex, Xindex, larger; XSegment segs[2]; double initGrid(), stepGrid(); /* * Grid display powers are computed by taking the log of * the largest numbers and rounding down to the nearest * multiple of 3. */ if (logXFlag) { expX = 0; } else { if (fabs(wi->UsrOrgX) > fabs(wi->UsrOppX)) { larger = fabs(wi->UsrOrgX); } else { larger = fabs(wi->UsrOppX); } expX = ((int) floor(nlog10(larger)/3.0)) * 3; } if (logYFlag) { expY = 0; } else { if (fabs(wi->UsrOrgY) > fabs(wi->UsrOppY)) { larger = fabs(wi->UsrOrgY); } else { larger = fabs(wi->UsrOppY); } expY = ((int) floor(nlog10(larger)/3.0)) * 3; } /* * With the powers computed, we can draw the axis labels. */ if (expY != 0) { (void) strcpy(final, YUnits); (void) strcat(final, " x 10"); Xspot = wi->dev_info.bdr_pad + ((strlen(YUnits)+5) * wi->dev_info.axis_width); Yspot = wi->dev_info.bdr_pad * 2 + wi->dev_info.title_height + wi->dev_info.axis_height/2; wi->dev_info.xg_text(wi->dev_info.user_state, Xspot, Yspot, final, T_RIGHT, T_AXIS); (void) sprintf(power, "%d", expY); wi->dev_info.xg_text(wi->dev_info.user_state, Xspot, Yspot, power, T_LOWERLEFT, T_AXIS); } else { Yspot = wi->dev_info.bdr_pad * 2 + wi->dev_info.title_height; wi->dev_info.xg_text(wi->dev_info.user_state, wi->dev_info.bdr_pad, Yspot, YUnits, T_UPPERLEFT, T_AXIS); } startX = wi->dev_info.area_w - wi->dev_info.bdr_pad; if (expX != 0) { (void) sprintf(power, "%d", expX); startX -= (strlen(power) + 5) * wi->dev_info.axis_width; wi->dev_info.xg_text(wi->dev_info.user_state, startX, wi->XOppY, power, T_LOWERLEFT, T_AXIS); (void) strcpy(final, XUnits); (void) strcat(final, " x 10"); wi->dev_info.xg_text(wi->dev_info.user_state, startX, wi->XOppY, final, T_RIGHT, T_AXIS); } else { wi->dev_info.xg_text(wi->dev_info.user_state, startX, wi->XOppY, XUnits, T_RIGHT, T_AXIS); } /* * First, the grid line labels */ Yincr = (wi->dev_info.axis_pad + wi->dev_info.axis_height) * wi->YUnitsPerPixel; Ystart = initGrid(wi->UsrOrgY, Yincr, logYFlag); for (Yindex = Ystart; Yindex < wi->UsrOppY; Yindex = stepGrid()) { Yspot = SCREENY(wi, Yindex); /* Write the axis label */ WriteValue(value, Yindex, expY, logYFlag); wi->dev_info.xg_text(wi->dev_info.user_state, wi->dev_info.bdr_pad + (7 * wi->dev_info.axis_width), Yspot, value, T_RIGHT, T_AXIS); } Xincr = (wi->dev_info.axis_pad + (wi->dev_info.axis_width * 7)) * wi->XUnitsPerPixel; Xstart = initGrid(wi->UsrOrgX, Xincr, logXFlag); for (Xindex = Xstart; Xindex < wi->UsrOppX; Xindex = stepGrid()) { Xspot = SCREENX(wi, Xindex); /* Write the axis label */ WriteValue(value, Xindex, expX, logXFlag); wi->dev_info.xg_text(wi->dev_info.user_state, Xspot, wi->dev_info.area_h - wi->dev_info.bdr_pad, value, T_BOTTOM, T_AXIS); } /* * Now, the grid lines or tick marks */ Yincr = (wi->dev_info.axis_pad + wi->dev_info.axis_height) * wi->YUnitsPerPixel; Ystart = initGrid(wi->UsrOrgY, Yincr, logYFlag); for (Yindex = Ystart; Yindex < wi->UsrOppY; Yindex = stepGrid()) { Yspot = SCREENY(wi, Yindex); /* Draw the grid line or tick mark */ if (tickFlag) { segs[0].x1 = wi->XOrgX; segs[0].x2 = wi->XOrgX + wi->dev_info.tick_len; segs[1].x1 = wi->XOppX - wi->dev_info.tick_len; segs[1].x2 = wi->XOppX; segs[0].y1 = segs[0].y2 = segs[1].y1 = segs[1].y2 = Yspot; } else { segs[0].x1 = wi->XOrgX; segs[0].x2 = wi->XOppX; segs[0].y1 = segs[0].y2 = Yspot; } if ((ABS(Yindex) < ZERO_THRES) && !logYFlag) { wi->dev_info.xg_seg(wi->dev_info.user_state, 1, segs, zeroWidth, L_ZERO, 0, 0); if (tickFlag) { wi->dev_info.xg_seg(wi->dev_info.user_state, 1, &(segs[1]), zeroWidth, L_ZERO, 0, 0); } } else { wi->dev_info.xg_seg(wi->dev_info.user_state, 1, segs, axisWidth, L_AXIS, 0, 0); if (tickFlag) { wi->dev_info.xg_seg(wi->dev_info.user_state, 1, &(segs[1]), axisWidth, L_AXIS, 0, 0); } } } Xincr = (wi->dev_info.axis_pad + (wi->dev_info.axis_width * 7)) * wi->XUnitsPerPixel; Xstart = initGrid(wi->UsrOrgX, Xincr, logXFlag); for (Xindex = Xstart; Xindex < wi->UsrOppX; Xindex = stepGrid()) { Xspot = SCREENX(wi, Xindex); /* Draw the grid line or tick marks */ if (tickFlag) { segs[0].x1 = segs[0].x2 = segs[1].x1 = segs[1].x2 = Xspot; segs[0].y1 = wi->XOrgY; segs[0].y2 = wi->XOrgY + wi->dev_info.tick_len; segs[1].y1 = wi->XOppY - wi->dev_info.tick_len; segs[1].y2 = wi->XOppY; } else { segs[0].x1 = segs[0].x2 = Xspot; segs[0].y1 = wi->XOrgY; segs[0].y2 = wi->XOppY; } if ((ABS(Xindex) < ZERO_THRES) && !logXFlag) { wi->dev_info.xg_seg(wi->dev_info.user_state, 1, segs, zeroWidth, L_ZERO, 0, 0); if (tickFlag) { wi->dev_info.xg_seg(wi->dev_info.user_state, 1, &(segs[1]), zeroWidth, L_ZERO, 0, 0); } } else { wi->dev_info.xg_seg(wi->dev_info.user_state, 1, segs, axisWidth, L_AXIS, 0, 0); if (tickFlag) { wi->dev_info.xg_seg(wi->dev_info.user_state, 1, &(segs[1]), axisWidth, L_AXIS, 0, 0); } } } /* Check to see if he wants a bounding box */ if (bbFlag) { XSegment bb[4]; /* Draw bounding box */ bb[0].x1 = bb[0].x2 = bb[1].x1 = bb[3].x2 = wi->XOrgX; bb[0].y1 = bb[2].y2 = bb[3].y1 = bb[3].y2 = wi->XOrgY; bb[1].x2 = bb[2].x1 = bb[2].x2 = bb[3].x1 = wi->XOppX; bb[0].y2 = bb[1].y1 = bb[1].y2 = bb[2].y1 = wi->XOppY; wi->dev_info.xg_seg(wi->dev_info.user_state, 4, bb, axisWidth, L_AXIS, 0, 0); } } static double gridBase, gridStep, gridJuke[101]; static int gridNJuke, gridCurJuke; #define ADD_GRID(val) (gridJuke[gridNJuke++] = log10(val)) double initGrid(low, step, logFlag) double low; /* desired low value */ double step; /* desired step (user coords) */ int logFlag; /* is axis logarithmic? */ { double ratio, x; double RoundUp(), stepGrid(); gridNJuke = gridCurJuke = 0; gridJuke[gridNJuke++] = 0.0; if (logFlag) { ratio = pow(10.0, step); gridBase = floor(low); gridStep = ceil(step); if (ratio <= 3.0) { if (ratio > 2.0) { ADD_GRID(3.0); } else if (ratio > 1.333) { ADD_GRID(2.0); ADD_GRID(5.0); } else if (ratio > 1.25) { ADD_GRID(1.5); ADD_GRID(2.0); ADD_GRID(3.0); ADD_GRID(5.0); ADD_GRID(7.0); } else { for (x = 1.0; x < 10.0 && (x+.5)/(x+.4) >= ratio; x += .5) { ADD_GRID(x + .1); ADD_GRID(x + .2); ADD_GRID(x + .3); ADD_GRID(x + .4); ADD_GRID(x + .5); } if (floor(x) != x) ADD_GRID(x += .5); for ( ; x < 10.0 && (x+1.0)/(x+.5) >= ratio; x += 1.0) { ADD_GRID(x + .5); ADD_GRID(x + 1.0); } for ( ; x < 10.0 && (x+1.0)/x >= ratio; x += 1.0) { ADD_GRID(x + 1.0); } if (x == 7.0) { gridNJuke--; x = 6.0; } if (x < 7.0) { ADD_GRID(x + 2.0); } if (x == 10.0) gridNJuke--; } x = low - gridBase; for (gridCurJuke = -1; x >= gridJuke[gridCurJuke+1]; gridCurJuke++){ } } } else { gridStep = RoundUp(step); gridBase = floor(low / gridStep) * gridStep; } return(stepGrid()); } double stepGrid() { if (++gridCurJuke >= gridNJuke) { gridCurJuke = 0; gridBase += gridStep; } return(gridBase + gridJuke[gridCurJuke]); } double RoundUp(val) double val; /* Value */ /* * This routine rounds up the given positive number such that * it is some power of ten times either 1, 2, or 5. It is * used to find increments for grid lines. */ { int exponent, idx; exponent = (int) floor(nlog10(val)); if (exponent < 0) { for (idx = exponent; idx < 0; idx++) { val *= 10.0; } } else { for (idx = 0; idx < exponent; idx++) { val /= 10.0; } } if (val > 5.0) val = 10.0; else if (val > 2.0) val = 5.0; else if (val > 1.0) val = 2.0; else val = 1.0; if (exponent < 0) { for (idx = exponent; idx < 0; idx++) { val /= 10.0; } } else { for (idx = 0; idx < exponent; idx++) { val *= 10.0; } } return val; } int WriteValue(str, val, exp, logFlag) char *str; /* String to write into */ double val; /* Value to print */ int exp; /* Exponent */ int logFlag; /* Is this a log axis? */ /* * Writes the value provided into the string in a fixed format * consisting of seven characters. The format is: * -ddd.dd */ { int idx; if (logFlag) { if (val == floor(val)) { (void) sprintf(str, "%.0e", pow(10.0, val)); } else { (void) sprintf(str, "%.2g", pow(10.0, val - floor(val))); } } else { if (exp < 0) { for (idx = exp; idx < 0; idx++) { val *= 10.0; } } else { for (idx = 0; idx < exp; idx++) { val /= 10.0; } } (void) sprintf(str, "%.2f", val); } } #define LEFT_CODE 0x01 #define RIGHT_CODE 0x02 #define BOTTOM_CODE 0x04 #define TOP_CODE 0x08 /* Clipping algorithm from Neumann and Sproull by Cohen and Sutherland */ #define C_CODE(xval, yval, rtn) \ rtn = 0; \ if ((xval) < wi->UsrOrgX) rtn = LEFT_CODE; \ else if ((xval) > wi->UsrOppX) rtn = RIGHT_CODE; \ if ((yval) < wi->UsrOrgY) rtn |= BOTTOM_CODE; \ else if ((yval) > wi->UsrOppY) rtn |= TOP_CODE int DrawData(wi) LocalWin *wi; /* * This routine draws the data sets themselves using the macros * for translating coordinates. */ { double sx1, sy1, sx2, sy2, tx, ty; int idx, subindex; int code1, code2, cd, mark_inside; int X_idx; XSegment *ptr; for (idx = 0; idx < MAXSETS; idx++) { X_idx = 0; for (subindex = 0; subindex < AllSets[idx].numPoints-1; subindex++) { /* Put segment in (sx1,sy1) (sx2,sy2) */ sx1 = AllSets[idx].xvec[subindex]; sy1 = AllSets[idx].yvec[subindex]; sx2 = AllSets[idx].xvec[subindex+1]; sy2 = AllSets[idx].yvec[subindex+1]; /* Now clip to current window boundary */ C_CODE(sx1, sy1, code1); C_CODE(sx2, sy2, code2); mark_inside = (code1 == 0); while (code1 || code2) { if (code1 & code2) break; cd = (code1 ? code1 : code2); if (cd & LEFT_CODE) { /* Crosses left edge */ ty = sy1 + (sy2 - sy1) * (wi->UsrOrgX - sx1) / (sx2 - sx1); tx = wi->UsrOrgX; } else if (cd & RIGHT_CODE) { /* Crosses right edge */ ty = sy1 + (sy2 - sy1) * (wi->UsrOppX - sx1) / (sx2 - sx1); tx = wi->UsrOppX; } else if (cd & BOTTOM_CODE) { /* Crosses bottom edge */ tx = sx1 + (sx2 - sx1) * (wi->UsrOrgY - sy1) / (sy2 - sy1); ty = wi->UsrOrgY; } else if (cd & TOP_CODE) { /* Crosses top edge */ tx = sx1 + (sx2 - sx1) * (wi->UsrOppY - sy1) / (sy2 - sy1); ty = wi->UsrOppY; } if (cd == code1) { sx1 = tx; sy1 = ty; C_CODE(sx1, sy1, code1); } else { sx2 = tx; sy2 = ty; C_CODE(sx2, sy2, code2); } } if (!code1 && !code2) { /* Add segment to list */ Xsegs[X_idx].x1 = SCREENX(wi, sx1); Xsegs[X_idx].y1 = SCREENY(wi, sy1); Xsegs[X_idx].x2 = SCREENX(wi, sx2); Xsegs[X_idx].y2 = SCREENY(wi, sy2); X_idx++; } /* Draw markers if requested and they are in drawing region */ if (markFlag && mark_inside) { if (pixelMarks) { if (bigPixel) { wi->dev_info.xg_dot(wi->dev_info.user_state, Xsegs[X_idx-1].x1, Xsegs[X_idx-1].y1, P_DOT, 0, idx % MAXATTR); } else { wi->dev_info.xg_dot(wi->dev_info.user_state, Xsegs[X_idx-1].x1, Xsegs[X_idx-1].y1, P_PIXEL, 0, PIXVALUE(idx)); } } else { /* Distinctive markers */ wi->dev_info.xg_dot(wi->dev_info.user_state, Xsegs[X_idx-1].x1, Xsegs[X_idx-1].y1, P_MARK, MARKSTYLE(idx), PIXVALUE(idx)); } } /* Draw bar elements if requested */ if (barFlag) { int barPixels, baseSpot; XSegment line; barPixels = (int) ((barWidth / wi->XUnitsPerPixel) + 0.5); if (barPixels <= 0) barPixels = 1; baseSpot = SCREENY(wi, barBase); line.x1 = line.x2 = Xsegs[X_idx-1].x1; line.y1 = baseSpot; line.y2 = Xsegs[X_idx-1].y1; wi->dev_info.xg_seg(wi->dev_info.user_state, 1, &line, barPixels, L_VAR, LINESTYLE(idx), PIXVALUE(idx)); } } /* Handle last marker */ if (markFlag && (AllSets[idx].numPoints > 0)) { C_CODE(AllSets[idx].xvec[AllSets[idx].numPoints-1], AllSets[idx].yvec[AllSets[idx].numPoints-1], mark_inside); if (mark_inside == 0) { if (pixelMarks) { if (bigPixel) { wi->dev_info.xg_dot(wi->dev_info.user_state, Xsegs[X_idx-1].x2, Xsegs[X_idx-1].y2, P_DOT, 0, idx % MAXATTR); } else { wi->dev_info.xg_dot(wi->dev_info.user_state, Xsegs[X_idx-1].x2, Xsegs[X_idx-1].y2, P_PIXEL, 0, PIXVALUE(idx)); } } else { /* Distinctive markers */ wi->dev_info.xg_dot(wi->dev_info.user_state, Xsegs[X_idx-1].x2, Xsegs[X_idx-1].y2, P_MARK, MARKSTYLE(idx), PIXVALUE(idx)); } } } /* Handle last bar */ if ((AllSets[idx].numPoints > 0) && barFlag) { int barPixels, baseSpot; XSegment line; barPixels = (int) ((barWidth / wi->XUnitsPerPixel) + 0.5); if (barPixels <= 0) barPixels = 1; baseSpot = SCREENY(wi, barBase); line.x1 = line.x2 = Xsegs[X_idx-1].x2; line.y1 = baseSpot; line.y2 = Xsegs[X_idx-1].y2; wi->dev_info.xg_seg(wi->dev_info.user_state, 1, &line, barPixels, L_VAR, LINESTYLE(idx), PIXVALUE(idx)); } /* Draw segments */ if (AllSets[idx].numPoints > 0 && (!noLines) && (X_idx > 0)) { ptr = Xsegs; while (X_idx > wi->dev_info.max_segs) { wi->dev_info.xg_seg(wi->dev_info.user_state, wi->dev_info.max_segs, ptr, lineWidth, L_VAR, LINESTYLE(idx), PIXVALUE(idx)); ptr += wi->dev_info.max_segs; X_idx -= wi->dev_info.max_segs; } wi->dev_info.xg_seg(wi->dev_info.user_state, X_idx, ptr, lineWidth, L_VAR, LINESTYLE(idx), PIXVALUE(idx)); } } } int DrawLegend(wi) LocalWin *wi; /* * This draws a legend of the data sets displayed. Only those that * will fit are drawn. */ { int idx, spot, lineLen, oneLen; XSegment leg_line; spot = wi->XOrgY; lineLen = 0; /* First pass draws the text */ for (idx = 0; idx < MAXSETS; idx++) { if ((AllSets[idx].numPoints > 0) && (spot + wi->dev_info.axis_height + 2 < wi->XOppY)) { /* Meets the criteria */ oneLen = strlen(AllSets[idx].setName); if (oneLen > lineLen) lineLen = oneLen; wi->dev_info.xg_text(wi->dev_info.user_state, wi->XOppX + wi->dev_info.bdr_pad, spot+2, AllSets[idx].setName, T_UPPERLEFT, T_AXIS); spot += 2 + wi->dev_info.axis_height + wi->dev_info.bdr_pad; } } lineLen = lineLen * wi->dev_info.axis_width; leg_line.x1 = wi->XOppX + wi->dev_info.bdr_pad; leg_line.x2 = leg_line.x1 + lineLen; spot = wi->XOrgY; /* second pass draws the lines */ for (idx = 0; idx < MAXSETS; idx++) { if ((AllSets[idx].numPoints > 0) && (spot + wi->dev_info.axis_height + 2 < wi->XOppY)) { leg_line.y1 = leg_line.y2 = spot - wi->dev_info.legend_pad; wi->dev_info.xg_seg(wi->dev_info.user_state, 1, &leg_line, 1, L_VAR, LINESTYLE(idx), PIXVALUE(idx)); if (markFlag && !pixelMarks) { wi->dev_info.xg_dot(wi->dev_info.user_state, leg_line.x1, leg_line.y1, P_MARK, MARKSTYLE(idx), PIXVALUE(idx)); } spot += 2 + wi->dev_info.axis_height + wi->dev_info.bdr_pad; } } } #define RND(val) ((int) ((val) + 0.5)) #ifdef TOOLBOX /*ARGSUSED*/ void do_hardcopy(prog, info, init_fun, dev_spec, file_or_dev, maxdim, ti_fam, ti_size, ax_fam, ax_size) char *prog; /* Program name for Xdefaults */ char *info; /* Some state information */ int (*init_fun)(); /* Hardcopy init function */ char *dev_spec; /* Device specification (if any) */ char *file_or_dev; /* Filename or device spec */ double maxdim; /* Maximum dimension in cm */ char *ti_fam, *ax_fam; /* Font family names */ double ti_size, ax_size; /* Font sizes in points */ /* * This routine resets the function pointers to those specified * by `init_fun' and causes a screen redisplay. If `dev_spec' * is non-zero, it will be considered a sprintf string with * one %s which will be filled in with `file_or_dev' and fed * to popen(3) to obtain a stream. Otherwise, `file_or_dev' * is considered to be a file and is opened for writing. The * resulting stream is fed to the initialization routine for * the device. */ { LocalWin *curWin = (LocalWin *) info; LocalWin thisWin; FILE *out_stream; char buf[MAXBUFSIZE], err[MAXBUFSIZE], ierr[ERRBUFSIZE]; char tilde[MAXBUFSIZE*10]; int final_w, final_h; double ratio; if (dev_spec) { (void) sprintf(buf, dev_spec, file_or_dev); out_stream = popen(buf, "w"); if (!out_stream) { #ifdef TOOLBOX do_error(sprintf(err, "Unable to issue command:\n %s\n", buf)); #else (void) fprintf(stderr, "Unable to issue command:\n %s\n", buf)); #endif return; } } else { tildeExpand(tilde, file_or_dev); out_stream = fopen(tilde, "w"); if (!out_stream) { #ifdef TOOLBOX do_error(sprintf(err, "Unable to open file `%s'\n", tilde)); #else (void) fprintf(stderr, "Unable to open file `%s'\n", tilde); #endif return; } } thisWin = *curWin; ratio = ((double) thisWin.dev_info.area_w) / ((double) thisWin.dev_info.area_h); if (thisWin.dev_info.area_w > thisWin.dev_info.area_h) { final_w = RND(maxdim * 10000.0); final_h = RND(maxdim/ratio * 10000.0); } else { final_w = RND(maxdim * ratio * 10000.0); final_h = RND(maxdim * 10000.0); } ierr[0] = '\0'; if ((*init_fun)(out_stream, final_w, final_h, ti_fam, ti_size, ax_fam, ax_size, &(thisWin.dev_info), ierr)) { DrawWindow(&thisWin); if (thisWin.dev_info.xg_end) { thisWin.dev_info.xg_end(thisWin.dev_info.user_state); } } else { #ifdef TOOLBOX do_error(ierr); #else (void) fprintf(stderr, "%s\n", ierr); #endif } if (dev_spec) { (void) pclose(out_stream); } else { (void) fclose(out_stream); } } #endif static char *tildeExpand(out, in) char *out; /* Output space for expanded file name */ char *in; /* Filename with tilde */ /* * This routine expands out a file name passed in `in' and places * the expanded version in `out'. It returns `out'. */ { char username[50], *userPntr; struct passwd *userRecord; out[0] = '\0'; /* Skip over the white space in the initial path */ while ((*in == ' ') || (*in == '\t')) in++; /* Tilde? */ if (in[0] == TILDE) { /* Copy user name into 'username' */ in++; userPntr = &(username[0]); while ((*in != '\0') && (*in != '/')) { *(userPntr++) = *(in++); } *(userPntr) = '\0'; /* Look up user directory in passwd file */ if ((userRecord = getpwnam(username)) != (struct passwd *) 0) { /* Found user in passwd file. Concatenate user directory */ strcat(out, userRecord->pw_dir); } } /* Concantenate remaining portion of file name */ strcat(out, in); return out; } /*ARGSUSED*/ static int XErrHandler(disp, evt) Display *disp; XErrorEvent *evt; /* * Displays a nicely formatted message and core dumps. */ { fprintf(stderr, "Fatal X Error: %s", ux11_error(evt)); abort(); } int stricmp(a, b) register char *a, *b; /* * This routine compares two strings disregarding case. */ { register int value; if ((a == (char *) 0) || (b == (char *) 0)) { return a - b; } for ( /* nothing */; ((*a | *b) && !(value = ((isupper(*a) ? *a - 'A' + 'a' : *a) - (isupper(*b) ? *b - 'A' + 'a' : *b)))); a++, b++) /* Empty Body */; return value; } \End\Of\Shar\ else echo "will not over write ./xgraph-11/xgraph.c" fi echo "Finished archive 4 of 6"