// This is a code fragment demonstrating a 3-d stereographic projection
// It is not compilable as is, you will have to incorporate it into
// your program.

/*****************************************************************************************************/
// put this in header file
#define _MAX_STARNAME	15 			// 15 characters for starname
#define SX				640			// resulting picture is 640 pixels horizontally
#define SY				480  		// resulting picture is 480 pixels vertically
#define SCX				(SX/2)  	// center of the picture X
#define SCY				(SY/2)		// center of the picture Y
// in cross eyed and wide eyed methods, there are two pictures displayed
// side by side. "Stereo pairs"
#define SCXL			(SX/4)   	// center of left picture X
#define SCXR			(SCX+SCXL)  // center of right picture X
#define CROSSEYE		0  			// stereo pairs using cross-eyed method
#define WIDEEYE			1 			// stereo pairs using wide-eyed method
#define ANAGLYPH		2       	// red-blue anaglyph image
#define NO3D			3       	// no 3-d, just a flat image
#define LEFT			0
#define RIGHT			1

// anaglyph colors
#define REDCOLOR		RGB(255,0,0)
#define BLUECOLOR		RGB(0,255,255)
#define BLACKCOLOR		RGB(0,0,0)

struct TriPoint {
	double	x;
	double 	y;
	double	z;
};

struct StarNode {
	char		starName[_MAX_STARNAME+1];
	TriPoint	loc;
};

// global variables
	StarNode			starList[100];	// make this into a linked list or other
										// dynamically allocated data structure
	TriPoint			eye[100][2]; 	// ditto.
	double				scaleFactor;	// magnification of resulting image
	short				numStars;   	// number of stars in starList
	short				viewMode; 		// cross-eyes, anaglyph, or whatever
	TriPoint			from[2];
	TriPoint			at,up;
	double				viewAngle,theta,kval;
	short				scxLeftEye[2];
	short				scxRightEye[2];


/*****************************************************************************************************/
// insert this into the initialization section

	scxLeftEye[CROSSEYE] = SCXR;
	scxRightEye[CROSSEYE] = SCXL;
	scxLeftEye[WIDEEYE] = SCXL;
	scxRightEye[WIDEEYE] = SCXR;

	scaleFactor = 75.0;
	viewMode = CROSSEYE;
	from[LEFT].x = -0.5;	// hovering above x-y plane, eye separation 1.0
	from[LEFT].y = 0.0;
	from[LEFT].z = 5.0;
	from[RIGHT].x = 0.5;
	from[RIGHT].y = 0.0;
	from[RIGHT].z = 5.0;
	at.x = 0.0;
	at.y = 0.0;
	at.z = 50.0;	// this seems to make a better perpective than 0.0
	up.x = 0.0;
	up.y = 1.0;
	up.z = 0.0;
	viewAngle = 60;
	theta = MakeRadians(viewAngle);
	dval = cos(theta / 2) / sin(theta / 2);

// you will have to create code to allow user to set the scaleFactor, viewMode,
// from, at, up, and viewAngle variables. Remember to recalculate theta
// and dval if viewAngle is changed, as is done above.

// read in star data
	double		_x,_y,_z;
	char		_starName[_MAX_STARNAME+1];
	FILE 		*datafile;
	char 		instring[258];

	numStars = -1;
	datafile = fopen("STEREO.DAT", "r");
	while (fgets(instring, 258, datafile))
	{
// insert code to load _starName, _x, _y, and _z from instring 

		numStars++;
		strcpy(starList[numStars].starName,_starName);
		starList[numStars].loc.x = _x;
		starList[numStars].loc.y = _y;
		starList[numStars].loc.z = _z;
	}
	fclose(datafile);

	CalcEye();
}
/*****************************************************************************************************/
// insert this into section that draws the screen

	short		a;

	if ((viewMode == CROSSEYE) || (viewMode == 	WIDEEYE))
	{
		// draw a vertical line separating the two stereo pairs
		MoveTo(SCX,0);
		LineTo(SCX,SY);
	}

	for (a = 0; a <= numStars; a++)
	{
		switch (viewMode)
		{
			case NO3D:		PlotStar(a);			break;
			case CROSSEYE:
			case WIDEEYE:	PlotStarCrossWide(a);	break;
			case ANAGLYPH:	PlotStarAnaglyph(a);	break;
		}
	}
/*****************************************************************************************************/
void PlotStar(short starNum)
{
	short		plotX,plotY;
	double		px,py;
	char		buffer[80];

	plotX = (starList[starNum].loc.x * scaleFactor) + SCX;
	plotY = SY - ((starList[starNum].loc.y * scaleFactor) + SCY);
	// make a 11x11 cross at the plot position
	MoveTo(plotX, plotY-5);
	LineTo(plotX, plotY+6);
	MoveTo(plotX-5, plotY);
	LineTo(plotX+6, plotY);
	// print the star name next to it
	wsprintf(buffer,"%s",starList[starNum].starName);
	TextOut(paintDCH, plotX+3, plotY-11, buffer, lstrlen(buffer));

}
/*****************************************************************************************************/
void PlotStarCrossWide(short starNum)
{
	short		plotX,plotY;
	char		buffer[80];

	wsprintf(buffer,"%s",starList[starNum].starName);

// draw left eye's image

	plotX = (eye[starNum][LEFT].x * scaleFactor) + scxLeftEye[viewMode];
	plotY = SY - ((eye[starNum][LEFT].y * scaleFactor) + SCY) ;

	if (((viewMode == CROSSEYE) && (plotX > SCX)) ||
		((viewMode == WIDEEYE) && (plotX < SCX))) {
		MoveTo(plotX, plotY-5);
		LineTo(plotX, plotY+6);
		MoveTo(plotX-5, plotY);
		LineTo(plotX+6, plotY);
		TextOut(plotX+3, plotY-11, buffer, lstrlen(buffer));
	}

// draw right eye's image

	plotX = (eye[starNum][RIGHT].x * scaleFactor) + scxRightEye[viewMode];
	plotY = SY - ((eye[starNum][RIGHT].y * scaleFactor) + SCY) ;

	if (((viewMode == CROSSEYE) && (plotX < SCX)) ||
		((viewMode == WIDEEYE) && (plotX > SCX))) {
		MoveTo(plotX, plotY-5);
		LineTo(plotX, plotY+6);
		MoveTo(plotX-5, plotY);
		LineTo(plotX+6, plotY);
		TextOut(plotX+3, plotY-11, buffer, lstrlen(buffer));
	}

}
/*****************************************************************************************************/
void PlotStarAnaglyph(short starNum)
{
	short		plotX,plotY;
	char		buffer[80];

	wsprintf(buffer,"%s",starList[starNum].starName);

	plotX = (eye[starNum][LEFT].x * scaleFactor) + SCX;
	plotY = SY - ((eye[starNum][LEFT].y * scaleFactor) + SCY) ;

// insert code to make pen draw in red

	MoveTo(paintDCH, plotX, plotY-5);
	LineTo(paintDCH, plotX, plotY+6);
	MoveTo(paintDCH, plotX-5, plotY);
	LineTo(paintDCH, plotX+6, plotY);
	TextOut(paintDCH, plotX+3, plotY-11, buffer, lstrlen(buffer));

	plotX = (eye[starNum][RIGHT].x * scaleFactor) + SCX;
	plotY = SY - ((eye[starNum][RIGHT].y * scaleFactor) + SCY) ;

// insert code to make pen draw in blue

	MoveTo(paintDCH, plotX, plotY-5);
	LineTo(paintDCH, plotX, plotY+6);
	MoveTo(paintDCH, plotX-5, plotY);
	LineTo(paintDCH, plotX+6, plotY);
	TextOut(paintDCH, plotX+3, plotY-11, buffer, lstrlen(buffer));

// insert code to make pen draw in black

}
/*****************************************************************************************************/
void Calc3D(short eyeID)
{
	short		a;
	TriPoint	aPrime,a1,a2,a3,temp,eyeC;
	double		aPrimeMag, tempMag;
	double		offX, offY, offZ;
	double		ex,ey;

	aPrime = Subtract(&at, &from[eyeID]);
	aPrimeMag = Mag(&aPrime);

	a3 = Divide(&aPrime, aPrimeMag);

	temp = Cross(&aPrime, &up);
	tempMag = Mag(&temp);

	a1 = Divide(&temp, tempMag);

	temp = Cross(&aPrime, &up);
	temp = Cross(&temp, &aPrime);
	tempMag = Mag(&temp);

	a2 = Divide(&temp, tempMag);

	offX = -a1.x * from[eyeID].x - a1.y * from[eyeID].y - a1.z * from[eyeID].z;
	offY = -a2.x * from[eyeID].x - a2.y * from[eyeID].y - a2.z * from[eyeID].z;
	offZ = -a3.x * from[eyeID].x - a3.y * from[eyeID].y - a3.z * from[eyeID].z;

	for (a = 0; a <= numStars; a++)
	{
		eyeC.x =
			(starList[a].loc.x * a1.x) +
			(starList[a].loc.y * a1.y) +
			(starList[a].loc.z * a1.z) +
			offX;
		eyeC.y =
			(starList[a].loc.x * a2.x) +
			(starList[a].loc.y * a2.y) +
			(starList[a].loc.z * a2.z) +
			offY;
		eyeC.z =
			(starList[a].loc.x * a3.x) +
			(starList[a].loc.y * a3.y) +
			(starList[a].loc.z * a3.z) +
			offZ;
		ex = eyeC.x * dval;
		if (eyeC.z != 0)	ex = ex / eyeC.z;
		ey = eyeC.y * dval;
		if (eyeC.z != 0)	ey = ey / eyeC.z;
		eye[a][eyeID].x = ex;
		eye[a][eyeID].y = ey;
	}
}
/*****************************************************************************************************/
void CalcEye()
{
	Calc3D(LEFT);
	Calc3D(RIGHT);
}
/*****************************************************************************************************/
double Mag(TriPoint *v)
{
	return sqrt((v->x * v->x) + (v->y * v->y) + (v->z * v->z));
}
/*****************************************************************************************************/
TriPoint Subtract(TriPoint *v1, TriPoint *v2)
{
	TriPoint	c;

	c.x = v1->x - v2->x;
	c.y = v1->y - v2->y;
	c.z = v1->z - v2->z;
	return c;
}
/*****************************************************************************************************/
TriPoint Cross(TriPoint *v1, TriPoint *v2)
{
	TriPoint	c;

	c.x = v1->y * v2->z - v2->y * v1->z;
	c.y = v1->z * v2->x - v2->z * v1->x;
	c.z = v1->x * v2->y - v2->x * v1->y;
	return c;
}
/*****************************************************************************************************/
TriPoint Divide(TriPoint *v, double num)
{
	TriPoint	result;

	result.x = v->x;
	result.y = v->y;
	result.z = v->z;

	if (num != 0)
	{
		result.x = v->x / num;
		result.y = v->y / num;
		result.z = v->z / num;
	}
	return result;
}
/*****************************************************************************************************/
double MakeRadians(double degrees)
{
	return degrees * 0.017453293;
}
/*****************************************************************************************************/
