VPR-6.0

vpr/SRC/base/graphics.c

Go to the documentation of this file.
00001 #include <math.h>
00002 #include <stdlib.h>
00003 #include <stdio.h>
00004 #include <string.h>
00005 
00006 #include "util.h"
00007 #include "graphics.h"
00008 #include "vpr_types.h"
00009 /*#include "draw.h" */
00010 
00011 #ifndef NO_GRAPHICS
00012 
00013 #include <X11/Xlib.h>
00014 #include <X11/Xutil.h>
00015 #include <X11/Xos.h>
00016 #include <X11/Xatom.h>
00017 
00018 #endif
00019 
00020 
00021 /** 
00022  * @file
00023  *
00024  * Written by Vaughn Betz at the University of Toronto, Department of       
00025  * Electrical and Computer Engineering.  Graphics package  Version 1.3.     
00026  * All rights reserved by U of T, etc.                                      
00027  *                                                                          
00028  * You may freely use this graphics interface for non-commercial purposes   
00029  * as long as you leave the written by Vaughn Betz message in it -- who     
00030  * knows, maybe someday an employer will see it and  give me a job or large 
00031  * sums of money :).                                                        
00032  *                                                                          
00033  * Revision History:                                                        
00034  *                                                                          
00035  * Sept. 19, 1997:  Incorporated Zoom Fit code of Haneef Mohammed at        
00036  * Cypress.  Makes it easy to zoom to a full view of the graphics.          
00037  *                                                                          
00038  * Sept. 11, 1997:  Added the create_button and delete_button interface to  
00039  * make it easy to add and destroy buttons from user code.  Removed the     
00040  * bnum parameter to the button functions, since it wasn't really needed.   
00041  *                                                                          
00042  * June 28, 1997:  Added filled arc drawing primitive.  Minor modifications 
00043  * to PostScript driver to make the PostScript output slightly smaller.     
00044  *                                                                          
00045  * April 15, 1997:  Added code to init_graphics so it waits for a window    
00046  * to be exposed before returning.  This ensures that users of non-         
00047  * interactive graphics can never draw to a window before it is available.  
00048  *                                                                          
00049  * Feb. 24, 1997:  Added code so the package will allocate  a private       
00050  * colormap if the default colormap doesn't have enough free colours.       
00051  *                                                                          
00052  * June 28, 1996:  Converted all internal functions in graphics.c to have   
00053  * internal (static) linkage to avoid any conflicts with user routines in   
00054  * the rest of the program.                                                 
00055  *                                                                          
00056  * June 12, 1996:  Added setfontsize and setlinewidth attributes.  Added    
00057  * pre-clipping of objects for speed (and compactness of PS output) when    
00058  * graphics are zoomed in.  Rewrote PostScript engine to shrink the output  
00059  * and make it easier to read.  Made drawscreen a callback function passed  
00060  * in rather than a global.  Graphics attribute calls are more efficient -- 
00061  * they check if they have to change anything before doing it.               
00062  *                                                                          
00063  * October 27, 1995:  Added the message area, a callback function for       
00064  * interacting with user button clicks, and implemented a workaround for a  
00065  * Sun X Server bug that misdisplays extremely highly zoomed graphics.      
00066  *                                                                          
00067  * Jan. 13, 1995:  Modified to incorporate PostScript Support.              
00068  */
00069 
00070 
00071 /****************** Types and defines local to this module ******************/
00072 
00073 #ifndef NO_GRAPHICS
00074 
00075 /* Uncomment the line below if your X11 header files don't define XPointer */
00076 /* typedef char *XPointer;                                                 */
00077 
00078 /**@{*/
00079 /** Macros for translation from world to PostScript coordinates */
00080 #define XPOST(worldx) (((worldx)-xleft)*ps_xmult + ps_left)
00081 #define YPOST(worldy) (((worldy)-ybot)*ps_ymult + ps_bot)
00082 /**@}*/
00083 
00084 /**@{*/
00085 /** Macros to convert from X Windows Internal Coordinates to my  
00086  * World Coordinates.  (This macro is used only rarely, so      
00087  * the divides don't hurt speed).                               
00088  */
00089 #define XTOWORLD(x) (((float) x)/xmult + xleft)
00090 #define YTOWORLD(y) (((float) y)/ymult + ytop)
00091 /**@}*/
00092 
00093 #define max(a,b) (((a) > (b))? (a) : (b))
00094 #define min(a,b) ((a) > (b)? (b) : (a))
00095 
00096 #define MWIDTH 104              /**< width of menu window */
00097 #define T_AREA_HEIGHT 24        /**< Height of text window */
00098 #define MAX_FONT_SIZE 40        /**< Largest point size of text */
00099 #define PI 3.141592654
00100 
00101 #define BUTTON_TEXT_LEN 20
00102 
00103 typedef struct
00104 {
00105     int width;
00106     int height;
00107     int xleft;
00108     int ytop;
00109     void (*fcn) (void (*drawscreen) (void));
00110     Window win;
00111     int istext;
00112     char text[BUTTON_TEXT_LEN];
00113     int ispoly;
00114     int poly[3][2];
00115     int ispressed;
00116 }
00117 t_button;
00118 
00119 
00120 
00121 
00122 /********************* Static variables local to this module ****************/
00123 
00124 static const int menu_font_size = 14;   /**< Font for menus and dialog boxes. */
00125 
00126 static t_button *button;        /**< [0..num_buttons-1] */
00127 static int num_buttons;         /**< Number of menu buttons */
00128 
00129 static int disp_type;           /**< Selects SCREEN or POSTSCRIPT */
00130 static Display *display;
00131 static int screen_num;
00132 static GC gc, gcxor, gc_menus;
00133 static XFontStruct *font_info[MAX_FONT_SIZE + 1];       /**< Data for each size */
00134 static int font_is_loaded[MAX_FONT_SIZE + 1];   /**< 1: loaded, 0: not  */
00135 static unsigned int display_width, display_height;      /**< screen size */
00136 static unsigned int top_width, top_height;      /**< window size */
00137 static Window toplevel, menu, textarea; /**< various windows */
00138 static float xleft, xright, ytop, ybot; /**< world coordinates */
00139 
00140 /** Initial world coordinates */
00141 static float saved_xleft, saved_xright, saved_ytop, saved_ybot;
00142 
00143 static float ps_left, ps_right, ps_top, ps_bot; /**< Figure boundaries for 
00144                                                    PostScript output, in PostScript coordinates.  */
00145 static float ps_xmult, ps_ymult;        /**< Transformation for PostScript. */
00146 static float xmult, ymult;      /**< Transformation factors */
00147 static Colormap private_cmap;   /**< "None" unless a private cmap was allocated. */
00148 
00149 /**@{*/
00150 /** Graphics state.  Set start-up defaults here. */
00151 static int currentcolor = BLACK;
00152 static int currentlinestyle = SOLID;
00153 static int currentlinewidth = 0;
00154 static int currentfontsize = 10;
00155 /**@}*/
00156 
00157 static char message[BUFSIZE] = "\0";    /**< User message to display */
00158 
00159 /** Color indices passed back from X Windows. */
00160 static int colors[NUM_COLOR];
00161 
00162 /** For PostScript output */
00163 static FILE *ps;
00164 
00165 /** MAXPIXEL and MINPIXEL are set to prevent what appears to be 
00166  * overflow with very large pixel values on the Sun X Server.  */
00167 #define MAXPIXEL 15000
00168 #define MINPIXEL -15000
00169 
00170 
00171 /********************** Subroutines local to this module ********************/
00172 
00173 /* Function declarations for button responses */
00174 
00175 static void translate_up(void (*drawscreen) (void));
00176 static void translate_left(void (*drawscreen) (void));
00177 static void translate_right(void (*drawscreen) (void));
00178 static void translate_down(void (*drawscreen) (void));
00179 static void zoom_in(void (*drawscreen) (void));
00180 static void zoom_out(void (*drawscreen) (void));
00181 static void zoom_fit(void (*drawscreen) (void));
00182 static void adjustwin(void (*drawscreen) (void));
00183 static void postscript(void (*drawscreen) (void));
00184 static void proceed(void (*drawscreen) (void));
00185 static void quit(void (*drawscreen) (void));
00186 
00187 static Bool test_if_exposed(Display * disp,
00188                             XEvent * event_ptr,
00189                             XPointer dummy);
00190 static void map_button(int bnum);
00191 static void unmap_button(int bnum);
00192 
00193 
00194 
00195 /********************** Subroutine definitions ******************************/
00196 
00197 
00198 /** Translates from my internal coordinates to X Windows coordinates   *
00199  * in the x direction.  Add 0.5 at end for extra half-pixel accuracy. */
00200 static int
00201 xcoord(float worldx)
00202 {
00203     int winx;
00204 
00205     winx = (int)((worldx - xleft) * xmult + 0.5);
00206 
00207 /* Avoid overflow in the X Window routines.  This will allow horizontal  *
00208  * and vertical lines to be drawn correctly regardless of zooming, but   *
00209  * will cause diagonal lines that go way off screen to change their      *
00210  * slope as you zoom in.  The only way I can think of to completely fix  *
00211  * this problem is to do all the clipping in advance in floating point,  *
00212  * then convert to integers and call X Windows.  This is a lot of extra  *
00213  * coding, and means that coordinates will be clipped twice, even though *
00214  * this "Super Zoom" problem won't occur unless users zoom way in on     * 
00215  * the graphics.                                                         */
00216 
00217     winx = max(winx, MINPIXEL);
00218     winx = min(winx, MAXPIXEL);
00219 
00220     return (winx);
00221 }
00222 
00223 
00224 /** Translates from my internal coordinates to X Windows coordinates   
00225  * in the y direction.  Add 0.5 at end for extra half-pixel accuracy. 
00226  */
00227 static int
00228 ycoord(float worldy)
00229 {
00230     int winy;
00231 
00232     winy = (int)((worldy - ytop) * ymult + 0.5);
00233 
00234 /* Avoid overflow in the X Window routines. */
00235     winy = max(winy, MINPIXEL);
00236     winy = min(winy, MAXPIXEL);
00237 
00238     return (winy);
00239 }
00240 
00241 
00242 /** Makes sure the font of the specified size is loaded.  Point_size   
00243  * MUST be between 1 and MAX_FONT_SIZE -- no check is performed here. 
00244  * Use proper point-size medium-weight upright helvetica font */
00245 static void
00246 load_font(int pointsize)
00247 {
00248     char fontname[44];
00249 
00250     sprintf(fontname, "-*-helvetica-medium-r-*--*-%d0-*-*-*-*-*-*",
00251             pointsize);
00252 
00253 #ifdef VERBOSE
00254     printf("Loading font: point size: %d, fontname: %s\n", pointsize,
00255            fontname);
00256 #endif
00257 
00258 /* Load font and get font information structure. */
00259 
00260     if((font_info[pointsize] = XLoadQueryFont(display, fontname)) == NULL)
00261         {
00262             fprintf(stderr, "Cannot open desired font\n");
00263             exit(-1);
00264         }
00265 }
00266 
00267 
00268 static void
00269 force_setcolor(int cindex)
00270 {
00271 
00272     static char *ps_cnames[NUM_COLOR] =
00273         { "white", "black", "grey55", "grey75",
00274         "blue", "green", "yellow", "cyan", "red", "darkgreen", "magenta", 
00275         "bisque", "lightblue", "thistle", "plum", "khaki", 
00276         "coral", "turquoise", "mediumpurple", "darkslateblue", "darkkhaki"
00277     };
00278 
00279     currentcolor = cindex;
00280 
00281     if(disp_type == SCREEN)
00282         {
00283             XSetForeground(display, gc, colors[cindex]);
00284         }
00285     else
00286         {
00287             fprintf(ps, "%s\n", ps_cnames[cindex]);
00288         }
00289 }
00290 
00291 
00292 void
00293 setcolor(int cindex)
00294 {
00295 
00296     if(currentcolor != cindex)
00297         force_setcolor(cindex);
00298 }
00299 
00300 int getcolor()
00301 {
00302     return currentcolor;
00303 }
00304 
00305 static void
00306 force_setlinestyle(int linestyle)
00307 {
00308 
00309 /* Note SOLID is 0 and DASHED is 1 for linestyle.                      */
00310 
00311 /* PostScript and X commands needed, respectively. */
00312 
00313     static char *ps_text[2] = { "linesolid", "linedashed" };
00314     static int x_vals[2] = { LineSolid, LineOnOffDash };
00315 
00316     currentlinestyle = linestyle;
00317 
00318     if(disp_type == SCREEN)
00319         {
00320             XSetLineAttributes(display, gc, currentlinewidth,
00321                                x_vals[linestyle], CapButt, JoinMiter);
00322         }
00323     else
00324         {
00325             fprintf(ps, "%s\n", ps_text[linestyle]);
00326         }
00327 }
00328 
00329 
00330 void
00331 setlinestyle(int linestyle)
00332 {
00333 
00334     if(linestyle != currentlinestyle)
00335         force_setlinestyle(linestyle);
00336 }
00337 
00338 
00339 static void
00340 force_setlinewidth(int linewidth)
00341 {
00342 
00343 /* linewidth should be greater than or equal to 0 to make any sense. */
00344 /* Note SOLID is 0 and DASHED is 1 for linestyle.                    */
00345 
00346     static int x_vals[2] = { LineSolid, LineOnOffDash };
00347 
00348     currentlinewidth = linewidth;
00349 
00350     if(disp_type == SCREEN)
00351         {
00352             XSetLineAttributes(display, gc, linewidth,
00353                                x_vals[currentlinestyle], CapButt, JoinMiter);
00354         }
00355     else
00356         {
00357             fprintf(ps, "%d setlinewidth\n", linewidth);
00358         }
00359 }
00360 
00361 
00362 void
00363 setlinewidth(int linewidth)
00364 {
00365 
00366     if(linewidth != currentlinewidth)
00367         force_setlinewidth(linewidth);
00368 }
00369 
00370 
00371 static void
00372 force_setfontsize(int pointsize)
00373 {
00374 
00375 /* Valid point sizes are between 1 and MAX_FONT_SIZE */
00376 
00377     if(pointsize < 1)
00378         pointsize = 1;
00379     else if(pointsize > MAX_FONT_SIZE)
00380         pointsize = MAX_FONT_SIZE;
00381 
00382     currentfontsize = pointsize;
00383 
00384 
00385     if(disp_type == SCREEN)
00386         {
00387             if(!font_is_loaded[pointsize])
00388                 {
00389                     load_font(pointsize);
00390                     font_is_loaded[pointsize] = 1;
00391                 }
00392             XSetFont(display, gc, font_info[pointsize]->fid);
00393         }
00394 
00395     else
00396         {
00397             /* PostScript:  set up font and centering function */
00398 
00399             fprintf(ps, "%d setfontsize\n", pointsize);
00400         }
00401 }
00402 
00403 
00404 /** For efficiency, this routine doesn't do anything if no change is
00405  * implied.  If you want to force the graphics context or PS file   
00406  * to have font info set, call force_setfontsize (this is necessary 
00407  * in initialization and X11 / Postscript switches).                
00408  */
00409 void
00410 setfontsize(int pointsize)
00411 {
00412     if(pointsize != currentfontsize)
00413         force_setfontsize(pointsize);
00414 }
00415 
00416 
00417 /** Creates a small window at the top of the graphics area for text messages */
00418 static void
00419 build_textarea(void)
00420 {
00421     XSetWindowAttributes menu_attributes;
00422     unsigned long valuemask;
00423 
00424     textarea = XCreateSimpleWindow(display, toplevel,
00425                                    0, top_height - T_AREA_HEIGHT,
00426                                    display_width, T_AREA_HEIGHT - 4, 2,
00427                                    colors[BLACK], colors[LIGHTGREY]);
00428     menu_attributes.event_mask = ExposureMask;
00429     /* ButtonPresses in this area are ignored. */
00430     menu_attributes.do_not_propagate_mask = ButtonPressMask;
00431     /* Keep text area on bottom left */
00432     menu_attributes.win_gravity = SouthWestGravity;
00433     valuemask = CWWinGravity | CWEventMask | CWDontPropagate;
00434     XChangeWindowAttributes(display, textarea, valuemask, &menu_attributes);
00435     XMapWindow(display, textarea);
00436 }
00437 
00438 
00439 /** Puts a triangle in the poly array for button[bnum] */
00440 static void
00441 setpoly(int bnum,
00442         int xc,
00443         int yc,
00444         int r,
00445         float theta)
00446 {
00447     int i;
00448 
00449     button[bnum].istext = 0;
00450     button[bnum].ispoly = 1;
00451     for(i = 0; i < 3; i++)
00452         {
00453             button[bnum].poly[i][0] = (int)(xc + r * cos(theta) + 0.5);
00454             button[bnum].poly[i][1] = (int)(yc + r * sin(theta) + 0.5);
00455             theta += 2 * PI / 3;
00456         }
00457 }
00458 
00459 
00460 /** Sets up the default menu buttons on the right hand side of the window. */
00461 static void
00462 build_default_menu(void)
00463 {
00464     XSetWindowAttributes menu_attributes;
00465     unsigned long valuemask;
00466     int i, xcen, x1, y1, bwid, bheight, space;
00467 
00468 
00469     menu = XCreateSimpleWindow(display, toplevel,
00470                                top_width - MWIDTH, 0, MWIDTH - 4,
00471                                display_height, 2, colors[BLACK],
00472                                colors[LIGHTGREY]);
00473     menu_attributes.event_mask = ExposureMask;
00474     /* Ignore button presses on the menu background. */
00475     menu_attributes.do_not_propagate_mask = ButtonPressMask;
00476     /* Keep menu on top right */
00477     menu_attributes.win_gravity = NorthEastGravity;
00478     valuemask = CWWinGravity | CWEventMask | CWDontPropagate;
00479     XChangeWindowAttributes(display, menu, valuemask, &menu_attributes);
00480     XMapWindow(display, menu);
00481 
00482     num_buttons = 11;
00483     button = (t_button *) my_malloc(num_buttons * sizeof(t_button));
00484 
00485 /* Now do the arrow buttons */
00486     bwid = 28;
00487     space = 3;
00488     y1 = 10;
00489     xcen = 51;
00490     x1 = xcen - bwid / 2;
00491     button[0].xleft = x1;
00492     button[0].ytop = y1;
00493     setpoly(0, bwid / 2, bwid / 2, bwid / 3, -PI / 2.); /* Up */
00494     button[0].fcn = translate_up;
00495 
00496     y1 += bwid + space;
00497     x1 = xcen - 3 * bwid / 2 - space;
00498     button[1].xleft = x1;
00499     button[1].ytop = y1;
00500     setpoly(1, bwid / 2, bwid / 2, bwid / 3, PI);       /* Left */
00501     button[1].fcn = translate_left;
00502 
00503     x1 = xcen + bwid / 2 + space;
00504     button[2].xleft = x1;
00505     button[2].ytop = y1;
00506     setpoly(2, bwid / 2, bwid / 2, bwid / 3, 0);        /* Right */
00507     button[2].fcn = translate_right;
00508 
00509     y1 += bwid + space;
00510     x1 = xcen - bwid / 2;
00511     button[3].xleft = x1;
00512     button[3].ytop = y1;
00513     setpoly(3, bwid / 2, bwid / 2, bwid / 3, +PI / 2.); /* Down */
00514     button[3].fcn = translate_down;
00515 
00516     for(i = 0; i < 4; i++)
00517         {
00518             button[i].width = bwid;
00519             button[i].height = bwid;
00520         }
00521 
00522 /* Rectangular buttons */
00523 
00524     y1 += bwid + space + 6;
00525     space = 8;
00526     bwid = 90;
00527     bheight = 26;
00528     x1 = xcen - bwid / 2;
00529     for(i = 4; i < num_buttons; i++)
00530         {
00531             button[i].xleft = x1;
00532             button[i].ytop = y1;
00533             y1 += bheight + space;
00534             button[i].istext = 1;
00535             button[i].ispoly = 0;
00536             button[i].width = bwid;
00537             button[i].height = bheight;
00538         }
00539 
00540     strcpy(button[4].text, "Zoom In");
00541     strcpy(button[5].text, "Zoom Out");
00542     strcpy(button[6].text, "Zoom Fit");
00543     strcpy(button[7].text, "Window");
00544     strcpy(button[8].text, "PostScript");
00545     strcpy(button[9].text, "Proceed");
00546     strcpy(button[10].text, "Exit");
00547 
00548     button[4].fcn = zoom_in;
00549     button[5].fcn = zoom_out;
00550     button[6].fcn = zoom_fit;
00551     button[7].fcn = adjustwin;
00552     button[8].fcn = postscript;
00553     button[9].fcn = proceed;
00554     button[10].fcn = quit;
00555 
00556     for(i = 0; i < num_buttons; i++)
00557         map_button(i);
00558 }
00559 
00560 
00561 /** Maps a button onto the screen and set it up for input, etc.        */
00562 static void
00563 map_button(int bnum)
00564 {
00565     button[bnum].win = XCreateSimpleWindow(display, menu,
00566                                            button[bnum].xleft,
00567                                            button[bnum].ytop,
00568                                            button[bnum].width,
00569                                            button[bnum].height, 0,
00570                                            colors[WHITE], colors[LIGHTGREY]);
00571     XMapWindow(display, button[bnum].win);
00572     XSelectInput(display, button[bnum].win, ButtonPressMask);
00573     button[bnum].ispressed = 1;
00574 }
00575 
00576 
00577 /** Unmaps a button from the screen.        */
00578 static void
00579 unmap_button(int bnum)
00580 {
00581     XUnmapWindow(display, button[bnum].win);
00582 }
00583 
00584 
00585 /** Creates a new button below the button containing prev_button_text.       
00586  * The text and button function are set according to button_text and        
00587  * button_func, respectively.                                               
00588  */
00589 void
00590 create_button(char *prev_button_text,
00591               char *button_text,
00592               void (*button_func) (void (*drawscreen) (void)))
00593 {
00594     int i, bnum, space;
00595 
00596     space = 8;
00597 
00598 /* Only allow new buttons that are text (not poly) types.                   */
00599 
00600     bnum = -1;
00601     for(i = 4; i < num_buttons; i++)
00602         {
00603             if(button[i].istext == 1 &&
00604                strcmp(button[i].text, prev_button_text) == 0)
00605                 {
00606                     bnum = i + 1;
00607                     break;
00608                 }
00609         }
00610 
00611     if(bnum == -1)
00612         {
00613             printf
00614                 ("Error in create_button:  button with text %s not found.\n",
00615                  prev_button_text);
00616             exit(1);
00617         }
00618 
00619     num_buttons++;
00620     button = (t_button *) my_realloc(button, num_buttons * sizeof(t_button));
00621 
00622 /* NB:  Requirement that you specify the button that this button goes under *
00623  * guarantees that button[num_buttons-2] exists and is a text button.       */
00624 
00625     button[num_buttons - 1].xleft = button[num_buttons - 2].xleft;
00626     button[num_buttons - 1].ytop = button[num_buttons - 2].ytop +
00627         button[num_buttons - 2].height + space;
00628     button[num_buttons - 1].height = button[num_buttons - 2].height;
00629     button[num_buttons - 1].width = button[num_buttons - 2].width;
00630     map_button(num_buttons - 1);
00631 
00632 
00633     for(i = num_buttons - 1; i > bnum; i--)
00634         {
00635             button[i].ispoly = button[i - 1].ispoly;
00636 /* No poly copy for now, as I'm only providing the ability to create text *
00637  * buttons.                                                               */
00638 
00639             button[i].istext = button[i - 1].istext;
00640             strcpy(button[i].text, button[i - 1].text);
00641             button[i].fcn = button[i - 1].fcn;
00642             button[i].ispressed = button[i - 1].ispressed;
00643         }
00644 
00645     button[bnum].istext = 1;
00646     button[bnum].ispoly = 0;
00647     my_strncpy(button[bnum].text, button_text, BUTTON_TEXT_LEN);
00648     button[bnum].fcn = button_func;
00649     button[bnum].ispressed = 1;
00650 }
00651 
00652 
00653 /** Destroys the button with text button_text. */
00654 void
00655 destroy_button(char *button_text)
00656 {
00657     int i, bnum;
00658 
00659     bnum = -1;
00660     for(i = 4; i < num_buttons; i++)
00661         {
00662             if(button[i].istext == 1 &&
00663                strcmp(button[i].text, button_text) == 0)
00664                 {
00665                     bnum = i;
00666                     break;
00667                 }
00668         }
00669 
00670     if(bnum == -1)
00671         {
00672             printf
00673                 ("Error in destroy_button:  button with text %s not found.\n",
00674                  button_text);
00675             exit(1);
00676         }
00677 
00678     for(i = bnum + 1; i < num_buttons; i++)
00679         {
00680             button[i - 1].ispoly = button[i].ispoly;
00681 /* No poly copy for now, as I'm only providing the ability to create text *
00682  * buttons.                                                               */
00683 
00684             button[i - 1].istext = button[i].istext;
00685             strcpy(button[i - 1].text, button[i].text);
00686             button[i - 1].fcn = button[i].fcn;
00687             button[i - 1].ispressed = button[i].ispressed;
00688         }
00689 
00690     unmap_button(num_buttons - 1);
00691     num_buttons--;
00692     button = (t_button *) my_realloc(button, num_buttons * sizeof(t_button));
00693 }
00694 
00695 
00696 /** Open the toplevel window, get the colors, 2 graphics         
00697  * contexts, load a font, and set up the toplevel window        
00698  * Calls build_default_menu to set up the default menu.         
00699  */
00700 void
00701 init_graphics(char *window_name)
00702 {
00703 
00704     char *display_name = NULL;
00705     int x, y;                   /* window position */
00706     unsigned int border_width = 2;      /* ignored by OpenWindows */
00707     XTextProperty windowName;
00708 
00709 /* X Windows' names for my colours. */
00710     char *cnames[NUM_COLOR] = { "white", "black", "grey55", "grey75", "blue",
00711         "green", "yellow", "cyan", "red", "RGBi:0.0/0.5/0.0", "magenta", 
00712         "bisque", "lightblue", "thistle", "plum", "khaki", 
00713         "coral", "turquoise", "mediumpurple", "darkslateblue", "darkkhaki"
00714     };
00715 
00716     XColor exact_def;
00717     Colormap cmap;
00718     int i;
00719     unsigned long valuemask = 0;        /* ignore XGCvalues and use defaults */
00720     XGCValues values;
00721     XEvent event;
00722 
00723 
00724     disp_type = SCREEN;         /* Graphics go to screen, not ps */
00725 
00726     for(i = 0; i <= MAX_FONT_SIZE; i++)
00727         font_is_loaded[i] = 0;  /* No fonts loaded yet. */
00728 
00729     /* connect to X server */
00730     if((display = XOpenDisplay(display_name)) == NULL)
00731         {
00732             fprintf(stderr, "Cannot connect to X server %s\n",
00733                     XDisplayName(display_name));
00734             exit(-1);
00735         }
00736 
00737     /* get screen size from display structure macro */
00738     screen_num = DefaultScreen(display);
00739     display_width = DisplayWidth(display, screen_num);
00740     display_height = DisplayHeight(display, screen_num);
00741 
00742     x = y = 0;
00743 
00744     top_width = 2 * display_width / 3;
00745     top_height = 4 * display_height / 5;
00746 
00747     cmap = DefaultColormap(display, screen_num);
00748     private_cmap = None;
00749 
00750     for(i = 0; i < NUM_COLOR; i++)
00751         {
00752             if(!XParseColor(display, cmap, cnames[i], &exact_def))
00753                 {
00754                     fprintf(stderr, "Color name %s not in database",
00755                             cnames[i]);
00756                     exit(-1);
00757                 }
00758             if(!XAllocColor(display, cmap, &exact_def))
00759                 {
00760                     fprintf(stderr, "Couldn't allocate color %s.\n",
00761                             cnames[i]);
00762 
00763                     if(private_cmap == None)
00764                         {
00765                             fprintf(stderr,
00766                                     "Will try to allocate a private colourmap.\n");
00767                             fprintf(stderr,
00768                                     "Colours will only display correctly when your "
00769                                     "cursor is in the graphics window.\n"
00770                                     "Exit other colour applications and rerun this "
00771                                     "program if you don't like that.\n\n");
00772 
00773                             private_cmap =
00774                                 XCopyColormapAndFree(display, cmap);
00775                             cmap = private_cmap;
00776                             if(!XAllocColor(display, cmap, &exact_def))
00777                                 {
00778                                     fprintf(stderr,
00779                                             "Couldn't allocate color %s as private.\n",
00780                                             cnames[i]);
00781                                     exit(1);
00782                                 }
00783                         }
00784 
00785                     else
00786                         {
00787                             fprintf(stderr,
00788                                     "Couldn't allocate color %s as private.\n",
00789                                     cnames[i]);
00790                             exit(1);
00791                         }
00792                 }
00793             colors[i] = exact_def.pixel;
00794         }
00795 
00796     toplevel =
00797         XCreateSimpleWindow(display, RootWindow(display, screen_num), x, y,
00798                             top_width, top_height, border_width,
00799                             colors[BLACK], colors[WHITE]);
00800 
00801     if(private_cmap != None)
00802         XSetWindowColormap(display, toplevel, private_cmap);
00803 
00804     /* hints stuff deleted. */
00805 
00806     XSelectInput(display, toplevel, ExposureMask | StructureNotifyMask |
00807                  ButtonPressMask);
00808 
00809 
00810     /* Create default Graphics Contexts.  valuemask = 0 -> use defaults. */
00811     gc = XCreateGC(display, toplevel, valuemask, &values);
00812     gc_menus = XCreateGC(display, toplevel, valuemask, &values);
00813 
00814     /* Create XOR graphics context for Rubber Banding */
00815     values.function = GXxor;
00816     values.foreground = colors[BLACK];
00817     gcxor = XCreateGC(display, toplevel, (GCFunction | GCForeground),
00818                       &values);
00819 
00820     /* specify font for menus.  */
00821     load_font(menu_font_size);
00822     font_is_loaded[menu_font_size] = 1;
00823     XSetFont(display, gc_menus, font_info[menu_font_size]->fid);
00824 
00825 /* Set drawing defaults for user-drawable area.  Use whatever the *
00826  * initial values of the current stuff was set to.                */
00827     force_setfontsize(currentfontsize);
00828     force_setcolor(currentcolor);
00829     force_setlinestyle(currentlinestyle);
00830     force_setlinewidth(currentlinewidth);
00831 
00832     XStringListToTextProperty(&window_name, 1, &windowName);
00833     XSetWMName(display, toplevel, &windowName);
00834 /* Uncomment to set icon name */
00835 /* XSetWMIconName (display, toplevel, &windowName); */
00836 
00837 /* XStringListToTextProperty copies the window_name string into            *
00838  * windowName.value.  Free this memory now.                                */
00839 
00840     free(windowName.value);
00841 
00842     XMapWindow(display, toplevel);
00843     build_textarea();
00844     build_default_menu();
00845 
00846 /* The following is completely unnecessary if the user is using the       *
00847  * interactive (event_loop) graphics.  It waits for the first Expose      *
00848  * event before returning so that I can tell the window manager has got   *
00849  * the top-level window up and running.  Thus the user can start drawing  *
00850  * into this window immediately, and there's no danger of the window not  *
00851  * being ready and output being lost.                                     */
00852 
00853     XPeekIfEvent(display, &event, test_if_exposed, NULL);
00854 }
00855 
00856 
00857 /** Returns True if the event passed in is an exposure event.   Note that 
00858  * the bool type returned by this function is defined in Xlib.h.         
00859  */
00860 static Bool
00861 test_if_exposed(Display * disp,
00862                 XEvent * event_ptr,
00863                 XPointer dummy)
00864 {
00865 
00866     if(event_ptr->type == Expose)
00867         {
00868             return (True);
00869         }
00870 
00871     return (False);
00872 }
00873 
00874 
00875 /** draws text center at xc, yc -- used only by menu drawing stuff */
00876 static void
00877 menutext(Window win,
00878          int xc,
00879          int yc,
00880          char *text)
00881 {
00882     int len, width;
00883 
00884     len = strlen(text);
00885     width = XTextWidth(font_info[menu_font_size], text, len);
00886     XDrawString(display, win, gc_menus, xc - width / 2, yc +
00887                 (font_info[menu_font_size]->ascent -
00888                  font_info[menu_font_size]->descent) / 2, text, len);
00889 }
00890 
00891 
00892 /** Draws button bnum in either its pressed or unpressed state.    */
00893 static void
00894 drawbut(int bnum)
00895 {
00896     int width, height, thick, i, ispressed;
00897     XPoint mypoly[6];
00898 
00899     ispressed = button[bnum].ispressed;
00900     thick = 2;
00901     width = button[bnum].width;
00902     height = button[bnum].height;
00903 /* Draw top and left edges of 3D box. */
00904     if(ispressed)
00905         {
00906             XSetForeground(display, gc_menus, colors[BLACK]);
00907         }
00908     else
00909         {
00910             XSetForeground(display, gc_menus, colors[WHITE]);
00911         }
00912 
00913 /* Note:  X Windows doesn't appear to draw the bottom pixel of *
00914  * a polygon with XFillPolygon, so I make this 1 pixel thicker *
00915  * to compensate.                                              */
00916     mypoly[0].x = 0;
00917     mypoly[0].y = height;
00918     mypoly[1].x = 0;
00919     mypoly[1].y = 0;
00920     mypoly[2].x = width;
00921     mypoly[2].y = 0;
00922     mypoly[3].x = width - thick;
00923     mypoly[3].y = thick;
00924     mypoly[4].x = thick;
00925     mypoly[4].y = thick;
00926     mypoly[5].x = thick;
00927     mypoly[5].y = height - thick;
00928     XFillPolygon(display, button[bnum].win, gc_menus, mypoly, 6, Convex,
00929                  CoordModeOrigin);
00930 
00931 /* Draw bottom and right edges of 3D box. */
00932     if(ispressed)
00933         {
00934             XSetForeground(display, gc_menus, colors[WHITE]);
00935         }
00936     else
00937         {
00938             XSetForeground(display, gc_menus, colors[BLACK]);
00939         }
00940     mypoly[0].x = 0;
00941     mypoly[0].y = height;
00942     mypoly[1].x = width;
00943     mypoly[1].y = height;
00944     mypoly[2].x = width;
00945     mypoly[2].y = 0;
00946     mypoly[3].x = width - thick;
00947     mypoly[3].y = thick;
00948     mypoly[4].x = width - thick;
00949     mypoly[4].y = height - thick;
00950     mypoly[5].x = thick;
00951     mypoly[5].y = height - thick;
00952     XFillPolygon(display, button[bnum].win, gc_menus, mypoly, 6, Convex,
00953                  CoordModeOrigin);
00954 
00955 /* Draw background */
00956     if(ispressed)
00957         {
00958             XSetForeground(display, gc_menus, colors[DARKGREY]);
00959         }
00960     else
00961         {
00962             XSetForeground(display, gc_menus, colors[LIGHTGREY]);
00963         }
00964 
00965 /* Give x,y of top corner and width and height */
00966     XFillRectangle(display, button[bnum].win, gc_menus, thick, thick,
00967                    width - 2 * thick, height - 2 * thick);
00968 
00969 /* Draw polygon, if there is one */
00970     if(button[bnum].ispoly)
00971         {
00972             for(i = 0; i < 3; i++)
00973                 {
00974                     mypoly[i].x = button[bnum].poly[i][0];
00975                     mypoly[i].y = button[bnum].poly[i][1];
00976                 }
00977             XSetForeground(display, gc_menus, colors[BLACK]);
00978             XFillPolygon(display, button[bnum].win, gc_menus, mypoly, 3,
00979                          Convex, CoordModeOrigin);
00980         }
00981 
00982 /* Draw text, if there is any */
00983     if(button[bnum].istext)
00984         {
00985             XSetForeground(display, gc_menus, colors[BLACK]);
00986             menutext(button[bnum].win, button[bnum].width / 2,
00987                      button[bnum].height / 2, button[bnum].text);
00988         }
00989 }
00990 
00991 
00992 /** Shows when the menu is active or inactive by colouring the 
00993  * buttons.                                                   
00994  */
00995 static void
00996 turn_on_off(int pressed)
00997 {
00998 
00999     int i;
01000 
01001     for(i = 0; i < num_buttons; i++)
01002         {
01003             button[i].ispressed = pressed;
01004             drawbut(i);
01005         }
01006 }
01007 
01008 
01009 static int
01010 which_button(Window win)
01011 {
01012     int i;
01013 
01014     for(i = 0; i < num_buttons; i++)
01015         {
01016             if(button[i].win == win)
01017                 return (i);
01018         }
01019     printf("Error:  Unknown button ID in which_button.\n");
01020     return (0);
01021 }
01022 
01023 
01024 static void
01025 drawmenu(void)
01026 {
01027     int i;
01028 
01029     for(i = 0; i < num_buttons; i++)
01030         {
01031             drawbut(i);
01032         }
01033 }
01034 
01035 
01036 /** Set up the factors for transforming from the user world to X Windows 
01037  * coordinates.                                                         
01038  */
01039 static void
01040 update_transform(void)
01041 {
01042     float mult, y1, y2, x1, x2;
01043 
01044 /* X Window coordinates go from (0,0) to (width-1,height-1) */
01045     xmult = ((float)top_width - 1. - MWIDTH) / (xright - xleft);
01046     ymult = ((float)top_height - 1. - T_AREA_HEIGHT) / (ybot - ytop);
01047 /* Need to use same scaling factor to preserve aspect ratio */
01048     if(fabs(xmult) <= fabs(ymult))
01049         {
01050             mult = fabs(ymult / xmult);
01051             y1 = ytop - (ybot - ytop) * (mult - 1.) / 2.;
01052             y2 = ybot + (ybot - ytop) * (mult - 1.) / 2.;
01053             ytop = y1;
01054             ybot = y2;
01055         }
01056     else
01057         {
01058             mult = fabs(xmult / ymult);
01059             x1 = xleft - (xright - xleft) * (mult - 1.) / 2.;
01060             x2 = xright + (xright - xleft) * (mult - 1.) / 2.;
01061             xleft = x1;
01062             xright = x2;
01063         }
01064     xmult = ((float)top_width - 1. - MWIDTH) / (xright - xleft);
01065     ymult = ((float)top_height - 1. - T_AREA_HEIGHT) / (ybot - ytop);
01066 }
01067 
01068 
01069 /** Postscript coordinates start at (0,0) for the lower left hand corner 
01070  * of the page and increase upwards and to the right.  For 8.5 x 11     
01071  * sheet, coordinates go from (0,0) to (612,792).  Spacing is 1/72 inch.
01072  * I'm leaving a minimum of half an inch (36 units) of border around    
01073  * each edge.                                                           
01074  */
01075 static void
01076 update_ps_transform(void)
01077 {
01078     float ps_width, ps_height;
01079 
01080     ps_width = 540.;            /* 72 * 7.5 */
01081     ps_height = 720.;           /* 72 * 10  */
01082 
01083     ps_xmult = ps_width / (xright - xleft);
01084     ps_ymult = ps_height / (ytop - ybot);
01085 /* Need to use same scaling factor to preserve aspect ratio.   *
01086  * I show exactly as much on paper as the screen window shows, *
01087  * or the user specifies.                                      */
01088     if(fabs(ps_xmult) <= fabs(ps_ymult))
01089         {
01090             ps_left = 36.;
01091             ps_right = 36. + ps_width;
01092             ps_bot = 396. - fabs(ps_xmult * (ytop - ybot)) / 2.;
01093             ps_top = 396. + fabs(ps_xmult * (ytop - ybot)) / 2.;
01094 /* Maintain aspect ratio but watch signs */
01095             ps_ymult = (ps_xmult * ps_ymult < 0) ? -ps_xmult : ps_xmult;
01096         }
01097     else
01098         {
01099             ps_bot = 36.;
01100             ps_top = 36. + ps_height;
01101             ps_left = 306. - fabs(ps_ymult * (xright - xleft)) / 2.;
01102             ps_right = 306. + fabs(ps_ymult * (xright - xleft)) / 2.;
01103 /* Maintain aspect ratio but watch signs */
01104             ps_xmult = (ps_xmult * ps_ymult < 0) ? -ps_ymult : ps_ymult;
01105         }
01106 }
01107 
01108 
01109 /** The program's main event loop.  Must be passed a user routine        
01110  * drawscreen which redraws the screen.  It handles all window resizing 
01111  * zooming etc. itself.  If the user clicks a button in the graphics    
01112  * (toplevel) area, the act_on_button routine passed in is called.      
01113  */
01114 void
01115 event_loop(void (*act_on_button) (float x,
01116                                   float y),
01117            void (*drawscreen) (void))
01118 {
01119     XEvent report;
01120     int bnum;
01121     float x, y;
01122 
01123 #define OFF 1
01124 #define ON 0
01125 
01126     turn_on_off(ON);
01127     while(1)
01128         {
01129             XNextEvent(display, &report);
01130             switch (report.type)
01131                 {
01132                 case Expose:
01133 #ifdef VERBOSE
01134                     printf("Got an expose event.\n");
01135                     printf("Count is: %d.\n", report.xexpose.count);
01136                     printf("Window ID is: %d.\n", report.xexpose.window);
01137 #endif
01138                     if(report.xexpose.count != 0)
01139                         break;
01140                     if(report.xexpose.window == menu)
01141                         drawmenu();
01142                     else if(report.xexpose.window == toplevel)
01143                         drawscreen();
01144                     else if(report.xexpose.window == textarea)
01145                         draw_message();
01146                     break;
01147                 case ConfigureNotify:
01148                     top_width = report.xconfigure.width;
01149                     top_height = report.xconfigure.height;
01150                     update_transform();
01151 #ifdef VERBOSE
01152                     printf("Got a ConfigureNotify.\n");
01153                     printf("New width: %d  New height: %d.\n", top_width,
01154                            top_height);
01155 #endif
01156                     break;
01157                 case ButtonPress:
01158 #ifdef VERBOSE
01159                     printf("Got a buttonpress.\n");
01160                     printf("Window ID is: %d.\n", report.xbutton.window);
01161 #endif
01162                     if(report.xbutton.window == toplevel)
01163                         {
01164                             x = XTOWORLD(report.xbutton.x);
01165                             y = YTOWORLD(report.xbutton.y);
01166                             act_on_button(x, y);
01167                         }
01168                     else
01169                         {       /* A menu button was pressed. */
01170                             bnum = which_button(report.xbutton.window);
01171 #ifdef VERBOSE
01172                             printf("Button number is %d\n", bnum);
01173 #endif
01174                             button[bnum].ispressed = 1;
01175                             drawbut(bnum);
01176                             XFlush(display);    /* Flash the button */
01177                             button[bnum].fcn(drawscreen);
01178                             button[bnum].ispressed = 0;
01179                             drawbut(bnum);
01180                             if(button[bnum].fcn == proceed)
01181                                 {
01182                                     turn_on_off(OFF);
01183                                     flushinput();
01184                                     return;     /* Rather clumsy way of returning *
01185                                                  * control to the simulator       */
01186                                 }
01187                         }
01188                     break;
01189                 }
01190         }
01191 }
01192 
01193 
01194 
01195 void
01196 clearscreen(void)
01197 {
01198     int savecolor;
01199 
01200     if(disp_type == SCREEN)
01201         {
01202             XClearWindow(display, toplevel);
01203         }
01204     else
01205         {
01206 /* erases current page.  Don't use erasepage, since this will erase *
01207  * everything, (even stuff outside the clipping path) causing       *
01208  * problems if this picture is incorporated into a larger document. */
01209             savecolor = currentcolor;
01210             setcolor(WHITE);
01211             fprintf(ps, "clippath fill\n\n");
01212             setcolor(savecolor);
01213         }
01214 }
01215 
01216 /** Return 1 if I can quarantee no part of this rectangle will         
01217  * lie within the user drawing area.  Otherwise return 0.             
01218  * Note:  this routine is only used to help speed (and to shrink ps   
01219  * files) -- it will be highly effective when the graphics are zoomed 
01220  * in and lots are off-screen.  I don't have to pre-clip for          
01221  * correctness.                                                       
01222  */
01223 static int
01224 rect_off_screen(float x1,
01225                 float y1,
01226                 float x2,
01227                 float y2)
01228 {
01229     float xmin, xmax, ymin, ymax;
01230 
01231     xmin = min(xleft, xright);
01232     if(x1 < xmin && x2 < xmin)
01233         return (1);
01234 
01235     xmax = max(xleft, xright);
01236     if(x1 > xmax && x2 > xmax)
01237         return (1);
01238 
01239     ymin = min(ytop, ybot);
01240     if(y1 < ymin && y2 < ymin)
01241         return (1);
01242 
01243     ymax = max(ytop, ybot);
01244     if(y1 > ymax && y2 > ymax)
01245         return (1);
01246 
01247     return (0);
01248 }
01249 
01250 
01251 /** Draw a line from (x1,y1) to (x2,y2) in the user-drawable area. 
01252  * Coordinates are in world (user) space.                         
01253  */
01254 void
01255 drawline(float x1,
01256          float y1,
01257          float x2,
01258          float y2)
01259 {
01260     if(rect_off_screen(x1, y1, x2, y2))
01261         return;
01262 
01263     if(disp_type == SCREEN)
01264         {
01265             /* Xlib.h prototype has x2 and y1 mixed up. */
01266             XDrawLine(display, toplevel, gc, xcoord(x1), ycoord(y1),
01267                       xcoord(x2), ycoord(y2));
01268         }
01269     else
01270         {
01271             fprintf(ps, "%.2f %.2f %.2f %.2f drawline\n", XPOST(x1),
01272                     YPOST(y1), XPOST(x2), YPOST(y2));
01273         }
01274 }
01275 
01276 /** (x1,y1) and (x2,y2) are diagonally opposed corners, in world coords. */
01277 void
01278 drawrect(float x1,
01279          float y1,
01280          float x2,
01281          float y2)
01282 {
01283     unsigned int width, height;
01284     int xw1, yw1, xw2, yw2, xl, yt;
01285 
01286     if(rect_off_screen(x1, y1, x2, y2))
01287         return;
01288 
01289     if(disp_type == SCREEN)
01290         {
01291 /* translate to X Windows calling convention. */
01292             xw1 = xcoord(x1);
01293             xw2 = xcoord(x2);
01294             yw1 = ycoord(y1);
01295             yw2 = ycoord(y2);
01296             xl = min(xw1, xw2);
01297             yt = min(yw1, yw2);
01298             width = abs(xw1 - xw2);
01299             height = abs(yw1 - yw2);
01300             XDrawRectangle(display, toplevel, gc, xl, yt, width, height);
01301         }
01302     else
01303         {
01304             fprintf(ps, "%.2f %.2f %.2f %.2f drawrect\n", XPOST(x1),
01305                     YPOST(y1), XPOST(x2), YPOST(y2));
01306         }
01307 }
01308 
01309 
01310 /** (x1,y1) and (x2,y2) are diagonally opposed corners in world coords. */
01311 void
01312 fillrect(float x1,
01313          float y1,
01314          float x2,
01315          float y2)
01316 {
01317     unsigned int width, height;
01318     int xw1, yw1, xw2, yw2, xl, yt;
01319 
01320     if(rect_off_screen(x1, y1, x2, y2))
01321         return;
01322 
01323     if(disp_type == SCREEN)
01324         {
01325 /* translate to X Windows calling convention. */
01326             xw1 = xcoord(x1);
01327             xw2 = xcoord(x2);
01328             yw1 = ycoord(y1);
01329             yw2 = ycoord(y2);
01330             xl = min(xw1, xw2);
01331             yt = min(yw1, yw2);
01332             width = abs(xw1 - xw2);
01333             height = abs(yw1 - yw2);
01334             XFillRectangle(display, toplevel, gc, xl, yt, width, height);
01335         }
01336     else
01337         {
01338             fprintf(ps, "%.2f %.2f %.2f %.2f fillrect\n", XPOST(x1),
01339                     YPOST(y1), XPOST(x2), YPOST(y2));
01340         }
01341 }
01342 
01343 
01344 /** Normalizes an angle to be between 0 and 360 degrees. */
01345 static float
01346 angnorm(float ang)
01347 {
01348     int scale;
01349 
01350     if(ang < 0)
01351         {
01352             scale = (int)(ang / 360. - 1);
01353         }
01354     else
01355         {
01356             scale = (int)(ang / 360.);
01357         }
01358     ang = ang - scale * 360.;
01359     return (ang);
01360 }
01361 
01362 
01363 /** Draws a circular arc.  X11 can do elliptical arcs quite simply, and 
01364  * PostScript could do them by scaling the coordinate axes.  Too much  
01365  * work for now, and probably too complex an object for users to draw  
01366  * much, so I'm just doing circular arcs.  Startang is relative to the 
01367  * Window's positive x direction.  Angles in degrees.                  
01368  */
01369 void
01370 drawarc(float xc,
01371         float yc,
01372         float rad,
01373         float startang,
01374         float angextent)
01375 {
01376     int xl, yt;
01377     unsigned int width, height;
01378 
01379 /* Conservative (but fast) clip test -- check containing rectangle of *
01380  * a circle.                                                          */
01381 
01382     if(rect_off_screen(xc - rad, yc - rad, xc + rad, yc + rad))
01383         return;
01384 
01385 /* X Windows has trouble with very large angles. (Over 360).    *
01386  * Do following to prevent its inaccurate (overflow?) problems. */
01387     if(fabs(angextent) > 360.)
01388         angextent = 360.;
01389 
01390     startang = angnorm(startang);
01391 
01392     if(disp_type == SCREEN)
01393         {
01394             xl = (int)(xcoord(xc) - fabs(xmult * rad));
01395             yt = (int)(ycoord(yc) - fabs(ymult * rad));
01396             width = (unsigned int)(2 * fabs(xmult * rad));
01397             height = width;
01398             XDrawArc(display, toplevel, gc, xl, yt, width, height,
01399                      (int)(startang * 64), (int)(angextent * 64));
01400         }
01401     else
01402         {
01403             fprintf(ps, "%.2f %.2f %.2f %.2f %.2f %s stroke\n", XPOST(xc),
01404                     YPOST(yc), fabs(rad * ps_xmult), startang,
01405                     startang + angextent,
01406                     (angextent < 0) ? "drawarcn" : "drawarc");
01407         }
01408 }
01409 
01410 
01411 /** Fills a circular arc.  Startang is relative to the Window's positive x   
01412  * direction.  Angles in degrees.                                           
01413  */
01414 void
01415 fillarc(float xc,
01416         float yc,
01417         float rad,
01418         float startang,
01419         float angextent)
01420 {
01421 
01422     int xl, yt;
01423     unsigned int width, height;
01424 
01425 /* Conservative (but fast) clip test -- check containing rectangle of *
01426  * a circle.                                                          */
01427 
01428     if(rect_off_screen(xc - rad, yc - rad, xc + rad, yc + rad))
01429         return;
01430 
01431 /* X Windows has trouble with very large angles. (Over 360).    *
01432  * Do following to prevent its inaccurate (overflow?) problems. */
01433 
01434     if(fabs(angextent) > 360.)
01435         angextent = 360.;
01436 
01437     startang = angnorm(startang);
01438 
01439     if(disp_type == SCREEN)
01440         {
01441             xl = (int)(xcoord(xc) - fabs(xmult * rad));
01442             yt = (int)(ycoord(yc) - fabs(ymult * rad));
01443             width = (unsigned int)(2 * fabs(xmult * rad));
01444             height = width;
01445             XFillArc(display, toplevel, gc, xl, yt, width, height,
01446                      (int)(startang * 64), (int)(angextent * 64));
01447         }
01448     else
01449         {
01450             fprintf(ps, "%.2f %.2f %.2f %.2f %.2f %s\n", fabs(rad * ps_xmult),
01451                     startang, startang + angextent, XPOST(xc), YPOST(yc),
01452                     (angextent < 0) ? "fillarcn" : "fillarc");
01453         }
01454 }
01455 
01456 
01457 void
01458 fillpoly(t_point * points,
01459          int npoints)
01460 {
01461 
01462     XPoint transpoints[MAXPTS];
01463     int i;
01464     float xmin, ymin, xmax, ymax;
01465 
01466     if(npoints > MAXPTS)
01467         {
01468             printf
01469                 ("Error in fillpoly:  Only %d points allowed per polygon.\n",
01470                  MAXPTS);
01471             printf("%d points were requested.  Polygon is not drawn.\n",
01472                    npoints);
01473             return;
01474         }
01475 
01476 /* Conservative (but fast) clip test -- check containing rectangle of *
01477  * polygon.                                                           */
01478 
01479     xmin = xmax = points[0].x;
01480     ymin = ymax = points[0].y;
01481 
01482     for(i = 1; i < npoints; i++)
01483         {
01484             xmin = min(xmin, points[i].x);
01485             xmax = max(xmax, points[i].x);
01486             ymin = min(ymin, points[i].y);
01487             ymax = max(ymax, points[i].y);
01488         }
01489 
01490     if(rect_off_screen(xmin, ymin, xmax, ymax))
01491         return;
01492 
01493     if(disp_type == SCREEN)
01494         {
01495             for(i = 0; i < npoints; i++)
01496                 {
01497                     transpoints[i].x = (short)xcoord(points[i].x);
01498                     transpoints[i].y = (short)ycoord(points[i].y);
01499                 }
01500             XFillPolygon(display, toplevel, gc, transpoints, npoints, Complex,
01501                          CoordModeOrigin);
01502         }
01503     else
01504         {
01505             fprintf(ps, "\n");
01506 
01507             for(i = npoints - 1; i >= 0; i--)
01508                 fprintf(ps, "%.2f %.2f\n", XPOST(points[i].x),
01509                         YPOST(points[i].y));
01510 
01511             fprintf(ps, "%d fillpoly\n", npoints);
01512         }
01513 }
01514 
01515 /** Draws text centered on xc,yc if it fits in boundx */
01516 void
01517 drawtext(float xc,
01518          float yc,
01519          const char *text,
01520          float boundx)
01521 {
01522     int len, width, xw_off, yw_off;
01523 
01524     len = strlen(text);
01525     width = XTextWidth(font_info[currentfontsize], text, len);
01526     if(width > fabs(boundx * xmult))
01527         return;                 /* Don't draw if it won't fit */
01528 
01529     xw_off = width / (2. * xmult);      /* NB:  sign doesn't matter. */
01530 
01531 /* NB:  2 * descent makes this slightly conservative but simplifies code. */
01532     yw_off = (font_info[currentfontsize]->ascent +
01533               2 * font_info[currentfontsize]->descent) / (2. * ymult);
01534 
01535 /* Note:  text can be clipped when a little bit of it would be visible *
01536  * right now.  Perhaps X doesn't return extremely accurate width and   *
01537  * ascent values, etc?  Could remove this completely by multiplying    *
01538  * xw_off and yw_off by, 1.2 or 1.5.                                   */
01539 
01540     if(rect_off_screen(xc - xw_off, yc - yw_off, xc + xw_off, yc + yw_off))
01541         return;
01542 
01543     if(disp_type == SCREEN)
01544         {
01545             XDrawString(display, toplevel, gc, xcoord(xc) - width / 2,
01546                         ycoord(yc) + (font_info[currentfontsize]->ascent -
01547                                       font_info[currentfontsize]->descent) /
01548                         2, text, len);
01549         }
01550     else
01551         {
01552             fprintf(ps, "(%s) %.2f %.2f censhow\n", text, XPOST(xc),
01553                     YPOST(yc));
01554         }
01555 }
01556 
01557 
01558 void
01559 flushinput(void)
01560 {
01561     if(disp_type != SCREEN)
01562         return;
01563     XFlush(display);
01564 }
01565 
01566 
01567 /** Sets the coordinate system the user wants to draw into.          */
01568 void
01569 init_world(float x1,
01570            float y1,
01571            float x2,
01572            float y2)
01573 {
01574     xleft = x1;
01575     xright = x2;
01576     ytop = y1;
01577     ybot = y2;
01578 
01579     saved_xleft = xleft;        /* Save initial world coordinates to allow full */
01580     saved_xright = xright;      /* view button to zoom all the way out.         */
01581     saved_ytop = ytop;
01582     saved_ybot = ybot;
01583 
01584     if(disp_type == SCREEN)
01585         {
01586             update_transform();
01587         }
01588     else
01589         {
01590             update_ps_transform();
01591         }
01592 }
01593 
01594 
01595 /** Draw the current message in the text area at the screen bottom. */
01596 void
01597 draw_message(void)
01598 {
01599     int len, width, savefontsize, savecolor;
01600     float ylow;
01601 
01602     if(disp_type == SCREEN)
01603         {
01604             XClearWindow(display, textarea);
01605             len = strlen(message);
01606             width = XTextWidth(font_info[menu_font_size], message, len);
01607 
01608             XSetForeground(display, gc_menus, colors[BLACK]);
01609             XDrawString(display, textarea, gc_menus,
01610                         (top_width - MWIDTH - width) / 2,
01611                         (T_AREA_HEIGHT - 4) / 2 +
01612                         (font_info[menu_font_size]->ascent -
01613                          font_info[menu_font_size]->descent) / 2, message,
01614                         len);
01615         }
01616 
01617     else
01618         {
01619 /* Draw the message in the bottom margin.  Printer's generally can't  *
01620  * print on the bottom 1/4" (area with y < 18 in PostScript coords.)  */
01621 
01622             savecolor = currentcolor;
01623             setcolor(BLACK);
01624             savefontsize = currentfontsize;
01625             setfontsize(menu_font_size - 2);    /* Smaller OK on paper */
01626             ylow = ps_bot - 8.;
01627             fprintf(ps, "(%s) %.2f %.2f censhow\n", message,
01628                     (ps_left + ps_right) / 2., ylow);
01629             setcolor(savecolor);
01630             setfontsize(savefontsize);
01631         }
01632 }
01633 
01634 
01635 /** Changes the message to be displayed on screen.   */
01636 void
01637 update_message(char *msg)
01638 {
01639     my_strncpy(message, msg, BUFSIZE);
01640     draw_message();
01641 }
01642 
01643 
01644 /** Zooms in by a factor of 1.666. */
01645 static void
01646 zoom_in(void (*drawscreen) (void))
01647 {
01648     float xdiff, ydiff;
01649 
01650     xdiff = xright - xleft;
01651     ydiff = ybot - ytop;
01652     xleft += xdiff / 5.;
01653     xright -= xdiff / 5.;
01654     ytop += ydiff / 5.;
01655     ybot -= ydiff / 5.;
01656 
01657     update_transform();
01658     drawscreen();
01659 }
01660 
01661 
01662 /** Zooms out by a factor of 1.666. */
01663 static void
01664 zoom_out(void (*drawscreen) (void))
01665 {
01666     float xdiff, ydiff;
01667 
01668     xdiff = xright - xleft;
01669     ydiff = ybot - ytop;
01670     xleft -= xdiff / 3.;
01671     xright += xdiff / 3.;
01672     ytop -= ydiff / 3.;
01673     ybot += ydiff / 3.;
01674 
01675     update_transform();
01676     drawscreen();
01677 }
01678 
01679 
01680 /** Sets the view back to the initial view set by init_world (i.e. a full     
01681   * view) of all the graphics.                                                
01682   */
01683 static void
01684 zoom_fit(void (*drawscreen) (void))
01685 {
01686 
01687     xleft = saved_xleft;
01688     xright = saved_xright;
01689     ytop = saved_ytop;
01690     ybot = saved_ybot;
01691 
01692     update_transform();
01693     drawscreen();
01694 }
01695 
01696 
01697 /** Moves view 1/2 screen up. */
01698 static void
01699 translate_up(void (*drawscreen) (void))
01700 {
01701     float ystep;
01702 
01703     ystep = (ybot - ytop) / 2.;
01704     ytop -= ystep;
01705     ybot -= ystep;
01706     update_transform();
01707     drawscreen();
01708 }
01709 
01710 
01711 /** Moves view 1/2 screen down. */
01712 static void
01713 translate_down(void (*drawscreen) (void))
01714 {
01715     float ystep;
01716 
01717     ystep = (ybot - ytop) / 2.;
01718     ytop += ystep;
01719     ybot += ystep;
01720     update_transform();
01721     drawscreen();
01722 }
01723 
01724 
01725 /** Moves view 1/2 screen left. */
01726 static void
01727 translate_left(void (*drawscreen) (void))
01728 {
01729     float xstep;
01730 
01731     xstep = (xright - xleft) / 2.;
01732     xleft -= xstep;
01733     xright -= xstep;
01734     update_transform();
01735     drawscreen();
01736 }
01737 
01738 
01739 /** Moves view 1/2 screen right. */
01740 static void
01741 translate_right(void (*drawscreen) (void))
01742 {
01743     float xstep;
01744 
01745     xstep = (xright - xleft) / 2.;
01746     xleft += xstep;
01747     xright += xstep;
01748     update_transform();
01749     drawscreen();
01750 }
01751 
01752 
01753 static void
01754 update_win(int x[2],
01755            int y[2],
01756            void (*drawscreen) (void))
01757 {
01758     float x1, x2, y1, y2;
01759 
01760     x[0] = min(x[0], top_width - MWIDTH);       /* Can't go under menu */
01761     x[1] = min(x[1], top_width - MWIDTH);
01762     y[0] = min(y[0], top_height - T_AREA_HEIGHT);       /* Can't go under text area */
01763     y[1] = min(y[1], top_height - T_AREA_HEIGHT);
01764 
01765     if((x[0] == x[1]) || (y[0] == y[1]))
01766         {
01767             printf("Illegal (zero area) window.  Window unchanged.\n");
01768             return;
01769         }
01770     x1 = XTOWORLD(min(x[0], x[1]));
01771     x2 = XTOWORLD(max(x[0], x[1]));
01772     y1 = YTOWORLD(min(y[0], y[1]));
01773     y2 = YTOWORLD(max(y[0], y[1]));
01774     xleft = x1;
01775     xright = x2;
01776     ytop = y1;
01777     ybot = y2;
01778     update_transform();
01779     drawscreen();
01780 }
01781 
01782 
01783 /** The window button was pressed.  Let the user click on the two 
01784  * diagonally opposed corners, and zoom in on this area.         
01785  */
01786 static void
01787 adjustwin(void (*drawscreen) (void))
01788 {
01789     XEvent report;
01790     int corner, xold, yold, x[2], y[2];
01791 
01792     corner = 0;
01793     xold = -1;
01794     yold = -1;                  /* Don't need to init yold, but stops compiler warning. */
01795 
01796     while(corner < 2)
01797         {
01798             XNextEvent(display, &report);
01799             switch (report.type)
01800                 {
01801                 case Expose:
01802 #ifdef VERBOSE
01803                     printf("Got an expose event.\n");
01804                     printf("Count is: %d.\n", report.xexpose.count);
01805                     printf("Window ID is: %d.\n", report.xexpose.window);
01806 #endif
01807                     if(report.xexpose.count != 0)
01808                         break;
01809                     if(report.xexpose.window == menu)
01810                         drawmenu();
01811                     else if(report.xexpose.window == toplevel)
01812                         {
01813                             drawscreen();
01814                             xold = -1;  /* No rubber band on screen */
01815                         }
01816                     else if(report.xexpose.window == textarea)
01817                         draw_message();
01818                     break;
01819                 case ConfigureNotify:
01820                     top_width = report.xconfigure.width;
01821                     top_height = report.xconfigure.height;
01822                     update_transform();
01823 #ifdef VERBOSE
01824                     printf("Got a ConfigureNotify.\n");
01825                     printf("New width: %d  New height: %d.\n", top_width,
01826                            top_height);
01827 #endif
01828                     break;
01829                 case ButtonPress:
01830 #ifdef VERBOSE
01831                     printf("Got a buttonpress.\n");
01832                     printf("Window ID is: %d.\n", report.xbutton.window);
01833                     printf("Location (%d, %d).\n", report.xbutton.x,
01834                            report.xbutton.y);
01835 #endif
01836                     if(report.xbutton.window != toplevel)
01837                         break;
01838                     x[corner] = report.xbutton.x;
01839                     y[corner] = report.xbutton.y;
01840                     if(corner == 0)
01841                         {
01842                             XSelectInput(display, toplevel, ExposureMask |
01843                                          StructureNotifyMask | ButtonPressMask
01844                                          | PointerMotionMask);
01845                         }
01846                     else
01847                         {
01848                             update_win(x, y, drawscreen);
01849                         }
01850                     corner++;
01851                     break;
01852                 case MotionNotify:
01853 #ifdef VERBOSE
01854                     printf("Got a MotionNotify Event.\n");
01855                     printf("x: %d    y: %d\n", report.xmotion.x,
01856                            report.xmotion.y);
01857 #endif
01858                     if(xold >= 0)
01859                         {       /* xold set -ve before we draw first box */
01860                             XDrawRectangle(display, toplevel, gcxor,
01861                                            min(x[0], xold), min(y[0], yold),
01862                                            abs(x[0] - xold),
01863                                            abs(y[0] - yold));
01864                         }
01865                     /* Don't allow user to window under menu region */
01866                     xold = min(report.xmotion.x, top_width - 1 - MWIDTH);
01867                     yold = report.xmotion.y;
01868                     XDrawRectangle(display, toplevel, gcxor, min(x[0], xold),
01869                                    min(y[0], yold), abs(x[0] - xold),
01870                                    abs(y[0] - yold));
01871                     break;
01872                 }
01873         }
01874     XSelectInput(display, toplevel, ExposureMask | StructureNotifyMask
01875                  | ButtonPressMask);
01876 }
01877 
01878 
01879 /** Takes a snapshot of the screen and stores it in pic?.ps.  The 
01880  * first picture goes in pic1.ps, the second in pic2.ps, etc.    
01881  */
01882 static void
01883 postscript(void (*drawscreen) (void))
01884 {
01885     static int piccount = 1;
01886     int success;
01887     char fname[20];
01888 
01889     sprintf(fname, "pic%d.ps", piccount);
01890     success = init_postscript(fname);
01891 
01892     if(success == 0)
01893         return;                 /* Couldn't open file, abort. */
01894 
01895     drawscreen();
01896     close_postscript();
01897     piccount++;
01898 }
01899 
01900 
01901 /** Dummy routine.  Just exit the event loop. */
01902 static void
01903 proceed(void (*drawscreen) (void))
01904 {
01905 
01906     /* Dummy routine.  Just exit the event loop. */
01907 
01908 }
01909 
01910 
01911 static void
01912 quit(void (*drawscreen) (void))
01913 {
01914 
01915     close_graphics();
01916     exit(0);
01917 }
01918 
01919 
01920 /** Release all my drawing structures (through the X server) and 
01921   * close down the connection.                                   
01922   */
01923 void
01924 close_graphics(void)
01925 {
01926     int i;
01927 
01928     for(i = 1; i <= MAX_FONT_SIZE; i++)
01929         if(font_is_loaded[i])
01930             XFreeFont(display, font_info[i]);
01931 
01932     XFreeGC(display, gc);
01933     XFreeGC(display, gcxor);
01934     XFreeGC(display, gc_menus);
01935 
01936     if(private_cmap != None)
01937         XFreeColormap(display, private_cmap);
01938 
01939     XCloseDisplay(display);
01940     free(button);
01941 }
01942 
01943 
01944 /** Opens a file for PostScript output.  The header information,  
01945  * clipping path, etc. are all dumped out.  If the file could    
01946  * not be opened, the routine returns 0; otherwise it returns 1. 
01947  */
01948 int
01949 init_postscript(char *fname)
01950 {
01951     ps = fopen(fname, "w");
01952     if(ps == NULL)
01953         {
01954             printf("Error: could not open %s for PostScript output.\n",
01955                    fname);
01956             printf("Drawing to screen instead.\n");
01957             return (0);
01958         }
01959     disp_type = POSTSCRIPT;     /* Graphics go to postscript file now. */
01960 
01961 /* Header for minimal conformance with the Adobe structuring convention */
01962     fprintf(ps, "%%!PS-Adobe-1.0\n");
01963     fprintf(ps, "%%%%DocumentFonts: Helvetica\n");
01964     fprintf(ps, "%%%%Pages: 1\n");
01965 /* Set up postscript transformation macros and page boundaries */
01966     update_ps_transform();
01967 /* Bottom margin is at ps_bot - 15. to leave room for the on-screen message. */
01968     fprintf(ps, "%%%%BoundingBox: %d %d %d %d\n",
01969             (int)ps_left, (int)(ps_bot - 15.), (int)ps_right, (int)ps_top);
01970     fprintf(ps, "%%%%EndComments\n");
01971 
01972     fprintf(ps, "/censhow   %%draw a centered string\n");
01973     fprintf(ps, " { moveto               %% move to proper spot\n");
01974     fprintf(ps, "   dup stringwidth pop  %% get x length of string\n");
01975     fprintf(ps, "   -2 div               %% Proper left start\n");
01976     fprintf(ps,
01977             "   yoff rmoveto         %% Move left that much and down half font height\n");
01978     fprintf(ps, "   show newpath } def   %% show the string\n\n");
01979 
01980     fprintf(ps, "/setfontsize     %% set font to desired size and compute "
01981             "centering yoff\n");
01982     fprintf(ps, " { /Helvetica findfont\n");
01983     fprintf(ps, "   exch scalefont\n");
01984     fprintf(ps, "   setfont         %% Font size set ...\n\n");
01985     fprintf(ps, "   0 0 moveto      %% Get vertical centering offset\n");
01986     fprintf(ps, "   (Xg) true charpath\n");
01987     fprintf(ps, "   flattenpath pathbbox\n");
01988     fprintf(ps, "   /ascent exch def pop -1 mul /descent exch def pop\n");
01989     fprintf(ps, "   newpath\n");
01990     fprintf(ps, "   descent ascent sub 2 div /yoff exch def } def\n\n");
01991 
01992     fprintf(ps, "%% Next two lines for debugging only.\n");
01993     fprintf(ps, "/str 20 string def\n");
01994     fprintf(ps, "/pnum {str cvs print (  ) print} def\n");
01995 
01996     fprintf(ps, "/drawline      %% draw a line from (x2,y2) to (x1,y1)\n");
01997     fprintf(ps, " { moveto lineto stroke } def\n\n");
01998 
01999     fprintf(ps, "/rect          %% outline a rectangle \n");
02000     fprintf(ps, " { /y2 exch def /x2 exch def /y1 exch def /x1 exch def\n");
02001     fprintf(ps, "   x1 y1 moveto\n");
02002     fprintf(ps, "   x2 y1 lineto\n");
02003     fprintf(ps, "   x2 y2 lineto\n");
02004     fprintf(ps, "   x1 y2 lineto\n");
02005     fprintf(ps, "   closepath } def\n\n");
02006 
02007     fprintf(ps, "/drawrect      %% draw outline of a rectanagle\n");
02008     fprintf(ps, " { rect stroke } def\n\n");
02009 
02010     fprintf(ps, "/fillrect      %% fill in a rectanagle\n");
02011     fprintf(ps, " { rect fill } def\n\n");
02012 
02013     fprintf(ps, "/drawarc { arc stroke } def           %% draw an arc\n");
02014     fprintf(ps, "/drawarcn { arcn stroke } def "
02015             "        %% draw an arc in the opposite direction\n\n");
02016 
02017     fprintf(ps, "%%Fill a counterclockwise or clockwise arc sector, "
02018             "respectively.\n");
02019     fprintf(ps,
02020             "/fillarc { moveto currentpoint 5 2 roll arc closepath fill } "
02021             "def\n");
02022     fprintf(ps,
02023             "/fillarcn { moveto currentpoint 5 2 roll arcn closepath fill } "
02024             "def\n\n");
02025 
02026     fprintf(ps,
02027             "/fillpoly { 3 1 roll moveto         %% move to first point\n"
02028             "   2 exch 1 exch {pop lineto} for   %% line to all other points\n"
02029             "   closepath fill } def\n\n");
02030 
02031 
02032     fprintf(ps, "%%Color Definitions:\n");
02033     fprintf(ps, "/white { 1 setgray } def\n");
02034     fprintf(ps, "/black { 0 setgray } def\n");
02035     fprintf(ps, "/grey55 { .55 setgray } def\n");
02036     fprintf(ps, "/grey75 { .75 setgray } def\n");
02037     fprintf(ps, "/blue { 0 0 1 setrgbcolor } def\n");
02038     fprintf(ps, "/green { 0 1 0 setrgbcolor } def\n");
02039     fprintf(ps, "/yellow { 1 1 0 setrgbcolor } def\n");
02040     fprintf(ps, "/cyan { 0 1 1 setrgbcolor } def\n");
02041     fprintf(ps, "/red { 1 0 0 setrgbcolor } def\n");
02042     fprintf(ps, "/darkgreen { 0 0.5 0 setrgbcolor } def\n");
02043     fprintf(ps, "/magenta { 1 0 1 setrgbcolor } def\n");
02044         fprintf(ps, "/bisque { 1 0.9 0.8 setrgbcolor } def\n");
02045         fprintf(ps, "/lightblue { 0.7 0.8 0.9 setrgbcolor } def\n");
02046         fprintf(ps, "/thistle { 0.8 0.7 0.8 setrgbcolor } def\n");
02047         fprintf(ps, "/plum {0.8 0.6 0.8 setrgbcolor } def\n");
02048         fprintf(ps, "/khaki { 1 0.9 0.6 setrgbcolor } def\n");
02049         fprintf(ps, "/coral { 1 0.7 0.6 setrgbcolor } def\n");
02050         fprintf(ps, "/turquoise { 0.5 0.6 0.9 setrgbcolor } def\n");
02051         fprintf(ps, "/mediumpurple { 0.7 0.6 0.7 setrgbcolor } def\n");
02052         fprintf(ps, "/darkslateblue { 0.7 0.5 0.7 setrgbcolor } def\n");
02053         fprintf(ps, "/darkkhaki { 0.9 0.7 0.4 setrgbcolor } def\n");
02054 
02055     fprintf(ps, "\n%%Solid and dashed line definitions:\n");
02056     fprintf(ps, "/linesolid {[] 0 setdash} def\n");
02057     fprintf(ps, "/linedashed {[3 3] 0 setdash} def\n");
02058 
02059     fprintf(ps, "\n%%%%EndProlog\n");
02060     fprintf(ps, "%%%%Page: 1 1\n\n");
02061 
02062 /* Set up PostScript graphics state to match current one. */
02063     force_setcolor(currentcolor);
02064     force_setlinestyle(currentlinestyle);
02065     force_setlinewidth(currentlinewidth);
02066     force_setfontsize(currentfontsize);
02067 
02068 /* Draw this in the bottom margin -- must do before the clippath is set */
02069     draw_message();
02070 
02071 /* Set clipping on page. */
02072     fprintf(ps, "%.2f %.2f %.2f %.2f rect ", ps_left, ps_bot, ps_right,
02073             ps_top);
02074     fprintf(ps, "clip newpath\n\n");
02075 
02076     return (1);
02077 }
02078 
02079 /** Properly ends postscript output and redirects output to screen. */
02080 void
02081 close_postscript(void)
02082 {
02083     fprintf(ps, "showpage\n");
02084     fprintf(ps, "\n%%%%Trailer\n");
02085     fclose(ps);
02086     disp_type = SCREEN;
02087     update_transform();         /* Ensure screen world reflects any changes      *
02088                                  * made while printing.                          */
02089 
02090 /* Need to make sure that we really set up the graphics context --  *
02091  * don't want the change requested to match the current setting and *
02092  * do nothing -> force the changes.                                 */
02093 
02094     force_setcolor(currentcolor);
02095     force_setlinestyle(currentlinestyle);
02096     force_setlinewidth(currentlinewidth);
02097     force_setfontsize(currentfontsize);
02098 }
02099 
02100 #else /* NO_GRAPHICS build -- rip out graphics */
02101 
02102 void
02103 event_loop(void (*act_on_button) (float x,
02104                                   float y),
02105            void (*drawscreen) (void))
02106 {
02107 }
02108 
02109 void
02110 init_graphics(char *window_name)
02111 {
02112 }
02113 void
02114 close_graphics(void)
02115 {
02116 }
02117 void
02118 update_message(char *msg)
02119 {
02120 }
02121 void
02122 draw_message(void)
02123 {
02124 }
02125 void
02126 init_world(float xl,
02127            float yt,
02128            float xr,
02129            float yb)
02130 {
02131 }
02132 void
02133 flushinput(void)
02134 {
02135 }
02136 void
02137 setcolor(int cindex)
02138 {
02139 }
02140 int
02141 getcolor(int cindex)
02142 {
02143         return 0;
02144 }
02145 void
02146 setlinestyle(int linestyle)
02147 {
02148 }
02149 void
02150 setlinewidth(int linewidth)
02151 {
02152 }
02153 void
02154 setfontsize(int pointsize)
02155 {
02156 }
02157 void
02158 drawline(float x1,
02159          float y1,
02160          float x2,
02161          float y2)
02162 {
02163 }
02164 void
02165 drawrect(float x1,
02166          float y1,
02167          float x2,
02168          float y2)
02169 {
02170 }
02171 void
02172 fillrect(float x1,
02173          float y1,
02174          float x2,
02175          float y2)
02176 {
02177 }
02178 void
02179 fillpoly(t_point * points,
02180          int npoints)
02181 {
02182 }
02183 void
02184 drawarc(float xcen,
02185         float ycen,
02186         float rad,
02187         float startang,
02188         float angextent)
02189 {
02190 }
02191 
02192 void
02193 fillarc(float xcen,
02194         float ycen,
02195         float rad,
02196         float startang,
02197         float angextent)
02198 {
02199 }
02200 
02201 void
02202 drawtext(float xc,
02203          float yc,
02204          const char *text,
02205          float boundx)
02206 {
02207 }
02208 void
02209 clearscreen(void)
02210 {
02211 }
02212 
02213 void
02214 create_button(char *prev_button_text,
02215               char *button_text,
02216               void (*button_func) (void (*drawscreen) (void)))
02217 {
02218 }
02219 
02220 void
02221 destroy_button(char *button_text)
02222 {
02223 }
02224 
02225 int
02226 init_postscript(char *fname)
02227 {
02228     return (1);
02229 }
02230 
02231 void
02232 close_postscript(void)
02233 {
02234 }
02235 
02236 #endif