1,1752c1,2934
< // gkf 23dec04 for the mac
< /* gkf Seattle, 8aug00 */
< /* This is the successful version from 6-9-99, but on 6-10-99 was
<    brought to beckman and is now being slightly improved, a la old_true/ */
< 
< 
< /* 6-3-99 
<    Should theta & sign_theta be floats, or merely converted to floats
<    when and if necessary? */
< 
< /** Doesn't work, but compiles: 5-26-99 **/
< /** Fundamental change - need to think about when including cavetrack:
<     Made theta only global:
<       Old: in chaptrack: static int theta; globally: int theta
<       New: globally: int theta 
<     This is needed for drawall & draw_intersections to use theta.
< **/
< 
< /** This is being canibalized/changed into the final May 1999 version
<     with visual 4D rotation:
<     These changes will be highlighted with an indicator: 'MAY 1999'
<     Still not done in June, so new edits: 'JUNE 1999'
< **/
< 
< /** Note: this grafixlab one is not caved - a caved version should exist
<     in Beckman **/
< 
< 
< 
< /* collision detection added 5/12/98 predawn 1am */
< 
< /* I think it works!, mostly 2/22/98 */
< /* still have to add collision detection */
< /* And either stairways or sound */
< /* To cave it, I must figure out how I want to use the wand to ask 
<    for a 4D rotation */
< 
< /* only changed for chaptrack - cavetrack probably won't work at all */
< /* lagmvg was needed because the round multiples of 8 and 1 that I think
<    of as typical aff & taff values are not that in the heart of chaptrack,
<    only outside of the mucking around in chaptrack.  So to use roundaff, 
<    roundtaff, showaff, showtaff, taff2aff & aff2taff right after a movement
<    had been completed, I needed lagmvg. */
< /* Changed location_w to location[3] */
< /* location[4] represents the absolute location */
< /* drawmaze() thinks LL is the location of the hidden variable */
< /* The old program thinks that location_w is really
<    the fourth location - not so good */
< /* Actually, should replace location with lctn */
< /* setmaze has a new maze, one with the concept of true 4D not stairs */
< 
< /* True4DMaze.c by Michael Pelsmajer 1998 */
< /* maze4Dogl gkf 4dec97 */
< /* This may just be the final elevator-style version */
< /* note: location incompatible with TURNMODE - to begin correct location 
<    again, need to zap */
< /* based heavily on illiSkel (C) 1996 G.Francis, C.Hartman */
< /* based even more so on... */
< /****************************************************************/
< /****            noosh.c = ishell97 to be                    ****/
< /****           descended from ishell95                      ****/
< /****            14 February 1995                            ****/
< /****            29 August   1995                            ****/
< /****             1 November 1995                            ****/ 
< /****            15 november 1996   ishell96.c               ****/
< /****            27 november 1996   ishell96.c               ****/
< /****            23 march    1997   newsh.c                  ****/
< /****    (C) 1994 Board of Trustees University of Illinois   ****/ 
< /****    A Model Real-Time Interactive CAVE Application      ****/
< /****    George Francis, Glenn Chappell, Chris Hartman       ****/
< /****    e-mail  gfrancis@math.uiuc.edu                      ****/
< /****    Generic RTICA by GGC 7/25/94 revised gkf 12.30.94   ****/ 
< /****    Revised by GGC and GKF august 95                    ****/
< /****    "Opengl ed" by AVB, nov 14-15, 1996                 ****/
< /****    Reformatted by AVB, nov 16, 1996                    ****/
< /****    adaptation to CAVE 2.6Beta started 6feb97 gkf       ****/
< /****    New version to fix many bugs and clean up 17mar97 cmh  */
< /****    Rationalization begun 26mar97 gkf                   ****/    
< /****************************************************************/
< /* CAVE trials 1apr97 bugs:
<    1. At 24x24x2 triangles = 1152 we get 24fps with all 8 processors
<       At 30x30x2 triangles = 1800 this drops to 16 fps on 8 processors
<       With fewer processors (mpunlock) the dropoff is disastrous, 
<    2. Current version (= 31mar97) does ambient only on process 2 (right
<       wall and floor) .... no paint at all!
<  
< /* 
< /* Some emerging conventions on the path of rationalization 
< 
<    draw<what> == this function uses GL calls for drawing something
<    cons       == pertaining to the console mode only
<    cave       == pertaining to the CAVE mode(s)
<                  all such functions are protected by #ifdef CAVEs for
<                  CAVE less compilation
<    audio      == pertaining to the vss audio stuff required three
<                  matched files: vssClient.h libsnd.a and vss 
<                  should also be protected by #ifdef SOUNDs
<    autotymer  == is (now) called only by the master process in the CAVE
<                  with the result that non-shared globals are not updated
<                  on the other walls. So we share them. 
<    <factor>  == a factor is a function that has been separated out for 
<                  (1) convenience (i.e. is not a factor of any other function) 
<                  (2) necessity (i.e. is a factor of another function )
<    <depth>   == from main() in the factor graph. 
< 
<    Of necessity, C requires that the factor graph be treelike with the leaves
<    at the top of the program code. For a C++ conversion, this tree will be 
<    turned upside down.  
< */
< 
< /* Factor graph for this program. Please indicate structural modifications 
<    The asterisk indicates a factor that has more than one function calling it. 
<    The Mm indicats something that is different from noosh. 
<    Mm  main  
<    Mm    arguments ....................command line switches and parameters 
<            getopt  
<          CAVEconfigure
<          getmem .......................allocate shared memory  
<            Malloc(share_var)
<          dataprep .....................done only once   
<            audioprep
<              AUDinit
<    Mm     *deFault 
<    Mm      initvv
<    Mm      makemaze/setmaze 
<          ifCAVE
<            FrameFunc(cavetrack)........done once every frame 
<                        *deFault........on the reset button combination
<                        *autotymer .....automatic clockwork animation
<                        *audiofunc .....sends messages to soundserver
<            InitApplic(drawcaveinit)....done each frame 
<            Display(drawcave)...........done each frame
<                     *drawstars
<                      graffiti..........on the front CAVE wall
<                         char2wall
<                     *drawall
<                        drawtor.........compressed version of tr1.c 
<                          dovert........do everything for one vertex
<                        drawcube........transfer from skel.c as exercise 
<            while(notESC) cavekeybo.....done asynchronously from display
<                            *deFault
<            audioclean .................shut down audio server
<            Exit........................shut down CAVE properly 
<          ifCONS
<            gluttery....................we use the GLUT library
<               drawcons.................analogue to drawcave
<                 *drawstars
<    Mm           *drawall 
<    Mm               drawmaze
<    Mm               drawstairs (commented out)
<    Mm           messages...............analogue to grafitti
<                   char2wall
<                   speedometer..........unix based speedometer
<    Mm         keyboard.................GLUT distinguishes between ascii keys 
<    Mm           *deFault
<                 *audioclean
<               special_keybo............and other kinds of keys
<               mousepushed..............mouse buttons were pressed
<               mousemoved...............mouse was moved       
<               reshaped.................what to do if window is reshaped
<    Mm         idle.....................done when nothing else is being done 
<    Mm            chaptrack.............updates the two affine matrices 
<    Mm           *autotymer.............preset directions for chaptrack
<                 *audiofunc.............updates the sound servers  
<          endIF 
< */
< 
< 
< //#pragma warning (disable:4305)  /* double-to-float  */
< //#pragma warning (disable:4101)	/* unreferenced local variable */
< //#pragma warning (disable:4056)	/* overflow in fp constant arithmetic */
< 
< #include <stdlib.h>
< //#include <sys/types.h>
< #include <math.h>
< 
< #include <sys/time.h>
< 
< #include <stdio.h>
< #include <malloc.h>
< 
< #include <glut.h>
< /*
< #ifdef SOUND
< #include <device.h>
< #include <string.h>
< #include "vssClient.h"
< #endif
< */
< #ifdef CAVE
< #include <cave_ogl.h>
< #include <cave.macros.h>
< #endif 
< 
< #include "maze4Dogl.h"
< 
< /* Examples of useful geometric macros, use inliners in C++ */
< #define  MAX(x,y)         (((x)<(y))?(y):(x))
< #define  MIN(x,y)         (((x)<(y))?(x):(y))
< #define  FOR(a,b,c)       for((a)=(b);(a)<(c);(a)++)
< #define  DOT(p,q)         ((p)[0]*(q)[0]+(p)[1]*(q)[1]+(p)[2]*(q)[2])
< #define  M_PI		   3.1415926
< #define  DG            M_PI/180
< #define  NRM(p)           sqrt(DOT((p),(p)))
< /*
< #define  S(u)          fsin(u*DG)
< #define  C(u)          fcos(u*DG)
< */
< #define  CLAMP(x,u,v) (x<u? u : (x>v ? v: x))
< 
< /* global variables ... add new ones on top of the stack */
< char which[20]; /* audio needs this */
< int delta; /* new variables for autotymer to change torus */
< float gap, gap0=1.; /*very bad kludge ... for letting arguments reset default gap value */
< int audiohandle; /* for telling vss the name of the .aud file */
< float lux[3]={1.,2.,3.};             /*light source direction vector        */
< float lu[3];     /* this needs to be shared current light direction vector*/
< float mysiz, speed, torq, focal, Far; /*console navigation variables         */
< int win = 0;                     /* 2 full screen, use 0 for demand sized */
< int knopf = 0;
< unsigned int BUT,XX,YY,SHIF;     /* used in chaptrack gluttery           */ 
< int xt,yt;                       /* once was xt,yt,xm,ym for viewportery */
< int caveyes=0;                      /* rename? ----------> caveflag */
< int mode,morph,msg,binoc;            /* pretty global */
< int th0, th1, dth, ta0, ta1, dta;    /* torus parameters */
< /* some of center.c's variables follow: */
< int rseed,location[4];/* location is absolute location */
< int in_maze=1;int w_shift=0; int rand_maze=0;
< float XYZchance,XZWchance,YZWchance,XYWchance,Delta;
< char phrase[256];
< long mx,my,xorig,yorig,xcen,ycen;
< int cube,thick;int moving;int frozen,demo; 
< int dir4[4];/* taff[3,8,13,18], the hidden direction */
< /* MAY 1999: New variables: */
< /* JUNE 1999: New variables: */
< int rotating_in_4D; /* Boolean: chooses draw_maze on 0, 
< 		       draw_intersections on 1 */
< float theta,sign_theta; /* for the 4D rotation */
< int ActualAxes; /* 1: xzw->yzw, -1: yzw->xzw
< 		   2: xyw->yzw, -2: yzw->xyw
< 		   3: xyz->yzw, -3: yzw->xyz
< 		   4: xyw->xzw, -4: xzw->xyw
< 		   5: xyz->xzw, -5: xzw->xyz
< 		   6: xyz->xyw, -6: xyw->xyz */
< /* theta_jump is change in theta for the 4D rotation only */
< /* theta_jump must divide 90  */
< #define theta_jump 3
< #define max_theta_delay 1
< 
< #define FLYMODE  (0)
< #define TURNMODE (1) 
< /* From center.c; IF, TOGGLE, PRESS, FLYMODE & TURNMODE renamed SkFOO */
< /* Let's use noosh's FLY/TURNMODE convention */
< #define  ABS(x)           (((x)<0)?-(x):(x))
< #define  SkIF(K)          if(getbutton(K))
< #define  SOAK(K)        while(getbutton(K))
< #define  SkTOGGLE(K,f)    SkIF(K){f = !f;SOAK(K);}
< #define  IFCLICK(i,K,a) {static flg=i; SkTOGGLE(K,flg); if(flg){a};}
< #define  ZDELTA         0xfffff
< #define  ROUND(x)         (floor(x+.5))
< #define  IFSHIFT        if(getbutton(LEFTSHIFTKEY)||getbutton(RIGHTSHIFTKEY))
< #define  SkPRESS(K,A,b)   SkIF(K){IFSHIFT{A;}else{b;}}
< #define  PRES_S(K,A,b)  SkIF(K){IFSHIFT{A;}else{b;};SOAK(K);}
< #define  CYCLER(K,f,m)  PRES_S((K), (f)=(((f)+(m)-1)%(m)), (f)=(++(f)%(m)) )
< #define  LF         (vert-=0.035)  /*as in line-feed */
< #define  LAB_L(s,u) sprintf(phrase,s,u); cmov2(horz,vert); charstr(phrase);
< 
< 
< /*Shared memory variables as per Stuart Levy 1994 */  
< struct share_var{ 
<    float  s_siz;         /*final scaling factor before projection      */  
<    int s_wnd;            /*wand or maus flag                           */
<    int s_gnd;            /*background color                            */
<    float s_lu[3];         /* current light direction */ 
<    int s_mauspaw ;      /*binary for mouse button state                     */
<    int s_th0 ;           /* since the autotymer changes these          */
<    int s_th1 ;           /* they need to be shared                      */
<    int s_ta0 ;           /*                                             */
<    int s_ta1 ;           /*                                             */
<    float s_gap ;           /*                                             */
<    GLfloat s_aff[16],s_starmat[16]; /*affine matrices for object, stars*/
<    GLfloat s_taff[25],s_id[16]; /* 5D affine matrix */
< } *s_var; 
< 
< #define siz     (s_var->s_siz)
< #define wnd     (s_var->s_wnd)
< #define gnd     (s_var->s_gnd)  /* use only if printframe function is present */
< #define lu      (s_var->s_lu )
< #define aff     (s_var->s_aff)  
< #define starmat (s_var->s_starmat)
< #define id     (s_var->s_id)  
< #define taff     (s_var->s_taff)  
< #define mauspaw (s_var->s_mauspaw)
< #define th0 (s_var->s_th0)
< #define th1 (s_var->s_th1)
< #define ta0 (s_var->s_ta0)
< #define ta1 (s_var->s_ta1)
< #define gap (s_var->s_gap)
< 
< /* these are used only in console mode      */
< int binoc;    /* flag                       */
< float nose;   /* to eye distance in console */
< 
< /* For diagnostic purposes */
< void showaff(){int ii,jj;printf("\n"); for(ii=0; ii<4; ii++){int i=ii*4;
<  printf(" %f %f %f %f \n",aff[i],aff[i+1],aff[i+2],aff[i+3]);}
<  printf("\t\tlocation");
<  for(ii=0;ii<4;ii++)printf(" %d",location[ii]);
<  printf("\tdir4");
<  for(ii=0;ii<4;ii++)printf(" %d",dir4[ii]);
<  printf(" \n");
< }
< void showtaff(){int ii,jj;printf("\n"); for(ii=0; ii<5; ii++){int i=ii*5;
<  printf(" %f %f %f %f %f \n",taff[i],taff[i+1],taff[i+2],taff[i+3],taff[i+4]);}
< }
< /* JUNE 1999 */
< void showAA(){printf("\nActualAxes=%i",ActualAxes);}
< 
< void roundaff(){int ii;
<  for(ii=0;ii<16;ii++)aff[ii]=ROUND(aff[ii]);
< } 
< void roundtaff(){int ii;
<  for(ii=0;ii<25;ii++)taff[ii]=ROUND(taff[ii]);
< } 
< 
< /* Updates taff from aff, dir4[3], location[3] */
< #define DSUM (dir4[0]+dir4[1]+dir4[2]+dir4[3])
< void aff2taff(){
<   int ii;
<   for(ii=0;ii<4;ii++)taff[3+(5*ii)]=(GLfloat)dir4[ii];/* hidden col */
<   taff[20]=aff[12];taff[21]=aff[13];taff[22]=aff[14]; /* location */  
<   if(dir4[3]!=0){
<     for(ii=0;ii<3;ii++)taff[15+ii]=0;
<     taff[0]=aff[0];taff[1]=aff[1];taff[2]=aff[2];      /* direction */
<     taff[5]=aff[4];taff[6]=aff[5];taff[7]=aff[6];
<     taff[10]=aff[8];taff[11]=aff[9];taff[12]=aff[10];
<     taff[23]=dir4[3]*4.0*(ROWS-(2*location[3])-1);}    /* location */
<   if(dir4[2]!=0){
<     for(ii=0;ii<3;ii++)taff[10+ii]=0;
<     taff[0]=aff[0];taff[1]=aff[1];taff[2]=aff[2];      /* direction */
<     taff[5]=aff[4];taff[6]=aff[5];taff[7]=aff[6];
<     taff[15]=aff[8];taff[16]=aff[9];taff[17]=aff[10];
<     taff[23]=dir4[2]*4.0*(ROWS-(2*location[2])-1);}    /* location */
<   if(dir4[1]!=0){
<     for(ii=0;ii<3;ii++)taff[5+ii]=0;
<     taff[0]=aff[0];taff[1]=aff[1];taff[2]=aff[2];      /* direction */
<     taff[10]=aff[4];taff[11]=aff[5];taff[12]=aff[6];
<     taff[15]=aff[8];taff[16]=aff[9];taff[17]=aff[10];
<     taff[23]=dir4[1]*4.0*(ROWS-(2*location[1])-1);}    /* location */
<   if(dir4[0]!=0){
<     for(ii=0;ii<3;ii++)taff[ii]=0;
<     taff[5]=aff[0];taff[6]=aff[1];taff[7]=aff[2];      /* direction */
<     taff[10]=aff[4];taff[11]=aff[5];taff[12]=aff[6];
<     taff[15]=aff[8];taff[16]=aff[9];taff[17]=aff[10];
<     taff[23]=dir4[0]*4.0*(ROWS-(2*location[0])-1);}    /* location */
< }
< void taff2aff(){
<   int ii;
<   for(ii=0;ii<4;ii++)dir4[ii]=ROUND(taff[3+(5*ii)]); /* update dir4 too */
<   aff[12]=taff[20];aff[13]=taff[21];aff[14]=taff[22];/* location */
<   if(dir4[3]!=0){
<     aff[0]=taff[0];aff[1]=taff[1];aff[2]=taff[2];      /* direction */
<     aff[4]=taff[5];aff[5]=taff[6];aff[6]=taff[7];
<     aff[8]=taff[10];aff[9]=taff[11];aff[10]=taff[12];}
<   if(dir4[2]!=0){
<     aff[0]=taff[0];aff[1]=taff[1];aff[2]=taff[2];      /* direction */
<     aff[4]=taff[5];aff[5]=taff[6];aff[6]=taff[7];
<     aff[8]=taff[15];aff[9]=taff[16];aff[10]=taff[17];}
<   if(dir4[1]!=0){
<     aff[0]=taff[0];aff[1]=taff[1];aff[2]=taff[2];      /* direction */
<     aff[4]=taff[10];aff[5]=taff[11];aff[6]=taff[12];
<     aff[8]=taff[15];aff[9]=taff[16];aff[10]=taff[17];}
<   if(dir4[0]!=0){
<     aff[0]=taff[5];aff[1]=taff[6];aff[2]=taff[7];      /* direction */
<     aff[4]=taff[10];aff[5]=taff[11];aff[6]=taff[12];
<     aff[8]=taff[15];aff[9]=taff[16];aff[10]=taff[17];}
< }
< 
< /* MAY 1999: Cleaned up comments */
< /* Rotate around the (fixed) XY,XZ&YZ planar axes */
< /* Recall Front is -z, Up is +y, and Right is +x */
< 
< void XYrotate(int sgn){GLfloat temp;GLfloat gsgn=(GLfloat)sgn;int ii;
< /* Rotate old (sgn)*Other to new Front */
<  for(ii=0;ii<5;ii++){
<    temp=(-1)*taff[3+(5*ii)];
<    taff[3+(5*ii)]=taff[2+(5*ii)]*gsgn;
<    taff[2+(5*ii)]=temp*gsgn;}}
< void XZrotate(int sgn){GLfloat temp;GLfloat gsgn=(GLfloat)sgn;int ii;
< /* Rotate old (sgn)*Other to new Up */
<  for(ii=0;ii<5;ii++){
<    temp=taff[3+(5*ii)];
<    taff[3+(5*ii)]=(-1)*taff[1+(5*ii)]*gsgn;
<    taff[1+(5*ii)]=temp*gsgn;}}
< void YZrotate(int sgn){GLfloat temp;GLfloat gsgn=(GLfloat)sgn;int ii;
< /* if Rotate old (sgn)*Other to new Right */
<  for(ii=0;ii<5;ii++){
<    temp=taff[3+(5*ii)];
<    taff[3+(5*ii)]=(-1)*taff[5*ii]*gsgn;
<    taff[5*ii]=temp*gsgn;}}
< 
< /* MAY 1999: Created XY,XZ,YZinitRotate */
< /* Initialize gradual rotation of 3D slice through 4D space
<    with XY,XZ&YZ planar axes fixed */
< void XYinitRotate(int sgn){
<   if(taff[3+0]!=0){ /*                   Moving Axis: x->? */
<     if(taff[2+5]!=0){ /*                              x->y */
<       ActualAxes=-1; init_edges(1);
<       sign_theta=(sgn)*(taff[3+0])*(-1)*(taff[2+5]);}
<     else if(taff[2+10]!=0){ /*                        x->z */
<       ActualAxes=-2; init_edges(2);
<       sign_theta=(sgn)*(taff[3+0])*(-1)*(taff[2+10]);}
<     else{ /* taff[2+15]!=0                            x->w */
<       ActualAxes=-3; init_edges(3);
<       sign_theta=(sgn)*(taff[3+0])*(-1)*(taff[2+15]);}
<   }
<   else if(taff[3+5]!=0){ /*              Moving Axis: y->? */
<     if(taff[2+0]!=0){ /*                              y->x */
<       ActualAxes=1; init_edges(1);
<       sign_theta=(sgn)*(taff[3+5])*(-1)*(taff[2+0]);}
<     else if(taff[2+10]!=0){ /*                        y->z */
<       ActualAxes=-4; init_edges(4);
<       sign_theta=(sgn)*(taff[3+5])*(-1)*(taff[2+10]);}
<     else{ /* taff[2+15]!=0                            y->w */
<       ActualAxes=-5; init_edges(5);
<       sign_theta=(sgn)*(taff[3+5])*(-1)*(taff[2+15]);}
<   }
<   else if(taff[3+10]!=0){ /*             Moving Axis: z->? */
<     if(taff[2+0]!=0){ /*                              z->x */
<       ActualAxes=2; init_edges(2);
<       sign_theta=(sgn)*(taff[3+10])*(-1)*(taff[2+0]);}
<     else if(taff[2+5]!=0){ /*                         z->y */
<       ActualAxes=4; init_edges(4);
<       sign_theta=(sgn)*(taff[3+10])*(-1)*(taff[2+5]);}
<     else{ /* taff[2+15]!=0                            z->w */
<       ActualAxes=-6; init_edges(6);
<       sign_theta=(sgn)*(taff[3+10])*(-1)*(taff[2+15]);}
<   }
<   else{ /* taff[3+15]!=0                 Moving Axis: w->? */
<     if(taff[2+0]!=0){ /*                              w->x */
<       ActualAxes=3; init_edges(3);
<       sign_theta=(sgn)*(taff[3+15])*(-1)*(taff[2+0]);}
<     else if(taff[2+5]!=0){ /*                         w->y */
<       ActualAxes=5; init_edges(5);
<       sign_theta=(sgn)*(taff[3+15])*(-1)*(taff[2+5]);}
<     else{ /* taff[2+10]!=0                            w->z */
<       ActualAxes=6; init_edges(6);
<       sign_theta=(sgn)*(taff[3+15])*(-1)*(taff[2+10]);}
<   }
< }/* XYinitRotate */
< void XZinitRotate(int sgn){
<   if(taff[3+0]!=0){ /*                   Moving Axis: x->? */
<     if(taff[1+5]!=0){ /*                              x->y */
<       ActualAxes=-1; init_edges(1);
<       sign_theta=(sgn)*(taff[3+0])*(taff[1+5]);}
<     else if(taff[1+10]!=0){ /*                        x->z */
<       ActualAxes=-2; init_edges(2);
<       sign_theta=(sgn)*(taff[3+0])*(taff[1+10]);}
<     else{ /* taff[1+15]!=0                            x->w */
<       ActualAxes=-3; init_edges(3);
<       sign_theta=(sgn)*(taff[3+0])*(taff[1+15]);}
<   }
<   else if(taff[3+5]!=0){ /*              Moving Axis: y->? */
<     if(taff[1+0]!=0){ /*                              y->x */
<       ActualAxes=1; init_edges(1);
<       sign_theta=(sgn)*(taff[3+5])*(taff[1+0]);}
<     else if(taff[1+10]!=0){ /*                        y->z */
<       ActualAxes=-4; init_edges(4);
<       sign_theta=(sgn)*(taff[3+5])*(taff[1+10]);}
<     else{ /* taff[1+15]!=0                            y->w */
<       ActualAxes=-5; init_edges(5);
<       sign_theta=(sgn)*(taff[3+5])*(taff[1+15]);}
<   }
<   else if(taff[3+10]!=0){ /*             Moving Axis: z->? */
<     if(taff[1+0]!=0){ /*                              z->x */
<       ActualAxes=2; init_edges(2);
<       sign_theta=(sgn)*(taff[3+10])*(taff[1+0]);}
<     else if(taff[1+5]!=0){ /*                         z->y */
<       ActualAxes=4; init_edges(4);
<       sign_theta=(sgn)*(taff[3+10])*(taff[1+5]);}
<     else{ /* taff[1+15]!=0                            z->w */
<       ActualAxes=-6; init_edges(6);
<       sign_theta=(sgn)*(taff[3+10])*(taff[1+15]);}
<   }
<   else{ /* taff[3+15]!=0                 Moving Axis: w->? */
<     if(taff[1+0]!=0){ /*                              w->x */
<       ActualAxes=3; init_edges(3);
<       sign_theta=(sgn)*(taff[3+15])*(taff[1+0]);}
<     else if(taff[1+5]!=0){ /*                         w->y */
<       ActualAxes=5; init_edges(5);
<       sign_theta=(sgn)*(taff[3+15])*(taff[1+5]);}
<     else{ /* taff[1+10]!=0                            w->z */
<       ActualAxes=6; init_edges(6);
<       sign_theta=(sgn)*(taff[3+15])*(taff[1+10]);}
<   }
< }/* XZinitRotate */
< void YZinitRotate(int sgn){
<   if(taff[3+0]!=0){ /*                   Moving Axis: x->? */
<     if(taff[0+5]!=0){ /*                              x->y */
<       ActualAxes=-1; init_edges(1);
<       sign_theta=(sgn)*(taff[3+0])*(taff[0+5]);}
<     else if(taff[0+10]!=0){ /*                        x->z */
<       ActualAxes=-2; init_edges(2);
<       sign_theta=(sgn)*(taff[3+0])*(taff[0+10]);}
<     else{ /* taff[0+15]!=0                            x->w */
<       ActualAxes=-3; init_edges(3);
<       sign_theta=(sgn)*(taff[3+0])*(taff[0+15]);}
<   }
<   else if(taff[3+5]!=0){ /*              Moving Axis: y->? */
<     if(taff[0+0]!=0){ /*                              y->x */
<       ActualAxes=1; init_edges(1);
<       sign_theta=(sgn)*(taff[3+5])*(taff[0+0]);}
<     else if(taff[0+10]!=0){ /*                        y->z */
<       ActualAxes=-4; init_edges(4);
<       sign_theta=(sgn)*(taff[3+5])*(taff[0+10]);}
<     else{ /* taff[0+15]!=0                            y->w */
<       ActualAxes=-5; init_edges(5);
<       sign_theta=(sgn)*(taff[3+5])*(taff[0+15]);}
<   }
<   else if(taff[3+10]!=0){ /*             Moving Axis: z->? */
<     if(taff[0+0]!=0){ /*                              z->x */
<       ActualAxes=2; init_edges(2);
<       sign_theta=(sgn)*(taff[3+10])*(taff[0+0]);}
<     else if(taff[0+5]!=0){ /*                         z->y */
<       ActualAxes=4; init_edges(4);
<       sign_theta=(sgn)*(taff[3+10])*(taff[0+5]);}
<     else{ /* taff[0+15]!=0                            z->w */
<       ActualAxes=-6; init_edges(6);
<       sign_theta=(sgn)*(taff[3+10])*(taff[0+15]);}
<   }
<   else{ /* taff[3+15]!=0                 Moving Axis: w->? */
<     if(taff[0+0]!=0){ /*                              w->x */
<       ActualAxes=3; init_edges(3);
<       sign_theta=(sgn)*(taff[3+15])*(taff[0+0]);}
<     else if(taff[0+5]!=0){ /*                         w->y */
<       ActualAxes=5; init_edges(5);
<       sign_theta=(sgn)*(taff[3+15])*(taff[0+5]);}
<     else{ /* taff[0+10]!=0                            w->z */
<       ActualAxes=6; init_edges(6);
<       sign_theta=(sgn)*(taff[3+15])*(taff[0+10]);}
<   }
< }/* YZinitRotate */
< /* MAY 1999: Created XY,XZ,YZinitRotate */
< 
< void getmem(void){
<   if(caveyes)
< #ifdef CAVE
<      s_var = CAVEMalloc(sizeof(struct share_var))
< #endif
<      ;
<    else
<      s_var = malloc(sizeof(struct share_var));
<   if(s_var==NULL)
<     {fprintf(stderr,"No room to share! %x \n",s_var); exit(1);}
< }
< 
< void char2wall(GLfloat x,GLfloat y, GLfloat z, char buf[]){
<      char *p; glRasterPos3f(x,y,z);
<      for(p = buf;*p;p++) glutBitmapCharacter(GLUT_BITMAP_9_BY_15,*p);
< }
< 
< void autotymer(int);  /* to allow deFault to call the autotymer later */
< 
< void deFault(void){
<   int ii; /*float tmp;*/
<   gnd = 0;
<   th0=5; th1=355;  dth=15; ta0=5; ta1=355; dta=15; gap = gap0;
<   msg=1; binoc=0; nose=.06; mauspaw = 0; wnd = 1; siz = 1.; 
<   mode=TURNMODE; if(caveyes)mode=FLYMODE;
<   speed=.1; torq=.02; focal = 1.5/1.1/1.1/1.1/1.1/1.1/1.1; mysiz=.01;
<   if(rand_maze)Far=13.;else Far=38.; if(demo)morph=1;else morph=0; frozen=0;
<   for(ii=0;ii<16;ii++)  starmat[ii]=aff[ii]=id[ii] = (ii/4==ii%4) ? 1 : 0;   
< /*  tmp=NRM(lux);  for(ii=0;ii<3;ii++)lux[ii]/=tmp;*/
< /*if(caveyes){aff[12]=0; aff[13]= 5; aff[14]= -3;}/*5ft beyond front wall*/ 
< /*else{*/
<   aff[12]=4*(ROWS1%2); aff[13]=4*(ROWS1%2);      /*inside the console */
<   if(in_maze)aff[14]=4*(ROWS1%2);
<   else aff[14]= -4*ROWS1; /* move it away */ 
< /*}*/
<   location[0] = (ROWS1/2)-1;location[1] = trunc(ROWS1/2)-1;
<   if(in_maze)location[2] = trunc(ROWS1/2)-1;else location[2]=ROWS;
<   location[3] = trunc(ROWS1/2)-1; 
<   rotating_in_4D=cube=0;theta=moving=0;
<   /* Note: much of the previous stuff is rendered irrelevent by the 
<      repositioning that is a part of the next line */
<   if(demo)autotymer(1);
<   for(ii=0;ii<25;ii++) taff[ii] = (ii/5==ii%5) ? 1 : 0;   
<   taff[20]=aff[12];taff[21]=aff[13];taff[22]=aff[14];
<   dir4[0]=0;dir4[1]=0;dir4[2]=0;dir4[3]=1;
<   /* Cannot update lagmvg=0; because it is local */
< }
< /*
< void drawvert(int th, int ta){  
< /* exercise ... float all the angles for a smoother homotopy */ 
< /*float lmb,spec,nn[3], dog, cat;
< nn[0] = C(th)*C(ta); /* unit sphere vector */  
< /*nn[1] = S(th)*C(ta);
< nn[2] =       S(ta);
< lmb = MAX(CLAMP(nn[0]*lu[0] + nn[1]*lu[1] + nn[2]*lu[2],0.,1.),.3);
< spec = MIN(1, 1*(1-10+10*lmb));                    /* clamp spec below maximum       */
< /*dog = (ta-ta0)/(float)(ta1-ta0); cat = (th-th0)/(float)(th1-th0);
< glColor3f(MAX(lmb*dog,spec),                     /* map R^2(dog,cat)->R^3(RGBspace */
< /*          MAX(lmb*(.25 + fabsf(cat -.5)),spec),  /* dog cat model of Hartman       */
< /*          MAX(lmb*(1 - cat ),spec));             /* illiLight by Ray Idaszak 1989  */
< /*glVertex3f(C(th) + .5*nn[0],S(th) + .5*nn[1],.5*nn[2]);
< }
< 
< void drawtor(void){
< int th, ta;
< dth = (int)((th1-th0)/24);  /* 24  meridian strips */ 
< /*dta = (int)((ta1-ta0)/24);  /* 24  triangle pairs per strip */
< /*for(th=th0; th < th1; th += dth){
<   glBegin(GL_TRIANGLE_STRIP);
<   for(ta=ta0; ta < ta1; ta += dta){ drawvert(th,ta); drawvert(th+gap*dth,ta); }
<   glEnd();
<   }
< }
< */
< void drawcube(void){ /* transfer from skel.c as an exercise  */ }
< 
< /* MAY 1999 */
< drawstairs(int dir4[4]){
<   int ii,jj,kk;
<   if(dir4[3]!=0)FOR(ii,0,ROWS)FOR(jj,0,ROWS)FOR(kk,0,ROWS){
<     if(xyzfaces[ii][jj][kk][location[3]]==0 && 
<        location[3]>=0 && location[3]<ROWS1)
<       drawportal(ii,jj,kk,thick,dir4[3]);
<     if(xyzfaces[ii][jj][kk][location[3]+1]==0 &&
<        location[3]>=-1 && location[3]<ROWS)
<       drawportal(ii,jj,kk,thick,(-1)*dir4[3]);
<   }
<   else if(dir4[2]!=0)FOR(ii,0,ROWS)FOR(jj,0,ROWS)FOR(kk,0,ROWS){
<     if(xywfaces[ii][jj][location[2]][kk]==0 && 
<        location[2]>=0 && location[2]<ROWS1)
<       drawportal(ii,jj,kk,thick,dir4[2]);
<     if(xywfaces[ii][jj][location[2]+1][kk]==0 &&
<        location[2]>=-1 && location[2]<ROWS)
<       drawportal(ii,jj,kk,thick,(-1)*dir4[2]);
<   }
<   else if(dir4[1]!=0)FOR(ii,0,ROWS)FOR(jj,0,ROWS)FOR(kk,0,ROWS){
<     if(xzwfaces[ii][location[1]][jj][kk]==0 && 
<        location[1]>=0 && location[1]<ROWS1)
<       drawportal(ii,jj,kk,thick,dir4[1]);
<     if(xzwfaces[ii][location[1]+1][jj][kk]==0 &&
<        location[1]>=-1 && location[1]<ROWS)
<       drawportal(ii,jj,kk,thick,(-1)*dir4[1]);
<   }
<   else /* dir4[0]!=0 */ FOR(ii,0,ROWS)FOR(jj,0,ROWS)FOR(kk,0,ROWS){
<     if(yzwfaces[location[0]][ii][jj][kk]==0 && 
<        location[0]>=0 && location[0]<ROWS1)
<       drawportal(ii,jj,kk,thick,dir4[0]);
<     if(yzwfaces[location[0]+1][ii][jj][kk]==0 &&
<        location[0]>=-1 && location[0]<ROWS)
<       drawportal(ii,jj,kk,thick,(-1)*dir4[0]);
<   }
< }
< 
< /* MAY 1999 */
< /* JUNE 1999 */
< void drawall(void){static int ss,tt;
<  if(rotating_in_4D==0){
<    drawmaze(((ROWS-ROUND(DSUM*taff[23]/4.0)-1)/2),Delta,dir4); 
<    drawstairs(dir4);   
<  }
<  else /* rotating_in_4D==1 */ {
<   /* JUNE 1999 EDIT
<   printf("\n drawall:theta = %f",theta);
<   printf("\n drawall:sign_theta = %f",sign_theta);
<      JUNE 1999 EDIT */
<    if(theta==theta_jump)
<      switch(abs(ActualAxes)){
<      case 1:ss=location[0];tt=location[1];break;
<      case 2:ss=location[0];tt=location[2];break;
<      case 3:ss=location[0];tt=location[3];break;
<      case 4:ss=location[1];tt=location[2];break;
<      case 5:ss=location[1];tt=location[3];break;
<      case 6:ss=location[2];tt=location[3];break;
<      }
<    if(ActualAxes>0)
<      draw_intersections(ss,tt,sign_theta*(theta)*DG,abs(ActualAxes),Delta);
<    else /* ActualAxes<0 */
<      draw_intersections(ss,tt,sign_theta*(90-theta)*DG,abs(ActualAxes),Delta);
<  }
< }
< 
< void drawstars(void){
< int i;
< static float star[1000][3];
< static int virgin=1;
< if(virgin){
<   int ii,jj;
<   float fact;
<   virgin=0;
<   for(ii=0;ii<1000;ii++)
<      {
<      for(jj=0;jj<3;jj++) star[ii][jj] =   rand()/(float)0x40000000-1.;
<      if(caveyes){
<            fact = 90./NRM(star[ii]);
<            for(jj=0;jj<3;jj++) star[ii][jj] *= fact;
<            }   
<      }
<   }
<   glMatrixMode(GL_MODELVIEW);
<   glPushMatrix();
<   glMultMatrixf(starmat);
<   glColor3f(0.6,0.6,0.3);    
<   glBegin(GL_POINTS);
<     for(i=0;i<1000;i++) glVertex3fv(star[i]);
<   glEnd();
<   glPopMatrix();
<   glClear(GL_DEPTH_BUFFER_BIT);
< }
< 
< void audioprep(void){
< #ifdef SOUND
<    if (!FBgnSoundServer())
<       fprintf(stderr,"UDP connection to sound server failed\n");
<    else if ((audiohandle = AUDinit("noosh.aud")) == -1)
<       fprintf(stderr,"Couldn't open noosh.aud\n");
< #endif
< }
< 
< void dohoot(void) {strcpy(which,"hoot");}
< void dotrump(void){strcpy(which,"trump");}
< void doflute(void){strcpy(which,"flute");}
< void dodrum(void) {strcpy(which,"drum");}
< 
< void audiofunc(void){
< #ifdef SOUND 
< #define LIRP(X,A,B,x,a,b)  (X = A + (x-a)*(B-A)/(b-a))
<   static float freq=440., ampl=1., indx=20., cmra=2.; 
<   static float data[4];
<   static omode =0; static ofocal = 0;
<   static beep =0;  /* the sound server */
< 
<   if(omode > mode){dohoot(); beep = 1;}
<   if(omode < mode){dodrum(); beep = 1;}
< 
<   LIRP(freq, 440, 1760, th0,  10, 170); /* frequeny mimics th1 */ 
<   LIRP(indx, 10, 1,  focal, .03, 2.0); /* index  mimics focal */ 
<   if(ofocal != focal){dodrum(); beep = 1;}    /* there is an fp bug here */
< 
< if(beep){data[0] = freq;  /* pitch in Herz      */ 
<           data[1] = ampl;  /* the sum of all amplitudes must be < 1.0 */ 
<           data[2] = indx;    
<           data[3] = cmra;
<           AUDupdate(audiohandle, which, 4, data);  /* send message  to vss */ 
<           beep = 0; 
< }
<   omode = mode;  ofocal = focal; 
< #endif
< }
< 
< void audioclean(void){
< #ifdef SOUND
<    AUDterminate(audiohandle);
<    EndSoundServer();
< #endif
< }
< 
< void arguments(int argc,char **argv){
<   extern char *optarg;
<   extern int optind; 
<   int chi;   
< /* defaults if not set by command line: */
<   rseed=43; XYZchance=.6, XYWchance= .7,  XZWchance=.75 , YZWchance=.6 ;
<   thick=2;demo=0;Delta=.15;
<   /* w: needs ONE number after -w, c<nothing> means NO number follows*/ 
<  /*
<   while ((chi = getopt(argc,argv,"w:cg:r:ts:x:y:z:h:odD:")) != -1) 
<   switch(chi)  {case 'c': caveyes=1; break;  
<                 case 'w': win=atoi(optarg); break; 
<                 case 'g': gap0=atof(optarg); break;
<       case 'r': rseed=atoi(optarg); rand_maze=1; break;
<       case 't': thick=1; break;   
<       case 's': speed=atof(optarg); break;
<       case 'x': YZWchance=atof(optarg); break;
<       case 'y': XZWchance=atof(optarg); break;
<       case 'z': XYWchance=atof(optarg); break;
<       case 'h': XYZchance=atof(optarg); break;
<       case 'o': in_maze=0; break;
<       case 'd': demo=1; break;
<       case 'D': Delta=atof(optarg); break;
<                 }
<   if (optind!=argc) {
<     fprintf(stderr,"%s: Incorrect usage\n",argv[0]);
<     fprintf(stderr,"Usage: %s [-w win] [-t] [-s speed] [-r rseed] [-x probability of red walls]\n  [-y probability of green walls] [-z probability of blue walls]\n  [-h probability of hidden walls] ([-o]) [-d] [-c] [-g ?] [-D Delta]\n",argv[0]);
<     /*    exit(-1);    */ /* Why is this in skel but not noosh? */
< /*  }
< */
< }
< 
< void dataprep(void){
<    audioprep();
<    deFault();
<    initvv();  /* initializes the vertices */
<    if(rand_maze)makemaze(rseed,XYZchance,XYWchance,XZWchance,YZWchance);
<    else setmaze();
<    init_colors(); /* MAY 1999 */
< }
< /* autymer adjusted to prevent user movement and _move_ autmomatically */
< void autotymer(int reset){int ii;
< /* cnt4D is used for all 4D rotations */
<  static int cnt4D=(max_theta_delay*((90/theta_jump)-1))+1;
< #define  TYME(cnt,max,act) {static cnt; if(first)cnt=max; else\
<                             if(cnt?cnt--:0){ act ; goto Break;}}
< #define  HYTHER  TYME( dwell  , 8,  moving= 0)\
<   TYME( hither , 1,  moving=10) TYME( dwell  , 8,  moving= 0)
< #define THYTHER  TYME( dwell  , 8,  moving= 0)\
<   TYME( thither, 1,  moving= 9) TYME( dwell  , 8,  moving= 0)
< /* The following is not being used: */
< #define PADDEDTYME(cnt,act) TYME( dwell  , 8,  moving= 0)\
<   TYME( cnt , 1, act) TYME( dwell  , 8,  moving= 0)
< 
<   static first = 1;  /* the first time autymer is called */
<   if(reset)first=1;  /* or if it is reset to start over  */
<   if(first){moving=0;theta=0;/* moving=0 necessary? */
<     location[0]=3;location[1]=2;location[2]=2;location[3]=2;
<     for(ii=0;ii<16;ii++)starmat[ii]=aff[ii]=id[ii]=(ii/4==ii%4)?1:0;   
<     /* JUNE 1999 edit: 'siz*' will have to be added to the next line */
<     aff[12]=4*(ROWS1%2)-8.0;aff[13]=4*(ROWS1%2);aff[14]=4*(ROWS1%2);
<     dir4[0]=dir4[1]=dir4[2]=0;dir4[3]=1;aff2taff();}
< 
<   /* elevator-only demo follows */
< 
<   TYME( forward, 16, moving= 8) 
<   TYME( forward, 16, moving= 8)
<   TYME( right  , 18, moving= 3)
<   TYME( forward, 16, moving= 8)
<   TYME( left   , 18, moving= 4)
<   TYME( left   , 18, moving= 4)
<   HYTHER
<   TYME( forward, 16, moving= 8)
<   TYME( up     , 18, moving= 5)
<   TYME( forward, 16, moving= 8)
<   TYME( down   , 18, moving= 6)
<   TYME( forward, 16, moving= 8)
<   TYME( bankRgt, 18, moving= 1)
<   TYME( bankLft, 18, moving= 2)
<   TYME( forward, 16, moving= 8)
<   TYME( left   , 18, moving= 4)
<   TYME( forward, 16, moving= 8)
<   HYTHER
<   THYTHER
<   THYTHER
<   TYME( forward, 16, moving= 8)
<   TYME( down   , 18, moving= 6)
<   TYME( forward, 16, moving= 8)
<   TYME( up     , 18, moving= 5)
<   TYME( forward, 16, moving= 8)
<   TYME( forward, 16, moving= 8)
<   TYME( left   , 18, moving= 4)
<   TYME( forward, 16, moving= 8)
<   THYTHER
<   TYME( forward, 16, moving= 8)
<   THYTHER
<   TYME( down   , 18, moving= 6)
<   TYME( up     , 18, moving= 5)
<   TYME( forward, 16, moving= 8)
<   THYTHER
<   TYME( left   , 18, moving= 4)
<   TYME( back   , 16, moving= 7)
<   HYTHER
<   TYME( back   , 16, moving= 7)
<   HYTHER
<   TYME( back   , 16, moving= 7)
<   HYTHER
<   HYTHER
<   HYTHER
<   TYME( forward, 16, moving= 8)
<   TYME( forward, 16, moving= 8)
<   TYME( forward, 16, moving= 8)
<   TYME( forward, 16, moving= 8)
<   THYTHER
<   THYTHER
<   TYME( left   , 18, moving= 4)
<   TYME( forward, 16, moving= 8)
<   TYME( right  , 18, moving= 3)
<   TYME( forward, 16, moving= 8)
<   TYME( dwell  , 30, moving= 0)
< 
<   /* elevator-less demo follows */
<   TYME( FourRotate, cnt4D, moving=11)
<   TYME( FourRotate, cnt4D, moving=12)
<   TYME( FourRotate, cnt4D, moving=13)
<   TYME( FourRotate, cnt4D, moving=14)
<   TYME( FourRotate, cnt4D, moving=16)
<   TYME( FourRotate, cnt4D, moving=15)
<   TYME( dwell  , 10, moving= 0)
<   TYME( forward, 16, moving= 8) 
<   TYME( forward, 16, moving= 8)
<   TYME( right  , 18, moving= 3)
<   TYME( forward, 16, moving= 8) 
<   TYME( FourRotate, cnt4D, moving=11)
<   TYME( right  , 18, moving= 3)
<   TYME( forward, 16, moving= 8) 
<   TYME( right  , 18, moving= 3)
<   TYME( forward, 16, moving= 8) 
<   TYME( up     , 18, moving= 5)
<   TYME( forward, 16, moving= 8) 
<   TYME( down   , 18, moving= 6)
<   TYME( forward, 16, moving= 8)
<   TYME( forward, 16, moving= 8)
<   TYME( FourRotate, cnt4D, moving=11)
<   TYME( left   , 18, moving= 4)
<   TYME( forward, 16, moving= 8)
<   TYME( FourRotate, cnt4D, moving=15)
<   TYME( FourRotate, cnt4D, moving=15)
<   TYME( FourRotate, cnt4D, moving=15)
<   TYME( FourRotate, cnt4D, moving=15)
<   TYME( FourRotate, cnt4D, moving=11)
<   TYME( forward, 16, moving= 8)
<   TYME( left   , 18, moving= 4)
<   TYME( forward, 16, moving= 8)
<   TYME( down   , 18, moving= 6)
<   TYME( forward, 16, moving= 8)
<   TYME( right  , 18, moving= 3)
<   TYME( bankLft, 18, moving= 2)
<   TYME( forward, 16, moving= 8)
<   TYME( FourRotate, cnt4D, moving=14)
<   TYME( forward, 16, moving= 8)
<   TYME( down   , 18, moving= 6)
<   TYME( forward, 16, moving= 8)
<   TYME( left   , 18, moving= 4)
<   TYME( forward, 16, moving= 8)
<   TYME( right  , 18, moving= 3)
<   TYME( forward, 16, moving= 8)
<   TYME( left   , 18, moving= 4)
<   TYME( forward, 16, moving= 8)
<   TYME( right  , 18, moving= 3)
<   TYME( forward, 16, moving= 8)
<   TYME( left   , 18, moving= 4)
<   TYME( forward, 16, moving= 8)
<   TYME( left   , 18, moving= 4)
<   TYME( left   , 18, moving= 4)
<   TYME( back   , 16, moving= 7)
<   TYME( up     , 18, moving= 5)
<   TYME( forward, 16, moving= 8)
<   TYME( down   , 18, moving= 6)
<   TYME( forward, 16, moving= 8)
<   TYME( forward, 16, moving= 8)
<   TYME( forward, 16, moving= 8)
<   TYME( forward, 16, moving= 8)
<   TYME( forward, 16, moving= 8)
<   TYME( forward, 16, moving= 8)
<   TYME( down   , 18, moving= 6)
<   TYME( forward, 16, moving= 8)
<   TYME( forward, 16, moving= 8)
<   TYME( down   , 18, moving= 6)
<   TYME( forward, 16, moving= 8)
<   TYME( forward, 16, moving= 8)
<   TYME( right  , 18, moving= 3)
<   TYME( forward, 16, moving= 8)
<   TYME( up     , 18, moving= 5)
<   TYME( forward, 16, moving= 8)
<   TYME( FourRotate, cnt4D, moving=12)
<   TYME( bankRgt, 18, moving= 1)
<   TYME( dwell  , 30, moving= 0)
< 
<   TYME(finish  , 1 , first = 1  )
<   first = 0;
<   Break:   ;   /* yes Virginia, C has gotos */
< }
< 
< /*Beware of competing keys in CAVEsimulation and wnd==1   */
< /* you may want to remap these keys to avoid the conflict */
< void keyboard(unsigned char key, int x, int y){
< #define  IF(K)            if(key==K)
< #define  PRESS(K,A,b)     IF(K){b;}IF((K-32)){A;}  
< /*was backwards in previous versions */
< #define  TOGGLE(K,flg)    IF(K){(flg) = 1-(flg); }
< #define  CYCLE(K,f,m)     PRESS((K), (f)=(((f)+(m)-1)%(m)),(f)=(++(f)%(m)))
< #define  SLIDI(K,f,m,M)   PRESS(K,(--f<m?m:f), (++f>M?M:f))
< #define  SLIDF(K,f,m,M,d) PRESS(K,((f -= d)<m?m:f), ((f += d)>M?M:f))
<    static int begin_morph=0;
< /* Only ASCII characters can be processes by this GLUT callback function */ 
<    TOGGLE('v',binoc);                            /* cross-eyed STEREO   */
<    TOGGLE('w',msg);                              /* writing on/off      */
<    knopf = 0;
<    PRESS('e',knopf=-1, knopf=1);
<    if(demo==0){
<      if(morph==0)begin_morph=1;  /* b_morph was 0 - afterwards, b_morph */
<      TOGGLE('h',morph);                            /* autotymer on/off    */
<      if(morph==0)begin_morph=0;  /* set to 1 if morph has been switched on  */
<      if(begin_morph){autotymer(1); begin_morph=0;}}
<    else IF('h'){rotating_in_4D=0;moving=0;theta=0;autotymer(1);}
<    TOGGLE('f',frozen);
<    PRESS('n', nose -= .001 , nose += .001 );     /* for binoculars      */
<    CYCLE(' ', mode,TURNMODE+1);                  /* fly/turn modes      */
<    PRESS('i', mysiz /= 1.1, mysiz *= 1.1)        /* rescale the world   */
<    PRESS('o', focal *= 1.1 , focal /= 1.1)       /* telephoto           */
<    PRESS('p', Far *= 1.01 , Far   /= 1.01)       /* rear clipping plane */
< /* PRESS('s',speed /= 1.02, speed *= 1.02);      /* flying speed        */
<    PRESS('q',torq /= 1.02, torq *= 1.02);        /* turning speed       */
<    PRESS('g',gap /= .99, gap *= .99);             /* gap parameter       */
<    PRESS('z', deFault(), deFault());             /* zap changes         */
<    PRESS('s', w_shift=10, w_shift=9);            /* move in 4th dimension */
<    PRESS('d', if(Delta==0)Delta=.01;else if(Delta<.25)Delta+=.005;else if(Delta<4)Delta *= 1.02; else Delta=4, if(Delta <= .02)Delta=0;else if(Delta<=.25)Delta-=.005;else Delta /= 1.02);
<    /*
<    PRESS('a',showaff(),showtaff());
<    PRESS('r',roundaff(),roundtaff());
<    PRESS('t',taff2aff(),aff2taff());
<    */
<    /* JUNE 1999 edit */
<    PRESS('a',showAA(),showAA());   
< 
<    IF('1')w_shift=11;/* rotate right towards +hidden */
<    IF('!')w_shift=12;/* rotate left towards +hidden */
<    IF('2')w_shift=13;/* rotate up towards hidden */
<    IF('@')w_shift=14;/* rotate down towards hidden */
<    IF('3')w_shift=15;/* rotate forwards towards hidden */
<    IF('#')w_shift=16;/* rotate back towards hidden */
<    IF(27) {audioclean(); exit(0); }              /* ESC exit            */
< }
< 
< void special_keybo(int key, int x, int y){
< /*non-ASCII keypresses go here, if you're lucky enough to know their names */
<   IF(GLUT_KEY_RIGHT)w_shift=11;
<   IF(GLUT_KEY_LEFT) w_shift=12;
<   IF(GLUT_KEY_UP)   w_shift=13;
<   IF(GLUT_KEY_DOWN) w_shift=14;
<   IF(GLUT_KEY_PAGE_UP) w_shift=15;
<   IF(GLUT_KEY_PAGE_DOWN)w_shift=16;
< //  IF(GLUT_KEY_F1)
< //  IF(GLUT_KEY_END)
< }
< 
< 
< 
< /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% */
< #ifdef CAVE
< /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% */
< 
< void cavekeybo(void){
< #define  CAVEIF(K)         if(CAVEgetbutton(K))
< #define  CAVEPRESS(K,A,b)  CAVEIF(K){if(CAVEgetbutton(CAVE_LEFTSHIFTKEY)||\
<                            CAVEgetbutton(CAVE_RIGHTSHIFTKEY)){b;}else{A;}}
< #define  CAVETOGGLE(K,flg) CAVEIF(K){(flg) = 1-(flg); }
< #define  CAVECYCLE(K,f,m)  CAVEPRESS((K),(f)=(((f)+(m)-1)%(m)),(f)=(++(f)%(m)) )
< #define  CAVESLIDI(K,f,m,M)  CAVEPRESS(K,(--f<m?m:f), (++f>M?M:f))
< #define  CAVESLIDF(K,f,m,M,d) CAVEPRESS(K,((f -= d)<m?m:f), ((f += d)>M?M:f))
<   CAVEPRESS(CAVE_PKEY, Far *= 1.01,   Far   /= 1.01) /* rear clipping plane*/
<   CAVEPRESS(CAVE_QKEY, speed /= 1.02, speed *= 1.02);/* flying speed       */
<   CAVEPRESS(CAVE_RKEY, torq /= 1.02,  torq *= 1.02); /* turning speed      */
<   CAVETOGGLE(CAVE_YKEY,wnd);                         /* mauspaw vs wandpaw */
< /* not yet implemented */ 
<    CAVEPRESS(CAVE_ZKEY, deFault(), deFault());        /* zap changes       */
< }
< 
< void graffiti(void){             /* used to be called speedo  */
<   char buf[256];                 /*messages written into here */ 
< #define  LABEL3(x,y,z,W,u){sprintf(buf,(W),(u));char2wall(x,y,z,buf);}
<   static float last=0.,fps;      /*for measuring time         */ 
<   if(!CAVEMasterDisplay()) return;  /* USE CAVEMasterWall in papeBeta */
<   if(floor(2.*(*CAVETime))>floor(2.*last)){  /* rationalize this */ 
<      last=*CAVETime;
<      fps=*CAVEFramesPerSecond;
<      }
<   if(mode==TURNMODE)glColor3f(1.,0.,1.);
<   else glColor3f(1.,1.,0.);
<   LABEL3(-3.8,1.,-5.0,\
< 	 "Hold3=auto Click3=freeze Hold2=tractor 1=mode %s","");
<   LABEL3(-3.8,1.5,-5.0,
<           "JoyRight=Yellow JoyLeft=Purple JoyAhead/Back=Delta+/-%s","gap");
<   
<   LABEL3(-4.8,8.0,-5.0, "not illiOpenSkelGLUT\
<       by Francis, Bourd, Hartman & Chappell, (C) 1995..1997 U.Illinois %s","")
<        LABEL3(-4.8,1.0,-5.0,"%5.1f fps",fps);
< }
< 
< void cavetrack(void){
<   float azi,ele,rol,hx,hy,hz,wx,wy,wz;
<   static float ohx,ohy,ohz;   /*old head position */
<   static float owx,owy,owz;   /*old wand position */
<   static float oazi,oele,orol;   /*old wand orientation */
<   static int opaw = 0,paw, joy = 0, friz;
<   /* static int theta=0; */
<   int ii;
<   if(!CAVEMasterDisplay()) return;  /* doit once per frame */
<   paw = wnd ? (CAVEBUTTON1*2+CAVEBUTTON2)*2+CAVEBUTTON3 : mauspaw;
<   if(paw) joy = 1; /* activate joystick by clicking any button*/
<   if(opaw == 4 && paw == 4){
<     if(demo==0){morph=1-morph;if(morph)autotymer(1);}/* autotymer on/off  */
<     else{moving=0;theta=0;autotymer(1);}}            /* autotymer restart */
<   if(opaw == 0 && paw == 4)frozen=1-frozen;
<   if(opaw!=0 && paw==1) mode = (mode+1)%(TURNMODE+1); /* click modes */
<   /* Hopefully, pulling the joystick back lowers Delta/
<      increases the wallsize, and joy right is yellow... */
<   /* Eventually should use the varying of the joy.  limits? */
<   if(joy&&(CAVE_JOYSTICK_Y>.55)){
<     if(Delta==0)Delta=.01; else if(Delta<.25)Delta+=.005;
<     else if(Delta<4)Delta *= 1.02; else Delta=4;}
<   if(joy&&(CAVE_JOYSTICK_Y<-.55)){
<     if(Delta <= .02)Delta=0;else if(Delta<=.25)Delta-=.005;
<     else Delta /= 1.02;}
<   if(joy&&(CAVE_JOYSTICK_X>.44))w_shift=10;
<   if(joy&&(CAVE_JOYSTICK_X>.44))w_shift=9;  /* joystick */
<   if(paw == 7){ /* restart, the most useful feature */
<     deFault(); ohx=ohy=ohz=0; owx=owy=owz=0;} 
<   if(wnd){ 
<       CAVEGetWandOrientation(azi,ele,rol);
<       CAVEGetHead(hx,hy,hz);  /*position*/
<       CAVEGetWand(wx,wy,wz);  /*position*/ 
<       }
<    else{  /* fix this for CAVE mouse flying */ 
<       azi = .5*512; ele = .5*-512;
<       } 
<   if(opaw!=2 && paw==2){
<     oazi=azi;oele=ele;orol=rol;owz=wz;
<   }
<    glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity();
< 
<    glTranslatef(ohx-hx,ohy-hy,ohz-hz); 
< 
<    if(!frozen){
<      if(morph)autotymer(0); /* advance autotymer */
<      if(mode==TURNMODE) glTranslatef(aff[12],aff[13],aff[14]);
<      switch(moving){
<      case 0 : if(morph)break;/* only used when autotymer has first=1 */
<        if((ele-oele)>20 && ABS(azi-oazi)<20)moving=6;
<        else if((ele-oele)<-20 && ABS(azi-oazi)<20)moving=5;
<        else if(ABS(azi-oazi)>=20 && (azi-oazi)>ABS(rol-orol))moving=3;
<        else if(ABS(azi-oazi)>=20 && (azi-oazi)<-ABS(rol-orol))moving=4;
<        else if((rol-orol)>=20)moving=1;
<        else if((orol-rol)>=20)moving=2;
<        else if((wz-owz)<-2)moving=7;
<        else if((wz-owz)>2)moving=8;
<        if(paw!=2)moving=0;    /* Only make a move if holding button 2 */
<        if(w_shift!=0)moving=w_shift;
<        /* collision check: */
<        /*
<        {int may_go=1;int dirx=(-1)*ROUND(aff[2]);
<        int diry=(-1)*ROUND(aff[6]);int dirz=(-1)*ROUND(aff[10]);
<        if(moving==7 && location[3]>=0 && location[3]<ROWS){
< 	 if(dirx!=0 && location[1]<ROWS && location[1]>=0 && 
< 	    location[2]<ROWS && location[2]>=0)
< 	   if((dirx>0 && location[0]<ROWS1 && location[0]>=0) || 
< 	      (dirx<0 && location[0]<ROWS && location[0]>=-1))
< 	     if(yzwfaces[location[0]+(1-dirx)/2][location[1]][location[2]][location[3]])
< 	       may_go=0;
< 	 if(diry!=0 && location[0]<ROWS && location[0]>=0 && 
< 	    location[2]<ROWS && location[2]>=0)
< 	   if((diry>0 && location[1]<ROWS1 && location[1]>=0) || 
< 	      (diry<0 && location[1]<ROWS && location[1]>=-1))
< 	     if(xzwfaces[location[0]][location[1]+(1-diry)/2][location[2]][location[3]])
< 	       may_go=0;
< 	 if(dirz!=0 && location[0]<ROWS && location[0]>=0 && 
< 	    location[1]<ROWS && location[1]>=0)
< 	   if((dirz>0 && location[2]<ROWS1 && location[2]>=0) || 
< 	      (dirz<0 && location[2]<ROWS && location[2]>=-1))
< 	     if(xywfaces[location[0]][location[1]][location[2]+(1-dirz)/2][location[3]])
< 	       may_go=0;}
<        if(moving==8 && location[3]>=0 && location[3]<ROWS){
< 	 if(dirx!=0 && location[1]<ROWS && location[1]>=0 && 
< 	    location[2]<ROWS && location[2]>=0)
< 	   if((dirx<0 && location[0]<ROWS1 && location[0]>=0) || 
< 	      (dirx>0 && location[0]<ROWS && location[0]>=-1))
< 	     if(yzwfaces[location[0]+(dirx+1)/2][location[1]][location[2]][location[3]])
< 	       may_go=0;
< 	 if(diry!=0 && location[0]<ROWS && location[0]>=0 && 
< 	    location[2]<ROWS && location[2]>=0)
< 	   if((diry<0 && location[1]<ROWS1 && location[1]>=0) || 
< 	      (diry>0 && location[1]<ROWS && location[1]>=-1))
< 	     if(xzwfaces[location[0]][location[1]+(diry+1)/2][location[2]][location[3]])
< 	       may_go=0;
< 	 if(dirz!=0 && location[0]<ROWS && location[0]>=0 && 
< 	    location[1]<ROWS && location[1]>=0)
< 	   if((dirz<0 && location[2]<ROWS1 && location[2]>=0) || 
< 	      (dirz>0 && location[2]<ROWS && location[2]>=-1))
< 	     if(xywfaces[location[0]][location[1]][location[2]+(dirz+1)/2][location[3]])
< 	       may_go=0;} 
<        if(moving==9)
< 	 if(location[0]<ROWS && location[0]>=0 && location[1]<ROWS && 
< 	    location[1]>=0 && location[2]<ROWS && location[2]>=0 &&
< 	    location[3]>=0 && location[3]<ROWS1 &&
< 	    xyzfaces[location[0]][location[1]][location[2]][location[3]]){
< 	   may_go=0;w_shift=0;}
<        if(moving==10)
< 	 if(location[0]<ROWS && location[0]>=0 && location[1]<ROWS && 
< 	    location[1]>=0 && location[2]<ROWS && location[2]>=0 &&
< 	    location[3]>=-1 && location[3]<ROWS &&
< 	    xyzfaces[location[0]][location[1]][location[2]][location[3]+1]){
< 	   may_go=0;w_shift=0;}
<        if(may_go==0)moving=0;}
<        */
<        /* collision check */ 
<        break;/* case 0 */
<      case 1 : theta+=1;glRotatef(-5,0.,0.,1.);
<        if(theta==18){theta=0;moving=0;}break;
<      case 2 : theta+=1;glRotatef( 5,0.,0.,1.);
<        if(theta==18){theta=0;moving=0;}break;
<      case 3 : theta+=1;glRotatef( 5,0.,1.,0.);
<        if(theta==18){theta=0;moving=0;}break;
<      case 4 : theta+=1;glRotatef(-5,0.,1.,0.);
<        if(theta==18){theta=0;moving=0;}break;
<      case 5 : theta+=1;glRotatef(-5,1.,0.,0.);
<        if(theta==18){theta=0;moving=0;}break;
<      case 6 : theta+=1;glRotatef( 5,1.,0.,0.);
<        if(theta==18){theta=0;moving=0;}break;
<      }/* switch */
<      
<      if(mode==FLYMODE){
<        glPushMatrix();
<        glMultMatrixf(starmat);
<        glGetFloatv(GL_MODELVIEW_MATRIX,starmat);
<        glPopMatrix();
<      }
< #define  DOT3(M,i) (M[4*i]*M[12]+M[4*i+1]*M[13]+M[4*i+2]*M[14])
<      switch(moving){
<      case 7 : theta+=1;glTranslatef(0.,0.,-.5);
<        if(theta==16){theta=0;moving=0;
<        FOR(ii,0,3)location[ii]=((ROWS-ROUND(DOT3(aff,ii)/4.0))-1)/2;}break;
<      case 8 : theta+=1;glTranslatef(0.,0., .5);
<        if(theta==16){theta=0;moving=0;
<        FOR(ii,0,3)location[ii]=((ROWS-ROUND(DOT3(aff,ii)/4.0))-1)/2;}break;
<      case 9 : location[3]-=1;moving=0;w_shift=0;break;
<      case 10: location[3]+=1;moving=0;w_shift=0;break;
<      }/* switch */
< /* Maybe in the future will be able to use more wand translate tractor */
<      if(mode == TURNMODE) glTranslatef(-aff[12],-aff[13],-aff[14]);
<    }/* if(!frozen) */
<    glMultMatrixf(aff); 
<    glGetFloatv(GL_MODELVIEW_MATRIX,aff);
< 
<   {int ii,jj; for(ii=0;ii<3;ii++){ lu[ii]=0; /* calculite */
<    for(jj=0;jj<3;jj++) lu[ii] += aff[ii*4+jj]*lux[jj];} }
< 
<   glPopMatrix();
<   opaw=paw; ohx=hx; ohy=hy; ohz=hz; owx=wx; owy=wy;
<   audiofunc();
< }
< 
< void drawcaveinit(void){   /* kludge? needed why ? */  
<    CAVEFar = 1000.;
<    glClearColor(0.,0.,0.,0.);
<    glClearDepth(1.);
< }
< 
< void drawcave(void){
<   float hx,hy,hz;
<   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
<   drawstars();
<   graffiti();
<   CAVEGetHead(hx,hy,hz);
<   glMatrixMode(GL_MODELVIEW);
<   glPushMatrix();
<   glTranslatef(hx,hy,hz);
<   glMultMatrixf(aff);
<   glScalef(siz,siz,siz);
<   drawall();
<   glPopMatrix();
< }
< #endif
< 
< 
< 
< float speedometer(void){
< double dbl; static double rate; static int ii=0;
< static struct timezone notused; static struct timeval now, then;
<    if(++ii % 8 == 0){  /* 8 times around measure time */
<       gettimeofday(&now, &notused); /* elapsed time */
<       dbl =  (double)(now.tv_sec - then.tv_sec)
<          +(double)(now.tv_usec - then.tv_usec)/1000000;
<       then = now;  rate = 8/dbl;
<       }
<    return((float)rate);
< }
< #if 0 
< float speedometer(void){
< double dbl; static double rate; static int ii=0;
< 
< static struct _timeb lnow, lthen;
< 
<    if(++ii % 8 == 0){  /* 8 times around measure time */
< 
< 	   _ftime(&lnow);
< 		dbl =  (double)(lnow.time - lthen.time)
< 			 +(double)(lnow.millitm - lthen.millitm)/1000;
< 		lthen = lnow;  rate = 8/dbl;
<       }
< 
<    return((float)rate);
< }
< #endif 
< void messages(void){     
<   char buf[256]; /* console messages are done differently from cave */
< #define  LABEL2(x,y,W,u) {sprintf(buf,(W),(u));char2wall(x,y,0.,buf);}
<   glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity();
<   gluOrtho2D(0,3000,0,3000);
<   glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity();
<       /*bull's eye*/
<       if(mode==0) glColor3f(0x22/255.,0x88/255.,0xdd/255.);
<         else      glColor3f(.8,.8,.8);
<       LABEL2(1500,1500,"%s","o");
<       /* writings */
<       if(mode==0) glColor3f(1.,0.,1.);
<         else      glColor3f(1.,1.,0.);
<       LABEL2(80,80,"%4.1f fps",speedometer());
<       LABEL2(80,2770,"(ESC)ape (Z)ap (V)Binoc%s","");
<       LABEL2(10,10,"illiMaze4D \
<    by Michael Pelsmajer, U Illinois, 1999 %s","");
<       LABEL2(80,2840,"(N)ose   %0.3f",nose);
<       LABEL2(80,2700,"(W)riting %s","");   
<       LABEL2(80,2630,"near clipper %g", mysiz*focal);
<       LABEL2(80,2560,"f(O)cal factor %g",focal);
<       LABEL2(80,2490,"my s(I)ze %.2g",mysiz);
<       LABEL2(80,2420,"far cli(P)er= %.2g",Far);
<       LABEL2(80,2350,"(D)elta %1.3f",Delta);
<       if(demo){LABEL2(80,2280,"(H)ypertour %s","");}
<       else LABEL2(80,2280,"(h)demo %s","");
<       LABEL2(80,2210,"(f)reeze/unfreeze motion%s","");
<       LABEL2(80,2140,"hither(S)%s","");
<       LABEL2(80,2070,"yonder(s)%s","");
<  //     if(mode==FLYMODE){LABEL2(80,2000,"%sWarning: rotate is wack! Use sparingly.","");}
<    //   LABEL2(80,1930,"Print 4D (A)ffine matrix or 5D (a)ffine matrix%s","");
<       LABEL2(80,1860,"arrows rotate yonder %s","");
<       {sprintf(buf,"             location %d %d %d %d",location[0],location[1],
< 	       location[2],location[3]);char2wall(300,80,0.,buf);}
<       glPopMatrix();
< glMatrixMode(GL_PROJECTION); glPopMatrix();
< } 
< 
< void chaptrack(int but,int xx,int yy,int shif){  
<   /* JUNE 1999: theta_delay to slow down 4D rotation */
<   static int theta_delay=0;
<   /* MAY 1999: commented it out: */
<   /* static int theta=0; */
<    static int lagmvg=0;/* moving, but slightly delayed updates */
<    /* for conversion/rounding of taff/aff towards end of chaptrack */
<    long dx,dy; 
<    dx = xx -.5*xt; dy = yy -.5*yt; 
<    glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity();
<    if(mode==FLYMODE) glTranslatef(aff[12],aff[13],aff[14]);
<    switch(moving){
<    case 0 : if(morph)break;/* only used when autotymer has first=1 */
<      if(but==4)moving=1;   /* rolls left */
<      if(but==1)moving=2;   /* rolls right */
<      if(dx>90 && dx>ABS(dy))moving=3;else if(dx<-90 && -1*dx>ABS(dy))moving=4;
<      else if(dy>90)moving=6;else if(dy<-90)moving=5; /* yawing */
< /*
<      else if(but==5)moving=8-shif;   later for guarantee ?
< */
<      if(w_shift!=0)moving=w_shift;
<      if(but&(1<<GLUT_MIDDLE_BUTTON))moving=8-shif;
<      {/* collision check */
<        int may_go=1;int dirx=(-1)*ROUND(aff[2]);
<        int diry=(-1)*ROUND(aff[6]);int dirz=(-1)*ROUND(aff[10]);
<        int relloc[4];/* location relative to aff */
<        /* The immediate following adjustments to dirx,etc is an expirement */
<        if(dir4[0]!=0){relloc[3]=location[0];relloc[0]=location[1];
<         relloc[1]=location[2];relloc[2]=location[3];}
<        if(dir4[1]!=0){relloc[3]=location[1];relloc[0]=location[0];
<         relloc[1]=location[2];relloc[2]=location[3];}
<        if(dir4[2]!=0){relloc[3]=location[2];relloc[0]=location[0];
<         relloc[1]=location[1];relloc[2]=location[3];}
<        if(dir4[3]!=0){relloc[3]=location[3];relloc[0]=location[0];
<         relloc[1]=location[1];relloc[2]=location[2];}
<        if(moving==7 && relloc[3]>=0 && relloc[3]<ROWS){
< 	 if(dirx!=0 && relloc[1]<ROWS && relloc[1]>=0 && 
< 	    relloc[2]<ROWS && relloc[2]>=0)
< 	   if((dirx>0 && relloc[0]<ROWS1 && relloc[0]>=0) || 
< 	      (dirx<0 && relloc[0]<ROWS && relloc[0]>=-1))
< 	     if(dir4[0]==0)
< 	       {if(yzwfaces[location[0]+(1-dirx)/2][location[1]][location[2]][location[3]])
< 		 may_go=0;}
< 	     else if(xzwfaces[location[0]][location[1]+(1-dirx)/2][location[2]][location[3]])
< 	       may_go=0;
< 	 if(diry!=0 && relloc[0]<ROWS && relloc[0]>=0 && 
< 	    relloc[2]<ROWS && relloc[2]>=0)
< 	   if((diry>0 && relloc[1]<ROWS1 && relloc[1]>=0) || 
< 	      (diry<0 && relloc[1]<ROWS && relloc[1]>=-1))
< 	     if(dir4[0]==0 && dir4[1]==0)
< 	       {if(xzwfaces[location[0]][location[1]+(1-diry)/2][location[2]][location[3]])
< 		 may_go=0;}
< 	     else if(xywfaces[location[0]][location[1]][location[2]+(1-diry)/2][location[3]])
< 	       may_go=0;
< 	 if(dirz!=0 && relloc[0]<ROWS && relloc[0]>=0 && 
< 	    relloc[1]<ROWS && relloc[1]>=0)
< 	   if((dirz>0 && relloc[2]<ROWS1 && relloc[2]>=0) || 
< 	      (dirz<0 && relloc[2]<ROWS && relloc[2]>=-1))
< 	     if(dir4[3]!=0){
< 	       if(xywfaces[location[0]][location[1]][location[2]+(1-dirz)/2][location[3]])
< 		 may_go=0;}
< 	     else if(xyzfaces[location[0]][location[1]][location[2]][location[3]+(1-dirz)/2])
< 	       may_go=0;
<        }
<        if(moving==8 && relloc[3]>=0 && relloc[3]<ROWS){
< 	 if(dirx!=0 && relloc[1]<ROWS && relloc[1]>=0 && 
< 	    relloc[2]<ROWS && relloc[2]>=0)
< 	   if((dirx<0 && relloc[0]<ROWS1 && relloc[0]>=0) || 
< 	      (dirx>0 && relloc[0]<ROWS && relloc[0]>=-1))
< 	     if(dir4[0]==0)
< 	       {if(yzwfaces[location[0]+(dirx+1)/2][location[1]][location[2]][location[3]])
< 		 may_go=0;}
< 	     else if(xzwfaces[location[0]][location[1]+(dirx+1)/2][location[2]][location[3]])
< 	       may_go=0;
< 	 if(diry!=0 && relloc[0]<ROWS && relloc[0]>=0 && 
< 	    relloc[2]<ROWS && relloc[2]>=0)
< 	   if((diry<0 && relloc[1]<ROWS1 && relloc[1]>=0) || 
< 	      (diry>0 && relloc[1]<ROWS && relloc[1]>=-1))
< 	     if(dir4[0]==0 && dir4[1]==0)
< 	       {if(xzwfaces[location[0]][location[1]+(diry+1)/2][location[2]][location[3]])
< 		 may_go=0;}
< 	     else if(xywfaces[location[0]][location[1]][location[2]+(diry+1)/2][location[3]])
< 	       may_go=0;
< 	 if(dirz!=0 && relloc[0]<ROWS && relloc[0]>=0 && 
< 	    relloc[1]<ROWS && relloc[1]>=0)
< 	   if((dirz<0 && relloc[2]<ROWS1 && relloc[2]>=0) || 
< 	      (dirz>0 && relloc[2]<ROWS && relloc[2]>=-1))
< 	     if(dir4[3]!=0){
< 	       if(xywfaces[location[0]][location[1]][location[2]+(dirz+1)/2][location[3]])
< 		 may_go=0;}
< 	     else if(xyzfaces[location[0]][location[1]][location[2]][location[3]+(dirz+1)/2])
< 	       may_go=0;} 
<        if(moving==9 || moving==10)
< 	 if(0<=relloc[0] && relloc[0]<ROWS &&
< 	    0<=relloc[1] && relloc[1]<ROWS &&
< 	    0<=relloc[2] && relloc[2]<ROWS){
< 	   if(dir4[0]!=0){
< 	     if((moving==9 && dir4[0]==1)||(moving==10 && dir4[0]==-1)){
< 	       if(0<=relloc[3] && relloc[3]<ROWS1)
< 		 if(yzwfaces[location[0]][location[1]][location[2]][location[3]])
< 		   {may_go=0;w_shift=0;} }
< 	     else 
< 	       if(-1<=relloc[3] && relloc[3]<ROWS)
< 		 if(yzwfaces[location[0]+1][location[1]][location[2]][location[3]])
< 		   {may_go=0;w_shift=0;} }
< 	   else if(dir4[1]!=0){
< 	     if((moving==9 && dir4[1]==1)||(moving==10 && dir4[1]==-1)){
< 	       if(0<=relloc[3] && relloc[3]<ROWS1)
< 		 if(xzwfaces[location[0]][location[1]][location[2]][location[3]])
< 		   {may_go=0;w_shift=0;} }
< 	     else 
< 	       if(-1<=relloc[3] && relloc[3]<ROWS)
< 		 if(xzwfaces[location[0]][location[1]+1][location[2]][location[3]])
< 		   {may_go=0;w_shift=0;} }
< 	   else if(dir4[2]!=0){
< 	     if((moving==9 && dir4[2]==1)||(moving==10 && dir4[2]==-1)){
< 	       if(0<=relloc[3] && relloc[3]<ROWS1)
< 		 if(xywfaces[location[0]][location[1]][location[2]][location[3]])
< 		   {may_go=0;w_shift=0;} }
< 	     else 
< 	       if(-1<=relloc[3] && relloc[3]<ROWS)
< 		 if(xywfaces[location[0]][location[1]][location[2]+1][location[3]])
< 		   {may_go=0;w_shift=0;} }
< 	   else /* dir4[3]!=0 */{
< 	     if((moving==9 && dir4[3]==1)||(moving==10 && dir4[3]==-1)){
< 	       if(0<=relloc[3] && relloc[3]<ROWS1)
< 		 if(xyzfaces[location[0]][location[1]][location[2]][location[3]])
< 		   {may_go=0;w_shift=0;} }
< 	     else 
< 	       if(-1<=relloc[3] && relloc[3]<ROWS)
< 		 if(xyzfaces[location[0]][location[1]][location[2]][location[3]+1])
< 		   {may_go=0;w_shift=0;} }
< 	 }/* w-shift collsion checked */
<        if(may_go==0)moving=0;}/* collision check */
<      lagmvg=moving;/* This needed to be in cases 9 and 10      */
<      /*               Perhaps should be in all cases somewhere */
<      break;/* case 0 */
<    case 1 : lagmvg=1;theta+=1;glRotatef(-5,0.,0.,1.);
<      if(theta==18){theta=0;moving=0;}break;
<    case 2 : lagmvg=2;theta+=1;glRotatef( 5,0.,0.,1.);
<      if(theta==18){theta=0;moving=0;}break;
<    case 3 : lagmvg=3;theta+=1;glRotatef( 5,0.,1.,0.);
<      if(theta==18){theta=0;moving=0;}break;
<    case 4 : lagmvg=4;theta+=1;glRotatef(-5,0.,1.,0.);
<      if(theta==18){theta=0;moving=0;}break;
<    case 5 : lagmvg=5;theta+=1;glRotatef(-5,1.,0.,0.);
<      if(theta==18){theta=0;moving=0;}break;
<    case 6 : lagmvg=6;theta+=1;glRotatef( 5,1.,0.,0.);
<      if(theta==18){theta=0;moving=0;}break;
< /* MAY 1999: cases 11-16 redone */
<    case 11: lagmvg=11;
<      if(theta==0){
<        rotating_in_4D=1; YZinitRotate(1);
<        if(ActualAxes<0)YZrotate(1);}
<      if(theta_delay==0)theta+=theta_jump;
<      theta_delay+=1;
<      if(theta_delay==max_theta_delay)theta_delay=0;
<      if(theta==90){
<        theta_delay=0;
<        theta=0;moving=0;w_shift=0;rotating_in_4D=0;
<        if(ActualAxes>0)YZrotate(1);}
<      break;
<    case 12: lagmvg=12;
<      if(theta==0){
<        rotating_in_4D=1; YZinitRotate(-1);
<        if(ActualAxes<0)YZrotate(-1);}
<      if(theta_delay==0)theta+=theta_jump;
<      theta_delay+=1;
<      if(theta_delay==max_theta_delay)theta_delay=0;
<      if(theta==90){
<        theta_delay=0;
<        theta=0;moving=0;w_shift=0;rotating_in_4D=0;
<        if(ActualAxes>0)YZrotate(-1);}
<      break;
<    case 13: lagmvg=13;
<      if(theta==0){
<        rotating_in_4D=1; XZinitRotate(1);
<        if(ActualAxes<0)XZrotate(1);}
<      if(theta_delay==0)theta+=theta_jump;
<      theta_delay+=1;
<      if(theta_delay==max_theta_delay)theta_delay=0;
<      if(theta==90){
<        theta_delay=0;
<        theta=0;moving=0;w_shift=0;rotating_in_4D=0;
<        if(ActualAxes>0)XZrotate(1);}
<      break;
<    case 14: lagmvg=14;
<      if(theta==0){
<        rotating_in_4D=1; XZinitRotate(-1);
<        if(ActualAxes<0)XZrotate(-1);}
<      if(theta_delay==0)theta+=theta_jump;
<      theta_delay+=1;
<      if(theta_delay==max_theta_delay)theta_delay=0;
<      if(theta==90){
<        theta_delay=0;
<        theta=0;moving=0;w_shift=0;rotating_in_4D=0;
<        if(ActualAxes>0)XZrotate(-1);}
<      break;
<    case 15: lagmvg=15;
<      if(theta==0){
<        rotating_in_4D=1; XYinitRotate(1);
<        if(ActualAxes<0)XYrotate(1);}
<      if(theta_delay==0)theta+=theta_jump;
<      theta_delay+=1;
<      if(theta_delay==max_theta_delay)theta_delay=0;
<      if(theta==90){
<        theta_delay=0;
<        theta=0;moving=0;w_shift=0;rotating_in_4D=0;
<        if(ActualAxes>0)XYrotate(1);}
<      break;
<    case 16: lagmvg=16;
<      if(theta==0){
<        rotating_in_4D=1; XYinitRotate(-1);
<        if(ActualAxes<0)XYrotate(-1);}
<      if(theta_delay==0)theta+=theta_jump;
<      theta_delay+=1;
<      if(theta_delay==max_theta_delay)theta_delay=0;
<      if(theta==90){
<        theta_delay=0;
<        theta=0;moving=0;w_shift=0;rotating_in_4D=0;
<        if(ActualAxes>0)XYrotate(-1);}
<      break;
< /* MAY 1999: cases 11-16 redone */
<    }/* switch */
< 
<    if(mode==TURNMODE){
<       glPushMatrix();
<       glMultMatrixf(starmat);
<       glGetFloatv(GL_MODELVIEW_MATRIX,starmat);
<       glPopMatrix();
<    }
< #define  DOT3(M,i) (M[4*i]*M[12]+M[4*i+1]*M[13]+M[4*i+2]*M[14])
<    switch(moving){
<    case 7 : lagmvg=7;theta+=1;glTranslatef(0.,0.,-.5);if(theta==16){
<      theta=0;moving=0;
<      /* update location */ /* Can't be updating location as normal! */
<      /*
<      if(dir4[3]!=0)
<        FOR(ii,0,3)location[ii]=((ROWS-ROUND(DOT3(aff,ii)/4.0))-1)/2;
<      if(dir4[2]!=0){
<        if(aff[2]!=0)location[0]=(ROWS-1-ROUND(aff[2]*aff[14]/4.0))/2;
<        else if(aff[6]!=0)location[1]=aff[6]*aff[14];
<        else location[3]=aff[10]*aff[14];}
<      else if(dir4[1]!=0){
<        if(aff[2]!=0)location[0]=aff[2]*aff[14];
<        else if(aff[6]!=0)location[2]=aff[6]*aff[14];
<        else location[3]=aff[10]*aff[14];}
<      else{
<        if(aff[2]!=0)location[1]=aff[2]*aff[14];
<        else if(aff[6]!=0)location[2]=aff[6]*aff[14];
<        else location[3]=aff[10]*aff[14];}
<    */
<      if(dir4[3]!=0){
<        if(aff[2]!=0)location[0]+=(int)aff[2];
<        else if(aff[6]!=0)location[1]+=(int)aff[6];
<        else location[2]+=(int)aff[10];}
<      else if(dir4[2]!=0){
<        if(aff[2]!=0)location[0]+=(int)aff[2];
<        else if(aff[6]!=0)location[1]+=(int)aff[6];
<        else location[3]+=(int)aff[10];}
<      else if(dir4[1]!=0){
<        if(aff[2]!=0)location[0]+=(int)aff[2];
<        else if(aff[6]!=0)location[2]+=(int)aff[6];
<        else location[3]+=(int)aff[10];}
<      else /*dir4[1]!=0*/{
<        if(aff[2]!=0)location[1]+=(int)aff[2];
<        else if(aff[6]!=0)location[2]+=(int)aff[6];
<        else location[3]+=(int)aff[10];}
<    }break;
<    case 8 : lagmvg=8;theta+=1;glTranslatef(0.,0., .5);if(theta==16){
<      theta=0;moving=0;
<      /* update location */
<      if(dir4[3]!=0){
<        if(aff[2]!=0)location[0]-=(int)aff[2];
<        else if(aff[6]!=0)location[1]-=(int)aff[6];
<        else location[2]-=(int)aff[10];}
<      else if(dir4[2]!=0){
<        if(aff[2]!=0)location[0]-=(int)aff[2];
<        else if(aff[6]!=0)location[1]-=(int)aff[6];
<        else location[3]-=(int)aff[10];}
<      else if(dir4[1]!=0){
<        if(aff[2]!=0)location[0]-=(int)aff[2];
<        else if(aff[6]!=0)location[2]-=(int)aff[6];
<        else location[3]-=(int)aff[10];}
<      else /*dir4[1]!=0*/{
<        if(aff[2]!=0)location[1]-=(int)aff[2];
<        else if(aff[6]!=0)location[2]-=(int)aff[6];
<        else location[3]-=(int)aff[10];}
<      /* FOR(ii,0,3)location[ii]=((ROWS-ROUND(DOT3(aff,ii)/4.0))-1)/2;*/
<    }break;
<    case 9 : 
<      lagmvg=9;
<      if(dir4[3]!=0)location[3]-=dir4[3];
<      else if(dir4[2]!=0)location[2]-=dir4[2];
<      else if(dir4[1]!=0)location[1]-=dir4[1];
<      else location[0]-=dir4[0];moving=0;w_shift=0;break;
<    case 10: 
<      lagmvg=10;
<      if(dir4[3]!=0)location[3]+=dir4[3];
<      else if(dir4[2]!=0)location[2]+=dir4[2];
<      else if(dir4[1]!=0)location[1]+=dir4[1];
<      else location[0]+=dir4[0];moving=0;w_shift=0;break;
<    }/* switch */
<    if(mode==FLYMODE) glTranslatef(-aff[12],-aff[13],-aff[14]);
<    glMultMatrixf(aff); 
<    glGetFloatv(GL_MODELVIEW_MATRIX,aff);
<    {int ii,jj; for(ii=0;ii<3;ii++){ lu[ii]=0; /* calculite */
<    for(jj=0;jj<3;jj++) lu[ii] += aff[ii*4+jj]*lux[jj];}}
<    glPopMatrix();
< 
<    if(lagmvg!=moving && lagmvg!=0){
<      if(lagmvg<9){ /* 3D rotation or movement*/
<        /*
<        if(lagmvg<7)printf("\nrotated in 3D:");
<        else printf("\nmoved in 3D:");
<        */
<        /*
<        showaff();roundaff();showaff();aff2taff();showtaff();}
<        */
<        roundaff();aff2taff();}
<      else if(lagmvg<11){ /* 4D movement */
<        /*printf("\nmoved in 4D:");*/
<        /*
<        showaff();showtaff();aff2taff();showtaff();}
<        */
<        aff2taff();}
<      else{ /* 4D rotation */
<        /*printf("\nrotated in 4D:");*/
<        /*
<        showtaff();roundtaff();showtaff();taff2aff();showaff();}
<        */
<        roundtaff();taff2aff();}
<    }
<    /* JUNE 1999 */
<    /* To make ActualAxes<0 cases work, too */
<    if(moving>10){roundtaff();taff2aff();}
< }
< 
< void reshaped(int xx, int yy){xt=xx ; yt=yy;}   /* what is this for ? */
< 
< void drawcons(void){  
<   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
<   if(binoc) glViewport(0,yt/4,xt/2,yt/2);
<   glMatrixMode(GL_PROJECTION); glLoadIdentity();
<   glFrustum(-mysiz*xt/yt,mysiz*xt/yt,-mysiz,mysiz,mysiz*focal,Far); 
<   glMatrixMode(GL_MODELVIEW); glLoadIdentity();
<   drawstars();
<   glTranslatef(-binoc*nose,0.0,0.0);
<   glMultMatrixf(aff);
<   drawall();
<   if(binoc){
<     glViewport(xt/2,yt/4,xt/2,yt/2); 
<     glMatrixMode(GL_MODELVIEW);
<     glLoadIdentity();
<     drawstars();
<     glTranslatef(binoc*nose,0.0,0.0);
<     glMultMatrixf(aff);
<     drawall();
<     }
<   glViewport(0,0,xt,yt);
<   if(msg) messages();
<   glutSwapBuffers();
< }
< 
< void idle(void){ /*do this when nothing else is happening*/
< if(frozen==0){
<   if(morph)autotymer(0);  /* advance autotymer */ 
<   glutPostRedisplay();  /*redraw the window*/
<   chaptrack(BUT,XX,YY,SHIF);
<   audiofunc();} 
< }
< 
< void mousepushed(int but,int stat,int x,int y){
<   if(stat==GLUT_DOWN) BUT |= (1<<but);
<   else BUT &= (-1 ^ (1<<but));  
<   XX=x; YY=y; SHIF=(glutGetModifiers()==GLUT_ACTIVE_SHIFT)?1:0;
< }
< 
< void mousemoved(int x,int y){  XX=x; YY=y; }
< 
< int  main(int argc, char **argv){  
<    arguments(argc,argv); 
<    if(caveyes)
< #ifdef CAVE
<        CAVEConfigure(&argc, argv, NULL)
< #endif
<        ;
<    getmem();
<    dataprep();
<  
<    if(caveyes){
< #ifdef CAVE
<        CAVEInit(); /* Each wall is (part of) a forked process from here on.*/ 
<        CAVEFrameFunction(cavetrack,0);  
<        /*is restricted to MasterWall, so once per frame */
<        CAVEInitApplication(drawcaveinit, 0); 
<        CAVEDisplay(drawcave, 0);
<        while(!CAVEgetbutton(CAVE_ESCKEY))
<          {
<          cavekeybo();  /* is asynchronous from display processes */ 
< 	 }
<        audioclean();
<        CAVEExit();
< #endif
<        ;}
<      else{ /*console main*/
<        glutInit(&argc, argv);
<        glutInitDisplayMode(GLUT_DOUBLE|GLUT_DEPTH);
<        switch(win)
<          { case 0: break;
<            case 1: glutInitWindowSize(640, 480);
<                    glutInitWindowPosition(0,1024-480);
<                    break;
<            case 2: glutInitWindowPosition(0,0);
< 	           break;
<          }
<        glutCreateWindow("<* illiSkel in C/OpenGL/GLUT *>");
<        if(win==2) glutFullScreen();
<        glEnable(GL_DEPTH_TEST);
<        glutDisplayFunc(drawcons);       
<        glutKeyboardFunc(keyboard);
<        glutSpecialFunc(special_keybo);
<        glutMouseFunc(mousepushed);
<        glutMotionFunc(mousemoved);       
<        glutPassiveMotionFunc(mousemoved); 
<        glutReshapeFunc(reshaped);
<        glutIdleFunc(idle);             
<        glutMainLoop();
<        }
< } 
---
> /*   create AltMaze in this image */
> 
> /* Added speed3D variable */
> /* Shared variables killed for single-screen use */
> /* TURNMODE thoroughly disabled */
> 
> /* Late Feb24 hopefully finished new setmaze() */
> /* And autotymer, although it seriously depends on the former being okay */
> 
> /* Feb19 - works in the CAVE, I think */
> 
> /* This was newT_Tried2CAVE.c 
>    So far, I've made the same correction that I did to TrueCave */
> /* It seems like sharing rotating_in_4D would be the solution to my
>    problem, but instead it crashes the whole business */
> /* Now add some diagnostics */
> 
> 
> /* This is the successful version, but I'm trying to make it caved.  
>    Currently it doesn't work properly, and I don't remember what's up 
>    with shared variables */
> 
> /* Do the colors BASIC_etc. need to be shared? */
> 
> 
> 
> /* This is the successful version from 6-9-99, but on 6-10-99 was
>    brought to beckman and is now being slightly improved, a la old_true/ */
> 
> 
> /* 6-3-99 
>    Should theta & sign_theta be floats, or merely converted to floats
>    when and if necessary? */
> 
> /** Doesn't work, but compiles: 5-26-99 **/
> /** Fundamental change - need to think about when including cavetrack:
>     Made theta only global:
>       Old: in chaptrack: static int theta; globally: int theta
>       New: globally: int Theta 
>     This is needed for drawall & draw_intersections to use theta.
> **/
> 
> /** This is being canibalized/changed into the final May 1999 version
>     with visual 4D rotation:
>     These changes will be highlighted with an indicator: 'MAY 1999'
>     Still not done in June, so new edits: 'JUNE 1999'
> **/
> 
> /** Note: this grafixlab one is not caved - a caved version should exist
>     in Beckman **/
> 
> 
> 
> /* collision detection added 5/12/98 predawn 1am */
> 
> /* I think it works!, mostly 2/22/98 */
> /* still have to add collision detection */
> /* And either stairways or sound */
> /* To cave it, I must figure out how I want to use the wand to ask 
>    for a 4D rotation */
> 
> /* only changed for chaptrack - cavetrack probably won't work at all */
> /* lagmvg was needed because the round multiples of 8 and 1 that I think
>    of as typical aff & taff values are not that in the heart of chaptrack,
>    only outside of the mucking around in chaptrack.  So to use roundaff, 
>    roundtaff, showaff, showtaff, taff2aff & aff2taff right after a movement
>    had been completed, I needed lagmvg. */
> /* Changed location_w to location[3] */
> /* location[4] represents the absolute location */
> /* drawmaze() thinks LL is the location of the hidden variable */
> /* The old program thinks that location_w is really
>    the fourth location - not so good */
> /* Actually, should replace location with lctn */
> /* setmaze has a new maze, one with the concept of true 4D not stairs */
> 
> /* True4DMaze.c by Michael Pelsmajer 1998 */
> /* maze4Dogl gkf 4dec97 */
> /* This may just be the final elevator-style version */
> /* note: location incompatible with TURNMODE - to begin correct location 
>    again, need to zap */
> /* based heavily on illiSkel (C) 1996 G.Francis, C.Hartman */
> /* based even more so on... */
> /****************************************************************/
> /****            noosh.c = ishell97 to be                    ****/
> /****           descended from ishell95                      ****/
> /****            14 February 1995                            ****/
> /****            29 August   1995                            ****/
> /****             1 November 1995                            ****/ 
> /****            15 november 1996   ishell96.c               ****/
> /****            27 november 1996   ishell96.c               ****/
> /****            23 march    1997   newsh.c                  ****/
> /****    (C) 1994 Board of Trustees University of Illinois   ****/ 
> /****    A Model Real-Time Interactive CAVE Application      ****/
> /****    George Francis, Glenn Chappell, Chris Hartman       ****/
> /****    e-mail  gfrancis@math.uiuc.edu                      ****/
> /****    Generic RTICA by GGC 7/25/94 revised gkf 12.30.94   ****/ 
> /****    Revised by GGC and GKF august 95                    ****/
> /****    "Opengl ed" by AVB, nov 14-15, 1996                 ****/
> /****    Reformatted by AVB, nov 16, 1996                    ****/
> /****    adaptation to CAVE 2.6Beta started 6feb97 gkf       ****/
> /****    New version to fix many bugs and clean up 17mar97 cmh  */
> /****    Rationalization begun 26mar97 gkf                   ****/    
> /****************************************************************/
> /* CAVE trials 1apr97 bugs:
>    1. At 24x24x2 triangles = 1152 we get 24fps with all 8 processors
>       At 30x30x2 triangles = 1800 this drops to 16 fps on 8 processors
>       With fewer processors (mpunlock) the dropoff is disastrous, 
>    2. Current version (= 31mar97) does ambient only on process 2 (right
>       wall and floor) .... no paint at all!
>  
> /* 
> /* Some emerging conventions on the path of rationalization 
> 
>    draw<what> == this function uses GL calls for drawing something
>    cons       == pertaining to the console mode only
>    cave       == pertaining to the CAVE mode(s)
>                  all such functions are protected by #ifdef CAVEs for
>                  CAVE less compilation
>    audio      == pertaining to the vss audio stuff required three
>                  matched files: vssClient.h libsnd.a and vss 
>                  should also be protected by #ifdef SOUNDs
>    autotymer  == is (now) called only by the master process in the CAVE
>                  with the result that non-shared globals are not updated
>                  on the other walls. So we share them. 
>    <factor>  == a factor is a function that has been separated out for 
>                  (1) convenience (i.e. is not a factor of any other function) 
>                  (2) necessity (i.e. is a factor of another function )
>    <depth>   == from main() in the factor graph. 
> 
>    Of necessity, C requires that the factor graph be treelike with the leaves
>    at the top of the program code. For a C++ conversion, this tree will be 
>    turned upside down.  
> */
> 
> /* Factor graph for this program. Please indicate structural modifications 
>    The asterisk indicates a factor that has more than one function calling it. 
>    The Mm indicats something that is different from noosh. 
>    Mm  main  
>    Mm    arguments ....................command line switches and parameters 
>            getopt  
>          CAVEconfigure
>          getmem .......................allocate shared memory  
>            Malloc(share_var)
>          dataprep .....................done only once   
>            audioprep
>              AUDinit
>    Mm     *deFault 
>    Mm      initvv
>    Mm      makemaze/setmaze 
>          ifCAVE
>            FrameFunc(cavetrack)........done once every frame 
>                        *deFault........on the reset button combination
>                        *autotymer .....automatic clockwork animation
>                        *audiofunc .....sends messages to soundserver
>            InitApplic(drawcaveinit)....done each frame 
>            Display(drawcave)...........done each frame
>                     *drawstars
>                      graffiti..........on the front CAVE wall
>                         char2wall
>                     *drawall
> 
>            while(notESC) cavekeybo.....done asynchronously from display
>                            *deFault
>            audioclean .................shut down audio server
>            Exit........................shut down CAVE properly 
>          ifCONS
>            gluttery....................we use the GLUT library
>               drawcons.................analogue to drawcave
>                 *drawstars
>    Mm           *drawall 
>    Mm               drawmaze
>    Mm               drawstairs (commented out)
>    Mm           messages...............analogue to grafitti
>                   char2wall
>                   speedometer..........unix based speedometer
>    Mm         keyboard.................GLUT distinguishes between ascii keys 
>    Mm           *deFault
>                 *audioclean
>               special_keybo............and other kinds of keys
>               mousepushed..............mouse buttons were pressed
>               mousemoved...............mouse was moved       
>               reshaped.................what to do if window is reshaped
>    Mm         idle.....................done when nothing else is being done 
>    Mm            chaptrack.............updates the two affine matrices 
>    Mm           *autotymer.............preset directions for chaptrack
>                 *audiofunc.............updates the sound servers  
>          endIF 
> */
> #include <stdlib.h>
> /*#include <sys/types.h>*/
> #include <math.h>
> #include <sys/time.h>
> #include <stdio.h>
> #include <malloc.h>
> #include <glut.h>
> 
> #ifdef SOUND
> /*
> #include <device.h>
> */
> #include <string.h>
> #include "vssClient.h"
> #endif
> #ifdef CAVE
> #include <cave_ogl.h>
> #include <cave.macros.h>
> #endif 
> 
> /* Old idea:
>    Possibly change to vary thickness or width of line objects to 
>    indicate longer stairways */
> 
> #define ADDXYz(M,i,j,siz) siz*(M[0]+i),siz*(M[1]+j),siz*(M[2]+0.02)
> #define ADDXZy(M,i,k,siz) siz*(M[0]+i),siz*(M[1]+0.02),siz*(M[2]+k)
> #define ADDYZx(M,j,k,siz) siz*(M[0]+0.02),siz*(M[1]+j),siz*(M[2]+k)
> #define ADDXY(M,i,j,siz) siz*(M[0]+i),siz*(M[1]+j),siz*(M[2])
> #define ADDXZ(M,i,k,siz) siz*(M[0]+i),siz*(M[1]),siz*(M[2]+k)
> #define ADDYZ(M,j,k,siz) siz*(M[0]),siz*(M[1]+j),siz*(M[2]+k)
> /* MAY 1999: */
> #define DDList siz*DD[0],siz*DD[1],siz*DD[2]
> 
> #define RAND_MAX 32767
> // #include <device.h>
> 
> int maygo;
> 
> /* The following pair must be consecutive: */
> #define ROWS 8
> #define ROWS1 9
> float vv[ROWS1][ROWS1][ROWS1][3];
> float DD[3];/* MAY 1999 */
> /* The maze data: */
> int xyzfaces[ROWS][ROWS][ROWS][ROWS1]; /* incidence matrix */
> int xzwfaces[ROWS][ROWS1][ROWS][ROWS];
> int yzwfaces[ROWS1][ROWS][ROWS][ROWS];
> int xywfaces[ROWS][ROWS][ROWS1][ROWS];
> /* MAY 1999: */
> GLfloat BASIC_BLUE[3],BASIC_GREEN[3],BASIC_RED[3],BASIC_YELLOW[3],BASIC_GREY[3];
> 
> /* moving_axes is always abs(ActualAxes) from main(), in:
>    setDD, init_edges, draw_hedge, draw_vedge, draw_hvedge, 
>    draw_otheredges, and draw_intersections */
> 
> initvv(){int ii,jj,kk;
>  for(ii=0;ii<ROWS1;ii++)for(jj=0;jj<ROWS1;jj++)for(kk=0;kk<ROWS1;kk++){
>    vv[ii][jj][kk][0] = (ii-ROWS/2.0)*8.0;
>    vv[ii][jj][kk][1] = (jj-ROWS/2.0)*8.0;
>    vv[ii][jj][kk][2] = (kk-ROWS/2.0)*8.0;}
> }
> 
> /* Creates faces with probability __CHANCE */
> makemaze(unsigned int seed,float xyzchance,float xywchance,float xzwchance,
> 	 float yzwchance)
> {int ii,jj,kk,ll; srand(seed);for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)
>   for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS1;ll++)
>     xyzfaces[ii][jj][kk][ll] = (0==trunc((rand()/xyzchance)/RAND_MAX));
>  for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)
>    for(kk=0;kk<ROWS1;kk++)for(ll=0;ll<ROWS;ll++)
>      xywfaces[ii][jj][kk][ll] = (0==trunc((rand()/xywchance)/RAND_MAX));
>  for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS1;jj++)
>    for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
>      xzwfaces[ii][jj][kk][ll] = (0==trunc((rand()/xzwchance)/RAND_MAX));
>  for(ii=0;ii<ROWS1;ii++)for(jj=0;jj<ROWS;jj++)
>    for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
>      yzwfaces[ii][jj][kk][ll] = (0==trunc((rand()/yzwchance)/RAND_MAX));
> }
> 
> /* Creates a specfic maze, intended for use with autotymer as a demo */
> /* FEB2000 version: with stairs, 4D rotations and a more reasonable
>    autotymer in the Cave */
> /* Needs ROWS=8 */
> setmaze(){int ii,jj,kk,ll;
>  for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)for(kk=0;kk<ROWS;kk++)
>    for(ll=0;ll<ROWS1;ll++)xyzfaces[ii][jj][kk][ll] = 0;
>  for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)for(kk=0;kk<ROWS1;kk++)
>    for(ll=0;ll<ROWS;ll++)xywfaces[ii][jj][kk][ll] = 0;
>  for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS1;jj++)for(kk=0;kk<ROWS;kk++)
>    for(ll=0;ll<ROWS;ll++)xzwfaces[ii][jj][kk][ll] = 0;
>  for(ii=0;ii<ROWS1;ii++)for(jj=0;jj<ROWS;jj++)for(kk=0;kk<ROWS;kk++)
>    for(ll=0;ll<ROWS;ll++)yzwfaces[ii][jj][kk][ll] = 0;
> 
>  /* First Hallway: */
>  for(kk=2;kk<7;kk++){
>    yzwfaces[0][4][kk][1]=1;
>    xzwfaces[0][4][kk][1]=1;
>    xyzfaces[0][4][kk][1]=1;}
>  for(kk=2;kk<6;kk++){
>    yzwfaces[1][4][kk][1]=1;
>    xzwfaces[0][5][kk][1]=1;
>    xyzfaces[0][4][kk][2]=1;}
>  xyzfaces[0][4][3][2]=0;
>  xywfaces[0][4][2][1]=1;
> 
>  /* Next Passage */
>  xywfaces[0][4][5][2]=1;
>  yzwfaces[1][4][3][2]=1;yzwfaces[1][4][4][2]=1;
>  for(kk=2;kk<5;kk++){
>    yzwfaces[0][4][kk][2]=1;
>    xzwfaces[0][4][kk][2]=1;
>    xzwfaces[0][5][kk][2]=1;
>    xyzfaces[0][4][kk][3]=1;}
>  for(ii=0;ii<3;ii++)xywfaces[ii][4][2][2]=1;
>  xywfaces[1][4][3][2]=1;xywfaces[2][4][3][2]=1;
>  yzwfaces[3][4][2][2]=1;
>  xzwfaces[1][4][2][2]=1;xzwfaces[2][4][2][2]=1;
>  xzwfaces[1][5][2][2]=1;xzwfaces[2][5][2][2]=1;
>  xyzfaces[1][4][2][2]=1;xyzfaces[1][4][2][3]=1;xyzfaces[2][4][2][2]=1;
> 
>  /* Big Room Entrance */
>  yzwfaces[1][4][2][3]=1;
>  xyzfaces[1][4][2][3]=1;xyzfaces[3][4][2][3]=1;
>  for(ii=1;ii<4;ii++){
>    xywfaces[ii][4][2][3]=1;xywfaces[ii][4][3][3]=1;
>    xzwfaces[ii][4][2][3]=1;xzwfaces[ii][5][2][3]=1;
>    xyzfaces[ii][4][2][4]=1;}
>  yzwfaces[4][4][1][3]=1;yzwfaces[4][4][3][3]=1;
>  xywfaces[4][4][1][3]=1;xywfaces[4][4][4][3]=1;
>  for(kk=1;kk<4;kk++){
>    xzwfaces[4][4][kk][3]=1;xzwfaces[4][5][kk][3]=1;
>    xyzfaces[4][4][kk][3]=1;xyzfaces[4][4][kk][4]=1;
>  }
> 
>  /* Big Room centered at [6][4][2][3] */
>  /* Depart from convention ii,jj,kk - x,y,z */
>  for(ii=-1;ii<2;ii++)for(jj=-1;jj<2;jj++)for(kk=-1;kk<2;kk++){
>    xywfaces[6+ii][4+jj][1][3+kk]=1;xywfaces[6+ii][4+jj][4][3+kk]=1;
>    xzwfaces[6+ii][3][2+jj][3+kk]=1;xzwfaces[6+ii][6][2+jj][3+kk]=1;
>    xyzfaces[6+ii][4+jj][2+kk][2]=1;xyzfaces[6+ii][4+jj][2+kk][5]=1;
>    yzwfaces[5][4+ii][2+jj][3+kk]=1;yzwfaces[8][4+ii][2+jj][3+kk]=1;
>  }
>  for(kk=1;kk<4;kk++)yzwfaces[5][4][kk][3]=0;
>  xyzfaces[6][4][3][5]=0;
> 
>  /* Exit the Big Room */
>  yzwfaces[6][4][3][5]=1;yzwfaces[7][4][3][5]=1;
>  xzwfaces[6][4][3][5]=1;xzwfaces[6][5][3][5]=1;
>  xywfaces[6][4][3][5]=1;xywfaces[6][4][4][5]=1;
> 
>  yzwfaces[6][4][3][6]=1;yzwfaces[7][4][3][6]=1;
>  yzwfaces[6][4][4][6]=1;yzwfaces[7][4][4][6]=1;
> 
>  xzwfaces[6][4][3][6]=1;xzwfaces[6][5][3][6]=1;
>  xzwfaces[6][4][4][6]=1;xzwfaces[6][5][4][6]=1;
>  xywfaces[6][4][3][6]=1;
>  xyzfaces[6][4][3][7]=1;
>  xyzfaces[6][4][4][6]=1;xyzfaces[6][4][4][7]=1;
> 
>  /* The Canal */
>  /* [1][1][6][] */
>  /* Extraneous, and the point is mostly made already
>  for(ll=3;ll<6;ll++){
>    xywfaces[1][1][6][ll]=1;xywfaces[1][1][7][ll]=1;
>    yzwfaces[1][1][6][ll]=1;yzwfaces[2][1][6][ll]=1;
>    xzwfaces[1][1][6][ll]=1;
>  }
>  xyzfaces[1][1][6][3]=1;xyzfaces[1][1][6][6]=1;
>  */
> }
> 
> drawmaze(int LL, float del, int dir4[4],float siz){int ii,jj,kk;
> /*
>  if(LL==-1 || LL==ROWS){
>    glColor3f(1.0,1.0,1.0);glBegin(GL_POINTS);
>    glVertex3f(vv[0][0][0][0]-4.0,vv[0][0][0][1]-4.0,vv[0][0][0][2]-4.0);
>    glVertex3f(vv[0][0][ROWS][0]-4.0,vv[0][0][ROWS][1]-4.0,
> 	      vv[0][0][ROWS][2]+4.0);
>    glVertex3f(vv[0][ROWS][0][0]-4.0,vv[0][ROWS][0][1]+4.0,
> 	      vv[0][ROWS][0][2]-4.0);
>    glVertex3f(vv[0][ROWS][ROWS][0]-4.0,vv[0][ROWS][ROWS][1]+4.0,
> 	      vv[0][ROWS][ROWS][2]+4.0);
>    glVertex3f(vv[ROWS][0][0][0]+4.0,vv[ROWS][0][0][1]-4.0,
> 	      vv[ROWS][0][0][2]-4.0);
>    glVertex3f(vv[ROWS][0][ROWS][0]+4.0,vv[ROWS][0][ROWS][1]-4.0,
> 	      vv[ROWS][0][ROWS][2]+4.0);
>    glVertex3f(vv[ROWS][ROWS][0][0]+4.0,vv[ROWS][ROWS][0][1]+4.0,
> 	      vv[ROWS][ROWS][0][2]-4.0);
>    glVertex3f(vv[ROWS][ROWS][ROWS][0]+4.0,vv[ROWS][ROWS][ROWS][1]+4.0,
> 	      vv[ROWS][ROWS][ROWS][2]+4.0);
>    glEnd();}
> */
>  if(LL>=0 && LL<ROWS) {
>   if(dir4[3]!=0){
>    for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)for(kk=0;kk<ROWS1;kk++)
>     if (xywfaces[ii][jj][kk][LL]){
>      glBegin(GL_TRIANGLE_STRIP); /* bgntmesh();*/
>      glColor3f(0.,0.,1.0);glVertex3f(ADDXY(vv[ii][jj][kk],del,del,siz));
>      glColor3f(0.,0.,1.0);glVertex3f(ADDXY(vv[ii+1][jj][kk],-1*del,del,siz));
>      glColor3f(0.,0.,1.0);glVertex3f(ADDXY(vv[ii][jj+1][kk],del,-1*del,siz));
>      glColor3f(0.,0.,1.0);glVertex3f(ADDXY(vv[ii+1][jj+1][kk],-1*del,-1*del,siz));
>      glEnd(); /*endtmesh(); */}}
>   else{
>    for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)for(kk=0;kk<ROWS1;kk++)
>     if ((xyzfaces[ii][jj][LL][kk]&&(dir4[2]!=0))||
> 	(xyzfaces[ii][LL][jj][kk]&&(dir4[1]!=0))||
> 	(xyzfaces[LL][ii][jj][kk]&&(dir4[0]!=0))){
>      glBegin(GL_TRIANGLE_STRIP); /* bgntmesh();*/
>      glColor3f(1.0,1.0,0.);glVertex3f(ADDXY(vv[ii][jj][kk],del,del,siz));
>      glColor3f(1.0,1.0,0.);glVertex3f(ADDXY(vv[ii+1][jj][kk],-1*del,del,siz));
>      glColor3f(1.0,1.0,0.);glVertex3f(ADDXY(vv[ii][jj+1][kk],del,-1*del,siz));
>      glColor3f(1.0,1.0,0.);glVertex3f(ADDXY(vv[ii+1][jj+1][kk],-1*del,-1*del,siz));
>      glEnd(); /*endtmesh(); */}}
>   if(dir4[3]!=0 || dir4[2]!=0){
>    for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS1;jj++)for(kk=0;kk<ROWS;kk++)
>     if ((xzwfaces[ii][jj][kk][LL]&&(dir4[3]!=0))||
> 	(xzwfaces[ii][jj][LL][kk]&&(dir4[2]!=0))){
>      glBegin(GL_TRIANGLE_STRIP); 
>      glColor3f(0.,1.0,0.);glVertex3f(ADDXZ(vv[ii][jj][kk],del,del,siz));
>      glColor3f(0.,1.0,0.);glVertex3f(ADDXZ(vv[ii+1][jj][kk],-1*del,del,siz));
>      glColor3f(0.,1.0,0.);glVertex3f(ADDXZ(vv[ii][jj][kk+1],del,-1*del,siz));
>      glColor3f(0.,1.0,0.);glVertex3f(ADDXZ(vv[ii+1][jj][kk+1],-1*del,-1*del,siz));
>      glEnd();}}
>   else{
>    for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS1;jj++)for(kk=0;kk<ROWS;kk++)
>     if ((xywfaces[ii][LL][jj][kk]&&(dir4[1]!=0))||
> 	(xywfaces[LL][ii][jj][kk]&&(dir4[0]!=0))){
>      glBegin(GL_TRIANGLE_STRIP); 
>      glColor3f(0.,0.,1.0);glVertex3f(ADDXZ(vv[ii][jj][kk],del,del,siz));
>      glColor3f(0.,0.,1.0);glVertex3f(ADDXZ(vv[ii+1][jj][kk],-1*del,del,siz));
>      glColor3f(0.,0.,1.0);glVertex3f(ADDXZ(vv[ii][jj][kk+1],del,-1*del,siz));
>      glColor3f(0.,0.,1.0);glVertex3f(ADDXZ(vv[ii+1][jj][kk+1],-1*del,-1*del,siz));
>      glEnd();}}
>   if(dir4[0]==0){
>    for(ii=0;ii<ROWS1;ii++)for(jj=0;jj<ROWS;jj++)for(kk=0;kk<ROWS;kk++)
>     if ((yzwfaces[ii][jj][kk][LL]&&(dir4[3]!=0))||
> 	(yzwfaces[ii][jj][LL][kk]&&(dir4[2]!=0))||
> 	(yzwfaces[ii][LL][jj][kk]&&(dir4[1]!=0))){
>      glBegin(GL_TRIANGLE_STRIP);glColor3f(1.0,0.,0.);
>      glVertex3f(ADDYZ(vv[ii][jj][kk],del,del,siz));
>      glColor3f(1.0,0.,0.);glVertex3f(ADDYZ(vv[ii][jj+1][kk],-1*del,del,siz));
>      glColor3f(1.0,0.,0.);glVertex3f(ADDYZ(vv[ii][jj][kk+1],del,-1*del,siz));
>      glColor3f(1.0,0.,0.);glVertex3f(ADDYZ(vv[ii][jj+1][kk+1],-1*del,-1*del,siz));
>      glEnd();}}
>   else{
>    for(ii=0;ii<ROWS1;ii++)for(jj=0;jj<ROWS;jj++)for(kk=0;kk<ROWS;kk++)
>     if (xzwfaces[LL][ii][jj][kk]){
>      glBegin(GL_TRIANGLE_STRIP);glColor3f(0.,1.0,0.);
>      glVertex3f(ADDYZ(vv[ii][jj][kk],del,del,siz));
>      glColor3f(0.,1.0,0.);glVertex3f(ADDYZ(vv[ii][jj+1][kk],-1*del,del,siz));
>      glColor3f(0.,1.0,0.);glVertex3f(ADDYZ(vv[ii][jj][kk+1],del,-1*del,siz));
>      glColor3f(0.,1.0,0.);glVertex3f(ADDYZ(vv[ii][jj+1][kk+1],-1*del,-1*del,siz));
>      glEnd();}}
>   /* 99->ff for same appearance on both sides.  Now one is darker. */
>  }}
> 
> drawHmark(int II,int JJ,int KK,int thick,float siz){int ii,jj;float vH[6][3];
>  static float vo[6][3] = { { 1.0, 0.0, 0.0 },
> 			   { 0.0, 1.0, 0.0 },
> 			   { 0.0, 0.0, 1.0 }, /* a purple octehedron */
> 			   {-1.0, 0.0, 0.0 },
> 			   { 0.0,-1.0, 0.0 },
> 			   { 0.0, 0.0,-1.0 } };
>  static int fr[]= {0,1,2,0,4,2,3,1,5,3,4,5,0};
>  for(ii=0;ii<6;ii++)for(jj=0;jj<3;jj++)
>    vH[ii][jj]=siz*(vo[ii][jj]+4+vv[II][JJ][KK][jj]);
>   glLineWidth(thick);
>   glBegin(GL_LINE_STRIP); /*bgnline(); */ 
>   glColor3f(.3,0.,.3); for(ii=0;ii<13;ii++)glVertex3fv(vH[fr[ii]]); 
>   glEnd(); /* endline(); */
> }
> drawhmark(int II,int JJ,int KK,int thick,float siz){ /* a dark grey cube */
>   static int first=1; static float vq[8][3]; float vh[8][3]; int ii,jj;
>     static int fr[]= {0,1,5,1,3,7,3,2,6,2,0,4,5,7,6,4};
>     if(first){for(ii=0;ii<8;ii++)for(jj=0;jj<3;jj++)
>       vq[ii][jj]=(ii&(1<<jj))?.65:-.65; first=0;}
>     for(ii=0;ii<8;ii++)for(jj=0;jj<3;jj++)
>       vh[ii][jj]=siz*(vq[ii][jj]+4+vv[II][JJ][KK][jj]);
>     glLineWidth(thick);
>     glBegin(GL_LINE_STRIP); glColor3f(.3,.35,.3); 
>     for(ii=0;ii<16;ii++)glVertex3fv(vh[fr[ii]]); 
>     glEnd();
> }
> 
> /* MAY 1999 */
> drawportal(int II,int JJ,int KK,int thick, int whichway, float siz){
>   if(whichway==1)drawHmark(II,JJ,KK,thick,siz);
>   else if(whichway==-1)drawhmark(II,JJ,KK,thick,siz);
> }
> 
> /* MAY 1999 */
> /* could be defined when declared - could even be made const int arrays */
> void init_colors(void){
>   BASIC_BLUE[0]=0;BASIC_BLUE[1]=0;BASIC_BLUE[2]=1;
>   BASIC_GREEN[0]=0;BASIC_GREEN[1]=1;BASIC_GREEN[2]=0;
>   BASIC_RED[0]=1;BASIC_RED[1]=0;BASIC_RED[2]=0;
>   BASIC_YELLOW[0]=1;BASIC_YELLOW[1]=1;BASIC_YELLOW[2]=0;
>   BASIC_GREY[0]=.5;BASIC_GREY[1]=.5;BASIC_GREY[2]=.5;
> }
> 
> /* MAY 1999 */
> void init_edges(int moving_axes);
> /* later when shared variables he,etc. have been declared */
> 
> /* MAY 1999 */
> /* JUNE 1999:
>    If -ii,-jj,-kk are used, then 'del' in draw_intersections must be 
>    redone, too */
> #define setDD(ii,jj,kk,ss,moving_axes){ \
>   switch(moving_axes){ \
>   case 1: \
>   case 2: \
>   case 3: \
>     DD[0] = (ii+0.5+ss-ROWS/2.0)*8.0; \
>     DD[1] = (jj-ROWS/2.0)*8.0; \
>     DD[2] = (kk-ROWS/2.0)*8.0; \
>     break; \
>   case 4: \
>   case 5: \
>     DD[0] = (jj-ROWS/2.0)*8.0; \
>     DD[1] = (ii+0.5+ss-ROWS/2.0)*8.0; \
>     DD[2] = (kk-ROWS/2.0)*8.0; \
>     break; \
>   case 6: \
>     DD[0] = (jj-ROWS/2.0)*8.0; \
>     DD[1] = (kk-ROWS/2.0)*8.0; \
>     DD[2] = (ii+0.5+ss-ROWS/2.0)*8.0; \
>     break; \
>   } \
> } \
> 
> /* If or when I make this one into a macro, I have to be very careful with
>    the comments - like getting rid of all of them */
> 
> /* MAY 1999 */
> /* These could easily be 'define' */
> /* Note: Here rectangles have just one face and 4 vertices...
>    do I really need 2 faces and 6 vertices? */
>   /* JUNE 1999
>      del_div8 because, unlike init_vv & drawmaze, 
>      setDD & draw_intersections misses the factor of 8.0 */
>   /* FEB2000 FEB13
>      This print statement used to cause the console maze to screw up,
>      even to core dump - Why?
>      printf("%d ",oe1[II][JJ][kk][ll]);      */
> #define draw_otheredges(dd,old_dd,ss,II,JJ,moving_axes,del,siz) { \
>   int kk,ll; \
>   float del_div8 = del/8.0; \
>   if((dd+(2*del_div8))<old_dd){ \
>     for(kk=0;kk<ROWS1;kk++)for(ll=0;ll<ROWS;ll++) \
>       if(oe1[II][JJ][kk][ll]){ \
> 	glBegin(GL_TRIANGLE_STRIP); \
> 	glColor3f(oe1_color[0],oe1_color[1],oe1_color[2]); \
> 	setDD(dd+del_div8,kk,ll+del_div8,ss,moving_axes);glVertex3f(DDList); \
> 	setDD(old_dd-del_div8,kk,ll+del_div8,ss,moving_axes); \
>         glVertex3f(DDList); \
> 	setDD(dd+del_div8,kk,ll+1-del_div8,ss,moving_axes); \
>         glVertex3f(DDList); \
> 	setDD(old_dd-del_div8,kk,ll+1-del_div8,ss,moving_axes); \
>         glVertex3f(DDList); \
> 	glEnd(); \
>       } \
>     for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS1;ll++) \
>       if(oe2[II][JJ][kk][ll]){ \
> 	glBegin(GL_TRIANGLE_STRIP); \
> 	glColor3f(oe2_color[0],oe2_color[1],oe2_color[2]); \
> 	setDD(dd+del_div8,kk+del_div8,ll,ss,moving_axes);glVertex3f(DDList); \
> 	setDD(old_dd-del_div8,kk+del_div8,ll,ss,moving_axes); \
>         glVertex3f(DDList); \
> 	setDD(dd+del_div8,kk+1-del_div8,ll,ss,moving_axes); \
>         glVertex3f(DDList); \
> 	setDD(old_dd-del_div8,kk+1-del_div8,ll,ss,moving_axes); \
>         glVertex3f(DDList); \
> 	glEnd(); \
>       } \
>   } \
>   else if((old_dd+(2*del_div8))<dd){ \
>     for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS1;ll++) \
>       if(oe2[II][JJ][kk][ll]){ \
> 	glBegin(GL_TRIANGLE_STRIP); \
> 	glColor3f(oe2_color[0],oe2_color[1],oe2_color[2]); \
> 	setDD(dd-del_div8,kk+del_div8,ll,ss,moving_axes);glVertex3f(DDList); \
> 	setDD(old_dd+del_div8,kk+del_div8,ll,ss,moving_axes); \
> glVertex3f(DDList); \
> 	setDD(dd-del_div8,kk+1-del_div8,ll,ss,moving_axes); \
> glVertex3f(DDList); \
> 	setDD(old_dd+del_div8,kk+1-del_div8,ll,ss,moving_axes); \
> glVertex3f(DDList); \
> 	glEnd(); \
>       } \
>     for(kk=0;kk<ROWS1;kk++)for(ll=0;ll<ROWS;ll++) \
>       if(oe1[II][JJ][kk][ll]){ \
> 	glBegin(GL_TRIANGLE_STRIP); \
> 	glColor3f(oe1_color[0],oe1_color[1],oe1_color[2]); \
> 	setDD(dd-del_div8,kk,ll+del_div8,ss,moving_axes);glVertex3f(DDList); \
> 	setDD(old_dd+del_div8,kk,ll+del_div8,ss,moving_axes); \
> glVertex3f(DDList); \
> 	setDD(dd-del_div8,kk,ll+1-del_div8,ss,moving_axes); \
> glVertex3f(DDList); \
> 	setDD(old_dd+del_div8,kk,ll+1-del_div8,ss,moving_axes); \
> glVertex3f(DDList); \
> 	glEnd(); \
>       }     \
>   } \
> }
> 
> #define draw_hedge(dd,kk,ll,ss,moving_axes,del,siz) { \
>   float del_div8 = del/8.0; \
>   glBegin(GL_TRIANGLE_STRIP); \
>   glColor3f(he_color[0],he_color[1],he_color[2]); \
>   setDD(dd,kk+del_div8,ll+del_div8,ss,moving_axes);glVertex3f(DDList); \
>   setDD(dd,kk+1-del_div8,ll+del_div8,ss,moving_axes);glVertex3f(DDList); \
>   setDD(dd,kk+del_div8,ll+1-del_div8,ss,moving_axes);glVertex3f(DDList); \
>   setDD(dd,kk+1-del_div8,ll+1-del_div8,ss,moving_axes);glVertex3f(DDList); \
>   glEnd(); \
> }
> 
> #define draw_vedge(dd,kk,ll,ss,moving_axes,del,siz) { \
>   float del_div8 = del/8.0; \
>   glBegin(GL_TRIANGLE_STRIP); \
>   glColor3f(ve_color[0],ve_color[1],ve_color[2]); \
>   setDD(dd,kk+del_div8,ll+del_div8,ss,moving_axes);glVertex3f(DDList); \
>   setDD(dd,kk+1-del_div8,ll+del_div8,ss,moving_axes);glVertex3f(DDList); \
>   setDD(dd,kk+del_div8,ll+1-del_div8,ss,moving_axes);glVertex3f(DDList); \
>   setDD(dd,kk+1-del_div8,ll+1-del_div8,ss,moving_axes);glVertex3f(DDList); \
>   glEnd(); \
> } 
> 
> #define draw_hvedge(dd,kk,ll,ss,moving_axes,del,siz) { \
>   float del_div8 = del/8.0; \
>   glBegin(GL_TRIANGLE_STRIP); \
>   glColor3f(BASIC_GREY[0],BASIC_GREY[1],BASIC_GREY[2]); \
>   setDD(dd,kk+del_div8,ll+del_div8,ss,moving_axes);glVertex3f(DDList); \
>   setDD(dd,kk+1-del_div8,ll+del_div8,ss,moving_axes);glVertex3f(DDList); \
>   setDD(dd,kk+del_div8,ll+1-del_div8,ss,moving_axes);glVertex3f(DDList); \
>   setDD(dd,kk+1-del_div8,ll+1-del_div8,ss,moving_axes);glVertex3f(DDList); \
>   glEnd(); \
> }
> 
> /* MAY 1999: Changed */
> void draw_intersections(int ss, int tt, float theta, int moving_axes, float del);
> 
> /* Examples of useful geometric macros, use inliners in C++ */
> #define  MAX(x,y)         (((x)<(y))?(y):(x))
> #define  MIN(x,y)         (((x)<(y))?(x):(y))
> #define  FOR(a,b,c)       for((a)=(b);(a)<(c);(a)++)
> #define  DOT(p,q)         ((p)[0]*(q)[0]+(p)[1]*(q)[1]+(p)[2]*(q)[2])
> #define  NRM(p)           sqrt(DOT((p),(p)))
> #define  DG            M_PI/180
> #define  S(u)          fsin(u*DG)
> #define  C(u)          fcos(u*DG)
> #define  CLAMP(x,u,v) (x<u? u : (x>v ? v: x))
> 
> /* global variables ... add new ones on top of the stack */
> char which[20]; /* audio needs this */
> int audiohandle; /* for telling vss the name of the .aud file */
> float lux[3]={1.,2.,3.};             /*light source direction vector        */
> float lu[3];     /* this needs to be shared current light direction vector*/
> float mysiz,speed, torq, focal, far; /*console navigation variables         */
> int win = 2;                         /* full screen, use 0 for demand sized */
> unsigned int BUT,XX,YY,SHIF;     /* used in chaptrack gluttery           */ 
> int xt,yt;                       /* once was xt,yt,xm,ym for viewportery */
> int caveyes=0;                      /* rename? ----------> caveflag */
> int mode,morph,msg,binoc;            /* pretty global */
> int th0, th1, dth, ta0, ta1, dta;    /* torus parameters */
> /* some of center.c's variables follow: */
> /* JUNE 1999 many changes */
> int rand_maze;
> float XYZchance,XZWchance,YZWchance,XYWchance;
> char phrase[256];
> /* long mx,my,xorig,yorig,xcen,ycen;*/
> int cube,thick,rseed,moving,frozen,w_shift; 
> #define MIDHEIGHT 5
> 
> /* MAY 1999: New variables: */
> /* JUNE 1999: New variables: */
> /* should any of them be shared? */
> /* int rotating_in_4D; /* Boolean: chooses draw_maze on 0, 
> 		       draw_intersections on 1 */
> /*float theta,sign_theta; /* for the 4D rotation */
> /*int ActualAxes; /* 1: xzw->yzw, -1: yzw->xzw
> 		   2: xyw->yzw, -2: yzw->xyw
> 		   3: xyz->yzw, -3: yzw->xyz
> 		   4: xyw->xzw, -4: xzw->xyw
> 		   5: xyz->xzw, -5: xzw->xyz
> 		   6: xyz->xyw, -6: xyw->xyz */
> /* theta_jump is change in theta for the 4D rotation only */
> /* theta_jump & rot_jump MUST divide 90 for autotymer to work */
> /* And floats are binary an don't divide perfectly unless
>    expressible well in binary, like .5 .25 .125 & integers */
> /* It shouldn't be quite like that, but it is */
> #define theta_jump 1
> int speed3D=3;
> float rot_jump[7]={1,2,3,5,7.5,10,15};
> /* fore_jump MUST divide 4 (perfectly binary, as above) */
> float fore_jump[7]={.125,.25,.25,.5,1.,2.,4.};
> 
> /* FEB2000 autotymer not yet fixed if above ones change. */
> 
> /* FEB2000 integer? max theta_delay still works! */
> #define max_theta_delay 1
> 
> #define FLYMODE  (0)
> #define TURNMODE (1) 
> /* From center.c; IF, TOGGLE, PRESS, FLYMODE & TURNMODE renamed SkFOO */
> /* Let's use noosh's FLY/TURNMODE convention */
> #define  ABS(x)           (((x)<0)?-(x):(x))
> #define  SkIF(K)          if(getbutton(K))
> #define  SOAK(K)        while(getbutton(K))
> #define  SkTOGGLE(K,f)    SkIF(K){f = !f;SOAK(K);}
> #define  IFCLICK(i,K,a) {static flg=i; SkTOGGLE(K,flg); if(flg){a};}
> #define  ZDELTA         0xfffff
> #define  ROUND(x)         (floor(x+.5))
> #define  IFSHIFT        if(getbutton(LEFTSHIFTKEY)||getbutton(RIGHTSHIFTKEY))
> #define  SkPRESS(K,A,b)   SkIF(K){IFSHIFT{A;}else{b;}}
> #define  PRES_S(K,A,b)  SkIF(K){IFSHIFT{A;}else{b;};SOAK(K);}
> #define  CYCLER(K,f,m)  PRES_S((K), (f)=(((f)+(m)-1)%(m)), (f)=(++(f)%(m)) )
> #define  LF         (vert-=0.035)  /*as in line-feed */
> #define  LAB_L(s,u) sprintf(phrase,s,u); cmov2(horz,vert); charstr(phrase);
> 
> /*Shared memory variables as per Stuart Levy 1994 */  
> /* Unshared! */
>    int inter_ss, inter_tt;
>    int ActualAxes;
>    float Theta;
>    float sign_theta;
>    int rotating_in_4D;
>    int dir4[4];           /* taff[3,8,13,18], the hidden direction */
>    int location[4];
>    float gap;          /* gap/2 between walls */
>    float siz;         /*final scaling factor before projection      */  
>    int wnd;            /*wand or maus flag                           */
>    int gnd;            /*background color                            */
>    float lu[3];         /* current light direction */ 
>    int mauspaw ;      /*binary for mouse button state                     */
>    int th0 ;           /* since the autotymer changes these          */
>    int th1 ;           /* they need to be shared                      */
>    int ta0 ;           /*                                             */
>    int ta1 ;           /*                                             */
>    GLfloat aff[16],starmat[16]; /*affine matrices for object, stars*/
>    GLfloat taff[25],id[16]; /* 5D affine matrix */
>    int he[ROWS][ROWS1][ROWS][ROWS];
>    int ve[ROWS1][ROWS][ROWS][ROWS];
>    int oe1[ROWS][ROWS][ROWS1][ROWS];
>    int oe2[ROWS][ROWS][ROWS][ROWS1];
>    GLfloat he_color[3];
>    GLfloat ve_color[3];
>    GLfloat oe1_color[3];
>    GLfloat oe2_color[3];
> 
> /* MAR2000 Console version only! */
> GLfloat temp_aff[16];
> int temp_location[4];
> 
> /* these are used only in console mode      */
> int binoc;    /* flag                       */
> float nose;   /* to eye distance in console */
> 
> void init_edges(int moving_axes){
>   int ii,jj,kk,ll;
>   switch(moving_axes){
>   case 1: 
>     for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS1;jj++)
>       for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	he[ii][jj][kk][ll]=xzwfaces[ii][jj][kk][ll];
>     for(ii=0;ii<ROWS1;ii++)for(jj=0;jj<ROWS;jj++)
>       for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	ve[ii][jj][kk][ll]=yzwfaces[ii][jj][kk][ll];
>     for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)
>       for(kk=0;kk<ROWS1;kk++)for(ll=0;ll<ROWS;ll++)
> 	oe1[ii][jj][kk][ll]=xywfaces[ii][jj][kk][ll];
>     for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)
>       for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS1;ll++)
> 	oe2[ii][jj][kk][ll]=xyzfaces[ii][jj][kk][ll];
>     for(ii=0;ii<3;ii++){
>       he_color[ii]=BASIC_GREEN[ii];
>       ve_color[ii]=BASIC_RED[ii];
>       oe1_color[ii]=BASIC_BLUE[ii];
>       oe2_color[ii]=BASIC_YELLOW[ii];}
>     break;
>   case 2: 
>     for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS1;jj++)
>       for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	he[ii][jj][kk][ll]=xywfaces[ii][kk][jj][ll];
>     for(ii=0;ii<ROWS1;ii++)for(jj=0;jj<ROWS;jj++)
>       for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	ve[ii][jj][kk][ll]=yzwfaces[ii][kk][jj][ll];
>     for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)
>       for(kk=0;kk<ROWS1;kk++)for(ll=0;ll<ROWS;ll++)
> 	oe1[ii][jj][kk][ll]=xzwfaces[ii][kk][jj][ll];
>     for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)
>       for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS1;ll++)
> 	oe2[ii][jj][kk][ll]=xyzfaces[ii][kk][jj][ll];
>     for(ii=0;ii<3;ii++){
>       he_color[ii]=BASIC_BLUE[ii];
>       ve_color[ii]=BASIC_RED[ii];
>       oe1_color[ii]=BASIC_GREEN[ii];
>       oe2_color[ii]=BASIC_YELLOW[ii];}
>     break;
>   case 3: 
>     for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS1;jj++)
>       for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	he[ii][jj][kk][ll]=xyzfaces[ii][kk][ll][jj];
>     for(ii=0;ii<ROWS1;ii++)for(jj=0;jj<ROWS;jj++)
>       for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	ve[ii][jj][kk][ll]=yzwfaces[ii][kk][ll][jj];
>     for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)
>       for(kk=0;kk<ROWS1;kk++)for(ll=0;ll<ROWS;ll++)
> 	oe1[ii][jj][kk][ll]=xzwfaces[ii][kk][ll][jj];
>     for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)
>       for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS1;ll++)
> 	oe2[ii][jj][kk][ll]=xywfaces[ii][kk][ll][jj];
>     for(ii=0;ii<3;ii++){
>       he_color[ii]=BASIC_YELLOW[ii];
>       ve_color[ii]=BASIC_RED[ii];
>       oe1_color[ii]=BASIC_GREEN[ii];
>       oe2_color[ii]=BASIC_BLUE[ii];}
>     break;
>   case 4: 
>     for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS1;jj++)
>       for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	he[ii][jj][kk][ll]=xywfaces[kk][ii][jj][ll];
>     for(ii=0;ii<ROWS1;ii++)for(jj=0;jj<ROWS;jj++)
>       for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	ve[ii][jj][kk][ll]=xzwfaces[kk][ii][jj][ll];
>     for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)
>       for(kk=0;kk<ROWS1;kk++)for(ll=0;ll<ROWS;ll++)
> 	oe1[ii][jj][kk][ll]=yzwfaces[kk][ii][jj][ll];
>     for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)
>       for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS1;ll++)
> 	oe2[ii][jj][kk][ll]=xyzfaces[kk][ii][jj][ll];
>     for(ii=0;ii<3;ii++){
>       he_color[ii]=BASIC_BLUE[ii];
>       ve_color[ii]=BASIC_GREEN[ii];
>       oe1_color[ii]=BASIC_RED[ii];
>       oe2_color[ii]=BASIC_YELLOW[ii];}
>     break;
>   case 5: 
>     for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS1;jj++)
>       for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	he[ii][jj][kk][ll]=xyzfaces[kk][ii][ll][jj];
>     for(ii=0;ii<ROWS1;ii++)for(jj=0;jj<ROWS;jj++)
>       for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	ve[ii][jj][kk][ll]=xzwfaces[kk][ii][ll][jj];
>     for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)
>       for(kk=0;kk<ROWS1;kk++)for(ll=0;ll<ROWS;ll++)
> 	oe1[ii][jj][kk][ll]=yzwfaces[kk][ii][ll][jj];
>     for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)
>       for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS1;ll++)
> 	oe2[ii][jj][kk][ll]=xywfaces[kk][ii][ll][jj];
>     for(ii=0;ii<3;ii++){
>       he_color[ii]=BASIC_YELLOW[ii];
>       ve_color[ii]=BASIC_GREEN[ii];
>       oe1_color[ii]=BASIC_RED[ii];
>       oe2_color[ii]=BASIC_BLUE[ii];}
>     break;
>   case 6: 
>     for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS1;jj++)
>       for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	he[ii][jj][kk][ll]=xyzfaces[kk][ll][ii][jj];
>     for(ii=0;ii<ROWS1;ii++)for(jj=0;jj<ROWS;jj++)
>       for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	ve[ii][jj][kk][ll]=xywfaces[kk][ll][ii][jj];
>     for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)
>       for(kk=0;kk<ROWS1;kk++)for(ll=0;ll<ROWS;ll++)
> 	oe1[ii][jj][kk][ll]=yzwfaces[kk][ll][ii][jj];
>     for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)
>       for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS1;ll++)
> 	oe2[ii][jj][kk][ll]=xzwfaces[kk][ll][ii][jj];
>     for(ii=0;ii<3;ii++){
>       he_color[ii]=BASIC_YELLOW[ii];
>       ve_color[ii]=BASIC_BLUE[ii];
>       oe1_color[ii]=BASIC_RED[ii];
>       oe2_color[ii]=BASIC_GREEN[ii];}
>     break;
>   }
> }/* MAY 1999 */
> 
> void draw_intersections(int ss, int tt, float theta, int moving_axes,
> 		   float del) {
>   /* static int virgin=0; */
>   
>   float ii,jj,old_ii,old_jj,slope,tanTh,sinTh,cosTh,dd,old_dd;
>   int skip_many,kk,ll;
>   int II,JJ;/* the edge of the point ii,jj */
>   int h_or_v;  /* 1 if he, 2 if ve, 3 if both, 0 if neither */
>   tanTh = tan(theta);
>   sinTh = sin(theta);
>   cosTh = cos(theta);
>   
>   /* FEB 2000
>   printf("hetc: %d %d %d %d\t",he[1][2][4][2],ve[1][2][4][2],oe1[1][2][4][2],oe2[1][2][4][2]);
>   */
> 
>   /* JUNE 1999 EDIT
>   printf("\n theta = %f",theta);
>      JUNE 1999 EDIT */
> 
>   if(theta>0){
>     /* Draw edges orthogonal to the moving axes, but uh... */
>     /* Draw other line segments later, as intersecting 
>        points are drawn */
>     if(ss>=0 && tt>=0 && ss<ROWS && tt<ROWS){
>       if(1.0<=tanTh){
> 	old_jj=tt+1;
> 	old_ii=ss+0.5+(old_jj-tt-0.5)/tanTh;
> 	jj=tt;
> 	ii=ss+0.5+(jj-tt-0.5)/tanTh;
> 	old_dd=.5/sinTh;dd=-.5/sinTh;
> 	draw_otheredges(dd,old_dd,ss,ss,tt,moving_axes,del,siz);
>       }
>       else /*(slope=1.0)>tanTh*/{
> 	old_ii=ss;
> 	old_jj=tt+0.5+(old_ii-ss-0.5)*tanTh;
> 	ii=ss+1;
> 	jj=tt+0.5+(ii-ss-0.5)*tanTh;
> 	old_dd=.5/cosTh;dd=-.5/cosTh;
> 	draw_otheredges(dd,old_dd,ss,ss,tt,moving_axes,del,siz);
>       }  
>     }
>     /* Determine whether or not the lower left part of the line-of-sight
>        intersects the maze at all, and find the first intersection with
>        the border */
>     if(ss<0 || tt<0) skip_many=1;
>     else{
>       if(ss<ROWS && tt<ROWS){
> 	skip_many=0;
> 	if(1.0<=tanTh){
> 	  old_jj=jj=tt;
> 	  old_ii=ii=ss+0.5+(jj-tt-0.5)/tanTh;
> 	  old_dd=dd=-.5/sinTh;}
> 	else /*(slope=1.0)>tanTh*/{
> 	  old_ii=ii=ss;
> 	  old_jj=jj=tt+0.5+(ii-ss-0.5)*tanTh;
> 	  old_dd=dd=-.5/cosTh;}
>       }
>       else{            /* not in maze, but in first quadrant */
> 	ii=ss+0.5;jj=tt+0.5;
> 	if(ss>=ROWS && tt>=ROWS){
> 	  skip_many=0;
> 	  slope=jj/(ii-ROWS);/* location to lower right corner */
> 	  if(tanTh>slope)skip_many=1;
> 	  slope=(jj-ROWS)/ii;/* location to upper left corner */
> 	  if(tanTh<slope)skip_many=1;
> 	  if(skip_many==0){
> 	    slope=(jj-ROWS)/(ii-ROWS);
> 	    if(tanTh<slope){
> 	      old_jj=jj=ROWS;
> 	      old_ii=ii=ss+0.5-(tt+0.5-jj)/tanTh;
> 	      old_dd=dd=-(tt+0.5-jj)/sinTh;
> 	    }
> 	    else{ /* tanTh>=slope */
> 	      old_ii=ii=ROWS;
> 	      old_jj=jj=tt+0.5-(ss+0.5-ii)*tanTh;
> 	      old_dd=dd=-(ss+0.5-ii)/cosTh;
> 	    }
> 	  }
> 	}
> 	else if(ss<ROWS){ /* && tt>=ROWS */
> 	  slope=(jj-ROWS)/ii;/* location to upper left corner */
> 	  if(tanTh<slope)skip_many=1;
> 	  else{
> 	    skip_many=0;
> 	    old_jj=jj=ROWS;
> 	    old_ii=ii=ss+0.5-(tt+0.5-jj)/tanTh;
> 	    old_dd=dd=-(tt+0.5-jj)/sinTh;
> 	  }
> 	}
> 	else{ /* tt<ROWS && ss>=ROWS */
> 	  slope=jj/(ii-ROWS);/* location to lower right corner */
> 	  if(tanTh>slope)skip_many=1;	
> 	  else{
> 	    skip_many=0;
> 	    old_ii=ii=ROWS;
> 	    old_jj=jj=tt+0.5-(ss+0.5-ii)*tanTh;
> 	    old_dd=dd=-(ss+0.5-ii)/cosTh;
> 	  }
> 	}
>       }
>     } /* determined skip_many and found first intersection point */
>     
>     if(!skip_many){   /* Draw the lower left intersection points 
> 			 and segments */
>       /***
> 	  if(!virgin)printf("tanTh=%f\n",tanTh);
> 	  if(!virgin)printf("ii=%f jj=%f\n",ii,jj);
>       ***/
>       
>       /* Draw the first interection point */
>       if(ii==ceil(ii) && jj==ceil(jj)){/* hits a corner */
> 	II = (int)ii;JJ=(int)jj;
> 	for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++){
> 	  if(ii==ROWS) h_or_v = he[II-1][JJ][kk][ll];
> 	  else if(ii==0) h_or_v = he[II][JJ][kk][ll];
> 	  else h_or_v = ((he[II][JJ][kk][ll] || he[II-1][JJ][kk][ll])?1:0);
> 	  if(jj==ROWS) h_or_v += 2*ve[II][JJ-1][kk][ll];
> 	  else if(jj==0) h_or_v += 2*ve[II][JJ][kk][ll];
> 	  else h_or_v += 2*((ve[II][JJ][kk][ll]||ve[II][JJ-1][kk][ll])?1:0);
> 	  switch(h_or_v){
> 	  case 1: draw_hedge(dd,kk,ll,ss,moving_axes,del,siz);break;
> 	  case 2: draw_vedge(dd,kk,ll,ss,moving_axes,del,siz);break;
> 	  case 3: draw_hvedge(dd,kk,ll,ss,moving_axes,del,siz);break;
> 	  }
> 	}
>       }
>       else if(jj==ceil(jj)){ /* point on horizontal edge */
> 	II=(int)floor(ii); JJ=(int)jj;
> 	for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	  if(he[II][JJ][kk][ll])
> 	    draw_hedge(dd,kk,ll,ss,moving_axes,del,siz);
>       }
>       else{                  /* point on a vertical edge */
> 	II=(int)ii; JJ=(int)floor(jj);
> 	for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	  if(ve[II][JJ][kk][ll])
> 	    draw_vedge(dd,kk,ll,ss,moving_axes,del,siz);
>       }
>       /* Drew the first intersection point */
>       
>       while(ii>0 && jj>0){  /* lower left interior intersection points */
> 	/***
> 	    if(!virgin)printf("while: ii=%f jj=%f",ii,jj);
> 	***/
> 	slope = (jj-ceil(jj)+1)/(ii-ceil(ii)+1);
> 	/***
> 	    if(!virgin)printf(" slope=%f\n",slope);
> 	***/
> 	if(slope==tanTh){     /* hits a corner, which is unlikely, but....
> 				 Up to 2 vert edges and 2 horiz edges to check */
> 	  ii = ceil(ii)-1; jj = ceil(jj)-1;II = (int)ii;JJ=(int)jj;
> 	  dd=-(ss+.5-ii)/cosTh;
> 	  for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++){
> 	    if(ii==0) h_or_v = he[II][JJ][kk][ll];
> 	    else h_or_v = ((he[II][JJ][kk][ll] || he[II-1][JJ][kk][ll])?1:0);
> 	    if(jj==0) h_or_v += 2*ve[II][JJ][kk][ll];
> 	    else h_or_v += 2*((ve[II][JJ][kk][ll]||ve[II][JJ-1][kk][ll])?1:0);
> 	    switch(h_or_v){
> 	    case 1: draw_hedge(dd,kk,ll,ss,moving_axes,del,siz);break;
> 	    case 2: draw_vedge(dd,kk,ll,ss,moving_axes,del,siz);break;
> 	    case 3: draw_hvedge(dd,kk,ll,ss,moving_axes,del,siz);break;
> 	    }
> 	  }
> 	}
> 	else if(slope<tanTh){ /* hits a horizontal edge */
> 	  jj=ceil(jj)-1;
> 	  ii=ss+0.5+(jj-tt-0.5)/tanTh;
> 	  dd=-(tt+.5-jj)/sinTh;
> 	  II=(int)floor(ii); JJ=(int)jj;
> 	  for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	    if(he[II][JJ][kk][ll])
> 	      draw_hedge(dd,kk,ll,ss,moving_axes,del,siz);
> 	}
> 	else /*slope>tanTh*/{ /* hits a vertical edge */
> 	  ii=ceil(ii)-1;
> 	  jj=tt+0.5+(ii-ss-0.5)*tanTh;
> 	  dd=-(ss+.5-ii)/cosTh;
> 	  II=(int)ii; JJ=(int)floor(jj);
> 	  for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	    if(ve[II][JJ][kk][ll])
> 	      draw_vedge(dd,kk,ll,ss,moving_axes,del,siz);
> 	}
> 	/* DrawOtherColoredLineSomewhere */
> 	II=floor((ii+old_ii)/2.0);
> 	JJ=floor((jj+old_jj)/2.0);
> 	draw_otheredges(dd,old_dd,ss,II,JJ,moving_axes,del,siz);
> 	old_ii=ii;old_jj=jj;old_dd=dd;
>       }
>     }
> 
>     /* SAME rigamarole for upper right points and segments */
>     if(ss>=ROWS || tt>=ROWS) skip_many=1;
>     else{
>       if(ss>=0 && tt>=0){
> 	skip_many=0;
> 	if(1.0<=tanTh){
> 	  old_jj=jj=tt+1;
> 	  old_ii=ii=ss+0.5+(jj-tt-0.5)/tanTh;
> 	  old_dd=dd=.5/sinTh;}
> 	else /*(slope=1.0)>tanTh*/{
> 	  old_ii=ii=ss+1;
> 	  old_jj=jj=tt+0.5+(ii-ss-0.5)*tanTh;
> 	  old_dd=dd=.5/cosTh;}
>       }
>       else{            /* not in maze, but in first quadrant */
> 	ii=ss+0.5;jj=tt+0.5;
> 	if(ss<0 && tt<0){
> 	  skip_many=0;
> 	  slope=jj/(ii-ROWS);/* location to lower right corner */
> 	  if(tanTh<slope)skip_many=1;
> 	  slope=(jj-ROWS)/ii;/* location to upper left corner */
> 	  if(tanTh>slope)skip_many=1;
> 	  if(skip_many==0){
> 	    slope=(jj)/(ii);
> 	    if(tanTh<slope){
> 	      old_jj=jj=0;
> 	      old_ii=ii=ss+0.5-(tt+0.5-jj)/tanTh;
> 	      old_dd=dd=(jj-(tt+0.5))/sinTh;
> 	    }
> 	    else{ /* tanTh>=slope */
> 	      old_ii=ii=0;
> 	      old_jj=jj=tt+0.5-(ss+0.5-ii)*tanTh;
> 	      old_dd=dd=(ii-(ss+0.5))/cosTh;
> 	    }
> 	  }
> 	}
> 	else if(ss>=0){ /* && tt<=0 */
> 	  slope=jj/(ii-ROWS);/* location to lower right corner */
> 	  if(tanTh<slope)skip_many=1;
> 	  else{
> 	    skip_many=0;
> 	    old_jj=jj=0;
> 	    old_ii=ii=ss+0.5-(tt+0.5-jj)/tanTh;
> 	    old_dd=dd=-(tt+0.5-jj)/sinTh;
> 	  }
> 	}
> 	else{ /* tt>0 && ss<=0 */
> 	  slope=(jj-ROWS)/ii;/* location to upper left corner */
> 	  if(tanTh>slope)skip_many=1;	
> 	  else{
> 	    skip_many=0;
> 	    old_ii=ii=0;
> 	    old_jj=jj=tt+0.5-(ss+0.5-ii)*tanTh;
> 	    old_dd=dd=-(ss+0.5-ii)/cosTh;
> 	  }
> 	}
>       }
>     } /* determined skip_many and found first intersection point */
>     
>     if(!skip_many){   /* Draw the upper right intersection points 
> 			 and segments */
>       /* Draw the first point */
>       if(ii==ceil(ii) && jj==ceil(jj)){/* hits a corner */
> 	II = (int)ii;JJ=(int)jj;
> 	for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++){
> 	  if(ii==ROWS) h_or_v = he[II-1][JJ][kk][ll];
> 	  else if(ii==0) h_or_v = he[II][JJ][kk][ll];
> 	  else h_or_v = ((he[II][JJ][kk][ll] || he[II-1][JJ][kk][ll])?1:0);
> 	  if(jj==ROWS) h_or_v += 2*ve[II][JJ-1][kk][ll];
> 	  else if(jj==0) h_or_v += 2*ve[II][JJ][kk][ll];
> 	  else h_or_v += 2*((ve[II][JJ][kk][ll]||ve[II][JJ-1][kk][ll])?1:0);
> 	  switch(h_or_v){
> 	  case 1: draw_hedge(dd,kk,ll,ss,moving_axes,del,siz);break;
> 	  case 2: draw_vedge(dd,kk,ll,ss,moving_axes,del,siz);break;
> 	  case 3: draw_hvedge(dd,kk,ll,ss,moving_axes,del,siz);break;
> 	  }
> 	}
>       }
>       else if(jj==ceil(jj)){ /* point on horizontal edge */
> 	II=(int)floor(ii); JJ=(int)jj;
> 	for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	  if(he[II][JJ][kk][ll])
> 	    draw_hedge(dd,kk,ll,ss,moving_axes,del,siz);
>       }
>       else{                  /* point on a vertical edge */
> 	II=(int)ii; JJ=(int)floor(jj);
> 	for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	  if(ve[II][JJ][kk][ll])
> 	    draw_vedge(dd,kk,ll,ss,moving_axes,del,siz);
>       }
>       /* Drew the first point */
>       
>       while(ii<ROWS && jj<ROWS){  /* upper right intersection points */
> 	slope = (jj-floor(jj)-1)/(ii-floor(ii)-1);
> 	if(slope==tanTh){
> 	  ii = floor(ii)+1; jj = floor(jj)+1;II = (int)ii;JJ=(int)jj;
> 	  dd=-(ss+.5-ii)/cosTh;
> 	  for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++){
> 	    if(ii==ROWS) h_or_v = he[II-1][JJ][kk][ll];
> 	    else h_or_v = ((he[II][JJ][kk][ll] || he[II-1][JJ][kk][ll])?1:0);
> 	    if(jj==ROWS) h_or_v += 2*ve[II][JJ-1][kk][ll];
> 	    else h_or_v += 2*((ve[II][JJ][kk][ll]||ve[II][JJ-1][kk][ll])?1:0);
> 	    switch(h_or_v){
> 	    case 1: draw_hedge(dd,kk,ll,ss,moving_axes,del,siz);break;
> 	    case 2: draw_vedge(dd,kk,ll,ss,moving_axes,del,siz);break;
> 	    case 3: draw_hvedge(dd,kk,ll,ss,moving_axes,del,siz);break;
> 	    }
> 	  }
> 	}
> 	else if(slope<tanTh){ /* hits a horizontal edge */
> 	  jj=floor(jj)+1;
> 	  ii=ss+0.5+(jj-tt-0.5)/tanTh;
> 	  dd=-(tt+.5-jj)/sinTh;
> 	  II=(int)floor(ii); JJ=(int)jj;
> 	  for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	    if(he[II][JJ][kk][ll])
> 	      draw_hedge(dd,kk,ll,ss,moving_axes,del,siz);
> 	}
> 	else /*slope>tanTh*/{ /* hits a vertical edge */
> 	  ii=floor(ii)+1;
> 	  jj=tt+0.5+(ii-ss-0.5)*tanTh;
> 	  dd=-(ss+.5-ii)/cosTh;
> 	  II=(int)ii; JJ=(int)floor(jj);
> 	  for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	    if(ve[II][JJ][kk][ll])
> 	      draw_vedge(dd,kk,ll,ss,moving_axes,del,siz);
> 	}
> 	/* DrawOtherColoredLineSomewhere */
> 	II=floor((ii+old_ii)/2.0);
> 	JJ=floor((jj+old_jj)/2.0);
> 	draw_otheredges(dd,old_dd,ss,II,JJ,moving_axes,del,siz);
> 	old_ii=ii;old_jj=jj;old_dd=dd;
>       }
>     }
>     /* virgin=1; */
>   }
>   
>   if(theta<0){
>     /* Draw line segment containing location */
>     /* Draw other line segments later, as intersecting 
>        points are drawn */
>     if(ss>=0 && tt>=0 && ss<ROWS && tt<ROWS){
>       if(-1.0>=tanTh){
> 	old_jj=tt+1;
> 	old_ii=ss+0.5+(old_jj-tt-0.5)/tanTh;      
> 	jj=tt;
> 	ii=ss+0.5+(jj-tt-0.5)/tanTh;
> 	old_dd=.5/sinTh;dd=-.5/sinTh;
> 	draw_otheredges(dd,old_dd,ss,ss,tt,moving_axes,del,siz);
>       }
>       else /*(slope=-1.0)<tanTh*/{
> 	old_ii=ss;
> 	old_jj=tt+0.5+(old_ii-ss-0.5)*tanTh;
> 	ii=ss+1;
> 	jj=tt+0.5+(ii-ss-0.5)*tanTh;
> 	old_dd=.5/cosTh;dd=-.5/cosTh;
> 	draw_otheredges(dd,old_dd,ss,ss,tt,moving_axes,del,siz);
>       }
>     }  
>     
>     /* Determine whether or not the upper left part of the line-of-sight
>        intersects the maze at all, and find the first intersection with
>        the border */
>     if(ss<0 || tt>=ROWS) skip_many=1;
>     else{
>       if(ss<ROWS && tt>=0){
> 	skip_many=0;
> 	if(-1.0>=tanTh){
> 	  old_jj=jj=tt+1;
> 	  old_ii=ii=ss+0.5+(jj-tt-0.5)/tanTh;
> 	  old_dd=dd=.5/sinTh;}
> 	else /*(slope=-1.0)<tanTh*/{
> 	  old_ii=ii=ss;
> 	  old_jj=jj=tt+0.5+(ii-ss-0.5)*tanTh;
> 	  old_dd=dd=-.5/cosTh;}
>       }
>       else{            /* not in maze, but in lower rightish aprt */
> 	ii=ss+0.5;jj=tt+0.5;
> 	if(ss>=ROWS && tt<0){
> 	  skip_many=0;
> 	  slope=(jj-ROWS)/(ii-ROWS);/* location to upper right corner */
> 	  if(tanTh<slope)skip_many=1;
> 	  slope=jj/ii;/* location to lower left corner */
> 	  if(tanTh>slope)skip_many=1;
> 	  if(skip_many==0){
> 	    slope=jj/(ii-ROWS);
> 	    if(tanTh>slope){
> 	      old_jj=jj=0;
> 	      old_ii=ii=ss+0.5-(tt+0.5-jj)/tanTh;
> 	      old_dd=dd=-(tt+0.5-jj)/sinTh;
> 	    }
> 	    else{ /* tanTh<=slope */
> 	      old_ii=ii=ROWS;
> 	      old_jj=jj=tt+0.5-(ss+0.5-ii)*tanTh;
> 	      old_dd=dd=-(ss+0.5-ii)/cosTh;
> 	    }
> 	  }
> 	}
> 	else if(ss<ROWS){ /* && tt<0 */
> 	  slope=jj/ii;/* location to lower left corner */
> 	  if(tanTh>slope)skip_many=1;
> 	  else{
> 	    skip_many=0;
> 	    old_jj=jj=0;
> 	    old_ii=ii=ss+0.5-(tt+0.5-jj)/tanTh;
> 	    old_dd=dd=-(tt+0.5-jj)/sinTh;
> 	  }
> 	}
> 	else{ /* tt>=0 && ss>=ROWS */
> 	  slope=(jj-ROWS)/(ii-ROWS);/* location to upper right corner */
> 	  if(tanTh<slope)skip_many=1;	
> 	  else{
> 	    skip_many=0;
> 	    old_ii=ii=ROWS;
> 	    old_jj=jj=tt+0.5-(ss+0.5-ii)*tanTh;
> 	    old_dd=dd=-(ss+0.5-ii)/cosTh;
> 	  }
> 	}
>       }
>     } /* determined skip_many and found first intersection point */
> 
>     if(!skip_many){   /* Draw the upper left intersection points 
> 			 and segments */
>       /* Draw the first intersection point */
>       /* no change here due to sign of theta */
>       if(ii==ceil(ii) && jj==ceil(jj)){/* hits a corner */
> 	II = (int)ii;JJ=(int)jj;
> 	for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++){
> 	  if(ii==ROWS) h_or_v = he[II-1][JJ][kk][ll];
> 	  else if(ii==0) h_or_v = he[II][JJ][kk][ll];
> 	  else h_or_v = ((he[II][JJ][kk][ll] || he[II-1][JJ][kk][ll])?1:0);
> 	  if(jj==ROWS) h_or_v += 2*ve[II][JJ-1][kk][ll];
> 	  else if(jj==0) h_or_v += 2*ve[II][JJ][kk][ll];
> 	  else h_or_v += 2*((ve[II][JJ][kk][ll]||ve[II][JJ-1][kk][ll])?1:0);
> 	  switch(h_or_v){
> 	  case 1: draw_hedge(dd,kk,ll,ss,moving_axes,del,siz);break;
> 	  case 2: draw_vedge(dd,kk,ll,ss,moving_axes,del,siz);break;
> 	  case 3: draw_hvedge(dd,kk,ll,ss,moving_axes,del,siz);break;
> 	  }
> 	}
>       }
>       else if(jj==ceil(jj)){ /* point on horizontal edge */
> 	II=(int)floor(ii); JJ=(int)jj;
> 	for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	  if(he[II][JJ][kk][ll])
> 	    draw_hedge(dd,kk,ll,ss,moving_axes,del,siz);
>       }
>       else{                  /* point on a vertical edge */
> 	II=(int)ii; JJ=(int)floor(jj);
> 	for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	  if(ve[II][JJ][kk][ll])
> 	    draw_vedge(dd,kk,ll,ss,moving_axes,del,siz);
>       }
>       /* Drew the first intersection point */
>       
>       while(ii>0 && jj<ROWS){  /* upper left interior intersection points */
> 	slope = (jj-floor(jj)-1)/(ii-ceil(ii)+1);
> 	if(slope==tanTh){     /* hits a corner, which is unlikely, but....
> 				 Up to 2 vert edges and 2 horiz edges to check */
> 	  ii = ceil(ii)-1; jj = floor(jj)+1;II = (int)ii;JJ=(int)jj;
> 	  dd=-(ss+.5-ii)/cosTh;
> 	  for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++){
> 	    if(ii==0) h_or_v = he[II][JJ][kk][ll];
> 	    else h_or_v = ((he[II][JJ][kk][ll] || he[II-1][JJ][kk][ll])?1:0);
> 	    if(jj==ROWS) h_or_v += 2*ve[II][JJ-1][kk][ll];
> 	    else h_or_v += 2*((ve[II][JJ][kk][ll]||ve[II][JJ-1][kk][ll])?1:0);
> 	    switch(h_or_v){
> 	    case 1: draw_hedge(dd,kk,ll,ss,moving_axes,del,siz);break;
> 	    case 2: draw_vedge(dd,kk,ll,ss,moving_axes,del,siz);break;
> 	    case 3: draw_hvedge(dd,kk,ll,ss,moving_axes,del,siz);break;
> 	    }
> 	  }
> 	}
> 	else if(slope>tanTh){ /* hits a horizontal edge */
> 	  jj=floor(jj)+1;
> 	  ii=ss+0.5+(jj-tt-0.5)/tanTh;
> 	  dd=-(tt+.5-jj)/sinTh;
> 	  II=(int)floor(ii); JJ=(int)jj;
> 	  for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	    if(he[II][JJ][kk][ll])
> 	      draw_hedge(dd,kk,ll,ss,moving_axes,del,siz);
> 	}
> 	else /*slope<tanTh*/{ /* hits a vertical edge */
> 	  ii=ceil(ii)-1;
> 	  jj=tt+0.5+(ii-ss-0.5)*tanTh;
> 	  dd=-(ss+.5-ii)/cosTh;
> 	  II=(int)ii; JJ=(int)floor(jj);
> 	  for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	    if(ve[II][JJ][kk][ll])
> 	      draw_vedge(dd,kk,ll,ss,moving_axes,del,siz);
> 	}
> 	/* DrawOtherColoredLineSomewhere */
> 	II=floor((ii+old_ii)/2.0);
> 	JJ=floor((jj+old_jj)/2.0);
> 	draw_otheredges(dd,old_dd,ss,II,JJ,moving_axes,del,siz);
> 	old_ii=ii;old_jj=jj;old_dd=dd;
>       }
>     }
>     
>     /* SAME rigamarole for lower right points and segments */
>     if(ss>=ROWS || tt<0) skip_many=1;
>     else{
>       if(ss>=0 && tt<ROWS){
> 	skip_many=0;
> 	if(-1.0>=tanTh){
> 	  old_jj=jj=tt;
> 	  old_ii=ii=ss+0.5+(jj-tt-0.5)/tanTh;
> 	  old_dd=dd=-.5/sinTh;}
> 	else /*(slope=-1.0)<tanTh*/{
> 	  old_ii=ii=ss+1;
> 	  old_jj=jj=tt+0.5+(ii-ss-0.5)*tanTh;
> 	  old_dd=dd=.5/cosTh;}
>       }
>       else{            /* not in maze, but in upper leftish part */
> 	ii=ss+0.5;jj=tt+0.5;
> 	if(ss<0 && tt>=ROWS){
> 	  skip_many=0;
> 	  slope=(jj-ROWS)/(ii-ROWS);/* location to upper right corner */
> 	  if(tanTh>slope)skip_many=1;
> 	  slope=jj/ii;/* location to lower left corner */
> 	  if(tanTh<slope)skip_many=1;
> 	  if(skip_many==0){
> 	    slope=(jj-ROWS)/(ii);
> 	    if(tanTh>slope){
> 	      old_jj=jj=ROWS;
> 	      old_ii=ii=ss+0.5-(tt+0.5-jj)/tanTh;
> 	      old_dd=dd=(jj-(tt+0.5))/sinTh;
> 	    }
> 	    else{ /* tanTh<=slope */
> 	      old_ii=ii=0;
> 	      old_jj=jj=tt+0.5-(ss+0.5-ii)*tanTh;
> 	      old_dd=dd=(ii-(ss+0.5))/cosTh;
> 	    }
> 	  }
> 	}
> 	else if(ss>=0){ /* && tt>=ROWS */
> 	  slope=(jj-ROWS)/(ii-ROWS);/* location to upper right corner */
> 	  if(tanTh>slope)skip_many=1;
> 	  else{
> 	    skip_many=0;
> 	    old_jj=jj=ROWS;
> 	    old_ii=ii=ss+0.5-(tt+0.5-jj)/tanTh;
> 	    old_dd=dd=-(tt+0.5-jj)/sinTh;
> 	  }
> 	}
> 	else{ /* tt<ROWS && ss<0 */
> 	  slope=jj/ii;/* location to lower left corner */
> 	  if(tanTh<slope)skip_many=1;	
> 	  else{
> 	    skip_many=0;
> 	    old_ii=ii=0;
> 	    old_jj=jj=tt+0.5-(ss+0.5-ii)*tanTh;
> 	    old_dd=dd=-(ss+0.5-ii)/cosTh;
> 	  }
> 	}
>       }
>     } /* determined skip_many and found first intersection point */
>     
>     if(!skip_many){   /* Draw the lower right intersection points 
> 			 and segments */
>       /* Draw the first point */
>       if(ii==ceil(ii) && jj==ceil(jj)){/* hits a corner */
> 	II = (int)ii;JJ=(int)jj;
> 	for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++){
> 	  if(ii==ROWS) h_or_v = he[II-1][JJ][kk][ll];
> 	  else if(ii==0) h_or_v = he[II][JJ][kk][ll];
> 	  else h_or_v = ((he[II][JJ][kk][ll] || he[II-1][JJ][kk][ll])?1:0);
> 	  if(jj==ROWS) h_or_v += 2*ve[II][JJ-1][kk][ll];
> 	  else if(jj==0) h_or_v += 2*ve[II][JJ][kk][ll];
> 	  else h_or_v += 2*((ve[II][JJ][kk][ll]||ve[II][JJ-1][kk][ll])?1:0);
> 	  switch(h_or_v){
> 	  case 1: draw_hedge(dd,kk,ll,ss,moving_axes,del,siz);break;
> 	  case 2: draw_vedge(dd,kk,ll,ss,moving_axes,del,siz);break;
> 	  case 3: draw_hvedge(dd,kk,ll,ss,moving_axes,del,siz);break;
> 	  }
> 	}
>       }
>       else if(jj==ceil(jj)){ /* point on horizontal edge */
> 	II=(int)floor(ii); JJ=(int)jj;
> 	for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	  if(he[II][JJ][kk][ll])
> 	    draw_hedge(dd,kk,ll,ss,moving_axes,del,siz);
>       }
>       else{                  /* point on a vertical edge */
> 	II=(int)ii; JJ=(int)floor(jj);
> 	for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	  if(ve[II][JJ][kk][ll])
> 	    draw_vedge(dd,kk,ll,ss,moving_axes,del,siz);
>       }
>       /* Drew the first point */
>       
>       while(ii<ROWS && jj>0){  /* lower right intersection points */
> 	slope = (jj-ceil(jj)+1)/(ii-floor(ii)-1);
> 	if(slope==tanTh){
> 	  ii = floor(ii)+1; jj = ceil(jj)-1;II = (int)ii;JJ=(int)jj;
> 	  II = (int)ii;JJ=(int)jj;
> 	  dd=-(ss+.5-ii)/cosTh;
> 	  for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++){
> 	    if(ii==ROWS) h_or_v = he[II-1][JJ][kk][ll];
> 	    else h_or_v = ((he[II][JJ][kk][ll] || he[II-1][JJ][kk][ll])?1:0);
> 	    if(jj==0) h_or_v += 2*ve[II][JJ][kk][ll];
> 	    else h_or_v += 2*((ve[II][JJ][kk][ll]||ve[II][JJ-1][kk][ll])?1:0);
> 	    switch(h_or_v){
> 	    case 1: draw_hedge(dd,kk,ll,ss,moving_axes,del,siz);break;
> 	    case 2: draw_vedge(dd,kk,ll,ss,moving_axes,del,siz);break;
> 	    case 3: draw_hvedge(dd,kk,ll,ss,moving_axes,del,siz);break;
> 	    }
> 	  }
> 	}
> 	else if(slope>tanTh){ /* hits a horizontal edge */
> 	  jj=ceil(jj)-1;
> 	  ii=ss+0.5+(jj-tt-0.5)/tanTh;
> 	  dd=-(tt+.5-jj)/sinTh;
> 	  II=(int)floor(ii); JJ=(int)jj;
> 	  for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	    if(he[II][JJ][kk][ll])
> 	      draw_hedge(dd,kk,ll,ss,moving_axes,del,siz);
> 	}
> 	else /*slope<tanTh*/{ /* hits a vertical edge */
> 	  ii=floor(ii)+1;
> 	  jj=tt+0.5+(ii-ss-0.5)*tanTh;
> 	  dd=-(ss+.5-ii)/cosTh;
> 	  II=(int)ii; JJ=(int)floor(jj);
> 	  for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS;ll++)
> 	    if(ve[II][JJ][kk][ll])
> 	      draw_vedge(dd,kk,ll,ss,moving_axes,del,siz);
> 	}
> 	/* DrawOtherColoredLineSomewhere */
> 	II=floor((ii+old_ii)/2.0);
> 	JJ=floor((jj+old_jj)/2.0);
> 	draw_otheredges(dd,old_dd,ss,II,JJ,moving_axes,del,siz);
> 	old_ii=ii;old_jj=jj;old_dd=dd;
>       }
>     }
>   }
> }
> 
> /* FEB 2000 */
> void set_inter_vars(){
>   switch(abs(ActualAxes)){
>   case 1:inter_ss=location[0];inter_tt=location[1];break;
>   case 2:inter_ss=location[0];inter_tt=location[2];break;
>   case 3:inter_ss=location[0];inter_tt=location[3];break;
>   case 4:inter_ss=location[1];inter_tt=location[2];break;
>   case 5:inter_ss=location[1];inter_tt=location[3];break;
>   case 6:inter_ss=location[2];inter_tt=location[3];break;
>   }
> }
> 
> /* For diagnostic purposes */
> void showaff(){int ii,jj;printf("\n"); for(ii=0; ii<4; ii++){int i=ii*4;
>  printf(" %f %f %f %f \n",aff[i],aff[i+1],aff[i+2],aff[i+3]);}
>  printf("\t\tlocation");
>  for(ii=0;ii<4;ii++)printf(" %d",location[ii]);
>  printf("\tdir4");
>  for(ii=0;ii<4;ii++)printf(" %d",dir4[ii]);
>  printf(" \n");
> }
> void showtaff(){int ii,jj;printf("\n"); for(ii=0; ii<5; ii++){int i=ii*5;
>  printf(" %f %f %f %f %f \n",taff[i],taff[i+1],taff[i+2],taff[i+3],taff[i+4]);}
> }
> /* JUNE 1999 */
> void showAA(){printf("\nActualAxes=%i",ActualAxes);}
> 
> void roundaff(){int ii;
>  aff[12]/=siz;aff[13]/=siz;aff[14]/=siz;
>  for(ii=0;ii<16;ii++)aff[ii]=ROUND(aff[ii]);
>  aff[12]*=siz;aff[13]*=siz;aff[14]*=siz;
> } 
> void roundtaff(){int ii;
>  taff[20]/=siz;taff[21]/=siz;taff[22]/=siz; 
>  for(ii=0;ii<25;ii++)taff[ii]=ROUND(taff[ii]);
>  taff[20]*=siz;taff[21]*=siz;taff[22]*=siz; 
> } 
> 
> /* Updates taff from aff, dir4[3], location[3] */
> #define DSUM (dir4[0]+dir4[1]+dir4[2]+dir4[3])
> void aff2taff(){
>   int ii;
>   for(ii=0;ii<4;ii++)taff[3+(5*ii)]=(GLfloat)dir4[ii];/* hidden col */
>   taff[20]=aff[12];taff[21]=aff[13];taff[22]=aff[14]; /* location */  
>   if(dir4[3]!=0){
>     for(ii=0;ii<3;ii++)taff[15+ii]=0;
>     taff[0]=aff[0];taff[1]=aff[1];taff[2]=aff[2];      /* direction */
>     taff[5]=aff[4];taff[6]=aff[5];taff[7]=aff[6];
>     taff[10]=aff[8];taff[11]=aff[9];taff[12]=aff[10];
>     taff[23]=siz*dir4[3]*4.0*(ROWS-(2*location[3])-1);}    /* location */
>   if(dir4[2]!=0){
>     for(ii=0;ii<3;ii++)taff[10+ii]=0;
>     taff[0]=aff[0];taff[1]=aff[1];taff[2]=aff[2];      /* direction */
>     taff[5]=aff[4];taff[6]=aff[5];taff[7]=aff[6];
>     taff[15]=aff[8];taff[16]=aff[9];taff[17]=aff[10];
>     taff[23]=siz*dir4[2]*4.0*(ROWS-(2*location[2])-1);}    /* location */
>   if(dir4[1]!=0){
>     for(ii=0;ii<3;ii++)taff[5+ii]=0;
>     taff[0]=aff[0];taff[1]=aff[1];taff[2]=aff[2];      /* direction */
>     taff[10]=aff[4];taff[11]=aff[5];taff[12]=aff[6];
>     taff[15]=aff[8];taff[16]=aff[9];taff[17]=aff[10];
>     taff[23]=siz*dir4[1]*4.0*(ROWS-(2*location[1])-1);}    /* location */
>   if(dir4[0]!=0){
>     for(ii=0;ii<3;ii++)taff[ii]=0;
>     taff[5]=aff[0];taff[6]=aff[1];taff[7]=aff[2];      /* direction */
>     taff[10]=aff[4];taff[11]=aff[5];taff[12]=aff[6];
>     taff[15]=aff[8];taff[16]=aff[9];taff[17]=aff[10];
>     taff[23]=siz*dir4[0]*4.0*(ROWS-(2*location[0])-1);}    /* location */
> }
> void taff2aff(){
>   int ii;
>   for(ii=0;ii<4;ii++)dir4[ii]=ROUND(taff[3+(5*ii)]); /* update dir4 too */
>   aff[12]=taff[20];aff[13]=taff[21];aff[14]=taff[22];/* location */
>   if(dir4[3]!=0){
>     aff[0]=taff[0];aff[1]=taff[1];aff[2]=taff[2];      /* direction */
>     aff[4]=taff[5];aff[5]=taff[6];aff[6]=taff[7];
>     aff[8]=taff[10];aff[9]=taff[11];aff[10]=taff[12];}
>   if(dir4[2]!=0){
>     aff[0]=taff[0];aff[1]=taff[1];aff[2]=taff[2];      /* direction */
>     aff[4]=taff[5];aff[5]=taff[6];aff[6]=taff[7];
>     aff[8]=taff[15];aff[9]=taff[16];aff[10]=taff[17];}
>   if(dir4[1]!=0){
>     aff[0]=taff[0];aff[1]=taff[1];aff[2]=taff[2];      /* direction */
>     aff[4]=taff[10];aff[5]=taff[11];aff[6]=taff[12];
>     aff[8]=taff[15];aff[9]=taff[16];aff[10]=taff[17];}
>   if(dir4[0]!=0){
>     aff[0]=taff[5];aff[1]=taff[6];aff[2]=taff[7];      /* direction */
>     aff[4]=taff[10];aff[5]=taff[11];aff[6]=taff[12];
>     aff[8]=taff[15];aff[9]=taff[16];aff[10]=taff[17];}
> }
> 
> /* MAY 1999: Cleaned up comments */
> /* Rotate around the (fixed) XY,XZ&YZ planar axes */
> /* Recall Front is -z, Up is +y, and Right is +x */
> 
> void XYrotate(int sgn){GLfloat temp;GLfloat gsgn=(GLfloat)sgn;int ii;
> /* Rotate old (sgn)*Other to new Front */
>  for(ii=0;ii<5;ii++){
>    temp=(-1)*taff[3+(5*ii)];
>    taff[3+(5*ii)]=taff[2+(5*ii)]*gsgn;
>    taff[2+(5*ii)]=temp*gsgn;}
>  roundtaff();taff2aff();}
> void XZrotate(int sgn){GLfloat temp;GLfloat gsgn=(GLfloat)sgn;int ii;
> /* Rotate old (sgn)*Other to new Up */
>  for(ii=0;ii<5;ii++){
>    temp=taff[3+(5*ii)];
>    taff[3+(5*ii)]=(-1)*taff[1+(5*ii)]*gsgn;
>    taff[1+(5*ii)]=temp*gsgn;}
>  roundtaff();taff2aff();}
> void YZrotate(int sgn){GLfloat temp;GLfloat gsgn=(GLfloat)sgn;int ii;
> /* if Rotate old (sgn)*Other to new Right */
>  for(ii=0;ii<5;ii++){
>    temp=taff[3+(5*ii)];
>    taff[3+(5*ii)]=(-1)*taff[5*ii]*gsgn;
>    taff[5*ii]=temp*gsgn;}
>  roundtaff();taff2aff();}
> 
> /* MAY 1999: Created XY,XZ,YZinitRotate */
> /* Initialize gradual rotation of 3D slice through 4D space
>    with XY,XZ&YZ planar axes fixed */
> void XYinitRotate(int sgn){
>   if(taff[3+0]!=0){ /*                   Moving Axis: x->? */
>     if(taff[2+5]!=0){ /*                              x->y */
>       ActualAxes=-1; init_edges(1);
>       sign_theta=(sgn)*(taff[3+0])*(-1)*(taff[2+5]);}
>     else if(taff[2+10]!=0){ /*                        x->z */
>       ActualAxes=-2; init_edges(2);
>       sign_theta=(sgn)*(taff[3+0])*(-1)*(taff[2+10]);}
>     else{ /* taff[2+15]!=0                            x->w */
>       ActualAxes=-3; init_edges(3);
>       sign_theta=(sgn)*(taff[3+0])*(-1)*(taff[2+15]);}
>   }
>   else if(taff[3+5]!=0){ /*              Moving Axis: y->? */
>     if(taff[2+0]!=0){ /*                              y->x */
>       ActualAxes=1; init_edges(1);
>       sign_theta=(sgn)*(taff[3+5])*(-1)*(taff[2+0]);}
>     else if(taff[2+10]!=0){ /*                        y->z */
>       ActualAxes=-4; init_edges(4);
>       sign_theta=(sgn)*(taff[3+5])*(-1)*(taff[2+10]);}
>     else{ /* taff[2+15]!=0                            y->w */
>       ActualAxes=-5; init_edges(5);
>       sign_theta=(sgn)*(taff[3+5])*(-1)*(taff[2+15]);}
>   }
>   else if(taff[3+10]!=0){ /*             Moving Axis: z->? */
>     if(taff[2+0]!=0){ /*                              z->x */
>       ActualAxes=2; init_edges(2);
>       sign_theta=(sgn)*(taff[3+10])*(-1)*(taff[2+0]);}
>     else if(taff[2+5]!=0){ /*                         z->y */
>       ActualAxes=4; init_edges(4);
>       sign_theta=(sgn)*(taff[3+10])*(-1)*(taff[2+5]);}
>     else{ /* taff[2+15]!=0                            z->w */
>       ActualAxes=-6; init_edges(6);
>       sign_theta=(sgn)*(taff[3+10])*(-1)*(taff[2+15]);}
>   }
>   else{ /* taff[3+15]!=0                 Moving Axis: w->? */
>     if(taff[2+0]!=0){ /*                              w->x */
>       ActualAxes=3; init_edges(3);
>       sign_theta=(sgn)*(taff[3+15])*(-1)*(taff[2+0]);}
>     else if(taff[2+5]!=0){ /*                         w->y */
>       ActualAxes=5; init_edges(5);
>       sign_theta=(sgn)*(taff[3+15])*(-1)*(taff[2+5]);}
>     else{ /* taff[2+10]!=0                            w->z */
>       ActualAxes=6; init_edges(6);
>       sign_theta=(sgn)*(taff[3+15])*(-1)*(taff[2+10]);}
>   }
> }/* XYinitRotate */
> void XZinitRotate(int sgn){
>   if(taff[3+0]!=0){ /*                   Moving Axis: x->? */
>     if(taff[1+5]!=0){ /*                              x->y */
>       ActualAxes=-1; init_edges(1);
>       sign_theta=(sgn)*(taff[3+0])*(taff[1+5]);}
>     else if(taff[1+10]!=0){ /*                        x->z */
>       ActualAxes=-2; init_edges(2);
>       sign_theta=(sgn)*(taff[3+0])*(taff[1+10]);}
>     else{ /* taff[1+15]!=0                            x->w */
>       ActualAxes=-3; init_edges(3);
>       sign_theta=(sgn)*(taff[3+0])*(taff[1+15]);}
>   }
>   else if(taff[3+5]!=0){ /*              Moving Axis: y->? */
>     if(taff[1+0]!=0){ /*                              y->x */
>       ActualAxes=1; init_edges(1);
>       sign_theta=(sgn)*(taff[3+5])*(taff[1+0]);}
>     else if(taff[1+10]!=0){ /*                        y->z */
>       ActualAxes=-4; init_edges(4);
>       sign_theta=(sgn)*(taff[3+5])*(taff[1+10]);}
>     else{ /* taff[1+15]!=0                            y->w */
>       ActualAxes=-5; init_edges(5);
>       sign_theta=(sgn)*(taff[3+5])*(taff[1+15]);}
>   }
>   else if(taff[3+10]!=0){ /*             Moving Axis: z->? */
>     if(taff[1+0]!=0){ /*                              z->x */
>       ActualAxes=2; init_edges(2);
>       sign_theta=(sgn)*(taff[3+10])*(taff[1+0]);}
>     else if(taff[1+5]!=0){ /*                         z->y */
>       ActualAxes=4; init_edges(4);
>       sign_theta=(sgn)*(taff[3+10])*(taff[1+5]);}
>     else{ /* taff[1+15]!=0                            z->w */
>       ActualAxes=-6; init_edges(6);
>       sign_theta=(sgn)*(taff[3+10])*(taff[1+15]);}
>   }
>   else{ /* taff[3+15]!=0                 Moving Axis: w->? */
>     if(taff[1+0]!=0){ /*                              w->x */
>       ActualAxes=3; init_edges(3);
>       sign_theta=(sgn)*(taff[3+15])*(taff[1+0]);}
>     else if(taff[1+5]!=0){ /*                         w->y */
>       ActualAxes=5; init_edges(5);
>       sign_theta=(sgn)*(taff[3+15])*(taff[1+5]);}
>     else{ /* taff[1+10]!=0                            w->z */
>       ActualAxes=6; init_edges(6);
>       sign_theta=(sgn)*(taff[3+15])*(taff[1+10]);}
>   }
> }/* XZinitRotate */
> void YZinitRotate(int sgn){
>   if(taff[3+0]!=0){ /*                   Moving Axis: x->? */
>     if(taff[0+5]!=0){ /*                              x->y */
>       ActualAxes=-1; init_edges(1);
>       sign_theta=(sgn)*(taff[3+0])*(taff[0+5]);}
>     else if(taff[0+10]!=0){ /*                        x->z */
>       ActualAxes=-2; init_edges(2);
>       sign_theta=(sgn)*(taff[3+0])*(taff[0+10]);}
>     else{ /* taff[0+15]!=0                            x->w */
>       ActualAxes=-3; init_edges(3);
>       sign_theta=(sgn)*(taff[3+0])*(taff[0+15]);}
>   }
>   else if(taff[3+5]!=0){ /*              Moving Axis: y->? */
>     if(taff[0+0]!=0){ /*                              y->x */
>       ActualAxes=1; init_edges(1);
>       sign_theta=(sgn)*(taff[3+5])*(taff[0+0]);}
>     else if(taff[0+10]!=0){ /*                        y->z */
>       ActualAxes=-4; init_edges(4);
>       sign_theta=(sgn)*(taff[3+5])*(taff[0+10]);}
>     else{ /* taff[0+15]!=0                            y->w */
>       ActualAxes=-5; init_edges(5);
>       sign_theta=(sgn)*(taff[3+5])*(taff[0+15]);}
>   }
>   else if(taff[3+10]!=0){ /*             Moving Axis: z->? */
>     if(taff[0+0]!=0){ /*                              z->x */
>       ActualAxes=2; init_edges(2);
>       sign_theta=(sgn)*(taff[3+10])*(taff[0+0]);}
>     else if(taff[0+5]!=0){ /*                         z->y */
>       ActualAxes=4; init_edges(4);
>       sign_theta=(sgn)*(taff[3+10])*(taff[0+5]);}
>     else{ /* taff[0+15]!=0                            z->w */
>       ActualAxes=-6; init_edges(6);
>       sign_theta=(sgn)*(taff[3+10])*(taff[0+15]);}
>   }
>   else{ /* taff[3+15]!=0                 Moving Axis: w->? */
>     if(taff[0+0]!=0){ /*                              w->x */
>       ActualAxes=3; init_edges(3);
>       sign_theta=(sgn)*(taff[3+15])*(taff[0+0]);}
>     else if(taff[0+5]!=0){ /*                         w->y */
>       ActualAxes=5; init_edges(5);
>       sign_theta=(sgn)*(taff[3+15])*(taff[0+5]);}
>     else{ /* taff[0+10]!=0                            w->z */
>       ActualAxes=6; init_edges(6);
>       sign_theta=(sgn)*(taff[3+15])*(taff[0+10]);}
>   }
> }/* YZinitRotate */
> /* MAY 1999: Created XY,XZ,YZinitRotate */
> 
> /* Was getmem() */
> 
> void char2wall(GLfloat x,GLfloat y, GLfloat z, char buf[]){
>      char *p; glRasterPos3f(x,y,z);
>      for(p = buf;*p;p++) glutBitmapCharacter(GLUT_BITMAP_9_BY_15,*p);
> }
> 
> void autotymer(int);  /* to allow deFault to call the autotymer later */
> 
> void deFault(void){
>   int ii; float tmp;
>   gnd = 0;w_shift=0;
>   th0=5; th1=355;  dth=15; ta0=5; ta1=355; dta=15;
>   msg=1; binoc=0; nose=.06; mauspaw = 0; wnd = 1; siz = 1.5; 
>   mode=FLYMODE;
>   speed3D=3; torq=.02; focal = 1.5/1.1/1.1/1.1/1.1/1.1/1.1; mysiz=.01;
>   if(rand_maze)far=13.;else far=99.; morph=0; frozen=0;
>   for(ii=0;ii<16;ii++)  starmat[ii]=aff[ii]=id[ii] = (ii/4==ii%4) ? 1 : 0;   
>   tmp=NRM(lux);  for(ii=0;ii<3;ii++)lux[ii]/=tmp;
> 
> /*if(caveyes){aff[12]=0; aff[13]= 5; aff[14]= -3;}/*5ft beyond front wall*/ 
>   location[0] = 3;location[1] = 4;location[2] = 6;location[3] = 3;
> 
>   aff[12]=-(location[0]+.5-ROWS/2.0)*8.0*siz;
>   aff[13]=-(location[1]+.5-ROWS/2.0)*8.0*siz;
>   aff[14]=-(location[2]+.5-ROWS/2.0)*8.0*siz;
>   dir4[0]=dir4[1]=dir4[2]=0;dir4[3]=1;aff2taff();
>   rotating_in_4D=cube=0;Theta=moving=0;
> }
> 
> /* MAY 1999 */
> drawstairs(void){
>   int ii,jj,kk;
>   if(dir4[3]!=0)FOR(ii,0,ROWS)FOR(jj,0,ROWS)FOR(kk,0,ROWS){
>     if(xyzfaces[ii][jj][kk][location[3]]==0 && 
>        location[3]>=0 && location[3]<ROWS1)
>       drawportal(ii,jj,kk,thick,dir4[3],siz);
>     if(xyzfaces[ii][jj][kk][location[3]+1]==0 &&
>        location[3]>=-1 && location[3]<ROWS)
>       drawportal(ii,jj,kk,thick,(-1)*dir4[3],siz);
>   }
>   else if(dir4[2]!=0)FOR(ii,0,ROWS)FOR(jj,0,ROWS)FOR(kk,0,ROWS){
>     if(xywfaces[ii][jj][location[2]][kk]==0 && 
>        location[2]>=0 && location[2]<ROWS1)
>       drawportal(ii,jj,kk,thick,dir4[2],siz);
>     if(xywfaces[ii][jj][location[2]+1][kk]==0 &&
>        location[2]>=-1 && location[2]<ROWS)
>       drawportal(ii,jj,kk,thick,(-1)*dir4[2],siz);
>   }
>   else if(dir4[1]!=0)FOR(ii,0,ROWS)FOR(jj,0,ROWS)FOR(kk,0,ROWS){
>     if(xzwfaces[ii][location[1]][jj][kk]==0 && 
>        location[1]>=0 && location[1]<ROWS1)
>       drawportal(ii,jj,kk,thick,dir4[1],siz);
>     if(xzwfaces[ii][location[1]+1][jj][kk]==0 &&
>        location[1]>=-1 && location[1]<ROWS)
>       drawportal(ii,jj,kk,thick,(-1)*dir4[1],siz);
>   }
>   else /* dir4[0]!=0 */ FOR(ii,0,ROWS)FOR(jj,0,ROWS)FOR(kk,0,ROWS){
>     if(yzwfaces[location[0]][ii][jj][kk]==0 && 
>        location[0]>=0 && location[0]<ROWS1)
>       drawportal(ii,jj,kk,thick,dir4[0],siz);
>     if(yzwfaces[location[0]+1][ii][jj][kk]==0 &&
>        location[0]>=-1 && location[0]<ROWS)
>       drawportal(ii,jj,kk,thick,(-1)*dir4[0],siz);
>   }
> }
> 
> void drawall(void){
>  if(rotating_in_4D==0){
>    drawmaze(((ROWS-ROUND(DSUM*taff[23]/(4.0*siz))-1)/2),gap,dir4,siz); 
>    drawstairs();   
>  }
>  else /* rotating_in_4D==1 */ {
> 
>    /* FEB2000
>    printf("Theta=%f\t",Theta);
>    */
> 
>    if(ActualAxes>0)
>      draw_intersections(inter_ss,inter_tt,sign_theta*(Theta)*DG,abs(ActualAxes),gap);
>    else /* ActualAxes<0 */
>      draw_intersections(inter_ss,inter_tt,sign_theta*(90-Theta)*DG,abs(ActualAxes),gap);
>  }
> }
> 
> void drawstars(void){
> int i;
> static float star[1000][3];
> static int virgin=1;
> if(virgin){
>   int ii,jj;
>   float fact;
>   virgin=0;
>   for(ii=0;ii<1000;ii++)
>      {
>      for(jj=0;jj<3;jj++) star[ii][jj] =   random()/(float)0x40000000-1.;
>      if(caveyes){
>            fact = 90./NRM(star[ii]);
>            for(jj=0;jj<3;jj++) star[ii][jj] *= fact;
>            }   
>      }
>   }
>   glMatrixMode(GL_MODELVIEW);
>   glPushMatrix();
>   glMultMatrixf(starmat);
>   glColor3f(0.6,0.6,0.3);    
>   glBegin(GL_POINTS);
>     for(i=0;i<1000;i++) glVertex3fv(star[i]);
>   glEnd();
>   glPopMatrix();
>   glClear(GL_DEPTH_BUFFER_BIT);
> }
> 
> void audioprep(void){
> #ifdef SOUND
>    if (!FBgnSoundServer())
>       fprintf(stderr,"UDP connection to sound server failed\n");
>    else if ((audiohandle = AUDinit("noosh.aud")) == -1)
>       fprintf(stderr,"Couldn't open noosh.aud\n");
> #endif
> }
> 
> void dohoot(void) {strcpy(which,"hoot");}
> void dotrump(void){strcpy(which,"trump");}
> void doflute(void){strcpy(which,"flute");}
> void dodrum(void) {strcpy(which,"drum");}
> 
> void audiofunc(void){
> #ifdef SOUND 
> #define LIRP(X,A,B,x,a,b)  (X = A + (x-a)*(B-A)/(b-a))
>   static float freq=440., ampl=1., indx=20., cmra=2.; 
>   static float data[4];
>   static omode =0; static ofocal = 0;
>   static beep =0;  /* the sound server */
> 
>   if(omode > mode){dohoot(); beep = 1;}
>   if(omode < mode){dodrum(); beep = 1;}
> 
>   LIRP(freq, 440, 1760, th0,  10, 170); /* frequeny mimics th1 */ 
>   LIRP(indx, 10, 1,  focal, .03, 2.0); /* index  mimics focal */ 
>   if(ofocal != focal){dodrum(); beep = 1;}    /* there is an fp bug here */
> 
> if(beep){data[0] = freq;  /* pitch in Herz      */ 
>           data[1] = ampl;  /* the sum of all amplitudes must be < 1.0 */ 
>           data[2] = indx;    
>           data[3] = cmra;
>           AUDupdate(audiohandle, which, 4, data);  /* send message  to vss */ 
>           beep = 0; 
> }
>   omode = mode;  ofocal = focal; 
> #endif
> }
> 
> void audioclean(void){
> #ifdef SOUND
>    AUDterminate(audiohandle);
>    EndSoundServer();
> #endif
> }
> 
> void arguments(int argc,char **argv){
>   extern char *optarg;
>   extern int optind; 
>   int chi;   
> /* defaults if not set by command line: */
>   rseed=43; XYZchance=.6, XYWchance= .7,  XZWchance=.75 , YZWchance=.6 ;
>   thick=2;gap=.15;rand_maze=0;
>   /* w: needs ONE number after -w, c<nothing> means NO number follows*/ 
>   while ((chi = getopt(argc,argv,"w:cg:r:t:s:x:y:z:h:")) != -1) 
>     switch(chi){
>     case 'c': caveyes=1; break;  
>     case 'w': win=atoi(optarg); break; 
>     case 'g': gap=atof(optarg); break;
>     case 'r': rseed=atoi(optarg); rand_maze=1; break;
>     case 't': thick=atoi(optarg); break;
>     case 's': speed3D=atoi(optarg); break;
>     case 'x': YZWchance=atof(optarg); break;
>     case 'y': XZWchance=atof(optarg); break;
>     case 'z': XYWchance=atof(optarg); break;
>     case 'h': XYZchance=atof(optarg); break;
>       if(gap<0 || gap>4)gap=.15;
>       break;
>     }
>   if (optind!=argc) {
>     fprintf(stderr,"%s: Incorrect usage\n",argv[0]);
>     fprintf(stderr,"Usage: %s [-w win] [-t] [-s speed] [-r rseed] [-x probability of red walls]\n  [-y probability of green walls] [-z probability of blue walls]\n  [-h probability of hidden walls] [-c] [-g gap]\n",argv[0]);
>     /*    exit(-1);    */ /* Why is this in skel but not noosh? */
>   }
> }
> 
> void dataprep(void){
>   gap=.15;
>    audioprep();
>    deFault();
>    initvv();  /* initializes the vertices */
>    if(rand_maze)makemaze(rseed,XYZchance,XYWchance,XZWchance,YZWchance);
>    else setmaze();
>    init_colors(); /* MAY 1999 */
> }
> /* autymer adjusted to prevent user movement and _move_ autmomatically */
> void autotymer(int reset){int ii;
> /* cnt4D is used for all 4D rotations */
>  static int cnt4D=(max_theta_delay*((90.0/theta_jump)-1))+1;
>  int cnt3Drot= 90.0/rot_jump[speed3D];
>  int cnt3Dmove= 8.0/fore_jump[speed3D];
>  int elevDelay= 4.0/fore_jump[speed3D];
> 
> #define  TYME(cnt,max,act) {static cnt; if(first)cnt=max; else\
>                             if(cnt?cnt--:0){ act ; goto Break;}}
> #define  HYTHER  TYME( dwell  , elevDelay,  moving= 0)\
>   TYME( hither , 1,  moving=10) TYME( dwell  , 8,  moving= 0)
> #define THYTHER  TYME( dwell  , elevDelay,  moving= 0)\
>   TYME( thither, 1,  moving= 9) TYME( dwell  , 8,  moving= 0)
> 
>   static first = 1;  /* the first time autymer is called */
>   if(reset)first=1;  /* or if it is reset to start over  */
>   if(first){moving=0;Theta=0;/* moving=0 necessary? */
>     location[0]=0;location[1]=4;location[2]=5;location[3]=1;
>     for(ii=0;ii<16;ii++)starmat[ii]=aff[ii]=id[ii]=(ii/4==ii%4)?1:0;   
>     aff[12]=-(location[0]+.5-ROWS/2.0)*8.0*siz;
>     aff[13]=-(location[1]+.5-ROWS/2.0)*8.0*siz;
>     aff[14]=-(location[2]+.5-ROWS/2.0)*8.0*siz;
>     dir4[0]=dir4[1]=dir4[2]=0;dir4[3]=1;aff2taff();}
> 
>   TYME( dwell  , 10, moving= 0)
>   TYME( forward, cnt3Dmove, moving= 8) 
>   TYME( forward, cnt3Dmove, moving= 8)
>   HYTHER
>   TYME( back   , cnt3Dmove, moving= 7)
>   TYME( dwell  , 10, moving= 0)
>   TYME( forward, cnt3Dmove, moving= 8) 
>   TYME( forward, cnt3Dmove, moving= 8)
>   TYME( right  , cnt3Drot, moving= 3)
>   TYME( forward, cnt3Dmove, moving= 8)
>   TYME( dwell  , 10, moving= 0)
>   TYME( FourRotate, cnt4D, moving=11)
>   TYME( dwell  , 10, moving= 0)
>   TYME( forward, cnt3Dmove, moving= 8)
>   TYME( right  , cnt3Drot, moving= 3)
>   TYME( forward, cnt3Dmove, moving= 8) 
>   TYME( left   , cnt3Drot, moving= 4)
>   TYME( forward, cnt3Dmove, moving= 8)
>   TYME( forward, cnt3Dmove, moving= 8)
>   TYME( dwell  , 10, moving= 0)
>   HYTHER
>   TYME( dwell  , 10, moving= 0)
>   THYTHER
>   THYTHER
>   TYME( dwell  , 10, moving= 0)
>   TYME( forward, cnt3Dmove, moving= 8) 
>   TYME( forward, cnt3Dmove, moving= 8)
>   TYME( right  , cnt3Drot, moving= 3)
>   TYME( forward, cnt3Dmove, moving= 8) 
>   TYME( forward, cnt3Dmove, moving= 8)
>   TYME( dwell  , 10, moving= 0)
>   TYME( forward, cnt3Dmove, moving= 8)
>   TYME( FourRotate, cnt4D, moving=16)
>   TYME( dwell  , 10, moving= 0)
>   TYME( forward, cnt3Dmove, moving= 8) 
>   TYME( forward, cnt3Dmove, moving= 8)
>   TYME( forward, cnt3Dmove, moving= 8)
>   TYME( right  , cnt3Drot, moving= 3)
>   TYME( dwell  , 10, moving= 0)
>   THYTHER
>   TYME( dwell  , 10, moving= 0)
>   THYTHER
>   TYME( dwell  , 10, moving= 0)
>   THYTHER
>   TYME( dwell  , 10, moving= 0)
>   THYTHER
>   TYME( dwell  , 10, moving= 0)
>   THYTHER
>   TYME( forward, cnt3Dmove, moving= 8) 
>   TYME( forward, cnt3Dmove, moving= 8)
>   TYME( forward, cnt3Dmove, moving= 8)
>   TYME( right  , cnt3Drot, moving= 3)
>   HYTHER
>   TYME( dwell  , 10, moving= 0)
>   TYME( FourRotate, cnt4D, moving=14)
>   TYME( dwell  , 30, moving= 0)
>   TYME( FourRotate, cnt4D, moving=13)
>   TYME( dwell  , 10, moving= 0)
>   THYTHER
>   TYME( left   , cnt3Drot, moving= 4)
>   TYME( forward, cnt3Dmove, moving= 8)
>   TYME( forward, cnt3Dmove, moving= 8)
>   TYME( forward, cnt3Dmove, moving= 8)
>   TYME( right  , cnt3Drot, moving= 3)
>   TYME( forward, cnt3Dmove, moving= 8) 
>   TYME( dwell  , 20, moving= 0)
>   TYME(finish  , 1 , first = 1  )
>   first = 0;
>   Break:   ;   /* yes Virginia, C has gotos */
> }
> 
> /*Beware of competing keys in CAVEsimulation and wnd==1   */
> /* you may want to remap these keys to avoid the conflict */
> void keyboard(unsigned char key, int x, int y){
> #define  IF(K)            if(key==K)
> #define  PRESS(K,A,b)     IF(K){b;}IF((K-32)){A;}  
> /*was backwards in previous versions */
> #define  TOGGLE(K,flg)    IF(K){(flg) = 1-(flg); }
> #define  CYCLE(K,f,m)     PRESS((K), (f)=(((f)+(m)-1)%(m)),(f)=(++(f)%(m)))
> #define  SLIDI(K,f,m,M)   PRESS(K,(--f<m?m:f), (++f>M?M:f))
> #define  SLIDF(K,f,m,M,d) PRESS(K,((f -= d)<m?m:f), ((f += d)>M?M:f))
>    static int begin_morph=0;
>    static int old_mode=0;
>    int ii;
> /* Only ASCII characters can be processes by this GLUT callback function */ 
>    TOGGLE('v',binoc);                            /* cross-eyed STEREO   */
>    TOGGLE('w',msg);                              /* writing on/off      */
>    if(mode==FLYMODE){
>    if(morph==0)begin_morph=1;  /* b_morph was 0 - afterwards, b_morph */
>    TOGGLE('h',morph);                            /* autotymer on/off    */
>    if(morph==0)begin_morph=0;  /* set to 1 if morph has been switched on  */
>    if(begin_morph){autotymer(1); begin_morph=0;}
>    }
>    TOGGLE('f',frozen);
>    PRESS('n', nose -= .001 , nose += .001 );     /* for binoculars      */
>    if(w_shift<11 && moving==0 && morph==0){
>    CYCLE(' ', mode,TURNMODE+1);                  /* fly/turn modes      */
>    }
>    if(mode!=old_mode){
>      old_mode=mode;
>      if(mode==TURNMODE){
>        for(ii=0;ii<16;ii++)temp_aff[ii]=aff[ii];
>        for(ii=0;ii<4;ii++)temp_location[ii]=location[ii];
>      }
>      else{
>        for(ii=0;ii<16;ii++)aff[ii]=temp_aff[ii];
>        for(ii=0;ii<4;ii++)location[ii]=temp_location[ii];
>      }
>    }       
>    PRESS('i', mysiz /= 1.1, mysiz *= 1.1)        /* rescale the world   */
>    PRESS('o', focal *= 1.1 , focal /= 1.1)       /* telephoto           */
>    PRESS('p', far *= 1.01 , far   /= 1.01)       /* rear clipping plane */
>      if(moving==0 && morph==0){
>    PRESS('s',if(speed3D>0)speed3D-=1, if(speed3D<6)speed3D+=1);            /* flying speed        */
>      }
>    PRESS('q',torq /= 1.02, torq *= 1.02);        /* turning speed       */
>    PRESS('z', deFault(), deFault());             /* zap changes         */
>    if(mode==FLYMODE){
>    PRESS('t', w_shift=10, w_shift=9);            /* move in 4th dimension */
>    }
>    PRESS('g', if(gap==0)gap=.01; else if(gap<.25)gap+=.005; else if(gap<4)gap*=1.02; else gap=4, if(gap <= .02)gap=0; else if(gap<=.25)gap-=.005; else gap /= 1.02);
>    /*  
>    PRESS('a',showaff(),showtaff());
>    PRESS('r',roundaff(),roundtaff());
>    PRESS('t',taff2aff(),aff2taff());
>    PRESS('a',showAA(),showAA());   
>    */
>    if(mode!=TURNMODE){ /* Not sure about these words: */
>    IF('1')w_shift=11;/* rotate right towards +hidden */
>    IF('!')w_shift=12;/* rotate left towards +hidden */
>    IF('2')w_shift=13;/* rotate up towards +hidden */
>    IF('@')w_shift=14;/* rotate down towards +hidden */
>    IF('3')w_shift=15;/* rotate forwards towards +hidden */
>    IF('#')w_shift=16;/* rotate back towards +hidden */
>    }
>    IF(27) {audioclean(); exit(0); }              /* ESC exit            */
> }
> 
> void special_keybo(int key, int x, int y){
> /*non-ASCII keypresses go here, if you're lucky enough to know their names */
>  fprintf(stderr," non-ASCII character was pressed.\n");
>  fprintf(stderr," use special_keybo() to process it\n");
> }
> /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% */
> #ifdef CAVE
> /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% */
> 
> void cavekeybo(void){
> #define  CAVEIF(K)         if(CAVEgetbutton(K))
> #define  CAVEPRESS(K,A,b)  CAVEIF(K){if(CAVEgetbutton(CAVE_LEFTSHIFTKEY)||\
>                            CAVEgetbutton(CAVE_RIGHTSHIFTKEY)){b;}else{A;}}
> #define  CAVETOGGLE(K,flg) CAVEIF(K){(flg) = 1-(flg); }
> #define  CAVECYCLE(K,f,m)  CAVEPRESS((K),(f)=(((f)+(m)-1)%(m)),(f)=(++(f)%(m)) )
> #define  CAVESLIDI(K,f,m,M)  CAVEPRESS(K,(--f<m?m:f), (++f>M?M:f))
> #define  CAVESLIDF(K,f,m,M,d) CAVEPRESS(K,((f -= d)<m?m:f), ((f += d)>M?M:f))
>   CAVEPRESS(CAVE_PKEY, far *= 1.01,   far   /= 1.01) /* rear clipping plane*/
>   CAVEPRESS(CAVE_QKEY, speed /= 1.02, speed *= 1.02);/* flying speed       */
>   CAVEPRESS(CAVE_RKEY, torq /= 1.02,  torq *= 1.02); /* turning speed      */
>   CAVETOGGLE(CAVE_YKEY,wnd);                         /* mauspaw vs wandpaw */
> /* not yet implemented */ 
>   CAVEPRESS(CAVE_ZKEY, deFault(), deFault());        /* zap changes       */
>   CAVEPRESS(CAVE_GKEY, if(gap==0)gap=.000001; else if(gap<.25)gap+=.0000005; else if(gap<4)gap*=1.000002; else gap=4, if(gap <= .0000002)gap=0; else if(gap<=.25)gap-=.0000005; else gap /= 1.000002);
>   CAVEPRESS(CAVE_SKEY, siz /= 1.000002; far /= 1.000002; taff[20]/=1.000002; \
> 	taff[21]/=1.000002;taff[22]/=1.000002;taff[23]/=1.000002; \
>         aff[12]/=1.000002;aff[13]/=1.000002;aff[14]/=1.000002; \
>       , siz *= 1.000002; far *=1.000002; taff[20]*=1.000002; \
>         taff[21]*=1.000002;taff[22]*=1.000002;taff[23]*=1.000002; \
> 	aff[12]*=1.000002;aff[13]*=1.000002;aff[14]*=1.000002; );
>   /*
>   {    
>     static int mode_delay;
>     if(CAVEgetbutton(CAVE_TKEY)){
>       if(mode==mode_delay)
> 	{mode=1-mode;printf("\tT:%d %d ",mode,mode_delay);}
>     }
>     else
>       if(mode!=mode_delay)
> 	{mode_delay=mode;printf("\tno T:%d %d ",mode,mode_delay);}
>   }
>   */
> }
> 
> void graffiti(void){             /* used to be called speedo  */
>   char buf[256];                 /*messages written into here */ 
> #define  LABEL3(x,y,z,W,u){sprintf(buf,(W),(u));char2wall(x,y,z,buf);}
>   static float last=0.,fps;      /*for measuring time         */ 
>   if(!CAVEMasterDisplay()) return;  /* USE CAVEMasterWall in papeBeta */
>   if(floor(2.*(*CAVETime))>floor(2.*last)){  /* rationalize this */ 
>      last=*CAVETime;
>      fps=*CAVEFramesPerSecond;
>      }
>   if(mode==TURNMODE)glColor3f(0.,1.,1.);
>   else glColor3f(1.,1.,1.);
>   {sprintf(buf,"location %d %d %d %d",location[0],location[1],
> 	   location[2],location[3]);char2wall(-3.8,3.,-5.0,buf);}
>   if(morph==0){
>   LABEL3(-3.8,2.,-5.0,
> 	 "hold M move wand=tractor       hold L move wand 4D?%s","");
>   LABEL3(-3.8,1.5,-5.0,\
> 	 "hold M: click R=Cube  click L=Octahedron %s","");
>   }
>   if(mode==FLYMODE){
>     if(morph==0){
>       LABEL3(-3.8,2.5,-5.0,\
> 	     "click R: Begin Demo               siz=%f",siz);
>     }   
>     else{
>       LABEL3(-3.8,2.5,-5.0,\
> 	     "click R: Freeze/Unfreeze          siz=%f",siz);
>       LABEL3(-3.8,2.,-5.0,
> 	     "click L or M: Stop Demo%s","");  
>     }}
>   else /* if mode==TURNMODE */ {
>     LABEL3(-3.8,2.5,-5.0,\
> 	   "click R: Main(fly)mode                siz=%f",siz);
>     LABEL3(-3.8,1.,-5.0,\
> 	   "Keyboard: S/s=grow/shrink G/g=gap%s","");
>   }
>   LABEL3(-4.8,8.0,-5.0, "4D MayMaze-something Version 0.2\
>        by Michael Pelsmajer, (C) 1998 U.Illinois %s","")
>   LABEL3(-5.2,1.0,-5.0,"%5.1f fps",fps);
> }
> 
> int collision_check(void){
>   /* return 0 if a collision, 1 if no collision */
>   int may_go=1;int dirx=(-1)*ROUND(aff[2]);
>   int diry=(-1)*ROUND(aff[6]);int dirz=(-1)*ROUND(aff[10]);
>   int relloc[4];/* location relative to aff */
>   if(dir4[0]!=0){relloc[3]=location[0];relloc[0]=location[1];
>   relloc[1]=location[2];relloc[2]=location[3];}
>   if(dir4[1]!=0){relloc[3]=location[1];relloc[0]=location[0];
>   relloc[1]=location[2];relloc[2]=location[3];}
>   if(dir4[2]!=0){relloc[3]=location[2];relloc[0]=location[0];
>   relloc[1]=location[1];relloc[2]=location[3];}
>   if(dir4[3]!=0){relloc[3]=location[3];relloc[0]=location[0];
>   relloc[1]=location[1];relloc[2]=location[2];}
>   if(moving==7 && relloc[3]>=0 && relloc[3]<ROWS){
>     if(dirx!=0 && relloc[1]<ROWS && relloc[1]>=0 && 
>        relloc[2]<ROWS && relloc[2]>=0)
>       if((dirx>0 && relloc[0]<ROWS1 && relloc[0]>=0) || 
> 	 (dirx<0 && relloc[0]<ROWS && relloc[0]>=-1))
> 	if(dir4[0]==0)
> 	  {if(yzwfaces[location[0]+(1-dirx)/2][location[1]][location[2]][location[3]])
> 	    may_go=0;}
> 	else if(xzwfaces[location[0]][location[1]+(1-dirx)/2][location[2]][location[3]])
> 	  may_go=0;
>     if(diry!=0 && relloc[0]<ROWS && relloc[0]>=0 && 
>        relloc[2]<ROWS && relloc[2]>=0)
>       if((diry>0 && relloc[1]<ROWS1 && relloc[1]>=0) || 
> 	 (diry<0 && relloc[1]<ROWS && relloc[1]>=-1))
> 	if(dir4[0]==0 && dir4[1]==0)
> 	  {if(xzwfaces[location[0]][location[1]+(1-diry)/2][location[2]][location[3]])
> 	    may_go=0;}
> 	else if(xywfaces[location[0]][location[1]][location[2]+(1-diry)/2][location[3]])
> 	  may_go=0;
>     if(dirz!=0 && relloc[0]<ROWS && relloc[0]>=0 && 
>        relloc[1]<ROWS && relloc[1]>=0)
>       if((dirz>0 && relloc[2]<ROWS1 && relloc[2]>=0) || 
> 	 (dirz<0 && relloc[2]<ROWS && relloc[2]>=-1))
> 	if(dir4[3]!=0){
> 	  if(xywfaces[location[0]][location[1]][location[2]+(1-dirz)/2][location[3]])
> 	    may_go=0;}
> 	else if(xyzfaces[location[0]][location[1]][location[2]][location[3]+(1-dirz)/2])
> 	  may_go=0;
>   }
>   if(moving==8 && relloc[3]>=0 && relloc[3]<ROWS){
>     if(dirx!=0 && relloc[1]<ROWS && relloc[1]>=0 && 
>        relloc[2]<ROWS && relloc[2]>=0)
>       if((dirx<0 && relloc[0]<ROWS1 && relloc[0]>=0) || 
> 	 (dirx>0 && relloc[0]<ROWS && relloc[0]>=-1))
> 	if(dir4[0]==0)
> 	  {if(yzwfaces[location[0]+(dirx+1)/2][location[1]][location[2]][location[3]])
> 	    may_go=0;}
> 	else if(xzwfaces[location[0]][location[1]+(dirx+1)/2][location[2]][location[3]])
> 	  may_go=0;
>     if(diry!=0 && relloc[0]<ROWS && relloc[0]>=0 && 
>        relloc[2]<ROWS && relloc[2]>=0)
>       if((diry<0 && relloc[1]<ROWS1 && relloc[1]>=0) || 
> 	 (diry>0 && relloc[1]<ROWS && relloc[1]>=-1))
> 	if(dir4[0]==0 && dir4[1]==0)
> 	  {if(xzwfaces[location[0]][location[1]+(diry+1)/2][location[2]][location[3]])
> 	    may_go=0;}
> 	else if(xywfaces[location[0]][location[1]][location[2]+(diry+1)/2][location[3]])
> 	  may_go=0;
>     if(dirz!=0 && relloc[0]<ROWS && relloc[0]>=0 && 
>        relloc[1]<ROWS && relloc[1]>=0)
>       if((dirz<0 && relloc[2]<ROWS1 && relloc[2]>=0) || 
> 	 (dirz>0 && relloc[2]<ROWS && relloc[2]>=-1))
> 	if(dir4[3]!=0){
> 	  if(xywfaces[location[0]][location[1]][location[2]+(dirz+1)/2][location[3]])
> 	    may_go=0;}
> 	else if(xyzfaces[location[0]][location[1]][location[2]][location[3]+(dirz+1)/2])
> 	  may_go=0;} 
>   /* 3D collision check */
>   if(moving==9 || moving==10)
>     if(0<=relloc[0] && relloc[0]<ROWS &&
>        0<=relloc[1] && relloc[1]<ROWS &&
>        0<=relloc[2] && relloc[2]<ROWS){
>       if(dir4[0]!=0){
> 	if((moving==9 && dir4[0]==1)||(moving==10 && dir4[0]==-1)){
> 	  if(0<=relloc[3] && relloc[3]<ROWS1)
> 	    if(yzwfaces[location[0]][location[1]][location[2]][location[3]])
> 	      {may_go=0;w_shift=0;} }
> 	else 
> 	  if(-1<=relloc[3] && relloc[3]<ROWS)
> 	    if(yzwfaces[location[0]+1][location[1]][location[2]][location[3]])
> 	      {may_go=0;w_shift=0;} }
>       else if(dir4[1]!=0){
> 	if((moving==9 && dir4[1]==1)||(moving==10 && dir4[1]==-1)){
> 	  if(0<=relloc[3] && relloc[3]<ROWS1)
> 	    if(xzwfaces[location[0]][location[1]][location[2]][location[3]])
> 	      {may_go=0;w_shift=0;} }
> 	else 
> 	  if(-1<=relloc[3] && relloc[3]<ROWS)
> 	    if(xzwfaces[location[0]][location[1]+1][location[2]][location[3]])
> 	      {may_go=0;w_shift=0;} }
>       else if(dir4[2]!=0){
> 	if((moving==9 && dir4[2]==1)||(moving==10 && dir4[2]==-1)){
> 	  if(0<=relloc[3] && relloc[3]<ROWS1)
> 	    if(xywfaces[location[0]][location[1]][location[2]][location[3]])
> 	      {may_go=0;w_shift=0;} }
> 	else 
> 	  if(-1<=relloc[3] && relloc[3]<ROWS)
> 	    if(xywfaces[location[0]][location[1]][location[2]+1][location[3]])
> 	      {may_go=0;w_shift=0;} }
>       else /* dir4[3]!=0 */{
> 	if((moving==9 && dir4[3]==1)||(moving==10 && dir4[3]==-1)){
> 	  if(0<=relloc[3] && relloc[3]<ROWS1)
> 	    if(xyzfaces[location[0]][location[1]][location[2]][location[3]])
> 	      {may_go=0;w_shift=0;} }
> 	else 
> 	  if(-1<=relloc[3] && relloc[3]<ROWS)
> 	    if(xyzfaces[location[0]][location[1]][location[2]][location[3]+1])
> 	      {may_go=0;w_shift=0;} }
>     }/* 4D elevator collision checked, i.e., w-shift collision checked */
>   // return may_go;
>   maygo=may_go;
> }
> 
> void cavetrack(void){
>   /* JUNE 1999: theta_delay to slow down 4D rotation */
>   static int theta_delay=0;
>    /* for conversion/rounding of taff/aff towards end of chaptrack */
>   int ii;
>   float azi,ele,rol,hx,hy,hz,wx,wy,wz;
>   static float ohx,ohy,ohz;   /*old head position */
>   static float hdx,hdy,hdz;   /*old head change in position */
>   static int virgin=1;
>   static float owx,owy,owz;   /*old wand position */
>   static float oazi,oele,orol;   /*old wand orientation */
>   static int opaw = 0,paw, joy = 0;
>   /* static int Theta=0; */
> 
>   if(virgin){virgin=0;hdx=hdy=hdz=0;}
> 
>   if(!CAVEMasterDisplay()) return;  /* doit once per frame */
>   paw = wnd ? (CAVEBUTTON1*2+CAVEBUTTON2)*2+CAVEBUTTON3 : mauspaw;
>   /*joy unused: if(paw) joy = 1; /* activate joystick by clicking any button*/
>   /* if(opaw==0 && paw==1 && moving==0 && morph==0)mode = (mode+1)%(2); /* click modes */
>   /* Could use the varying of the joy.  limits? */
>   if(mode==TURNMODE){ /* if(moving==0){Was gap & siz adjuster} */ }
>   else /*if(mode==FLYMODE)*/ {
>     /* morph==1 case must precede ==0 case, or it immediately freezes */
>     if(morph==1){
>       if(opaw==0 && paw==1)frozen=1-frozen;
>       if(paw==2 || paw==4 || paw==6){morph=0;frozen=0;}/* autotymer off */
>     }
>     if(opaw==0 && paw==1 && morph==0){
>       morph=1;autotymer(1);ohx=ohy=ohz=0; owx=owy=owz=0; hdx=hdy=hdz=0;
>     }/* autotymer on */
>   }
>   if(morph==0){
>     if(opaw == 2 && paw == 3)w_shift=10;
>     if(opaw == 2 && paw == 6)w_shift=9;
>   }
>   if(paw == 7){ /* restart, the most useful feature */
>     deFault(); ohx=ohy=ohz=0; owx=owy=owz=0; hdx=hdy=hdz=0;} 
>   if(wnd){ 
>       CAVEGetWandOrientation(azi,ele,rol);
>       CAVEGetHead(hx,hy,hz);  /*position*/
>       CAVEGetWand(wx,wy,wz);  /*position*/ 
>       }
>    else{  /* fix this for CAVE mouse flying */ 
>       azi = .5*512; ele = .5*-512;
>       } 
>   if((opaw!=2 && paw==2)||(opaw!=4 && paw==4)){
>     oazi=azi;oele=ele;orol=rol;owx=wx;owy=wy;owz=wz;
>   }
>    glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity();
> 
>    /************
>    glTranslatef(-hdx,-hdy,-hdz);/** NEW **/
>    hdx=ohx-hx;hdy=ohy-hy;hdz=ohz-hz;/** NEW **/
>    
>    /*   glTranslatef(ohx-hx,ohy-hy,ohz-hz); */ 
> 
>    if(!frozen){
>      if(morph)autotymer(0); /* advance autotymer */
>      if(mode==TURNMODE) glTranslatef(aff[12],aff[13],aff[14]);
>      switch(moving){
>      case 0 : if(morph)break;/* only used when autotymer has first=1 */
>        if(paw==2){
> 	 if((ele-oele)>20 && ABS(azi-oazi)<26)moving=6;
> 	 else if((ele-oele)<-26 && ABS(azi-oazi)<20)moving=5;
> 	 else if(ABS(azi-oazi)>=26 && (azi-oazi)>ABS(rol-orol))moving=3;
> 	 else if(ABS(azi-oazi)>=26 && (azi-oazi)<-ABS(rol-orol))moving=4;
> 	 else if((rol-orol)>=28)moving=2;
> 	 else if((orol-rol)>=28)moving=1;
> 	 else if((wz-owz)<-.2)moving=7;
> 	 else if((wz-owz)>.2)moving=8;
>        }/* Only make a move if holding button 2 */
>        else if(paw==4){
> 	 int most_movement;
> 	 if(ABS(wx-owx)>ABS(wz-owz)){
> 	   if(ABS(wx-owx)>ABS(wy-owy))most_movement=0;
> 	   else most_movement=1;}
> 	 else if(ABS(wy-owy)>ABS(wz-owz))most_movement=1;
> 	 else most_movement=2;
> 
> 	 /* Move the wand in the direction that you want +hidden to point */
> 	 if(most_movement==0){
> 	   if((wx-owx)>.4)moving=11;
> 	   else if((wx-owx)<-.4)moving=12;}
> 	 else if(most_movement==1){
> 	   if((wy-owy)<-.4)moving=14;
> 	   else if((wy-owy)>.4)moving=13;}
> 	 else{
> 	   if((wz-owz)<-.4)moving=15;
> 	   else if((wz-owz)>.4)moving=16;}
> 	 if(moving>10){
> 	   owx=wx;owy=wy;owz=wz;
> 	   if(mode==TURNMODE)moving=0;}
>        }
>        else moving=0; /* Just in case, from leftover code */
>        if(w_shift!=0)moving=w_shift;
>        if(maygo==0)moving=0;
>        break;/* case 0 */
>    case 1 : Theta+=rot_jump[speed3D];glRotatef((-1*rot_jump[speed3D]),0.,0.,1.);break;
>    case 2 : Theta+=rot_jump[speed3D];glRotatef( rot_jump[speed3D],0.,0.,1.);break;
>    case 3 : Theta+=rot_jump[speed3D];glRotatef( rot_jump[speed3D],0.,1.,0.);break;
>    case 4 : Theta+=rot_jump[speed3D];glRotatef((-1*rot_jump[speed3D]),0.,1.,0.);break;
>    case 5 : Theta+=rot_jump[speed3D];glRotatef((-1*rot_jump[speed3D]),1.,0.,0.);break;
>    case 6 : Theta+=rot_jump[speed3D];glRotatef( rot_jump[speed3D],1.,0.,0.);break;
> /* MAY 1999: cases 11-16 redone */
>    case 11: 
>      if(Theta==0){
>        YZinitRotate(1);set_inter_vars();
>        if(ActualAxes<0)YZrotate(1);}
>      if(theta_delay==0)Theta+=theta_jump;
>      theta_delay+=1;
>      if(theta_delay==max_theta_delay)theta_delay=0;
>      if(Theta>=90){if(ActualAxes>0)YZrotate(1);}
>      break;
>    case 12: 
>      if(Theta==0){
>        YZinitRotate(-1);set_inter_vars();
>        if(ActualAxes<0)YZrotate(-1);}
>      if(theta_delay==0)Theta+=theta_jump;
>      theta_delay+=1;
>      if(theta_delay==max_theta_delay)theta_delay=0;
>      if(Theta>=90){if(ActualAxes>0)YZrotate(-1);}
>      break;
>    case 13:
>      if(Theta==0){
>        XZinitRotate(1);set_inter_vars();
>        if(ActualAxes<0)XZrotate(1);}
>      if(theta_delay==0)Theta+=theta_jump;
>      theta_delay+=1;
>      if(theta_delay==max_theta_delay)theta_delay=0;
>      if(Theta>=90){if(ActualAxes>0)XZrotate(1);}
>      break;
>    case 14: 
>      if(Theta==0){
>        XZinitRotate(-1);set_inter_vars();
>        if(ActualAxes<0)XZrotate(-1);}
>      if(theta_delay==0)Theta+=theta_jump;
>      theta_delay+=1;
>      if(theta_delay==max_theta_delay)theta_delay=0;
>      if(Theta>=90){if(ActualAxes>0)XZrotate(-1);}
>      break;
>    case 15: 
>      if(Theta==0){
>        XYinitRotate(1);set_inter_vars();
>        if(ActualAxes<0)XYrotate(1);}
>      if(theta_delay==0)Theta+=theta_jump;
>      theta_delay+=1;
>      if(theta_delay==max_theta_delay)theta_delay=0;
>      if(Theta>=90){if(ActualAxes>0)XYrotate(1);}
>      break;
>    case 16: 
>      if(Theta==0){
>        XYinitRotate(-1);set_inter_vars();
>        if(ActualAxes<0)XYrotate(-1);}
>      if(theta_delay==0)Theta+=theta_jump;
>      theta_delay+=1;
>      if(theta_delay==max_theta_delay)theta_delay=0;
>      if(Theta>=90){if(ActualAxes>0)XYrotate(-1);}
>      break;
> /* MAY 1999: cases 11-16 redone */
>      }/* switch */
>    if(mode==FLYMODE){
>       glPushMatrix();
>       glMultMatrixf(starmat);
>       glGetFloatv(GL_MODELVIEW_MATRIX,starmat);
>       glPopMatrix();
>    }
> #define  DOT3(M,i) (M[4*i]*M[12]+M[4*i+1]*M[13]+M[4*i+2]*M[14])
>    switch(moving){
>    case 7 : Theta+=fore_jump[speed3D];glTranslatef(0.,0.,-1*fore_jump[speed3D]*siz);break;
>    case 8 : Theta+=fore_jump[speed3D];glTranslatef(0.,0.,fore_jump[speed3D]*siz);break;
>    case 9:
>      if(dir4[3]!=0)location[3]-=dir4[3];
>      else if(dir4[2]!=0)location[2]-=dir4[2];
>      else if(dir4[1]!=0)location[1]-=dir4[1];
>      else location[0]-=dir4[0];break;
>    case 10:
>      if(dir4[3]!=0)location[3]+=dir4[3];
>      else if(dir4[2]!=0)location[2]+=dir4[2];
>      else if(dir4[1]!=0)location[1]+=dir4[1];
>      else location[0]+=dir4[0];break;
>    }/* switch */
> 
> /* Maybe in the future will be able to use more wand translate tractor */
>      if(mode == TURNMODE) glTranslatef(-aff[12],-aff[13],-aff[14]);
>    }/* if(!frozen) */
> 
>    glMultMatrixf(aff); 
>    glGetFloatv(GL_MODELVIEW_MATRIX,aff);
> 
>   glPopMatrix();
>   opaw=paw; ohx=hx; ohy=hy; ohz=hz;
> 
>   /* Replace lagmvg cases: */
>   switch(moving){
>   case 1:
>   case 2:
>   case 3:
>   case 4:
>   case 5:
>   case 6: if(Theta>=90){
>     Theta=0;moving=0;/* printf("\nrotated in 3D:"); */
>     roundaff();aff2taff();
>   }break;
>   case 7: if(Theta>=8){
>     Theta=0;moving=0;
>     /* update location */ 
>     if(dir4[3]!=0){
>       if(aff[2]!=0)location[0]+=(int)aff[2];
>       else if(aff[6]!=0)location[1]+=(int)aff[6];
>       else location[2]+=(int)aff[10];}
>     else if(dir4[2]!=0){
>       if(aff[2]!=0)location[0]+=(int)aff[2];
>       else if(aff[6]!=0)location[1]+=(int)aff[6];
>       else location[3]+=(int)aff[10];}
>     else if(dir4[1]!=0){
>       if(aff[2]!=0)location[0]+=(int)aff[2];
>       else if(aff[6]!=0)location[2]+=(int)aff[6];
>       else location[3]+=(int)aff[10];}
>     else /*dir4[1]!=0*/{
>       if(aff[2]!=0)location[1]+=(int)aff[2];
>       else if(aff[6]!=0)location[2]+=(int)aff[6];
>       else location[3]+=(int)aff[10];}
>     /* printf("\nmoved in 3D:"); */roundaff();aff2taff();
>   }break;
>   case 8: if(Theta>=8){
>     Theta=0;moving=0;
>     /* update location */
>     if(dir4[3]!=0){
>       if(aff[2]!=0)location[0]-=(int)aff[2];
>       else if(aff[6]!=0)location[1]-=(int)aff[6];
>       else location[2]-=(int)aff[10];}
>     else if(dir4[2]!=0){
>       if(aff[2]!=0)location[0]-=(int)aff[2];
>       else if(aff[6]!=0)location[1]-=(int)aff[6];
>       else location[3]-=(int)aff[10];}
>     else if(dir4[1]!=0){
>       if(aff[2]!=0)location[0]-=(int)aff[2];
>       else if(aff[6]!=0)location[2]-=(int)aff[6];
>       else location[3]-=(int)aff[10];}
>     else /*dir4[1]!=0*/{
>       if(aff[2]!=0)location[1]-=(int)aff[2];
>       else if(aff[6]!=0)location[2]-=(int)aff[6];
>       else location[3]-=(int)aff[10];}
>     /* printf("\nmoved in 3D:"); */roundaff();aff2taff();
>   }break;
>   case 9:
>   case 10: moving=0;w_shift=0;/* printf("\nmoved in 4D:"); */aff2taff();
>     break;
>   case 11:
>   case 12:
>   case 13:
>   case 14:
>   case 15:
>   case 16: 
>     if(Theta==theta_jump)rotating_in_4D=1; 
>     if(Theta>=90){
>       Theta=0;moving=0;w_shift=0;rotating_in_4D=0;theta_delay=0;
>       /* printf("\nrotated in 4D:"); */
>       break; 
>     }
>   }  
>    audiofunc();
> }
> 
> void drawcaveinit(void){   /* kludge? needed why ? */  
>    CAVEFar = 1000.;
>    glClearColor(0.,0.,0.,0.);
>    glClearDepth(1.);
> }
> 
> void drawcave(void){
>   float hx,hy,hz;
>   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
>   drawstars();
>   graffiti();
>   CAVEGetHead(hx,hy,hz);
>   glMatrixMode(GL_MODELVIEW);
>   glPushMatrix();
>   glTranslatef(0,MIDHEIGHT,0);
>   glMultMatrixf(aff);
>   /*  glScalef(siz,siz,siz); */
>   drawall();
>   glPopMatrix();
> }
> #endif
> 
> float speedometer(void){
> double dbl; static double rate; static int ii=0;
> static struct timezone notused; static struct timeval now, then;
>    if(++ii % 8 == 0){  /* 8 times around measure time */
>       gettimeofday(&now, &notused); /* elapsed time */
>       dbl =  (double)(now.tv_sec - then.tv_sec)
>          +(double)(now.tv_usec - then.tv_usec)/1000000;
>       then = now;  rate = 8/dbl;
>       }
>    return((float)rate);
> }
>  
> void messages(void){     
>   char buf[256]; /* console messages are done differently from cave */
> #define  LABEL2(x,y,W,u) {sprintf(buf,(W),(u));char2wall(x,y,0.,buf);}
>   glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity();
>   gluOrtho2D(0,3000,0,3000);
>   glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity();
>       /*bull's eye*/
>   if(mode==TURNMODE)glColor3f(0.,1.,1.);
>   else glColor3f(1.,1.,1.);
>       LABEL2(1500,1500,"%s","o");
>       /* writings */
>       LABEL2(80,80,"%4.1f fps",speedometer());
>       LABEL2(80,2770,"(ESC)ape (Z)ap (V)Binoc%s","");
>       LABEL2(10,10,"4dMaze       \
>    by Michael Pelsmajer, U Illinois, 2000 %s","");
>       LABEL2(80,2840,"(N)ose   %0.3f",nose);
>       LABEL2(80,2700,"(W)riting    3d (S)peed %d",(1+speed3D));   
>       LABEL2(80,2630,"near clipper %g", mysiz*focal);
>       LABEL2(80,2560,"f(O)cal factor %g",focal);
>       LABEL2(80,2490,"my s(I)ze %.2g",mysiz);
>       LABEL2(80,2420,"far cli(P)er= %.2g",far);
>       LABEL2(80,2350,"(g)ap %1.3f",gap);
>       if(mode==FLYMODE){
>       LABEL2(80,2280,"(h) %sDemo","");
>       }
>       LABEL2(80,2210,"(f)reeze/unfreeze motion%s","");
>       if(mode==FLYMODE){
>       LABEL2(80,2140,"Take grey cubic s(T)airs%s","");
>       LABEL2(80,2070,"Take purple diamond s(t)airs%s","");
>       }
>       if(mode==TURNMODE){LABEL2(80,2910,"%sRotations are temporary in this mode.","");}
>       /*
>       LABEL2(80,1930,"Print 4D (A)ffine matrix or 5D (a)ffine matrix%s","");
>       */
>       if(mode==FLYMODE){
>       LABEL2(80,2910,"1,2,3 & shift-1,2,3: Rotate the +hidden axis to your right/left up/down front/back%s","");
>       }
>       if(mode==FLYMODE)
>       {sprintf(buf,"Absolute location: %d %d %d %d",location[0],location[1],
> 	       location[2],location[3]);char2wall(10,150,0.,buf);}
>       glPopMatrix();
> glMatrixMode(GL_PROJECTION); glPopMatrix();
> } 
> 
> void chaptrack(int but,int xx,int yy,int shif){  
>   /* JUNE 1999: theta_delay to slow down 4D rotation */
>   static int theta_delay=0;
>   /* MAY 1999: commented it out: */
>   /* static int Theta=0; */
>    /* for conversion/rounding of taff/aff towards end of chaptrack */
>    long dx,dy; 
>    dx = xx -.5*xt; dy = yy -.5*yt; 
>    glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity();
>    if(mode==TURNMODE) glTranslatef(aff[12],aff[13],aff[14]);
>    switch(moving){
>    case 0 : if(morph)break;/* only used when autotymer has first=1 */
>      if(but&(1<<GLUT_RIGHT_BUTTON ))moving=1;
>      if(but&(1<<GLUT_LEFT_BUTTON  ))moving=2;
>      if(dx>90 && dx>ABS(dy))moving=3;else if(dx<-90 && -1*dx>ABS(dy))moving=4;
>      else if(dy>90)moving=6;else if(dy<-90)moving=5;
>      else if(mode==FLYMODE && (but&(1<<GLUT_MIDDLE_BUTTON)))moving=8-shif;
>      if(w_shift!=0)moving=w_shift;
>      if(maygo==0)moving=0;
>      break;/* case 0 */
>    case 1 : Theta+=rot_jump[speed3D];glRotatef(-1*rot_jump[speed3D],0.,0.,1.);break;
>    case 2 : Theta+=rot_jump[speed3D];glRotatef( rot_jump[speed3D],0.,0.,1.);break;
>    case 3 : Theta+=rot_jump[speed3D];glRotatef( rot_jump[speed3D],0.,1.,0.);break;
>    case 4 : Theta+=rot_jump[speed3D];glRotatef(-1*rot_jump[speed3D],0.,1.,0.);break;
>    case 5 : Theta+=rot_jump[speed3D];glRotatef(-1*rot_jump[speed3D],1.,0.,0.);break;
>    case 6 : Theta+=rot_jump[speed3D];glRotatef( rot_jump[speed3D],1.,0.,0.);break;
> /* MAY 1999: cases 11-16 redone */
>    case 11: 
>      if(Theta==0){
>        /* different place in cavetrack */
>        rotating_in_4D=1; YZinitRotate(1);set_inter_vars();
>        if(ActualAxes<0)YZrotate(1);}
>      if(theta_delay==0)Theta+=theta_jump;
>      theta_delay+=1;
>      if(theta_delay==max_theta_delay)theta_delay=0;
>      if(Theta>=90){if(ActualAxes>0)YZrotate(1);}
>      break;
>    case 12:
>      if(Theta==0){
>        rotating_in_4D=1; YZinitRotate(-1);set_inter_vars();
>        if(ActualAxes<0)YZrotate(-1);}
>      if(theta_delay==0)Theta+=theta_jump;
>      theta_delay+=1;
>      if(theta_delay==max_theta_delay)theta_delay=0;
>      if(Theta>=90){if(ActualAxes>0)YZrotate(-1);}
>      break;
>    case 13: 
>      if(Theta==0){
>        rotating_in_4D=1; XZinitRotate(1);set_inter_vars();
>        if(ActualAxes<0)XZrotate(1);}
>      if(theta_delay==0)Theta+=theta_jump;
>      theta_delay+=1;
>      if(theta_delay==max_theta_delay)theta_delay=0;
>      if(Theta>=90){if(ActualAxes>0)XZrotate(1);}
>      break;
>    case 14: 
>      if(Theta==0){
>        rotating_in_4D=1; XZinitRotate(-1);set_inter_vars();
>        if(ActualAxes<0)XZrotate(-1);}
>      if(theta_delay==0)Theta+=theta_jump;
>      theta_delay+=1;
>      if(theta_delay==max_theta_delay)theta_delay=0;
>      if(Theta>=90){if(ActualAxes>0)XZrotate(-1);}
>      break;
>    case 15: 
>      if(Theta==0){
>        rotating_in_4D=1; XYinitRotate(1);set_inter_vars();
>        if(ActualAxes<0)XYrotate(1);}
>      if(theta_delay==0)Theta+=theta_jump;
>      theta_delay+=1;
>      if(theta_delay==max_theta_delay)theta_delay=0;
>      if(Theta>=90){if(ActualAxes>0)XYrotate(1);}
>      break;
>    case 16: 
>      if(Theta==0){
>        rotating_in_4D=1; XYinitRotate(-1);set_inter_vars();
>        if(ActualAxes<0)XYrotate(-1);}
>      if(theta_delay==0)Theta+=theta_jump;
>      theta_delay+=1;
>      if(theta_delay==max_theta_delay)theta_delay=0;
>      if(Theta>=90){if(ActualAxes>0)XYrotate(-1);}
>      break;
> /* MAY 1999: cases 11-16 redone */
>    }/* switch */
> 
>    if(mode==FLYMODE){
>       glPushMatrix();
>       glMultMatrixf(starmat);
>       glGetFloatv(GL_MODELVIEW_MATRIX,starmat);
>       glPopMatrix();
>    }
> #define  DOT3(M,i) (M[4*i]*M[12]+M[4*i+1]*M[13]+M[4*i+2]*M[14])
>    switch(moving){
>    case 7 : Theta+=fore_jump[speed3D];glTranslatef(0.,0.,-1*fore_jump[speed3D]*siz);break;
>    case 8 : Theta+=fore_jump[speed3D];glTranslatef(0.,0.,fore_jump[speed3D]*siz);break;
>    case 9:
>      if(dir4[3]!=0)location[3]-=dir4[3];
>      else if(dir4[2]!=0)location[2]-=dir4[2];
>      else if(dir4[1]!=0)location[1]-=dir4[1];
>      else location[0]-=dir4[0];break;
>    case 10:
>      if(dir4[3]!=0)location[3]+=dir4[3];
>      else if(dir4[2]!=0)location[2]+=dir4[2];
>      else if(dir4[1]!=0)location[1]+=dir4[1];
>      else location[0]+=dir4[0];break;
>    }/* switch */
>    if(mode==TURNMODE) glTranslatef(-aff[12],-aff[13],-aff[14]);
>    glMultMatrixf(aff); 
>    glGetFloatv(GL_MODELVIEW_MATRIX,aff);
>    {int ii,jj; for(ii=0;ii<3;ii++){ lu[ii]=0; /* calculite */
>    for(jj=0;jj<3;jj++) lu[ii] += aff[ii*4+jj]*lux[jj];}}
>    glPopMatrix();
> 
>   /* Replace lagmvg cases: */
>   switch(moving){
>   case 1:
>   case 2:
>   case 3:
>   case 4:
>   case 5:
>   case 6: if(Theta>=90){
>     Theta=0;moving=0;/* printf("\nrotated in 3D:"); */
>     roundaff();aff2taff();
>   }break;
>   case 7: if(Theta>=8){
>     Theta=0;moving=0;
>     /* update location */ 
>     if(dir4[3]!=0){
>       if(aff[2]!=0)location[0]+=(int)aff[2];
>       else if(aff[6]!=0)location[1]+=(int)aff[6];
>       else location[2]+=(int)aff[10];}
>     else if(dir4[2]!=0){
>       if(aff[2]!=0)location[0]+=(int)aff[2];
>       else if(aff[6]!=0)location[1]+=(int)aff[6];
>       else location[3]+=(int)aff[10];}
>     else if(dir4[1]!=0){
>       if(aff[2]!=0)location[0]+=(int)aff[2];
>       else if(aff[6]!=0)location[2]+=(int)aff[6];
>       else location[3]+=(int)aff[10];}
>     else /*dir4[1]!=0*/{
>       if(aff[2]!=0)location[1]+=(int)aff[2];
>       else if(aff[6]!=0)location[2]+=(int)aff[6];
>       else location[3]+=(int)aff[10];}
>     /* printf("\nmoved in 3D:");*/roundaff();aff2taff();
>   }break;
>   case 8: if(Theta>=8){
>     Theta=0;moving=0;
>     /* update location */
>     if(dir4[3]!=0){
>       if(aff[2]!=0)location[0]-=(int)aff[2];
>       else if(aff[6]!=0)location[1]-=(int)aff[6];
>       else location[2]-=(int)aff[10];}
>     else if(dir4[2]!=0){
>       if(aff[2]!=0)location[0]-=(int)aff[2];
>       else if(aff[6]!=0)location[1]-=(int)aff[6];
>       else location[3]-=(int)aff[10];}
>     else if(dir4[1]!=0){
>       if(aff[2]!=0)location[0]-=(int)aff[2];
>       else if(aff[6]!=0)location[2]-=(int)aff[6];
>       else location[3]-=(int)aff[10];}
>     else /*dir4[1]!=0*/{
>       if(aff[2]!=0)location[1]-=(int)aff[2];
>       else if(aff[6]!=0)location[2]-=(int)aff[6];
>       else location[3]-=(int)aff[10];}
>     /* printf("\nmoved in 3D:"); */roundaff();aff2taff();
>   }break;
>   case 9:
>   case 10: moving=0;w_shift=0;/* printf("\nmoved in 4D:"); */aff2taff();
>     break;
>   case 11:
>   case 12:
>   case 13:
>   case 14:
>   case 15:
>   case 16: if(Theta>=90){
>     Theta=0;moving=0;w_shift=0;rotating_in_4D=0;theta_delay=0;
>     /* printf("\nrotated in 4D:"); */
>     /* These are not needed, right? */ roundtaff();taff2aff();
>     break; 
>   }
>   }  
> }
> 
> void reshaped(int xx, int yy){xt=xx ; yt=yy;}   /* what is this for ? */
> 
> void drawcons(void){  
>   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
>   if(binoc) glViewport(0,yt/4,xt/2,yt/2);
>   glMatrixMode(GL_PROJECTION); glLoadIdentity();
>   glFrustum(-mysiz*xt/yt,mysiz*xt/yt,-mysiz,mysiz,mysiz*focal,far); 
>   glMatrixMode(GL_MODELVIEW); glLoadIdentity();
>   drawstars();
>   glTranslatef(-binoc*nose,0.0,0.0);
>   glMultMatrixf(aff);
>   drawall();
>   if(binoc){
>     glViewport(xt/2,yt/4,xt/2,yt/2); 
>     glMatrixMode(GL_MODELVIEW);
>     glLoadIdentity();
>     drawstars();
>     glTranslatef(binoc*nose,0.0,0.0);
>     glMultMatrixf(aff);
>     drawall();
>     }
>   glViewport(0,0,xt,yt);
>   if(msg) messages();
>   glutSwapBuffers();
> }
> 
> void idle(void){ /*do this when nothing else is happening*/
> if(frozen==0){
>   if(morph)autotymer(0);  /* advance autotymer */ 
>   glutPostRedisplay();  /*redraw the window*/
>   chaptrack(BUT,XX,YY,SHIF);
>   audiofunc();} 
> }
> 
> void mousepushed(int but,int stat,int x,int y){
>   if(stat==GLUT_DOWN) BUT |= (1<<but);
>   else BUT &= (-1 ^ (1<<but));  
>   XX=x; YY=y; SHIF=(glutGetModifiers()==GLUT_ACTIVE_SHIFT)?1:0;
> }
> 
> void mousemoved(int x,int y){  XX=x; YY=y; }
> 
> void main(int argc, char **argv){  
>    arguments(argc,argv); 
>    if(caveyes)
> #ifdef CAVE
>        CAVEConfigure(&argc, argv, NULL)
> #endif
>        ;
>    dataprep();
>  
>    if(caveyes){
> #ifdef CAVE
>        CAVEInit(); /* Each wall is (part of) a forked process from here on.*/ 
>        CAVEFrameFunction(cavetrack,0);  
>        /*is restricted to MasterWall, so once per frame */
>        CAVEInitApplication(drawcaveinit, 0); 
>        CAVEDisplay(drawcave, 0);
>        while(!CAVEgetbutton(CAVE_ESCKEY))
>          {
>          cavekeybo();  /* is asynchronous from display processes */ 
> 	 }
>        audioclean();
>        CAVEExit();
> #endif
>        ;}
>      else{ /*console main*/
>        glutInit(&argc, argv);
>        glutInitDisplayMode(GLUT_DOUBLE|GLUT_DEPTH);
>        switch(win)
>          { case 0: break;
>            case 1: glutInitWindowSize(640, 480);
>                    glutInitWindowPosition(0,1024-480);
>                    break;
>            case 2: glutInitWindowPosition(0,0);
> 	           break;
>          }
>        glutCreateWindow("<* illiSkel in C/OpenGL/GLUT *>");
>        if(win==2) glutFullScreen();
>        glEnable(GL_DEPTH_TEST);
>        glutDisplayFunc(drawcons);       
>        glutKeyboardFunc(keyboard);
>        glutSpecialFunc(special_keybo);
>        glutMouseFunc(mousepushed);
>        glutMotionFunc(mousemoved);       
>        glutPassiveMotionFunc(mousemoved); 
>        glutReshapeFunc(reshaped);
>        glutIdleFunc(idle);             
>        glutMainLoop();
>        }
> } 
