#ifndef lint static char *rcs="$Header: /mit/hst/src/plot/RCS/Plot.c,v 3.1 90/08/28 17:31:31 tldavis Exp $"; #endif /***************************************************************************** * * Plot widget for X11 Cardiovascular Simulator * Copyright 1988 by Timothy L. Davis * * $Source: /mit/hst/src/plot/RCS/Plot.c,v $ * $Log: Plot.c,v $ * Revision 3.1 90/08/28 17:31:31 tldavis * Removed a bug that cropped up with an absent X resource file. * * Revision 3.0 90/08/28 14:41:51 tldavis * Added support for the XtNtiming set of flags (at least 16 bit int). * XtNtiming has flags which determine the current portion of cardiac cycle, * the portion to plot, and whether to average data before plotting. * * Revision 1.1 90/08/19 18:42:25 tldavis * Initial revision * * Revision 2.2 90/08/18 18:36:40 tldavis * Many changes: reworked all the display code for speed and bugfixes. * Now fully supports color resources. * * Revision 2.1 90/08/10 10:57:23 dot * moved Xaw header files to X11/Xaw for R4 * * Revision 2.0 90/08/06 17:59:46 tldavis * Final X11R3 version. * * Revision 1.24 90/08/06 17:05:47 tldavis * Fixed problems with color on Suns. * * Revision 1.23 90/05/31 16:29:06 tldavis * Pre-ACIS checkin. * * Revision 1.22 90/02/09 15:28:45 tldavis * Avoids redisplaying at almost all costs. * Tweaked to keep plotting in same place after exposures. * * Revision 1.21 89/10/12 22:15:29 tldavis * Added possibility for bitmap saving, if you #define PIXDRAW. * * Revision 1.20 89/10/10 23:13:12 tldavis * mwm support, works well but no saving of background pixmap. * Works with either stripchart or scope or xy mode. * R3 fonts fixed. * Revision 1.13 89/02/22 12:27:07 tldavis * X11R3 version (no saves of old data). * Revision 1.10 88/10/10 21:45:34 hst * Fixed several bugs to do with labels, removed ball for speed reasons, * Revision 1.2 88/08/03 18:21:34 tldavis * First successful compile of X11 version. * Revision 1.1 88/08/03 14:53:09 tldavis * Initial revision * * PLOT: Everything needed for the plot windows, both striphart and X v. Y * style plots. Accepts up to 3 separate variables on the Y axis, plotted * against a common X axis. Each variable may be "auto-rescaling", meaning * that the plot axes for that variable are recalculated if the values go * out of bounds. * * WidgetClass is plotWidgetClass * * void PlotData(Window w, double *pressures) is the main routine to * provide the plot widget with a new set of data values to be plotted. * pressures should point to a 9-element array containing the 6 pressures, * time, Cl(t) and Cr(t) for that time, as produced inside simulate.c * Generally, this routine should only be called by the control loop which * polls the simulation data pipe and broadcasts new data to all active * plots. * * void PlotModifySimulation(Window w, int place, int type, double value) * allows for changes to the widget's view of the simulation, so that * variables for plotting may be calculated appropriately from the given * pressures. * * define PIXDRAW to draw all data to the background pixmap. It won't be lost * when the window is obscured, but this is MUCH slower than the other way. * * define MOVINGBALL to put a pen-ball head at the right of the stripchart. * This also slows down the animation. * * define SAVEDATA to save a list of previously-plotted data. * ****************************************************************************/ #ifdef NOTDEF #define PIXDRAW /* Save plot data in background pixmaps SURE IS SLOW! */ #endif #include #ifdef SVR4 #include #else #include #endif #include #include #include #include #include "PlotP.h" #include "../form/PlotForm.h" /* For Modify Axes menu. */ #include "../sim/CVDefs.h" #include extern double getval(); /* from ../sim/eqns.c */ extern unsigned int UpdatePeriod(); /* from ../sim/eqns.c */ extern double drawAxis(); static int drawOldData(); extern Display *dpy; extern Window root; extern int screen; #define PAD 40 /* pad around outside of graph (to include labels/nums) */ #define AXISPAD 12 /* pad for axis dimensions */ #define SAMPLEPAD 32 /* pad for sample line dashes */ /* screen resolution in pixels per cm */ #define RESOLUTION (10.*XDisplayWidth(dpy,XDefaultScreen(dpy))/ \ XDisplayWidthMM(dpy,XDefaultScreen(dpy))) #define offset(field) XtOffset(PlotWidget,plot.field) static XtResource resources[] = { {XtNcolor1, XtCForeground, XtRPixel, sizeof(Pixel), offset(color1), XtRString, "XtDefaultForeground"}, {XtNcolor2, XtCForeground, XtRPixel, sizeof(Pixel), offset(color2), XtRString, "XtDefaultForeground"}, {XtNcolor3, XtCForeground, XtRPixel, sizeof(Pixel), offset(color3), XtRString, "XtDefaultForeground"}, {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel), offset(foreground), XtRString, "XtDefaultForeground"}, {XtNbackground, XtCBackground, XtRPixel, sizeof(Pixel), offset(background), XtRString, "XtDefaultBackground"}, {XtNplotback, XtCBackground, XtRPixel, sizeof(Pixel), offset(plotback), XtRString, "XtDefaultBackground"}, {XtNlabel, XtCLabel, XtRString, sizeof(String), offset(title), XtRString, NULL}, {XtNpaperSpeed, XtCValue, XtRDouble, sizeof(double), offset(paperSpeed), XtRString, "5.0"}, {XtNdecayTime, XtCValue, XtRDouble, sizeof(double), offset(decayTime), XtRString, "1.0"}, {XtNplaceX, XtCValue, XtRInt, sizeof(int), offset(placeX), XtRString, "0"}, {XtNplace1, XtCValue, XtRInt, sizeof(int), offset(place1), XtRString, "-1"}, {XtNplace2, XtCValue, XtRInt, sizeof(int), offset(place2), XtRString, "-1"}, {XtNplace3, XtCValue, XtRInt, sizeof(int), offset(place3), XtRString, "-1"}, {XtNtypeX, XtCValue, XtRInt, sizeof(int), offset(typeX), XtRString, "0"}, {XtNtype1, XtCValue, XtRInt, sizeof(int), offset(type1), XtRString, "-1"}, {XtNtype2, XtCValue, XtRInt, sizeof(int), offset(type2), XtRString, "-1"}, {XtNtype3, XtCValue, XtRInt, sizeof(int), offset(type3), XtRString, "-1"}, {XtNlabelX, XtCValue, XtRString, sizeof(String), offset(labelX), XtRString, "Time"}, {XtNlabel1, XtCValue, XtRString, sizeof(String), offset(label1), XtRString, NULL}, {XtNlabel2, XtCValue, XtRString, sizeof(String), offset(label2), XtRString, NULL}, {XtNlabel3, XtCValue, XtRString, sizeof(String), offset(label3), XtRString, NULL}, {XtNminX, XtCValue, XtRDouble, sizeof(double), offset(minX), XtRString, "0."}, {XtNmin1, XtCValue, XtRDouble, sizeof(double), offset(min1), XtRString, "0."}, {XtNmin2, XtCValue, XtRDouble, sizeof(double), offset(min2), XtRString, "0."}, {XtNmin3, XtCValue, XtRDouble, sizeof(double), offset(min3), XtRString, "0."}, {XtNmaxX, XtCValue, XtRDouble, sizeof(double), offset(maxX), XtRString, "100."}, {XtNmax1, XtCValue, XtRDouble, sizeof(double), offset(max1), XtRString, "100."}, {XtNmax2, XtCValue, XtRDouble, sizeof(double), offset(max2), XtRString, "100."}, {XtNmax3, XtCValue, XtRDouble, sizeof(double), offset(max3), XtRString, "100."}, {XtNunitX, XtCValue, XtRString, sizeof(String), offset(unitX), XtRString, NULL}, {XtNunit1, XtCValue, XtRString, sizeof(String), offset(unit1), XtRString, NULL}, {XtNunit2, XtCValue, XtRString, sizeof(String), offset(unit2), XtRString, NULL}, {XtNunit3, XtCValue, XtRString, sizeof(String), offset(unit3), XtRString, NULL}, {XtNautoScaleX, XtCValue, XtRBoolean, sizeof(Boolean), offset(autoX), XtRBoolean, False}, {XtNautoScale1, XtCValue, XtRBoolean, sizeof(Boolean), offset(auto1), XtRBoolean, False}, {XtNautoScale2, XtCValue, XtRBoolean, sizeof(Boolean), offset(auto2), XtRBoolean, False}, {XtNautoScale3, XtCValue, XtRBoolean, sizeof(Boolean), offset(auto3), XtRBoolean, False}, {XtNstripMode, XtCValue, XtRBoolean, sizeof(Boolean), offset(stripMode), XtRBoolean, False}, {XtNcallback, XtCCallback, XtRCallback, sizeof(caddr_t), offset(callbacks), XtRCallback, NULL}, {XtNtiming, XtCValue, XtRInt, sizeof(int), offset(timing), XtRString, "0"}, }; #undef offset /* * FORWARD defines for the widget activity routines to be put in core struct * These routines follow widget interaction rules. */ static void ClassInitialize(); static void Initialize(); static void Realize(); static void Destroy(); static void Redisplay(); static void UpdateCaliper(), WriteCaliper(), EraseCaliper(); static Boolean SetValues(); static XtGeometryResult GeometryManager(); PlotClassRec plotClassRec = { { /* core_class fields */ #define superclass ((WidgetClass) &compositeClassRec) /* superclass */ superclass, /* class_name */ "Plot", /* widget_size */ sizeof(PlotRec), /* class_initialize */ ClassInitialize, /* class_part_initialize */ NULL, /* class_inited */ False, /* initialize */ Initialize, /* initialize_hook */ NULL, /* realize */ Realize, /* actions */ NULL, /* TOO SLOW */ /* num_actions */ 0, /* resources */ resources, /* num_resources */ XtNumber(resources), /* xrm_class */ NULLQUARK, /* compress_motion */ True, /* compress_exposure */ True, /* compress_enterleave */ True, /* visible_interest */ False, /* destroy */ Destroy, /* resize */ XtInheritResize, /* expose */ Redisplay, /* set_values */ SetValues, /* set_values_hook */ NULL, /* set_values_almost */ XtInheritSetValuesAlmost, /* get_values_hook */ NULL, /* accept_focus */ NULL, /* version */ XtVersion, /* callback_private */ NULL, /* tm_table */ NULL, /* TOO SLOW */ /* query_geometry */ XtInheritQueryGeometry, /* ***NEWR3*** display_accel*/ XtInheritDisplayAccelerator, /* ***NEWR3*** extension */ NULL }, { /* composite_class fields */ /* geometry_manager */ GeometryManager, /* change_managed */ XtInheritChangeManaged, /* insert_child */ XtInheritInsertChild, /* delete_child */ XtInheritDeleteChild, /* ***NEWR3*** extension */ NULL }, { /* Plot fields */ /* gc's */ (GC)NULL, (GC)NULL, (GC)NULL, (GC)NULL, /* small_font */ (XFontStruct *)NULL, /* pointer_cursor */ (Cursor)NULL, (Cursor)NULL, } }; WidgetClass plotWidgetClass = (WidgetClass) &plotClassRec; /**************************************************************** * * Private Procedures * ****************************************************************/ /*FORWARD*/ static void ModCallback(), RefreshCallback(); /*ARGSUSED*/ static void CvtStringToDouble(args, num_args, fromVal, toVal) XrmValue *args; Cardinal *num_args; XrmValue *fromVal, *toVal; { static double i; if (*num_args != 0) XtWarning("String to Double conversion needs no extra arguments"); if (sscanf ((char *)fromVal->addr,"%lf",&i) == 1) { toVal->size = sizeof(double); toVal->addr = (caddr_t) &i; } } /* * registered private widget initialization and event handling routines: * */ #ifdef PIXDRAW static GC whiteGC = (GC)0; #endif static char Dash2[] = {1, 3, 0}; static char Dash3[] = {3, 5, 0}; static char *trans = ": notify() unset() unhighlight() \n"; static XtTranslations parsed_translations; static void ClassInitialize() { XGCValues gcvalues; unsigned long fontmask = 0; if (plotClassRec.plot_class.small_font = XLoadQueryFont(dpy,"*-courier-medium-r-*-*-*-80-*")) fontmask = GCFont; /* successful font acquisition, else default. */ plotClassRec.plot_class.pointer_cursor = XCreateFontCursor(dpy, XC_arrow); if (plotClassRec.plot_class.small_font) gcvalues.font = plotClassRec.plot_class.small_font->fid; plotClassRec.plot_class.plotwin_cursor = XCreateFontCursor(dpy, XC_crosshair); gcvalues.graphics_exposures = False; plotClassRec.plot_class.gc1 = XCreateGC(dpy, root, GCGraphicsExposures | fontmask, &gcvalues); gcvalues.line_style = LineOnOffDash; plotClassRec.plot_class.gc2 = XCreateGC(dpy, root, GCGraphicsExposures | fontmask | GCLineStyle, &gcvalues); XSetDashes(dpy, plotClassRec.plot_class.gc2, 0, Dash2, strlen(Dash2)); gcvalues.line_style = LineOnOffDash; plotClassRec.plot_class.gc3 = XCreateGC(dpy, root, GCGraphicsExposures | fontmask | GCLineStyle, &gcvalues); XSetDashes(dpy, plotClassRec.plot_class.gc3, 0, Dash3, strlen(Dash3)); XtAddConverter(XtRString, XtRDouble, CvtStringToDouble, (XtConvertArgList)0, 0); #ifdef PIXDRAW gcvalues.foreground = BlackPixel(dpy,screen); gcvalues.background = WhitePixel(dpy,screen); whiteGC = XCreateGC(dpy, root, GCForeground | GCBackground, &gcvalues); #endif gcvalues.function = GXcopy; plotClassRec.plot_class.pointerGC = XCreateGC(dpy, root, GCFunction | fontmask, &gcvalues); parsed_translations = XtParseTranslationTable(trans); } /* ClassInitialize() */ static void Initialize(request, new) Widget request, new; { PlotWidget pw = (PlotWidget) new; PlotPart *plot = & pw->plot; static PlotRec blank = {{0,}, {0,}, {0, 0, 0, 0, 0, 0, 0, 0.0, 0.0, -1, -1, -1, -1, -1, -1, -1, -1, 0, }}; int i; Arg arg[10]; XSetForeground(dpy, plotClassRec.plot_class.gc1, plot->color1); XSetBackground(dpy, plotClassRec.plot_class.gc1, plot->plotback); XSetForeground(dpy, plotClassRec.plot_class.gc2, plot->color2); XSetBackground(dpy, plotClassRec.plot_class.gc2, plot->plotback); XSetForeground(dpy, plotClassRec.plot_class.gc3, plot->color3); XSetBackground(dpy, plotClassRec.plot_class.gc3, plot->plotback); XSetForeground(dpy, plotClassRec.plot_class.pointerGC, plot->foreground); XSetBackground(dpy, plotClassRec.plot_class.pointerGC, plot->background); plot->titlePad = 5; plot->labelPad = 5; plot->slideDots = 1; plot->outX = plot->out1 = plot->out2 = plot->out3 = 0; plot->phase2 = plot->phase3 = 0; plot->lastX = plot->last1 = plot->last2 = plot->last3 = 0.; plot->sfX = 0.0; if (pw->core.width == 0) pw->core.width = 400; if (pw->core.height == 0) pw->core.height = 200; plot->plotwin = 0; i=0; XtSetArg(arg[i], XtNlabel, "Modify Axes");i++; plot->modButton = XtCreateManagedWidget("Modify Axes", commandWidgetClass, (Widget)pw, arg, i); XtAddCallback(plot->modButton, XtNcallback, ModCallback, (caddr_t)pw); XtOverrideTranslations(plot->modButton, parsed_translations); i=0; XtSetArg(arg[i], XtNlabel, "Refresh");i++; plot->refreshButton = XtCreateManagedWidget("Refresh", commandWidgetClass, (Widget)pw, arg, i); XtAddCallback(plot->refreshButton, XtNcallback, RefreshCallback,(caddr_t)pw); /* Leave them unmanaged for now... manage them selectively in SetValues */ i=0; XtSetArg(arg[i], XtNforeground, plot->color1);i++; XtSetArg(arg[i], XtNbackground, plot->background);i++; plot->label1w = XtCreateWidget("label1", labelWidgetClass, (Widget)pw, arg, i); i=0; XtSetArg(arg[i], XtNforeground, plot->color2);i++; XtSetArg(arg[i], XtNbackground, plot->background);i++; plot->label2w = XtCreateWidget("label2", labelWidgetClass, (Widget)pw, arg, i); i=0; XtSetArg(arg[i], XtNforeground, plot->color3);i++; XtSetArg(arg[i], XtNbackground, plot->background);i++; plot->label3w = XtCreateWidget("label3", labelWidgetClass, (Widget)pw, arg, i); XtAddEventHandler(new, PointerMotionMask, False,UpdateCaliper, (caddr_t)0); XtAddEventHandler(new, LeaveWindowMask, False, EraseCaliper, (caddr_t)0); XtAddEventHandler(new, ButtonPressMask, False, WriteCaliper, (caddr_t)0); SetValues((Widget) &blank, request, new); } /* Initialize() */ static void Realize(widget, mask, attributes) Widget widget; Mask *mask; XSetWindowAttributes *attributes; { PlotPart *plot = &((PlotWidget)widget)->plot; *mask = *mask | CWBackingStore | CWSaveUnder; attributes->backing_store = Always; attributes->save_under = True; (*superclass->core_class.realize)(widget, mask, attributes); XDefineCursor(dpy, XtWindow(widget), plotClassRec.plot_class.pointer_cursor); plot->plotwin = XCreateSimpleWindow(dpy, XtWindow(widget), 1, 1, 1, 1, 0, (unsigned long)plot->foreground, (unsigned long)plot->plotback); XDefineCursor(dpy, plot->plotwin, plotClassRec.plot_class.plotwin_cursor); plot->plotpix = (Drawable)0; XMapWindow(dpy, plot->plotwin); } /* Realize() */ static void Destroy(w) Widget w; { PlotPart *plot = &((PlotWidget)w)->plot; #ifdef PIXDRAW /*|||### XFreePixmap(dpy, (Pixmap)plot->plotpix); */ #endif XDestroyWindow(dpy, plot->plotwin); } /* * * Repaint the widget window * */ /*ARGSUSED*/ static void Redisplay(w, event, region) Widget w; XEvent *event; Region region; /*unused*/ { Window window = XtWindow(w); PlotWidget pw = (PlotWidget) w; PlotPart *plot = &pw->plot; CorePart *core = &pw->core; GC gc1 = plotClassRec.plot_class.gc1, gc2 = plotClassRec.plot_class.gc2, gc3 = plotClassRec.plot_class.gc3, gc = plotClassRec.plot_class.pointerGC; XFontStruct *smallFont = plotClassRec.plot_class.small_font; register int startX, startY, endX, endY; /* start,end of plotwin widget */ #ifdef SAVEDATA double oldestVisibleTime; #endif int label1pos = 0, label2pos = 0, label3pos = 0; if ((event && (event->type != Expose || (event->type == Expose && event->xexpose.count))) || !w || !XtIsRealized(w)) return; /*Skip all region events but last expose (count == 0)*/ /* draw the widget */ XSetBackground(dpy, gc1, plot->background); XSetBackground(dpy, gc2, plot->background); XSetBackground(dpy, gc3, plot->background); XClearWindow(dpy, window); #ifdef PIXDRAW XClearWindow(dpy, plot->plotwin); #endif /* Establish coordinates for graphing and conversion constants */ /* X-axis related stuff */ startX = PAD; if (plot->place1 > -1) startX += AXISPAD; if (plot->place2 > -1) startX += PAD+AXISPAD; endX = core->width - AXISPAD; if (plot->place3 > -1) endX -= PAD; if (plot->typeX == TIME) { if (plot->stripMode) plot->maxX = plot->currentTime; plot->sfX = plot->paperSpeed * RESOLUTION; plot->minX = plot->maxX - (endX - startX) / plot->sfX; /* fix the end of the graph again to fix the roundoff error */ endX = startX + plot->sfX*(plot->maxX - plot->minX); } /* Y-axis oriented stuff */ { int top = plot->titlePad; int bot = core->height - plot->labelPad; /* add the modify axes button and the refresh button to the top */ plot->xoff = PAD; XtMoveWidget(plot->modButton, plot->xoff, top); plot->xoff += plot->modButton->core.width + 8; XtMoveWidget(plot->refreshButton,plot->xoff,top); plot->xoff += plot->refreshButton->core.width + 8; top += (plot->modButton->core.height > plot->refreshButton->core.height) ? plot->modButton->core.height : plot->refreshButton->core.height; if (XtIsManaged(plot->label3w)) { bot -= plot->label3w->core.height; label3pos = bot + (plot->label3w->core.height + 0.5)/2; XtConfigureWidget(plot->label3w, plot->labelPad + SAMPLEPAD, bot, core->width - 2*plot->labelPad - SAMPLEPAD, plot->label3w->core.height,0); bot -= plot->labelPad; } if (XtIsManaged(plot->label2w)) { bot -= plot->label2w->core.height; label2pos = bot + (plot->label2w->core.height + 0.5)/2; XtConfigureWidget(plot->label2w, plot->labelPad + SAMPLEPAD, bot, core->width- 2*plot->labelPad - SAMPLEPAD, plot->label2w->core.height,0); bot -= plot->labelPad; } if (XtIsManaged(plot->label1w)) { bot -= plot->label1w->core.height; label1pos = bot + (plot->label1w->core.height + 0.5)/2; XtConfigureWidget(plot->label1w, plot->labelPad + SAMPLEPAD, bot, core->width-2*plot->labelPad - SAMPLEPAD, plot->label1w->core.height,0); bot -= plot->labelPad; } bot -= AXISPAD*2; startY = bot-AXISPAD; endY = top+AXISPAD; } { int oldw = plot->plotWidth; int oldh = plot->plotHeight; plot->plotWidth = endX-startX; plot->plotHeight = startY-endY; plot->slideDots = (endX-startX)*abs(endY-startY)/10000. + 1; plot->nextHash = ceil(plot->minX); if (plot->plotWidth < 2 || plot->plotHeight < 2) { /***abort display***/ return; } XMoveResizeWindow(dpy, plot->plotwin, startX, endY, plot->plotWidth, plot->plotHeight); #ifdef SAVEDATA oldestVisibleTime = plot->currentTime - plot->decayTime; if (plot->decayTime == 0.0) oldestVisibleTime = 0.0; /* visible forever */ if (plot->typeX == TIME) oldestVisibleTime = plot->minX; #endif if (plot->typeX != TIME) { plot->sfX = drawAxis(dpy, window, gc, smallFont, startX, startY+AXISPAD, endX, startY+AXISPAD, plot->minX, plot->maxX, 1); } if (!plot->plotpix || plot->plotWidth != oldw || plot->plotHeight != oldh){ #ifdef PIXDRAW Pixmap newpix = XCreatePixmap(dpy, plot->plotwin, plot->plotWidth, plot->plotHeight, XDisplayPlanes(dpy, XDefaultScreen(dpy))); XFillRectangle(dpy, newpix, whiteGC, 0, 0, plot->plotWidth, plot->plotHeight); if (plot->plotpix) XFreePixmap(dpy, plot->plotpix); XSetWindowBackgroundPixmap(dpy, plot->plotwin, newpix); #else Window newpix = plot->plotwin; #endif /****||||||?????? copy old pixmap to new */ XClearWindow(dpy, plot->plotwin); plot->plotpix = (Drawable) newpix; } } if (plot->place1 > -1) { plot->sf1 = drawAxis(dpy, window, gc1, smallFont, startX - AXISPAD, startY, startX - AXISPAD, endY, plot->min1, plot->max1, 1); XDrawImageString(dpy, window, gc,startX-AXISPAD, startY+AXISPAD, "#1", 2); XDrawLine(dpy, window, gc1, plot->labelPad, label1pos, plot->labelPad + SAMPLEPAD, label1pos); plot->lastPt1 = drawOldData(pw, gc1,smallFont, plot->max1, plot->sf1); } if (plot->place2 > -1) { XSetLineAttributes(dpy, gc2, 0, LineSolid, CapButt, JoinMiter); plot->sf2 = drawAxis(dpy,window,gc2,smallFont, startX-2*AXISPAD-PAD,startY, startX-2*AXISPAD-PAD,endY, plot->min2, plot->max2, 1); XDrawImageString(dpy, window, gc, startX-2*AXISPAD-PAD, startY+AXISPAD, "#2", 2); XSetLineAttributes(dpy, gc2, 0, LineOnOffDash, CapButt, JoinMiter); XDrawLine(dpy, window, gc2, plot->labelPad, label2pos, plot->labelPad + SAMPLEPAD, label2pos); plot->lastPt2 = drawOldData(pw, gc2,smallFont, plot->max2, plot->sf2); } if (plot->place3 > -1) { XSetLineAttributes(dpy, gc3, 0, LineSolid, CapButt, JoinMiter); plot->sf3 = drawAxis(dpy, window, gc3, smallFont, endX+AXISPAD, startY, endX+AXISPAD, endY, plot->min3, plot->max3, -1); XDrawImageString(dpy, window, gc, endX+AXISPAD-5, startY+AXISPAD, "#3", 2); XSetLineAttributes(dpy, gc3, 0, LineOnOffDash, CapButt, JoinMiter); XDrawLine(dpy, window, gc3, plot->labelPad, label3pos, plot->labelPad + SAMPLEPAD, label3pos); plot->lastPt3 = drawOldData(pw, gc3,smallFont, plot->max3, plot->sf3); } XSetBackground(dpy, gc1, plot->plotback); XSetBackground(dpy, gc2, plot->plotback); XSetBackground(dpy, gc3, plot->plotback); /* (superclass->core_class.expose)(w, event, region);*/ } /* END Redisplay() */ /*ARGSUSED*/ static void UpdateCaliper(w, client, event) Widget w; caddr_t client;/*unused*/ XEvent *event; { Window window = XtWindow(w); PlotWidget pw = (PlotWidget) w; PlotPart *plot = &pw->plot; GC gc = plotClassRec.plot_class.pointerGC; XMotionEvent *mev = (XMotionEvent *) event; char buf[132], bufX[40], buf1[40], buf2[40], buf3[40]; int xPt, yPt; Window root_ret, child; int rootx, rooty; unsigned int mask; if (XQueryPointer(dpy, plot->plotwin, &root_ret, &child, &rootx, &rooty, &xPt, &yPt, &mask) == False) return; /* wrong screen */ if (mev->subwindow == plot->plotwin) { if (plot->sfX) sprintf(bufX,"X:%-7.3f", xPt/plot->sfX + plot->minX); if (plot->place1 > -1 && plot->sf1) sprintf(buf1,", Y1:%-7.3f", yPt/plot->sf1 + plot->max1); else buf1[0] = (char)0; if (plot->place2 > -1 && plot->sf2) sprintf(buf2,", Y2:%-7.3f", yPt/plot->sf2 + plot->max2); else buf2[0] = (char)0; if (plot->place3 > -1 && plot->sf3) sprintf(buf3,", Y3:%-7.3f", yPt/plot->sf3 + plot->max3); else buf3[0] = (char)0; sprintf(buf,"%s%s%s%s", bufX, buf1, buf2, buf3); XDrawImageString(dpy, window, gc, plot->xoff, plot->titlePad + 20, buf, strlen(buf)); } else EraseCaliper(w, client, event); } /*ARGSUSED*/ static void WriteCaliper(w, client, event) Widget w; caddr_t client; /*unused*/ XEvent *event; { Window window = XtWindow(w); PlotWidget pw = (PlotWidget) w; PlotPart *plot = &pw->plot; GC gc = plotClassRec.plot_class.pointerGC; XButtonEvent *bev = (XButtonEvent *) event; char buf[132], bufX[40], buf1[40], buf2[40], buf3[40]; int xPt, yPt; Window child; if (bev->subwindow == plot->plotwin && XTranslateCoordinates(dpy, window, plot->plotwin, bev->x, bev->y, &xPt, &yPt, &child)) { XSetBackground(dpy, plotClassRec.plot_class.pointerGC, plot->plotback); if (plot->sfX) sprintf(bufX,"X:%-.3f", xPt/plot->sfX + plot->minX); if (plot->place1 > -1 && plot->sf1) sprintf(buf1," Y1:%-.3f", yPt/plot->sf1 + plot->max1); else buf1[0] = (char)0; if (plot->place2 > -1 && plot->sf2) sprintf(buf2," Y2:%-.3f", yPt/plot->sf2 + plot->max2); else buf2[0] = (char)0; if (plot->place3 > -1 && plot->sf3) sprintf(buf3," Y3:%-.3f", yPt/plot->sf3 + plot->max3); else buf3[0] = (char)0; sprintf(buf,"%s%s%s%s", bufX, buf1, buf2, buf3); XDrawLine(dpy, plot->plotpix, gc, xPt-10, yPt, xPt+10, yPt); XDrawLine(dpy, plot->plotpix, gc, xPt, yPt-10, xPt, yPt+10); xPt += 10; if (yPt < 15) yPt += 18; else yPt -= 10; XDrawImageString(dpy, plot->plotpix, gc, xPt, yPt, buf, strlen(buf)); XSetBackground(dpy, plotClassRec.plot_class.pointerGC, plot->background); } } /*ARGSUSED*/ static void EraseCaliper(w, client, event) Widget w; caddr_t client; /*unused*/ XEvent *event; { Window window = XtWindow(w); PlotWidget pw = (PlotWidget) w; PlotPart *plot = &pw->plot; CorePart *core = &pw->core; /* Clear text including descenders, but don't bite into axes. */ XClearArea(dpy, window, 0, 0, (int)core->width, plot->titlePad+20+5, False); } /*ARGSUSED*/ static int drawOldData(pw, gc, font, maxY, sfY) PlotWidget pw; GC gc; XFontStruct *font; double maxY, sfY; { return(-1); } #ifdef MOVINGBALL /*ARGSUSED*/ static void moveBall(w, x1, y1, x2, y2) Drawable w; int x1, y1, x2, y2; { GC invert_gc = plotClassRec.plot_class.pointerGC; /*||| A travesty! This will slow it down miserably!!!!!!!! ||||||*/ XFillRectangle(dpy, w, invert_gc, x2-1, y2-1, 3, 3); XFlush(dpy); XFillRectangle(dpy, w, invert_gc, x2-1, y2-1, 3, 3); } #endif /* * ModCallback() is the action for the modify axes button. */ /*ARGSUSED*/ static void ModCallback(widget, closure, call_data) Widget widget; caddr_t closure, call_data; { PlotWidget pw = (PlotWidget) closure; PlotPart *plot = &pw->plot; Widget menu; AxisDataRec adX, ad1, ad2, ad3; adX.paperSpeed = plot->paperSpeed; adX.place = plot->placeX; adX.type = plot->typeX; adX.title = plot->labelX; adX.min = plot->minX; adX.max = plot->maxX; adX.units = plot->unitX; adX.autoScale = plot->autoX; adX.stripMode = plot->stripMode; ad1.place = plot->place1; ad1.type = plot->type1; ad1.title = plot->label1; ad1.min = plot->min1; ad1.max = plot->max1; ad1.units = plot->unit1; ad1.autoScale = plot->auto1; ad2.place = plot->place2; ad2.type = plot->type2; ad2.title = plot->label2; ad2.min = plot->min2; ad2.max = plot->max2; ad2.units = plot->unit2; ad2.autoScale = plot->auto2; ad3.place = plot->place3; ad3.type = plot->type3; ad3.title = plot->label3; ad3.min = plot->min3; ad3.max = plot->max3; ad3.units = plot->unit3; ad3.autoScale = plot->auto3; menu = MakePlotMenu((Widget)pw, XtSetValues, 500, 5, &adX, &ad1, &ad2, &ad3); #ifdef DEBUG printf("Pushed Modify Button ...\n"); #endif XtPopup(menu, XtGrabExclusive); } /*ARGSUSED*/ static void RefreshCallback(widget, closure, call_data) Widget widget; caddr_t closure, call_data; { PlotPart *plot = &((PlotWidget)closure)->plot; /* ||| XPixFill plot->plotWin to BlackPixel would give a nice effect */ XClearWindow(dpy, plot->plotwin); /* Clear here, since we won't do it on Redisplay any more */ plot->lastPtX = plot->lastPt1 = plot->lastPt2 = plot->lastPt3 = -1; Redisplay((Widget)closure, (XEvent *)NULL, (Region)NULL); } /*ARGSUSED*/ static Boolean SetValues(prev, request, new_widget) Widget prev, request, new_widget; { PlotPart *old = & ((PlotWidget)prev)->plot; PlotPart *new = & ((PlotWidget)new_widget)->plot; Arg arg[10]; int i; if (old->place3 != new->place3 || old->type3 != new->type3 || old->place2 != new->place2 || old->type2 != new->type2 || old->place1 != new->place1 || old->type1 != new->type1 || old->placeX != new->placeX || old->typeX != new->typeX) { } if (new->place1 > -1) { char buf[1024]; if (new->typeX == TIME) sprintf(buf, "#1: %s (%s) v. %s (%g %s)", new->label1, new->unit1, new->labelX, (double)new->paperSpeed, new->unitX); else sprintf(buf, "#1: %s (%s) v. %s (%s)", new->label1, new->unit1, new->labelX, new->unitX); i=0; XtSetArg(arg[i], XtNlabel, buf);i++; XtSetValues(new->label1w, arg, i); XtManageChild(new->label1w); } else { new->label1 = NULL; XtUnmanageChild(new->label1w); } if (new->place2 > -1) { char buf[1024]; if (new->typeX == TIME) sprintf(buf, "#2: %s (%s) v. %s (%g %s)", new->label2, new->unit2, new->labelX, (double)new->paperSpeed, new->unitX); else sprintf(buf, "#2: %s (%s) v. %s (%s)", new->label2, new->unit2, new->labelX, new->unitX); i=0; XtSetArg(arg[i], XtNlabel, buf);i++; XtSetValues(new->label2w, arg, i); XtManageChild(new->label2w); } else { new->label2 = NULL; XtUnmanageChild(new->label2w); } if (new->place3 > -1) { char buf[1024]; if (new->typeX == TIME) sprintf(buf, "#3: %s (%s) v. %s (%g %s)", new->label3, new->unit3, new->labelX, (double)new->paperSpeed, new->unitX); else sprintf(buf, "#3: %s (%s) v. %s (%s)", new->label3, new->unit3, new->labelX, new->unitX); i=0; XtSetArg(arg[i], XtNlabel, buf);i++; XtSetValues(new->label3w, arg, i); XtManageChild(new->label3w); } else { new->label3 = NULL; XtUnmanageChild(new->label3w); } new->lastPtX = new->lastPt1 = new->lastPt2 = new->lastPt3 = -1; new->nextHash = 0; if (new->stripMode != old->stripMode) { new->slideDots = 1; new->outX = new->out1 = new->out2 = new->out3 = 0; new->lastX = new->last1 = new->last2 = new->last3 = 0.; } return(True); /* always redisplay */ } /*ARGSUSED*/ static XtGeometryResult GeometryManager(w, request, geometry_return) Widget w; XtWidgetGeometry *request; XtWidgetGeometry *geometry_return; { return(XtGeometryYes); /* always allow subwidgets to resize themselves */ } /**************************************************************** * * Public Procedures * ****************************************************************/ /*ARGSUSED*/ void PlotModifySimulation(w, place, type, value) Widget w; int place, type; double value; { /* No longer used. */ #ifdef DEBUG printf("Setting p:%d, t:%d, v:%lf\n",place,type,value); #endif } /* * * Plot New Data event handler * Could stand to be cleaned up for execution speed! */ /*PUBLIC*/ void PlotData(w, pressures) Widget w; double *pressures; { register PlotPart *plot = &((PlotWidget)w)->plot; double x, y1 = 0.0, y2 = 0.0, y3 = 0.0; register int xPt, yPt; double slideInc; /* in seconds */ #ifdef SAVEDATA double eraseTime; #endif GC gc1 = plotClassRec.plot_class.gc1, gc2 = plotClassRec.plot_class.gc2, gc3 = plotClassRec.plot_class.gc3; if (!plot->plotpix) /* Not yet realized. */ return; if (!UpdatePeriod(pressures, &plot->timing) && ((plot->timing&0x1800) != 0x1800)) { if (plot->timing & 0xf0 && !(plot->timing&0x800)) plot->lastPt1 = plot->lastPt2 = plot->lastPt3 = -1; return; /* Only plot interesting parts of the cycle */ } if (plot->timing & 0x0800) {/* Plotting average instead of direct value */ plot->cntX++; plot->cnt1++; plot->cnt2++; plot->cnt3++; if (plot->placeX > -1) plot->sumX += getval(pressures, plot->placeX, plot->typeX); if (plot->place1 > -1) plot->sum1 += getval(pressures, plot->place1, plot->type1); if (plot->place2 > -1) plot->sum2 += getval(pressures, plot->place2, plot->type2); if (plot->place3 > -1) plot->sum3 += getval(pressures, plot->place3, plot->type3); if (plot->timing & 0x1000) { /* Start of beat, so plot it */ plot->sumX /= plot->cntX; plot->cntX = 0; plot->sum1 /= plot->cnt1; plot->cnt1 = 0; plot->sum2 /= plot->cnt2; plot->cnt2 = 0; plot->sum3 /= plot->cnt3; plot->cnt3 = 0; } else return; /* Wait until the start of the next beat. */ } if (plot->typeX != TIME && (plot->timing & 0x0800)) { x=plot->sumX; plot->sumX=0.0; } else x=getval(pressures, plot->placeX, plot->typeX); if (plot->typeX == TIME && plot->currentTime > x) plot->nextHash = plot->currentTime; plot->currentTime = getval(pressures, SYSTEM, TIME); if (plot->typeX == TIME) { if (plot->lastPtX < 0) { /* Redefine the X axis */ plot->maxX = x; plot->sfX = plot->paperSpeed * RESOLUTION; plot->minX = plot->maxX - plot->plotWidth / plot->sfX; Redisplay(w, (XEvent *)NULL, (Region)NULL); } if (x >= plot->maxX) { /* Off right edge of plot */ if (plot->stripMode) { /* Strip Chart mode: slide image to left by slideDots dots */ int diff; slideInc = plot->slideDots / plot->sfX; while ((diff= plot->sfX*(x - plot->minX) - (plot->plotWidth+plot->slideDots)) > 0) { /* make sure x will be visible */ plot->slideDots+= diff; slideInc = plot->slideDots / plot->sfX; } plot->minX += slideInc; plot->maxX += slideInc; plot->lastPtX -= plot->slideDots; XCopyArea(dpy, plot->plotpix, plot->plotpix, gc1, plot->slideDots, 0, plot->plotWidth - plot->slideDots, plot->plotHeight, 0, 0); #ifdef PIXDRAW XFillRectangle(dpy, plot->plotpix, whiteGC, plot->plotWidth - plot->slideDots, 0, plot->slideDots, plot->plotHeight /*full height*/); #else XClearArea(dpy,(Window)plot->plotpix, plot->plotWidth-plot->slideDots, 0, plot->slideDots, 0 /* full height */ , False); #endif #ifdef NOTDEF /*|||??? do we need? Some implementations write new before copying */ XFlush(dpy); #endif } else /* Not stripMode */ { /* Oscilloscope scan mode: restart at beginning of plot */ slideInc = plot->maxX - plot->minX; plot->minX += slideInc; plot->maxX += slideInc; plot->lastPt1 = plot->lastPt2 = plot->lastPt3 = -1; } } } #ifdef SAVEDATA eraseTime = plot->currentTime - plot->decayTime; if (plot->decayTime == 0.0) eraseTime = 0.0; /* never erase */ #endif xPt = plot->sfX * (x - plot->minX); if (plot->typeX == TIME && !plot->stripMode) { /* Erase a band of old data just in front of the current data */ if (plot->lastPtX > xPt) { #ifdef PIXDRAW XFillRectangle(dpy,plot->plotpix, whiteGC, 0, 0, xPt+6, plot->plotHeight); #else XClearArea(dpy, (Window) plot->plotpix, 0, 0, xPt + 6, 0, False); #endif if (plot->lastPtX > 0) plot->lastPtX = 0; /* start over from left side of screen */ else plot->lastPtX = xPt; /* but not if we aren't going to be there */ } else { if (xPt != plot->lastPtX) { #ifdef PIXDRAW XFillRectangle(dpy, plot->plotpix, whiteGC, plot->lastPtX+6, 0, plot->slideDots, plot->plotHeight); #else XClearArea(dpy, (Window) plot->plotpix, plot->lastPtX+6, 0, xPt-plot->lastPtX, 0 /*entire height*/ ,False); #endif } } } if (plot->lastPtX < 0) plot->lastPtX = xPt; if (plot->place1 > -1) { if (plot->timing&0x0800) { y1 = plot->sum1; plot->sum1 = 0.0; } else y1 = getval(pressures, plot->place1, plot->type1); yPt = plot->sf1 * (y1 - plot->max1); if (plot->lastPtX != xPt || plot->lastPt1 != yPt) { if (plot->lastPt1 == -1) { plot->lastPt1 = yPt; plot->lastPtX = xPt; } XDrawLine(dpy, plot->plotpix, gc1, plot->lastPtX,plot->lastPt1,xPt, yPt); plot->lastPt1 = yPt; #ifdef MOVINGBALL moveBall(plot->plotpix, plot->lastPtX, plot->lastPt1, xPt, yPt); #endif } } if (plot->place2 > -1) { if (plot->timing&0x0800) { y2 = plot->sum2; plot->sum2 = 0.0; } else y2 = getval(pressures, plot->place2, plot->type2); yPt = plot->sf2 * (y2 - plot->max2); if (plot->lastPtX != xPt || plot->lastPt2 != yPt) { if (plot->lastPt2 == -1) { plot->lastPt2 = yPt; plot->lastPtX = xPt; } if ( abs(xPt - plot->lastPtX) > abs(yPt - plot->lastPt2)) plot->phase2 = (plot->phase2 + abs(xPt - plot->lastPtX)) % 4; else plot->phase2 = (plot->phase2 + abs(yPt - plot->lastPt2)) % 4; XSetDashes(dpy, gc2, plot->phase2, Dash2, strlen(Dash2)); XDrawLine(dpy,plot->plotpix, gc2, plot->lastPtX,plot->lastPt2, xPt, yPt); plot->lastPt2 = yPt; #ifdef MOVINGBALL moveBall(plot->plotpix, plot->lastPtX, plot->lastPt2, xPt, yPt); #endif } } if (plot->place3 > -1) { if (plot->timing&0x0800) { y3 = plot->sum3; plot->sum3 = 0.0; } else y3 = getval(pressures, plot->place3, plot->type3); yPt = plot->sf3 * (y3 - plot->max3); if (plot->lastPtX != xPt || plot->lastPt3 != yPt) { if (plot->lastPt3 == -1) { plot->lastPt3 = yPt; plot->lastPtX = xPt; } if ( abs(xPt - plot->lastPtX) > abs(yPt - plot->lastPt3)) plot->phase3 = (plot->phase3 + abs(xPt - plot->lastPtX)) % 8; else plot->phase3 = (plot->phase3 + abs(yPt - plot->lastPt3)) % 8; XSetDashes(dpy, gc3, plot->phase3, Dash3, strlen(Dash3)); XDrawLine(dpy,plot->plotpix, gc3, plot->lastPtX,plot->lastPt3, xPt, yPt); plot->lastPt3 = yPt; #ifdef MOVINGBALL moveBall(plot->plotpix, plot->lastPtX, plot->lastPt3, xPt, yPt); #endif } } plot->lastPtX = xPt; if (plot->typeX == TIME && plot->nextHash + 0.1 <= x) { xPt = plot->sfX*(plot->nextHash - plot->minX); yPt = plot->plotHeight; XDrawLine(dpy, plot->plotpix, plotClassRec.plot_class.pointerGC, xPt, yPt, xPt, yPt-3); plot->nextHash += 1; } if (plot->autoX) { switch (plot->outX) { case 0: if (x > plot->maxX) plot->outX = 1; else if (x < plot->minX) plot->outX = -1; break; case -1: if (plot->lastX < y1) { plot->minX = plot->lastX - (plot->maxX-plot->lastX) * .1; plot->outX = 0; Redisplay(w, (XEvent *)NULL, (Region)NULL); } break; case 1: if (plot->lastX > y1) { plot->maxX = plot->lastX + (plot->lastX-plot->minX) * .1; plot->outX = 0; Redisplay(w, (XEvent *)NULL, (Region)NULL); } break; } plot->lastX = x; } /* * Check for need to automatically rescale */ if (plot->auto1) { switch (plot->out1) { case 0: if (y1 > plot->max1) plot->out1 = 1; else if (y1 < plot->min1) plot->out1 = -1; break; case -1: if (plot->last1 < y1) { plot->min1 = plot->last1 - (plot->max1-plot->last1) * .1; plot->out1 = 0; Redisplay(w, (XEvent *)NULL, (Region)NULL); } break; case 1: if (plot->last1 > y1) { plot->max1 = plot->last1 + (plot->last1-plot->min1) * .1; plot->out1 = 0; Redisplay(w, (XEvent *)NULL, (Region)NULL); } break; } plot->last1 = y1; } if (plot->auto2) { switch (plot->out2) { case 0: if (y2 > plot->max2) plot->out2 = 1; else if (y2 < plot->min2) plot->out2 = -1; break; case -1: if (plot->last2 < y2) { plot->min2 = plot->last2 - (plot->max2-plot->last2) * .1; plot->out2 = 0; Redisplay(w, (XEvent *)NULL, (Region)NULL); } break; case 1: if (plot->last2 > y2) { plot->max2 = plot->last2 + (plot->last2-plot->min2) * .1; plot->out2 = 0; Redisplay(w, (XEvent *)NULL, (Region)NULL); } break; } plot->last2 = y2; } if (plot->auto3) { switch (plot->out3) { case 0: if (y3 > plot->max3) plot->out3 = 1; else if (y3 < plot->min3) plot->out3 = -1; break; case -1: if (plot->last3 < y3) { plot->min3 = plot->last3 - (plot->max3-plot->last3) * .1; plot->out3 = 0; Redisplay(w, (XEvent *)NULL, (Region)NULL); } break; case 1: if (plot->last3 > y3) { plot->max3 = plot->last3 + (plot->last3-plot->min3) * .1; plot->out3 = 0; Redisplay(w, (XEvent *)NULL, (Region)NULL); } break; } plot->last3 = y3; } #ifdef PIXDRAW XClearWindow(dpy, plot->plotwin); #endif } /* END of PlotData() */