/************************************************************************** * * * Copyright (C) 1989, Silicon Graphics, Inc. * * * * These coded instructions, statements, and computer programs contain * * unpublished proprietary information of Silicon Graphics, Inc., and * * are protected by Federal copyright law. They may not be disclosed * * to third parties or copied or duplicated in any form, in whole or * * in part, without the prior written consent of Silicon Graphics, Inc. * * * **************************************************************************/ /* * 3D moving color bar module. * J. M. Barton 03/19/88 */ # include # include # include # include # include # include # include # include # include # include "grosview.h" /* * DEFXPERY basically defined how long a bar is */ # define DEFXPERY 9.5 /* aspect ratio */ # define PNTINCH 72.0 /* points per inch */ # define DEFFONT "TimesBoldItalic" # define J_LEFT 0 /* left justify number */ # define J_RIGHT 1 /* right justify number */ # define XSHRINK ((float)0.97) /* to provide border around bar */ # define YBAROFF ((float)0.05) /* bottom of slot to bar bottom */ # define YBARSCALE ((float)0.55) /* size of bar space */ /* * define text base above base of bar * An entire bar is 1.0 units high - so we center the baseline of the * text in the space remaining after the real bar portion */ # define YTEXTBASE ((((1.0 - (YBAROFF + YBARSCALE)) - cheight)/2) + (YBAROFF+YBARSCALE)) # define CHEIGHT 0.25 /* size of a canonical character */ # define BORDER (YBARSCALE*0.2) /* size of bar and window border */ # define YBARMID (YBAROFF+(YBARSCALE/2)) # define YBARTOP (YBAROFF+YBARSCALE) # define YBARBOT (YBAROFF) # define MINYSIZE 45 /* minimum pixels in Y */ # define MINXSIZE ((long)(DEFXPERY*MINYSIZE))/* minimum pixels in X */ # define NDIVS 10 /* number of ticks in bar */ # define YSIZE ((nbars + (width - 1)) / width) # define AVGSTR " Avg " # define TICKPER 0.2 # define GETCPOS(x,y) getcpos(x, y) # define scrmask(r,l,b,t) # define TALPHA "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890" Colorindex frontcolor = BLACK; Colorindex backcolor = DEFBACKCOLOR; Colorindex palecolor = DEFPALECOLOR; char *fontface; int arbsize = 0; int width = 1; static colorblock_t *bars[MAXBARS]; static int nbars = 0; static int windes; static char doublebuffered = 0; static char *tfont; static fmfonthandle rtfont; static fmfontinfo rtfontinfo; static int inredraw = 0; static float cheight = CHEIGHT; static float bheight = 0.0; /* baseline height */ static fmfonthandle deffontfh; /* handle for basic dft font */ /* * Window screen coordinates for other modules to use. */ static long winposx; /* origin in x */ static long winposy; /* origin in y */ static long winsizex; /* size in x */ static long winsizey; /* size in y */ static int newwin = 1; /* * Sliding scale to use when autosizing the bar under varying conditions. */ # define MAXSCALE 9 static struct { int disval; int decay; /* scale decay (in units) */ int advance; /* advance hysterisis (in units) */ } sscale[MAXSCALE] = { 10, 20, 5, 50, 75, 5, 100, 100, 5, 200, 70, 10, 1000, 50, 10, 5000, 50, 15, 20000, 50, 15, 100000, 50, 20, 500000, 50, 30, }; static void cbwinsetup(int); static void cbredrawstrip(colorblock_t *); static void cbredraw1(colorblock_t *); static void cbstredraw(colorblock_t *); static void cbstquickdraw(colorblock_t *); static void cbdrawmax(colorblock_t *); static void cbredrawnum(colorblock_t *); static void cbonebar(colorblock_t *); static void cbnumdraw(colorblock_t *, int, int, float); static void barlimit(colorblock_t *, double *); static void barclamp(colorblock_t *, double *); static void barmax(colorblock_t *, double *); static void cbstalloc(colorblock_t *, int); static int cbydist(Screencoord, Screencoord); # define doscrmask(x) \ scrmask((x)->cb_scrmask[0], (x)->cb_scrmask[1], \ (x)->cb_scrmask[2], (x)->cb_scrmask[3]); # define _drawlimit(cbp) \ cbnumdraw(cbp, 2, J_RIGHT, cbp->cb_tlimit) # define _drawmax(cbp) \ cbnumdraw(cbp, 1, J_RIGHT, cbp->cb_dispmax) # define _drawsum(cbp) \ cbnumdraw(cbp, 0, J_RIGHT, cbp->cb_lastavg1) # define halfpix(tp) (tp->cb_ftwid/2) void rtbar_text_start(void) {} /* beginning of text to lock down */ void FMPRSTR(char *s) { if (nofont) return; else fmprstr(s); } static char * displaynum(float pstr, int scale) { static char tstr[10]; char *str = tstr; if (pstr < 0) { *str++ = '-'; pstr = fabs(pstr); } if (scale) { if (pstr < 1024) sprintf(str, "%.1fm", pstr); else sprintf(str, "%.2fg", pstr/1024.0); } else if (pstr < 64*1024) sprintf(str, "%d", (int) pstr); else if (pstr < 1024*1024) sprintf(str, "%.1fk", pstr / 1024.0); else if (pstr < 1024*1024*1024) sprintf(str, "%.1fm", pstr / (1024.0*1024)); else sprintf(str, "%.2fg", pstr / (1024.0*1024*1024)); return(str); } float masksum(colorblock_t *cbp, float *data) { int i; float rval; rval = 0; for (i = 0; i < cbp->cb_nsects; i++) { if (!(mask(i)&cbp->cb_nmask)) rval += data[i]; } return(rval); } float unmasksum(colorblock_t *cbp, float *data) { int i; float rval; rval = 0; for (i = 0; i < cbp->cb_nsects; i++) { rval += data[i]; } return(rval); } /* * Swap the buffers. */ void cbswapbuf(void) { if (doublebuffered) { swapbuffers(); cbredraw(); } } /* * Redraw the bars and names. */ void cbredraw(void) { colorblock_t *tp; inredraw = 1; tp = nextbar(1); while (tp != 0) { if (tp->cb_type == T_NUM) tp->cb_flags |= TF_REMAX; cbredraw1(tp); tp = nextbar(0); } inredraw = 0; } /* * cbdrawlegend - draw the character string legend for a particular * bar. */ void cbdrawlegend(colorblock_t *tp) { int j; color(backcolor); rectf(tp->cb_cposx, tp->cb_cposy, tp->cb_cposx + tp->cb_strwid, tp->cb_cposy + (rtfontinfo.ysize * tp->cb_ftsywid)); color(frontcolor); cmov2(tp->cb_cposx, tp->cb_cposy + (rtfontinfo.yorig * tp->cb_ftsywid)); FMPRSTR(tp->cb_header); for (j = 0; j < tp->cb_nsects; j++) { color(tp->cb_colors[j]); FMPRSTR(tp->cb_legend[j]); } if (tp->cb_type == T_NUM) { if (tp->cb_flags & TF_AVERAGE) { color(frontcolor); FMPRSTR(AVGSTR); } } cbdrawabs(tp); } /* * cbdraw - draw the given color bar at the given place in the given * colors. calls the clamping function if necessary. */ void cbdraw(colorblock_t *cbp, ...) { va_list ap; double nvals[MAXSECTS]; int i; #ifdef JEFF logcbdraw(&va_alist); #endif if (cbp->cb_flags & TF_ONETRIP) { cbp->cb_flags &= ~TF_ONETRIP; return; } va_start(ap, cbp); for (i = 0; i < cbp->cb_nsects; i++) { nvals[i] = va_arg(ap, double); } va_end(ap); switch (cbp->cb_type) { case T_SABS: case T_ABS: barlimit(cbp, nvals); break; case T_SREL: case T_REL: barclamp(cbp, nvals); break; case T_NUM: barmax(cbp, nvals); break; } cbredraw1(cbp); } /* * cbdrawabs - draw the limit value for a bar. */ void cbdrawabs(colorblock_t *tp) { if (tp->cb_type == T_ABS || tp->cb_type == T_SABS) _drawlimit(tp); } /* * cbredraw1 - redraw the bar as passed in. */ static void cbredraw1(colorblock_t *cbp) { int i; float offset; float psize; if (cbp->cb_mess) { cbmessage(cbp, cbp->cb_mess); return; } if (cbp->cb_type == T_SABS || cbp->cb_type == T_SREL) cbredrawstrip(cbp); else if (cbp->cb_type == T_NUM) cbredrawnum(cbp); else { pushmatrix(); loadmatrix(cbp->cb_matrix); offset = 0; for (i = 0; i < cbp->cb_nsects; i++) { psize = cbp->cb_aspect * cbp->cb_last[i]; color(cbp->cb_colors[i]); rectf(offset, 0.0, offset + psize, 1.0); offset += psize; } popmatrix(); } if ((cbp->cb_type == T_ABS || cbp->cb_type == T_SABS)) if (cbp->cb_flags & (TF_MAX|TF_AVERAGE)) { cbdrawmax(cbp); } } static void cbredrawstrip(colorblock_t *cbp) { int i; strip_t *sp = cbp->cb_stp; float *fp; sp->st_cursamp = (sp->st_cursamp + 1) % sp->st_nsamples; fp = sp->st_samples + (sp->st_cursamp * cbp->cb_nsects); for (i = 0; i < cbp->cb_nsects; i++, fp++) *fp = cbp->cb_last[i]; pushmatrix(); loadmatrix(cbp->cb_matrix); if (inredraw || (cbp->cb_flags & TF_STRDR)) cbstredraw(cbp); else cbstquickdraw(cbp); popmatrix(); } static void cbstredraw(colorblock_t *cbp) { int i; int j; strip_t *sp = cbp->cb_stp; float offset; float incr; float height; int start; float *wf; int whereseen = 0; int toff; int btoff; incr = cbp->cb_aspect / sp->st_nsamples; offset = 0; cbp->cb_pixcross = 0; if ((cbp->cb_flags & TF_WHERE) && sp->st_abswhere == sp->st_cursamp) cbp->cb_flags &= ~TF_WHERE; if (cbp->cb_flags & TF_TICK) { toff = cbp->cb_tick - sp->st_tickcnt; btoff = cbp->cb_bigtick - sp->st_bigtickcnt; } start = (sp->st_cursamp + 1) % sp->st_nsamples; for (i = start; ; i = (i + 1) % sp->st_nsamples) { height = 0; wf = sp->st_samples + (i * cbp->cb_nsects); for (j = 0; j < cbp->cb_nsects; j++) { if (((cbp->cb_flags & TF_WHERE) && !whereseen) && j != cbp->cb_nsects - 1) color(palecolor); else color(cbp->cb_colors[j]); rectf(offset, height, offset + incr, (height + *wf)); height += *wf++; } if ((cbp->cb_flags & TF_WHERE) && sp->st_abswhere == i) { color(RED); move(offset+incr-cbp->cb_ftxwid, 0.0, 0.0); draw(offset+incr-cbp->cb_ftxwid, 1.0, 0.0); whereseen++; } if (cbp->cb_flags & TF_TICK) { float npix; toff--; if (toff < 0) { toff = cbp->cb_tick; if (cbp->cb_flags & TF_BIGTICK) { btoff--; if (btoff < 0) { btoff = cbp->cb_bigtick; npix = TICKPER*2; } else npix = TICKPER; } else npix = TICKPER; color(frontcolor); move(offset, 1.0, 0.0); draw(offset, 1.0-npix, 0.0); } } offset += incr; if (i == sp->st_cursamp) break; } } static void cbstquickdraw(colorblock_t *cbp) { strip_t *sp = cbp->cb_stp; float incr; float *wf; float fx; float height; float npix; int i; if (cbp->cb_flags & TF_SCHANGE) { cbp->cb_flags &= ~TF_SCHANGE; cbstredraw(cbp); return; } incr = cbp->cb_aspect/sp->st_nsamples; wf = sp->st_samples+(sp->st_cursamp*cbp->cb_nsects); fx = cbp->cb_aspect - incr; if (incr < cbp->cb_ftwid) { cbp->cb_pixcross += incr; if (cbp->cb_pixcross >= cbp->cb_ftwid) { float tavg; float nht; float nfx; for (tavg = 0, i = 0; i < cbp->cb_nsects; i++) tavg += sp->st_avg[i]/sp->st_avgcnt; tavg = 1.0 / tavg; height = 0; nfx = cbp->cb_aspect - cbp->cb_ftwid; for (i = 0; i < cbp->cb_nsects; i++) { color(cbp->cb_colors[i]); nht = (sp->st_avg[i]/sp->st_avgcnt)*tavg; rectf(nfx, height, nfx + cbp->cb_ftwid, (height + nht)); height += nht; sp->st_avg[i] = 0; } sp->st_avgcnt = 0; if (sp->st_forcetick) { float nfx; if (sp->st_forcetick == 1) npix = TICKPER; else npix = TICKPER*2; sp->st_forcetick = 0; nfx = cbp->cb_aspect - cbp->cb_ftwid; color(frontcolor); move(nfx, 1.0, 0.0); draw(nfx, 1.0-npix, 0.0); } rectcopy(sp->st_irect[0], sp->st_irect[1], sp->st_irect[2], sp->st_irect[3], sp->st_fpos[0], sp->st_fpos[1]); cbp->cb_pixcross = 0; } else { float *twf = wf; for (i = 0; i < cbp->cb_nsects; i++) sp->st_avg[i] += *twf++; sp->st_avgcnt++; } } else { rectcopy(sp->st_irect[0], sp->st_irect[1], sp->st_irect[2], sp->st_irect[3], sp->st_fpos[0], sp->st_fpos[1]); } height = 0; for (i = 0; i < cbp->cb_nsects; i++) { color(cbp->cb_colors[i]); rectf(fx, height, fx + incr, (height + *wf)); height += *wf++; } if (cbp->cb_flags & TF_TICK) { sp->st_tickcnt++; if (sp->st_tickcnt >= cbp->cb_tick) { if (incr < cbp->cb_ftwid) sp->st_forcetick = 1; if (cbp->cb_flags & TF_BIGTICK) { sp->st_bigtickcnt++; if (sp->st_bigtickcnt >= cbp->cb_bigtick) { npix = TICKPER*2; sp->st_bigtickcnt = 0; if (incr < cbp->cb_ftwid) sp->st_forcetick = 2; } else npix = TICKPER; } else npix = TICKPER; sp->st_tickcnt = 0; if (!sp->st_forcetick) { color(frontcolor); move(fx, 1.0, 0.0); draw(fx, 1.0-npix, 0.0); } } } } static void cbdrawmax(colorblock_t *cbp) { if (cbp->cb_flags & TF_MAX) { if (!inredraw) { if (cbp->cb_max1 == 0) goto checksum; if (!(cbp->cb_flags & TF_REMAX)) goto checksum; } _drawmax(cbp); } checksum: if (cbp->cb_flags & TF_AVERAGE) { if (!inredraw) { if (cbp->cb_lastavg1 == cbp->cb_dispavg1) return; cbp->cb_lastavg1 = cbp->cb_dispavg1; } _drawsum(cbp); } } static void cbredrawnum(colorblock_t *cbp) { char *dbuf; float swid; int i; float ysc; float sum; int rv; if (!(cbp->cb_flags & TF_NUMDISP)) if (!inredraw) goto checkmax; cbp->cb_flags &= ~TF_NUMDISP; color(backcolor); swid = cbp->cb_hdstart; rectf(swid, cbp->cb_cposmid+halfpix(cbp), cbp->cb_cposx+cbp->cb_hdlen, cbp->cb_cpostop); color(frontcolor); swid += rtfontinfo.xorig * cbp->cb_ftwid; ysc = cbp->cb_cposmid + (rtfontinfo.yorig*cbp->cb_ftsywid); sum = 0; for (i = 0; i < cbp->cb_nsects - 1; i++) { cmov2(swid, ysc); dbuf = displaynum(cbp->cb_results[i], 0); sum += cbp->cb_avg[i]; swid += (fmgetstrwidth(rtfont, cbp->cb_legend[i])* cbp->cb_ftwid); color(cbp->cb_colors[i]); FMPRSTR(dbuf); } if ((rv = strlen(cbp->cb_legend[cbp->cb_nsects-1])) != 0) { cmov2(swid, ysc); dbuf = displaynum(cbp->cb_results[cbp->cb_nsects-1], 0); color(cbp->cb_colors[i]); FMPRSTR(dbuf); } if (cbp->cb_flags & TF_AVERAGE) { if (rv) { swid += (fmgetstrwidth(rtfont, cbp->cb_legend[cbp->cb_nsects-1])* cbp->cb_ftwid); } else sum -= cbp->cb_avg[cbp->cb_nsects-2]; if (cbp->cb_flags & TF_MBSCALE) dbuf = displaynum(sum, 1); else dbuf = displaynum(sum, 0); strcat(dbuf, " "); swid = swid + fmgetstrwidth(rtfont, AVGSTR)*cbp->cb_ftwid - fmgetstrwidth(rtfont, dbuf)*cbp->cb_ftwid; cmov2(swid, ysc); color(frontcolor); FMPRSTR(dbuf); } checkmax: if (cbp->cb_flags & TF_MAX) { if (!(cbp->cb_flags & TF_REMAX)) if (!inredraw) return; cbp->cb_flags &= ~TF_REMAX; swid = cbp->cb_hdstart; color(backcolor); rectf(swid, cbp->cb_cposbot, cbp->cb_cposx+cbp->cb_hdlen, cbp->cb_cposmid-halfpix(cbp)); ysc = cbp->cb_cposbot + (rtfontinfo.yorig * cbp->cb_ftsywid); swid += rtfontinfo.xorig * cbp->cb_ftwid; sum = 0; color(cbp->cb_maxcol.front); for (i = 0; i < cbp->cb_nsects - 1; i++) { cmov2(swid, ysc); dbuf = displaynum(cbp->cb_max[i], 0); sum += cbp->cb_max[i]; swid += (fmgetstrwidth(rtfont, cbp->cb_legend[i])* cbp->cb_ftwid); FMPRSTR(dbuf); } if ((rv = strlen(cbp->cb_legend[cbp->cb_nsects-1])) != 0) { cmov2(swid, ysc); dbuf = displaynum(cbp->cb_max[i], 0); FMPRSTR(dbuf); } if (cbp->cb_flags & TF_AVERAGE) { if (rv) { swid += (fmgetstrwidth(rtfont, cbp->cb_legend[cbp->cb_nsects-1])* cbp->cb_ftwid); } else sum -= cbp->cb_avg[cbp->cb_nsects-2]; if (cbp->cb_flags & TF_MBSCALE) dbuf = displaynum(sum, 1); else dbuf = displaynum(sum, 0); strcat(dbuf, " "); swid = swid + fmgetstrwidth(rtfont, AVGSTR)*cbp->cb_ftwid - fmgetstrwidth(rtfont, dbuf)*cbp->cb_ftwid; cmov2(swid, ysc); FMPRSTR(dbuf); } } } /* * Put a message in the window for the user. */ void cbmessage(colorblock_t *cbp, char *t) { if (t == 0) { cbredraw1(cbp); cbp->cb_mess = 0; } pushmatrix(); loadmatrix(cbp->cb_matrix); color(WHITE); rectf(0.0, 0.0, cbp->cb_aspect, 1.0); cmov2(0.1, 0.3); color(RED); FMPRSTR(t); popmatrix(); cbp->cb_mess = t; } static void cbnumdraw(colorblock_t *cbp, int which, int just, float pstr) { float y; int back; int front; long swid; float xcoff; float ycoff; char *str; int scale = 0; if (cbp->cb_flags & TF_MBSCALE) scale = 1; switch (which) { case 0: y = cbp->cb_cposbot; back = cbp->cb_sumcol.back; front = cbp->cb_sumcol.front; break; case 1: y = cbp->cb_cposmid; back = cbp->cb_maxcol.back; front = cbp->cb_maxcol.front; break; case 2: y = cbp->cb_cposy; if (cbp->cb_flags & TF_NOSCALE) { front = cbp->cb_limcol.back; back = cbp->cb_limcol.front; } else { back = cbp->cb_limcol.back; front = cbp->cb_limcol.front; } break; } str = displaynum(pstr, scale); xcoff = (rtfontinfo.xorig * cbp->cb_ftwid); ycoff = (rtfontinfo.yorig * cbp->cb_ftsywid); color(back); rectf(cbp->cb_cmaxx, y, cbp->cb_cmaxx+cbp->cb_cmwid, y+cheight); color(front); swid = fmgetstrwidth(rtfont, str); switch (just) { case J_LEFT: cmov2(cbp->cb_cmaxx+xcoff, y+ycoff); FMPRSTR(str); break; case J_RIGHT: cmov2((cbp->cb_cmaxx+cbp->cb_cmwid)-(swid*cbp->cb_ftwid), y+ycoff); FMPRSTR(str); break; } if ((which == 2) && (cbp->cb_flags & TF_EXCEED)) { color(RED); rect(cbp->cb_cmaxx, y, cbp->cb_cmaxx+cbp->cb_cmwid, y+cheight); color(RED); } } /* * Normalize a set of data points. */ static void normalize(int size, float norm, float *array) { int i; if (norm <= 0) norm = 1.0; else norm = 1.0 / norm; for (i = 0; i < size; i++) array[i] *= norm; } static int rollcopy(int size, float scale, float *old, double *new) { int i; int ifc = 0; float tmp; for (i = 0; i < size; i++) { tmp = fabs((scale*new[i])+((1-scale)*old[i])); if (tmp != old[i]) { ifc++; old[i] = tmp; } } return(ifc); } static int frollcopy(int size, float scale, float *old, float *new) { int i; int ifc = 0; float tmp; for (i = 0; i < size; i++) { tmp = fabs((scale*new[i])+((1-scale)*old[i])); if (tmp != old[i]) { ifc++; old[i] = tmp; } } return(ifc); } /* * barclamp - clamp the values to get a smooth movement out of the bar. */ static void barclamp(colorblock_t *cbp, double *new) { (void) rollcopy(cbp->cb_nsects, cbp->cb_upmove, cbp->cb_results, new); (void) frollcopy(cbp->cb_nsects, 1.0, cbp->cb_last, cbp->cb_results); normalize(cbp->cb_nsects,unmasksum(cbp, cbp->cb_results), cbp->cb_last); } /* * barmax - keep maximum value information before calling * clamping function. */ static void barmax(colorblock_t *cbp, double *new) { int i; if (rollcopy(cbp->cb_nsects, 1.0, cbp->cb_results, new)) cbp->cb_flags |= TF_NUMDISP; if (cbp->cb_flags & TF_AVERAGE) { if (frollcopy(cbp->cb_nsects, 1.0/cbp->cb_avgtick, cbp->cb_avg, cbp->cb_results)) { cbp->cb_flags |= TF_REMAX; cbp->cb_flags |= TF_NUMDISP; } } if (cbp->cb_flags & TF_MAX) { if (cbp->cb_flags & TF_MAXRESET) { if (cbp->cb_maxcnt-- <= 0) { for (i = 0; i < cbp->cb_nsects; i++) cbp->cb_max[i] = 0; cbp->cb_maxcnt = cbp->cb_maxtick; cbp->cb_flags &= ~TF_MAXRESET; } } for (i = 0; i < cbp->cb_nsects; i++) { if (cbp->cb_results[i] > cbp->cb_max[i]) { cbp->cb_flags |= TF_REMAX; cbp->cb_max[i] = cbp->cb_results[i]; } else if (!(cbp->cb_flags & TF_MAXRESET)) { if (cbp->cb_maxtick <= 0) continue; if (cbp->cb_results[i] < cbp->cb_max[i]) { if (cbp->cb_amax[i]-- <= 0) { cbp->cb_max[i] = 0; cbp->cb_amax[i] = cbp->cb_maxtick; } } else cbp->cb_amax[i] = cbp->cb_maxtick; } } } } /* * barlimit - if a counter value exceeds the current maximum, re-scale. */ static void barlimit(colorblock_t *cbp, double *new) { int i; int tchg; double sum; double fullsum; float cmax; int nreal; float scaler; /* * Normalize the data with the new numbers. */ if (strlen(cbp->cb_legend[cbp->cb_nsects-1]) == 0) nreal = cbp->cb_nsects - 1; else nreal = cbp->cb_nsects; rollcopy(cbp->cb_nsects, cbp->cb_upmove, cbp->cb_results, new); if (cbp->cb_flags & TF_AVERAGE) { rollcopy(nreal,1.0/cbp->cb_avgtick,cbp->cb_avg, new); if (--cbp->cb_avgcnt <= 0) { cmax = masksum(cbp, cbp->cb_avg); cbp->cb_dispavg1 = cmax; cbp->cb_avgcnt = cbp->cb_avgtick; } } if (cbp->cb_flags & TF_MAX) { if (cbp->cb_flags & TF_MAXRESET) { rollcopy(nreal, 1.0, cbp->cb_max, new); cmax = masksum(cbp, cbp->cb_max); if (cbp->cb_maxcnt-- <= 0) { cbp->cb_max1 = cbp->cb_dispmax = 0; cbp->cb_flags &= ~TF_MAXRESET; } } else cmax = masksum(cbp, cbp->cb_results); if (cmax < 0) cmax = 0; if (cmax > cbp->cb_max1) cbp->cb_max1 = cmax; if (!(cbp->cb_flags & TF_MAXRESET) && cbp->cb_maxtick > 0) { if (cmax < cbp->cb_dispmax) { if (cbp->cb_maxcnt-- <= 0) { cbp->cb_maxcnt = cbp->cb_maxtick; cbp->cb_dispmax = cbp->cb_max1 = cmax; cbp->cb_flags |= TF_REMAX; } } else cbp->cb_maxcnt = cbp->cb_maxtick; } if (cbp->cb_dispmax < cbp->cb_max1) { cbp->cb_dispmax = cbp->cb_max1; cbp->cb_flags |= TF_REMAX; } } /* * If a scale change is required, do so. */ sum = masksum(cbp, cbp->cb_results); if (!(cbp->cb_flags & TF_NOSCALE)) { tchg = 0; if (cbp->cb_tlimit == 0 && sum <= cbp->cb_tlimit) { cbp->cb_tlimit = sscale[0].disval; cbdrawabs(cbp); tchg++; } else if (sum > cbp->cb_tlimit) { if (--cbp->cb_nabove <= 0) { for (i = 0; i < MAXSCALE - 1 && sscale[i].disval < sum; i++); cbp->cb_tlimit = sscale[i].disval; cbdrawabs(cbp); cbp->cb_nbelow = sscale[i].decay; cbp->cb_nabove = sscale[i].advance; cbp->cb_curscale = i; tchg++; } } else if (!(cbp->cb_flags & TF_CREEP)) { if (cbp->cb_nbelow <= 0) { for (i = 0; i < MAXSCALE - 1 && sscale[i].disval < sum; i++); if (i < cbp->cb_curscale) { cbp->cb_curscale--; cbp->cb_tlimit = sscale[cbp->cb_curscale].disval; cbp->cb_nabove = sscale[i].advance; cbdrawabs(cbp); tchg++; } cbp->cb_nbelow = sscale[cbp->cb_curscale].decay; } else cbp->cb_nbelow--; } if (tchg) { if (cbp->cb_stp) { cbp->cb_stp->st_abswhere = cbp->cb_stp->st_cursamp; cbp->cb_flags |= (TF_WHERE|TF_SCHANGE); } } } /* * Finally, scale the actual values to match the scale factor. */ fullsum = 0; for (i = 0; i < nreal; i++) fullsum += cbp->cb_last[i] = cbp->cb_results[i]; if (nreal == cbp->cb_nsects - 1) { if (fullsum > cbp->cb_tlimit) { scaler = fullsum; cbp->cb_last[nreal] = 0; } else { scaler = cbp->cb_tlimit; cbp->cb_last[nreal] = cbp->cb_tlimit - fullsum; } } else { fullsum += cbp->cb_last[cbp->cb_nsects] = cbp->cb_results[cbp->cb_nsects]; scaler = fullsum; } if (sum > (cbp->cb_tlimit+(cbp->cb_tlimit*0.01))) { if (!(cbp->cb_flags & TF_EXCEED)) { cbp->cb_flags |= TF_EXCEED; cbdrawabs(cbp); } } else if (cbp->cb_flags & TF_EXCEED) { cbp->cb_flags &= ~TF_EXCEED; cbdrawabs(cbp); } normalize(cbp->cb_nsects, scaler, cbp->cb_last); } void rtbar_text_end(void) {} /* end of text to lock down */ /* * cbinit - create and initialize a new color bar. all you get from this * call is a control block, not mapped to any screen position. the * caller is responsible for initializing the transformation matrix and * the color list. */ colorblock_t * cbinit(void) { colorblock_t *tp; if ((nbars % MAXBARS) == 0) bars[nbars/MAXBARS] = (colorblock_t *) calloc(sizeof(colorblock_t), MAXBARS); tp = bars[nbars/MAXBARS] + (nbars % MAXBARS); memset((void *) tp, 0, sizeof(*tp)); nbars++; tp->cb_valid = 1; tp->cb_rb = cbindex(tp); return(tp); } /* * cbindex - given a color bar pointer, return it's index */ int cbindex(colorblock_t *cbp) { int i; for (i = 0; i < MAXBARS; i++) if (((long)cbp >= (long)bars[i]) && ((long)cbp<((long)bars[i]+(MAXBARS*sizeof(colorblock_t))))) return((i*MAXBARS) + (cbp - bars[i])); return(-1); } /* * cbptr - given an index, return a pointer to a color bar */ colorblock_t * cbptr(int index) { return((bars[index/MAXBARS]) + (index % MAXBARS)); } /* * cbinitindex - initialize a color bar at a specific index */ colorblock_t * cbinitindex(int index) { colorblock_t *tp; if (bars[index/MAXBARS] == 0) bars[index/MAXBARS] = (colorblock_t *) calloc(sizeof(colorblock_t), MAXBARS); tp = (bars[index/MAXBARS]) + (index % MAXBARS); if (!tp->cb_valid) { memset((void *) tp, 0, sizeof(colorblock_t)); tp->cb_valid = 1; } nbars++; tp->cb_rb = index; return(tp); } colorblock_t * nextbar(int begin) { static int pos; colorblock_t *tp; if (begin) pos = 0; for (;;) { if (bars[pos/MAXBARS] == 0) return(0); tp = (bars[pos/MAXBARS]) + (pos % MAXBARS); pos++; if (tp->cb_valid) return(tp); } } /* * cbstalloc - allocate space for the strip chart information. */ static void cbstalloc(colorblock_t *cp, int n) { strip_t *sp; float *wf; int j; int i; if (cp->cb_stp == 0) { cp->cb_stp = sp = (strip_t *) calloc(1, sizeof(strip_t)); cp->cb_pixcross = 0; sp->st_nsamples = n; sp->st_cursamp = n - 1; sp->st_abswhere = 0; sp->st_samples = (float *) calloc(n, sizeof(float)*cp->cb_nsects); for (i = 0; i < n; i++) { wf = sp->st_samples + (i * cp->cb_nsects); for (j = 0; j < cp->cb_nsects - 1; j++) *wf++ = 0; *wf = 1.0; } } } /* * cblayout - once all bars are initialized, this function figures out * all the display parameters for the window and bars and sets * up the window. */ void cblayout(char *title) { static int fontinit = 0; int i; int j; colorblock_t *tp; Screencoord ix; Screencoord iy; Screencoord six; Screencoord siy; Coord ywhere; int wrow; Coord offset; int truncw = YSIZE; int wcfontht; static int old_wcfontht; /* * Open up the window. */ if (newwin) { newwin = 0; old_wcfontht = -9999; /* * First make sure we can find the font we wish to use. */ if (!fontinit) { fminit(); fontinit = 1; } if (fontface == 0) tfont = DEFFONT; else tfont = fontface; if ((rtfont = fmfindfont(tfont)) == 0) { fprintf(stderr, "%s: unable to find font \"%s\"\n", pname, tfont); exit(1); } if ((deffontfh = fmfindfont(DEFFONT)) == 0) { /* default font not found?? not a disaster */ deffontfh = fmfindfont(tfont); } /* * Now open up the window. */ if (!(title != 0 || do_border)) noborder(); if (debugger) foreground(); wrow = (int)(((float) nbars / width) + 0.99); /* * Now deal with what the user said in laying it out. * The rule is that arbsize is overruled by specifying * the origin or preferred size. */ if (prefpos) { if (prefwsize) prefposition(prefxpos, prefxpos + prefxsize, prefypos, prefypos + prefysize); else prefposition(prefxpos, prefxpos + (MINXSIZE * width), prefypos, prefypos + (MINYSIZE * wrow)); } else if (prefwsize) prefsize(prefxsize, prefysize); else if (!arbsize) { if (debug) fprintf(stderr, "width %d wrow %d aspect(%.2f,%d),size(%d,%d)\n", width, wrow, DEFXPERY*width, wrow, MINXSIZE*width, MINYSIZE*wrow); keepaspect((long)(DEFXPERY*100) * width, wrow * 100); minsize(MINXSIZE * width, MINYSIZE * wrow); } fflush(logfd); if (title != 0) windes = winopen(title); else windes = winopen("gr_osview"); qdevice(LEFTMOUSE); qdevice(WINQUIT); qdevice(WINSHUT); qdevice(WINFREEZE); qdevice(WINTHAW); qdevice(ESCKEY); shademodel(FLAT); } else { winset(windes); /* * If not the first pass, we will need to re-scale the * font, so discard what we had and start over. */ } /* * Shape the viewport and set up the base transformation. */ inredraw = 1; cbwinsetup(0); /* * Determine the font size to use. The first step of this is to * figure out where our window is on the screen. */ cmov2(0.0, 0.0); GETCPOS(&ix, &iy); cmov2(0.0, CHEIGHT); GETCPOS(&six, &siy); /* if (debug || printpos) printf("cp <%d,%d>, ch <%d,%d>, pixht <%d,%d>\n", ix, iy, six, siy, six - ix, siy - iy); */ wcfontht = cbydist(iy, siy); if (wcfontht <= 0) wcfontht = 1; { fmfonthandle nfont = NULL; double pntsz; fmfonthandle ndeffont = NULL; int alphasz; if (wcfontht!=old_wcfontht) { float pixinch; fmfreefont(rtfont); if ((rtfont = fmfindfont(tfont)) == 0) { fprintf(stderr, "%s: unable to find font \"%s\"\n", pname, tfont); exit(1); } pixinch = (float)getgdesc(GD_YPMAX)/ ((float)getgdesc(GD_YMMAX)*.03937); pntsz = (wcfontht / pixinch) * PNTINCH; if ((nfont = fmscalefont(rtfont, pntsz)) == 0) { fprintf(stderr, "%s: unable to scale font \"%s\" to point size %f\n", pname, tfont, pntsz); exit(1); } alphasz = fmgetstrwidth(nfont, TALPHA); if (strcmp(tfont, DEFFONT)) { int defalphasz; /* * Since all 'tuning' for fit is done on the default * font - we must tweak other fonts to fit */ if ((ndeffont = fmscalefont(deffontfh, pntsz)) == 0) { fprintf(stderr, "%s: unable to scale font \"%s\" to point size %f\n", pname, DEFFONT, pntsz); exit(1); } defalphasz = fmgetstrwidth(ndeffont, TALPHA); fmfreefont(ndeffont); if (debug) printf("pntsz %f defalphasz %d alphasz %d\n", pntsz, defalphasz, alphasz); while (alphasz > defalphasz) { pntsz -= 0.25; fmfreefont(nfont); if ((nfont = fmscalefont(rtfont, pntsz)) == 0) { fprintf(stderr, "%s: unable to scale font \"%s\" to pointsize %f\n", pname, tfont, pntsz); exit(1); } alphasz = fmgetstrwidth(nfont, TALPHA); if (debug) printf("pntsz %f defalphasz %d alphasz %d\n", pntsz, defalphasz, alphasz); } } fmgetfontinfo(nfont, &rtfontinfo); fmfreefont(rtfont); rtfont = nfont; old_wcfontht=wcfontht; /* * alter real cheight based on font we really got * oldC is to newC as old ht is to new ht */ cheight = ((float)rtfontinfo.ysize * CHEIGHT) / wcfontht; bheight = ((float)rtfontinfo.yorig * CHEIGHT) / wcfontht; if (debug) { printf("wcfontht %d f.ysize %d f.yorig %d cheight %.2f, bheight %.2f\n", wcfontht, rtfontinfo.ysize, rtfontinfo.yorig, cheight, bheight); printf("f.xsize %d f.xorig %d\n", rtfontinfo.xsize, rtfontinfo.xorig); } } } fmsetfont(rtfont); /* * Now layout the bars in it. */ wrow = 0; tp = nextbar(1); while (tp != 0) { /* * Set up the bar space. */ ywhere = (Coord) (truncw - 1) - wrow + YBAROFF; for (j = 0; j < width && tp != 0; j++, i++) { /* * Determine our position and set initial scaling. */ tp->cb_aspect = DEFXPERY; offset = (DEFXPERY * (1.0-XSHRINK)) / 2.0; tp->cb_xwhere = (DEFXPERY * j) + offset; tp->cb_ywhere = ywhere; pushmatrix(); translate(tp->cb_xwhere, tp->cb_ywhere, 0.0); scale(XSHRINK, 1.0, 1.0); cmov2(0.0, 0.0); GETCPOS(&tp->cb_scrmask[0], &tp->cb_scrmask[2]); cmov2(tp->cb_aspect, 1.0); GETCPOS(&tp->cb_scrmask[1], &tp->cb_scrmask[3]); getmatrix(tp->cb_matrix); popmatrix(); cbonebar(tp); if ((tp = nextbar(0)) == 0) break; } wrow++; } /* * Finalize the window. */ cbwinsetup(2); inredraw = 0; } /* * cbtakedown - destroy all window information and color bars. */ void cbtakedown(void) { nbars = 0; winclose(windes); newwin = 1; fmfreefont(rtfont); } static void cbonebar(colorblock_t *tp) { Screencoord ix; Screencoord iy; Screencoord six; Screencoord siy; float xwhere = tp->cb_xwhere; float ywhere = tp->cb_ywhere; int border; int j; float psize; long slen; strip_t *sp; float minx; static int maxscreenx = -1; /* * If text supression was requested, then also suppress the * max and average counters. For the scale, if it is locked, * set the foreground and background to the bar background, * making it invisible. */ if (nofont) { tp->cb_flags &= ~(TF_MAX|TF_AVERAGE); tp->cb_limcol.back = tp->cb_limcol.front = backcolor; } /* * Now set up the character drawing space. * Text is drawn in original world space, while bar drawing is * done in a custom coordinate system. */ tp->cb_strwid = tp->cb_aspect * XSHRINK; tp->cb_cposx = xwhere; tp->cb_cposy = ywhere + YTEXTBASE; tp->cb_cposz = 0.0; psize = (YBARSCALE - (2 * cheight)) / 3; tp->cb_cposbot = ywhere + psize; tp->cb_cposmid = tp->cb_cposbot + cheight + psize; tp->cb_cpostop = ywhere + YBARSCALE; /* * Setting this up is also affected by the brain-damaged rules * in the GL for reflective window coordinates. */ cmov2(tp->cb_cposx, 0.0); GETCPOS(&ix, &iy); cmov2(tp->cb_cposx + tp->cb_strwid, 1.0); GETCPOS(&six, &siy); tp->cb_twid = six - ix; tp->cb_ftwid = tp->cb_strwid / tp->cb_twid; tp->cb_ftsywid = 1.0 / cbydist(iy, siy); /* * Figure out the maximum length of the text string area. */ slen = fmgetstrwidth(rtfont, tp->cb_header); tp->cb_hdstart = tp->cb_cposx + (slen * tp->cb_ftwid); for (j = 0; j < tp->cb_nsects; j++) slen += fmgetstrwidth(rtfont, tp->cb_legend[j]); if (tp->cb_type == T_NUM && (tp->cb_flags & TF_AVERAGE)) slen += fmgetstrwidth(rtfont, AVGSTR); tp->cb_hdlen = (slen * tp->cb_ftwid); /* * Move drawing of bar to appropriate place. */ color(frontcolor); linewidth((short) 1); doscrmask(tp); /* * If an absolute limit display is in use, then calculate out * the character positions. */ if (tp->cb_type == T_ABS || tp->cb_type == T_SABS) { long swid; float xv; /* * If the bar contains the side fields, then draw a rectangle * around them now. */ swid = fmgetstrwidth(rtfont, "2222222"); tp->cb_cmaxx = tp->cb_cposx+tp->cb_strwid- (tp->cb_cmwid = (swid*tp->cb_ftwid)); /* rect(tp->cb_cmaxx-tp->cb_ftwid, tp->cb_ywhere-tp->cb_ftsywid, tp->cb_cposx+tp->cb_strwid+tp->cb_ftwid, tp->cb_cposy+cheight+(2*tp->cb_ftsywid)); */ pushmatrix(); loadmatrix(tp->cb_matrix); if (tp->cb_flags & (TF_MAX|TF_AVERAGE)) { /* * A max value display requires that the bar be * scaled down in size for room to display. */ xv = (tp->cb_strwid - tp->cb_cmwid) / tp->cb_aspect; scale(xv, YBARSCALE, 1.0); } else scale(1.0, YBARSCALE, 1.0); } else { pushmatrix(); loadmatrix(tp->cb_matrix); scale(1.0, YBARSCALE, 1.0); } /* * Determine if a border is desired and set the clamping * function. */ switch (tp->cb_type) { case T_SABS: if (tp->cb_flags & TF_NOBORD) border = 0; else border = 1; break; case T_SREL: if (tp->cb_flags & TF_NOBORD) border = 0; else border = 1; break; case T_NUM: border = 0; /* no lines at all */ popmatrix(); move(tp->cb_hdstart, tp->cb_cpostop + (2*tp->cb_ftsywid), 0.0); draw(tp->cb_cposx + tp->cb_hdlen, tp->cb_cpostop + (2*tp->cb_ftsywid), 0.0); cmov2(tp->cb_cposx, tp->cb_cposmid+(rtfontinfo.yorig*tp->cb_ftsywid)); color(frontcolor); FMPRSTR("Current"); if (tp->cb_flags & TF_MAX) { cmov2(tp->cb_cposx, tp->cb_cposbot+(rtfontinfo.yorig*tp->cb_ftsywid)); color(tp->cb_maxcol.front); FMPRSTR("Maximum"); } pushmatrix(); loadmatrix(tp->cb_matrix); break; case T_ABS: goto checkborder; case T_REL: default: checkborder: if (tp->cb_flags & TF_NOBORD) border = 0; /* no border */ else border = 2; /* real live hash marks */ break; } /* * In bar space at this point. Get initial world/pixel ratio in Y. */ cmov2(0.0, 0.0); GETCPOS(&ix, &iy); cmov2(1.0, 1.0); GETCPOS(&six, &siy); tp->cb_ftywid = 1.0 / cbydist(iy, siy); /* one pixel in Y */ tp->cb_ftxwid = 1.0 / (six - ix); /* one pixel in X */ if (tp->cb_type == T_SREL || tp->cb_type == T_SABS) { cbstalloc(tp, tp->cb_nsamp); sp = tp->cb_stp; cmov2(0.0, 0.0); GETCPOS(&sp->st_fpos[0], &sp->st_fpos[1]); sp->st_fpos[0] -= winposx; sp->st_fpos[1] = cbydist(winposy, sp->st_fpos[1]); } /* * Create the border around the bar. */ if (border == 2) { float bmin; float v[2]; /* * Wide border with hash marks. */ rect(0.0, 0.0, tp->cb_aspect, 1.0); bmin = 2.5 * tp->cb_ftywid; if (bmin < BORDER) { /* * Largish window, easy to do. */ bmin = BORDER; } translate(tp->cb_ftxwid*2, bmin, 0.0); scale(1-((tp->cb_ftxwid*3)/DEFXPERY), 1-(bmin+(tp->cb_ftywid)), 1.0); /* * Now go down the bar and add ticks marks in * WHITE over the lower black border. */ color(frontcolor); rectf(0.0, 0.0, tp->cb_aspect, -bmin); color(WHITE); psize = tp->cb_aspect / NDIVS; for (j = 0; j <= NDIVS; j++) { v[0] = j * psize; bgnline(); v[1] = 0.0; v2f(v); v[1] = -bmin; v2f(v); endline(); } if (tp->cb_type == T_SREL || tp->cb_type == T_SABS) { sp->st_fpos[0]++; sp->st_fpos[1] += 2; } } else if (border == 1) { /* * Narrow border. */ rect(0.0, 0.0, tp->cb_aspect, 1.0); /* * Want exactly enough room for a one-pixel wide line * around the bar. */ translate(tp->cb_ftxwid, tp->cb_ftywid, 0.0); scale(1.0-((tp->cb_ftxwid*2)/DEFXPERY), 1.0-(tp->cb_ftywid*2), 1.0); if (tp->cb_type == T_SREL || tp->cb_type == T_SABS) { sp->st_fpos[0]++; sp->st_fpos[1]++; } } /* * The bar was rescaled by drawing the border. Recalculate * the pixel ratios. */ cmov2(0.0, 0.0); GETCPOS(&ix, &iy); cmov2(1.0, 1.0); GETCPOS(&six, &siy); tp->cb_ftywid = 1.0 / cbydist(iy, siy); /* one pixel in Y */ tp->cb_ftxwid = 1.0 / (six - ix); /* one pixel in X */ /* * Now, if drawing a strip chart, figure out where the rectangles * are in screen coordinates. */ if (tp->cb_type == T_SREL || tp->cb_type == T_SABS) { /* * Insure that the rectangle always copies at least one * pixel's worth of stuff. */ minx = (tp->cb_ftwid > (tp->cb_aspect/sp->st_nsamples) ? tp->cb_ftwid : (tp->cb_aspect/sp->st_nsamples)); cmov2(0.0+minx, 0.0); GETCPOS(&sp->st_irect[0], &sp->st_irect[1]); sp->st_irect[0] -= winposx; sp->st_irect[1] = sp->st_fpos[1]; cmov2(tp->cb_aspect, 1.0); GETCPOS(&sp->st_irect[2], &sp->st_irect[3]); sp->st_irect[2] -= winposx; sp->st_irect[3] = cbydist(winposy, sp->st_irect[3]); if (printpos) printf("irect[%d,%d,%d,%d], fpos[%d,%d]\n", sp->st_irect[0], sp->st_irect[1], sp->st_irect[2], sp->st_irect[3], sp->st_fpos[0], sp->st_fpos[1]); /* * Disable the rectcopy if there's anything weird about * where the window is. */ if (maxscreenx == -1) maxscreenx = getgdesc(GD_XPMAX); if (sp->st_irect[0] + winposx > maxscreenx || sp->st_irect[2] + winposx > maxscreenx || sp->st_fpos[0] + winposx > maxscreenx || sp->st_irect[0] < 0 || sp->st_irect[2] < 0 || sp->st_fpos[0] < 0) tp->cb_flags |= TF_STRDR; else tp->cb_flags &= ~TF_STRDR; } getmatrix(tp->cb_matrix); popmatrix(); /* * Finally, perform any other random setup that needs to be * done. */ } /* * cbwinsetup - responsible for initializing the window and laying out * the bars and wording in a reasonable manner within it. */ static void cbwinsetup(int x) { if (x < 2) { /* * Re-initialize the window in case it was moved. */ reshapeviewport(); getsize(&winsizex, &winsizey); getorigin(&winposx, &winposy); if (debug || printpos) printf("Origin <%ld,%ld>, Size <%ld,%ld>\n", winposx, winposy, winsizex, winsizey); if (doublebuffered) { backbuffer(TRUE); frontbuffer(TRUE); } /* * Set up world space matrix. */ viewport(0, winsizex-1, 0, winsizey-1); color(backcolor); clear(); ortho(0.0, (Coord) (DEFXPERY*width), 0.0, (Coord) YSIZE, -1.0, 1.0); if (debug) printf("ortho 0,%f X 0,%f\n", DEFXPERY*width, (double)YSIZE); } if (x) { colorblock_t *tp; /* * Figure out where all * the words should go and draw them. */ tp = nextbar(1); while (tp != 0) { doscrmask(tp); cbdrawlegend(tp); tp = nextbar(0); } scrmask(winposx, winposx+winsizex, winposy, winposy+winsizey); /* * Finally, if a message is present in any of the bars, * then slam it out. */ tp = nextbar(1); while (tp != 0) { if (tp->cb_mess) cbmessage(tp, tp->cb_mess); tp = nextbar(0); } } } /* * cbpreops - perform any pre-setup initialization needed. */ void cbpreops(void) { frontcolor = BLACK; backcolor = DEFBACKCOLOR; palecolor = DEFPALECOLOR; /* * Make sure no color bars are allocated. If we ever go to dynamic * allocation, this must free them all. */ nbars = 0; /* we WILL lose memory here, unless you want to fix it ... */ } /* * cbresetbar - reset a colorbar for data */ void cbresetbar(colorblock_t *cb) { int i; cb->cb_flags |= TF_ONETRIP; for (i = 0; i < MAXSECTS; i++) { cb->cb_mess = 0; cb->cb_max[i] = 0; cb->cb_last[i] = 0; cb->cb_avg[i] = 0; cb->cb_results[i] = 0; cb->cb_dispavg1 = cb->cb_lastavg1 = 0; cb->cb_max1 = cb->cb_dispmax = 0; cb->cb_nabove = 0; cb->cb_nbelow = 0; } } /* * Routine to figure out the difference between two character positions, * one of which may be in the GL's brain-damaged no-man's land. */ static int cbydist(Screencoord iy, Screencoord siy) { int j; static int ymax = 0; if (ymax == 0) ymax = getgdesc(GD_YPMAX); if (siy < 0 && iy < 0) { j = -iy + siy; } else if (siy < iy) { j = (ymax + siy) - iy; } else { j = siy - iy; } return(j); }