/* ------------------------------------------------------------------------- */ /* ezgraph3d.c -- Graphics routines (except for marching cubes). * * Copyright (C) 1996 - 1998, 2006, 2007 Dwight Barkley and Matthew Dowle * * RCS Information * --------------------------- * $Revision: 1.5.1.1 $ * $Date: 2007/05/07 10:07:03 $ * ------------------------------------------------------------------------- */ /* Modifications for EZView: 2010, Vadim Biktashev vnb@liv.ac.uk */ #include #include #include #include #include #include #include #include #include #include #include #include #include "ezscroll.h" #include "ezgraph3d.h" #include "ezmarching.h" /* ------------------------------------------------------------------------- * This file contains all graphics manipulation routines. The functions that * compute the iso-surfaces and filaments and render these are in ezmarching. * * The important things to know about this file are: * * (1) X11 is used to open the graphics window and handle the events * (i.e. key presses and pointer motions within the window). InitX() is a * long routine that opens the window; Event_check() contains the event loop * that looks for window events; QuitX() quits X obviously. You should be * able to switch over to a higher-level method with only minor * modifications except for these routines. * * (2) After the window is open, OpenGL is used to handle all the rendering. * myReshape() must be called before anything can be plotted. To understand * this function see the OpenGL manual. * * The routines near the top of this file handle the interactive graphics * through OpenGL calls. Note: to add a new interactive feature, add to the * event loop in Event_check() and add a corresponding routine to perform the * desired task. * * (3) Note on the graphics modes. * * There are 3 modes the program can be in: * MODE_SIMULATING Simulation progresses through time (it cannot be * rotated while in this mode) * MODE_VIEWING Simulation is paused. While in this mode, graphics * lists are created for use in rotations. New list must * be created for each basic change in what is plotted, * e.g switching u-field to v-field, turning on clipping, * etc. * MODE_ROTATING Mouse button 1 is down so the view responds to mouse * motion. Graphics lists are called and no new * iso-surfaces are computed (i.e. fast rotations). * ------------------------------------------------------------------------- */ /* * Global variables for this file only * ----------------------------------- */ static Display *theDisplay; static int theDWidth; static int theDHeight; static Window theWindow; static XTextProperty theWindowName, theIconName; static int WindowWidth; static int WindowHeight; static int winx, winy; static int horspace, vertspace; static int ezmode; /* Mode flag */ static int lastx, lasty; /* Used by Rotate() and Mouse_down() */ static GLbitfield ezdepth=1; /* Depth buffer flag */ static Real plot_length[3]; /* Lengths of the simulation volume in graphics * coordinates (see Draw_ini()). */ /* These arrays set the light sources and properties. See Draw_ini(). */ static GLfloat mat_emission[] = {0.5, 0.5, 0.5, 1.0}; static GLfloat mat_diffusive[] = {0, 0, 0, 0}; static GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0}; static GLfloat mat_shininess[] = {100.0}; static GLfloat light_position0[] = {100, 100, -100, 0}; /* static GLfloat light_ambient1[] = {0., 0., 0., 0.}; */ /* static GLfloat light_diffuse1[] = {0.4, 0.4, 0.4, 0.0}; */ /* static GLfloat light_specular1[] = {0.3, 0.3, 0.3, 0.0}; */ static GLfloat light_position1[] = {-100, 100, -100, 0}; static GLfloat light_position2[] = {-100, -100, -100, 0}; static GLfloat light_position3[] = {100, -100, -100, 0}; /* * Private functions * ----------------- */ static void Restart (void); /* static void Pause (void); */ static void Make_rot_list (void); static void Make_body_lists (void); static void Delete_body_list (void); static void Show_body (void); static void Hide_body (void); static void View_u_field (void); static void View_v_field (void); static void View_w_field (void); static void View_U_field (void); static void View_V_field (void); static void View_W_field (void); static void Toggle_no_field (void); static void Toggle_backs_removal (void); static void Toggle_clipping_plane (void); static void Toggle_depth_buffer (void); static void Toggle_filament_plotting (void); static void Toggle_history_writing (void); static void Toggle_image_saving (void); static int Toggle_filament_writing (void); static void Mouse_up (void); static void Mouse_down (int x, int y); static void Rotate (int x, int y, float new_theta, float new_phi); static void setView (GLfloat theta, GLfloat phi); static void myReshape (int w, int h); static void Assign_par (void); static void InitX (int *winx, int *winy, int width, int height); static Bool WaitForNotify (Display *d, XEvent *e, char *arg); static void Save_image (void); static void Save_all (void); static void MoveWindow (int dx, int dy); /* ========================================================================= */ void Draw (void) { /* Highest level plotting routine. If in MODE_SIMULATING or * MODE_VIEWING then call the Marching_Cubes function. * * If in MODE_ROTATING call glCallList() which rapidly plots the new view * of the pre-compiled graphics scene. */ /* static char *name = WINDOW_TITLE; */ char name[64]=WINDOW_TITLE; char *pname=&(name[0]); /* strcpy(name,WINDOW_TITLE); */ /* sprintf(name,"%s",WINDOW_TITLE); */ LAYERS; sprintf(name,"%s %d#%d%d%d m=%d t=%.2f %s%c [%c%c%c%c%c] [%c%c%c] (%.1f,%.1f)", WINDOW_TITLE, color_mode,ulayer,vlayer,wlayer, m,m*dt, (mstep>0)?"->":"<-", ezmode==MODE_SIMULATING?' ':'*', remove_backs? 'B':'_', clipping? 'C':'_', ezdepth? 'D':'_', show_filament? 'F':'_', show_surface? '_':'N', history? 'H':'_', save_images? 'I':'_', write_filament?'W':'_', theta, phi ); XStringListToTextProperty(&pname,1,&theWindowName); XSetWMName(theDisplay, theWindow, &theWindowName); glClearColor(bg_r,bg_g,bg_b,0.0); glClear(GL_COLOR_BUFFER_BIT | ezdepth); /* Clear the color buffer. * Clear the depth buffer if on, * (ezdepth=DEPTH_BUFFER_BIT) */ switch (ezmode) { case MODE_SIMULATING : case MODE_VIEWING : Marching_cubes(norm_res,show_filament,clipping,plot_length); if (showbody) { if (!glIsList(BODY_LIST)) Make_body_lists(); if (glIsList(BODY_LIST)) glCallList(BODY_LIST); } break; case MODE_ROTATING : glCallList(ROT_LIST); if (showbody) { if (!glIsList(BODY_ROT_LIST)) Make_body_lists(); if (glIsList(BODY_ROT_LIST)) glCallList(BODY_ROT_LIST); } break; } if (GRAPHICS) glXSwapBuffers(theDisplay,theWindow); /* If not in simulating mode, save images using 'I' key */ if (save_images && ezmode==MODE_SIMULATING) Save_image(); } /* ========================================================================= */ static void Make_rot_list (void) { /* Make new graphics lists for fast rotations. */ if (glIsList(ROT_LIST)) glDeleteLists(ROT_LIST,1); glNewList(ROT_LIST, GL_COMPILE); Marching_cubes(rot_res,show_filament,clipping, plot_length); glEndList(); if (verbose>1) printf("rotation list made\n"); } /* ========================================================================= */ static void Show_body (void) { showbody=1; if(verbose) printf("show body\n"); Draw(); } /* ========================================================================= */ static void Hide_body (void) { showbody=0; if(verbose) printf("hide body\n"); Draw(); } /* ========================================================================= */ static void Make_body_lists (void) { /* Make new graphics list to remember the current objects. */ /* Define vars to save current visualization pars */ #define _(n,t,i) t safe##n=n; #include "ezbody.h" #undef _(n,t,i) /* Impose the body visualization pars */ #define _(n,t,i) n=body##n; #include "ezbody.h" #undef _(n,t,i) LAYERS; if (glIsList(BODY_LIST)) glDeleteLists(BODY_LIST,1); glNewList(BODY_LIST, GL_COMPILE); Marching_cubes(norm_res,show_filament,clipping,plot_length); glEndList(); if (glIsList(BODY_ROT_LIST)) glDeleteLists(BODY_ROT_LIST,1); glNewList(BODY_ROT_LIST, GL_COMPILE); Marching_cubes(rot_res,show_filament,clipping,plot_length); glEndList(); /* Restore the saved visualization pars */ #define _(n,t,i) n=safe##n; #include "ezbody.h" #undef _(n,t,i) LAYERS; if (verbose>1) printf("body lists made\n"); } /* ========================================================================= */ static void Delete_body_list (void) { /* Delete remembered list if it exists */ if (0==glIsList(BODY_LIST)) return; glDeleteLists(BODY_LIST,1); Draw(); if (verbose) printf("body image is off\n"); } /* ========================================================================= */ static void Restart (void) { /* Return to MODE_SIMULATING. This has the effect of restarting the simulation via a return from Event_check(). */ if (ezmode == MODE_VIEWING) { ezmode = MODE_SIMULATING; } } /* ========================================================================= */ /* static */ void Pause (void) { /* The simulation has paused -- go to MODE_VIEWING. Call Make_rot_list() in * preparation for rotation. */ if (ezmode == MODE_SIMULATING) { ezmode = MODE_VIEWING; Make_rot_list(); } } /* ========================================================================= */ static void View_u_field (void) { ulayer=0; vlayer=1; wlayer=2; LAYERS; if (ezmode != MODE_SIMULATING) { Make_rot_list(); Draw(); } } /* ========================================================================= */ static void View_v_field (void) { ulayer=1; vlayer=2; wlayer=0; LAYERS; if (ezmode != MODE_SIMULATING) { Make_rot_list(); Draw(); } } /* ========================================================================= */ static void View_w_field (void) { ulayer=2; vlayer=0; wlayer=1; LAYERS; if (ezmode != MODE_SIMULATING) { Make_rot_list(); Draw(); } } /* ========================================================================= */ static void View_U_field (void) { ulayer=0; vlayer=2; wlayer=1; LAYERS; if (ezmode != MODE_SIMULATING) { Make_rot_list(); Draw(); } } /* ========================================================================= */ static void View_V_field (void) { ulayer=1; vlayer=0; wlayer=2; LAYERS; if (ezmode != MODE_SIMULATING) { Make_rot_list(); Draw(); } } /* ========================================================================= */ static void View_W_field (void) { ulayer=2; vlayer=1; wlayer=0; LAYERS; if (ezmode != MODE_SIMULATING) { Make_rot_list(); Draw(); } } /* ========================================================================= */ static void Toggle_no_field (void) { if (show_surface) show_surface = FALSE; else show_surface = TRUE; if (ezmode != MODE_SIMULATING) { Make_rot_list(); Draw(); } } /* ========================================================================= */ static void Toggle_backs_removal (void) { if (remove_backs) remove_backs = FALSE; else remove_backs = TRUE; if (ezmode != MODE_SIMULATING) { Make_rot_list(); Draw(); } } /* ========================================================================= */ static void Toggle_clipping_plane (void) { if (clipping) { glDisable(GL_CLIP_PLANE0); clipping = FALSE; } else { glEnable(GL_CLIP_PLANE0); clipping = TRUE; } if (ezmode != MODE_SIMULATING) { Make_rot_list(); Draw(); } } /* ========================================================================= */ static void Toggle_depth_buffer (void) { if (glIsEnabled(GL_DEPTH_TEST)) { glDisable(GL_DEPTH_TEST); ezdepth = 0; } else { glEnable(GL_DEPTH_TEST); ezdepth = GL_DEPTH_BUFFER_BIT; } if (ezmode != MODE_SIMULATING) { Draw(); } } /* ========================================================================= */ static void Toggle_filament_plotting (void) { if (show_filament) show_filament = FALSE; else show_filament = TRUE; if (ezmode != MODE_SIMULATING) { Make_rot_list(); Draw(); } } /* ========================================================================= */ static void Toggle_history_writing (void) { if (history) history = FALSE; else { if (verbose>2) printf("will write to history file now\n"); history = TRUE; } if (ezmode != MODE_SIMULATING) Draw(); } /* ========================================================================= */ static void Toggle_image_saving (void) { if (save_images) save_images = FALSE; else { save_images = TRUE; if (verbose>2) printf("will save every image now\n"); } if (ezmode != MODE_SIMULATING) Draw(); } /* ========================================================================= */ static int Toggle_filament_writing (void) { if (write_filament) write_filament = FALSE; else { write_filament = TRUE; if (verbose>2) printf("will write filament data now\n"); } if (ezmode != MODE_SIMULATING) Draw(); return write_filament; } /* ========================================================================= */ static void Mouse_up (void) { if (ezmode == MODE_ROTATING) { ezmode = MODE_VIEWING; Draw(); Make_rot_list(); } } /* ========================================================================= */ static void Mouse_down (int x, int y) { /* If the mode is MODE_VIEWING then on mouse down the state goes to * MODE_ROTATING. x and y are the location of the mouse when the button * was pressed and these are saved as lastx and lasty for use in * rotating(). */ if (ezmode == MODE_VIEWING) { lastx = x; /* Rotate() uses these values to calculate the distance */ lasty = y; /* moved by the mouse since the last Draw() call */ ezmode = MODE_ROTATING; Draw(); } } /* ========================================================================= */ static void Rotate (int x, int y, float new_theta, float new_phi) { /* Rotates volume either in response to changes in mouse location or to * angles new_theta, new_phi. More specifically: * * if MODE_ROTATING: change the viewing angles theta and phi by the * difference of (x,y) - (lastx,lasty), i.e. the difference between the * current mouse location and the previous location (set in call to * Rotate() or to Mouse_down()). * * else (not in MODE_ROTATING): set theta and phi to incoming values. */ /* static GLfloat theta, phi; */ if (ezmode == MODE_ROTATING) { theta += ROTATE_SCALE * (x-lastx); phi -= ROTATE_SCALE * (y-lasty); lastx = x; lasty = y; } else { theta = new_theta; phi = new_phi; } /* Require both theta and phi to be in the interval [-180,180). Extending * the range of theta to [-180,180) prevents 'flipping' at theta equal 90 * and -90. */ if (theta > 180.0) theta -= 360.0; else if (theta <= -180) theta += 360.0; if (phi > 180.0) phi -= 360.0; else if (phi <= -180) phi += 360.0; setView((GLfloat)theta, (GLfloat)phi); } /* ========================================================================= */ static void setView(GLfloat theta, GLfloat phi) { /* OpenGL calls to set rendering (transformation matrix). Note that the * last call (which is the first action applied to a triangle being * rendered) is to center the 3D volume about the origin. There are then * rotations in theta and phi. The second translation pulls the view away * from the cube. I have found that DISTANCE=5 is about right -- the * distance (as measured in unit lengths as they appear on the screen) the * volume is from the eye under normal view conditions is about 5. In * principle this varies with the size of the window on the screen. For * an orthographic projection this is irrelevant so long as the cube is in * the viewed volume. */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.0,0.0,-DISTANCE); glRotatef(-phi,1.0,0.0,0.0); glRotatef(theta,0.0,0.0,1.0); glTranslatef(-plot_length[0]/2,-plot_length[1]/2,-plot_length[2]/2); } /* ========================================================================= */ static void myReshape(int w, int h) { GLfloat half_width; /* half_width is the half width of the volume viewed in GL. In general if * half_width is large, then rendered simulation volume will appear small, * and vice versa. PLOT_SIZE in ezgraph3d.h allows adjustment of this * without changing any of the code below. * * For orthographic projection setting half_width this is straightforward: * The simulation volume has maximum side of 1 in graphics coordinates * (set Draw_ini()). If half_width is set to sqrt(3)/2 = 0.866..., then a * unit cube, and hence the simulation volume, will always lie entirely * within the viewport so I choose this for the default. For ortho * projection, the near are far values are set to large values and are * irrelevant so long as the volume lies between near and far. * * For perspective projection the situation is more complicated because * the half_width applies at the value of near (see the OpenGL manual). * Hence the value of near and half_width must be set together so that a * unit cube fills the viewport but does not cut the near plane. The * values below accomplish this. The far value if irrelevant and is set to * a large value. */ glMatrixMode (GL_PROJECTION); glLoadIdentity (); #if PERSPECTIVE /* Perspective projection */ half_width = 0.7/PLOT_SIZE; glFrustum(-half_width, half_width, -half_width, half_width, DISTANCE-1., 20.); #else /* Orthographic projection */ half_width = 0.866/PLOT_SIZE; glOrtho (-half_width, half_width, -half_width, half_width, -20., 20.); #endif glMatrixMode (GL_MODELVIEW); glViewport (0, 0, w, h); } /* ========================================================================= */ void Draw_ini (void) { /* Initialize everything necessary for 3D plotting. */ int show_filament_save=show_filament; show_filament = FALSE; /* The lengths of the simulation volume in graphics coordinates are set. I * choose to have the largest plot_length=1. Thus the volume lies inside * the unit cube in graphics coordinates. */ { int nmax = max(max(NX,NY),NZ); plot_length[0] = (NX-1.)/(nmax-1.); plot_length[1] = (NY-1.)/(nmax-1.); plot_length[2] = (NZ-1.)/(nmax-1.); } /* Initialization for marching cubes. */ Marching_ini(); /* At this point everything has been initialized for finding filaments * without graphics. Can return after setting a few things. Setting field * to NO_FIELD and ezmode to MODE_SIMULATING will give only minimum OpenGL * calls. clipping must be set but is not important. */ if( !GRAPHICS ) { ulayer=vlayer=wlayer=-1; LAYERS; ezmode = MODE_SIMULATING; clipping = FALSE; return; } /* Create X window and prepares for use by OpenGL */ winx=WINX; winy=WINY; InitX(&winx,&winy,WINSIZE,WINSIZE); /* The default should be smooth anyway */ glShadeModel(GL_SMOOTH); /* The depth buffer is disabled by default in GL. Here we enable * it. To start with depth buffer disabled, don't call glEnable and * set ezdepth = 0. */ glEnable(GL_DEPTH_TEST); ezdepth = GL_DEPTH_BUFFER_BIT; /* Enable light sources and properties */ glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, mat_emission); glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffusive); glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess); glLightfv(GL_LIGHT0, GL_POSITION, light_position0); /* glLightfv(GL_LIGHT1, GL_AMBIENT, light_ambient1); */ /* glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse1); */ /* glLightfv(GL_LIGHT1, GL_SPECULAR, light_specular1); */ glLightfv(GL_LIGHT1, GL_POSITION, light_position1); glLightfv(GL_LIGHT2, GL_POSITION, light_position2); glLightfv(GL_LIGHT3, GL_POSITION, light_position3); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_LIGHT1); /* glEnable(GL_LIGHT2); */ /* glEnable(GL_LIGHT3); */ glEnable(GL_COLOR_MATERIAL); glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE); /* glColorMaterial(GL_FRONT_AND_BACK,GL_DIFFUSE); */ glEnable(GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); /* glBlendFunc (GL_SRC_ALPHA, GL_ONE); */ /* Set the background color. */ glClearColor(bg_r,bg_g,bg_b,0.0); /* Set up the clipping plane. Note that clipping is not on enabled * initially, but could be by inserting a call to Toggle_clipping_plane(). * Here the clipping plane is set to pass half way through the volume. * plane[3] controls this. I start with clipping off (clipping=FALSE) */ { GLdouble plane[4]; plane[0] = 0.0; plane[1] = 0.0; plane[2] = -1.0; plane[3] = plot_length[2]/2; setView(0., 0.); glClipPlane(GL_CLIP_PLANE0, plane); clipping = FALSE; } /* Initialize the view direction */ /* Rotate(0, 0, (float)INITIAL_THETA, (float)INITIAL_PHI); */ Rotate(0, 0, theta, phi); /* Set starting mode */ if(START_PAUSED) { ezmode = MODE_VIEWING; Make_rot_list(); } else { ezmode = MODE_SIMULATING; } show_filament=show_filament_save; } /* ========================================================================= */ /* Assign parameters dialogue in the parent command-line window */ enum par_t { par_int, par_Real }; enum par_count { #define _(n,t,i) par_##n, #include "ezpar.h" #undef _(n,t,i) #define _(n,t,i) par_body##n, #include "ezbody.h" #undef _(n,t,i) numpar }; typedef struct {char *name; void *addr;enum par_t type;} pardef; pardef pars[] = { #define _(n,t,i) {#n,(void *)&n,par_##t}, #include "ezpar.h" #undef _(n,t,i) #define _(n,t,i) {"body"#n,(void *)&body##n,par_##t}, #include "ezbody.h" #undef _(n,t,i) {"",NULL,par_int} }; #ifdef READLINE /* The generator of completions for readline */ char *param_generator (const char *, int); char **param_completion (const char *, int, int); static int readline_on=0; void initialize_readline (void) { rl_readline_name = "bbxview"; rl_attempted_completion_function = param_completion; readline_on=1; } char **param_completion(const char *text,int start,int end) { char **matches = (char **) NULL; if (start == 0) matches = rl_completion_matches(text,param_generator); return (matches); } char *param_generator (const char *text, int state) { static int list_index, len; char *name; if (state==0) { list_index = -1; len = strlen(text); } while ((++list_index)"),BUFLEN); add_history(buf); } else { printf("parameter="); fgets(buf,BUFLEN,stdin); } #else printf("parameter="); fgets(buf,BUFLEN,stdin); #endif name=strtok_r(buf,SEP,&lasts); if (!name) break; np=-1; for(ip=0;ip=horspace) winx-=horspace; } winy += dy; if (vertspace) { while (winy<0) winy+=vertspace; while (winy>=vertspace) winy-=vertspace; } XMoveWindow(theDisplay,theWindow,winx,winy); } /* ========================================================================= */ int Event_check (void) { static XEvent theEvent; static KeySym theKeySym; static int theKeyBufferMaxLen = 64; static char theKeyBuffer[65]; static XComposeStatus theComposeStatus; /* NB these masks discovered experimentally; */ /* need to find out proper XLib coding for them if any */ #define CTL (0x0004) #define SHIFT (0x0001) static unsigned int state; int write_filament_save; if( !GRAPHICS ) return(0); /* Save write_filament, and then turn filament writing is turned off. This * is done so that no filaments are written as a result of graphics calls * in the event loop. At the end, the write_filament is restored. */ write_filament_save = write_filament; write_filament = FALSE; /* X Event Loop * * If there is something in the queue then each event is processed in * turn. When the queue is empty, and the mode (which may have been changed * by the events just processed) is MODE_SIMULATING then control is * returned (presumably to the main loop in main()). However, when the * queue is empty and the mode is either MODE_ROTATING or MODE_VIEWING then * XNextEvent is called which blocks until the next X event is received * (eg. the space bar being pressed which sets the mode back to * MODE_SIMULATING and so control returns to main()). */ while(XPending(theDisplay) || (ezmode != MODE_SIMULATING)) { XNextEvent(theDisplay, &theEvent); switch(theEvent.type) { /* switch according to X event */ case KeyPress: /* A KeyPress event has occurred. */ XLookupString((XKeyEvent *) &theEvent, theKeyBuffer, theKeyBufferMaxLen, &theKeySym, &theComposeStatus); state= (theEvent.xkey.state) & ( CTL | SHIFT ); switch(theKeySym) { /* switch according to the pressed key */ case XK_Escape: exit(0); /* hard exit, nothing saved */ case XK_Q: case XK_q: return(1); case XK_P: case XK_p: Pause(); Draw(); break; case XK_space: Restart(); break; /*******************************/ case XK_B: case XK_b: Toggle_backs_removal(); break; case XK_C: case XK_c: Toggle_clipping_plane(); break; case XK_D: case XK_d: Toggle_depth_buffer(); break; case XK_F: case XK_f: Toggle_filament_plotting(); break; case XK_H: case XK_h: Toggle_history_writing(); break; case XK_I: case XK_i: Toggle_image_saving(); break; case XK_L: case XK_l: write_filament_save=Toggle_filament_writing(); break; case XK_N: case XK_n: Toggle_no_field(); break; case XK_u: View_u_field(); break; case XK_v: View_v_field(); break; case XK_w: View_w_field(); break; case XK_U: View_U_field(); break; case XK_V: View_V_field(); break; case XK_W: View_W_field(); break; case XK_R: case XK_r: Rotate(0,0,INITIAL_THETA,INITIAL_PHI); Draw(); break; case XK_S: case XK_s: Save_all(); break; case XK_Z: case XK_z: Rotate(0,0,0.,0.); Draw(); break; case XK_0: color_mode=0; Make_rot_list(); Draw(); break; case XK_1: color_mode=1; Make_rot_list(); Draw(); break; case XK_2: color_mode=2; Make_rot_list(); Draw(); break; case XK_3: color_mode=3; Make_rot_list(); Draw(); break; case XK_4: color_mode=4; Make_rot_list(); Draw(); break; case XK_5: color_mode=5; Make_rot_list(); Draw(); break; case XK_6: color_mode=6; Make_rot_list(); Draw(); break; case XK_7: color_mode=7; Make_rot_list(); Draw(); break; case XK_8: color_mode=8; Make_rot_list(); Draw(); break; case XK_9: color_mode=9; Make_rot_list(); Draw(); break; case XK_plus: Show_body(); break; case XK_BackSpace: Hide_body(); break; case XK_Left: switch(state) { case (CTL+SHIFT): MoveWindow(-10,0); break; case (CTL): MoveWindow(-1,0); break; case (SHIFT): Rotate(0,0,theta-10*ROTATE_SCALE,phi); break; default: Rotate(0,0,theta-ROTATE_SCALE,phi); break; } Draw(); break; case XK_Right: switch(state) { case (CTL+SHIFT): MoveWindow(+10,0); break; case (CTL): MoveWindow(+1,0); break; case (SHIFT): Rotate(0,0,theta+10*ROTATE_SCALE,phi); break; default: Rotate(0,0,theta+ROTATE_SCALE,phi); break; } Draw(); break; case XK_Up: switch(state) { case (CTL+SHIFT): MoveWindow(0,-10); break; case (CTL): MoveWindow(0,-1); break; case (SHIFT): Rotate(0,0,theta,phi+10*ROTATE_SCALE); break; default: Rotate(0,0,theta,phi+ROTATE_SCALE); break; } Draw(); break; case XK_Down: switch(state) { case (CTL+SHIFT): MoveWindow(0,+10); break; case (CTL): MoveWindow(0,+1); break; case (SHIFT): Rotate(0,0,theta,phi-10*ROTATE_SCALE); break; default: Rotate(0,0,theta,phi-ROTATE_SCALE); break; } Draw(); break; case XK_Page_Down: m+=mstep; Read_fmt(fmt,template,m); Draw(); break; case XK_Page_Up: m-=mstep; Read_fmt(fmt,template,m); Draw(); break; case XK_minus: mstep*=-1; Draw(); break; case XK_equal: Assign_par(); break; /* default: XBell(theDisplay,0); */ } /* switch(theKeySym) */ break; case EnterNotify: /* This case is necessary for window managers which do not set keyboard * focus to theWindow when the pointer enters theWindow. */ XSetInputFocus(theDisplay, theWindow, RevertToPointerRoot, CurrentTime); break; case Expose: /* Window mapped for the first time and if you uncover some part of the * window. If you start paused and you see nothing in the window then * its possible that the problem is that the first Expose event is not * being caught for some reason. */ Draw(); break; case ConfigureNotify: /* Window size is changed by the user (or the window is initially * opened). Note that InitX contains code that constrains the window * to be square. */ myReshape(theEvent.xconfigure.width, theEvent.xconfigure.height); break; case ButtonPress: if (theEvent.xbutton.button == 1) { Mouse_down(theEvent.xbutton.x, theEvent.xbutton.y); } break; case ButtonRelease: if (theEvent.xbutton.button == 1) { Mouse_up(); } break; case MotionNotify: if (theEvent.xmotion.state & Button1Mask) { /* Remove all the MotionNotify events currently in the queue leaving * the last one in theEvent. This ensures the image 'sticks' to the * mouse. */ while (XCheckWindowEvent(theDisplay,theWindow, PointerMotionMask, &theEvent)); Rotate(theEvent.xmotion.x, theEvent.xmotion.y, 0., 0.); Draw(); } break; } /* switch (theEvent.type) */ } /* while (XPending(theDisplay) || (ezmode != MODE_SIMULATING)) */ /* restore write_filament */ write_filament = write_filament_save; return(0); } /* ========================================================================= */ static void InitX (int *winx, int *winy, int width, int height) { /* Initializes X and opens a window. */ static XVisualInfo *theVisualInfo; static GLXContext theGLXContext; static Colormap theColormap; static int theScreen; static int theDepth; static char *theDisplayName = NULL; static XEvent event; static Atom del_atom; static XSizeHints theSizeHints; static XSetWindowAttributes theSWA; static char *name = WINDOW_TITLE; static int num1,num2; static int list[] = {GLX_RGBA, GLX_DOUBLEBUFFER, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, GLX_ALPHA_SIZE, 1, GLX_DEPTH_SIZE, 1, None } ; /* DEPTH and DOUBLE are the important ones. Perhaps need to add error * checking when DOUBLE_BUFFER and DEPTH not available. In the aux library * the first entry in list was GLX_LEVEL, 0, */ /* Open the display */ if ((theDisplay = XOpenDisplay(NULL)) == NULL) { fprintf(stderr, "ERROR: Could not open a connection to X on display %s\n", XDisplayName(theDisplayName)); exit(1); } if (!glXQueryExtension(theDisplay, &num1, &num2)) { fprintf(stderr, "ERROR: No glx extension on display %s\n", XDisplayName(theDisplayName)); exit(1); } theScreen = DefaultScreen(theDisplay); theDepth = DefaultDepth (theDisplay, theScreen); theDWidth = DisplayWidth (theDisplay, theScreen); theDHeight = DisplayHeight(theDisplay, theScreen); /* Remember the window size for Save_image */ WindowWidth=width; WindowHeight=height; /* How much space for moving the window without goint beyond screen */ horspace=theDWidth-WindowWidth; vertspace=theDHeight-WindowHeight; if (!(theVisualInfo = glXChooseVisual(theDisplay, theScreen, list))) { fprintf(stderr, "ERROR: Couldn't find visual"); exit(-1); } if (!(theGLXContext = glXCreateContext(theDisplay, theVisualInfo, None, GL_TRUE))) { /* Last parameter indicates that, if possible, then render directly to * graphics hardware and bypass the X server. This should be faster. */ fprintf(stderr, "ERROR: Can not create a context!\n"); exit(-1); } theColormap = XCreateColormap(theDisplay, RootWindow(theDisplay, theVisualInfo->screen), theVisualInfo->visual, AllocNone); /* AllocAll would generate a BadMatch. */ if (!(theColormap)) { fprintf(stderr, "ERROR: couldn't create Colormap\n"); exit(-1); } theSWA.colormap = theColormap; theSWA.border_pixel = 0; theSWA.event_mask = (EnterWindowMask | KeyPressMask | StructureNotifyMask | ButtonPressMask | ButtonReleaseMask | ExposureMask | PointerMotionMask); if (horspace) { while (*winx<0) *winx+=horspace; while (*winx>=horspace) *winx-=horspace; } if (vertspace) { while (*winy<0) *winy+=vertspace; while (*winy>=vertspace) *winy-=vertspace; } theWindow = XCreateWindow(theDisplay, RootWindow(theDisplay, theVisualInfo->screen), *winx, *winy, width, height, 0, theVisualInfo->depth, InputOutput, theVisualInfo->visual, CWBorderPixel|CWColormap|CWEventMask, &theSWA); if (!(theWindow)) { fprintf(stderr, "ERROR: couldn't create X window\n"); exit(-1); } /* Remember the window size for Save_image */ WindowWidth=width; WindowHeight=height; /* Set standard window properties. theWindowName and theIconName are set * to Name, which unless you change it (in ezgraph3d.h), it will be * EZ-Scroll. */ XStringListToTextProperty(&name,1,&theWindowName); XStringListToTextProperty(&name,1,&theIconName); theSizeHints.base_width = width; theSizeHints.base_height = height; theSizeHints.min_aspect.x = width; /* Maintain x:y ratio */ theSizeHints.max_aspect.x = width; theSizeHints.min_aspect.y = height; theSizeHints.max_aspect.y = height; theSizeHints.flags = PSize|PAspect; if(!(WM_CTRLS_POS)) theSizeHints.flags |= USPosition; /* Setting USPosition here seems to make the WM honor the x and y * specified by XCreateWindow above. Not setting this should give control * of position to the WM. Note that the root window of an application is * special in that the WM has special privileges over its properties so * this may not work on all platforms. */ XSetWMProperties(theDisplay, theWindow, &theWindowName, &theIconName, NULL, 0, &theSizeHints, NULL, NULL); /* Express interest in WM killing this application */ if ((del_atom = XInternAtom(theDisplay, "WM_DELETE_WINDOW", TRUE)) != None) { XSetWMProtocols(theDisplay, theWindow, &del_atom, 1); } XMapWindow(theDisplay, theWindow); XIfEvent(theDisplay, &event, WaitForNotify, (char *)theWindow); glXMakeCurrent(theDisplay, theWindow, theGLXContext); /* Print useful information. I suggest printing this at least once */ if(verbose>1) { printf("%s version %d of the X Window System, X%d R%d\n", ServerVendor (theDisplay), VendorRelease (theDisplay), ProtocolVersion (theDisplay), ProtocolRevision(theDisplay)); if(theDepth==1) { printf("Color plane depth...........%d (monochrome)\n", theDepth); } else { printf("Color plane depth...........%d \n", theDepth); } printf("Display Width...............%d \n", theDWidth); printf("Display Height..............%d \n", theDHeight); printf("The display %s\n", XDisplayName(theDisplayName)); } } /* ========================================================================= */ static Bool WaitForNotify(Display *d, XEvent *e, char *arg) { /* As seen in the Porting Guide. */ return (e->type == MapNotify) && (e->xmap.window == (Window)arg); } /* ========================================================================= */ void QuitX (void) { if(GRAPHICS) XCloseDisplay(theDisplay); return; } /* ========================================================================= */ /* Save to a file a png image of what is currently on the screen using glReadPixels + netpbm converter. */ static void Save_image (void) { static int num = 0; static int m_prev = 0; static char cmd[4096]; int bufsize=WindowWidth*WindowHeight*3; unsigned char *buf; char *fifoname=tempnam(NULL,NULL); pid_t child_pid; FILE *fifo; int status; if (!theWindow) return; if (-1==mkfifo(fifoname,0600)) {perror("Save_image could not make a fifo"); return;} if (m != m_prev) num=0; switch (child_pid=fork()) { case -1: perror("Save_image could not fork"); return; case 0: sprintf(cmd,"cat %s | pnmflip -tb | pnmtopng > %s%06d-%02d.png",fifoname,"snap",m,num); system(cmd); _exit(0); default: if (NULL==(fifo=fopen(fifoname,"w"))) {perror("Save_image could not write to fifo"); return;} fprintf(fifo,"P6\n%d %d\n%d\n",WindowWidth,WindowHeight,255); XRaiseWindow(theDisplay,theWindow); glFlush(); /* printf("glFlush done\n"); */ buf=malloc(bufsize); glReadPixels(0,0,WindowWidth,WindowHeight,GL_RGB,GL_UNSIGNED_BYTE,buf); fwrite(buf,1,bufsize,fifo); if (0!=fclose(fifo)) perror("Save_image could not close fifo"); waitpid(child_pid,&status,0); if (0==(WIFEXITED(status))) fprintf(stderr,"Save_image child status %08x\n",status); free(buf); unlink(fifoname); m_prev=m; num++; } } /* ========================================================================= */