/* * ********************************************************************* * * Copyright (C) 1988, 1990 Stanford University. * * * Permission to use, copy, modify, and distribute this * * * software and its documentation for any purpose and without * * * fee is hereby granted, provided that the above copyright * * * notice appear in all copies. Stanford University * * * makes no representations about the suitability of this * * * software for any purpose. It is provided "as is" without * * * express or implied warranty. Export of this software outside * * * of the United States of America may require an export license. * * ********************************************************************* */ #include #include #include #include /* **mdg** */ #include #include #include "ana.h" #include "ana_glob.h" #define DATE_LEN 25 /* length of ascii date returned by ctime */ #ifdef TCL_IRSIM public int psBanner = TRUE; public int psLegend = FALSE; public int psTimes = TRUE; public int psOutline = TRUE; #else private int psBanner = TRUE; private int psLegend = FALSE; private int psTimes = TRUE; private int psOutline = TRUE; #endif /* TCL_IRSIM */ private void DrawOutline(), PrintNames(), PrintTraces(); private void PrintSignal(), PrintVector(), PrintTimes(), PrintLegend(); private void WritePreamble(); public void WritePSfile(); #ifndef TCL_IRSIM public void SetPSParms( m ) MenuItem *m; { int *parm; char *s = m->str; switch( s[1] ) { case 'b' : psBanner ^= TRUE; break; case 'l' : psLegend ^= TRUE; break; case 't' : psTimes ^= TRUE; break; case 'o' : psOutline ^= TRUE; break; } if (m->mark == MENU_UNMARK) m->mark = MENU_MARK; else m->mark = MENU_UNMARK; } #endif /* !TCL_IRSIM */ typedef enum { black, white, gray, xpat } pattern; private char fname[256] = ""; private FILE *psout; private pattern currPat; public void printPS( s ) char *s; { if( traces.disp == 0 or tims.first >= tims.last ) { PRINT( "\nThere's nothing to print" ); XBell( display, 0 ); return; } #ifdef TCL_IRSIM if ((s == NULL) && (banner != NULL)) { (void) strncpy( fname, banner, bannerLen ); fname[bannerLen] = '\0'; } else if (s != NULL) { strcpy(fname, s); } if ((banner == NULL) && psBanner) { banner = strdup(fname); bannerLen = strlen(banner); } if (!strstr(fname, ".ps")) strcat(fname, ".ps" ); WritePSfile(fname); #else if( *fname == '\0' ) { (void) strncpy( fname, banner, bannerLen ); fname[bannerLen] = '\0'; strcat(fname, ".ps" ); } PRINTF( "\nEnter filename (%s)", fname ); Query( " > ", WritePSfile ); #endif } public void WritePSfile( psfname ) char *psfname; { char *date; time_t theTime, time(); int page = 1; if( psfname == NULL ) return; if( *psfname == '\0' ) psfname = fname; else (void) strcpy( fname, psfname ); if( (psout = fopen( psfname, "w" )) == NULL ) { PRINTF( "\ncan't open '%s' for output", psfname ); return; } PRINTF( "\nWriting %s...", psfname ); if (window != 0) { XDefineCursor( display, window, cursors.timer ); XFlush( display ); } currPat = black; WritePreamble(); theTime = time( 0 ); date = ctime( &theTime ); fprintf( psout, "%%%%Page: 1 %d\n", page++); fprintf( psout, "%%%%PageOrientation: Landscape\n"); fprintf( psout, "MSAVE\n" ); DrawOutline( date ); if( psTimes ) PrintTimes( tims.start, tims.end ); PrintNames(); PrintTraces( tims.start, min( tims.end, tims.last ) ); fprintf( psout, "showpage MRESTORE\n" ); if( psLegend ) { fprintf( psout, "%%%%Page: 1 %d\n", page++); fprintf( psout, "MSAVE\n" ); DrawOutline( date ); PrintLegend(); fprintf( psout, "showpage MRESTORE\n" ); } fprintf( psout, "%%%%EOF\n"); (void) fclose( psout ); PRINT( "done" ); if (window != 0) { XDefineCursor( display, window, cursors.deflt ); } } /* Macros to Convert from screen to PostScript units */ #define PS_UNITS 72 /* PostScript unit (1/inch) */ #define ps_YSIZE (15 * PS_UNITS / 2) /* y page size = 7.5 inch */ #define ps_XSIZE (10 * PS_UNITS) /* x page size = 10 inch */ #define XMARGIN (PS_UNITS / 2) /* margins 1/2 inch */ #define YMARGIN (PS_UNITS / 2) #define FSIZE 9 /* default font size */ #define MINFSIZE 4 /* minimum font size */ #define LEGFSIZE 7 /* legend font size */ #define YTRACE (traceBox.bot - traceBox.top + 1) #define TIMES_HEIGHT 20 #define BANNER_HEIGHT 15 #define psHEIGHT ( ps_YSIZE - TIMES_HEIGHT - BANNER_HEIGHT ) /* scale conversion macros */ #define PSX( x ) ( (x) * (ps_XSIZE - 2) / traceBox.right ) #define PSY( y ) ( (YWINDOWSIZE - (y)) * psHEIGHT / YTRACE ) #define PS( x, y ) PSX( x ), PSY( y ) #define BANNER_BOT ( psHEIGHT + PSY( traceBox.bot ) ) #define BANNER_TOP ( BANNER_BOT + BANNER_HEIGHT ) #define BANNER_MID ( (BANNER_TOP + BANNER_BOT + 1) / 2 ) #define TIMES_TOP PSY( traceBox.bot ) #define TIMES_BOT (TIMES_TOP - TIMES_HEIGHT ) /**************************************************************************/ /* Redefine the graphics operators to generate the PostScript code */ /**************************************************************************/ #define BLACK 1 /* anything will do */ #define WHITE 0 #define VLine( X, BOT, TOP, COLOR ) \ fprintf( psout, "%d %d %d VL\n", PS( X, BOT), PSY( TOP ) ); #define HLine( LEFT, RIGHT, Y, COLOR ) \ fprintf( psout, "%d %d %d HL\n", PS( LEFT, Y ), PSX( RIGHT ) ); #define Line( X1, Y1, X2, Y2 ) \ fprintf( psout, "%d %d %d %d L\n", PS( X1, Y1 ), PS( X2, Y2 ) ); #define FillAREA( X, Y, WIDTH, HEIGHT, COLOR ) \ { \ pattern opatt = SetPattern( COLOR ); \ fprintf( psout, "%d %d %d %d BOX fill\n", PS( X, Y ), \ PS( (X) + (WIDTH) - 1, (Y) + (HEIGHT) - 1 ) ); \ (void) SetPattern( opatt ); \ } \ #define RECT( X, Y, X2, Y2, OP ) \ fprintf( psout, "%d %d %d %d BOX %s\n", X, Y, X2, Y2, #OP ); #define StrLeft( S, LEN, LEFT, BOT, FG, BG ) \ { \ psString( S, LEN ); \ fprintf( psout, "%d %d SL\n", PS( LEFT, BOT ) ); \ } \ #define StrRight( S, LEN, RIGHT, MID, FG, BG ) \ { \ psString( S, LEN ); \ fprintf( psout, "%d %d SR\n", PS( RIGHT, MID ) ); \ } \ #define StrCenter( S, LEN, LEFT, RIGHT, MID, FG, BG ) \ { \ psString( S, LEN ); \ fprintf( psout, "%d %d %d SC\n", PSX( LEFT ), PS( RIGHT, MID ) ); \ } \ /**************************************************************************/ /* * Write out an string, prepending parens with '\' */ private void psString( s, len ) char *s; int len; { putc( '(', psout ); while( *s != '\0' and len != 0 ) { if( (*s == '(' ) || (*s == ')') ) putc( '\\', psout ); putc( *s, psout ); s++; len--; } putc( ')', psout ); } private pattern SetPattern( patt ) pattern patt; { float grayscale; pattern ret; if( patt == white ) grayscale = 1.0; else if( patt == gray ) grayscale = 0.82; else if( patt == xpat ) grayscale = 0.68; else /* black */ grayscale = 0.0; fprintf( psout, "%g setgray\n", grayscale ); ret = currPat; currPat = patt; return( ret ); } private void DrawOutline( date ) char *date; { pattern opatt; char info[ 256 ]; if( psBanner ) { opatt = SetPattern( gray ); RECT( 0, BANNER_BOT, ps_XSIZE, BANNER_TOP, fill ); (void) SetPattern( opatt ); RECT( 0, BANNER_BOT, ps_XSIZE, BANNER_TOP, stroke ); psString( banner, bannerLen ); fprintf( psout, "%d %d FSIZE 2 div sub SL\n", 6, BANNER_MID ); if( strncmp( banner, fname, bannerLen ) != 0 ) { (void) sprintf( info, "(%s) %s", fname, date ); psString( info, bannerLen + DATE_LEN ); } else psString( date, DATE_LEN ); fprintf( psout, " %d %d SR\n", PSX( traceBox.right - 4 ), BANNER_MID); } if( psOutline ) RECT( 0, TIMES_BOT, ps_XSIZE, BANNER_TOP, stroke ) } /* FactorTbl: time grid at multiples of 1/FactorTbl => .1 .5 .25 1 */ private int FactorTbl[] = { 10, 2, 4, 1}; private void PrintTimes( t1, t2 ) TimeType t1, t2; { TimeType step, half_step, i; int x; char s[ 25 ]; for( step = tims.steps, i = 1; step >= 10; step /= 10, i *= 10 ); for( x = 0; ; x++ ) { int nsteps = tims.steps /( i / FactorTbl[x] ); if( nsteps > 5 and nsteps < 15 ) break; } step = i / FactorTbl[x]; half_step = ( step > 2 ) ? (step / 2) - 1 : 2; fprintf( psout, "0 setlinewidth [1 3] 0 setdash /svfnt currentfont def\n"); fprintf( psout, "theFont 0.7 FSIZE mul scalefont setfont\n" ); psString( "time (ns)", 30 ); fprintf( psout, " %d %d %d SC\n", 0, PSX(traceBox.left) - 1, TIMES_TOP ); i = ((t1 + step - 1) / step) * step; if( i != t1 ) { x = TimeToX( t1 ); VLine( x, traceBox.top, traceBox.bot + 3, BLACK ); if( i - t1 >= half_step ) { (void) sprintf( s, "%.2f", d2ns( t1 ) ); x = 2 * PSX( x ); fprintf( psout, "(%s) 0 %d %d SC\n", s, x, (TIMES_BOT+TIMES_TOP)/2 ); } } while( i <= t2 ) { x = TimeToX( i ); VLine( x, traceBox.top, traceBox.bot + 3, BLACK ); (void) sprintf( s, "%.2f", d2ns( i ) ); x = 2 * PSX( x ); fprintf( psout, "(%s) 0 %d %d SC\n", s, x, (TIMES_BOT+TIMES_TOP)/2 ); i += step; } if( i > t2 and (t2 - i + step) >= half_step ) { x = TimeToX( t2 ); VLine( x, traceBox.top, traceBox.bot + 3, BLACK ); (void) sprintf( s, "%.2f", d2ns( t2 ) ); x = PSX( x ); fprintf( psout, "(%s) %d %d SR\n", s, x, (TIMES_BOT+TIMES_TOP)/2 ); } HLine( traceBox.left - 2, traceBox.right, traceBox.bot, BLACK ); fprintf( psout, "0.6 setlinewidth [] 0 setdash svfnt setfont\n" ); } private void PrintNames() { Coord x, y; TraceEnt *t; int i; #ifdef TCL_IRSIM x = traceBox.right - 2; #else x = namesBox.right - 2; #endif for( t = traces.first, i = traces.disp; i != 0; i--, t = t->next ) { y = (t->bot + t->top) / 2; StrRight( t->name, t->len, x, y, BLACK, WHITE ); } } private void PrintTraces( t1, t2 ) TimeType t1,t2; { Trptr t; int nt; for( t = traces.first, nt = traces.disp; nt != 0; nt--, t = t->next ) { if( IsVector( t ) ) PrintVector( t, t1, t2 ); else PrintSignal( t, t1, t2 ); } } private void PrintSignal( t, t1, t2 ) Trptr t; register TimeType t1, t2; { register hptr h; register int val, change; int x1, x2; if( t1 >= tims.last ) return; h = t->cache[0].wind; x1 = TimeToX( t1 ); while( t1 < t2 ) { val = h->val; while( h->time < t2 and h->val == val ) NEXTH( h, h ); if( h->time > t2 ) { change = FALSE; t1 = t2; } else { change = ( h->val != val ); t1 = h->time; } x2 = TimeToX( t1 ); switch( val ) { case LOW : HLine( x1, x2, t->bot, WHITE ); break; case HIGH : HLine( x1, x2, t->top, WHITE ); break; case X : FillAREA( x1, t->top, x2 - x1 + 1, t->bot - t->top + 1, xpat ); if( x1 > traceBox.left + 1 ) VLine( x1, t->bot, t->top, WHITE ); break; } if( change ) VLine( x2, t->bot, t->top, WHITE ); x1 = x2; } } private void PrintVector( t, t1, t2 ) register Trptr t; register TimeType t1, t2; { hptr *start, *changes; TimeType firstChange; int x1, x2, xx, mid, nbits, strlen; if( t1 >= tims.last ) return; nbits = t->n.vec->nbits; start = tmpHBuff; changes = &(tmpHBuff[ nbits ]); if (t->bdigit == 5) strlen = (nbits + 2) / 3; else if (t->bdigit == 6) strlen = 1 + ((nbits + 1) / 3); else strlen = (nbits + t->bdigit - 1) / t->bdigit; { register hptr h, *s; register int n, val; register hptr *ch = changes; register TimeType tm = tims.end; s = start; /* initialize start array */ firstChange = tims.start; for( n = nbits - 1; n >= 0; n-- ) { h = s[n] = t->cache[n].wind; val = h->val; while( h->time < tm and h->val == val ) NEXTH( h, h ); ch[n] = h; } } mid = (t->top + t->bot) / 2; x2 = TimeToX( t2 ); x1 = TimeToX( firstChange ); while( t1 < t2 ) { { /* find nearest change in time */ register hptr *ch; register int n; t1 = tims.end + 1; for( ch = changes, n = nbits - 1; n >= 0; n-- ) { if( ch[n]->time < t1 ) t1 = ch[n]->time; } } if( t1 <= t2 ) /* change before t2 => draw it */ { x2 = TimeToX( t1 ); if( x2 - x1 > 3 ) { HLine( x1 + 2, x2 - 2, t->top, WHITE ); HLine( x1 + 2, x2 - 2, t->bot, WHITE ); xx = 2; } else xx = (x2 - x1 - 2); VLine( x2, t->bot - 2, t->top + 2, WHITE ); if( x2 > traceBox.left + 1 ) { Line( x2 - xx, t->top, x2, t->top + 2 ); Line( x2 - xx, t->bot, x2, t->bot - 2 ); } if( x2 < traceBox.right - 1 ) { Line( x2, t->top + 2, x2 + 2, t->top ); Line( x2, t->bot - 2, x2 + 2, t->bot ); } } else /* change after t2 */ { register TimeType tm; tm = min( t1, min( tims.end, tims.last ) ); x2 = TimeToX( tm ); HLine( x1 + 2, x2, t->top, WHITE ); HLine( x1 + 2, x2, t->bot, WHITE ); } { char *str; str = HistToStr( start, nbits, t->bdigit, 1 ); StrCenter( str, strlen, x1, x2, mid, WHITE, BLACK ); } { register hptr h; register hptr *ch, *s; register int n, val; register TimeType tm = tims.end; for( s = start, ch = changes, n = nbits - 1; n >= 0; n-- ) { if( ch[n]->time == t1 ) { h = s[n] = ch[n]; val = h->val; while( h->time < tm and h->val == val ) NEXTH( h, h ); ch[n] = h; } } } x1 = x2; } } private void PrintLegend() { int i, nbits; TraceEnt *t; #ifdef TCL_IRSIM fprintf( psout, "/GX %d def\n", PSX( traceBox.left + 25 ) ); #else fprintf( psout, "/GX %d def\n", PSX( namesBox.right + 25 ) ); #endif fprintf( psout, "/GY %d def\n", BANNER_BOT - 2 * (FSIZE + 3) ); fprintf( psout, "(Legend:) 4 %d SL\n", BANNER_BOT - FSIZE - 2 ); fprintf( psout, "/FSIZE %d def FSIZE SF\n", LEGFSIZE ); for( i = traces.disp, t = traces.first; i != 0; i--, t = t->next ) { if( t->vector ) { for( nbits = t->n.vec->nbits - 1; nbits >= 0; nbits-- ) psString( t->n.vec->nodes[nbits]->nname, 1000 ); nbits = t->n.vec->nbits; } else { psString( t->n.nd->nname, 1000 ); nbits = 1; } psString( t->name, 1000 ); fprintf( psout, "%d LE\n", nbits ); } } void WritePreamble() { int pages; static char defs[] = "%!\n\ /MSAVE { /mStat save def } def\n\ /MRESTORE { mStat restore } def\n\ /SET { exch def } def\n\ /SF { /wi SET theFont [wi 0 0 FSIZE 0 0] makefont setfont } def\n\ /L { newpath moveto lineto stroke } def\n\ /VL { 2 index exch L } def\n\ /HL { 1 index L } def\n\ /BOX { /@yb SET /@xb SET /@yt SET /@xt SET newpath\n\ @xb @yb moveto @xb @yt lineto @xt @yt lineto @xt @yb lineto closepath } def\n\ /SL { moveto show } def\n\ /SR { exch dup /@xr SET 2 index stringwidth pop sub dup\n\ 0 le { pop 0 @xr 3 -1 roll SC } { exch FSIZE 2 div sub moveto show }\n\ ifelse } def\n\ /SCP { /@s SET @s stringwidth pop /@sw SET @sw 2 div sub\n\ dup @sw add maxX gt { pop maxX @sw sub } if exch moveto @s show } def\n\ /SC { FSIZE 2 div sub /@y SET /@x2 SET /@x1 SET /@s SET\n\ /@l @x2 @x1 sub def /@x @x1 @x2 add 2 div def\n\ /@w @s stringwidth pop def @l @w gt\n\ { @y @x @s SCP }\n\ { @l FSIZE mul @w 1 add div dup MINFSIZE lt\n\ { pop } { /svf currentfont def SF @y @x @s SCP svf setfont } ifelse }ifelse\n\ } def\n\ /LE { exch GX GY FSIZE 2 div add SR /@x GX def\n\ { dup stringwidth pop /@w SET /@x @x FSIZE add def @x @w add\n\ 720 ge { /@x GX FSIZE add def /GY GY FSIZE 2 add sub def } if\n\ @x GY SL /@x @x @w add def } repeat /GY GY FSIZE 4 add sub def } def\n\ "; /* determine number of pages */ pages = psLegend ? 2 : 1; /* print data and procedure definitions */ fprintf( psout, "%%!PS-Adobe-2.0\n"); fprintf( psout, "%%%%Pages: %d\n", pages); fprintf( psout, "%%%%EndComments\n"); fprintf( psout, "%s", defs ); fprintf( psout, "/FSIZE %d def /MINFSIZE %d def ", FSIZE, MINFSIZE ); fprintf( psout, "/maxX %d def\n", PSX( traceBox.right ) ); /* Switch to landscape mode */ fprintf( psout, "%d 0 translate\n", 17 * PS_UNITS / 2 ); fprintf( psout, "90 rotate\n" ); fprintf( psout, "%d %d", XMARGIN, YMARGIN + TIMES_HEIGHT - PSY( traceBox.bot) ); fprintf( psout, " translate\n" ); fprintf( psout, "1 setlinecap 0.6 setlinewidth \n" ); /* Set up text font */ fprintf( psout,"/theFont /Helvetica findfont def FSIZE SF\n"); }