/* BALL.C Interactive simulation of Free-Throw Space A project for Math 559, Chaotic Dynamical Systems, Professor Stephane Laederich For the Silicon Graphics GL package Copyright (C) Paul E. Debevec 1992-1998 debevec@cs.berkeley.edu Release 1.0 April 1998 Compile using the following command: cc ball.c -lc_s -lgl_s -lm -o ball */ #include #include #include #include #include #define BALL_RAD 0.395833333333 /* Ball Radius */ #define RIM_RATIO 0.027777777778 /* rim thickness over rim diameter */ #define XVIEW 1.25 #define YVIEW 0.5 #define ZVIEW 8 #define ZOOMFACTOR 4.0 #define MAXRES 40 /* Define Variable Modeling Parameters */ int courtmod = 1; int netmod = 1; int boardmod = 1; int ballmod = 2; int hoopmod = 3; int vecmod = 1; int postmod = 2; int viewmod = 2; int make_movie = 0; int clongcuts =10; int hlongcuts =10; int hlatcuts =10; int slongcuts =10; int slatcuts =10; int numweaves =10; int numhooks =10; int stepsperframe = 4; /* Define pop-up menus */ int mainmenu; int netmenu; int viewmenu; int hoopmenu; int ballmenu; int boardmenu; int vecmenu; int courtmenu; int postmenu; int savemenu; int stepmenu; Matrix Identity = { 1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1 }; /************************/ /* Material Definitions */ /************************/ float vecmat[] = { /* Vector indicator material */ AMBIENT, 0.2,0.3,0.6, DIFFUSE,0.12,0.269,0.765, SPECULAR,0.2,0.2,0.2, SHININESS,10, LMNULL }; float ballmat[] = { /* Basketball material */ AMBIENT, 0.2,0.1,0.0, DIFFUSE,0.72,0.269,0.065, SPECULAR,0.1,0.1,0.1, SHININESS,10, LMNULL }; float rimmat[] = { /* Hoop Rim material */ AMBIENT, 0.2,0.1,0.1, DIFFUSE,0.82,0.209,0.065, SPECULAR,0.9,0.9,0.9, SHININESS,50, LMNULL }; float bfloormat[] = { /* Court floor material */ AMBIENT, 0.75,0.4,0.2, DIFFUSE,0.2,0.2,0.2, SPECULAR,0.1,0.1,0.1, SHININESS,10, LMNULL }; float boardmat[] = { /* Backboard material */ AMBIENT, 0.2,0.2,0.2, DIFFUSE,1,1,1, SPECULAR,0.1,0.1,0.1, SHININESS,10, LMNULL }; static float lm[] = { /* Defines the lighting model */ AMBIENT,0.4,0.4,0.4, LOCALVIEWER,1,TWOSIDE,0,LMNULL }; static float lt0 [] = { /* defines the lighting source */ LCOLOR,0.8,0.8,0.8, AMBIENT,0.3,0.3,0.3, POSITION,0.4,0.5,0.3,0, LMNULL }; static float lt1 [] = { /* defines the lighting source */ LCOLOR,0.0,0.0,0.0, AMBIENT,0.1,0.1,0.1, POSITION,+0.5,1,0.5,0, LMNULL }; /* Store initial positions of the valuators */ int d0init,d1init,d2init,d3init,d4init,d5init,d6init,d7init; long mapwin,hoopwin; /* Windows */ long curwin = 0; /* ID of input focus window */ short val; /* Used for event input */ /* The colormap and some colors */ static float colormap[256][3]; static float black[3] = {0.0,0.0,0.0}; static float white[3] = {1.0,1.0,1.0}; static float textc[3] = {0.2,0.8,1.0}; /* Used for text */ main() { long xorigin,yorigin,xsize,ysize,xor,yor,ytext; float rx,ry,x,y,z; double vx,vy,x0,y0; short val; /* Used for event input */ long p[2]; int i,j,m,bounces,replot; int imax,jmax; double zoom, vxcent, vycent, mvx,mvy; long key; char outstr[80]; int xmouse,ymouse,oldx,oldy; int count,length,dir; int numzooms; int drawme; int pupval; void plot(double x,double y,double vx,double vy); int dynamic(double x0, double y0, double vx, double vy, int show); void cmapinterp(int i1,float r1,float g1,float b1,int i2,float r2,float g2,float b2); void initall(); void menuinit(); void winsave(char *filename); hoopwin = winopen("Free Throw Simulation"); winposition(1270-800,1270,1000-500,1000); prefposition(5,260,5,400); mapwin = winopen("Free Throw Space"); qdevice(ESCKEY); qdevice(LEFTMOUSE); qdevice(RIGHTMOUSE); qdevice(MIDDLEMOUSE); qdevice(MENUBUTTON); RGBmode(); gconfig(); clear(); /* Set up the graphics windows */ winset(hoopwin); getorigin(&xorigin,&yorigin); getsize(&xsize,&ysize); RGBmode(); doublebuffer(); gconfig(); lsetdepth(getgdesc(GD_ZMIN),getgdesc(GD_ZMAX)); zbuffer(1); mmode(MVIEWING); loadmatrix(Identity); perspective(450,xsize/(float)ysize,0.1,6000.0); lmdef(DEFLIGHT,1,0,lt0); lmdef(DEFLIGHT,2,0,lt1); lmdef(DEFLMODEL,1,0,lm); lmbind(LMODEL,1); lmbind(LIGHT0,1); lmbind(LIGHT1,2); d0init = getvaluator(DIAL0); d1init = getvaluator(DIAL1); d2init = getvaluator(DIAL2); d3init = getvaluator(DIAL3); d4init = getvaluator(DIAL4); d5init = getvaluator(DIAL5); d6init = getvaluator(DIAL6); d7init = getvaluator(DIAL7); menuinit(); initall(); x0=10.0; y0=(-3.0); vx=(-13); vy=15; plot(x0,y0,vx,vy); winset(mapwin); /* Create Color Map */ cmapinterp(128,1,1,0,255,1,1,0); /* Megabounce Makes */ cmapinterp(136,0,0.7,0,154,1,1,1); /* High-Bounce Makes */ cmapinterp(129,0,0.15,0,136,0,0.7,0); /* Low-Bounce Makes */ cmapinterp(0,0,0.3,1,127,0,0.3,1); /* Megabounce Misses */ cmapinterp(120,1,0,0,127,0.2,0,0); /* High-Bounce Misses */ cmapinterp(107,0,0,1,120,1,0,0); /* Low-Bounce Misses */ imax=128; jmax=128; zoom=0.1; vxcent=(-15.0); vycent=15.0; rectzoom((float)3,(float)3); numzooms=0; drawme = 1; while (1) { replot=0; getorigin(&xor,&yor); count=0; length=0; dir=0; i=imax/2 -1; j=jmax/2 -1; oldx=oldy=0; c3f(white); sboxfi(0,256,31,256+31); /* Main plotting loop */ for (m=0;(m<(imax*jmax)) && !replot;m++) { vx = vxcent + (2.0*(float)j/jmax -1) / zoom; vy = vycent + (2.0*(float)i/imax -1) / zoom; bounces=dynamic(x0,y0,vx,vy,0); c3f(colormap[bounces + 128]); sboxfi(2*j,2*i,2*j+1,2*i+1); xmouse=(getvaluator(MOUSEX)-xor)/2*2; ymouse=(getvaluator(MOUSEY)-yor)/2*2; if (drawme || (curwin==mapwin) && ((oldx != xmouse) || (oldy != ymouse))) { /*replot coordinates*/ drawme = 0; rectcopy(xmouse-4,ymouse-4,xmouse+5,ymouse+5,1,257); mvx = vxcent + ((float)(xmouse/2) /(jmax/2) -1) / zoom; mvy = vycent + ((float)(ymouse/2) /(imax/2) -1) / zoom; c3f(black); sboxfi(32,256,255,300); c3f(textc); ytext=395; cmov2i(2,ytext-=12); sprintf(outstr,"A Chaotic Dynamic Simulation"); charstr(outstr); cmov2i(2,ytext-=12); sprintf(outstr," of Free-Throw Space"); charstr(outstr); cmov2i(2,ytext-=16); sprintf(outstr," Paul Debevec"); charstr(outstr); cmov2i(2,ytext-=12); sprintf(outstr,"www.cs.berkeley.edu/~debevec"); charstr(outstr); cmov2i(2,ytext-=18); sprintf(outstr," Left:Shoot Mid/Right: Zoom"); charstr(outstr); cmov2i(40,ytext=284); sprintf(outstr,"Magnification %1.0f%%",zoom*1000); charstr(outstr); cmov2i(40,ytext-=12); sprintf(outstr,"x=%3.15f",mvx); charstr(outstr); cmov2i(40,ytext-=12); sprintf(outstr,"y= %3.15f",mvy); charstr(outstr); } oldx=xmouse;oldy=ymouse; if (getbutton(SW31)) { winset(hoopwin); plot(x0,y0,vx,vy); winset(mapwin); } /* Event Handler */ if (qtest()) { key = qread(&val); switch (key) { case INPUTCHANGE: curwin=val; if (curwin==0) { /* Clear out little window */ c3f(black); sboxfi(1,257,29,257+29); } break; case ESCKEY: exit(-1); break; case LEFTMOUSE: /* Recreate the clicked-on shot */ if (val!=0) break; if (curwin==mapwin) { winset(hoopwin); bounces=dynamic(x0,y0,mvx,mvy,1); winset(mapwin); if (bounces > 0) /* Form output string */ sprintf(outstr,"%d bounce%s, 2 pts.",bounces-1,(bounces==2)?"":"s"); else if (bounces < 0) sprintf(outstr,"%d bounce%s, 0 pts.",-bounces-1,(bounces==(-2))?"":"s"); c3f(black); sboxfi(32,301,255,320); cmov2i(40,302); c3f(textc); charstr(outstr); } break; case MIDDLEMOUSE: case RIGHTMOUSE: /* Zoom in or out */ if (curwin==mapwin && val==0) { vxcent = vxcent + ((float)(xmouse/2) /(jmax/2) -1) / zoom; vycent = vycent + ((float)(ymouse/2) /(imax/2) -1) / zoom; zoom*=(key == MIDDLEMOUSE)?ZOOMFACTOR:(double)1.0/ZOOMFACTOR; numzooms+=(key == MIDDLEMOUSE)?1:(-1); c3f(black); clear(); replot=1; oldx=oldy=(-1); /* force replot upon entry */ } else { if ((key==RIGHTMOUSE) && (val!=0) && (curwin==hoopwin)) { pupval = dopup(mainmenu); winset(hoopwin); plot(x0,y0,vx,vy); winset(mapwin); } } break; case REDRAW: if (val==hoopwin) { winset(hoopwin); reshapeviewport(); getsize(&xsize,&ysize); perspective(450,xsize/(float)ysize,0.1,6000.0); plot(x0,y0,vx,vy); winset(mapwin); } else if (val==mapwin) { reshapeviewport(); c3f(black); clear(); replot=1; } break; } } /* Calculate next point in spiral */ switch(dir) { case 0:i++;break; case 1:j++;break; case 2:i--;break; case 3:j--;break; } count++; if (count > length/2) {length++;dir=(dir+1) % 4;count=0;} } } } void winsave(char *filename) /* Saves the active RGB window to the specified file */ { FILE *f; unsigned char *buffer; unsigned int *lbuffer; unsigned int temp; long xsize,ysize; int numpix,size; register i,x,y; getsize(&xsize,&ysize); numpix=xsize*ysize; buffer = malloc(4*numpix); lrectread(0,0,xsize-1,ysize-1,(unsigned long *)buffer); lbuffer = (unsigned int *)buffer; /* Flip upside-down */ for (y=0;y= 2) { numweaves=9; numhooks=12; } /* Initialize Net Coordinates */ dtheta = 2 * M_PI / numhooks; for (weave=0;weave < numweaves;weave++) { r1=0.75 - 0.3 * sqrt(sqrt(0.5*(double)weave)); r2=0.012; y = (-1.7*(sqrt(weave)) / sqrt(numweaves)); x = 1.25; z = 0; for (hook = 0, theta = 0 ; hook < numhooks; hook++, theta += dtheta) { theta2=theta + (weave % 2)*0.5*dtheta; net[weave][hook][0]=x+r1*cos(theta2); net[weave][hook][1]=y; net[weave][hook][2]=z+r1*sin(theta2); netn[weave][hook][0]=cos(theta2); netn[weave][hook][1]=0; netn[weave][hook][2]=sin(theta2); } } } void initball(int model) { int i,j; double x,y,z,r1,r2; double theta,theta2,dtheta,phi,dphi,length; slatcuts = 5; slongcuts = 8; if (model >= 2) { slatcuts=12; slongcuts=12; } /* Initialize Sphere Coordinates */ dtheta = M_PI/slatcuts; dphi = 2 * M_PI/slongcuts; for (i = 0, theta = -M_PI/2 ; i <= slatcuts; i++, theta += dtheta) { for (j = 0, phi = 0; j < slongcuts; j++, phi += dphi) { ball[j][i][0] = balln[j][i][0] = cos(theta) * cos(phi); ball[j][i][1] = balln[j][i][1] = sin(theta); ball[j][i][2] = balln[j][i][2] = cos(theta) * sin(phi); } } } void inithoop(int model) { int i,j; double x,y,z,r1,r2; double theta,theta2,dtheta,phi,dphi,length; double a,b,c,d,e,f; hlatcuts=6; hlongcuts=6; if (model == 2) { hlatcuts=12; hlongcuts=12; } if (model == 3) { hlatcuts=36; hlongcuts=24; } /* Initialize Torus Coordinates */ dtheta = 2 * M_PI/hlatcuts; dphi = 2 * M_PI/hlongcuts; for (i = 0, theta = 0 ; i < hlatcuts; i++, theta += dtheta) { for (j = 0, phi = 0; j <= hlongcuts; j++, phi += dphi) { if (j == hlongcuts) phi = 0; hoop[j][i][0] = (1 + RIM_RATIO * cos(phi))* cos(theta); hoop[j][i][1] = RIM_RATIO * sin(phi); hoop[j][i][2] = (1 + RIM_RATIO * cos(phi))* sin(theta); /* hoopn[j][i][0] = cos(theta) * cos(phi); hoopn[j][i][1] = sin(phi); hoopn[j][i][2] = sin(theta) * cos(phi); */ d = (-sin(theta))*(1+RIM_RATIO*cos(phi)); e = 0; f = cos(theta)*(1+RIM_RATIO*cos(phi)); a = (-RIM_RATIO)*cos(theta)*sin(phi); b = RIM_RATIO*cos(phi); c = (-RIM_RATIO)*sin(theta)*sin(phi); x = b*f-c*e; y = c*d-a*f; z = a*e-b*d; /* x = RIM_RATIO*cos(phi)*cos(theta)*(1+RIM_RATIO*cos(phi)); y = (-RIM_RATIO)*cos(theta)*cos(theta)*sin(phi)*(1+RIM_RATIO*cos(phi)) + RIM_RATIO*sin(theta)*sin(theta)*sin(phi)*(1+RIM_RATIO*cos(phi)); z = (-RIM_RATIO)*cos(phi)*sin(theta)*(1+RIM_RATIO*cos(phi)); */ length = sqrt(x*x + y*y + z*z); hoopn[j][i][0] = x / length; hoopn[j][i][1] = y / length; hoopn[j][i][2] = z / length; } } } void drawnet() /* Draws the net */ { int hook,hook2,weave,strand; double radius=0.01; double h; int numballs; void drawsphere(double xc, double yc, double zc, double r); for (weave=0;weave < numweaves-1;weave++) { for (hook = 0; hook < numhooks; hook++) { hook2=(numhooks+hook+((weave%2 != 0)?1:(-1)))%numhooks; if (netmod == 3) { numballs=(int)(2.0*(net[weave][0][1]-net[weave+1][0][1])/radius); for (strand=0;strand0) drawsphere(net[weave][hook][0],net[weave][hook][1],net[weave][hook][2],1.75*radius); } } } void drawtorus(double xc, double yc, double zc, double r1) /* Draws the torus graphics primitive for the hoop */ { int i,j; pushmatrix(); translate(xc,yc,zc); scale(r1,r1,r1); for (i = 0; i < hlatcuts; i++) { bgntmesh(); for (j = 0; j <= hlongcuts; j++) { n3f(hoopn[j%hlongcuts][i]); v3f(hoop[j%hlongcuts][i]); n3f(hoopn[j%hlongcuts][(i+1)%hlatcuts]); v3f(hoop[j%hlongcuts][(i+1)%hlatcuts]); } endtmesh(); } popmatrix(); } static float vecn[3] = {0.0,0.0,1.0}; void drawvec(double x0,double y0,double x1,double y1) { float vec[3]; bgntmesh(); n3f(vecn); vec[2] = 0; vec[0] = x1; vec[1] = y1; v3f(vec); vec[0] = x0-(y1-y0)/12; vec[1] = y0+(x1-x0)/12; v3f(vec); vec[0] = x0+(y1-y0)/12; vec[1] = y0-(x1-x0)/12; v3f(vec); endtmesh(); } static float bfloorn[3] = {0.0,1.0,0.0}; static float bfloor[4][3] = { {-4.0 , -10.0 , 25.0}, {-4.0 , -10.0 , -25.0}, {50.0 , -10.0 , -25.0}, {50.0 , -10.0 , 25.0}}; static float boardn[3] = {1.0,0.0,0.0}; static float board[4][3] = { {0.0 , -0.5 , -3.0}, {0.0 , 4.0 , -3.0}, {0.0 , 4.0 , 3.0}, {0.0 , -0.5 , 3.0}}; static float connectn[3] = {0.0,-1.0,0.0}; static float connect[4][3] = { {0.0 , 0.0 , -0.25}, {0.5 , 0.0 , -0.25}, {0.5 , 0.0 , 0.25}, {0.0 , 0.0 , 0.25}}; void drawbfloor() { bgntmesh(); n3f(bfloorn); v3f(bfloor[0]); v3f(bfloor[1]); v3f(bfloor[3]); v3f(bfloor[2]); endtmesh(); } void drawboard() { bgnpolygon(); /* n3f(boardn); */ RGBcolor(255,255,255); v3f(board[0]); v3f(board[1]); v3f(board[2]); v3f(board[3]); endpolygon(); lmdef(DEFMATERIAL,1,0,rimmat); lmbind(MATERIAL,1); bgnpolygon(); n3f(connectn); v3f(connect[0]); v3f(connect[1]); v3f(connect[2]); v3f(connect[3]); endpolygon(); } /* Beginning of the dynamic simulation code */ #define a 16.0837 /* g/2 in feet/sec */ #define xloc(t,vx,x0) ((x0)+((t)*(vx))) #define yloc(t,vy,y0) ((y0)+((t)*(vy))-((a)*(t)*(t))) #define cdist(x,y,xc,yc,r) (sqrt(((x)-(xc))*((x)-(xc)) + ((y)-(yc))*((y)-(yc))) - (r)) double bdist(double x,double y,double x0, double y0, double x1, double y1) /* Distance of the ball from a box, ie the backboard */ { double maxyet; maxyet = x0-x; if ((x-x1) > maxyet) maxyet=(x-x1); if ((y0-y) > maxyet) maxyet=(y0-y); if ((y-y1) > maxyet) maxyet=(y-y1); return(maxyet); } double objdist(double x, double y, int whichobj) /* Distance of the ball from a circle */ { double dist; double bdist(double x,double y,double x0, double y0, double x1, double y1); switch (whichobj) { case 0: dist = cdist(x,y,2, 0,BALL_RAD); break; case 1: dist = cdist(x,y,0.5,0,BALL_RAD); break; case 2: dist = cdist(x,y,0 ,4,BALL_RAD); break; case 3: dist = bdist(x,y,-BALL_RAD,0,BALL_RAD,4); break; } return(dist); } double Dist(double x, double y, int *closest) /* Distance of the ball to the hoop & board structure */ { double objdist(double x, double y, int whichobj); int i; double dist,mindist; mindist = objdist(x,y,0); *closest = 0; for (i=1;i<4;i++) { dist = objdist(x,y,i); if (dist < mindist) { mindist=dist; *closest=i; } } return(mindist); } void Bounce(double x,double y,double *vx,double *vy,int closest) /* Bounces the ball off the board or rim */ { double xc,yc,vxt,vyt,r,sinth,costh; if (closest == 3) *vx = (- (*vx)); else { switch (closest) { case 0: xc=2;yc= 0; break; case 1: xc = 0.5;yc=0; break; case 2: xc = 0;yc=4; break; } r = sqrt( (x-xc)*(x-xc) + (y-yc)*(y-yc) ); sinth = (y-yc) / r; costh = (x-xc) / r; vxt = (*vx) * costh + (*vy) * sinth; vyt = (-(*vx)) * sinth + (*vy) * costh; vxt = (-vxt); *vx = vxt * costh - vyt * sinth; *vy = vxt * sinth + vyt * costh; } } #define terminated(show,x,y,vx,vy) (((y)0) { while (begint>(t+dt)) t+=dt; x=xloc(t,vx,x0); y=yloc(t,vy,y0); } } /* Begin main loop */ while (!terminated(show,x,y,vx,vy-2*a*t)) { cvy=vy-2*a*t; /***********/ /* Move ball until intersection detected */ /***********/ while ((!terminated(show,x,y,vx,cvy)) && (firstone || (Dist(x,y,&closest) > 0))) { if (((i++)%stepsperframe == 0) && show) { plot(x,y,vx,cvy); /* Display state of system */ if (make_movie) { printf("Making movie frame...\n"); sprintf(filename,"Movie/movie%04d.ppm",framenum++); winsave(filename); } } t+=(firstone)?firststep:dt; x=xloc(t,vx,x0); y=yloc(t,vy,y0); cvy=vy-2*a*t; madeit=madeit || ((x>0.5) && (x<2) && (y<0) && (oldy>=0) && (cvy<0)); firstone=0; oldy=y; } if (!terminated(show,x,y,vx,cvy)) { /***********/ /* Find exact time of intersection with bisection method*/ /***********/ ta=t-dt; lastplot=tb=t; iter=0; olddif=0; while ((dif=tb-ta) !=olddif) { olddif=dif; iter++; t = 0.5 * (tb+ta); if (Dist(xloc(tb,vx,x0),yloc(tb,vy,y0),&dummy) * Dist(xloc(t ,vx,x0),yloc(t ,vy,y0),&closest) < 0.0) ta = t; else tb = t; } x=xloc(t,vx,x0); y=yloc(t,vy,y0); madeit=madeit || ((x>0) && (x<2) && (y<0) && (oldy>=0) && (vy-2*a*t<0)); /***********/ /* Bounce the ball off the object */ /***********/ x0=x; y0=y; vy = vy - 2*a*t; firststep = lastplot + dt - t; t=0; Bounce(x,y,&vx,&vy,closest); numbounces++; firstone=1; } } /* Loop */ if (madeit) return(numbounces+1); else return(-(numbounces+1)); }