1
/* ploticus data display engine. Software, documentation, and examples.
2
* Copyright 1998-2002 Stephen C. Grubb (scg@jax.org).
3
* Covered by GPL; see the file ./Copyright for details. */
5
/* PCODE - all text/graphics requests (except init) are processed
6
via this function. Many E function calls such
7
as Emov and Elin are actually macros defined in
8
elib.d, which call pcode with a single character
9
op code and perhaps some parameters.
11
==========================================================================
20
T, C, J text, centered text, right justified text
27
D text direction (ps only)
29
Z print/eject (ps only)
30
W wait for a key or mouse click (x11 only)
31
w cycle notifier from within a loop (x11 only)
33
z clear screen (x11 only)
34
d make window disappear (x11 only)
35
a make window re-appear (x11 only)
36
e set text scale factor (x11 only, when user resizes window)
37
Q end of file (tells ps and gif drivers to wrap up)
39
b save window to backing store (x11 only)
40
B restore window from backing store (x11 only)
43
==========================================================================
47
#include "graphcore.h"
53
static int Enew = 0, Edrawing = 0; /* used by postscript section */
54
static int Evertchar = 0; /* true if character direction is vertical..*/
55
/* ..used only by x11 */
56
static double Ecurx; /* real starting place of centered &rt justified text */
57
static double Ewidth; /* width of most recently drawn text line. */
58
static char Eprevop; /* previous op */
59
static int Esquelched = 0; /* is output being squelched? 1=yes, 0=no */
60
static int Epspage = 1; /* current page # */
61
static int Epsvirginpage = 1; /* 1 when nothing has yet been drawn on current page */
63
static int Ekeeping_bb = 1;
65
static double EBBx1 = 999; /* coords of bounding box for entire run */
66
static double EBBy1 = 999;
67
static double EBBx2 = -999;
68
static double EBBy2 = -999;
70
static double Esbb_x1 = 999; /* coords of "sub" bounding box available to app */
71
static double Esbb_y1 = 999;
72
static double Esbb_x2 = -999;
73
static double Esbb_y2 = -999;
74
static int keep_sub_bb = 0;
75
static int tightbb = 0;
76
static double scx1, scy1, scx2, scy2; /* specified crop zone */
77
static int specifycrop = 0; /* 0 = no 1 = absolute values 2 = values relative to bounding box */
79
static double globalscale = 1.0;
80
static double globalscaley = 1.0;
81
static double posterxo = 0.0;
82
static double posteryo = 0.0;
83
static int postermode = 0;
84
static double lmlx = 0.0, lmly = 0.0; /* last move local x and y
85
(saves location of last 'MOVE', including any effects of globalscale and poster ofs */
86
static int pcodedebug = 0;
87
static FILE *pcodedebugfp = 0;
90
/* ======================= */
92
char op; /* op code */
93
double x, y; /* coordinates */
94
char s[]; /* optional character string */
98
/* Postscript: inform driver we're beginning a new page.. */
99
if( Edev == 'p' && Epsvirginpage && !GL_member( op, "Q" ) ) {
100
EPSnewpage( Epspage );
105
/* For clear op: do it then return; don't include in bounding box calculations */
108
/* compensate EWinw and EWiny by globalscale because this op will
109
be doubly affected by globalscale.. */
110
Ecblock( 0.0, 0.0, EWinx/globalscale, EWiny/globalscaley, s, 0 );
117
if( op == 'M' ) { Ex1 = x; Ey1 = y; } /* remember most recent 'move', untainted
118
by globalscale or posterofs.. */
120
/* scale x, y according to global scale, for all move, draw and text size ops */
121
if( globalscale != 1.0 ) {
122
if( GL_member( op, "LMPI" )) { x *= globalscale; y *= globalscaley; }
123
if( op == 'Y' ) y *= globalscale; /* dash scale */
126
/* if poster offset specified, translate */
128
if( GL_member( Edev, "pcx" ) && GL_member( op, "LMP" )) {
134
if( Debug && op == 'Q' )
135
fprintf( Diagfp, "Done with page. Writing out result file. Computed bounding box is: %-5.2f, %-5.2f to %-5.2f, %-5.2f\n", EBBx1, EBBy1, EBBx2, EBBy2 );
138
/* device interfaces ======================== */
140
/* if output is squelched, do not do any draw operations, except textsize, which
141
returns text width, which is necessary for bb calculations.. */
145
if( Edev == 'x' ) { EXWpointsize( (int)(x), &Ecurtextwidth ); }
151
/* interface to X11 xlib driver.. */
152
else if( Edev == 'x' ) {
155
case 'L' : EXWlineto( x, y ); break;
156
case 'M' : EXWmoveto( x, y ); break;
157
case 'P' : EXWpath( x, y ); break;
158
case 'T' : if( !Evertchar ) EXWtext( s, &Ewidth );
160
case 'r' : EXWcolor( s ); return( 0 );
161
case 's' : EXWfill( ); return( 0 );
162
case 'U' : EXWflush(); return( 0 );
163
case 'b' : EXWsavewin(); return( 0 );
164
case 'B' : EXWrestorewin(); return( 0 );
165
case 'I' : EXWpointsize( (int)(x), &Ecurtextwidth ); return( 0 );
166
case 'C' : if( !Evertchar ) {
167
EXWmoveto( lmlx-6.0, lmly );
168
EXWcentext( s, 12.0, &Ecurx, &Ewidth );
171
case 'J' : if( !Evertchar ) {
172
EXWmoveto( lmlx-12.0, lmly );
173
EXWrightjust( s, 12.0, &Ecurx, &Ewidth );
176
case 'W' : EXWwait(); return( 0 );
177
case 'Y' : EXWlinetype( s, x, y ); return( 0 );
178
case 'D' : if( x == 90 || x == 270 ) Evertchar = 1;
182
case 'w' : EXWasync(); return( 0 );
183
case '.' : EXWdot( x, y ); break;
184
case 'd' : EXWdisappear(); return( 0 );
185
case 'a' : EXWappear(); return( 0 );
186
case 'e' : EXWscaletext( x ); return( 0 );
192
/* interface to postscript driver */
196
if( Edrawing ) EPSstroke();
201
case 'L' : if( Enew ) EPSmoveto( lmlx, lmly );
206
case 'M' : Enew = 1; break;
207
case 'P' : if( Enew ) EPSmoveto( lmlx, lmly );
211
case 'T' : EPStext( op, lmlx, lmly, s, 0.0 ); break;
213
case 'C' : if( !Evertchar ) EPStext( op, lmlx - 6.0, lmly, s, 12.0 );
214
else if( Evertchar )EPStext( op, lmlx, lmly - 6.0, s, 12.0 );
216
case 'J' : if( !Evertchar ) EPStext( op, lmlx-12.0, lmly, s, 12.0 );
217
else if( Evertchar ) EPStext( op, lmlx, lmly - 12.0, s, 12.0 );
220
case 's' : EPSfill( ); return( 0 );
221
case 'r' : EPScolor( s ); return( 0 );
222
case 'c' : EPSclosepath(); return( 0 );
223
case 'O' : EPSpaper( (int)x ); return( 0 );
224
case 'I' : EPSpointsize( (int)x ); return( 0 );
225
case 'F' : EPSfont( s ); return( 0 );
226
case 'D' : EPSchardir( (int)x );
227
if( x == 90 || x == 270 ) Evertchar = 1;
230
case 'Y' : EPSlinetype( s, x, y ); return( 0 );
231
case 'Z' : EPSshow(); Epspage++; Epsvirginpage = 1; return( 0 );
232
case 'Q' : if( !Epsvirginpage ) { EPSshow(); Epspage++; }
234
EPStrailer( Epspage - 1, EBBx1-0.05, EBBy1-0.05, EBBx2+0.05, EBBy2+0.05 );
235
else if( specifycrop == 1 )
236
EPStrailer( Epspage - 1, scx1, scy1, scx2, scy2 );
237
else if( specifycrop == 2 ) {
238
EPStrailer( Epspage - 1, (EBBx1-0.05)-scx1, (EBBy1-0.05)-scy1,
239
(EBBx2+0.05)+scx2, (EBBy2+0.05)+scy2 );
242
/* add 0.2" margin to be generous in cases of fat lines, etc. */
243
EPStrailer( Epspage - 1, EBBx1-0.2, EBBy1-0.2, EBBx2+0.2, EBBy2+0.2 );
246
/* case '.' : EPSdot( x, y ); ??? */
250
/* Added for svg support - BT 05/11/01 */
251
/* interface to svg driver */
252
else if( Edev == 's' ) {
255
if( Edrawing ) SVGstroke();
260
case 'L' : if( Enew ) SVGmoveto( lmlx, lmly );
265
case 'M' : Enew = 1; break;
266
case 'P' : if( Enew ) SVGmoveto( lmlx, lmly );
271
case 'T' : SVGtext( op, lmlx, lmly, s, 0.0 ); break;
273
case 'C' : if( !Evertchar ) SVGtext( op, lmlx , lmly, s, 0.0 );
274
else if( Evertchar )SVGtext( op, lmlx, lmly , s, 0.0 );
276
case 'J' : if( !Evertchar ) SVGtext( op, lmlx, lmly, s, 0.0 );
277
else if( Evertchar ) SVGtext( op, lmlx, lmly , s, 0.0 );
280
case 's' : SVGfill( ); return( 0 );
281
case 'r' : SVGcolor( s ); return( 0 );
282
case 'c' : SVGclosepath(); return( 0 );
283
case 'O' : SVGpaper( (int)x ); return( 0 );
284
case 'I' : SVGpointsize( (int)x ); return( 0 );
285
case 'F' : SVGfont( s ); return( 0 );
286
case 'D' : SVGchardir( (int)x );
287
if( x == 90 || x == 270 ) Evertchar = 1;
290
case 'Y' : SVGlinetype( s, x, y ); return( 0 );
291
case 'Z' : SVGshow(); Epspage++; Epsvirginpage = 1; return( 0 );
292
case 'Q' : if( !Epsvirginpage ) { SVGshow(); Epspage++; }
294
SVGtrailer( Epspage - 1, EBBx1-0.05, EBBy1-0.05, EBBx2+0.05, EBBy2+0.05 );
295
else if( specifycrop == 1 )
296
SVGtrailer( Epspage - 1, scx1, scy1, scx2, scy2 );
297
else if( specifycrop == 2 ) {
298
SVGtrailer( Epspage - 1, (EBBx1-0.05)-scx1, (EBBy1-0.05)-scy1,
299
(EBBx2+0.05)+scx2, (EBBy2+0.05)+scy2 );
302
/* add 0.2" margin to be generous in cases of fat lines, etc. */
303
SVGtrailer( Epspage - 1, EBBx1-0.2, EBBy1-0.2, EBBx2+0.2, EBBy2+0.2 );
309
/* interface to GD driver.. */
310
else if( Edev == 'g' ) {
313
case 'L' : EGlineto( x, y ); break;
314
case 'M' : EGmoveto( x, y ); break;
315
case 'P' : EGpathto( x, y ); break;
316
case 'T' : EGtext( s ); break;
317
case 'C' : EGcentext( s ); break;
318
case 'F' : EGfont( s ); break;
319
case 'J' : EGrightjust( s ); break;
320
case 's' : EGfill(); return( 0 );
321
case 'r' : EGcolor( s ); return( 0 );
322
case 'I' : EGtextsize( (int)x ); return( 0 );
323
case 'D' : EGchardir( (int)x );
324
if( (int)x != 0 ) Evertchar = 1;
327
case 'Y' : EGlinetype( s, x, y ); return( 0 );
328
case 'Q' : Egetoutfilename( buf );
329
if( buf[0] == '\0' ) strcpy( buf, "out.gif" );
331
/* see if anything has been drawn, if not, return */
332
if ( EBBx2 < -998 && EBBy2 < -998 ) return( 0 );
334
if( tightbb ) EGeof( buf, EBBx1, EBBy1, EBBx2, EBBy2 );
335
else if( specifycrop == 1 )
336
EGeof( buf, scx1, scy1, scx2, scy2 );
337
else if( specifycrop == 2 )
338
EGeof( buf, (EBBx1)-scx1, (EBBy1)-scy1, (EBBx2)+scx2, (EBBy2)+scy2 );
339
else EGeof( buf, EBBx1-0.2, EBBy1-0.2, EBBx2+0.2, EBBy2+0.2 );
350
fprintf( stderr, "[pcode got %c]", op );
351
Eerr( 12021, "Graphcore has not yet been initialized", "" );
356
sprintf( sdev, "%c", Edev );
357
Eerr( 12022, "Unrecognized graphic device code", sdev );
365
if( op == 'M' ) { lmlx = x; lmly = y; } /* remember most recent 'move' */
369
/* figure approximate text dimensions */
370
if( Edev != 'x' && GL_member( op, "TCJ" )) {
371
Ewidth = strlen( s ) * Ecurtextwidth;
372
Ewidth *= globalscale;
376
/* keep bounding box info (minima and maxima) */
377
if( GL_member( op, "LP" ) ) {
378
if( Eprevop == 'M' ) Ebb( lmlx, lmly );
381
/* normal (horizontal) text operations. (vertical text below) */
382
else if( op == 'T' && !Evertchar ) {
383
if( Eprevop == 'M' ) Ebb( lmlx, lmly );
384
Ebb( lmlx + Ewidth+0.05, lmly + (Ecurtextheight*globalscale) );
386
else if( op == 'C' && !Evertchar ) {
387
Ebb( lmlx - ((Ewidth/2.0)+0.05), lmly );
388
Ebb( lmlx + ((Ewidth/2.0)+0.05), lmly + (Ecurtextheight*globalscale) );
390
else if( op == 'J' && !Evertchar ) {
391
Ebb( lmlx - (Ewidth+0.05), lmly );
392
Ebb( lmlx, lmly + (Ecurtextheight*globalscale) );
399
/* handle vertical text .. must be simulated for x windows;
400
also gets bounding box for vertical text operations (all devices) */
402
if( Evertchar && GL_member( op, "TCJ" )) Everttextsim( op, s );
412
Eshow(); /* eject page and return.. */
415
else if( Edev == 'g' ) {
423
/* ============================================= */
424
/* EBB - keep an overall bounding box for the entire image.
425
Also call Echeckbb() to maintain nested object bounding boxes.. */
426
/* Ebb( double x, double y ) */
432
if( ( x < EBBx1 && x < 0.0 ) || (x > EBBx2 && x > 8.0 ) ) fprintf( pcodedebugfp, "draw out X = %g\n", x );
433
if( ( y < EBBy1 && y < 0.0 ) || (y > EBBy2 && y > 8.0 ) ) fprintf( pcodedebugfp, "draw out Y = %g\n", y );
435
if( x < EBBx1 ) EBBx1 = x;
436
if( x > EBBx2 ) EBBx2 = x;
437
if( y < EBBy1 ) EBBy1 = y;
438
if( y > EBBy2 ) EBBy2 = y;
442
if( x < Esbb_x1 ) Esbb_x1 = x;
443
if( x > Esbb_x2 ) Esbb_x2 = x;
444
if( y < Esbb_y1 ) Esbb_y1 = y;
445
if( y > Esbb_y2 ) Esbb_y2 = y;
452
/* ============================================== */
453
/* ERESETBB - needed for multiple pages */
463
/* ============================================= */
464
/* EGETBB - get current bounding box.. */
465
Egetbb( xlo, ylo, xhi, yhi )
466
double *xlo, *ylo, *xhi, *yhi;
468
*xlo = EBBx1 / globalscale;
469
*ylo = EBBy1 / globalscaley;
470
*xhi = EBBx2 / globalscale;
471
*yhi = EBBy2 / globalscaley;
476
/* ============================================== */
477
/* EGETTEXTSIZE - get width and height of last text item.. */
486
/* ================================================ */
487
/* vertical text bounding box, also simulation for X11 displays */
488
Everttextsim( op, s )
491
double dist, y1, y2, x, y;
498
if( Edev == 'x' ) dist = len * Ecurtextheight;
499
else dist = len * Ecurtextwidth;
501
if( op == 'T' ) { y1 = lmly; y2 = lmly + dist; }
502
else if( op == 'C' ) { y1 = lmly - (dist/2); y2 = lmly + (dist/2); }
503
else if( op == 'J' ) { y1 = lmly - dist; y2 = lmly; }
504
if( Edev == 'x' ) x = lmlx - Ecurtextwidth;
509
for( i = 0; i < len; i++ ) {
510
sprintf( let, "%c", s[i] );
517
Ebb( x-(Ecurtextheight*globalscale), y1 );
518
Ebb( x-(Ecurtextheight*globalscale), y2 );
524
/* ==================================================== */
525
/* 1 = squelch all display activity, 0 restore to normal */
526
/* Used to calculate bounding box without displaying */
527
/* handles nested calls. */
528
Esquelch_display( mode )
531
static int snest = 0;
536
else if( mode == 0 ) {
537
if( snest > 0 ) snest--;
538
if( snest == 0 )Esquelched = 0;
542
/* ==================================================== */
557
/* ==================================================== */
558
/* ETIGHTBB - switch ON=don't add margin when doing final BB crop */
565
/* ==================================================== */
567
Especifycrop( mode, x1, y1, x2, y2 )
568
int mode; /* 0=off 1=absolute values 2=relative to tightcrop values */
569
double x1, y1, x2, y2;
581
/* ==================================================== */
582
/* special GD calls... */
584
/* ==================================================== */
585
/* EGIFRECT - direct interface to GD driver for better efficiency on rectangles */
586
Egifrect( xlo, yhi, xhi, ylo, color )
587
double xlo, yhi, xhi, ylo;
592
strcpy( oldcolor, Ecurcolor );
593
if( globalscale != 1.0 ) {
594
xlo *= globalscale; ylo *= globalscaley;
595
xhi *= globalscale; yhi *= globalscaley;
597
EGrect( xlo, yhi, xhi, ylo, color );
604
/* ==================================================== */
605
/* EIMLOAD - tell the gif driver to load a GIF image */
606
Eimload( filename, scalex, scaley )
608
double scalex, scaley;
611
if( globalscale != 1.0 ) {
612
scalex *= globalscale;
613
scaley *= globalscaley;
615
return( EGimload( filename, scalex, scaley ) );
621
/* ==================================================== */
622
/* EIMPLACE - tell the gif driver to place a GIF image */
623
Eimplace( x, y, imalign, xscale, yscale )
626
double xscale, yscale;
629
if( globalscale != 1.0 ) {
632
/* xscale and yscale are always passed as 1.0;
633
do not scale here as image is scaled when read */
635
return( EGimplace( x, y, imalign, xscale, yscale ) );
642
/* ===================================================== */
643
/* ESETGLOBALSCALE - set global scale factor */
644
Esetglobalscale( sx, sy )
647
if( sx < 0.01 || sx > 20.0 ) return( Eerr( 20815, "Invalid global scaling", "" ) );
648
if( sy < 0.01 || sy > 20.0 ) return( Eerr( 20815, "Invalid global scaling", "" ) );
651
Estandard_lwscale = 1.0 * sx;
654
/* ===================================================== */
655
/* EGETGLOBALSCALE - get global scale factor */
656
Egetglobalscale( sx, sy )
664
/* ======================================= */
665
/* ESETPOSTEROFS - set poster offset (paginated postscript only).
666
x, y are in absolute units, and are where the lower-left of the page will be.
667
So if I have a poster made of 4 8.5x11 sheets held portrait style,
668
the lowerleft would use 0,0
669
the lowerright would use 8,0
670
the upperleft would use 0,10.5
671
the lowerright would use 8,10.5
672
(8 and 10.5 are used because of print margins).
673
The four pages can then be trimmed w/ a paper cutter and butted up against
674
one another to create a poster.
676
Esetposterofs( x, y )
680
posterxo = x * (-1.0);
681
posteryo = y * (-1.0);
684
/* ========================================== */
685
/* EPCODEDEBUG - turn on/off local debugging */
686
Epcodedebug( mode, fp )
688
FILE *fp; /* stream for diagnostic output */