/*
	Create a view of the HSV color space for classroom use

	Sample for introductory computer graphics course -- copyright (c) 2000, Steve Cunningham
*/
#include "glut.h"
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#define SIZE 20.0

static char ch;
typedef GLfloat point3[3];
typedef GLfloat color [4];

// function prototypes
void myinit(void);
void display(void);
void convertHSV2RGB(float, float, float, float *, float *, float *);
void HSV(void);
void reshape(int,int);
void keyboard(unsigned char,int,int);

void myinit(void)
{
	glClearColor( 0.5, 0.5, 0.5, 0.0 );
	glEnable(GL_DEPTH_TEST); // allow z-buffer display
	glShadeModel(GL_SMOOTH);
}

void display( void )
{
    typedef GLfloat point3[3];
	#define Angle 2.0

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //  NOTE that we do not take ourselves back to the original raw-world state.
    //  We let the modelview matrix hold our global view of the model, and
    //  we modify it by including the new rotation each time a key is pressed
    switch(ch)
    {
		case 'w':
			glRotatef( Angle, 1.0, 0.0, 0.0); break;
		case 'q':
			glRotatef(-Angle, 1.0, 0.0, 0.0); break;
		case 's':
			glRotatef( Angle, 0.0, 1.0, 0.0); break;
		case 'a':
			glRotatef(-Angle, 0.0, 1.0, 0.0); break;
		case 'x':
			glRotatef( Angle, 0.0, 0.0, 1.0); break;
		case 'z':
			glRotatef(-Angle, 0.0, 0.0, 1.0); break;
	}

	//  NOW we save the state of the modelview transformation so we can restore
	//  it after the axes and cube have been drawn.
	glPushMatrix();
	HSV();
	glPopMatrix();
	glutSwapBuffers();
 }

void reshape(int w,int h)
{
	glViewport(0,0,(GLsizei)w,(GLsizei)h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(60.0,1.0,1.0,30.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt( 0.0,  10.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
}

void convertHSV2RGB( float h, float s, float v, float *r, float *g, float *b)
{
//	conversion from Foley et.al., fig. 13.34, p. 593
	float f, p, q, t;
	int   k;

	if (s == 0.0) {	// achromatic case
		*r = *g = *b = v;
	}
	else {	// chromatic case
		if (h == 360.0) h=0.0;
		h = h/60.0;
		k = (int)h;
		f = h - (float)k;
		p = v * (1.0 - s);
		q = v * (1.0 - (s * f));
		t = v * (1.0 - (s * (1.0 - f)));
		switch (k) {
			case 0: *r = v; *g = t; *b = p; break;
			case 1: *r = q; *g = v; *b = p; break;
			case 2: *r = p; *g = v; *b = t; break;
			case 3: *r = p; *g = q; *b = v; break;
			case 4: *r = t; *g = p; *b = v; break;
			case 5: *r = v; *g = p; *b = q; break;
		}
	}
}

void HSV(void)
{
#define NSTEPS 144
#define steps (float)NSTEPS
#define TWOPI 6.28318

	int i;
	float r, g, b;
	
	glBegin(GL_TRIANGLE_FAN);	//	cone of the HSV space
		glColor3f(0.0, 0.0, 0.0);
		glVertex3f(0.0, 0.0, -2.0);
		for (i=0; i<=NSTEPS; i++) {
			convertHSV2RGB(360.0*(float)i/steps, 1.0, 1.0, &r, &g, &b);
			glColor3f(r, g, b);
			glVertex3f(2.0*cos(TWOPI*(float)i/steps),2.0*sin(TWOPI*(float)i/steps),2.0);
		}
	glEnd();
	glBegin(GL_TRIANGLE_FAN);	//	top plane of the HSV space
		glColor3f(1.0, 1.0, 1.0);
		glVertex3f(0.0, 0.0, 2.0);
		for (i=0; i<=NSTEPS; i++) {
			convertHSV2RGB(360.0*(float)i/steps, 1.0, 1.0, &r, &g, &b);
			glColor3f(r, g, b);
			glVertex3f(2.0*cos(TWOPI*(float)i/steps),2.0*sin(TWOPI*(float)i/steps),2.0);
		}
	glEnd();
}

void keyboard(unsigned char key, int x, int y)
{
	switch (key)
	{
		case 'w' :
		case 'q' :
		case 's' :
		case 'a' :
		case 'x' :
		case 'z' :
			ch = key; break;
	}
	glutPostRedisplay();
}

void main(int argc, char** argv)
{
	glutInit(&argc,argv);
	glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(500,500);
	glutInitWindowPosition(70,70);
	glutCreateWindow("HSV Color Space");
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);

	myinit();
	glutMainLoop();
}