/*** "cs3d.c" by Thomas Insel

A three-dimensional cellular automata shell developed for Professor George 
Francis's Math 198 class at the University of Illinois, Urbana-Champaign

cs3d incorporates code by Prof. George Francis, Steve Kommrush, Pablo Mena, 
and J.Obermark.

cc $1 /usr/lib/libgl_s.a /usr/lib/libc_s.a /usr/lib/libm.a /usr/lib/libX11_s.a

***/

#include <vogl.h>
#include <vodevice.h>
#include <math.h>
#include <sys/time.h>

/*macros by G.K.Francis*/
#define  FOR(a,b,c)     for(a=b;a<c;a++)
#define  IF(K)          if(getbutton(K))
#define  IFSHIFT        if(getbutton(LEFTSHIFTKEY)||getbutton(RIGHTSHIFTKEY))
#define  PRESS(K,A,b)   IF(K){IFSHIFT{A;}else{b;}}
#define  SOAK(K)        while(getbutton(K))

int done=0; /*exit the program?*/
int LLL=4, ULL=5, LBL=5, UBL=5; /*life rules*/
int dims=10; /*dimension of world*/
int dm[]= {0,2,1,1,1,1,1,1,1,1}; /*0=not,1=frame,2=solid*/
int SHOWFRAME=1;
float spin=0, maus = 1.; /* Hartman rotor variables */

float Q; /*scale*/
float gaps[10]={1.,.9,.84,.78,.72,.66,.60,.54,.48,.42}; /*cube scale*/

float  near=4., far=40., pos=-20.; /*display*/
int game=1; /*1=life4555, 2=...*/

float vq[8][3]; /* Kommrush cube in main */
int fr[]= {0,1,5,1,3,7,3,2,6,2,0,4,5,7,6,4}; /*wire cube*/
int fq[]= {7,5,6,4,2,0,1,5,3,7,6,2,1,0}; /*my solid cube, still has bugs*/

int OW[100][100][100], NW[100][100][100];

#define	white	colors[1]
#define	black	colors[0]
#define	red	colors[2]
#define blue	colors[4]
float colors[8][3]={{0.,0.,0.}, /*black*/
                    {1.,1.,1.}, /*white*/
                    {1.,0.,0.}, /*red*/
                    {0.,1.,0.}, /*green*/
                    {0.,0.,1.}, /*blue*/ 
                    {0.,1.,1.},
                    {1.,0.,1.},
                    {1.,1.,0.} };

def_simple_light_calc() { /*j.obermark*/
  float modelspec[]={AMBIENT,.5,.5,.5,ATTENUATION,1.0,0.0,LMNULL};
  float lightspec[]={POSITION,0.,0.,1.,.9,LMNULL};
  lmdef (DEFMATERIAL, 1, 0, NULL);
  lmdef (DEFLIGHT, 1, 0, lightspec);
  lmdef (DEFLMODEL, 1, 0, modelspec);
  }

use_simple_light_calc () { /*j.obermark*/
  lmbind (MATERIAL,1); lmbind (LIGHT0,1); lmbind (LMODEL,1); lmcolor(LMC_AD); }

zeroworld() { int ii,jj,kk; 
    FOR(ii,0,dims)FOR(jj,0,dims)FOR(kk,0,dims)OW[ii][jj][kk]=0; }

randomworld() { int ii,jj,kk; 
    FOR(ii,0,dims)FOR(jj,0,dims)FOR(kk,0,dims)
       if(game==3)
          OW[ii][jj][kk]=(((int)random())%6);
       else 
          OW[ii][jj][kk]=( (((int)random())%100)<11 );
}

xfer() { int i,j,k;
  FOR(i,0,dims)FOR(j,0,dims)FOR(k,0,dims) OW[i][j][k]=NW[i][j][k]; }

trans(axis) int axis; {int i,j,k; 
#define AX(a) (axis==a)
  FOR(i,0,dims)FOR(j,0,dims)FOR(k,0,dims) 
    NW[i][j][k]=OW[(i+AX(1))%dims][(j+AX(2))%dims][(k+AX(3))%dims];
  xfer(); }

newdims(dim) int dim;
{int i,j;
   dims=dim;
   Q=3.5/dims;
   FOR(i,0,dims)FOR(j,0,dims) /*needs work here*/
	OW[i][j][dims]=0;
	OW[i][dims][j]=0;
	OW[dims][i][j]=0;
}

int neighbors(X,Y,Z,kind) int X,Y,Z,kind; /*adapted from Pablo Mena's life*/
{   int i,j,k,count=0;

    for(i=dims-1;i<=dims+1;i++)
      for(j=dims-1;j<=dims+1;j++)
        for(k=dims-1;k<=dims+1;k++)
           count+=(OW[(X+i)%dims][(Y+j)%dims][(Z+k)%dims]==kind);
    count-=(OW[X][Y][Z]==kind);
    return(count);
}

generation()
{  int i,j,k,sum;

   switch(game){
     case(1): case(2): /*life, adapted from p.mena*/
        FOR(i,0,dims)FOR(j,0,dims)FOR(k,0,dims){
           sum=neighbors(i,j,k,1);
           NW[i][j][k]=!( ((OW[i][j][k]==1)&&((sum>ULL)||(sum<LLL))) || 
                           ((OW[i][j][k]==0)&&((sum>UBL)||(sum<LBL))) );
           };break;
     case(3): /*color cycler*/
        FOR(i,0,dims)FOR(j,0,dims)FOR(k,0,dims)
          NW[i][j][k]=(OW[i][j][k]+1)%6;
        break;
     }
   xfer();
}

drawcube(o1,o2,o3,type) int o1,o2,o3,type;
{   int ii,jj;float vtemp[3],o[3],dimf;
    o[0]=(float)o1;o[1]=(float)o2;o[2]=(float)o3;dimf=(float)dims;
    linewidth(2);

    if(dm[type]==2){ /*solid*/
       bgntmesh();
       FOR(ii,0,14){
         FOR(jj,0,3)vtemp[jj]=Q*(vq[fq[ii]][jj]*gaps[type]+2*o[jj]-dimf);
         c3f(colors[OW[o1][o2][o3]]);
         n3f(vq[fq[ii]]);
         v3f(vtemp);if(ii==6|ii==10|ii==9) swaptmesh(); }
       endtmesh(); }
    else if(dm[type]==1){ /*wire frame*/
       bgnline();
       FOR(ii,0,16){
         FOR(jj,0,3)vtemp[jj]=Q*(vq[fr[ii]][jj]*gaps[type]+2*o[jj]-dimf);
         c3f(colors[OW[o1][o2][o3]]);
         n3f(vq[fr[ii]]); 
         v3f(vtemp); }
        endline(); }
}

bigbox()
{   int ii,jj;float vtemp[3],o[3],dimf;
    dimf=(float)dims;

    linewidth(2);
    bgnline();
    FOR(ii,0,16) {
	FOR(jj,0,3)vtemp[jj]= Q*( dimf*vq[fr[ii]][jj]*1.02 - 1 );
        c3f(blue);
	n3f(vq[fr[ii]]);
	v3f(vtemp); }
    endline();
}

draworld()
{  int X,Y,Z;
   FOR(X,0,dims)FOR(Y,0,dims)FOR(Z,0,dims)
   if(dm[OW[X][Y][Z]]!=0) drawcube(X,Y,Z,OW[X][Y][Z]);
   if(SHOWFRAME)bigbox();
}

glider()
{
     OW[3][7][0]=1;OW[4][7][0]=1;OW[2][8][0]=1; OW[3][8][1]=1;OW[4][8][1]=1;
     OW[5][8][0]=1;OW[2][9][0]=1; OW[3][9][1]=1;OW[4][9][1]=1;OW[5][9][0]=1;
}

rotor()
{
   OW[5][6][7]=1;OW[5][6][8]=1;OW[6][5][7]=1;OW[6][6][6]=1;OW[6][6][9]=1;
   OW[6][7][7]=1;OW[6][7][8]=1;OW[7][6][7]=1;OW[7][6][8]=1;OW[6][5][8]=1;
}

keybd() { int i;
  PRESS(ONEKEY,dm[1]=0,if(dm[1]==1)dm[1]=2;else dm[1]=1;SOAK(ONEKEY));
  PRESS(TWOKEY,dm[2]=0,if(dm[2]==1)dm[2]=2;else dm[2]=1;SOAK(TWOKEY));
  PRESS(THREEKEY,dm[3]=0,if(dm[3]==1)dm[3]=2;else dm[3]=1;SOAK(THREEKEY));
  PRESS(FOURKEY,dm[4]=0,if(dm[4]==1)dm[4]=2;else dm[4]=1;SOAK(FOURKEY));
  PRESS(FIVEKEY,dm[5]=0,if(dm[5]==1)dm[5]=2;else dm[5]=1;SOAK(FIVEKEY));
  PRESS(SKEY,spin=-1,spin=1);
  PRESS(QKEY,Q-=.01,Q+=.01);
  PRESS(XKEY,SOAK(XKEY);trans(1),trans(1));
  PRESS(YKEY,SOAK(YKEY);trans(2),trans(2));
  PRESS(ZKEY,SOAK(ZKEY);trans(3),trans(3));
  PRESS(F1KEY,if(gaps[1]>=.1)gaps[1]-=.05,if(gaps[1]<=1)gaps[1]+=.05);
  PRESS(F2KEY,if(gaps[2]>=.1)gaps[2]-=.05,if(gaps[2]<=1)gaps[2]+=.05);
  PRESS(F3KEY,if(gaps[3]>=.1)gaps[3]-=.05,if(gaps[3]<=1)gaps[3]+=.05);
  PRESS(F4KEY,if(gaps[4]>=.1)gaps[4]-=.05,if(gaps[4]<=1)gaps[4]+=.05);
  PRESS(F5KEY,if(gaps[5]>=.1)gaps[5]-=.05,if(gaps[5]<=1)gaps[5]+=.05);
  PRESS(BKEY,SHOWFRAME=1,SHOWFRAME=0);
  IF(SPACEKEY){generation();SOAK(SPACEKEY);}
  IF(NKEY){randomworld();SOAK(NKEY);}
  IF(MKEY){zeroworld();glider();}
  PRESS(DKEY,newdims(dims-1);SOAK(DKEY),newdims(dims+1);SOAK(DKEY));
}

int dortmenu(item) int item; {
   if(item)game=item;
   if(item==1){LLL=4;ULL=5;LBL=5;UBL=5;}
   if(item==2){LLL=5;ULL=7;LBL=6; UBL=6;}
}

int dolfmenu(item) int item;
{ switch(item){
	case(1):zeroworld();break;
	case(2):break;
	case(3):break;
	case(4):break; /*domenu here?*/
	case(5):SHOWFRAME=!SHOWFRAME;break;
	case(6):done=1;break;}
}

int dommenu1(item) int item; 
{
	switch(item){
  		case(1):randomworld();break;
		case(2):zeroworld();glider();break;
		case(3):rotor();break;}
}

int dommenu2(item) int item; {if(item==1)randomworld();}

int dommenu3(item) int item; {if(item==1)randomworld();}

doHelp() { 
  printf("Usage:  cs3d [display]\n");
  printf("        cs3d -help\n");
  }

main(argc,argv) int argc; char **argv; {
  int ii,jj ; float temp; long menu1; short val;
  int lfmenu,rtmenu,mmenu[4]; long windid;
  char helpstr[10];

/* Chris Hartman's rotor variables*/
  int  dx=0, dy=0, ox, oy, xx, yy;
  float mm[4][4], id[4][4];
  FOR(ii,0,4)FOR(jj,0,4)mm[ii][jj]=id[ii][jj]= (ii==jj)?1:0;

/* Steve Kommrush's cube by Gray-code */
  FOR(ii,0,8)FOR(jj,0,3)vq[ii][jj]=(ii&(1<<jj))?1:-1;

sprintf(helpstr,"help");
if(argc>1)if(!strncasecmp(argv[1],helpstr)){doHelp();exit(1);}
sprintf(helpstr,"-help");
if(argc>1)if(!strncasecmp(argv[1],helpstr)){doHelp();exit(1);}

Q=3.5/dims; /*setup scale*/

if(argc>1)windid=dglopen(argv[1],DGLTSOCKET);
minsize(255,255);
keepaspect(1.,1.);
winopen("cs3d");
zbuffer(1); doublebuffer(); RGBmode(); gconfig();
lsetdepth(0,0x7fffff);
zfunction(ZF_LESS);

mmode(MPROJECTION);
loadmatrix(id);
mmode(MVIEWING);
loadmatrix(id);
perspective(400,1.,near,far);
def_simple_light_calc();
use_simple_light_calc();

zeroworld(); randomworld();

qdevice(RIGHTMOUSE); qdevice(LEFTMOUSE);

rtmenu=defpup("Automaton%t|Life 4555%F|Life 5766|cycler",dortmenu);
lfmenu=defpup("Automata Shell%t|clear%F|sides...|lighting...|automata%m|toggle box|quit",dolfmenu,rtmenu);
mmenu[1]= defpup("Life 4555%t|random%F|glider|bronco",dommenu1);
mmenu[2]= defpup("Life 5766%t|random%F|item 2|item 3",dommenu2);
mmenu[3]= defpup("Cycler%t|random%F",dommenu3);

while(!getbutton(ESCKEY) && !done){

	keybd();
	if(qtest())switch(qread(&val)) {
	   case(RIGHTMOUSE):if(val)dopup(mmenu[game]);break;
	   case(LEFTMOUSE):if(val)dopup(lfmenu);break;
	   }

	/*Hartman rotor*/
	if (spin<1) {
        	xx = maus*getvaluator(MOUSEX);
		yy = maus*getvaluator(MOUSEY);
		if (spin<0) {ox=xx; oy=yy;spin=0;}
        dx = xx-ox; dy = yy-oy; ox=xx; oy=yy;}

        loadmatrix(id);
        rot(-dy,'x'); rot(dx,'y');
        multmatrix(mm); getmatrix(mm);
	loadmatrix(id);
	translate(0.,0.,pos);
	multmatrix(mm);

        cpack(0); clear(); zclear();

	draworld();
	c3f(black); n3f(white); v3f(black); /*black bkgnd*/

	swapbuffers();
        reshapeviewport();
	}

if(argc>1)dglclose(windid);

}
