#define TEXTURES

/* Wand controls are fine-tuned to this particular CAVE, with its slightly
   off-center reading of some wand angles */
/* 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 

#ifdef TEXTURES
#include "readtex.h"
#endif

#define NUM_Textures 11
/* Must correspond with definetextures()*/

struct textures{
  char      name[100];
  int       width, height, components;
  unsigned  *image;
};

struct tex_ptrs{
  struct textures texture[NUM_Textures];
} *tex_ptr;  

#ifdef TEXTURES

void definetextures(void){
  strcpy(tex_ptr->texture[0].name,"images/g.t.rgb");
  strcpy(tex_ptr->texture[1].name,"images/ug.rgb");
  strcpy(tex_ptr->texture[2].name,"images/u-c.rgb");
  strcpy(tex_ptr->texture[3].name,"images/stalin.rgb");
  strcpy(tex_ptr->texture[4].name,"images/0496cox128.rgb");
  strcpy(tex_ptr->texture[5].name,"images/0497cox128.rgb");
  strcpy(tex_ptr->texture[6].name,"images/0616cox128.rgb");
  strcpy(tex_ptr->texture[7].name,"images/0967cox_crop128.rgb");
  strcpy(tex_ptr->texture[8].name,"images/0970cox128.rgb");
  strcpy(tex_ptr->texture[9].name,"images/conch128.rgb");
  strcpy(tex_ptr->texture[10].name,"images/hughesevert128.rgb");
}
/* jj in setmaze() = jj-2 in definetextures() */

int texinit(struct textures *t){
  if(strlen(t->name)>0 && t->image==NULL){
    t->image = read_texture(t->name, &t->width, &t->height, &t->components);
    if (t->image == NULL){
      fprintf(stderr, "Error: Can't load image file \"%s\".\n",t->name);
      return 0;} 
    else{
      printf("%s:%d x %d image loaded with ", t->name, t->width, t->height);
      printf("%d components\n", t->components);}
    if (t->components < 3 || t->components > 4){
      printf("Error: must be RGB or RGBA image\n");
      return 0;}
  }
  return 1;
}

void texenable(struct textures *t){
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexImage2D(GL_TEXTURE_2D, 0, t->components, t->width, t->height, 
	       0, GL_RGBA, GL_UNSIGNED_BYTE, t->image);
}

#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)
#define ADDall(M,i,j,k,siz) siz*(M[0]+i),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>

/* 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 */
/* jj in definetextures = jj+2 in setmaze; in setmaze 0=(no wall), 1=color */
/* 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]=2;/* texture */

 /* 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]=3;/* texture */
 xywfaces[1][4][3][2]=1;xywfaces[2][4][3][2]=1;
 yzwfaces[3][4][2][2]=4;/* texture */
 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[2][4][2][2]=1;

 /* Big Room Entrance */
 yzwfaces[2][4][2][3]=1;
 xyzfaces[1][4][2][3]=1;xyzfaces[3][4][2][3]=5;/* texture */
 for(ii=2;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]=6;}/* texture */
 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;
 }
 xzwfaces[6][3][1][3]=7;xzwfaces[6][6][1][3]=7;/* texture *//* texture */
 xzwfaces[6][3][2][3]=8;xzwfaces[6][6][2][3]=8;/* texture *//* texture */
 xzwfaces[6][3][3][3]=9;xzwfaces[6][6][3][3]=9;/* texture *//* texture */
 yzwfaces[8][4][2][3]=10;/* texture */
 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;
}

#define COLOR_BLOCK(ii,jj,kk,del,siz) \
       glVertex3f(ADDall(vv[ii][jj][kk],del,del,del,siz)); \
       glVertex3f(ADDall(vv[ii+1][jj][kk],-1*del,del,del,siz)); \
       glVertex3f(ADDall(vv[ii][jj+1][kk],del,-1*del,del,siz)); \
       glVertex3f(ADDall(vv[ii+1][jj+1][kk],-1*del,-1*del,del,siz)); \
       glVertex3f(ADDall(vv[ii][jj+1][kk+1],del,-1*del,-1*del,siz)); \
       glVertex3f(ADDall(vv[ii+1][jj+1][kk+1],-1*del,-1*del,-1*del,siz)); \
       glVertex3f(ADDall(vv[ii+1][jj][kk],-1*del,del,del,siz)); \
       glVertex3f(ADDall(vv[ii+1][jj][kk+1],-1*del,del,-1*del,siz)); \
       glVertex3f(ADDall(vv[ii][jj][kk],del,del,del,siz)); \
       glVertex3f(ADDall(vv[ii][jj][kk+1],del,del,-1*del,siz)); \
       glVertex3f(ADDall(vv[ii][jj+1][kk],del,-1*del,del,siz)); \
       glVertex3f(ADDall(vv[ii][jj+1][kk+1],del,-1*del,-1*del,siz)); \
       glVertex3f(ADDall(vv[ii+1][jj][kk+1],-1*del,del,-1*del,siz)); \
       glVertex3f(ADDall(vv[ii+1][jj+1][kk+1],-1*del,-1*del,-1*del,siz));

draw_fourthD(int LL, float del, int dir4[4],float siz){int ii,jj,kk;
 if(LL<0 || LL>ROWS)return;
 if(dir4[3]!=0){
   for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)for(kk=0;kk<ROWS;kk++)
     if (xyzfaces[ii][jj][kk][LL]){
       glBegin(GL_TRIANGLE_STRIP);glColor3f(1.0,1.0,0.);
       COLOR_BLOCK(ii,jj,kk,del,siz);
       glEnd();}}
 else if(dir4[2]!=0){
   for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)for(kk=0;kk<ROWS;kk++)
     if(xywfaces[ii][jj][LL][kk]){
       glBegin(GL_TRIANGLE_STRIP);glColor3f(0.,0.,1.0);
       COLOR_BLOCK(ii,jj,kk,del,siz);
       glEnd();}}
 else if(dir4[1]!=0){
   for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)for(kk=0;kk<ROWS;kk++)
     if(xzwfaces[ii][LL][jj][kk]){
       glBegin(GL_TRIANGLE_STRIP);glColor3f(0.,1.0,0.);
       COLOR_BLOCK(ii,jj,kk,del,siz);
       glEnd();}}
  else /*dir4[2]!=0*/{
   for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)for(kk=0;kk<ROWS;kk++)
     if(yzwfaces[LL][ii][jj][kk]){
       glBegin(GL_TRIANGLE_STRIP);glColor3f(1.0,0.,0.);
       COLOR_BLOCK(ii,jj,kk,del,siz);
       glEnd();}}
}

#define TEX_BLOCK(ii,jj,kk,del,siz) \
       glTexCoord2f(0.0, 0.0);glVertex3f(ADDall(vv[ii][jj][kk],del,del,del,siz)); \
       glTexCoord2f(1.0, 0.0);glVertex3f(ADDall(vv[ii+1][jj][kk],-1*del,del,del,siz)); \
       glTexCoord2f(1.0, 1.0);glVertex3f(ADDall(vv[ii+1][jj+1][kk],-1*del,-1*del,del,siz)); \
       glTexCoord2f(0.0, 1.0);glVertex3f(ADDall(vv[ii][jj+1][kk],del,-1*del,del,siz)); \
       glTexCoord2f(0.0, 0.0);glVertex3f(ADDall(vv[ii][jj][kk+1],del,del,-1*del,siz)); \
       glTexCoord2f(1.0, 0.0);glVertex3f(ADDall(vv[ii+1][jj][kk+1],-1*del,del,-1*del,siz)); \
       glTexCoord2f(1.0, 1.0);glVertex3f(ADDall(vv[ii+1][jj+1][kk+1],-1*del,-1*del,-1*del,siz)); \
       glTexCoord2f(0.0, 1.0);glVertex3f(ADDall(vv[ii][jj+1][kk+1],del,-1*del,-1*del,siz)); \
       glTexCoord2f(0.0, 0.0);glVertex3f(ADDall(vv[ii][jj][kk],del,del,del,siz)); \
       glTexCoord2f(1.0, 0.0);glVertex3f(ADDall(vv[ii+1][jj][kk],-1*del,del,del,siz)); \
       glTexCoord2f(1.0, 1.0);glVertex3f(ADDall(vv[ii+1][jj][kk+1],-1*del,del,-1*del,siz)); \
       glTexCoord2f(0.0, 1.0);glVertex3f(ADDall(vv[ii][jj][kk+1],del,del,-1*del,siz)); \
       glTexCoord2f(0.0, 0.0);glVertex3f(ADDall(vv[ii][jj+1][kk],del,-1*del,del,siz)); \
       glTexCoord2f(1.0, 0.0);glVertex3f(ADDall(vv[ii+1][jj+1][kk],-1*del,-1*del,del,siz)); \
       glTexCoord2f(1.0, 1.0);glVertex3f(ADDall(vv[ii+1][jj+1][kk+1],-1*del,-1*del,-1*del,siz)); \
       glTexCoord2f(0.0, 1.0);glVertex3f(ADDall(vv[ii][jj+1][kk+1],del,-1*del,-1*del,siz)); \
       glTexCoord2f(0.0, 0.0);glVertex3f(ADDall(vv[ii][jj][kk],del,del,del,siz)); \
       glTexCoord2f(1.0, 0.0);glVertex3f(ADDall(vv[ii][jj+1][kk],del,-1*del,del,siz)); \
       glTexCoord2f(1.0, 1.0);glVertex3f(ADDall(vv[ii][jj+1][kk+1],del,-1*del,-1*del,siz)); \
       glTexCoord2f(0.0, 1.0);glVertex3f(ADDall(vv[ii][jj][kk+1],del,del,-1*del,siz)); \
       glTexCoord2f(0.0, 0.0);glVertex3f(ADDall(vv[ii+1][jj][kk],-1*del,del,del,siz)); \
       glTexCoord2f(1.0, 0.0);glVertex3f(ADDall(vv[ii+1][jj+1][kk],-1*del,-1*del,del,siz)); \
       glTexCoord2f(1.0, 1.0);glVertex3f(ADDall(vv[ii+1][jj+1][kk+1],-1*del,-1*del,-1*del,siz)); \
       glTexCoord2f(0.0, 1.0);glVertex3f(ADDall(vv[ii+1][jj][kk+1],-1*del,del,-1*del,siz));

#ifdef TEXTURES
draw_tex_fourthD(int LL, float del, int dir4[4],float siz){int ii,jj,kk;
 int ctex;/* current texture */
 if(LL<0 || LL>ROWS)return;
 if(dir4[3]!=0){
   for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)for(kk=0;kk<ROWS;kk++){
     if (xyzfaces[ii][jj][kk][LL]==1){
       glBegin(GL_TRIANGLE_STRIP);glColor3f(1.0,1.0,0.);
       COLOR_BLOCK(ii,jj,kk,del,siz);
       glEnd();}
     else if(xyzfaces[ii][jj][kk][LL]>1){
       ctex=xyzfaces[ii][jj][kk][LL];
       texenable(&tex_ptr->texture[ctex-2]);
       glEnable(GL_TEXTURE_2D);glBegin(GL_QUADS);
       TEX_BLOCK(ii,jj,kk,del,siz);
       glEnd();glDisable(GL_TEXTURE_2D);}
   }
 }
 else if(dir4[2]!=0){
   for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)for(kk=0;kk<ROWS;kk++){
     if(xywfaces[ii][jj][LL][kk]==1){
       glBegin(GL_TRIANGLE_STRIP);glColor3f(0.,0.,1.0);
       COLOR_BLOCK(ii,jj,kk,del,siz);
       glEnd();}
     else if(xywfaces[ii][jj][LL][kk]>1){
       ctex=xywfaces[ii][jj][LL][kk];
       texenable(&tex_ptr->texture[ctex-2]);
       glEnable(GL_TEXTURE_2D);glBegin(GL_QUADS);
       TEX_BLOCK(ii,jj,kk,del,siz);
       glEnd();glDisable(GL_TEXTURE_2D);}
   }
 }
 else if(dir4[1]!=0){
   for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)for(kk=0;kk<ROWS;kk++){
     if(xzwfaces[ii][LL][jj][kk]==1){
       glBegin(GL_TRIANGLE_STRIP);glColor3f(0.,1.0,0.);
       COLOR_BLOCK(ii,jj,kk,del,siz);
       glEnd();}
     else if(xzwfaces[ii][LL][jj][kk]>1){
       ctex=xzwfaces[ii][LL][jj][kk];
       texenable(&tex_ptr->texture[ctex-2]);
       glEnable(GL_TEXTURE_2D);glBegin(GL_QUADS);
       TEX_BLOCK(ii,jj,kk,del,siz);
       glEnd();glDisable(GL_TEXTURE_2D);}
   }
 }
 else /*dir4[2]!=0*/{
   for(ii=0;ii<ROWS;ii++)for(jj=0;jj<ROWS;jj++)for(kk=0;kk<ROWS;kk++){
     if(yzwfaces[LL][ii][jj][kk]==1){
       glBegin(GL_TRIANGLE_STRIP);glColor3f(1.0,0.,0.);
       COLOR_BLOCK(ii,jj,kk,del,siz);
       glEnd();}
     else if(yzwfaces[LL][ii][jj][kk]>1){
       ctex=yzwfaces[LL][ii][jj][kk];
       texenable(&tex_ptr->texture[ctex-2]);
       glEnable(GL_TEXTURE_2D);glBegin(GL_QUADS);
       TEX_BLOCK(ii,jj,kk,del,siz);
       glEnd();glDisable(GL_TEXTURE_2D);}
   }
 }
}
#endif

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();} 
*/ /* Drew grid frame around maze space, inferior to buildmaze version */
 if(LL<0 || LL>=ROWS)return;
 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);glColor3f(0.,0.,1.0);
       glVertex3f(ADDXY(vv[ii][jj][kk],del,del,siz));
       glVertex3f(ADDXY(vv[ii+1][jj][kk],-1*del,del,siz));
       glVertex3f(ADDXY(vv[ii][jj+1][kk],del,-1*del,siz));
       glVertex3f(ADDXY(vv[ii+1][jj+1][kk],-1*del,-1*del,siz));
       glEnd();}}
 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);glColor3f(1.0,1.0,0.);
       glVertex3f(ADDXY(vv[ii][jj][kk],del,del,siz));
       glVertex3f(ADDXY(vv[ii+1][jj][kk],-1*del,del,siz));
       glVertex3f(ADDXY(vv[ii][jj+1][kk],del,-1*del,siz));
       glVertex3f(ADDXY(vv[ii+1][jj+1][kk],-1*del,-1*del,siz));
       glEnd();}}
 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));
       glVertex3f(ADDXZ(vv[ii+1][jj][kk],-1*del,del,siz));
       glVertex3f(ADDXZ(vv[ii][jj][kk+1],del,-1*del,siz));
       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));
       glVertex3f(ADDXZ(vv[ii+1][jj][kk],-1*del,del,siz));
       glVertex3f(ADDXZ(vv[ii][jj][kk+1],del,-1*del,siz));
       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));
       glVertex3f(ADDYZ(vv[ii][jj+1][kk],-1*del,del,siz));
       glVertex3f(ADDYZ(vv[ii][jj][kk+1],del,-1*del,siz));
       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));
       glVertex3f(ADDYZ(vv[ii][jj+1][kk],-1*del,del,siz));
       glVertex3f(ADDYZ(vv[ii][jj][kk+1],del,-1*del,siz));
       glVertex3f(ADDYZ(vv[ii][jj+1][kk+1],-1*del,-1*del,siz));
       glEnd();}}
}
#ifdef TEXTURES
drawmaze_tex(int LL, float del, int dir4[4],float siz){int ii,jj,kk;
int ctex;/* current texture */
 if(LL<0 || LL>=ROWS)return; 
  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]==1){
     glBegin(GL_TRIANGLE_STRIP);
     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();}
    else if(xywfaces[ii][jj][kk][LL]>1){
     ctex=xywfaces[ii][jj][kk][LL];
     texenable(&tex_ptr->texture[ctex-2]);
     glEnable(GL_TEXTURE_2D);glBegin(GL_QUADS);
     glTexCoord2f(0.0, 0.0);glVertex3f(ADDXY(vv[ii][jj][kk],del,del,siz));
     glTexCoord2f(1.0, 0.0);glVertex3f(ADDXY(vv[ii+1][jj][kk],-1*del,del,siz));
     glTexCoord2f(1.0, 1.0);glVertex3f(ADDXY(vv[ii+1][jj+1][kk],-1*del,-1*del,siz));
     glTexCoord2f(0.0, 1.0);glVertex3f(ADDXY(vv[ii][jj+1][kk],del,-1*del,siz));
     glEnd();glDisable(GL_TEXTURE_2D);}
   }}
  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]==1)&&(dir4[2]!=0))||
       ((xyzfaces[ii][LL][jj][kk]==1)&&(dir4[1]!=0))||
       ((xyzfaces[LL][ii][jj][kk]==1)&&(dir4[0]!=0))){
     glBegin(GL_TRIANGLE_STRIP);
     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();ctex=0;}
    else if((xyzfaces[ii][jj][LL][kk]>1)&&(dir4[2]!=0))ctex=xyzfaces[ii][jj][LL][kk];
    else if((xyzfaces[ii][LL][jj][kk]>1)&&(dir4[1]!=0))ctex=xyzfaces[ii][LL][jj][kk];
    else if((xyzfaces[LL][ii][jj][kk]>1)&&(dir4[0]!=0))ctex=xyzfaces[LL][ii][jj][kk];
    else ctex=0;
    if(ctex){
     texenable(&tex_ptr->texture[ctex-2]);
     glEnable(GL_TEXTURE_2D);glBegin(GL_QUADS);
     glTexCoord2f(0.0, 0.0);glVertex3f(ADDXY(vv[ii][jj][kk],del,del,siz));
     glTexCoord2f(1.0, 0.0);glVertex3f(ADDXY(vv[ii+1][jj][kk],-1*del,del,siz));
     glTexCoord2f(1.0, 1.0);glVertex3f(ADDXY(vv[ii+1][jj+1][kk],-1*del,-1*del,siz));
     glTexCoord2f(0.0, 1.0);glVertex3f(ADDXY(vv[ii][jj+1][kk],del,-1*del,siz));
     glEnd();glDisable(GL_TEXTURE_2D);}
   }}

  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]==1)&&(dir4[3]!=0))||
	((xzwfaces[ii][jj][LL][kk]==1)&&(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();ctex=0;}
    else if((xzwfaces[ii][jj][kk][LL]>1)&&(dir4[3]!=0))ctex=xzwfaces[ii][jj][kk][LL];
    else if((xzwfaces[ii][jj][LL][kk]>1)&&(dir4[2]!=0))ctex=xzwfaces[ii][jj][LL][kk];
    else ctex=0;
   if(ctex){
    texenable(&tex_ptr->texture[ctex-2]);
    glEnable(GL_TEXTURE_2D);glBegin(GL_QUADS);
    glTexCoord2f(1.0, 1.0); glVertex3f(ADDXZ(vv[ii][jj][kk],del,del,siz));
    glTexCoord2f(0.0, 1.0);glVertex3f(ADDXZ(vv[ii+1][jj][kk],-1*del,del,siz));
    glTexCoord2f(0.0, 0.0);glVertex3f(ADDXZ(vv[ii+1][jj][kk+1],-1*del,-1*del,siz));
    glTexCoord2f(1.0, 0.0);glVertex3f(ADDXZ(vv[ii][jj][kk+1],del,-1*del,siz));
    glEnd();glDisable(GL_TEXTURE_2D);}
   }}  
  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]==1)&&(dir4[1]!=0))||
	((xywfaces[LL][ii][jj][kk]==1)&&(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();ctex=0;}
    else if((xywfaces[ii][LL][jj][kk]>1)&&(dir4[1]!=0))ctex=xywfaces[ii][LL][jj][kk];
    else if((xywfaces[LL][ii][jj][kk]>1)&&(dir4[0]!=0))ctex=xywfaces[LL][ii][jj][kk];
    else ctex=0;
    if(ctex){
     texenable(&tex_ptr->texture[ctex-2]);
     glEnable(GL_TEXTURE_2D);glBegin(GL_QUADS);
     glTexCoord2f(1.0, 1.0); glVertex3f(ADDXZ(vv[ii][jj][kk],del,del,siz));
     glTexCoord2f(0.0, 1.0);glVertex3f(ADDXZ(vv[ii+1][jj][kk],-1*del,del,siz));
     glTexCoord2f(0.0, 0.0);glVertex3f(ADDXZ(vv[ii+1][jj][kk+1],-1*del,-1*del,siz));
     glTexCoord2f(1.0, 0.0);glVertex3f(ADDXZ(vv[ii][jj][kk+1],del,-1*del,siz));
     glEnd();glDisable(GL_TEXTURE_2D);}
   }}

  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]==1)&&(dir4[3]!=0))||
	((yzwfaces[ii][jj][LL][kk]==1)&&(dir4[2]!=0))||
	((yzwfaces[ii][LL][jj][kk]==1)&&(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();ctex=0;}
    else if((yzwfaces[ii][jj][kk][LL]>1)&&(dir4[3]!=0))ctex=yzwfaces[ii][jj][kk][LL];
    else if((yzwfaces[ii][jj][LL][kk]>1)&&(dir4[2]!=0))ctex=yzwfaces[ii][jj][LL][kk];
    else if((yzwfaces[ii][LL][jj][kk]>1)&&(dir4[1]!=0))ctex=yzwfaces[ii][LL][jj][kk];
    else ctex=0;
    if(ctex){
     texenable(&tex_ptr->texture[ctex-2]);
     glEnable(GL_TEXTURE_2D);glBegin(GL_QUADS);
     glTexCoord2f(1.0, 0.0); glVertex3f(ADDYZ(vv[ii][jj][kk],del,del,siz));
     glTexCoord2f(1.0, 1.0);glVertex3f(ADDYZ(vv[ii][jj+1][kk],-1*del,del,siz));
     glTexCoord2f(0.0, 1.0);glVertex3f(ADDYZ(vv[ii][jj+1][kk+1],-1*del,-1*del,siz));
     glTexCoord2f(0.0, 0.0);glVertex3f(ADDYZ(vv[ii][jj][kk+1],del,-1*del,siz));
     glEnd();glDisable(GL_TEXTURE_2D);}
   }}
  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]==1){
     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();ctex=0;}
    else if(xzwfaces[LL][ii][jj][kk]>1){
     ctex=xzwfaces[LL][ii][jj][kk];
     texenable(&tex_ptr->texture[ctex-2]);
     glEnable(GL_TEXTURE_2D);glBegin(GL_QUADS);
     glTexCoord2f(1.0, 0.0); glVertex3f(ADDYZ(vv[ii][jj][kk],del,del,siz));
     glTexCoord2f(1.0, 1.0);glVertex3f(ADDYZ(vv[ii][jj+1][kk],-1*del,del,siz));
     glTexCoord2f(0.0, 1.0);glVertex3f(ADDYZ(vv[ii][jj+1][kk+1],-1*del,-1*del,siz));
     glTexCoord2f(0.0, 0.0);glVertex3f(ADDYZ(vv[ii][jj][kk+1],del,-1*del,siz));
     glEnd();glDisable(GL_TEXTURE_2D);}
   }}
}
#endif

#define SET_STAIR_COLOR(clr) \
  if(clr==0)glColor4f(.7*bright,0.,0.,bright); \
  else if(clr==1)glColor4f(0.,.63*bright,0.,bright); \
  else if(clr==2)glColor4f(0.,0.,.7*bright,bright); \
  else /*clr==3*/glColor4f(.68*bright,.66*bright,0.,bright);

drawHmark(int II,int JJ,int KK,int thick,float siz,float bright,
	  int colorkey){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 }, /* an 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);
  /*  glColor3f(bright*.3,0.,bright*.3); */
  SET_STAIR_COLOR(colorkey);
  for(ii=0;ii<13;ii++)glVertex3fv(vH[fr[ii]]); 
  glEnd();
}
drawhmark(int II,int JJ,int KK,int thick,float siz,float bright,
	  int colorkey){ /* a 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(bright*.2,bright*.2,bright*.2); */
    SET_STAIR_COLOR(colorkey);
    for(ii=0;ii<16;ii++)glVertex3fv(vh[fr[ii]]); 
    glEnd();
}

drawportal(int II,int JJ,int KK,int thick, int whichway, float size);

void audStairs(int depth, int whichone);

/* 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 tex_otheredges(dd,old_dd,ss,II,JJ,moving_axes,del,siz) { \
  int kk,ll,ctex; \
  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]==1){ \
	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(); \
      } \
      else if(oe1[II][JJ][kk][ll]){ \
        ctex=oe1[II][JJ][kk][ll]; \
	texenable(&tex_ptr->texture[ctex-2]); \
        glEnable(GL_TEXTURE_2D);glBegin(GL_QUADS); \
        glTexCoord2f(1.0, 1.0); \
	setDD(dd+del_div8,kk,ll+del_div8,ss,moving_axes);glVertex3f(DDList); \
        glTexCoord2f(0.0, 1.0); \
	setDD(old_dd-del_div8,kk,ll+del_div8,ss,moving_axes); \
        glVertex3f(DDList); \
        glTexCoord2f(0.0, 0.0); \
	setDD(old_dd-del_div8,kk,ll+1-del_div8,ss,moving_axes); \
        glVertex3f(DDList); \
        glTexCoord2f(1.0, 0.0); \
	setDD(dd+del_div8,kk,ll+1-del_div8,ss,moving_axes); \
        glVertex3f(DDList); \
        glEnd();glDisable(GL_TEXTURE_2D); \
      } \
    } \
    for(kk=0;kk<ROWS;kk++)for(ll=0;ll<ROWS1;ll++){ \
      if(oe2[II][JJ][kk][ll]==1){ \
	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(oe2[II][JJ][kk][ll]){ \
	ctex=oe2[II][JJ][kk][ll]; \
	texenable(&tex_ptr->texture[ctex-2]); \
        glEnable(GL_TEXTURE_2D);glBegin(GL_QUADS); \
        glTexCoord2f(1.0, 1.0); \
	setDD(dd+del_div8,kk+del_div8,ll,ss,moving_axes);glVertex3f(DDList); \
        glTexCoord2f(0.0, 1.0); \
	setDD(old_dd-del_div8,kk+del_div8,ll,ss,moving_axes); \
        glVertex3f(DDList); \
        glTexCoord2f(0.0, 0.0); \
	setDD(old_dd-del_div8,kk+1-del_div8,ll,ss,moving_axes); \
        glVertex3f(DDList); \
        glTexCoord2f(1.0, 0.0); \
	setDD(dd+del_div8,kk+1-del_div8,ll,ss,moving_axes); \
        glVertex3f(DDList); \
        glEnd();glDisable(GL_TEXTURE_2D); \
      } \
    } \
  } \
  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]==1){ \
	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(oe2[II][JJ][kk][ll]){ \
	ctex=oe2[II][JJ][kk][ll]; \
	texenable(&tex_ptr->texture[ctex-2]); \
        glEnable(GL_TEXTURE_2D);glBegin(GL_QUADS); \
        glTexCoord2f(1.0, 1.0); \
	setDD(dd-del_div8,kk+del_div8,ll,ss,moving_axes);glVertex3f(DDList); \
        glTexCoord2f(0.0, 1.0); \
	setDD(old_dd+del_div8,kk+del_div8,ll,ss,moving_axes); \
        glVertex3f(DDList); \
        glTexCoord2f(0.0, 0.0); \
	setDD(old_dd+del_div8,kk+1-del_div8,ll,ss,moving_axes); \
        glVertex3f(DDList); \
        glTexCoord2f(1.0, 0.0); \
	setDD(dd-del_div8,kk+1-del_div8,ll,ss,moving_axes); \
        glVertex3f(DDList); \
        glEnd();glDisable(GL_TEXTURE_2D); \
      } \
    } \
    for(kk=0;kk<ROWS1;kk++)for(ll=0;ll<ROWS;ll++){ \
      if(oe1[II][JJ][kk][ll]==1){ \
	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(); \
      } \
      else if(oe1[II][JJ][kk][ll]){ \
	ctex=oe1[II][JJ][kk][ll]; \
	texenable(&tex_ptr->texture[ctex-2]); \
        glEnable(GL_TEXTURE_2D);glBegin(GL_QUADS); \
        glTexCoord2f(1.0, 1.0); \
	setDD(dd-del_div8,kk,ll+del_div8,ss,moving_axes);glVertex3f(DDList); \
        glTexCoord2f(0.0, 1.0); \
	setDD(old_dd+del_div8,kk,ll+del_div8,ss,moving_axes); \
        glVertex3f(DDList); \
        glTexCoord2f(0.0, 0.0); \
	setDD(old_dd+del_div8,kk,ll+1-del_div8,ss,moving_axes); \
	glVertex3f(DDList); \
        glTexCoord2f(1.0, 0.0); \
	setDD(dd-del_div8,kk,ll+1-del_div8,ss,moving_axes); \
	glVertex3f(DDList); \
        glEnd();glDisable(GL_TEXTURE_2D); \
      } \
    } \
  } \
}

#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(); \
}

#define tex_hedge(dd,kk,ll,ss,moving_axes,del,siz,ctex) { \
  float del_div8 = del/8.0; \
  texenable(&tex_ptr->texture[ctex-2]); \
  glEnable(GL_TEXTURE_2D);glBegin(GL_QUADS); \
  glTexCoord2f(1.0, 1.0); \
  setDD(dd,kk+del_div8,ll+del_div8,ss,moving_axes);glVertex3f(DDList); \
  glTexCoord2f(0.0, 1.0); \
  setDD(dd,kk+1-del_div8,ll+del_div8,ss,moving_axes);glVertex3f(DDList); \
  glTexCoord2f(0.0, 0.0); \
  setDD(dd,kk+1-del_div8,ll+1-del_div8,ss,moving_axes);glVertex3f(DDList); \
  glTexCoord2f(1.0, 0.0); \
  setDD(dd,kk+del_div8,ll+1-del_div8,ss,moving_axes);glVertex3f(DDList); \
  glEnd();glDisable(GL_TEXTURE_2D); \
}

#define tex_vedge(dd,kk,ll,ss,moving_axes,del,siz,ctex) { \
  float del_div8 = del/8.0; \
  texenable(&tex_ptr->texture[ctex-2]); \
  glEnable(GL_TEXTURE_2D);glBegin(GL_QUADS); \
  glTexCoord2f(1.0, 1.0); \
  setDD(dd,kk+del_div8,ll+del_div8,ss,moving_axes);glVertex3f(DDList); \
  glTexCoord2f(0.0, 1.0); \
  setDD(dd,kk+1-del_div8,ll+del_div8,ss,moving_axes);glVertex3f(DDList); \
  glTexCoord2f(0.0, 0.0); \
  setDD(dd,kk+1-del_div8,ll+1-del_div8,ss,moving_axes);glVertex3f(DDList); \
  glTexCoord2f(1.0, 0.0); \
  setDD(dd,kk+del_div8,ll+1-del_div8,ss,moving_axes);glVertex3f(DDList); \
  glEnd();glDisable(GL_TEXTURE_2D); \
} 

/* 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)           fsqrt(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,torq, focal, far; /*console navigation variables         */
int speed;
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 soundyes=1;
int textureyes=1;
int mode,morph,msg;            /* 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,could_move=0; 
int arg_wnd; float arg_gap; /* arguments() precedes getmem() */
int AUDflag; /* sonar at next opportunity */
#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 */
/* Perhaps it shouldn't be quite like that, but it is */
/* fore_jump MUST divide 4 (perfectly binary, as above) */
/* Good choices of {theta_jump,rot_jump,fore_jump} are
   (from slow & smooth to fast & jumpy):*/
static float theta_jump[8]={0.5,0.75,1,1.5,2.5,3.75,5,7.5};
static float rot_jump[8]={1,1.5,2,3,5,7.5,10,15};
static float fore_jump[8]={.125,.125,.25,.25,.5,1.,2.,4.};
/*   (In the CAVE, only the first three are okay.) */
/* 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 */  
struct share_var{ 
   float s_stairlight;
   int s_inter_ss, s_inter_tt;
   int s_ActualAxes;
   float s_Theta;
   float s_sign_theta;
   int s_tween_levels;
   int s_rotating_in_4D;
   int s_dir4[4];           /* taff[3,8,13,18], the hidden direction */
   int s_location[4];
   float s_gap;          /* gap/2 between walls */
   float  s_siz;         /*final scaling factor before projection      */  
   int s_wnd;            /* 0=maus 1=FLYwand 2=TRACTORwand             */
   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 ;           /*                                             */
   GLfloat s_aff[16],s_starmat[16]; /*affine matrices for object, stars*/
   GLfloat s_taff[25],s_id[16]; /* 5D affine matrix */
   int s_he[ROWS][ROWS1][ROWS][ROWS];
   int s_ve[ROWS1][ROWS][ROWS][ROWS];
   int s_oe1[ROWS][ROWS][ROWS1][ROWS];
   int s_oe2[ROWS][ROWS][ROWS][ROWS1];
   GLfloat s_he_color[3];
   GLfloat s_ve_color[3];
   GLfloat s_oe1_color[3];
   GLfloat s_oe2_color[3];
} *s_var; 

#define stairlight (s_var->s_stairlight)
#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)
#define location (s_var->s_location)
#define dir4    (s_var->s_dir4)
#define Theta (s_var->s_Theta)
#define sign_theta (s_var->s_sign_theta)
#define ActualAxes (s_var->s_ActualAxes)
#define rotating_in_4D (s_var->s_rotating_in_4D)
#define tween_levels (s_var->s_tween_levels)
#define inter_ss (s_var->s_inter_ss)
#define inter_tt (s_var->s_inter_tt)
#define he      (s_var->s_he)
#define ve      (s_var->s_ve)
#define oe1     (s_var->s_oe1)
#define oe2     (s_var->s_oe2)
#define he_color (s_var->s_he_color)
#define ve_color (s_var->s_ve_color)
#define oe1_color (s_var->s_oe1_color)
#define oe2_color (s_var->s_oe2_color)

/* these are used only in console mode      */
int binoc;    /* flag                       */
float nose;   /* to eye distance in console */

drawportal(int II,int JJ,int KK,int thick, int whichway, float size,
	   int colorkey){
  if(whichway==1)drawHmark(II,JJ,KK,thick,size,stairlight,colorkey);
  else if(whichway==-1)drawhmark(II,JJ,KK,thick,size,stairlight,colorkey);
}

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;
      }
    }
  }
}

#ifdef TEXTURES
/* Never use a texture when slice intersects corner of two panels */
void draw_intersections_tex(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;
	tex_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;
	tex_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]?1:0;
	  else if(ii==0) h_or_v = he[II][JJ][kk][ll]?1:0;
	  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]?1:0);
	  else if(jj==0) h_or_v += 2*(ve[II][JJ][kk][ll]?1:0);
	  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]==1){
	    draw_hedge(dd,kk,ll,ss,moving_axes,del,siz);}
	  else if(he[II][JJ][kk][ll])
	    tex_hedge(dd,kk,ll,ss,moving_axes,del,siz,he[II][JJ][kk][ll]);
	} 
      }
      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]==1){
	    draw_vedge(dd,kk,ll,ss,moving_axes,del,siz);}
	  else if(ve[II][JJ][kk][ll])
	    tex_vedge(dd,kk,ll,ss,moving_axes,del,siz,ve[II][JJ][kk][ll]);
	}
      }
      /* 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]?1:0;
	    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]?1:0);
	    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]==1){
	      draw_hedge(dd,kk,ll,ss,moving_axes,del,siz);}
	    else if(he[II][JJ][kk][ll])
	      tex_hedge(dd,kk,ll,ss,moving_axes,del,siz,he[II][JJ][kk][ll]);
	  }
	}
	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]==1){
	      draw_vedge(dd,kk,ll,ss,moving_axes,del,siz);}
	    else if(ve[II][JJ][kk][ll])
	      tex_vedge(dd,kk,ll,ss,moving_axes,del,siz,ve[II][JJ][kk][ll]);
	  }
	}
	/* DrawOtherColoredLineSomewhere */
	II=floor((ii+old_ii)/2.0);
	JJ=floor((jj+old_jj)/2.0);
	tex_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]?1:0;
	  else if(ii==0) h_or_v = he[II][JJ][kk][ll]?1:0;
	  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]?1:0);
	  else if(jj==0) h_or_v += 2*(ve[II][JJ][kk][ll]?1:0);
	  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]==1){
	    draw_hedge(dd,kk,ll,ss,moving_axes,del,siz);}
	  else if(he[II][JJ][kk][ll])
	    tex_hedge(dd,kk,ll,ss,moving_axes,del,siz,he[II][JJ][kk][ll]);
	}
      }
      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]==1){
	    draw_vedge(dd,kk,ll,ss,moving_axes,del,siz);}
	  else if(ve[II][JJ][kk][ll])
	    tex_vedge(dd,kk,ll,ss,moving_axes,del,siz,ve[II][JJ][kk][ll]);
	}
      }
      /* 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]?1:0;
	    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]?1:0);
	    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]==1){
	      draw_hedge(dd,kk,ll,ss,moving_axes,del,siz);}
	    else if(he[II][JJ][kk][ll])
	      tex_hedge(dd,kk,ll,ss,moving_axes,del,siz,he[II][JJ][kk][ll]);
	  }
	}
	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]==1){
	      draw_vedge(dd,kk,ll,ss,moving_axes,del,siz);}
	    else if(ve[II][JJ][kk][ll])
	      tex_vedge(dd,kk,ll,ss,moving_axes,del,siz,ve[II][JJ][kk][ll]);
	  }
	}
	/* DrawOtherColoredLineSomewhere */
	II=floor((ii+old_ii)/2.0);
	JJ=floor((jj+old_jj)/2.0);
	tex_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;
	tex_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;
	tex_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]?1:0;
	  else if(ii==0) h_or_v = he[II][JJ][kk][ll]?1:0;
	  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]?1:0);
	  else if(jj==0) h_or_v += 2*(ve[II][JJ][kk][ll]?1:0);
	  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]==1){
	    draw_hedge(dd,kk,ll,ss,moving_axes,del,siz);}
	  else if(he[II][JJ][kk][ll])
	    tex_hedge(dd,kk,ll,ss,moving_axes,del,siz,he[II][JJ][kk][ll]);
	}
      }
      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]==1){
	    draw_vedge(dd,kk,ll,ss,moving_axes,del,siz);}
	  else if(ve[II][JJ][kk][ll])
	    tex_vedge(dd,kk,ll,ss,moving_axes,del,siz,ve[II][JJ][kk][ll]);
	}
      }
      /* 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]?1:0;
	    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]?1:0);
	    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]==1){
	      draw_hedge(dd,kk,ll,ss,moving_axes,del,siz);}
	    else if(he[II][JJ][kk][ll])
	      tex_hedge(dd,kk,ll,ss,moving_axes,del,siz,he[II][JJ][kk][ll]);
	  }
	}
	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]==1){
	      draw_vedge(dd,kk,ll,ss,moving_axes,del,siz);}
	    else if(ve[II][JJ][kk][ll])
	      tex_vedge(dd,kk,ll,ss,moving_axes,del,siz,ve[II][JJ][kk][ll]);
	  }
	}
	/* DrawOtherColoredLineSomewhere */
	II=floor((ii+old_ii)/2.0);
	JJ=floor((jj+old_jj)/2.0);
	tex_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]?1:0;
	  else if(ii==0) h_or_v = he[II][JJ][kk][ll]?1:0;
	  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]?1:0);
	  else if(jj==0) h_or_v += 2*(ve[II][JJ][kk][ll]?1:0);
	  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]==1){
	    draw_hedge(dd,kk,ll,ss,moving_axes,del,siz);}
	  else if(he[II][JJ][kk][ll])
	    tex_hedge(dd,kk,ll,ss,moving_axes,del,siz,he[II][JJ][kk][ll]);
	}
      }
      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]==1){
	    draw_vedge(dd,kk,ll,ss,moving_axes,del,siz);}
	  else if(ve[II][JJ][kk][ll])
	    tex_vedge(dd,kk,ll,ss,moving_axes,del,siz,ve[II][JJ][kk][ll]);
	}
      }
      /* 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]?1:0;
	    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]?1:0);
	    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]==1){
	      draw_hedge(dd,kk,ll,ss,moving_axes,del,siz);}
	    else if(he[II][JJ][kk][ll])
	      tex_hedge(dd,kk,ll,ss,moving_axes,del,siz,he[II][JJ][kk][ll]);
	  }
	}
	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]==1){
	      draw_vedge(dd,kk,ll,ss,moving_axes,del,siz);}
	    else if(ve[II][JJ][kk][ll])
	      tex_vedge(dd,kk,ll,ss,moving_axes,del,siz,ve[II][JJ][kk][ll]);
	  }
	}
	/* DrawOtherColoredLineSomewhere */
	II=floor((ii+old_ii)/2.0);
	JJ=floor((jj+old_jj)/2.0);
	tex_otheredges(dd,old_dd,ss,II,JJ,moving_axes,del,siz);
	old_ii=ii;old_jj=jj;old_dd=dd;
      }
    }
  }
}
#endif

/* 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 */

void getmem(void){
  if(caveyes){
#ifdef CAVE
     tex_ptr=(tex_ptrs*)CAVEMalloc(sizeof(struct tex_ptrs));     
     s_var =(share_var*)CAVEMalloc(sizeof(struct share_var)); /* no ; here ! */
#endif /* CAVE */
     }  /* but ;  here */
  else{ /* CONSOLE */
     tex_ptr=(tex_ptrs*)malloc(sizeof(struct tex_ptrs));
     s_var =(share_var*)malloc(sizeof(struct share_var));}
  if(s_var==NULL || tex_ptr==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;
  msg=1; binoc=0; nose=.06; mauspaw = 0; siz = 1.5; 
  mode=FLYMODE;
  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();
  tween_levels=rotating_in_4D=cube=0;Theta=moving=0;stairlight=1.;
}

/* 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,3);
    if(xyzfaces[ii][jj][kk][location[3]+1]==0 &&
       location[3]>=-1 && location[3]<ROWS)
      drawportal(ii,jj,kk,thick,(-1)*dir4[3],siz,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],siz,2);
    if(xywfaces[ii][jj][location[2]+1][kk]==0 &&
       location[2]>=-1 && location[2]<ROWS)
      drawportal(ii,jj,kk,thick,(-1)*dir4[2],siz,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],siz,1);
    if(xzwfaces[ii][location[1]+1][jj][kk]==0 &&
       location[1]>=-1 && location[1]<ROWS)
      drawportal(ii,jj,kk,thick,(-1)*dir4[1],siz,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],siz,0);
    if(yzwfaces[location[0]+1][ii][jj][kk]==0 &&
       location[0]>=-1 && location[0]<ROWS)
      drawportal(ii,jj,kk,thick,(-1)*dir4[0],siz,0);
  }
}

hearstairs(void){
  static int depth=0;
  static int whichone=1; /* +-1 :check currently +-hidden direction */
  /* If whichone==dir[4]!=0, then we want to check in canonical positive 
     direction */
  if(dir4[3]!=0){ 
    if(whichone!=dir4[3]){
      while((location[3]-depth)>=0 && 
	    xyzfaces[location[0]][location[1]][location[2]][location[3]-depth]==0)
	depth+=1;
      if(location[3]<depth)depth=-1;
      audStairs(depth,(-1)*dir4[3]);/* (-1)*dir4[3]==whichone */ depth=0;}
    else{
      while((location[3]+depth)<ROWS && 
	    xyzfaces[location[0]][location[1]][location[2]][location[3]+1+depth]==0)
	depth+=1;
      if((location[3]+depth)>=ROWS)depth=-1;
      audStairs(depth,dir4[3]);/* dir4[3]==whichone */ depth=0;}
  }
  else if(dir4[2]!=0){
    if(whichone!=dir4[2]){
      while((location[2]-depth)>=0 && 
	    xywfaces[location[0]][location[1]][location[2]-depth][location[3]]==0)
	depth+=1;
      if(location[2]<depth)depth=-1;
      audStairs(depth,(-1)*dir4[2]);depth=0;}
    else{
      while((location[2]+depth)<ROWS && 
	    xywfaces[location[0]][location[1]][location[2]+1+depth][location[3]]==0)
	depth+=1;
      if((location[2]+depth)>=ROWS)depth=-1;
      audStairs(depth,dir4[2]);depth=0;}
  }
  else if(dir4[1]!=0){
    if(whichone!=dir4[1]){
      while((location[1]-depth)>=0 && 
	    xzwfaces[location[0]][location[1]-depth][location[2]][location[3]]==0)
	depth+=1;
      if(location[1]<depth)depth=-1;
      audStairs(depth,(-1)*dir4[1]);depth=0;}
    else{
      while((location[1]+depth)<ROWS && 
	    xzwfaces[location[0]][location[1]+1+depth][location[2]][location[3]]==0)
	depth+=1;
      if((location[1]+depth)>=ROWS)depth=-1;
      audStairs(depth,dir4[1]);depth=0;}
  }    
  else /* dir4[0]!=0 */ {
    if(whichone!=dir4[0]){
      while((location[0]-depth)>=0 && 
	    yzwfaces[location[0]-depth][location[1]][location[2]][location[3]]==0)
	depth+=1;
      if(location[0]<depth)depth=-1;
      audStairs(depth,(-1)*dir4[0]);depth=0;}
    else{
      while((location[0]+depth)<ROWS && 
	    yzwfaces[location[0]+1+depth][location[1]][location[2]][location[3]]==0)
	depth+=1;
      if((location[0]+depth)>=ROWS)depth=-1;
      audStairs(depth,dir4[0]);depth=0;}
  }    
  whichone*=-1;
}

void drawall(void){
 if(rotating_in_4D==0){
#ifdef TEXTURES
   if(textureyes){
     if(tween_levels){
       if((DSUM/2.0 + moving)==9.5)
	 draw_tex_fourthD(((ROWS-ROUND(DSUM*taff[23]/(4.0*siz))-1)/2)+1,gap,dir4,siz);
       else
	 draw_tex_fourthD(((ROWS-ROUND(DSUM*taff[23]/(4.0*siz))-1)/2),gap,dir4,siz);
     }
     else
       drawmaze_tex(((ROWS-ROUND(DSUM*taff[23]/(4.0*siz))-1)/2),gap,dir4,siz);
   }
   else 
#endif
     {
       if(tween_levels){
	 if((DSUM/2.0 + moving)==9.5)
	   draw_fourthD(((ROWS-ROUND(DSUM*taff[23]/(4.0*siz))-1)/2)+1,gap,dir4,siz);
	 else
	   draw_fourthD(((ROWS-ROUND(DSUM*taff[23]/(4.0*siz))-1)/2),gap,dir4,siz);
       }
       else
	 drawmaze(((ROWS-ROUND(DSUM*taff[23]/(4.0*siz))-1)/2),gap,dir4,siz); 
     }
   glEnable(GL_BLEND);
   glDepthMask(GL_FALSE);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   drawstairs();
   glDepthMask(GL_TRUE);   
   glDisable(GL_BLEND);
   
   if(AUDflag){hearstairs();AUDflag=0;}
 }
 else /* rotating_in_4D==1 */ {

   /* FEB2000
   printf("Theta=%f\t",Theta);
   */
#ifdef TEXTURES
   if(textureyes){
   if(ActualAxes>0)
     draw_intersections_tex(inter_ss,inter_tt,sign_theta*(Theta)*DG,abs(ActualAxes),gap);
   else /* ActualAxes<0 */
     draw_intersections_tex(inter_ss,inter_tt,sign_theta*(90-Theta)*DG,abs(ActualAxes),gap);
   }
   else{
#endif
   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);
#ifdef TEXTURES
   }
#endif
 }
}

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);
}

float odometer(void);

void audioprep(void){
  if(soundyes){
#ifdef SOUND
   if (!BeginSoundServer())
      fprintf(stderr,"UDP connection to sound server failed\n");
   else if ((audiohandle = AUDinit("maze.aud")) == -1)
      fprintf(stderr,"Couldn't open maze.aud\n");
#endif
   ;}
  AUDflag=0;
}

void audiofunc(void){
#ifdef 0
#define LIRP(X,A,B,x,a,b)  (X = A + (x-a)*(B-A)/(b-a))

void dohoot(void) {strcpy(which,"hoot");}
void dotrump(void){strcpy(which,"trump");}
void doflute(void){strcpy(which,"flute");}
void dodrum(void) {strcpy(which,"drum");}

  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
}
/* if(override) shut 'em up */
audWandOrientation(int override){
#ifdef SOUND 
  static int now_playing=0;
  static float data[1];
  if(override || (could_move==0 && now_playing!=0)){
    AUDupdate(audiohandle,"WandKill",0,data);/* Kill now_playing sound */
    now_playing=0;}
  else if(could_move>0 && could_move<9 && now_playing!=1){
    AUDupdate(audiohandle,"Wand3D",0,data); /* Change to sound 1 */ 
    now_playing=1;}
  else if((could_move>8) && now_playing!=2){
    AUDupdate(audiohandle,"Wand4D",0,data); /* Change to sound 2*/
    now_playing=2;}
#endif
}

/* Depends on moving too! */
audCollision(int now_colliding, int override){ /* both are flags */
  if(soundyes){
#ifdef SOUND 
  static float data[1];/* pause=1.0 play=0.0 */
  static int was_colliding=0;
  if(override){
    was_colliding=0; data[0]=1.;
    AUDupdate(audiohandle,"Collision",1,data); /* send message to vss */
  }
  else if(was_colliding!=now_colliding){
    data[0]=(float)was_colliding;
    AUDupdate(audiohandle,"Collision",1,data);
    was_colliding=now_colliding;
  }
#endif
  ;}
}

void audStairs(int depth, int whichone){
  if(soundyes){
#ifdef SOUND 
  /* First cheep plays from 0.0 to .05
     The closest possible echo plays from: 0.1 to .15
     whichone is +1 or -1, the current hidden direction,
       which corresponds to freq's 2640 & 1320 */
  float cheepdata[3] = {0.1,.15,0};
  cheepdata[0]+=(depth*0.2); cheepdata[1]+=(depth*0.2);
  cheepdata[2]=((whichone+1)/2)?2640:1320;
  if(depth!=-1)AUDupdate(audiohandle,"TwoCheep", 3, cheepdata);
  else AUDupdate(audiohandle,"OneCheep", 3, cheepdata);
#endif
  ;}
}

/* Note: time between stages is approximately
   4.0/(fore_jump*speedometer()) seconds */
void audMove(int which_move){
  if(soundyes){
#ifdef SOUND
    static int old_move=0; /* each default: stopped sound number */
    static int old_4D=4;
    static float data[1];
    if(which_move>=3){
      if(which_move!=old_4D){
	if(which_move==3)AUDupdate(audiohandle,"Begin4D",0,data);
	else /*which_move==4*/ AUDupdate(audiohandle,"StopOnly4D",0,data);
	old_4D=which_move;
      }
    }
    else if(old_move!=which_move){
      if(which_move==0){
	AUDupdate(audiohandle,"StopMotion",0,data);
	old_4D=4;}
      else if(which_move==1)
	AUDupdate(audiohandle,"BeginRotate",0,data);
      else /* which_move==2 */
	AUDupdate(audiohandle,"BeginMove",0,data);
      old_move=which_move;
    }
#endif
  }
}

void audioclean(void){
  if(soundyes){
#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=1;arg_gap=.15;rand_maze=0;arg_wnd=1;speed=1;

  /* w: needs ONE number after -w, c<nothing> means NO number follows*/ 
  while ((chi = getopt(argc,argv,"w:cg:r:t:s:STx:y:z:h:W:")) != -1) 
    switch(chi){
    case 'c': caveyes=1; break;  
    case 'w': win=atoi(optarg); break; 
    case 'g': arg_gap=atof(optarg); 
      if(arg_gap<0 || arg_gap>1)arg_gap=.15;
      else arg_gap*=4.0;
      break;
    case 'r': rseed=atoi(optarg); rand_maze=1; break;
    case 't': thick=atoi(optarg); break;
    case 's': speed=atoi(optarg);
      if(speed<1 || speed>8)speed=1;
      else speed-=1;
      break;
    case 'S': soundyes=0; break;
    case 'T': textureyes=0; 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 'W': arg_wnd=atoi(optarg); 
      if(arg_wnd<0 || arg_wnd>2)arg_wnd=1;
      break;
    }
  if (optind!=argc) {
    fprintf(stderr,"%s: Incorrect usage\n",argv[0]);
    fprintf(stderr,"Usage: %s [-w win] [-t line thickness] [-s speed (1-8)] [-S NoSound] [-T NoTextures] [-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] [-W wnd]\n",argv[0]);
    /*    exit(-1);    */ /* Why is this in skel but not noosh? */
  }
}

void dataprep(void){
  int ii;
  gap=arg_gap;wnd=arg_wnd;
   audioprep();
#ifdef TEXTURES
   if(textureyes){
     definetextures();
     for(ii=0;ii<NUM_Textures;ii++) texinit(&tex_ptr->texture[ii]);
   }
#endif
   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[speed])-1))+1;
 static int cnt3Drot= 90.0/rot_jump[speed];
 static int cnt3Dmove= 8.0/fore_jump[speed];
 static int elevDelay= 4.0/fore_jump[speed];

#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;rotating_in_4D=tween_levels=0;/* all are 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)
  TYME( dwell  , 10, moving= 0)
  TYME( hither , cnt3Dmove,  moving=10)
  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  , 20, moving= 0)
  TYME( FourRotate, cnt4D, moving=11)
  TYME( dwell  , 20, 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  , 20, moving= 0)
  TYME( hither , cnt3Dmove,  moving=10)
  TYME( dwell  , 20, moving= 0)
  TYME( thither , cnt3Dmove,  moving=9)
  TYME( dwell  , 20, moving= 0)
  TYME( thither , cnt3Dmove,  moving=9)
  TYME( dwell  , 20, 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( dwell  , 20, moving= 0)
  TYME( FourRotate, cnt4D, moving=16)
  TYME( dwell  , 20, moving= 0)
  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( right  , cnt3Drot, moving= 3)
  TYME( dwell  , 20, moving= 0)
  TYME( thither , cnt3Dmove,  moving=9)
  TYME( dwell  , 20, moving= 0)
  TYME( thither , cnt3Dmove,  moving=9)
  TYME( dwell  , 20, moving= 0)
  TYME( thither , cnt3Dmove,  moving=9)
  TYME( dwell  , 20, moving= 0)
  TYME( thither , cnt3Dmove,  moving=9)
  TYME( dwell  , 20, moving= 0)
  TYME( thither , cnt3Dmove,  moving=9)
  TYME( dwell  , 20, moving= 0)
  TYME( left  , cnt3Drot, moving= 4)
  TYME( forward, cnt3Dmove, moving= 8)
  TYME( forward, cnt3Dmove, moving= 8)
  TYME( right  , cnt3Drot, moving= 3)
  TYME( hither , cnt3Dmove,  moving=10)
  if(caveyes==0){
    TYME( back   , cnt3Dmove, moving= 7)
    TYME( back   , cnt3Dmove, moving= 7)}
  TYME( dwell  , 20, moving= 0)
  TYME( FourRotate, cnt4D, moving=14)
  TYME( dwell  , 50, moving= 0)
  TYME( FourRotate, cnt4D, moving=13)
  TYME( dwell  , 20, moving= 0)
  if(caveyes==0){
    TYME( forward, cnt3Dmove, moving= 8)
    TYME( forward, cnt3Dmove, moving= 8)}
  TYME( thither , cnt3Dmove,  moving=9)
  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 */
}

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;}
	else 
	  if(-1<=relloc[3] && relloc[3]<ROWS)
	    if(yzwfaces[location[0]+1][location[1]][location[2]][location[3]])
	      may_go=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;}
	else 
	  if(-1<=relloc[3] && relloc[3]<ROWS)
	    if(xzwfaces[location[0]][location[1]+1][location[2]][location[3]])
	      may_go=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;}
	else 
	  if(-1<=relloc[3] && relloc[3]<ROWS)
	    if(xywfaces[location[0]][location[1]][location[2]+1][location[3]])
	      may_go=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;}
	else 
	  if(-1<=relloc[3] && relloc[3]<ROWS)
	    if(xyzfaces[location[0]][location[1]][location[2]][location[3]+1])
	      may_go=0;}
    }/* 4D elevator collision checked, i.e., w-shift collision checked */
  return may_go;
}

/*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))
/* Only ASCII characters can be processes by this GLUT callback function */ 
   TOGGLE('v',binoc);                            /* cross-eyed STEREO   */
   TOGGLE('w',msg);                              /* writing on/off      */
   if(!rand_maze){                         /* autotymer controls */
     if(key=='h'){                              
       if(!morph){morph=1;autotymer(1); audCollision(0,1);}
       else frozen=1-frozen;}
     if(key=='H'){morph=0;frozen=0;}
   }
   PRESS('n', nose -= .001 , nose += .001 );     /* for binoculars      */
   /*   if(moving<11){
   CYCLE(' ', mode,TURNMODE+1);                  /* fly/turn modes      */
   /*}*//* No turnmode until can modify to make it more reasonable */
/*   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('q',torq /= 1.02, torq *= 1.02);        /* turning speed       */
   PRESS('z', deFault(), deFault());             /* zap changes         */
   PRESS('m', could_move=9, could_move=10);      /* 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')could_move=11;/* rotate right towards +hidden */
   IF('!')could_move=12;/* rotate left towards +hidden */
   IF('2')could_move=13;/* rotate up towards +hidden */
   IF('@')could_move=14;/* rotate down towards +hidden */
   IF('3')could_move=15;/* rotate forwards towards +hidden */
   IF('#')could_move=16;/* rotate back towards +hidden */
   }
   if((key=='s' || key=='S') && rotating_in_4D==0 && tween_levels==0)AUDflag=1;
   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 wandpaws */
/* 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.);
  LABEL3(-4.8,8.0,-5.0,\
    "illiMaze4D by Michael Pelsmajer, (C) 1998,1999,2000 U.Illinois %s","");
  {sprintf(buf,"location %d %d %d %d",location[0],location[1],
	   location[2],location[3]);char2wall(-3.8,3.,-5.0,buf);}
  /*  if(!wnd)return;*//* CAVE maus should be disabled or fixed! */
  if(wnd==1){
    if(morph==0)
      LABEL3(-3.8,2.,-5.0,
	     "move wand: 3D motion       hold L, move wand: 4D motion%s","");
    if(rotating_in_4D==0 && tween_levels==0)
      LABEL3(-3.8,1.5,-5.0,"click M: Sonar%s","");;
  }
  else if(wnd==2){
    if(morph==0){
      LABEL3(-3.8,2.,-5.0,
        "hold L, move wand = 3D tractor   hold M, move wand = Rotate slice%s","");
      LABEL3(-3.8,1.5,-5.0,\
	     "hold M: click R= +hidden  click L= -hidden %s","");}
    else if(rotating_in_4D==0 && tween_levels==0)
      LABEL3(-3.8,1.5,-5.0,"click M: Sonar%s","");
  }
  if(morph==0){
    LABEL3(-3.8,2.5,-5.0,\
	   "click R: Begin Hypertour               siz=%f",siz);}
  else{
    LABEL3(-3.8,2.5,-5.0,\
	             "click R: Pause/Unpause Hypertour       siz=%f",siz);
    LABEL3(-3.8,2.,-5.0,"click L: Stop Hypertour%s","");
  }

/* If TURNMODE were enabled, then hypertour should be restricted to flymode */
  LABEL3(-5.2,1.0,-5.0,"%5.1f fps",fps);
}

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==4 && wnd>=1){morph=0;frozen=0;}/* autotymer off */
    }
    if(opaw==0 && paw==1 && morph==0  && (!rand_maze)){
      morph=1;autotymer(1);ohx=ohy=ohz=0; owx=owy=owz=0; hdx=hdy=hdz=0;
      audWandOrientation(1);audCollision(0,1); /* Make silent */
    }/* autotymer on */
  }
  if(rotating_in_4D==0 && tween_levels==0 &&
     (wnd>=1 && paw==2 && opaw!=2) )
    AUDflag=1; /* sonar */
  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;
      } 

  /* The '4' is needed if wnd==2, but shouldn't hurt otherwise */
  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)audMove(0);
   else{
     if(morph)autotymer(0); /* advance autotymer */
     if(mode==TURNMODE) glTranslatef(aff[12],aff[13],aff[14]);

     /* The wand orientation could cause such a move: */
     if(!morph){ /* determine could_move */
     could_move=0;
     if(wnd==1){
       /* Perhaps optimally fine-tuned for these algorithms?
	  Perhaps rol threshhold should be lower */
       if(paw==0){
	 /*
	 int too_diagonal=0;
	 if(ele>40){could_move=5;too_diagonal++;}
	 if(ele<-13){could_move=6;too_diagonal++;}
	 if(azi>=26){could_move=4;too_diagonal++;}
	 if(azi<=-26){could_move=3;too_diagonal++;}
	 if(rol>=28){could_move=1;too_diagonal++;}
	 if(rol<=-58){could_move=2;too_diagonal++;}
	 if(wz-hz<-1.26){could_move=8;too_diagonal++;}
	 if(wz-hz>-.71){could_move=7;too_diagonal++;}
	 if(too_diagonal>1)could_move=0;
	 */
	 /* More natural: */
	 if(ABS(ele-13.5)>ABS(2*azi) && ABS(ele-13.5)>ABS((2*rol)+30)){
	   if(ele>40)could_move=5;if(ele<-13)could_move=6;}
	 else if(ABS(azi)>ABS((2*ele)-27) && ABS(azi)>ABS((2*rol)+30)){
	   if(azi>26)could_move=4;if(azi<-26)could_move=3;}
	 else if(ABS(rol+15)>ABS((2*ele)-27) && ABS(rol+15)>ABS(2*azi)){
	   if(rol>28)could_move=1;if(rol<-58)could_move=2;}
	 if(!could_move){
	   if(wz-hz<-1.4)could_move=8;
	   if(wz-hz>-.1)could_move=7;}
       }
       else if(paw==4){
	 if(ABS(ele-13.5)>ABS(2*azi) && ABS(ele-13.5)>ABS((2*rol)+30)){
	   if(ele>40)could_move=13;if(ele<-13)could_move=14;}
	 else if(ABS(azi)>ABS((2*ele)-27) && ABS(azi)>ABS((2*rol)+30)){
	   if(azi>26)could_move=12;if(azi<-26)could_move=11;}
	 else if(ABS(rol+15)>ABS((2*ele)-27) && ABS(rol+15)>ABS(2*azi)){
	   if(rol>28)could_move=16;if(rol<-58)could_move=15;}
	 if(wz-owz>.35)could_move=9;
	 if(wz-owz<-.35)could_move=10;
	 if(mode==TURNMODE)could_move=0;
       }
     }
     if(wnd==2){
       if(opaw == 2 && paw == 3)could_move=10;
       if(opaw == 2 && paw == 6)could_move=9;
       if(paw==4){
	 if((ele-oele)>20 && ABS(azi-oazi)<26)could_move=6;
	 else if((ele-oele)<-26 && ABS(azi-oazi)<20)could_move=5;
	 else if(ABS(azi-oazi)>=26 && (azi-oazi)>ABS(rol-orol))could_move=3;
	 else if(ABS(azi-oazi)>=26 && (azi-oazi)<-ABS(rol-orol))could_move=4;
	 else if((rol-orol)>=28)could_move=2;
	 else if((orol-rol)>=28)could_move=1;
	 else if((wz-owz)<-.2)could_move=7;
	 else if((wz-owz)>.2)could_move=8;
       }/* Only make a move if holding button 2 */
       else if(paw==2){
	 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)could_move=11;
	   else if((wx-owx)<-.4)could_move=12;}
	 else if(most_movement==1){
	   if((wy-owy)<-.4)could_move=14;
	   else if((wy-owy)>.4)could_move=13;}
	 else{
	   if((wz-owz)<-.4)could_move=15;
	   else if((wz-owz)>.4)could_move=16;}
	 if(could_move>10){
	   /* owx=wx;owy=wy;owz=wz;*/
	   if(mode==TURNMODE)could_move=0;}
       }
     }
     audWandOrientation(0);/* if(!morph), too */
     }/* could_move determined */

     switch(moving){
     case 0 : 
       audMove(0);
       if(morph)break;/* only used when autotymer has first=1 */
       moving=could_move;
       if(collision_check()==0){
	 audCollision(1,0);/* Must be before moving is reset */
	 moving=0;
       }
       else audCollision(0,0);
       /* could_move=0;*/ /* Fine but not needed b/c reset above */
       break;/* case 0 */
   case 1 : Theta+=rot_jump[speed];glRotatef((-1*rot_jump[speed]),0.,0.,1.);break;
   case 2 : Theta+=rot_jump[speed];glRotatef( rot_jump[speed],0.,0.,1.);break;
   case 3 : Theta+=rot_jump[speed];glRotatef( rot_jump[speed],0.,1.,0.);break;
   case 4 : Theta+=rot_jump[speed];glRotatef((-1*rot_jump[speed]),0.,1.,0.);break;
   case 5 : Theta+=rot_jump[speed];glRotatef((-1*rot_jump[speed]),1.,0.,0.);break;
   case 6 : Theta+=rot_jump[speed];glRotatef( rot_jump[speed],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[speed];
     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[speed];
     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[speed];
     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[speed];
     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[speed];
     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[speed];
     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[speed];glTranslatef(0.,0.,-1*fore_jump[speed]*siz);break;
   case 8 : Theta+=fore_jump[speed];glTranslatef(0.,0.,fore_jump[speed]*siz);break;
   case 9:
     Theta+=fore_jump[speed];
     if(Theta==4){
       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];
       aff2taff(); tween_levels=1;
     }
     else tween_levels=0;
     break;
   case 10:
     Theta+=fore_jump[speed];
     if(Theta==4){
       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];
       aff2taff(); tween_levels=1;       
     }
     else tween_levels=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) essentially */

   glMultMatrixf(aff); 
   glGetFloatv(GL_MODELVIEW_MATRIX,aff);

  glPopMatrix();
  opaw=paw; ohx=hx; ohy=hy; ohz=hz;

  /* Replace lagmvg cases: */
  if(!frozen){ /* Necessary for proper audio */
  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();
  }else audMove(1); 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();
  }else audMove(2); 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();
  }else audMove(2); break;
  case 9:
  case 10:
    audMove(2);audMove(3);
    stairlight=ABS(4.-Theta)/4.0;
    if(Theta>=8){Theta=0;moving=0;audMove(4);/* printf("\nmoved in 4D:"); */}
    break;
  case 11:
  case 12:
  case 13:
  case 14:
  case 15:
  case 16: 
    if(Theta==theta_jump[speed])rotating_in_4D=1; 
    if(Theta>=90){
      Theta=0;moving=0;rotating_in_4D=0;theta_delay=0;audMove(4);
      /* printf("\nrotated in 4D:"); */
      break; 
    }
    else audMove(1);audMove(3); 
  }
  }
  /*   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 odometer(void){
    static struct timeval tv0;  /* time when program started */
    struct timeval tv;
    gettimeofday(&tv, NULL);
    if(tv0.tv_sec == 0) tv0 = tv;  /* initialize tv0 */
    return (tv.tv_sec - tv0.tv_sec) + (tv.tv_usec - tv0.tv_usec)*1e-6;
}

float speedometer(void){
       static float rate;
       static int ii=0;
       static float then;
       float now = odometer();
   if(++ii % 8 == 0){ rate = 8.0/ (now-then);  then = now; }
   return(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 */
      if(mode==TURNMODE) glColor3f(1.,0.,1.);
        else      glColor3f(1.,1.,1.);
      LABEL2(80,2770,"(ESC)ape (Z)ap (V)Binoc%s","");
      LABEL2(80,2700,"(W)riting      (N)ose   %0.3f",nose);
      LABEL2(80,2630,"f(O)cal factor %g",focal);
      LABEL2(80,2560,"far cli(P)er %3.1f",far);
      LABEL2(80,2490,"(G)ap %1.3f",gap);
      if(!morph){
	LABEL2(80,2420,"(H)ypertour%s","");}
      else{
	LABEL2(80,2420,"(H)ypertour pause/unpause/quit%s","");}
      LABEL2(80,2350,"(M)ove in +/- hidden direction%s","");
      if(rotating_in_4D==0){
      LABEL2(80,2280,"(1/2/3) Rotate your right/up/front towards the +hidden axis%s","");
      LABEL2(80,2210,"(S)onar (along hidden axis)","");
      }
      /*LABEL2(80,2490,"my s(I)ze %.2g",mysiz);*/
      /*LABEL2(80,2630,"near clipper %g", mysiz*focal);*/
      /*if(mode==TURNMODE){LABEL2(80,1860,"%sWarning: rotate is wack! Use sparingly.","");}*/ /* Rotate is _disabled_ */
      {sprintf(buf,"location %d %d %d %d",location[0],location[1],
	       location[2],location[3]);char2wall(300,80,0.,buf);}
      LABEL2(80,80,"%4.1f fps",speedometer());
      LABEL2(10,10,"illiMaze4D   \
   by Michael Pelsmajer, U Illinois, (C) 1998,1999,2000 %s","");
      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]);
   if(could_move && (moving || morph))could_move=0;
   else if(!morph && !could_move && !moving){
     if(but&(1<<GLUT_RIGHT_BUTTON ))could_move=1;
     if(but&(1<<GLUT_LEFT_BUTTON  ))could_move=2;
     if(dx>90 && dx>ABS(dy))could_move=3;
     else if(dx<-90 && -1*dx>ABS(dy))could_move=4;
     else if(dy>90)moving=6;else if(dy<-90)could_move=5;
     else if(but&(1<<GLUT_MIDDLE_BUTTON))could_move=8-shif;
   } /* determined could_move */
   switch(moving){
   case 0 : 
     audMove(0);
     if(morph)break;/* only used when autotymer has first=1 */
     moving=could_move;
     if(collision_check()==0){
       audCollision(1,0);/* Must be before moving is reset */
       moving=0;
     }
     else audCollision(0,0);
     could_move=0;
     break;/* case 0 */
   case 1 : Theta+=rot_jump[speed];glRotatef(-1*rot_jump[speed],0.,0.,1.);break;
   case 2 : Theta+=rot_jump[speed];glRotatef( rot_jump[speed],0.,0.,1.);break;
   case 3 : Theta+=rot_jump[speed];glRotatef( rot_jump[speed],0.,1.,0.);break;
   case 4 : Theta+=rot_jump[speed];glRotatef(-1*rot_jump[speed],0.,1.,0.);break;
   case 5 : Theta+=rot_jump[speed];glRotatef(-1*rot_jump[speed],1.,0.,0.);break;
   case 6 : Theta+=rot_jump[speed];glRotatef( rot_jump[speed],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[speed];
     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[speed];
     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[speed];
     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[speed];
     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[speed];
     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[speed];
     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[speed];glTranslatef(0.,0.,-1*fore_jump[speed]*siz);break;
   case 8 : Theta+=fore_jump[speed];glTranslatef(0.,0.,fore_jump[speed]*siz);break;
   case 9:
     Theta+=fore_jump[speed];
     if(Theta==4){
       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];
       aff2taff(); tween_levels=1;
     }
     else tween_levels=0;
     break;
   case 10:
     Theta+=fore_jump[speed];
     if(Theta==4){
       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];
       aff2taff(); tween_levels=1;
     }
     else tween_levels=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();
  }else audMove(1); 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();
  }else audMove(2); 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();
  }else audMove(2); break;
  case 9:
  case 10: 
    audMove(2);audMove(3);stairlight=ABS(4.-Theta)/4.0;
    if(Theta>=8){Theta=0;moving=0;audMove(4);/* printf("\nmoved in 4D:"); */}
    break;
  case 11:
  case 12:
  case 13:
  case 14:
  case 15:
  case 16: if(Theta>=90){
    Theta=0;moving=0;rotating_in_4D=0;theta_delay=0;audMove(4);
    /* printf("\nrotated in 4D:"); */
    /* These are not needed, right? */ roundtaff();taff2aff();
    break; 
  }
  else audMove(1);audMove(3); 
  }
}

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();*/} 
  else audMove(0);
  glutPostRedisplay();
}

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
       ;
   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();
       }
} 
