
#define TRUE 1
#define FALSE 0

	/* Z bounds */
#define ZMAX 500.0	/* Farthest back things get */
#define ZMIN (-100.0)		/* closest things get before perspective blows up */
#define ZDEPTH (ZMAX-ZMIN)	/* length of total Z space */
#define ZSMALL (ZMIN+1.0)	/* fudge factor to keep perspective in bounds */

	/* total number of colors */
#define COLORS 256
	/* Number of colors in ramp of a single star */
#define ZCOLS 32
	/* Number of different color ramps */
#define ZRAMPS (COLORS/ZCOLS)



	/* number of stars */
int scount = 333;
	/* 3-D star positions */
double *x, *y, *z;
	/* Color of stars */
char *color;


int sw, sh;
double cenx,ceny;
int total_frames = 50;

make_ramp(int start, int colors, int rmax, int gmax, int bmax)
{
int i;

for (i=0; i<colors; ++i)
	SetColorMap(start+i, rmax*i/colors, gmax*i/colors, bmax*i/colors);
}

typedef struct {int r; int g; int b;} Rgb3;

Rgb3 rtab[ZRAMPS] =
	{
		{255, 255, 255},
		{255, 200, 200},
		{200, 255, 200},
		{200, 200, 255},
		{255, 100, 100},
		{100, 100, 255},
		{100, 255, 100},
		{200, 200, 200},
	};

init_colors()
/* Make a bunch of color ramps */
{
int i;

for (i=0; i<ZRAMPS; ++i)
	make_ramp(ZCOLS*i, ZCOLS, rtab[i].r, rtab[i].g, rtab[i].b);
}

random_star(int starix)
/* Initialize the data associated with a star index to a random value */
{
x[starix] = rnd(2000)-1000;
y[starix] = rnd(2000)-1000;
z[starix] = rnd(595)-95;
color[starix] = rnd(ZRAMPS)*ZCOLS;
}

init_stars(int count)
/* Allocate star arrays and set all stars to a random positions and colors */
{
int i;

if ((x = malloc(count*sizeof *x)) == NULL)
	goto ERROUT;
if ((y = malloc(count*sizeof *y)) == NULL)
	goto ERROUT;
if ((z = malloc(count * sizeof *z)) == NULL)
	goto ERROUT;
if ((color = malloc(count * sizeof *color)) == NULL)
	goto ERROUT;
scount = count;
for (i=0; i<scount; ++i)
    random_star(i);
return(TRUE);
ERROUT:
Qtext("Not enough memory for star arrays");
return(FALSE);
}

see_stars()
/* Draw all the stars */
{
int i;
double zscale,zz;

for (i=0; i<scount; ++i)
	{
	zz = z[i];		/* get Z coordinate */
	if (zz > ZSMALL)
		{
			/* set color to match z */
        SetColor(color[i]+(ZMAX-zz)*(ZCOLS/ZDEPTH)); 
        zscale = -ZMIN/(zz-ZMIN);	/* find perspective shrink factor */
        Dot(cenx+x[i]*zscale,ceny+y[i]*zscale); 
		}
    }
}

move_stars()
/* Move all the stars towards viewer */
{
int i;
double dist = ZDEPTH/total_frames;

for (i=0; i<scount; ++i)
    {
    if ((z[i] = z[i]-dist) < ZMIN)
        z[i] += ZDEPTH;
    }
}


init()
/* Do one time initializations */
{
GetSize(&sw,&sh);		/* find out screen size */
cenx = sw/2;			/* and calculate screen center */
ceny = sh/2;
if (!init_stars(scount))
	return(FALSE);			/* set up the arrays holding the stars */
init_colors();			/* set up ramped color map */
return(TRUE);
}



main()
{
int i;

if (!Qnumber(&total_frames, 1, 200, "Number of frames for star-field"))
	return;
if (total_frames < 1)
	return;
if (SetFrameCount(1) < 0)
	return;
Clear();
if (!Qnumber(&scount, 10, 1000, "Number of stars in field"))
	return;
if (scount < 1)
	return;
if (!init())
	return;
if (SetFrameCount(total_frames) < 0)
	return;
for (i=0;i<total_frames;++i)
    {
    see_stars();
    move_stars();
    NextFrame();
    }
}

