/*
	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 convertHLS2RGB(float, float, float, float *, float *, float *);
float value(float, float, float);
void HLS(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();
	HLS();
	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( 7.0,  0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
}

void convertHLS2RGB( float h, float l, float s, float *r, float *g, float *b)
{
//	conversion from Foley et.al., Figure 13.37, page 596
	float m1, m2;
	
	if (l <= 0.5) m2 = l*(1.0+s);
	else          m2 = l + s - l*s;
	m1 = 2.0*l - m2;
	if (s == 0.0) {	// achromatic cast
		*r = *g = *b = l;
	}
	else {	// chromatic case
		*r = value(m1, m2, h+120.0);
		*g = value(m1, m2, h);
		*b = value(m1, m2, h-120.0);
	}
}

float value( float n1, float n2, float hue)
{	// helper function for the HLS->RGB conversion
	if (hue > 360.0) hue -= 360.0;
	if (hue < 0.0)   hue += 360.0;	// no more than one correction will be needed if h bounded above
	if (hue < 60.0)  return( n1 + (n2 - n1)*hue/60.0 );
	if (hue < 180.0) return( n2 );
	if (hue < 240.0) return( n1 + (n2 - n1)*(240.0 - hue)/60.0 );
	return( n1 );
}

void HLS(void)
{
//#define NSTEPS 6	// HLS double hexcone at this time
#define NSTEPS 120
#define steps (float)NSTEPS
#define TWOPI 6.28318

	int i;
	float r, g, b;
	
	glBegin(GL_TRIANGLE_FAN);	//	bottom cone of the HLS space
		glColor3f(0.0, 0.0, 0.0);
		glVertex3f(0.0, 0.0, -3.0);
		for (i=0; i<=NSTEPS; i++) {
			convertHLS2RGB(360.0*(float)i/steps, 0.5, 1.0, &r, &g, &b);
			glColor3f(r, g, b);
			glVertex3f(2.0*cos(TWOPI*(float)i/steps),2.0*sin(TWOPI*(float)i/steps),0.0);
		}
	glEnd();
	glBegin(GL_TRIANGLE_FAN);	//	top cone of the HLS space
		glColor3f(1.0, 1.0, 1.0);
		glVertex3f(0.0, 0.0, 3.0);
		for (i=0; i<=NSTEPS; i++) {
			convertHLS2RGB(360.0*(float)i/steps, 0.5, 1.0, &r, &g, &b);
			glColor3f(r, g, b);
			glVertex3f(2.0*cos(TWOPI*(float)i/steps),2.0*sin(TWOPI*(float)i/steps),0.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("HLS double-cone color space");
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutKeyboardFunc(keyboard);

	myinit();
	glutMainLoop();
}