#include <GL/gl.h>
#include <GL/glut.h>
#include <math.h>

#if 0
#define fcos(a)	(float)cos((float)a)
#define fsin(a) (float)sin((float)a)
#endif

float a, b, c = 0.f;
float da = .01f;
float db = .01f;
float dc = .01f;

GLUquadricObj *cone, *base, *sphere;

void redraw()
{
    float x, y, z, r;

    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); /* clear screen */

    r = 1.5f + 5.f * fabs(fcos(c / 5.f));
    x = r * fcos(a);
    z = r * fsin(a);
    y = r * fcos(b);

       
    glPushMatrix(); /* save identity matrix */
    /*
     ** last operation; position objects in viewing frustum so they
     ** appear to be at origin, and viewer is at x, y, z
     */
    gluLookAt(x, y, z, 0., 0., 0., 0.f, 1.f, 0.f);
    
    /* 
    ** draw some objects; they're defined on z axis, so they must
    ** be rotated to get them on x axis.
    */
    glPushMatrix();
    glColor3f(1.f, 1.f, 1.f);
    glRotatef(90.f, 0.f, 1.f, 0.f);
    glTranslatef(0.f, 0.f, .1f);
    gluCylinder(cone, .5, 0., 1., 20, 20); /* cone on z-axis */
    gluDisk(base, 0., .5, 20, 20); /* base of cone */
    glTranslatef(0.f, 0.f, -1.1f);
    gluSphere(sphere, .25f, 20, 20);
    glPopMatrix();

    
    glBegin(GL_LINE_STRIP); 
    glColor3f(1.f, 0.f, 0.f);  /* X axis red */

    glVertex3f(-5.f, 0.f, 0.f);
    glVertex3f(5.f, 0.f, 0.f);
    glVertex3f(4.5f, .25f, 0.f);
    glVertex3f(4.5f, -.25f, 0.f);
    glVertex3f(5.f, 0.f, 0.f);
    glEnd();

    glBegin(GL_LINE_STRIP);
    glColor3f(0.f, 1.f, 0.f); /* Y axis green */

    glVertex3f(0.f, -5.f, 0.f);
    glVertex3f(0.f, 5.f, 0.f);
    glVertex3f( .25f, 4.5f, 0.f);
    glVertex3f(-.25f, 4.5f, 0.f);
    glVertex3f(0.f, 5.f, 0.f);
    glEnd();

    glBegin(GL_LINE_STRIP);
    glColor3f(0.f, 0.f, 1.f); /* Z axis blue */

    glVertex3f(0.f, 0.f, -5.f);
    glVertex3f(0.f, 0.f, 5.f);
    glVertex3f(0.f, .25f, 4.5f);
    glVertex3f(0.f, -.25f, 4.5f);
    glVertex3f(0.f, 0.f, 5.f);
    glEnd();

    glPopMatrix();

    glutSwapBuffers();
}
void key(unsigned char key, int x, int y)
{
    if(key == '\033') {
	gluDeleteQuadric(sphere);
	exit(0);
    }
}


void mouse(int button, int state, int x, int y)
{
    if(state == GLUT_DOWN) /* only process mouse up events */
	return;

    switch(button) {
      case GLUT_LEFT_BUTTON:
	if(da) /* toggle x/y rotation */
	    da = 0.f;
	else
	    da = .01f;
	break;
      case GLUT_MIDDLE_BUTTON:
	if(db) /* toggle z rotation */
	    db = 0.f;
	else
	    db = .01f;
	break;
      case GLUT_RIGHT_BUTTON:
	if(dc) /* toggle distance cycle */
	    dc = 0.f;
	else
	    dc = .01f;
	break;
    }

}

void anim()
{
    redraw(); /* draw a frame */
    a += da; /* update parameters */
    b += db;
    c += dc;
}

void main(int argc, char **argv)
{

    glutInit(&argc, argv);
    glutInitWindowSize(512,512);
    glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH);
    (void)glutCreateWindow("View");
    glutDisplayFunc(redraw);
    glutMouseFunc(mouse);
    glutKeyboardFunc(key);
    glutIdleFunc(anim);
    glEnable(GL_DEPTH_TEST); /* remove hidden objects */

    glMatrixMode(GL_PROJECTION);
    /* 
    ** x and y range from -.5 to .5 at front of view volume; frustum
    ** gets wider as you get farther from the screen. If you want to
    ** make frustum more square, make near and far values larger.
    ** far - near = 12; viewing distance .5 -> 2; + length of axis.
    */
    glFrustum(-.5, .5, -.5, .5, .5, 12.5);

    glMatrixMode(GL_MODELVIEW);

    sphere = gluNewQuadric(); /*create some interesting objects */
    cone = gluNewQuadric();
    base = gluNewQuadric();

    glutMainLoop();
    gluDeleteQuadric(sphere);
}
