/* * Easygl Version 2.0.1 * Written by Vaughn Betz at the University of Toronto, Department of * * Electrical and Computer Engineering, with additions by Paul Leventis * * and William Chow of Altera, and Guy Lemieux of the University of * * Brish Columbia. * * All rights reserved by U of T, etc. * * * * You may freely use this graphics interface for non-commercial purposes * * as long as you leave the author info above in it. * * * Revision History: * * * * V2.0.1 Sept. 2012 (Vaughn Betz) * - Fixed a bug in Win32 where postscript output would make the graphics * crash when you redrew. * - Made a cleaner makefile to simplify platform selection. * - Commented and reorganized some of the code. Started cleaning up some of the * win32 code. Win32 looks inefficient; it is saving and updating graphics contexts * all the time even though we know when the context is valid vs. not-valid. * TODO: make win32 work more like X11 (minimize gc updates). * * V2.0: Nov. 21, 2011 (Vaughn Betz) * - Updated example code, and some cleanup and bug fixes to win32 code. * - Removed some win32 code that had no X11 equivalent or wasn't well * documented. * - Used const char * where appropriate to get rid of g++ warnings. * - Made interface to things like xor drawing more consistent, and added to * example program. * - Made a simpler (easygl.cpp) interface to the graphics library for * use by undergraduate students. * * V1.06 : July 23, 2003 : (Guy Lemieux) * - added some typecasts to cleanly compile with g++ and MS c++ tools * - if WIN32 not defined, it defines X11 automatically * - fixed X11 compilation; WIN32 broke some things * * V1.05 : July 26, 2001 : (William) * * - changed keyboard detect function to accept an int (virtual key) * * * * V1.04 : June 29, 2001 : (William) * * - added drawcurve(), fillcurve() using Bezier curves * * (support WIN32 screen / ps) * * - added pt on object capability : using a memory buffer to draw an * * graphics objects, then query if a point fall on the object (bear the * * object's colour) : object_start(), object_end(), pt_on_object() * * - added drawellipticarc(), fillellipticarc() * * - added findfontsize() to help find a pointsize of a given height * * - extended t_report to keep xleft, xright, ytop, ybot * * - added update_window() to set the window bb * * * * V1.03 : June 18, 2001 : (William) * * - added change_button_text() * * * * V1.02 : June 13, 2001 : (William) * * - extension to mouse click function : can tell if ctrl/shift keys are * * pressed * * * * V1.01 : June 1, 2001 : (William) * * - add tooltip support * * * * V1.0 : May 14, 2001 : (William) * * - fixed a problem with line styles, initial release on the internet * * * * March 27, 2001 : (William) * * - added setcolor_by_colorref to make more colors available (in Win32) * * * * February 16, 2001 : (William) * * - added quick zoom using right mouse clicks * * * * February 11, 2001 : (William) * * - can define cleanup(), passed in when calling init_graphics(), and * * called when shutting down * * * * February 1, 2001 : (William) * * - fix xor mode redraw problem * * * * September 19, 2000 : (William) * * - can define mouse_move callback function * * - can add separators in between buttons * * * * September 8, 2000 : (William) * * - added result_structure(), * * - can define background color in init_graphics * * * * August 10, 2000 : (William Chow, choww@eecg.utoronto.ca) * * - Finished all Win32 support functions * * - use XOR mode for window zooming box * * - added double buffering feature * * * * January 12, 1999: (Paul) * * - Fixed a bunch of stuff with the Win32 support (memory leaks, etc) * * - Made the clipping function using the update rectangle for Win32 * * * * January 9, 1999: (Paul Leventis, leventi@eecg.utoronto.ca) * * - Added Win32 support. Should work under Windows98/95/NT 4.0/NT 5.0. * * - Added a check to deselect_all to determine whether the screen needs to * * be updated or not. Should elminate flicker from mouse clicks * * - Added invalidate_screen() call to graphics.c that in turn calls * * update_screen, so this function was made non-static and added to the * * header file. This is due to differences in the structure of Win32 * * windowing apps. * * - Win32 needs clipping (though done automatically, could be faster) * * * * Sept. 19, 1997: Incorporated Zoom Fit code of Haneef Mohammed at * * Cypress. Makes it easy to zoom to a full view of the graphics. * * * * Sept. 11, 1997: Added the create_and destroy_button interface to * * make it easy to add and destroy buttons from user code. Removed the * * bnum parameter to the button functions, since it wasn't really needed. * * * * June 28, 1997: Added filled arc drawing primitive. Minor modifications * * to PostScript driver to make the PostScript output slightly smaller. * * * * April 15, 1997: Added code to init_graphics so it waits for a window * * to be exposed before returning. This ensures that users of non- * * interactive graphics can never draw to a window before it is available. * * * * Feb. 24, 1997: Added code so the package will allocate a private * * colormap if the default colormap doesn't have enough free colours. * * * * June 28, 1996: Converted all internal functions in graphics.c to have * * internal (static) linkage to avoid any conflicts with user routines in * * the rest of the program. * * * * June 12, 1996: Added setfontsize and setlinewidth attributes. Added * * pre-clipping of objects for speed (and compactness of PS output) when * * graphics are zoomed in. Rewrote PostScript engine to shrink the output * * and make it easier to read. Made drawscreen a callback function passed * * in rather than a global. Graphics attribute calls are more efficient -- * * they check if they have to change anything before doing it. * * * * October 27, 1995: Added the message area, a callback function for * * interacting with user button clicks, and implemented a workaround for a * * Sun X Server bug that misdisplays extremely highly zoomed graphics. * * * * Jan. 13, 1995: Modified to incorporate PostScript Support. */ #ifndef NO_GRAPHICS // Strip everything out and just put in stubs if NO_GRAPHICS defined /********************************** * Common Preprocessor Directives * **********************************/ #define TRUE 1 #define FALSE 0 #include #include #include #include #include #include #include "graphics.h" using namespace std; #if defined(X11) || defined(WIN32) /* Macros for translation from world to PostScript coordinates */ #define XPOST(worldx) (((worldx)-xleft)*ps_xmult + ps_left) #define YPOST(worldy) (((worldy)-ybot)*ps_ymult + ps_bot) /* Macros to convert from X Windows Internal Coordinates to my * * World Coordinates. (This macro is used only rarely, so * * the divides don't hurt speed). */ #define XTOWORLD(x) (((float) x)*xdiv + xleft) #define YTOWORLD(y) (((float) y)*ydiv + ytop) #ifndef max #define max(a,b) (((a) > (b))? (a) : (b)) #endif #ifndef min #define min(a,b) ((a) > (b)? (b) : (a)) #endif #define MWIDTH 104 /* width of menu window */ #define T_AREA_HEIGHT 24 /* Height of text window */ #define MAX_FONT_SIZE 24 /* Largest point size of text. */ // Some computers only have up to 24 point #define PI 3.141592654 #define BUTTON_TEXT_LEN 100 #define BUFSIZE 1000 #endif /********************************************* * X-Windows Specific Preprocessor Directives * *********************************************/ #ifdef X11 #include #include #include #include /* Uncomment the line below if your X11 header files don't define XPointer */ /* typedef char *XPointer; */ // Really large pixel values cna make some X11 implementations draw crazy things // (internal overflow in the X11 library). Use these constants to clip. #define MAXPIXEL 15000 #define MINPIXEL -15000 #endif /* X11 Preprocessor Directives */ /************************************************************* * Microsoft Windows (WIN32) Specific Preprocessor Directives * *************************************************************/ #ifdef WIN32 #pragma warning(disable : 4996) // Turn off annoying warnings about strcmp. #include // Lines below are for displaying errors in a message box on windows. #define SELECT_ERROR() { char msg[BUFSIZE]; sprintf (msg, "Error %i: Couldn't select graphics object on line %d of graphics.c\n", GetLastError(), __LINE__); MessageBox(NULL, msg, NULL, MB_OK); exit(-1); } #define DELETE_ERROR() { char msg[BUFSIZE]; sprintf (msg, "Error %i: Couldn't delete graphics object on line %d of graphics.c\n", GetLastError(), __LINE__); MessageBox(NULL, msg, NULL, MB_OK); exit(-1); } #define CREATE_ERROR() { char msg[BUFSIZE]; sprintf (msg, "Error %i: Couldn't create graphics object on line %d of graphics.c\n", GetLastError(), __LINE__); MessageBox(NULL, msg, NULL, MB_OK); exit(-1); } #define DRAW_ERROR() { char msg[BUFSIZE]; sprintf (msg, "Error %i: Couldn't draw graphics object on line %d of graphics.c\n", GetLastError(), __LINE__); MessageBox(NULL, msg, NULL, MB_OK); exit(-1); } /* Avoid funny clipping problems under windows that I suspect are caused by round-off * in the Win32 libraries. */ #define MAXPIXEL 3000 #define MINPIXEL -3000 #define DEGTORAD(x) ((x)/180.*PI) #define FONTMAG 1.3 #endif /* Win32 preprocessor Directives */ /************************************************************* * Common Type Definitions * *************************************************************/ /* Used to define where the output of drawscreen (graphics primitives in the user-controlled * area) currently goes to: the screen or a postscript file. */ typedef enum { SCREEN = 0, POSTSCRIPT = 1 } t_display_type; /* Indicates if this button displays text, a polygon or is just a separator. */ typedef enum { BUTTON_TEXT = 0, BUTTON_POLY, BUTTON_SEPARATOR } t_button_type; /* Structure used to define the buttons on the right hand side of the main window (menu region). * width, height: button size, in pixels. * xleft, ytop: coordinates, in pixels, of the top-left corner of the button relative to its * containing (menu) window. * fcn: a callback function that is called when the button is pressed. This function takes one * argument, a function pointer to the routine that can draw the graphics area (user routine). * win, hwnd: X11 and Win32 data pointer to the window, respectively. * button_type: indicates if this button displays text, a polygon or is just a separator. * text: the text to display if this is a text button. * poly: the polygon (up to 3 points right now) to display if this is a polygon button * is_pressed: has the button been pressed, and is currently executing its callback? * is_enabled: can you press this button right now? Visually will look "pushed in" when * not enabled, and won't respond to clicks. */ typedef struct { int width; int height; int xleft; int ytop; void (*fcn) (void (*drawscreen) (void)); #ifdef X11 Window win; #else HWND hwnd; #endif t_button_type type; char text[BUTTON_TEXT_LEN]; int poly[3][2]; bool ispressed; bool enabled; } t_button; /* Structure used to store overall graphics state variables. * TODO: Gradually move more file scope variables in here. * initialized: true if the graphics window & state have been * created and initialized, false otherwise. * disp_type: Selects SCREEN or POSTSCRIPT * background_cindex: index of the window (or page for PS) background colour */ typedef struct { bool initialized; int disp_type; int background_cindex; } t_gl_state; /********************************************************************* * File scope variables. TODO: group in structs * *********************************************************************/ // Need to initialize graphics_loaded to false, since checking it is // how we avoid multiple construction or destruction of the graphics // window. static t_gl_state gl_state = {false, SCREEN, 0}; static const int menu_font_size = 12; /* Font for menus and dialog boxes. */ static t_button *button = NULL; /* [0..num_buttons-1] */ static int num_buttons = 0; /* Number of menu buttons */ static int display_width, display_height; /* screen size */ static int top_width, top_height; /* window size */ static float xleft, xright, ytop, ybot; /* world coordinates */ static float saved_xleft, saved_xright, saved_ytop, saved_ybot; static float ps_left, ps_right, ps_top, ps_bot; /* Figure boundaries for * * PostScript output, in PostScript coordinates. */ static float ps_xmult, ps_ymult; /* Transformation for PostScript. */ static float xmult, ymult; /* Transformation factors */ static float xdiv, ydiv; static int currentcolor; static int currentlinestyle; static int currentlinewidth; static int currentfontsize; static e_draw_mode current_draw_mode; /* For PostScript output */ static FILE *ps; static int ProceedPressed; static char statusMessage[BUFSIZE] = ""; /* User message to display */ static bool font_is_loaded[MAX_FONT_SIZE + 1]; static bool get_keypress_input, get_mouse_move_input; static const char *ps_cnames[NUM_COLOR] = {"white", "black", "grey55", "grey75", "blue", "green", "yellow", "cyan", "red", "darkgreen", "magenta", "bisque", "lightblue", "thistle", "plum", "khaki", "coral", "turquoise", "mediumpurple", "darkslateblue", "darkkhaki"}; /********************************************* * Common Subroutine Declarations * *********************************************/ static void *my_malloc(int ibytes); static void *my_realloc(void *memblk, int ibytes); static int xcoord (float worldx); static int ycoord (float worldy); static void force_setcolor(int cindex); static void force_setlinestyle(int linestyle); static void force_setlinewidth(int linewidth); static void force_setfontsize (int pointsize); static void load_font(int pointsize); static void reset_common_state (); static void build_default_menu (void); /* Function declarations for button responses */ static void translate_up (void (*drawscreen) (void)); static void translate_left (void (*drawscreen) (void)); static void translate_right (void (*drawscreen) (void)); static void translate_down (void (*drawscreen) (void)); static void zoom_in (void (*drawscreen) (void)); static void zoom_out (void (*drawscreen) (void)); static void zoom_fit (void (*drawscreen) (void)); static void adjustwin (void (*drawscreen) (void)); static void postscript (void (*drawscreen) (void)); static void proceed (void (*drawscreen) (void)); static void quit (void (*drawscreen) (void)); static void map_button (int bnum); static void unmap_button (int bnum); #ifdef X11 /************************************************* * X-Windows Specific File-scope Variables * **************************************************/ static Display *display; static int screen_num; static GC gc, gcxor, gc_menus, current_gc; static XFontStruct *font_info[MAX_FONT_SIZE+1]; /* Data for each size */ static Window toplevel, menu, textarea; /* various windows */ static Colormap private_cmap; /* "None" unless a private cmap was allocated. */ /* Color indices passed back from X Windows. */ static int colors[NUM_COLOR]; /**************************************************** * X-Windows Specific Subroutine Declarations * *****************************************************/ static Bool test_if_exposed (Display *disp, XEvent *event_ptr, XPointer dummy); static void build_textarea (void); static void drawbut (int bnum); static int which_button (Window win); static void turn_on_off (int pressed); static void drawmenu(void); #endif /* X11 Declarations */ #ifdef WIN32 /***************************************************** * Microsoft Windows (Win32) File Scope Variables * *****************************************************/ static const int win32_line_styles[2] = { PS_SOLID, PS_DASH }; static const COLORREF win32_colors[NUM_COLOR] = { RGB(255, 255, 255), RGB(0, 0, 0), RGB(128, 128, 128), RGB(192, 192, 192), RGB(0, 0, 255), RGB(0, 255, 0), RGB(255, 255, 0), RGB(0, 255, 255), RGB(255, 0, 0), RGB(0, 128, 0), RGB(255, 0, 255), RGB(255, 228, 196), RGB(173, 216, 230), RGB(216, 191, 216), RGB(221, 160, 221), RGB(240, 230, 140), RGB(255, 127, 80), RGB(64, 224, 208), RGB(147, 112, 219), RGB(72, 61, 139), RGB(189, 183, 107)}; static TCHAR szAppName[256], szGraphicsName[] = TEXT("VPR Graphics"), szStatusName[] = TEXT("VPR Status"), szButtonsName[] = TEXT("VPR Buttons"); static HPEN hGraphicsPen; static HBRUSH hGraphicsBrush, hGrayBrush; static HDC hGraphicsDC, hForegroundDC, hBackgroundDC, hCurrentDC, /* WC : double-buffer */ hObjtestDC, hAllObjtestDC; /* object test */ /* WC */ static HFONT hGraphicsFont; static LOGFONT *font_info[MAX_FONT_SIZE+1]; /* Data for each size */ /* Handles to the top level window and 3 subwindows. */ static HWND hMainWnd, hGraphicsWnd, hButtonsWnd, hStatusWnd; static int cxClient, cyClient; /* These are used for the "Window" graphics button. They keep track of whether we're entering * the window rectangle to zoom to, etc. */ static int windowAdjustFlag = 0, adjustButton = -1; static RECT adjustRect, updateRect; static boolean InEventLoop = FALSE; //static HBITMAP buttonImages[4]; /******************************************************************* * Win32-specific subroutine declarations * *******************************************************************/ /* Callback functions for the top-level window and 3 sub-windows. * Windows uses an odd mix of events and callbacks, so it needs these. */ static LRESULT CALLBACK GraphicsWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); static LRESULT CALLBACK StatusWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); static LRESULT CALLBACK ButtonsWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); static LRESULT CALLBACK MainWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); // For Win32, need to save pointers to these callback functions at file // scope, since windows has a bizarre event loop structure where you poll // for events, but then dispatch the event and get called via a callback // from windows (GraphicsWND, below). I can't figure out why windows // does things this way, but it is what makes saving these function pointers // necessary. VB. static void (*mouseclick_ptr)(float x, float y); static void (*mousemove_ptr)(float x, float y); static void (*keypress_ptr)(char entered_char); static void (*drawscreen_ptr)(void); static void invalidate_screen(); static void reset_win32_state (); static void win32_drain_message_queue (); void drawtoscreen(); void displaybuffer(); #endif /* Win32 Declarations */ /********************************************************* * Common Subroutine Definitions * *********************************************************/ /* safer malloc */ static void *my_malloc(int ibytes) { void *mem; mem = (void*)malloc(ibytes); if (mem == NULL) { printf("memory allocation failed!"); exit(-1); } return mem; } /* safer realloc */ static void *my_realloc(void *memblk, int ibytes) { void *mem; mem = (void*)realloc(memblk, ibytes); if (mem == NULL) { printf("memory allocation failed!"); exit(-1); } return mem; } /* Translates from my internal coordinates to real-world coordinates * * in the x direction. Add 0.5 at end for extra half-pixel accuracy. */ static int xcoord (float worldx) { int winx; winx = (int) ((worldx-xleft)*xmult + 0.5); /* Avoids overflow in the Window routines. This will allow horizontal * * and vertical lines to be drawn correctly regardless of zooming, but * * will cause diagonal lines that go way off screen to change their * * slope as you zoom in. The only way I can think of to completely fix * * this problem is to do all the clipping in advance in floating point, * * then convert to integers and call Windows. This is a lot of extra * * coding, and means that coordinates will be clipped twice, even though * * this "Super Zoom" problem won't occur unless users zoom way in on * * the graphics. */ winx = max (winx, MINPIXEL); winx = min (winx, MAXPIXEL); return (winx); } /* Translates from my internal coordinates to real-world coordinates * * in the y direction. Add 0.5 at end for extra half-pixel accuracy. */ static int ycoord (float worldy) { int winy; winy = (int) ((worldy-ytop)*ymult + 0.5); /* Avoid overflow in the X/Win32 Window routines. */ winy = max (winy, MINPIXEL); winy = min (winy, MAXPIXEL); return (winy); } #ifdef WIN32 static void invalidate_screen(void) { /* Tells the graphics engine to redraw the graphics display since information has changed */ if(!InvalidateRect(hGraphicsWnd, NULL, FALSE)) DRAW_ERROR(); if(!UpdateWindow(hGraphicsWnd)) DRAW_ERROR(); } #endif /* Sets the current graphics context colour to cindex, regardless of whether we think it is * needed or not. */ static void force_setcolor (int cindex) { currentcolor = cindex; if (gl_state.disp_type == SCREEN) { #ifdef X11 XSetForeground (display, current_gc, colors[cindex]); #else /* Win32 */ int win_linestyle; LOGBRUSH lb; lb.lbStyle = BS_SOLID; lb.lbColor = win32_colors[cindex]; lb.lbHatch = (LONG)NULL; win_linestyle = win32_line_styles[currentlinestyle]; if(!DeleteObject(hGraphicsPen)) DELETE_ERROR(); int linewidth = max (currentlinewidth, 1); // Win32 won't draw 0 width dashed lines. hGraphicsPen = ExtCreatePen(PS_GEOMETRIC | win_linestyle | PS_ENDCAP_FLAT, linewidth, &lb, (LONG)NULL, NULL); if(!hGraphicsPen) CREATE_ERROR(); if(!DeleteObject(hGraphicsBrush)) DELETE_ERROR(); hGraphicsBrush = CreateSolidBrush(win32_colors[currentcolor]); if(!hGraphicsBrush) CREATE_ERROR(); #endif } else { fprintf (ps,"%s\n", ps_cnames[cindex]); } } /* Sets the current graphics context colour to cindex if it differs from the old colour */ void setcolor (int cindex) { if (currentcolor != cindex) force_setcolor (cindex); } /* Sets the current graphics context color to the index that corresponds to the * string name passed in. Slower, but maybe more convenient for simple * client code. */ void setcolor (string cname) { int icolor = -1; for (int i = 0; i < NUM_COLOR; i++) { if (cname == ps_cnames[i]) { icolor = i; break; } } if (icolor == -1) { cout << "Error: unknown color " << cname << endl; } else { setcolor (icolor); } } int getcolor() { return currentcolor; } /* Sets the current linestyle to linestyle in the graphics context. * Note SOLID is 0 and DASHED is 1 for linestyle. */ static void force_setlinestyle (int linestyle) { currentlinestyle = linestyle; if (gl_state.disp_type == SCREEN) { #ifdef X11 static int x_vals[2] = {LineSolid, LineOnOffDash}; XSetLineAttributes (display, current_gc, currentlinewidth, x_vals[linestyle], CapButt, JoinMiter); #else // Win32 LOGBRUSH lb; lb.lbStyle = BS_SOLID; lb.lbColor = win32_colors[currentcolor]; lb.lbHatch = (LONG)NULL; int win_linestyle = win32_line_styles[linestyle]; if(!DeleteObject(hGraphicsPen)) DELETE_ERROR(); int linewidth = max (currentlinewidth, 1); // Win32 won't draw 0 width dashed lines. hGraphicsPen = ExtCreatePen(PS_GEOMETRIC | win_linestyle | PS_ENDCAP_FLAT, linewidth, &lb, (LONG)NULL, NULL); if(!hGraphicsPen) CREATE_ERROR(); #endif } else { if (linestyle == SOLID) fprintf (ps,"linesolid\n"); else if (linestyle == DASHED) fprintf (ps, "linedashed\n"); else { printf ("Error: invalid linestyle: %d\n", linestyle); exit (1); } } } /* Change the linestyle in the graphics context only if it differs from the current * linestyle. */ void setlinestyle (int linestyle) { if (linestyle != currentlinestyle) force_setlinestyle (linestyle); } /* Sets current linewidth in the graphics context. * linewidth should be greater than or equal to 0 to make any sense. */ static void force_setlinewidth (int linewidth) { currentlinewidth = linewidth; if (gl_state.disp_type == SCREEN) { #ifdef X11 static int x_vals[2] = {LineSolid, LineOnOffDash}; XSetLineAttributes (display, current_gc, linewidth, x_vals[currentlinestyle], CapButt, JoinMiter); #else /* Win32 */ LOGBRUSH lb; lb.lbStyle = BS_SOLID; lb.lbColor = win32_colors[currentcolor]; lb.lbHatch = (LONG)NULL; int win_linestyle = win32_line_styles[currentlinestyle]; if(!DeleteObject(hGraphicsPen)) DELETE_ERROR(); if (linewidth == 0) linewidth = 1; // Win32 won't draw dashed 0 width lines. hGraphicsPen = ExtCreatePen(PS_GEOMETRIC | win_linestyle | PS_ENDCAP_FLAT, linewidth, &lb, (LONG)NULL, NULL); if(!hGraphicsPen) CREATE_ERROR(); #endif } else { fprintf(ps,"%d setlinewidth\n", linewidth); } } /* Sets the linewidth in the grahpics context, if it differs from the current value. */ void setlinewidth (int linewidth) { if (linewidth != currentlinewidth) force_setlinewidth (linewidth); } /* Force the selected fontsize to be applied to the graphics context, * whether or not it appears to match the current fontsize. This is necessary * when switching between postscript and window out, for example. * Valid point sizes are between 1 and MAX_FONT_SIZE. */ static void force_setfontsize (int pointsize) { if (pointsize < 1) pointsize = 1; #ifdef WIN32 pointsize = (int)((float)pointsize * FONTMAG); #endif if (pointsize > MAX_FONT_SIZE) pointsize = MAX_FONT_SIZE; currentfontsize = pointsize; if (gl_state.disp_type == SCREEN) { load_font (pointsize); #ifdef X11 XSetFont(display, current_gc, font_info[pointsize]->fid); #else /* Win32 */ if(!DeleteObject(hGraphicsFont)) DELETE_ERROR(); hGraphicsFont = CreateFontIndirect(font_info[pointsize]); if(!hGraphicsFont) CREATE_ERROR(); if(!SelectObject(hGraphicsDC, hGraphicsFont) ) SELECT_ERROR(); #endif } else { /* PostScript: set up font and centering function */ fprintf(ps,"%d setfontsize\n",pointsize); } } /* For efficiency, this routine doesn't do anything if no change is * implied. If you want to force the graphics context or PS file * to have font info set, call force_setfontsize (this is necessary * in initialization and X11 / Postscript switches). */ void setfontsize (int pointsize) { if (pointsize != currentfontsize) force_setfontsize (pointsize); } /* Puts a triangle in the poly array for button[bnum]. Haven't made this work for * win32 yet and instead put "U", "D" excetra on the arrow buttons. * VB To-do: make work for win32 someday. */ #ifdef X11 static void setpoly (int bnum, int xc, int yc, int r, float theta) { int i; button[bnum].type = BUTTON_POLY; for (i=0;i<3;i++) { button[bnum].poly[i][0] = (int) (xc + r*cos(theta) + 0.5); button[bnum].poly[i][1] = (int) (yc + r*sin(theta) + 0.5); theta += (float)(2*PI/3); } } #endif // X11 /* Maps a button onto the screen and set it up for input, etc. */ static void map_button (int bnum) { button[bnum].ispressed = 0; if (button[bnum].type != BUTTON_SEPARATOR) { #ifdef X11 button[bnum].win = XCreateSimpleWindow(display,menu, button[bnum].xleft, button[bnum].ytop, button[bnum].width, button[bnum].height, 0, colors[WHITE], colors[LIGHTGREY]); XMapWindow (display, button[bnum].win); XSelectInput (display, button[bnum].win, ButtonPressMask); #else button[bnum].hwnd = CreateWindow( TEXT("button"), TEXT(button[bnum].text), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, button[bnum].xleft, button[bnum].ytop, button[bnum].width, button[bnum].height, hButtonsWnd, (HMENU)(200+bnum), (HINSTANCE) GetWindowLong(hMainWnd, GWL_HINSTANCE), NULL); if(!InvalidateRect(hButtonsWnd, NULL, TRUE)) DRAW_ERROR(); if(!UpdateWindow(hButtonsWnd)) DRAW_ERROR(); #endif } else { // Separator, not a button. #ifdef X11 button[bnum].win = -1; #else // WIN32 button[bnum].hwnd = NULL; if(!InvalidateRect(hButtonsWnd, NULL, TRUE)) DRAW_ERROR(); if(!UpdateWindow(hButtonsWnd)) DRAW_ERROR(); #endif } } static void unmap_button (int bnum) { /* Unmaps (removes) a button from the screen. */ if (button[bnum].type != BUTTON_SEPARATOR) { #ifdef X11 XUnmapWindow (display, button[bnum].win); #else if(!DestroyWindow(button[bnum].hwnd)) DRAW_ERROR(); if(!InvalidateRect(hButtonsWnd, NULL, TRUE)) DRAW_ERROR(); if(!UpdateWindow(hButtonsWnd)) DRAW_ERROR(); #endif } } /* Creates a new button below the button containing prev_button_text. * * The text and button function are set according to button_text and * * button_func, respectively. */ void create_button (const char *prev_button_text , const char *button_text, void (*button_func) (void (*drawscreen) (void))) { int i, bnum, space, bheight; t_button_type button_type = BUTTON_TEXT; space = 8; /* Only allow new buttons that are text or separator (not poly) types. * They can also only go after buttons that are text buttons. */ bnum = -1; for (i=0; i < num_buttons;i++) { if (button[i].type == BUTTON_TEXT && strcmp (button[i].text, prev_button_text) == 0) { bnum = i + 1; break; } } if (bnum == -1) { printf ("Error in create_button: button with text %s not found.\n", prev_button_text); exit (1); } button = (t_button *) my_realloc (button, (num_buttons+1) * sizeof (t_button)); /* NB: Requirement that you specify the button that this button goes under * * guarantees that button[num_buttons-2] exists and is a text button. */ /* Special string to make a separator. */ if (!strncmp(button_text, "---", 3)) { bheight = 2; button_type = BUTTON_SEPARATOR; } else bheight = 26; for (i=num_buttons;i>bnum;i--) { button[i].xleft = button[i-1].xleft; button[i].ytop = button[i-1].ytop + bheight + space; button[i].height = button[i-1].height; button[i].width = button[i-1].width; button[i].type = button[i-1].type; strcpy (button[i].text, button[i-1].text); button[i].fcn = button[i-1].fcn; button[i].ispressed = button[i-1].ispressed; button[i].enabled = button[i-1].enabled; unmap_button (i-1); } i = bnum; button[i].xleft = 6; button[i].ytop = button[i-1].ytop + button[i-1].height + space; button[i].height = bheight; button[i].width = 90; button[i].type = button_type; strncpy (button[i].text, button_text, BUTTON_TEXT_LEN); button[i].fcn = button_func; button[i].ispressed = false; button[i].enabled = true; num_buttons++; for (i = 0; i use defaults. */ current_gc = gc = XCreateGC(display, toplevel, valuemask, &values); gc_menus = XCreateGC(display, toplevel, valuemask, &values); /* Create XOR graphics context for Rubber Banding */ values.function = GXxor; values.foreground = colors[cindex]; gcxor = XCreateGC(display, toplevel, (GCFunction | GCForeground), &values); /* specify font for menus. */ load_font(menu_font_size); XSetFont(display, gc_menus, font_info[menu_font_size]->fid); /* Set drawing defaults for user-drawable area. Use whatever the * * initial values of the current stuff was set to. */ force_setfontsize(currentfontsize); force_setcolor (currentcolor); force_setlinestyle (currentlinestyle); force_setlinewidth (currentlinewidth); // Need a non-const name to pass to XStringListTo... // (even though X11 won't change it). char *window_name_copy = (char *) my_malloc (BUFSIZE * sizeof (char)); strncpy (window_name_copy, window_name, BUFSIZE); XStringListToTextProperty(&window_name_copy, 1, &windowName); free (window_name_copy); window_name_copy = NULL; XSetWMName (display, toplevel, &windowName); /* XSetWMIconName (display, toplevel, &windowName); */ /* XStringListToTextProperty copies the window_name string into * * windowName.value. Free this memory now. */ free (windowName.value); XMapWindow (display, toplevel); build_textarea (); build_default_menu (); /* The following is completely unnecessary if the user is using the * * interactive (event_loop) graphics. It waits for the first Expose * * event before returning so that I can tell the window manager has got * * the top-level window up and running. Thus the user can start drawing * * into this window immediately, and there's no danger of the window not * * being ready and output being lost. */ XPeekIfEvent (display, &event, test_if_exposed, NULL); #else /* WIN32 */ WNDCLASS wndclass; HINSTANCE hInstance = GetModuleHandle(NULL); int x, y; LOGBRUSH lb; lb.lbStyle = BS_SOLID; lb.lbColor = win32_colors[currentcolor]; lb.lbHatch = (LONG)NULL; x = 0; y = 0; /* get screen size from display structure macro */ display_width = GetSystemMetrics( SM_CXSCREEN ); if (!(display_width)) CREATE_ERROR(); display_height = GetSystemMetrics( SM_CYSCREEN ); if (!(display_height)) CREATE_ERROR(); top_width = 2*display_width/3; top_height = 4*display_height/5; /* Grab the Application name */ wsprintf(szAppName, TEXT(window_name)); //hGraphicsPen = CreatePen(win32_line_styles[SOLID], 1, win32_colors[BLACK]); hGraphicsPen = ExtCreatePen(PS_GEOMETRIC | win32_line_styles[currentlinestyle] | PS_ENDCAP_FLAT, 1, &lb, (LONG)NULL, NULL); if(!hGraphicsPen) CREATE_ERROR(); hGraphicsBrush = CreateSolidBrush(win32_colors[DARKGREY]); if(!hGraphicsBrush) CREATE_ERROR(); hGrayBrush = CreateSolidBrush(win32_colors[LIGHTGREY]); if(!hGrayBrush) CREATE_ERROR(); load_font (currentfontsize); hGraphicsFont = CreateFontIndirect(font_info[currentfontsize]); if (!hGraphicsFont) CREATE_ERROR(); /* Register the Main Window class */ wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; wndclass.lpfnWndProc = MainWND; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = hInstance; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION); wndclass.hCursor = LoadCursor( NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH) CreateSolidBrush(win32_colors[cindex]); wndclass.lpszMenuName = NULL; wndclass.lpszClassName = szAppName; if (!RegisterClass(&wndclass)) { printf ("Error code: %d\n", GetLastError()); MessageBox(NULL, TEXT("Initialization of Windows graphics (init_graphics) failed."), szAppName, MB_ICONERROR); exit(-1); } /* Register the Graphics Window class */ wndclass.lpfnWndProc = GraphicsWND; wndclass.hIcon = NULL; wndclass.lpszClassName = szGraphicsName; if(!RegisterClass(&wndclass)) DRAW_ERROR(); /* Register the Status Window class */ wndclass.lpfnWndProc = StatusWND; wndclass.hIcon = NULL; wndclass.lpszClassName = szStatusName; wndclass.hbrBackground = hGrayBrush; if(!RegisterClass(&wndclass)) DRAW_ERROR(); /* Register the Buttons Window class */ wndclass.lpfnWndProc = ButtonsWND; wndclass.hIcon = NULL; wndclass.lpszClassName = szButtonsName; wndclass.hbrBackground = hGrayBrush; if (!RegisterClass(&wndclass)) DRAW_ERROR(); hMainWnd = CreateWindow(szAppName, TEXT(window_name), WS_OVERLAPPEDWINDOW, x, y, top_width, top_height, NULL, NULL, hInstance, NULL); if(!hMainWnd) DRAW_ERROR(); /* Set drawing defaults for user-drawable area. Use whatever the * * initial values of the current stuff was set to. */ if (ShowWindow(hMainWnd, SW_SHOWNORMAL)) DRAW_ERROR(); build_default_menu(); if (!UpdateWindow(hMainWnd)) DRAW_ERROR(); win32_drain_message_queue (); #endif gl_state.initialized = true; } static void reset_common_state () { currentcolor = BLACK; currentlinestyle = SOLID; currentlinewidth = 0; currentfontsize = 12; current_draw_mode = DRAW_NORMAL; for (int i=0;i<=MAX_FONT_SIZE;i++) font_is_loaded[i] = false; /* No fonts loaded yet. */ ProceedPressed = false; get_keypress_input = false; get_mouse_move_input = false; } static void update_transform (void) { /* Set up the factors for transforming from the user world to X Windows * * coordinates. */ float mult, y1, y2, x1, x2; /* X Window coordinates go from (0,0) to (width-1,height-1) */ xmult = (top_width - 1 - MWIDTH) / (xright - xleft); ymult = (top_height - 1 - T_AREA_HEIGHT)/ (ybot - ytop); /* Need to use same scaling factor to preserve aspect ratio */ if (fabs(xmult) <= fabs(ymult)) { mult = (float)(fabs(ymult/xmult)); y1 = ytop - (ybot-ytop)*(mult-1)/2; y2 = ybot + (ybot-ytop)*(mult-1)/2; ytop = y1; ybot = y2; } else { mult = (float)(fabs(xmult/ymult)); x1 = xleft - (xright-xleft)*(mult-1)/2; x2 = xright + (xright-xleft)*(mult-1)/2; xleft = x1; xright = x2; } xmult = (top_width - 1 - MWIDTH) / (xright - xleft); ymult = (top_height - 1 - T_AREA_HEIGHT)/ (ybot - ytop); xdiv = 1/xmult; ydiv = 1/ymult; } static void update_ps_transform (void) { /* Postscript coordinates start at (0,0) for the lower left hand corner * * of the page and increase upwards and to the right. For 8.5 x 11 * * sheet, coordinates go from (0,0) to (612,792). Spacing is 1/72 inch.* * I'm leaving a minimum of half an inch (36 units) of border around * * each edge. */ float ps_width, ps_height; ps_width = 540.; /* 72 * 7.5 */ ps_height = 720.; /* 72 * 10 */ ps_xmult = ps_width / (xright - xleft); ps_ymult = ps_height / (ytop - ybot); /* Need to use same scaling factor to preserve aspect ratio. * * I show exactly as much on paper as the screen window shows, * * or the user specifies. */ if (fabs(ps_xmult) <= fabs(ps_ymult)) { ps_left = 36.; ps_right = (float)(36. + ps_width); ps_bot = (float)(396. - fabs(ps_xmult * (ytop - ybot))/2); ps_top = (float)(396. + fabs(ps_xmult * (ytop - ybot))/2); /* Maintain aspect ratio but watch signs */ ps_ymult = (ps_xmult*ps_ymult < 0) ? -ps_xmult : ps_xmult; } else { ps_bot = 36.; ps_top = (float)(36. + ps_height); ps_left = (float)(306. - fabs(ps_ymult * (xright - xleft))/2); ps_right = (float)(306. + fabs(ps_ymult * (xright - xleft))/2); /* Maintain aspect ratio but watch signs */ ps_xmult = (ps_xmult*ps_ymult < 0) ? -ps_ymult : ps_ymult; } } /* The program's main event loop. Must be passed a user routine * drawscreen which redraws the screen. It handles all window resizing * zooming etc. itself. If the user clicks a mousebutton in the graphics * (toplevel) area, the act_on_mousebutton routine passed in is called. */ void event_loop (void (*act_on_mousebutton)(float x, float y), void (*act_on_mousemove)(float x, float y), void (*act_on_keypress)(char key_pressed), void (*drawscreen) (void)) { #ifdef X11 XEvent report; int bnum; float x, y; #define OFF 1 #define ON 0 turn_on_off (ON); while (1) { XNextEvent (display, &report); switch (report.type) { case Expose: #ifdef VERBOSE printf("Got an expose event.\n"); printf("Count is: %d.\n",report.xexpose.count); printf("Window ID is: %d.\n",report.xexpose.window); #endif if (report.xexpose.count != 0) break; if (report.xexpose.window == menu) drawmenu(); else if (report.xexpose.window == toplevel) drawscreen(); else if (report.xexpose.window == textarea) draw_message(); break; case ConfigureNotify: top_width = report.xconfigure.width; top_height = report.xconfigure.height; update_transform(); drawmenu(); draw_message(); #ifdef VERBOSE printf("Got a ConfigureNotify.\n"); printf("New width: %d New height: %d.\n",top_width,top_height); #endif break; case ButtonPress: #ifdef VERBOSE printf("Got a buttonpress.\n"); printf("Window ID is: %d.\n",report.xbutton.window); #endif if (report.xbutton.window == toplevel) { x = XTOWORLD(report.xbutton.x); y = YTOWORLD(report.xbutton.y); act_on_mousebutton (x, y); } else { /* A menu button was pressed. */ bnum = which_button(report.xbutton.window); #ifdef VERBOSE printf("Button number is %d\n",bnum); #endif if (button[bnum].enabled) { button[bnum].ispressed = 1; drawbut(bnum); XFlush(display); /* Flash the button */ button[bnum].fcn (drawscreen); button[bnum].ispressed = 0; drawbut(bnum); if (button[bnum].fcn == proceed) { turn_on_off(OFF); flushinput (); return; /* Rather clumsy way of returning * * control to the simulator */ } } } break; case MotionNotify: #ifdef VERBOSE printf("Got a MotionNotify Event.\n"); printf("x: %d y: %d\n",report.xmotion.x,report.xmotion.y); #endif if (get_mouse_move_input && report.xmotion.x <= top_width-MWIDTH && report.xmotion.y <= top_height-T_AREA_HEIGHT) act_on_mousemove(XTOWORLD(report.xmotion.x), YTOWORLD(report.xmotion.y)); break; case KeyPress: #ifdef VERBOSE printf("Got a KeyPress Event.\n"); #endif if (get_keypress_input) { char keyb_buffer[20]; XComposeStatus composestatus; KeySym keysym; int length, max_bytes; max_bytes = 1; length = XLookupString( &report.xkey, keyb_buffer, max_bytes, &keysym, &composestatus ); keyb_buffer[length] = '\0'; /* terminating NULL */ act_on_keypress(keyb_buffer[0]); } break; } } #else /* Win32 */ MSG msg; mouseclick_ptr = act_on_mousebutton; mousemove_ptr = act_on_mousemove; keypress_ptr = act_on_keypress; drawscreen_ptr = drawscreen; ProceedPressed = FALSE; InEventLoop = TRUE; invalidate_screen(); while(!ProceedPressed && GetMessage(&msg, NULL, 0, 0)) { //TranslateMessage(&msg); if (msg.message == WM_CHAR) { // only the top window can get keyboard events msg.hwnd = hMainWnd; } DispatchMessage(&msg); } InEventLoop = FALSE; #endif } void clearscreen (void) { int savecolor; if (gl_state.disp_type == SCREEN) { #ifdef X11 XClearWindow (display, toplevel); #else /* Win32 */ savecolor = currentcolor; setcolor(gl_state.background_cindex); fillrect (xleft, ytop, xright, ybot); setcolor(savecolor); #endif } else { // Postscript /* erases current page. Don't use erasepage, since this will erase * * everything, (even stuff outside the clipping path) causing * * problems if this picture is incorporated into a larger document. */ savecolor = currentcolor; setcolor (gl_state.background_cindex); fprintf(ps,"clippath fill\n\n"); setcolor (savecolor); } } /* Return 1 if I can quarantee no part of this rectangle will * * lie within the user drawing area. Otherwise return 0. * * Note: this routine is only used to help speed (and to shrink ps * * files) -- it will be highly effective when the graphics are zoomed * * in and lots are off-screen. I don't have to pre-clip for * * correctness. */ static int rect_off_screen (float x1, float y1, float x2, float y2) { float xmin, xmax, ymin, ymax; xmin = min (xleft, xright); if (x1 < xmin && x2 < xmin) return (1); xmax = max (xleft, xright); if (x1 > xmax && x2 > xmax) return (1); ymin = min (ytop, ybot); if (y1 < ymin && y2 < ymin) return (1); ymax = max (ytop, ybot); if (y1 > ymax && y2 > ymax) return (1); return (0); } void drawline (float x1, float y1, float x2, float y2) { /* Draw a line from (x1,y1) to (x2,y2) in the user-drawable area. * * Coordinates are in world (user) space. */ #ifdef WIN32 HPEN hOldPen; #endif if (rect_off_screen(x1,y1,x2,y2)) return; if (gl_state.disp_type == SCREEN) { #ifdef X11 /* Xlib.h prototype has x2 and y1 mixed up. */ XDrawLine(display, toplevel, current_gc, xcoord(x1), ycoord(y1), xcoord(x2), ycoord(y2)); #else /* Win32 */ hOldPen = (HPEN)SelectObject(hGraphicsDC, hGraphicsPen); if(!(hOldPen)) SELECT_ERROR(); if (!BeginPath(hGraphicsDC)) DRAW_ERROR(); if(!MoveToEx (hGraphicsDC, xcoord(x1), ycoord(y1), NULL)) DRAW_ERROR(); if(!LineTo (hGraphicsDC, xcoord(x2), ycoord(y2))) DRAW_ERROR(); if (!EndPath(hGraphicsDC)) DRAW_ERROR(); if (!StrokePath(hGraphicsDC)) DRAW_ERROR(); if(!SelectObject(hGraphicsDC, hOldPen)) SELECT_ERROR(); #endif } else { fprintf(ps,"%.2f %.2f %.2f %.2f drawline\n",XPOST(x1),YPOST(y1), XPOST(x2),YPOST(y2)); } } /* (x1,y1) and (x2,y2) are diagonally opposed corners, in world coords. */ void drawrect (float x1, float y1, float x2, float y2) { int xw1, yw1, xw2, yw2; #ifdef WIN32 HPEN hOldPen; HBRUSH hOldBrush; #else unsigned int width, height; int xl, yt; #endif if (rect_off_screen(x1,y1,x2,y2)) return; if (gl_state.disp_type == SCREEN) { /* translate to X Windows calling convention. */ xw1 = xcoord(x1); xw2 = xcoord(x2); yw1 = ycoord(y1); yw2 = ycoord(y2); #ifdef X11 xl = min(xw1,xw2); yt = min(yw1,yw2); width = abs (xw1-xw2); height = abs (yw1-yw2); XDrawRectangle(display, toplevel, current_gc, xl, yt, width, height); #else /* Win32 */ if(xw1 > xw2) { int temp = xw1; xw1 = xw2; xw2 = temp; } if(yw1 > yw2) { int temp = yw1; yw1 = yw2; yw2 = temp; } hOldPen = (HPEN)SelectObject(hGraphicsDC, hGraphicsPen); if(!(hOldPen)) SELECT_ERROR(); hOldBrush = (HBRUSH)SelectObject(hGraphicsDC, GetStockObject(NULL_BRUSH)); if(!(hOldBrush)) SELECT_ERROR(); if(!Rectangle(hGraphicsDC, xw1, yw1, xw2, yw2)) DRAW_ERROR(); if(!SelectObject(hGraphicsDC, hOldPen)) SELECT_ERROR(); if(!SelectObject(hGraphicsDC, hOldBrush)) SELECT_ERROR(); #endif } else { fprintf(ps,"%.2f %.2f %.2f %.2f drawrect\n",XPOST(x1),YPOST(y1), XPOST(x2),YPOST(y2)); } } /* (x1,y1) and (x2,y2) are diagonally opposed corners in world coords. */ void fillrect (float x1, float y1, float x2, float y2) { int xw1, yw1, xw2, yw2; #ifdef WIN32 HPEN hOldPen; HBRUSH hOldBrush; #else unsigned int width, height; int xl, yt; #endif if (rect_off_screen(x1,y1,x2,y2)) return; if (gl_state.disp_type == SCREEN) { /* translate to X Windows calling convention. */ xw1 = xcoord(x1); xw2 = xcoord(x2); yw1 = ycoord(y1); yw2 = ycoord(y2); #ifdef X11 xl = min(xw1,xw2); yt = min(yw1,yw2); width = abs (xw1-xw2); height = abs (yw1-yw2); XFillRectangle(display, toplevel, current_gc, xl, yt, width, height); #else /* Win32 */ if(xw1 > xw2) { int temp = xw1; xw1 = xw2; xw2 = temp; } if(yw1 > yw2) { int temp = yw1; yw1 = yw2; yw2 = temp; } hOldPen = (HPEN)SelectObject(hGraphicsDC, hGraphicsPen); if(!(hOldPen)) SELECT_ERROR(); hOldBrush = (HBRUSH)SelectObject(hGraphicsDC, hGraphicsBrush); if(!(hOldBrush)) SELECT_ERROR(); if(!Rectangle(hGraphicsDC, xw1, yw1, xw2, yw2)) DRAW_ERROR(); if(!SelectObject(hGraphicsDC, hOldPen)) SELECT_ERROR(); if(!SelectObject(hGraphicsDC, hOldBrush)) SELECT_ERROR(); #endif } else { fprintf(ps,"%.2f %.2f %.2f %.2f fillrect\n",XPOST(x1),YPOST(y1), XPOST(x2),YPOST(y2)); } } /* Normalizes an angle to be between 0 and 360 degrees. */ static float angnorm (float ang) { int scale; if (ang < 0) { scale = (int) (ang / 360. - 1); } else { scale = (int) (ang / 360.); } ang = ang - scale * 360; return (ang); } void drawellipticarc (float xc, float yc, float radx, float rady, float startang, float angextent) { int xl, yt; unsigned int width, height; #ifdef WIN32 HPEN hOldPen; int p1, p2, p3, p4; #endif /* Conservative (but fast) clip test -- check containing rectangle of * * an ellipse. */ if (rect_off_screen (xc-radx,yc-rady,xc+radx,yc+rady)) return; /* X Windows has trouble with very large angles. (Over 360). * * Do following to prevent its inaccurate (overflow?) problems. */ if (fabs(angextent) > 360.) angextent = 360.; startang = angnorm (startang); if (gl_state.disp_type == SCREEN) { xl = (int) (xcoord(xc) - fabs(xmult*radx)); yt = (int) (ycoord(yc) - fabs(ymult*rady)); width = (unsigned int) (2*fabs(xmult*radx)); height = (unsigned int) (2*fabs(ymult*rady)); #ifdef X11 XDrawArc (display, toplevel, current_gc, xl, yt, width, height, (int) (startang*64), (int) (angextent*64)); #else // Win32 /* set arc direction */ if (angextent > 0) { p1 = (int)(xcoord(xc) + fabs(xmult*radx)*cos(DEGTORAD(startang))); p2 = (int)(ycoord(yc) - fabs(ymult*rady)*sin(DEGTORAD(startang))); p3 = (int)(xcoord(xc) + fabs(xmult*radx)*cos(DEGTORAD(startang+angextent-.001))); p4 = (int)(ycoord(yc) - fabs(ymult*rady)*sin(DEGTORAD(startang+angextent-.001))); } else { p1 = (int)(xcoord(xc) + fabs(xmult*radx)*cos(DEGTORAD(startang+angextent+.001))); p2 = (int)(ycoord(yc) - fabs(ymult*rady)*sin(DEGTORAD(startang+angextent+.001))); p3 = (int)(xcoord(xc) + fabs(xmult*radx)*cos(DEGTORAD(startang))); p4 = (int)(ycoord(yc) - fabs(ymult*rady)*sin(DEGTORAD(startang))); } hOldPen = (HPEN)SelectObject(hGraphicsDC, hGraphicsPen); if(!(hOldPen)) SELECT_ERROR(); if(!Arc(hGraphicsDC, xl, yt, xl+width, yt+height, p1, p2, p3, p4)) DRAW_ERROR(); if(!SelectObject(hGraphicsDC, hOldPen)) SELECT_ERROR(); #endif } else { fprintf(ps, "gsave\n"); fprintf(ps, "%.2f %.2f translate\n", XPOST(xc), YPOST(yc)); fprintf(ps, "%.2f 1 scale\n", fabs(radx*ps_xmult)/fabs(rady*ps_ymult)); fprintf(ps, "0 0 %.2f %.2f %.2f %s\n", /*XPOST(xc)*/ /*YPOST(yc)*/ fabs(rady*ps_xmult), startang, startang+angextent, (angextent < 0) ? "drawarcn" : "drawarc") ; fprintf(ps, "grestore\n"); } } /* Startang is relative to the Window's positive x direction. Angles in degrees. */ void drawarc (float xc, float yc, float rad, float startang, float angextent) { drawellipticarc(xc, yc, rad, rad, startang, angextent); } /* Fills a elliptic arc. Startang is relative to the Window's positive x * direction. Angles in degrees. */ void fillellipticarc (float xc, float yc, float radx, float rady, float startang, float angextent) { int xl, yt; unsigned int width, height; #ifdef WIN32 HPEN hOldPen; HBRUSH hOldBrush; int p1, p2, p3, p4; #endif /* Conservative (but fast) clip test -- check containing rectangle of * * a circle. */ if (rect_off_screen (xc-radx,yc-rady,xc+radx,yc+rady)) return; /* X Windows has trouble with very large angles. (Over 360). * * Do following to prevent its inaccurate (overflow?) problems. */ if (fabs(angextent) > 360.) angextent = 360.; startang = angnorm (startang); if (gl_state.disp_type == SCREEN) { xl = (int) (xcoord(xc) - fabs(xmult*radx)); yt = (int) (ycoord(yc) - fabs(ymult*rady)); width = (unsigned int) (2*fabs(xmult*radx)); height = (unsigned int) (2*fabs(ymult*rady)); #ifdef X11 XFillArc (display, toplevel, current_gc, xl, yt, width, height, (int) (startang*64), (int) (angextent*64)); #else // Win32 /* set pie direction */ if (angextent > 0) { p1 = (int)(xcoord(xc) + fabs(xmult*radx)*cos(DEGTORAD(startang))); p2 = (int)(ycoord(yc) - fabs(ymult*rady)*sin(DEGTORAD(startang))); p3 = (int)(xcoord(xc) + fabs(xmult*radx)*cos(DEGTORAD(startang+angextent-.001))); p4 = (int)(ycoord(yc) - fabs(ymult*rady)*sin(DEGTORAD(startang+angextent-.001))); } else { p1 = (int)(xcoord(xc) + fabs(xmult*radx)*cos(DEGTORAD(startang+angextent+.001))); p2 = (int)(ycoord(yc) - fabs(ymult*rady)*sin(DEGTORAD(startang+angextent+.001))); p3 = (int)(xcoord(xc) + fabs(xmult*radx)*cos(DEGTORAD(startang))); p4 = (int)(ycoord(yc) - fabs(ymult*rady)*sin(DEGTORAD(startang))); } hOldPen = (HPEN)SelectObject(hGraphicsDC, GetStockObject(NULL_PEN)); if(!(hOldPen)) SELECT_ERROR(); hOldBrush = (HBRUSH)SelectObject(hGraphicsDC, hGraphicsBrush); if(!(hOldBrush)) SELECT_ERROR(); // Win32 API says a zero return value indicates an error, but it seems to always // return zero. Don't check for an error on Pie. Pie(hGraphicsDC, xl, yt, xl+width, yt+height, p1, p2, p3, p4); // if(!Pie(hGraphicsDC, xl, yt, xl+width, yt+height, p1, p2, p3, p4)); // DRAW_ERROR(); if(!SelectObject(hGraphicsDC, hOldPen)) SELECT_ERROR(); if(!SelectObject(hGraphicsDC, hOldBrush)) SELECT_ERROR(); #endif } else { fprintf(ps, "gsave\n"); fprintf(ps, "%.2f %.2f translate\n", XPOST(xc), YPOST(yc)); fprintf(ps, "%.2f 1 scale\n", fabs(radx*ps_xmult)/fabs(rady*ps_ymult)); fprintf(ps, "%.2f %.2f %.2f 0 0 %s\n", /*XPOST(xc)*/ /*YPOST(yc)*/ fabs(rady*ps_xmult), startang, startang+angextent, (angextent < 0) ? "fillarcn" : "fillarc") ; fprintf(ps, "grestore\n"); } } void fillarc (float xc, float yc, float rad, float startang, float angextent) { fillellipticarc(xc, yc, rad, rad, startang, angextent); } void fillpoly (t_point *points, int npoints) { #ifdef X11 XPoint transpoints[MAXPTS]; #else POINT transpoints[MAXPTS]; HPEN hOldPen; HBRUSH hOldBrush; #endif int i; float xmin, ymin, xmax, ymax; if (npoints > MAXPTS) { printf("Error in fillpoly: Only %d points allowed per polygon.\n", MAXPTS); printf("%d points were requested. Polygon is not drawn.\n",npoints); return; } /* Conservative (but fast) clip test -- check containing rectangle of * * polygon. */ xmin = xmax = points[0].x; ymin = ymax = points[0].y; for (i=1;i=0;i--) fprintf (ps, "%.2f %.2f\n", XPOST(points[i].x), YPOST(points[i].y)); fprintf (ps, "%d fillpoly\n", npoints); } } /* Draws text centered on xc,yc if it fits in boundx */ void drawtext (float xc, float yc, const char *text, float boundx) { int len, width, xw_off, yw_off, font_ascent, font_descent; #ifdef X11 len = strlen(text); width = XTextWidth(font_info[currentfontsize], text, len); font_ascent = font_info[currentfontsize]->ascent; font_descent = font_info[currentfontsize]->descent; #else /* WC : WIN32 */ HFONT hOldFont; SIZE textsize; TEXTMETRIC textmetric; hOldFont = (HFONT)SelectObject(hGraphicsDC, hGraphicsFont); if(!(hOldFont)) SELECT_ERROR(); if(SetTextColor(hGraphicsDC, win32_colors[currentcolor]) == CLR_INVALID) DRAW_ERROR(); len = strlen(text); if (!GetTextExtentPoint32(hGraphicsDC, text, len, &textsize)) DRAW_ERROR(); width = textsize.cx; if (!GetTextMetrics(hGraphicsDC, &textmetric)) DRAW_ERROR(); font_ascent = textmetric.tmAscent; font_descent = textmetric.tmDescent; #endif if (width > fabs(boundx*xmult)) { #ifdef WIN32 if(!SelectObject(hGraphicsDC, hOldFont)) SELECT_ERROR(); #endif return; /* Don't draw if it won't fit */ } xw_off = (int)(width/(2.*xmult)); /* NB: sign doesn't matter. */ /* NB: 2 * descent makes this slightly conservative but simplifies code. */ yw_off = (int)((font_ascent + 2 * font_descent)/(2.*ymult)); /* Note: text can be clipped when a little bit of it would be visible * * right now. Perhaps X doesn't return extremely accurate width and * * ascent values, etc? Could remove this completely by multiplying * * xw_off and yw_off by, 1.2 or 1.5. */ if (rect_off_screen(xc-xw_off, yc-yw_off, xc+xw_off, yc+yw_off)) { #ifdef WIN32 if(!SelectObject(hGraphicsDC, hOldFont)) SELECT_ERROR(); #endif return; } if (gl_state.disp_type == SCREEN) { #ifdef X11 XDrawString(display, toplevel, current_gc, xcoord(xc)-width/2, ycoord(yc) + (font_info[currentfontsize]->ascent - font_info[currentfontsize]->descent)/2, text, len); #else /* Win32 */ SetBkMode(hGraphicsDC, TRANSPARENT); if(!TextOut (hGraphicsDC, xcoord(xc)-width/2, ycoord(yc) - (font_ascent + font_descent)/2, text, len)) DRAW_ERROR(); if(!SelectObject(hGraphicsDC, hOldFont)) SELECT_ERROR(); #endif } else { fprintf(ps,"(%s) %.2f %.2f censhow\n",text,XPOST(xc),YPOST(yc)); } } void flushinput (void) { if (gl_state.disp_type != SCREEN) return; #ifdef X11 XFlush(display); #endif } void init_world (float x1, float y1, float x2, float y2) { /* Sets the coordinate system the user wants to draw into. */ xleft = x1; xright = x2; ytop = y1; ybot = y2; saved_xleft = xleft; /* Save initial world coordinates to allow full */ saved_xright = xright; /* view button to zoom all the way out. */ saved_ytop = ytop; saved_ybot = ybot; if (gl_state.disp_type == SCREEN) { update_transform(); } else { update_ps_transform(); } } /* Draw the current message in the text area at the screen bottom. */ void draw_message (void) { int savefontsize, savecolor; float ylow; #ifdef X11 int len, width; #endif if (gl_state.disp_type == SCREEN) { #ifdef X11 XClearWindow (display, textarea); len = strlen (statusMessage); width = XTextWidth(font_info[menu_font_size], statusMessage, len); XSetForeground(display, gc_menus,colors[WHITE]); XDrawRectangle(display, textarea, gc_menus, 0, 0, top_width - MWIDTH, T_AREA_HEIGHT); XSetForeground(display, gc_menus,colors[BLACK]); XDrawLine(display, textarea, gc_menus, 0, T_AREA_HEIGHT-1, top_width-MWIDTH, T_AREA_HEIGHT-1); XDrawLine(display, textarea, gc_menus, top_width-MWIDTH-1, 0, top_width-MWIDTH-1, T_AREA_HEIGHT-1); XDrawString(display, textarea, gc_menus, (top_width - MWIDTH - width)/2, T_AREA_HEIGHT/2 + (font_info[menu_font_size]->ascent - font_info[menu_font_size]->descent)/2, statusMessage, len); #else if(!InvalidateRect(hStatusWnd, NULL, TRUE)) DRAW_ERROR(); if(!UpdateWindow(hStatusWnd)) DRAW_ERROR(); #endif } else { /* Draw the message in the bottom margin. Printer's generally can't * * print on the bottom 1/4" (area with y < 18 in PostScript coords.) */ savecolor = currentcolor; setcolor (BLACK); savefontsize = currentfontsize; setfontsize (menu_font_size - 2); /* Smaller OK on paper */ ylow = ps_bot - 8; fprintf(ps,"(%s) %.2f %.2f censhow\n",statusMessage,(ps_left+ps_right)/2., ylow); setcolor (savecolor); setfontsize (savefontsize); } } /* Changes the message to be displayed on screen. */ void update_message (const char *msg) { strncpy (statusMessage, msg, BUFSIZE); draw_message (); #ifdef X11 // Make this appear immediately. Win32 does that automaticaly. XFlush (display); #endif // X11 } /* Zooms in by a factor of 1.666. */ static void zoom_in (void (*drawscreen) (void)) { float xdiff, ydiff; xdiff = xright - xleft; ydiff = ybot - ytop; xleft += xdiff/5; xright -= xdiff/5; ytop += ydiff/5; ybot -= ydiff/5; update_transform (); drawscreen(); } /* Zooms out by a factor of 1.666. */ static void zoom_out (void (*drawscreen) (void)) { float xdiff, ydiff; xdiff = xright - xleft; ydiff = ybot - ytop; xleft -= xdiff/3; xright += xdiff/3; ytop -= ydiff/3; ybot += ydiff/3; update_transform (); drawscreen(); } /* Sets the view back to the initial view set by init_world (i.e. a full * * view) of all the graphics. */ static void zoom_fit (void (*drawscreen) (void)) { xleft = saved_xleft; xright = saved_xright; ytop = saved_ytop; ybot = saved_ybot; update_transform (); drawscreen(); } /* Moves view 1/2 screen up. */ static void translate_up (void (*drawscreen) (void)) { float ystep; ystep = (ybot - ytop)/2; ytop -= ystep; ybot -= ystep; update_transform(); drawscreen(); } /* Moves view 1/2 screen down. */ static void translate_down (void (*drawscreen) (void)) { float ystep; ystep = (ybot - ytop)/2; ytop += ystep; ybot += ystep; update_transform(); drawscreen(); } /* Moves view 1/2 screen left. */ static void translate_left (void (*drawscreen) (void)) { float xstep; xstep = (xright - xleft)/2; xleft -= xstep; xright -= xstep; update_transform(); drawscreen(); } /* Moves view 1/2 screen right. */ static void translate_right (void (*drawscreen) (void)) { float xstep; xstep = (xright - xleft)/2; xleft += xstep; xright += xstep; update_transform(); drawscreen(); } static void update_win (int x[2], int y[2], void (*drawscreen)(void)) { float x1, x2, y1, y2; x[0] = min(x[0],top_width-MWIDTH); /* Can't go under menu */ x[1] = min(x[1],top_width-MWIDTH); y[0] = min(y[0],top_height-T_AREA_HEIGHT); /* Can't go under text area */ y[1] = min(y[1],top_height-T_AREA_HEIGHT); if ((x[0] == x[1]) || (y[0] == y[1])) { printf("Illegal (zero area) window. Window unchanged.\n"); return; } x1 = XTOWORLD(min(x[0],x[1])); x2 = XTOWORLD(max(x[0],x[1])); y1 = YTOWORLD(min(y[0],y[1])); y2 = YTOWORLD(max(y[0],y[1])); xleft = x1; xright = x2; ytop = y1; ybot = y2; update_transform(); drawscreen(); } /* The window button was pressed. Let the user click on the two * * diagonally opposed corners, and zoom in on this area. */ static void adjustwin (void (*drawscreen) (void)) { #ifdef X11 XEvent report; int corner, xold, yold, x[2], y[2]; corner = 0; xold = -1; yold = -1; /* Don't need to init yold, but stops compiler warning. */ while (corner<2) { XNextEvent (display, &report); switch (report.type) { case Expose: #ifdef VERBOSE printf("Got an expose event.\n"); printf("Count is: %d.\n",report.xexpose.count); printf("Window ID is: %d.\n",report.xexpose.window); #endif if (report.xexpose.count != 0) break; if (report.xexpose.window == menu) drawmenu(); else if (report.xexpose.window == toplevel) { drawscreen(); xold = -1; /* No rubber band on screen */ } else if (report.xexpose.window == textarea) draw_message(); break; case ConfigureNotify: top_width = report.xconfigure.width; top_height = report.xconfigure.height; update_transform(); drawmenu(); draw_message(); #ifdef VERBOSE printf("Got a ConfigureNotify.\n"); printf("New width: %d New height: %d.\n",top_width,top_height); #endif break; case ButtonPress: #ifdef VERBOSE printf("Got a buttonpress.\n"); printf("Window ID is: %d.\n",report.xbutton.window); printf("Location (%d, %d).\n", report.xbutton.x, report.xbutton.y); #endif if (report.xbutton.window != toplevel) break; x[corner] = report.xbutton.x; y[corner] = report.xbutton.y; if (corner == 0) { /* XSelectInput (display, toplevel, ExposureMask | StructureNotifyMask | ButtonPressMask | PointerMotionMask); */ } else { update_win(x,y,drawscreen); } corner++; break; case MotionNotify: if (corner) { #ifdef VERBOSE printf("Got a MotionNotify Event.\n"); printf("x: %d y: %d\n",report.xmotion.x,report.xmotion.y); #endif if (xold >= 0) { /* xold set -ve before we draw first box */ // Erase prior box. set_draw_mode(DRAW_XOR); XDrawRectangle(display,toplevel,gcxor,min(x[0],xold), min(y[0],yold),abs(x[0]-xold),abs(y[0]-yold)); set_draw_mode(DRAW_NORMAL); } /* Don't allow user to window under menu region */ xold = min(report.xmotion.x,top_width-1-MWIDTH); yold = report.xmotion.y; set_draw_mode(DRAW_XOR); // Use forcing versions, as we want these modes to apply // to the xor drawing context, and the regular versions // won't update the drawing context if there is no change in line // width etc. (but they might only be on the normal context) force_setlinewidth(1); force_setlinestyle(DASHED); force_setcolor(gl_state.background_cindex); // Draw rubber band box. XDrawRectangle(display,toplevel,gcxor,min(x[0],xold), min(y[0],yold),abs(x[0]-xold),abs(y[0]-yold)); set_draw_mode(DRAW_NORMAL); } break; } } /* XSelectInput (display, toplevel, ExposureMask | StructureNotifyMask | ButtonPressMask); */ #else /* Win32 */ /* Implemented as WM_LB... events */ /* Begin window adjust */ if (!windowAdjustFlag) { windowAdjustFlag = 1; } #endif } static void postscript (void (*drawscreen) (void)) { /* Takes a snapshot of the screen and stores it in pic?.ps. The * * first picture goes in pic1.ps, the second in pic2.ps, etc. */ static int piccount = 1; int success; char fname[BUFSIZE]; sprintf(fname,"pic%d.ps",piccount); printf("Writing postscript output to file %s\n", fname); success = init_postscript (fname); if (success) { drawscreen(); close_postscript (); piccount++; } else { printf ("Error initializing for postscript output.\n"); #ifdef WIN32 MessageBox(hMainWnd, "Error initializing postscript output.", NULL, MB_OK); #endif } } static void proceed (void (*drawscreen) (void)) { ProceedPressed = TRUE; } static void quit (void (*drawscreen) (void)) { close_graphics(); exit(0); } /* Release all my drawing structures (through the X server) and * * close down the connection. */ void close_graphics (void) { if (!gl_state.initialized) return; #ifdef X11 int i; for (i=0;i<=MAX_FONT_SIZE;i++) { if (font_is_loaded[i]) XFreeFont(display,font_info[i]); } XFreeGC(display,gc); XFreeGC(display,gcxor); XFreeGC(display,gc_menus); if (private_cmap != None) XFreeColormap (display, private_cmap); XCloseDisplay(display); #else /* Win32 */ int i; // Free the font data structure for each loaded font. for (i = 0; i <= MAX_FONT_SIZE; i++) { if (font_is_loaded[i]) { free (font_info[i]); } } // Destroy the window if(!DestroyWindow(hMainWnd)) DRAW_ERROR(); // free the window class (type information held by MS Windows) // for each of the four window types we created. Otherwise a // later call to init_graphics to open the graphics window up again // will fail. if (!UnregisterClass (szAppName, GetModuleHandle(NULL)) ) DRAW_ERROR(); if (!UnregisterClass (szGraphicsName, GetModuleHandle(NULL)) ) DRAW_ERROR(); if (!UnregisterClass (szStatusName, GetModuleHandle(NULL)) ) DRAW_ERROR(); if (!UnregisterClass (szButtonsName, GetModuleHandle(NULL)) ) DRAW_ERROR(); #endif free(button); button = NULL; for (i = 0; i <= MAX_FONT_SIZE; i++) { font_is_loaded[i] = false; font_info[i] = NULL; } gl_state.initialized = false; } /* Opens a file for PostScript output. The header information, * * clipping path, etc. are all dumped out. If the file could * * not be opened, the routine returns 0; otherwise it returns 1. */ int init_postscript (const char *fname) { ps = fopen (fname,"w"); if (ps == NULL) { printf("Error: could not open %s for PostScript output.\n",fname); printf("Drawing to screen instead.\n"); return (0); } gl_state.disp_type = POSTSCRIPT; /* Graphics go to postscript file now. */ /* Header for minimal conformance with the Adobe structuring convention */ fprintf(ps,"%%!PS-Adobe-1.0\n"); fprintf(ps,"%%%%DocumentFonts: Helvetica\n"); fprintf(ps,"%%%%Pages: 1\n"); /* Set up postscript transformation macros and page boundaries */ update_ps_transform(); /* Bottom margin is at ps_bot - 15. to leave room for the on-screen message. */ fprintf(ps,"%%%%HiResBoundingBox: %.2f %.2f %.2f %.2f\n", ps_left, ps_bot - 15., ps_right, ps_top); fprintf(ps,"%%%%EndComments\n"); fprintf(ps,"/censhow %%draw a centered string\n"); fprintf(ps," { moveto %% move to proper spot\n"); fprintf(ps," dup stringwidth pop %% get x length of string\n"); fprintf(ps," -2 div %% Proper left start\n"); fprintf(ps," yoff rmoveto %% Move left that much and down half font height\n"); fprintf(ps," show newpath } def %% show the string\n\n"); fprintf(ps,"/setfontsize %% set font to desired size and compute " "centering yoff\n"); fprintf(ps," { /Helvetica findfont\n"); fprintf(ps," 8 scalefont\n"); fprintf(ps," setfont %% Font size set ...\n\n"); fprintf(ps," 0 0 moveto %% Get vertical centering offset\n"); fprintf(ps," (Xg) true charpath\n"); fprintf(ps," flattenpath pathbbox\n"); fprintf(ps," /ascent exch def pop -1 mul /descent exch def pop\n"); fprintf(ps," newpath\n"); fprintf(ps," descent ascent sub 2 div /yoff exch def } def\n\n"); fprintf(ps,"%% Next two lines for debugging only.\n"); fprintf(ps,"/str 20 string def\n"); fprintf(ps,"/pnum {str cvs print ( ) print} def\n"); fprintf(ps,"/drawline %% draw a line from (x2,y2) to (x1,y1)\n"); fprintf(ps," { moveto lineto stroke } def\n\n"); fprintf(ps,"/rect %% outline a rectangle \n"); fprintf(ps," { /y2 exch def /x2 exch def /y1 exch def /x1 exch def\n"); fprintf(ps," x1 y1 moveto\n"); fprintf(ps," x2 y1 lineto\n"); fprintf(ps," x2 y2 lineto\n"); fprintf(ps," x1 y2 lineto\n"); fprintf(ps," closepath } def\n\n"); fprintf(ps,"/drawrect %% draw outline of a rectanagle\n"); fprintf(ps," { rect stroke } def\n\n"); fprintf(ps,"/fillrect %% fill in a rectanagle\n"); fprintf(ps," { rect fill } def\n\n"); fprintf (ps,"/drawarc { arc stroke } def %% draw an arc\n"); fprintf (ps,"/drawarcn { arcn stroke } def " " %% draw an arc in the opposite direction\n\n"); fprintf (ps,"%%Fill a counterclockwise or clockwise arc sector, " "respectively.\n"); fprintf (ps,"/fillarc { moveto currentpoint 5 2 roll arc closepath fill } " "def\n"); fprintf (ps,"/fillarcn { moveto currentpoint 5 2 roll arcn closepath fill } " "def\n\n"); fprintf (ps,"/fillpoly { 3 1 roll moveto %% move to first point\n" " 2 exch 1 exch {pop lineto} for %% line to all other points\n" " closepath fill } def\n\n"); fprintf(ps,"%%Color Definitions:\n"); fprintf(ps,"/white { 1 setgray } def\n"); fprintf(ps,"/black { 0 setgray } def\n"); fprintf(ps,"/grey55 { .55 setgray } def\n"); fprintf(ps,"/grey75 { .75 setgray } def\n"); fprintf(ps,"/blue { 0 0 1 setrgbcolor } def\n"); fprintf(ps,"/green { 0 1 0 setrgbcolor } def\n"); fprintf(ps,"/yellow { 1 1 0 setrgbcolor } def\n"); fprintf(ps,"/cyan { 0 1 1 setrgbcolor } def\n"); fprintf(ps,"/red { 1 0 0 setrgbcolor } def\n"); fprintf(ps,"/darkgreen { 0 0.5 0 setrgbcolor } def\n"); fprintf(ps,"/magenta { 1 0 1 setrgbcolor } def\n"); fprintf(ps,"/bisque { 1 0.89 0.77 setrgbcolor } def\n"); fprintf(ps,"/lightblue { 0.68 0.85 0.9 setrgbcolor } def\n"); fprintf(ps,"/thistle { 0.85 0.75 0.85 setrgbcolor } def\n"); fprintf(ps,"/plum { 0.87 0.63 0.87 setrgbcolor } def\n"); fprintf(ps,"/khaki { 0.94 0.9 0.55 setrgbcolor } def\n"); fprintf(ps,"/coral { 1 0.5 0.31 setrgbcolor } def\n"); fprintf(ps,"/turquoise { 0.25 0.88 0.82 setrgbcolor } def\n"); fprintf(ps,"/mediumpurple { 0.58 0.44 0.86 setrgbcolor } def\n"); fprintf(ps,"/darkslateblue { 0.28 0.24 0.55 setrgbcolor } def\n"); fprintf(ps,"/darkkhaki { 0.74 0.72 0.42 setrgbcolor } def\n"); fprintf(ps,"\n%%Solid and dashed line definitions:\n"); fprintf(ps,"/linesolid {[] 0 setdash} def\n"); fprintf(ps,"/linedashed {[3 3] 0 setdash} def\n"); fprintf(ps,"\n%%%%EndProlog\n"); fprintf(ps,"%%%%Page: 1 1\n\n"); /* Set up PostScript graphics state to match current one. */ force_setcolor (currentcolor); force_setlinestyle (currentlinestyle); force_setlinewidth (currentlinewidth); force_setfontsize (currentfontsize); /* Draw this in the bottom margin -- must do before the clippath is set */ draw_message (); /* Set clipping on page. */ fprintf(ps,"%.2f %.2f %.2f %.2f rect ",ps_left, ps_bot,ps_right,ps_top); fprintf(ps,"clip newpath\n\n"); return (1); } /* Properly ends postscript output and redirects output to screen. */ void close_postscript (void) { fprintf(ps,"showpage\n"); fprintf(ps,"\n%%%%Trailer\n"); fclose (ps); gl_state.disp_type = SCREEN; update_transform(); /* Ensure screen world reflects any changes * * made while printing. */ /* Need to make sure that we really set up the graphics context. * The current font set indicates the last font used in a postscript call, * etc., *NOT* the font set in the X11 or Win32 graphics context. Force the * current font, colour etc. to be applied to the graphics context, so * subsequent drawing commands work properly. */ force_setcolor (currentcolor); force_setlinestyle (currentlinestyle); force_setlinewidth (currentlinewidth); force_setfontsize (currentfontsize); } /* Sets up the default menu buttons on the right hand side of the window. */ static void build_default_menu (void) { int i, xcen, x1, y1, bwid, bheight, space; const int NUM_ARROW_BUTTONS = 4, NUM_STANDARD_BUTTONS = 12, SEPARATOR_BUTTON_INDEX = 8; #ifdef X11 unsigned long valuemask; XSetWindowAttributes menu_attributes; menu = XCreateSimpleWindow(display,toplevel, top_width-MWIDTH, 0, MWIDTH, display_height, 0, colors[BLACK], colors[LIGHTGREY]); menu_attributes.event_mask = ExposureMask; /* Ignore button presses on the menu background. */ menu_attributes.do_not_propagate_mask = ButtonPressMask; /* Keep menu on top right */ menu_attributes.win_gravity = NorthEastGravity; valuemask = CWWinGravity | CWEventMask | CWDontPropagate; XChangeWindowAttributes(display, menu, valuemask, &menu_attributes); XMapWindow (display, menu); #endif button = (t_button *) my_malloc (NUM_STANDARD_BUTTONS * sizeof (t_button)); /* Now do the arrow buttons */ bwid = 28; space = 3; y1 = 10; xcen = 51; x1 = xcen - bwid/2; button[0].xleft = x1; button[0].ytop = y1; #ifdef X11 setpoly (0, bwid/2, bwid/2, bwid/3, -PI/2.); /* Up */ #else button[0].type = BUTTON_TEXT; strcpy(button[0].text, "U"); #endif button[0].fcn = translate_up; y1 += bwid + space; x1 = xcen - 3*bwid/2 - space; button[1].xleft = x1; button[1].ytop = y1; #ifdef X11 setpoly (1, bwid/2, bwid/2, bwid/3, PI); /* Left */ #else button[1].type = BUTTON_TEXT; strcpy(button[1].text, "L"); #endif button[1].fcn = translate_left; x1 = xcen + bwid/2 + space; button[2].xleft = x1; button[2].ytop = y1; #ifdef X11 setpoly (2, bwid/2, bwid/2, bwid/3, 0); /* Right */ #else button[2].type = BUTTON_TEXT; strcpy(button[2].text, "R"); #endif button[2].fcn = translate_right; y1 += bwid + space; x1 = xcen - bwid/2; button[3].xleft = x1; button[3].ytop = y1; #ifdef X11 setpoly (3, bwid/2, bwid/2, bwid/3, +PI/2.); /* Down */ #else button[3].type = BUTTON_TEXT; strcpy(button[3].text, "D"); #endif button[3].fcn = translate_down; for (i = 0; i < NUM_ARROW_BUTTONS; i++) { button[i].width = bwid; button[i].height = bwid; button[i].enabled = 1; } /* Rectangular buttons */ y1 += bwid + space + 6; space = 8; bwid = 90; bheight = 26; x1 = xcen - bwid/2; for (i = NUM_ARROW_BUTTONS; i < NUM_STANDARD_BUTTONS; i++) { button[i].xleft = x1; button[i].ytop = y1; button[i].type = BUTTON_TEXT; button[i].width = bwid; button[i].enabled = 1; if (i != SEPARATOR_BUTTON_INDEX) { button[i].height = bheight; y1 += bheight + space; } else { button[i].height = 2; button[i].type = BUTTON_SEPARATOR; y1 += 2 + space; } } strcpy (button[4].text,"Zoom In"); strcpy (button[5].text,"Zoom Out"); strcpy (button[6].text,"Zoom Fit"); strcpy (button[7].text,"Window"); strcpy (button[8].text,"---1"); strcpy (button[9].text,"PostScript"); strcpy (button[10].text,"Proceed"); strcpy (button[11].text,"Exit"); button[4].fcn = zoom_in; button[5].fcn = zoom_out; button[6].fcn = zoom_fit; button[7].fcn = adjustwin; // see 'adjustButton' below in WIN32 section button[8].fcn = NULL; button[9].fcn = postscript; button[10].fcn = proceed; button[11].fcn = quit; for (i = 0; i < NUM_STANDARD_BUTTONS; i++) map_button (i); num_buttons = NUM_STANDARD_BUTTONS; #ifdef WIN32 adjustButton = 7; if(!InvalidateRect(hButtonsWnd, NULL, TRUE)) DRAW_ERROR(); if(!UpdateWindow(hButtonsWnd)) DRAW_ERROR(); #endif } /* Makes sure the font of the specified size is loaded. Point_size * * MUST be between 1 and MAX_FONT_SIZE. */ static void load_font(int pointsize) { if (pointsize > MAX_FONT_SIZE || pointsize < 1) { printf ("Error: font size %d is out of valid range, 1 to %d.\n", pointsize, MAX_FONT_SIZE); return; } if (font_is_loaded[pointsize]) // Nothing to do. return; #ifdef X11 #define NUM_FONT_TYPES 3 char fontname[NUM_FONT_TYPES][BUFSIZE]; int ifont; bool success = false; /* Use proper point-size medium-weight upright helvetica font */ // Exists on most X11 systems. // Backup font: lucidasans, in the new naming style. sprintf(fontname[0],"-*-helvetica-medium-r-*--*-%d0-*-*-*-*-*-*", pointsize); sprintf(fontname[1], "lucidasans-%d", pointsize); sprintf(fontname[2],"-schumacher-clean-medium-r-*--*-%d0-*-*-*-*-*-*", pointsize); for (ifont = 0; ifont < NUM_FONT_TYPES; ifont++) { #ifdef VERBOSE printf ("Loading font: point size: %d, fontname: %s\n",pointsize, fontname[ifont]); #endif /* Load font and get font information structure. */ if ((font_info[pointsize] = XLoadQueryFont(display,fontname[ifont])) == NULL) { #ifdef VERBOSE fprintf( stderr, "Cannot open font %s\n", fontname[ifont]); #endif } else { success = true; break; } } if (!success) { printf ("Error in load_font: cannot load any font of pointsize %d.\n", pointsize); printf ("Use xlsfonts to list available fonts, and modify load_font\n"); printf ("in graphics.cpp.\n"); exit (1); } #else /* WIN32 */ LOGFONT *lf = font_info[pointsize] = (LOGFONT*)my_malloc(sizeof(LOGFONT)); ZeroMemory(lf, sizeof(LOGFONT)); lf->lfHeight = pointsize; lf->lfWeight = FW_NORMAL; lf->lfCharSet = ANSI_CHARSET; lf->lfOutPrecision = OUT_DEFAULT_PRECIS; lf->lfClipPrecision = CLIP_DEFAULT_PRECIS; lf->lfQuality = PROOF_QUALITY; lf->lfPitchAndFamily = VARIABLE_PITCH | FF_SWISS; strcpy(lf->lfFaceName, "Arial"); #endif font_is_loaded[pointsize] = true; } /* Return information useful for debugging. * Used to return the top-level window object too, but that made graphics.h * export all windows and X11 headers to the client program, so VB deleted * that object (mainwnd) from this structure. */ void report_structure(t_report *report) { report->xmult = xmult; report->ymult = ymult; report->xleft = xleft; report->xright = xright; report->ytop = ytop; report->ybot = ybot; report->ps_xmult = ps_xmult; report->ps_ymult = ps_ymult; report->top_width = top_width; report->top_height = top_height; } void set_mouse_move_input (bool enable) { get_mouse_move_input = enable; } void set_keypress_input (bool enable) { get_keypress_input = enable; } void enable_or_disable_button (int ibutton, bool enabled) { if (button[ibutton].type != BUTTON_SEPARATOR) { button[ibutton].enabled = enabled; #ifdef WIN32 EnableWindow(button[ibutton].hwnd, button[ibutton].enabled); #else // X11 drawbut(ibutton); XFlush(display); #endif } } void set_draw_mode (enum e_draw_mode draw_mode) { /* Set normal (overwrite) or xor (useful for rubber-banding) * drawing mode. */ if (draw_mode == DRAW_NORMAL) { #ifdef X11 current_gc = gc; #else if (!SetROP2(hGraphicsDC, R2_COPYPEN)) SELECT_ERROR(); #endif } else { // DRAW_XOR #ifdef X11 current_gc = gcxor; #else if (!SetROP2(hGraphicsDC, R2_XORPEN)) SELECT_ERROR(); #endif } current_draw_mode = draw_mode; } void change_button_text(const char *button_name, const char *new_button_text) { /* Change the text on a button with button_name to new_button_text. * Not a strictly necessary function, since you could intead just * destroy button_name and make a new buton. */ int i, bnum; bnum = -1; for (i=4;itype == Expose) { return (True); } return (False); } static void menutext(Window win, int xc, int yc, const char *text) { /* draws text center at xc, yc -- used only by menu drawing stuff */ int len, width; len = strlen(text); width = XTextWidth(font_info[menu_font_size], text, len); XDrawString(display, win, gc_menus, xc-width/2, yc + (font_info[menu_font_size]->ascent - font_info[menu_font_size]->descent)/2, text, len); } static void drawbut (int bnum) { /* Draws button bnum in either its pressed or unpressed state. */ int width, height, thick, i, ispressed; XPoint mypoly[6]; width = button[bnum].width; height = button[bnum].height; if (button[bnum].type == BUTTON_SEPARATOR) { int x,y; x = button[bnum].xleft; y = button[bnum].ytop; XSetForeground(display, gc_menus,colors[WHITE]); XDrawLine(display, menu, gc_menus, x, y+1, x+width, y+1); XSetForeground(display, gc_menus,colors[BLACK]); XDrawLine(display, menu, gc_menus, x, y, x+width, y); return; } ispressed = button[bnum].ispressed; thick = 2; /* Draw top and left edges of 3D box. */ if (ispressed) { XSetForeground(display, gc_menus,colors[BLACK]); } else { XSetForeground(display, gc_menus,colors[WHITE]); } /* Note: X Windows doesn't appear to draw the bottom pixel of * * a polygon with XFillPolygon, so I make this 1 pixel thicker * * to compensate. */ mypoly[0].x = 0; mypoly[0].y = height; mypoly[1].x = 0; mypoly[1].y = 0; mypoly[2].x = width; mypoly[2].y = 0; mypoly[3].x = width-thick; mypoly[3].y = thick; mypoly[4].x = thick; mypoly[4].y = thick; mypoly[5].x = thick; mypoly[5].y = height-thick; XFillPolygon(display,button[bnum].win,gc_menus,mypoly,6,Convex, CoordModeOrigin); /* Draw bottom and right edges of 3D box. */ if (ispressed) { XSetForeground(display, gc_menus,colors[WHITE]); } else { XSetForeground(display, gc_menus,colors[BLACK]); } mypoly[0].x = 0; mypoly[0].y = height; mypoly[1].x = width; mypoly[1].y = height; mypoly[2].x = width; mypoly[2].y = 0; mypoly[3].x = width-thick; mypoly[3].y = thick; mypoly[4].x = width-thick; mypoly[4].y = height-thick; mypoly[5].x = thick; mypoly[5].y = height-thick; XFillPolygon(display,button[bnum].win,gc_menus,mypoly,6,Convex, CoordModeOrigin); /* Draw background */ if (ispressed) { XSetForeground(display, gc_menus,colors[DARKGREY]); } else { XSetForeground(display, gc_menus,colors[LIGHTGREY]); } /* Give x,y of top corner and width and height */ XFillRectangle (display,button[bnum].win,gc_menus,thick,thick, width-2*thick, height-2*thick); /* Draw polygon, if there is one */ if (button[bnum].type == BUTTON_POLY) { for (i=0;i<3;i++) { mypoly[i].x = button[bnum].poly[i][0]; mypoly[i].y = button[bnum].poly[i][1]; } XSetForeground(display, gc_menus,colors[BLACK]); XFillPolygon(display,button[bnum].win,gc_menus,mypoly,3,Convex, CoordModeOrigin); } /* Draw text, if there is any */ if (button[bnum].type == BUTTON_TEXT) { if (button[bnum].enabled) XSetForeground(display, gc_menus,colors[BLACK]); else XSetForeground(display, gc_menus,colors[DARKGREY]); menutext(button[bnum].win,button[bnum].width/2, button[bnum].height/2,button[bnum].text); } } static int which_button (Window win) { int i; for (i=0;iptMinTrackSize.x = display_width / 2; lpMinMaxInfo->ptMinTrackSize.y = display_height / 2; return 0; case WM_DESTROY: if(!DeleteObject(hGrayBrush)) DELETE_ERROR(); PostQuitMessage(0); return 0; case WM_KEYDOWN: if (get_keypress_input) keypress_ptr((char) wParam); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); } static LRESULT CALLBACK GraphicsWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static TEXTMETRIC tm; PAINTSTRUCT ps; static RECT oldAdjustRect; static HPEN hDotPen = 0; static HBITMAP hbmBuffer = 0, hbmObjtest, hbmAllObjtest; static int X, Y, i; switch(message) { case WM_CREATE: /* Get the text metrics once upon creation (system font cannot change) */ hCurrentDC = hGraphicsDC = hForegroundDC = GetDC (hwnd); if(!hGraphicsDC) DRAW_ERROR(); hBackgroundDC = CreateCompatibleDC(hForegroundDC); if (!hBackgroundDC) CREATE_ERROR(); if (!SetMapMode(hBackgroundDC, MM_TEXT)) CREATE_ERROR(); hbmBuffer = CreateCompatibleBitmap(hForegroundDC, display_width, display_height); if (!(hbmBuffer)) CREATE_ERROR(); if (!SelectObject(hBackgroundDC, hbmBuffer)) SELECT_ERROR(); // monochrome bitmap hObjtestDC = CreateCompatibleDC(hForegroundDC); if (!hObjtestDC) CREATE_ERROR(); if (!SetMapMode(hObjtestDC, MM_TEXT)) CREATE_ERROR(); hbmObjtest = CreateCompatibleBitmap(hObjtestDC, display_width, display_height); if (!(hbmObjtest)) CREATE_ERROR(); if (!SelectObject(hObjtestDC, hbmObjtest)) SELECT_ERROR(); // monochrome bitmap hAllObjtestDC = CreateCompatibleDC(hForegroundDC); if (!hObjtestDC) CREATE_ERROR(); if (!SetMapMode(hAllObjtestDC, MM_TEXT)) CREATE_ERROR(); hbmAllObjtest = CreateCompatibleBitmap(hAllObjtestDC, display_width, display_height); if (!(hbmAllObjtest)) CREATE_ERROR(); if (!SelectObject(hAllObjtestDC, hbmAllObjtest)) SELECT_ERROR(); //if(!GetTextMetrics (hGraphicsDC, &tm)) // DRAW_ERROR(); if(!SetBkMode(hGraphicsDC, TRANSPARENT)) DRAW_ERROR(); /* Setup the pens, etc */ currentlinestyle = SOLID; currentcolor = BLACK; currentlinewidth = 1; /* if(!ReleaseDC (hwnd, hGraphicsDC)) DRAW_ERROR(); hGraphicsDC = 0; */ currentfontsize = 12; return 0; case WM_PAINT: // was in xor mode, but got a general redraw. // switch to normal drawing so we repaint properly. if (current_draw_mode == DRAW_XOR) { set_draw_mode(DRAW_NORMAL); invalidate_screen(); return 0; } hCurrentDC = hGraphicsDC; drawtoscreen(); /*hGraphicsDC =*/ BeginPaint(hwnd, &ps); if(!hGraphicsDC) DRAW_ERROR(); if (InEventLoop) { if(!GetUpdateRect(hwnd, &updateRect, FALSE)) { updateRect.left = 0; updateRect.right = top_width; updateRect.top = 0; updateRect.bottom = top_height; } if(windowAdjustFlag > 1) { hDotPen = CreatePen(PS_DASH, 1, win32_colors[gl_state.background_cindex]); if(!hDotPen) CREATE_ERROR(); if (!SetROP2(hGraphicsDC, R2_XORPEN)) SELECT_ERROR(); if(!SelectObject(hGraphicsDC, GetStockObject(NULL_BRUSH))) SELECT_ERROR(); if(!SelectObject(hGraphicsDC, hDotPen)) SELECT_ERROR(); if(!Rectangle(hGraphicsDC, oldAdjustRect.left, oldAdjustRect.top, oldAdjustRect.right, oldAdjustRect.bottom)) DRAW_ERROR(); if(!Rectangle(hGraphicsDC, adjustRect.left, adjustRect.top, adjustRect.right, adjustRect.bottom)) DRAW_ERROR(); oldAdjustRect = adjustRect; if (!SetROP2(hGraphicsDC, R2_COPYPEN)) SELECT_ERROR(); if(!SelectObject(hGraphicsDC, GetStockObject(NULL_PEN))) SELECT_ERROR(); if(!DeleteObject(hDotPen)) DELETE_ERROR(); } else drawscreen_ptr(); } EndPaint(hwnd, &ps); hGraphicsDC = hCurrentDC; /* Crash hard if called at wrong time */ /* hGraphicsDC = NULL;*/ return 0; case WM_SIZE: /* Window has been resized. Save the new client dimensions */ cxClient = LOWORD (lParam); cyClient = HIWORD (lParam); update_transform(); return 0; case WM_DESTROY: if(!DeleteObject(hGraphicsPen)) DELETE_ERROR(); if(!DeleteObject(hGraphicsBrush)) DELETE_ERROR(); if(!DeleteObject(hGraphicsFont)) DELETE_ERROR(); if (!DeleteObject(hbmBuffer)) DELETE_ERROR(); if (!DeleteObject(hbmObjtest)) DELETE_ERROR(); if (!DeleteObject(hbmAllObjtest)) DELETE_ERROR(); if(!DeleteDC(hBackgroundDC)) DELETE_ERROR(); if(!DeleteDC(hObjtestDC)) DELETE_ERROR(); if(!DeleteDC(hAllObjtestDC)) DELETE_ERROR(); if(!ReleaseDC(hwnd, hForegroundDC)) DELETE_ERROR(); PostQuitMessage(0); return 0; case WM_LBUTTONDOWN: if (!windowAdjustFlag) { mouseclick_ptr(XTOWORLD(LOWORD(lParam)), YTOWORLD(HIWORD(lParam))); } else { // Special handling for the "Window" command, which takes multiple clicks. // First you push the button, then you click for one corner, then you click for the other // corner. if(windowAdjustFlag == 1) { windowAdjustFlag ++; X = adjustRect.left = adjustRect.right = LOWORD(lParam); Y = adjustRect.top = adjustRect.bottom = HIWORD(lParam); oldAdjustRect = adjustRect; } else { int i; int adjustx[2], adjusty[2]; windowAdjustFlag = 0; button[adjustButton].ispressed = 0; SendMessage(button[adjustButton].hwnd, BM_SETSTATE, 0, 0); for (i=0; i= 2) { if ( X > LOWORD(lParam)) { adjustRect.left = LOWORD(lParam); adjustRect.right = X; } else { adjustRect.left = X; adjustRect.right = LOWORD(lParam); } if ( Y > HIWORD(lParam)) { adjustRect.top = HIWORD(lParam); adjustRect.bottom = Y; } else { adjustRect.top = Y; adjustRect.bottom = HIWORD(lParam); } if(!InvalidateRect(hGraphicsWnd, &oldAdjustRect, FALSE)) DRAW_ERROR(); if(!InvalidateRect(hGraphicsWnd, &adjustRect, FALSE)) DRAW_ERROR(); if(!UpdateWindow(hGraphicsWnd)) DRAW_ERROR(); return 0; } else if (get_mouse_move_input) mousemove_ptr(XTOWORLD(LOWORD(lParam)), YTOWORLD(HIWORD(lParam))); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); } static LRESULT CALLBACK StatusWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; RECT rect; switch(message) { case WM_CREATE: hdc = GetDC(hwnd); if(!hdc) DRAW_ERROR(); if(!SetBkMode(hdc, TRANSPARENT)) DRAW_ERROR(); if(!ReleaseDC(hwnd, hdc)) DRAW_ERROR(); return 0; case WM_PAINT: hdc = BeginPaint(hwnd, &ps); if(!hdc) DRAW_ERROR(); if(!GetClientRect(hwnd, &rect)) DRAW_ERROR(); if(!SelectObject(hdc, GetStockObject(NULL_BRUSH))) SELECT_ERROR(); if(!SelectObject(hdc, GetStockObject(WHITE_PEN))) SELECT_ERROR(); if(!Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom)) DRAW_ERROR(); if(!SelectObject(hdc, GetStockObject(BLACK_PEN))) SELECT_ERROR(); if(!MoveToEx(hdc, rect.left, rect.bottom-1, NULL)) DRAW_ERROR(); if(!LineTo(hdc, rect.right-1, rect.bottom-1)) DRAW_ERROR(); if(!LineTo(hdc, rect.right-1, rect.top)) DRAW_ERROR(); if(!DrawText(hdc, TEXT(statusMessage), -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE)) DRAW_ERROR(); if(!EndPaint(hwnd, &ps)) DRAW_ERROR(); return 0; case WM_SIZE: return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); } static LRESULT CALLBACK ButtonsWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; RECT rect; static HBRUSH hBrush; int i; switch(message) { case WM_COMMAND: if (!windowAdjustFlag) { button[LOWORD(wParam) - 200].fcn(invalidate_screen); if (windowAdjustFlag) { adjustButton = LOWORD(wParam) - 200; button[adjustButton].ispressed = 1; for (i=0; i MAXPTS) DRAW_ERROR(); /* Conservative (but fast) clip test -- check containing rectangle of * * polygon. */ xmin = xmax = points[0].x; ymin = ymax = points[0].y; for (i=1;i= ymax * 0.9) { if (height <= ymax) ret = 0; else ret = 1; } else ret = -1; if(!SelectObject(hGraphicsDC, hOldFont)) SELECT_ERROR(); return ret; } int findfontsize(float ymax) { // find the correct point size which will fit in the specified ymax as the max // height of the font, using a binary search int bot = 1; int top = MAX_FONT_SIZE; int mid, check; while (bot <= top) { mid = (bot+top)/2; check = check_fontsize(mid, ymax); if (!(check)) return mid; else if (check > 0) // too big top = mid - 1; else // too small bot = mid + 1; } if (bot > MAX_FONT_SIZE) return MAX_FONT_SIZE; return -1; // can't fit } #endif /******** Win32 Specific Definitions ********************/ #else /***** NO_GRAPHICS *******/ /* No graphics at all. Stub everything out so calling program doesn't have to change * but of course graphics won't do anything. */ #include "graphics.h" void event_loop (void (*act_on_mousebutton) (float x, float y), void (*act_on_mousemove) (float x, float y), void (*act_on_keypress) (char key_pressed), void (*drawscreen) (void)) { } void init_graphics (const char *window_name, int cindex) { } void close_graphics (void) { } void update_message (const char *msg) { } void draw_message (void) { } void init_world (float xl, float yt, float xr, float yb) { } void flushinput (void) { } void setcolor (int cindex) { } int getcolor (void) { return 0; } void setlinestyle (int linestyle) { } void setlinewidth (int linewidth) { } void setfontsize (int pointsize) { } void drawline (float x1, float y1, float x2, float y2) { } void drawrect (float x1, float y1, float x2, float y2) { } void fillrect (float x1, float y1, float x2, float y2) { } void fillpoly (t_point *points, int npoints) { } void drawarc (float xcen, float ycen, float rad, float startang, float angextent) { } void drawellipticarc (float xc, float yc, float radx, float rady, float startang, float angextent) { } void fillarc (float xcen, float ycen, float rad, float startang, float angextent) { } void fillellipticarc (float xc, float yc, float radx, float rady, float startang, float angextent) { } void drawtext (float xc, float yc, const char *text, float boundx) { } void clearscreen (void) { } void create_button (const char *prev_button_text , const char *button_text, void (*button_func) (void (*drawscreen) (void))) { } void destroy_button (const char *button_text) { } int init_postscript (const char *fname) { return (1); } void close_postscript (void) { } void report_structure(t_report*) { } void set_mouse_move_input (bool) { } void set_keypress_input (bool) { } void set_draw_mode (enum e_draw_mode draw_mode) { } void enable_or_disable_button(int ibutton, bool enabled) { } void change_button_text(const char *button_text, const char *new_button_text) { } #ifdef WIN32 void drawtobuffer(void) { } void drawtoscreen(void) { } void displaybuffer(void) { } void drawcurve(t_point *points, int npoints) { } void fillcurve(t_point *points, int npoints) { } void object_start(int all) { } void object_end() { } int pt_on_object(float x, float y) { } int findfontsize(float ymax) { } #endif // WIN32 (subset of commands) #endif // NO_GRAPHICS