
/*
 * GearTrain Simulator * Version:  1.00
 *
 * Copyright (C) 1999  Shobhan Kumar Dutta  All Rights Reserved.
 * <skdutta@del3.vsnl.net.in>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * SHOBHAN KUMAR DUTTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
 * OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */


#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <GL/glut.h>
#include <string.h>
#include <stdio.h>

#ifndef min
#define min(x, y) ( x < y ? x : y )
#endif

#ifndef M_PI
#define M_PI 3.14159265
#endif	/*  */
typedef GLfloat TDA[4];

TDA background;


struct AXLE
  {
    char name[20];
      GLint id;
      GLfloat radius;
      GLint axis;
      TDA color;
      TDA position;
      GLfloat length;
      GLint motored;
      GLfloat angular_velocity;
      GLint direction;
  };


struct GEAR
  {
    char name[20];
    char type[7];
      GLint face;
      GLint id;
      GLfloat radius;
      GLfloat width;
      GLint teeth;
      GLfloat tooth_depth;
      GLfloat angle;
      GLfloat angular_velocity;
      TDA color;
      GLint relative_position;
      TDA position;
    char axle_name[20];
      GLint axis;
      GLint direction;
      GLint motored;
  };


struct BELT
  {
    char name[20];
      GLint id;
    char gear1_name[20];
    char gear2_name[20];
  };


FILE * mainfile;
struct GEAR g[10];
struct AXLE a[10];
struct BELT b[10];
int number_of_gears;
int number_of_axles;
int number_of_belts;


char Buf1[256], Buf2[256], Buf3[256], Buf4[256], Buf5[256];

static GLint T0 = 0;
static GLint Frames = 0;


#ifndef _WIN32
static void
strset (char buf[], char ch)
{
    int i;
    for (i = 0; i < strlen (buf); i++)
      buf[i] = ch;
}
#endif


static void
Clear_Buffers ()
{
    strset (Buf1, 0);
    strset (Buf2, 0);
    strset (Buf3, 0);
    strset (Buf4, 0);
    strset (Buf5, 0);
}


static void
LoadTriplet (TDA A)
{
    int result;
    Clear_Buffers ();
    result = fscanf (mainfile, "%s %s %s %s", Buf1, Buf2, Buf3, Buf4);
    assert(result != EOF);
    A[0] = atof (Buf2);
    A[1] = atof (Buf3);
    A[2] = atof (Buf4);
}


static void
LoadReal (float *a)
{
    int result;
    Clear_Buffers ();
    result = fscanf (mainfile, "%s %s", Buf1, Buf2);
    assert(result != EOF);
    *a = atof (Buf2);
}


static void
LoadInteger (int *a)
{
    int result;
    Clear_Buffers ();
    result = fscanf (mainfile, "%s %s", Buf1, Buf2);
    assert(result != EOF);
    *a = atoi (Buf2);
}


static void
LoadText (char *a)
{
    int result;
    Clear_Buffers ();
    result = fscanf (mainfile, "%s %s", Buf1, Buf2);
    assert(result != EOF);
    strcpy (a, Buf2);
}


static void
getdata (char filename[])
{
    int gear_count = 0, axle_count = 0, belt_count = 0, i;

    mainfile = fopen (filename, "r");
    if (!mainfile) {
       printf("Error: couldn't open %s\n", filename);
       exit(-1);
    }

    do
    {
	int result;
	Clear_Buffers ();
	result = fscanf (mainfile, "%s", Buf1);
	(void) result;
	if (ferror (mainfile))
	{
	    printf ("\nError opening file !\n");
	    exit (1);
	}

	if (!(strcmp (Buf1, "BACKGROUND")))
	  LoadTriplet (background);

	if (!(strcmp (Buf1, "ANAME")))
	{
	    LoadText (a[axle_count].name);
	    axle_count++;
	}

	if (!(strcmp (Buf1, "ARADIUS")))
	  LoadReal (&a[axle_count - 1].radius);

	if (!(strcmp (Buf1, "AAXIS")))
	  LoadInteger (&a[axle_count - 1].axis);

	if (!(strcmp (Buf1, "ACOLOR")))
	  LoadTriplet (a[axle_count - 1].color);

	if (!(strcmp (Buf1, "APOSITION")))
	  LoadTriplet (a[axle_count - 1].position);

	if (!(strcmp (Buf1, "ALENGTH")))
	  LoadReal (&a[axle_count - 1].length);

	if (!(strcmp (Buf1, "AMOTORED")))
	  LoadInteger (&a[axle_count - 1].motored);

	if (!(strcmp (Buf1, "AANGULARVELOCITY")))
	  LoadReal (&a[axle_count - 1].angular_velocity);

	if (!(strcmp (Buf1, "ADIRECTION")))
	  LoadInteger (&a[axle_count - 1].direction);

	if (!(strcmp (Buf1, "GNAME")))
	{
	    LoadText (g[gear_count].name);
	    gear_count++;
	}

	if (!(strcmp (Buf1, "GTYPE")))
	  LoadText (g[gear_count - 1].type);

	if (!(strcmp (Buf1, "GFACE")))
	  LoadInteger (&g[gear_count - 1].face);

	if (!(strcmp (Buf1, "GRADIUS")))
	  LoadReal (&g[gear_count - 1].radius);

	if (!(strcmp (Buf1, "GWIDTH")))
	  LoadReal (&g[gear_count - 1].width);

	if (!(strcmp (Buf1, "GTEETH")))
	  LoadInteger (&g[gear_count - 1].teeth);

	if (!(strcmp (Buf1, "GTOOTHDEPTH")))
	  LoadReal (&g[gear_count - 1].tooth_depth);

	if (!(strcmp (Buf1, "GCOLOR")))
	  LoadTriplet (g[gear_count - 1].color);

	if (!(strcmp (Buf1, "GAXLE")))
	  LoadText (g[gear_count - 1].axle_name);

	if (!(strcmp (Buf1, "GPOSITION")))
	  LoadInteger (&g[gear_count - 1].relative_position);

	if (!(strcmp (Buf1, "BELTNAME")))
	{
	    LoadText (b[belt_count].name);
	    belt_count++;
	}

	if (!(strcmp (Buf1, "GEAR1NAME")))
	  LoadText (b[belt_count - 1].gear1_name);

	if (!(strcmp (Buf1, "GEAR2NAME")))
	  LoadText (b[belt_count - 1].gear2_name);
    }

    while (Buf1[0] != 0);

    for (i = 0; i < number_of_gears; i++)
    {
	g[i].axis = -1;
	g[i].direction = 0;
	g[i].angular_velocity = 0.0;
    }

    number_of_gears = gear_count;
    number_of_axles = axle_count;
    number_of_belts = belt_count;
    fclose (mainfile);
}


static void
axle (GLint j, GLfloat radius, GLfloat length)
{
    GLfloat angle, rad, incr = 10.0 * M_PI / 180.0;

    /* draw main cylinder */
    glBegin (GL_QUADS);
    for (angle = 0.0; angle < 360.0; angle += 5.0)
    {
	rad = angle * M_PI / 180.0;
	glNormal3f (cos (rad), sin (rad), 0.0);
	glVertex3f (radius * cos (rad), radius * sin (rad), length / 2);
	glVertex3f (radius * cos (rad), radius * sin (rad), -length / 2);
	glVertex3f (radius * cos (rad + incr), radius * sin (rad + incr), -length / 2);
	glVertex3f (radius * cos (rad + incr), radius * sin (rad + incr), length / 2);
    }
    glEnd ();

    /* draw front face */
    glNormal3f (0.0, 0.0, 1.0);
    glBegin (GL_TRIANGLES);
    for (angle = 0.0; angle < 360.0; angle += 5.0)
    {
	rad = angle * M_PI / 180.0;
	glVertex3f (0.0, 0.0, length / 2);
      	glVertex3f (radius * cos (rad), radius * sin (rad), length / 2);
	glVertex3f (radius * cos (rad + incr), radius * sin (rad + incr), length / 2);
	glVertex3f (0.0, 0.0, length / 2);
    }
    glEnd ();

    /* draw back face */
    glNormal3f (0.0, 0.0, -1.0);
    glBegin (GL_TRIANGLES);
    for (angle = 0.0; angle <= 360.0; angle += 5.0)
    {
	rad = angle * M_PI / 180.0;
	glVertex3f (0.0, 0.0, -length / 2);
	glVertex3f (radius * cos (rad), radius * sin (rad), -length / 2);
	glVertex3f (radius * cos (rad + incr), radius * sin (rad + incr), -length / 2);
	glVertex3f (0.0, 0.0, -length / 2);
    }
    glEnd ();
}



static void
gear (GLint j, char type[], GLfloat radius, GLfloat width,
      GLint teeth, GLfloat tooth_depth)
{
    GLint i;
    GLfloat r1, r2;
    GLfloat angle, da;
    GLfloat u, v, len, fraction = 0.5;
    GLfloat n = 1.0;

    r1 = radius - tooth_depth;
    r2 = radius;

    da = 2.0 * M_PI / teeth / 4.0;
    if (!g[j].face)
    {
	fraction = -0.5;
	n = -1.0;
    }
    if (!(strcmp (type, "NORMAL")))
    {
	fraction = 0.5;
	n = 1.0;
    }

    /* draw front face */
    if (!(strcmp (type, "NORMAL")))
    {
	glNormal3f (0.0, 0.0, 1.0 * n);
	glBegin (GL_QUAD_STRIP);
	for (i = 0; i <= teeth; i++)
	{
	    angle = i * 2.0 * M_PI / teeth;
	    glVertex3f (0.0, 0.0, width * fraction);
	    glVertex3f (r1 * cos (angle), r1 * sin (angle), width * fraction);
	    glVertex3f (0.0, 0.0, width * fraction);
	    glVertex3f (r1 * cos (angle + 3 * da), r1 * sin (angle + 3 * da), width * fraction);
	}
	glEnd ();
    }
    else
    {
	glNormal3f (0.0, 0.0, 1.0 * n);
	glBegin (GL_QUAD_STRIP);
	for (i = 0; i <= teeth; i++)
	{
	    angle = i * 2.0 * M_PI / teeth;
	    glVertex3f (0.0, 0.0, width * fraction);
	    glVertex3f ((r2 - width) * cos (angle), (r2 - width) * sin (angle), width * fraction);
	    glVertex3f (0.0, 0.0, width * fraction);
	    glVertex3f ((r2 - width) * cos (angle + 3 * da), (r2 - width) * sin (angle + 3 * da), width * fraction);
	}
	glEnd ();
    }

    /* draw front sides of teeth */
    if (!(strcmp (type, "NORMAL")))
    {
	glNormal3f (0.0, 0.0, 1.0 * n);
	glBegin (GL_QUADS);
	da = 2.0 * M_PI / teeth / 4.0;
	for (i = 0; i < teeth; i++)
	{
	    angle = i * 2.0 * M_PI / teeth;
	    glVertex3f (r1 * cos (angle), r1 * sin (angle), width * fraction);
	    glVertex3f (r2 * cos (angle + da), r2 * sin (angle + da), width * fraction);
	    glVertex3f (r2 * cos (angle + 2 * da), r2 * sin (angle + 2 * da), width * fraction);
	    glVertex3f (r1 * cos (angle + 3 * da), r1 * sin (angle + 3 * da), width * fraction);
	}
	glEnd ();
    }

    glNormal3f (0.0, 0.0, -1.0 * n);

    /* draw back face */
    glBegin (GL_QUAD_STRIP);
    for (i = 0; i <= teeth; i++)
    {
	angle = i * 2.0 * M_PI / teeth;
	glVertex3f (r1 * cos (angle), r1 * sin (angle), -width * fraction);
	glVertex3f (0.0, 0.0, -width * fraction);
	glVertex3f (r1 * cos (angle + 3 * da), r1 * sin (angle + 3 * da), -width * fraction);
	glVertex3f (0.0, 0.0, -width * fraction);
    }
    glEnd ();

    /* draw back sides of teeth */
    glNormal3f (0.0, 0.0, -1.0 * n);
    glBegin (GL_QUADS);
    da = 2.0 * M_PI / teeth / 4.0;
    for (i = 0; i < teeth; i++)
    {
	angle = i * 2.0 * M_PI / teeth;
	glVertex3f (r1 * cos (angle + 3 * da), r1 * sin (angle + 3 * da), -width * fraction);
	glVertex3f (r2 * cos (angle + 2 * da), r2 * sin (angle + 2 * da), -width * fraction);
	glVertex3f (r2 * cos (angle + da), r2 * sin (angle + da), -width * fraction);
	glVertex3f (r1 * cos (angle), r1 * sin (angle), -width * fraction);
    }
    glEnd ();


    /* draw outward faces of teeth */
    if (!(strcmp (type, "NORMAL")))
    {
	glBegin (GL_QUAD_STRIP);
	for (i = 0; i < teeth; i++)
	{
	    angle = i * 2.0 * M_PI / teeth;

	    glVertex3f (r1 * cos (angle), r1 * sin (angle), width * fraction);
	    glVertex3f (r1 * cos (angle), r1 * sin (angle), -width * fraction);
	    u = r2 * cos (angle + da) - r1 * cos (angle);
	    v = r2 * sin (angle + da) - r1 * sin (angle);
	    len = sqrt (u * u + v * v);
	    u /= len;
	    v /= len;
	    glNormal3f (v, -u, 0.0);
	    glVertex3f (r2 * cos (angle + da), r2 * sin (angle + da), width * fraction);
	    glVertex3f (r2 * cos (angle + da), r2 * sin (angle + da), -width * fraction);
	    glNormal3f (cos (angle), sin (angle), 0.0);
	    glVertex3f (r2 * cos (angle + 2 * da), r2 * sin (angle + 2 * da), width * fraction);
	    glVertex3f (r2 * cos (angle + 2 * da), r2 * sin (angle + 2 * da), -width * fraction);
	    u = r1 * cos (angle + 3 * da) - r2 * cos (angle + 2 * da);
	    v = r1 * sin (angle + 3 * da) - r2 * sin (angle + 2 * da);
	    glNormal3f (v, -u, 0.0);
	    glVertex3f (r1 * cos (angle + 3 * da), r1 * sin (angle + 3 * da), width * fraction);
	    glVertex3f (r1 * cos (angle + 3 * da), r1 * sin (angle + 3 * da), -width * fraction);
	    glNormal3f (cos (angle), sin (angle), 0.0);
	}
    }
    else
    {
	glBegin (GL_QUAD_STRIP);
	for (i = 0; i < teeth; i++)
	{
	    angle = i * 2.0 * M_PI / teeth;
	    glVertex3f (r1 * cos (angle), r1 * sin (angle), width * fraction);
	    glVertex3f (r1 * cos (angle), r1 * sin (angle), -width * fraction);
	    u = r2 * cos (angle + da) - r1 * cos (angle);
	    v = r2 * sin (angle + da) - r1 * sin (angle);
	    len = sqrt (u * u + v * v);
	    u /= len;
	    v /= len;
	    glNormal3f (v, -u, 0.0);
	    glVertex3f ((r2 - width) * cos (angle + da), (r2 - width) * sin (angle + da), width * fraction);
	    glVertex3f (r2 * cos (angle + da), r2 * sin (angle + da), -width * fraction);
	    glNormal3f (cos (angle), sin (angle), n);
	    glVertex3f ((r2 - width) * cos (angle + 2 * da), (r2 - width) * sin (angle + 2 * da), width * fraction);
	    glVertex3f (r2 * cos (angle + 2 * da), r2 * sin (angle + 2 * da), -width * fraction);
	    u = r1 * cos (angle + 3 * da) - r2 * cos (angle + 2 * da);
	    v = r1 * sin (angle + 3 * da) - r2 * sin (angle + 2 * da);
	    glNormal3f (v, -u, 0.0);
	    glVertex3f (r1 * cos (angle + 3 * da), r1 * sin (angle + 3 * da), width * fraction);
	    glVertex3f (r1 * cos (angle + 3 * da), r1 * sin (angle + 3 * da), -width * fraction);
	    glNormal3f (cos (angle), sin (angle), n);
	}
    }

    glVertex3f (r1 * cos (0), r1 * sin (0), width * fraction);
    glVertex3f (r1 * cos (0), r1 * sin (0), -width * fraction);
    glEnd ();
}


static void
belt (struct GEAR g1, struct GEAR g2)
{
    GLfloat D, alpha, phi, angle, incr, width;
    GLint indexes[3] =
    {
       0, 0, 0
    };

    GLfloat col[3] =
    {
       0.0, 0.0, 0.0
    };

    width = min (g1.width, g2.width);
    D = sqrt (pow (g1.position[0] - g2.position[0], 2) + pow (g1.position[1] - g2.position[1], 2) + pow (g1.position[2] - g2.position[2], 2));
    alpha = acos ((g2.position[0] - g1.position[0]) / D);
    phi = acos ((g1.radius - g2.radius) / D);
    glBegin (GL_QUADS);
    glColor3fv (col);
    glMaterialiv (GL_FRONT, GL_COLOR_INDEXES, indexes);
    incr = 1.2 * 360.0 / g1.teeth * M_PI / 180.00;
    for (angle = alpha + phi; angle <= 2 * M_PI - phi + alpha; angle += 360.0 / g1.teeth * M_PI / 180.00)
    {
	glNormal3f (cos (angle), sin (angle), 0.0);
	glVertex3f (g1.radius * cos (angle), g1.radius * sin (angle), width * 0.5);
	glVertex3f (g1.radius * cos (angle), g1.radius * sin (angle), -width * 0.5);
	glVertex3f (g1.radius * cos (angle + incr), g1.radius * sin (angle + incr), -width * 0.5);
	glVertex3f (g1.radius * cos (angle + incr), g1.radius * sin (angle + incr), width * 0.5);
    }
    glEnd ();
    glBegin (GL_QUADS);
    glColor3fv (col);
    glMaterialiv (GL_FRONT, GL_COLOR_INDEXES, indexes);
    incr = 1.2 * 360.0 / g2.teeth * M_PI / 180.00;
    for (angle = -phi + alpha; angle <= phi + alpha; angle += 360.0 / g1.teeth * M_PI / 180.0)
    {
	glNormal3f (cos (angle), sin (angle), 0.0);
	glVertex3f (g2.radius * cos (angle) + g2.position[0] - g1.position[0], g2.radius * sin (angle) + g2.position[1] - g1.position[1], width * 0.5);
	glVertex3f (g2.radius * cos (angle) + g2.position[0] - g1.position[0], g2.radius * sin (angle) + g2.position[1] - g1.position[1], width * -0.5);
	glVertex3f (g2.radius * cos (angle + incr) + g2.position[0] - g1.position[0], g2.radius * sin (angle + incr) + g2.position[1] - g1.position[1], width * -0.5);
	glVertex3f (g2.radius * cos (angle + incr) + g2.position[0] - g1.position[0], g2.radius * sin (angle + incr) + g2.position[1] - g1.position[1], width * 0.5);
    }
    glEnd ();

    glBegin (GL_QUADS);
    glColor3fv (col);
    glMaterialiv (GL_FRONT, GL_COLOR_INDEXES, indexes);
    glVertex3f (g1.radius * cos (alpha + phi), g1.radius * sin (alpha + phi), width * 0.5);
    glVertex3f (g1.radius * cos (alpha + phi), g1.radius * sin (alpha + phi), width * -0.5);
    glVertex3f (g2.radius * cos (alpha + phi) + g2.position[0] - g1.position[0], g2.radius * sin (alpha + phi) + g2.position[1] - g1.position[1], width * -0.5);
    glVertex3f (g2.radius * cos (alpha + phi) + g2.position[0] - g1.position[0], g2.radius * sin (alpha + phi) + g2.position[1] - g1.position[1], width * 0.5);
    glVertex3f (g1.radius * cos (alpha - phi), g1.radius * sin (alpha - phi), width * 0.5);
    glVertex3f (g1.radius * cos (alpha - phi), g1.radius * sin (alpha - phi), width * -0.5);
    glVertex3f (g2.radius * cos (alpha - phi) + g2.position[0] - g1.position[0], g2.radius * sin (alpha - phi) + g2.position[1] - g1.position[1], width * -0.5);
    glVertex3f (g2.radius * cos (alpha - phi) + g2.position[0] - g1.position[0], g2.radius * sin (alpha - phi) + g2.position[1] - g1.position[1], width * 0.5);
    glEnd ();
}


static int
axle_find (char axle_name[])
{
    int i;

    for (i = 0; i < number_of_axles; i++)
    {
	if (!(strcmp (axle_name, a[i].name)))
           break;
    }
    return i;
}


static int
gear_find (char gear_name[])
{
    int i;

    for (i = 0; i < number_of_gears; i++)
    {
	if (!(strcmp (gear_name, g[i].name)))
           break;
    }
    return i;
}


static void
process ()
{
    GLfloat x, y, z, D, dist;
    GLint axle_index, i, j, g1, g2, k;
    char error[80];

    for (i = 0; i < number_of_gears; i++)
    {
	x = 0.0;
	y = 0.0;
	z = 0.0;
	axle_index = axle_find (g[i].axle_name);
	g[i].axis = a[axle_index].axis;
	g[i].motored = a[axle_index].motored;
	if (a[axle_index].motored)
	{
	    g[i].direction = a[axle_index].direction;
	    g[i].angular_velocity = a[axle_index].angular_velocity;
	}
	if (g[i].axis == 0)
           x = 1.0;
        else if (g[i].axis == 1)
           y = 1.0;
        else
           z = 1.0;

	g[i].position[0] = a[axle_index].position[0] + x * g[i].relative_position;
	g[i].position[1] = a[axle_index].position[1] + y * g[i].relative_position;
	g[i].position[2] = a[axle_index].position[2] + z * g[i].relative_position;
    }

    for (k = 0; k < number_of_axles; k++)
    {
	for (i = 0; i < number_of_gears - 1; i++)
	{
	    for (j = 0; j < number_of_gears; j++)
	    {
		if (!(strcmp (g[i].type, g[j].type)) && (!(strcmp (g[i].type, "NORMAL"))) && ((strcmp (g[i].axle_name, g[j].axle_name) != 0)) && (g[i].axis == g[j].axis))
		{
		    D = sqrt (pow (g[i].position[0] - g[j].position[0], 2) + pow (g[i].position[1] - g[j].position[1], 2) + pow (g[i].position[2] - g[j].position[2], 2));
		    if (D < 1.1 * (g[i].radius - g[i].tooth_depth + g[j].radius - g[j].tooth_depth))
		    {
			printf (error, "Gear %s and %s are too close to each other.", g[i].name, g[j].name);

                        /*MessageBox(NULL,error,windowName,MB_ICONEXCLAMATION|MB_OK);*/
			exit (1);
		    }

		    if (g[i].axis == 0)
		    {
			dist = g[i].position[0] - g[j].position[0];
		    }
		    else if (g[i].axis == 1)
		    {
			dist = g[i].position[1] - g[j].position[1];
		    }
		    else
                       dist = g[i].position[2] - g[j].position[2];

		    dist = fabs (dist);

		    if (dist < (g[i].width / 2 + g[j].width / 2))
		    {
			if ((g[i].motored) && (!(g[j].motored)) && (D < 0.95 * (g[i].radius + g[j].radius)))
			{
			    axle_index = axle_find (g[j].axle_name);
			    if ((a[axle_index].direction != 0) && (g[j].angular_velocity != g[i].angular_velocity * g[i].teeth / g[j].teeth * g[i].radius / g[j].radius))
			    {
				printf (error, "Error in tooth linkage of gears %s and %s.", g[i].name, g[j].name);
                                /*MessageBox(NULL,error,windowName,MB_ICONEXCLAMATION|MB_OK);*/
				exit (1);
			    }

			    g[j].motored = (a[axle_index].motored = 1);
			    g[j].direction = (a[axle_index].direction = -g[i].direction);
			    a[axle_index].angular_velocity = g[i].angular_velocity * g[i].teeth / g[j].teeth;
			    g[j].angular_velocity = (a[axle_index].angular_velocity *= g[i].radius / g[j].radius);
			}

			if ((!(g[i].motored)) && (g[j].motored) && (D < 0.95 * (g[i].radius + g[j].radius)))
			{
			    axle_index = axle_find (g[i].axle_name);
			    if ((a[axle_index].direction != 0) && (g[i].angular_velocity != g[j].angular_velocity * g[j].teeth / g[i].teeth * g[j].radius / g[i].radius))
			    {
				printf (error, "Error in tooth linkage of gears %s and %s.", g[i].name, g[j].name);
                                /*MessageBox(NULL,error,windowName,MB_ICONEXCLAMATION|MB_OK);*/
				exit (1);
			    }

			    g[i].motored = (a[axle_index].motored = 1);
			    g[i].direction = (a[axle_index].direction = -g[j].direction);
			    a[axle_index].angular_velocity = g[j].angular_velocity * g[j].teeth / g[i].teeth;
			    g[i].angular_velocity = (a[axle_index].angular_velocity *= g[j].radius / g[i].radius);

			}
		    }
		}

		if (!(strcmp (g[i].type, g[j].type)) && (!(strcmp (g[i].type, "BEVEL"))) && ((strcmp (g[i].axle_name, g[j].axle_name) != 0)) && (g[i].axis != g[j].axis))
		{
		    D = sqrt (pow (g[i].position[0] - g[j].position[0], 2) + pow (g[i].position[1] - g[j].position[1], 2) + pow (g[i].position[2] - g[j].position[2], 2));
		    if ((g[i].motored) && (!(g[j].motored)) && (D < 0.95 * sqrt (g[i].radius * g[i].radius + g[j].radius * g[j].radius)))
		    {
			axle_index = axle_find (g[j].axle_name);
			if ((a[axle_index].direction != 0) && (g[j].angular_velocity != g[i].angular_velocity * g[i].teeth / g[j].teeth * g[i].radius / g[j].radius))
			{
			    printf (error, "Error in tooth linkage of gears %s and %s.", g[i].name, g[j].name);
                            /*MessageBox(NULL,error,windowName,MB_ICONEXCLAMATION|MB_OK);*/
			    exit (1);
			}
			g[j].motored = (a[axle_index].motored = 1);
			g[j].direction = (a[axle_index].direction = -g[i].direction);
			a[axle_index].angular_velocity = g[i].angular_velocity * g[i].teeth / g[j].teeth;
			g[j].angular_velocity = (a[axle_index].angular_velocity *= g[i].radius / g[j].radius);
		    }


		    if ((!(g[i].motored)) && (g[j].motored) && (D < 0.95 * sqrt (g[i].radius * g[i].radius + g[j].radius * g[j].radius)))
		    {
			axle_index = axle_find (g[i].axle_name);
			if ((a[axle_index].direction != 0) && (g[i].angular_velocity != g[j].angular_velocity * g[j].teeth / g[i].teeth * g[j].radius / g[i].radius))
			{
			    printf (error, "Error in tooth linkage of gears %s and %s.", g[i].name, g[j].name);
                            /*MessageBox(NULL,error,windowName,MB_ICONEXCLAMATION|MB_OK);*/
			    exit (1);
			}
			g[i].motored = (a[axle_index].motored = 1);
			g[i].direction = (a[axle_index].direction = -g[j].direction);
			a[axle_index].angular_velocity = g[j].angular_velocity * g[j].teeth / g[i].teeth;
			g[i].angular_velocity = (a[axle_index].angular_velocity *= g[j].radius / g[i].radius);
		    }
		}
	    }
	}

	for (i = 0; i < number_of_gears; i++)
	{
	    axle_index = axle_find (g[i].axle_name);
	    g[i].motored = a[axle_index].motored;
	    if (a[axle_index].motored)
	    {
		g[i].direction = a[axle_index].direction;
		g[i].angular_velocity = a[axle_index].angular_velocity;
	    }
	}

	for (i = 0; i < number_of_belts; i++)
	{
	    g1 = gear_find (b[i].gear1_name);
	    g2 = gear_find (b[i].gear2_name);
	    D = sqrt (pow (g[g1].position[0] - g[g2].position[0], 2) + pow (g[g1].position[1] - g[g2].position[1], 2) + pow (g[g1].position[2] - g[g2].position[2], 2));
	    if (!((g[g1].axis == g[g2].axis) && (!strcmp (g[g1].type, g[g2].type)) && (!strcmp (g[g1].type, "NORMAL"))))
	    {
		printf (error, "Belt %s invalid.", b[i].name);
                /*MessageBox(NULL,error,windowName,MB_ICONEXCLAMATION|MB_OK);*/
		exit (1);
	    }

	    if ((g[g1].axis == g[g2].axis) && (!strcmp (g[g1].type, g[g2].type)) && (!strcmp (g[g1].type, "NORMAL")))
	    {
	      /*
	         if((g[g1].motored)&&(g[g2].motored))
	         if(g[g2].angular_velocity!=(g[g1].angular_velocity*g[g1].radius/g[g2].radius))
	         {
	         printf(error,"Error in belt linkage of gears %s and %s".,g[g1].name,g[g2].name);
	         MessageBox(NULL,error,windowName,MB_ICONEXCLAMATION|MB_OK);
	         exit(1);
	         }
              */
               if (g[g1].axis == 0)
                  {
		    dist = g[g1].position[0] - g[g2].position[0];
		}
		else if (g[i].axis == 1)
		{
		    dist = g[g1].position[1] - g[g2].position[1];
		}
		else
                   dist = g[g1].position[2] - g[g2].position[2];

		dist = fabs (dist);

		if (dist > (g[g1].width / 2 + g[g2].width / 2))
		{
		    printf (error, "Belt %s invalid.", b[i].name);
                    /*MessageBox(NULL,error,windowName,MB_ICONEXCLAMATION|MB_OK);*/
		    exit (1);
		}

		if (dist < (g[g1].width / 2 + g[g2].width / 2))
		{
		    if (D < g[g1].radius + g[g2].radius)
		    {
			printf (error, "Gears %s and %s too close to be linked with belts", g[g1].name, g[g2].name);
                        /*MessageBox(NULL,error,windowName,MB_ICONEXCLAMATION|MB_OK);*/
			exit (1);
		    }

		    if ((g[g1].motored) && (!(g[g2].motored)))
		    {
			axle_index = axle_find (g[g2].axle_name);
			g[g2].motored = (a[axle_index].motored = 1);
			g[g2].direction = (a[axle_index].direction = g[g1].direction);
			g[g2].angular_velocity = (a[axle_index].angular_velocity = g[g1].angular_velocity * g[g1].radius / g[g2].radius);
		    }

		    if ((!(g[g1].motored)) && (g[g2].motored))
		    {
			axle_index = axle_find (g[g1].axle_name);
			g[g1].motored = (a[axle_index].motored = 1);
			g[g1].direction = (a[axle_index].direction = g[g2].direction);
			g[g1].angular_velocity = (a[axle_index].angular_velocity = g[g2].angular_velocity * g[g2].radius / g[g1].radius);
		    }
		}
	    }
	}

	for (i = 0; i < number_of_gears; i++)
	{
	    axle_index = axle_find (g[i].axle_name);
	    g[i].motored = a[axle_index].motored;
	    if (a[axle_index].motored)
	    {
		g[i].direction = a[axle_index].direction;
		g[i].angular_velocity = a[axle_index].angular_velocity;
	    }
	}
    }
}



GLfloat view_rotx = 20.0, view_roty = 30.0, view_rotz = 10.0;


static void
draw (void)
{
    int i;
    GLfloat x, y, z;
    int index;

    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glPushMatrix ();
    glRotatef (view_rotx, 1.0, 0.0, 0.0);
    glRotatef (view_roty, 0.0, 1.0, 0.0);
    glRotatef (view_rotz, 0.0, 0.0, 1.0);

    for (i = 0; i < number_of_gears; i++)
    {
	x = 0.0;
	y = 0.0;
	z = 0.0;
	glPushMatrix ();
/*glTranslatef( -3.0, -2.0, 0.0 );*/
	glTranslatef (g[i].position[0], g[i].position[1], g[i].position[2]);
	if (g[i].axis == 0)
           y = 1.0;
        else if (g[i].axis == 1)
           x = 1.0;
        else
           z = 1.0;

	if (z != 1.0)
           glRotatef (90.0, x, y, z);

	glRotatef (g[i].direction * g[i].angle, 0.0, 0.0, 1.0);
	glCallList (g[i].id);
	glPopMatrix ();
    }

    for (i = 0; i < number_of_axles; i++)
    {
	x = 0.0;
	y = 0.0;
	z = 0.0;
	glPushMatrix ();
	glTranslatef (a[i].position[0], a[i].position[1], a[i].position[2]);
	if (a[i].axis == 0)
           y = 1.0;
        else if (a[i].axis == 1)
           x = 1.0;
        else
           z = 1.0;

	if (z != 1.0)
           glRotatef (90.0, x, y, z);

	glCallList (a[i].id);
	glPopMatrix ();
    }

    for (i = 0; i < number_of_belts; i++)
    {
	x = 0.0;
	y = 0.0;
	z = 0.0;
	glPushMatrix ();
	index = gear_find (b[i].gear1_name);
	glTranslatef (g[index].position[0], g[index].position[1], g[index].position[2]);
	if (g[index].axis == 0)
           y = 1.0;
        else if (g[index].axis == 1)
           x = 1.0;
        else
           z = 1.0;

	if (z != 1.0)
           glRotatef (90.0, x, y, z);

	glCallList (b[i].id);
	glPopMatrix ();
    }

    glPopMatrix ();
    glutSwapBuffers ();

    {
	GLint t = glutGet(GLUT_ELAPSED_TIME);
	Frames++;
	if (t - T0 >= 5000) {
	    GLfloat seconds = (t - T0) / 1000.0;
	    GLfloat fps = Frames / seconds;
	    printf("%d frames in %g seconds = %g FPS\n", Frames, seconds, fps);
            fflush(stdout);
	    T0 = t;
	    Frames = 0;
	}
    }
}


static void
idle (void)
{
    int i;
    static double t0 = -1.;
    double dt, t = glutGet(GLUT_ELAPSED_TIME) / 1000.0;
    if (t0 < 0.0)
       t0 = t;
    dt = t - t0;
    t0 = t;
    for (i = 0; i < number_of_gears; i++)
      g[i].angle += g[i].angular_velocity * dt;
    glutPostRedisplay();
}




/* change view angle, exit upon ESC */
static void
key (unsigned char k, int x, int y)
{
    switch (k)
    {
    case 'x':
	view_rotx += 5.0;
        break;
    case 'X':
	view_rotx -= 5.0;
        break;
    case 'y':
	view_roty += 5.0;
        break;
    case 'Y':
	view_roty -= 5.0;
        break;
    case 'z':
	view_rotz += 5.0;
        break;
    case 'Z':
	view_rotz -= 5.0;
        break;
    case 0x1B:
	exit(0);
    }
}




/* new window size or exposure */
static void
reshape (int width, int height)
{
    glViewport (0, 0, (GLint) width, (GLint) height);
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    if (width > height)
    {
	GLfloat w = (GLfloat) width / (GLfloat) height;
	glFrustum (-w, w, -1.0, 1.0, 5.0, 60.0);
    }
    else
    {
	GLfloat h = (GLfloat) height / (GLfloat) width;
	glFrustum (-1.0, 1.0, -h, h, 5.0, 60.0);
    }

    glMatrixMode (GL_MODELVIEW);
    glLoadIdentity ();
    glTranslatef (0.0, 0.0, -40.0);
    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}



static void
init (void)
{
    GLfloat matShine = 20.00F;
    GLfloat light0Pos[4] =
    {
       0.70F, 0.70F, 1.25F, 0.50F
    };
    int i;

    glClearColor (background[0], background[1], background[2], 1.0F);
    glClearIndex ((GLfloat) 0.0);

    glMaterialf (GL_FRONT_AND_BACK, GL_SHININESS, matShine);
    glLightfv (GL_LIGHT0, GL_POSITION, light0Pos);
    glEnable (GL_LIGHT0);

    glEnable (GL_LIGHTING);
    glEnable (GL_DEPTH_TEST);
    for (i = 0; i < number_of_gears; i++)
      g[i].angle = 0.0;

    for (i = 0; i < number_of_gears; i++)
    {
	g[i].id = glGenLists (1);
	glNewList (g[i].id, GL_COMPILE);
	glColor3fv (g[i].color);
	glMaterialfv (GL_FRONT, GL_SPECULAR, g[i].color);
	gear (i, g[i].type, g[i].radius, g[i].width, g[i].teeth, g[i].tooth_depth);
	glEndList ();
    }

    for (i = 0; i < number_of_axles; i++)
    {
	a[i].id = glGenLists (1);
	glNewList (a[i].id, GL_COMPILE);
	glColor3fv (a[i].color);
	glMaterialfv (GL_FRONT, GL_SPECULAR, a[i].color);
	axle (i, a[i].radius, a[i].length);
	glEndList ();
    }

    for (i = 0; i < number_of_belts; i++)
    {
	b[i].id = glGenLists (1);
	glNewList (b[i].id, GL_COMPILE);
	belt (g[gear_find (b[i].gear1_name)], g[gear_find (b[i].gear2_name)]);
	glEndList ();
    }

    glEnable (GL_COLOR_MATERIAL);
}



int
main (int argc, char *argv[])
{
    char *file;

    glutInitWindowSize(640,480);
    glutInit(&argc, argv); 
    glutInitDisplayMode (GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE );

    if (glutCreateWindow ("Gear Train Simulation") == GL_FALSE)
      exit (1);

    if (argc < 2)
       file = "geartrain.dat";
    else
       file = argv[1];

    getdata (file);
    process ();
    init ();

    glutDisplayFunc (draw);
    glutReshapeFunc (reshape);
    glutKeyboardFunc (key);
    glutIdleFunc (idle);
    glutMainLoop ();
    return 0;
}
