/*
 *  main.c
 *  demo
 *
 *  Created by Lee Iverson on Sun Jan 12 2003.
 *  Copyright (c) 2003 UBC. All rights reserved.
 *
 * Under Linux:
 */

#include <stdlib.h>

/* This may need to change to reflect the location of
 * the glut.h file.  Some other options are <glut.h>,
 * <X11/glut.h> or <GLUT/glut.h> (on Mac OS X).
 */
#include <GL/glut.h>

void
drawTri (float x0, float y0, float x1, float y1, float x2, float y2)
{
  glVertex2f (x0, y0);
  glVertex2f (x1, y1);
  glVertex2f (x2, y2);
}

void
sierpinski (int height, float x0, float y0, float x1, float y1, float x2, float y2)
{
  if (height == 0) {
    drawTri (x0, y0, x1, y1, x2, y2);
  } else {
    float mx0 = (x0+x1)/2, my0 = (y0+y1)/2;
    float mx1 = (x1+x2)/2, my1 = (y1+y2)/2;
    float mx2 = (x2+x0)/2, my2 = (y2+y0)/2;
    --height;
    sierpinski (height,  x0,  y0, mx0, my0, mx2, my2);
    sierpinski (height, mx0, my0,  x1,  y1, mx1, my1);
    sierpinski (height, mx2, my2, mx1, my1,  x2,  y2);
  }
}

void
sierpinski2D (GLuint drawMode, int height)
{
  glBegin (drawMode);
  sierpinski (height, 0.0, 1.0, -1.0, -1.0, 1.0, -1.0);
  glEnd ();
}

void
divideTriangle (int height, GLfloat p0[], GLfloat p1[], GLfloat p2[])
{
  if (height == 0) {
    glVertex3fv (p0);
    glVertex3fv (p1);
    glVertex3fv (p2);
  } else {
    GLfloat p01[3], p12[3], p02[3];
    int i;
    for (i = 0; i < 3; ++i) {
      p01[i] = (p0[i]+p1[i])/2;
      p12[i] = (p2[i]+p1[i])/2;
      p02[i] = (p0[i]+p2[i])/2;
    }
    --height;
    divideTriangle (height, p0, p01, p02);
    divideTriangle (height, p2, p02, p12);
    divideTriangle (height, p1, p12, p01);
  }
}

void
tetra (int height, GLfloat p0[], GLfloat p1[], GLfloat p2[], GLfloat p3[])
{
  glColor3f (1.0, 1.0, 1.0);
  divideTriangle (height, p0, p1, p2);
  glColor3f (1.0, 1.0, 0.0);
  divideTriangle (height, p3, p2, p1);
  glColor3f (0.0, 1.0, 1.0);
  divideTriangle (height, p0, p3, p1);
  glColor3f (1.0, 0.0, 1.0);
  divideTriangle (height, p0, p2, p3);
}

void
sierpinski3D (GLuint drawMode, int height)
{
  static GLfloat p0[] = { 0,  1,  0};
  static GLfloat p1[] = {-1, -1,  1};
  static GLfloat p2[] = { 1, -1,  1};
  static GLfloat p3[] = { 0, -1, -1};
  glBegin (drawMode);
  tetra (height, p0, p1, p2, p3);
  glEnd ();  
}

struct {
  int height;		/* Depth of recursion */
  GLfloat theta;	/* Rotation angle */
  int rotating;		/* Animating rotation? */
  GLuint drawMode;	/* Drawing primitives used */
  GLuint polyMode;	/* Polygon drawing mode */
  void (*displayFunc) (GLuint drawMode, int height);
} params = {
  5, 0.0, 0, GL_POINTS, GL_FILL, sierpinski2D
};

void
display ()
{
  glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glColor3f (1.0, 1.0, 1.0);

  glPushMatrix ();
  glRotatef (params.theta, 0.2, 1.0, 0.0);
  glPolygonMode (GL_FRONT_AND_BACK, params.polyMode);
  (*params.displayFunc) (params.drawMode, params.height);
  glPopMatrix ();
  glutSwapBuffers ();
}

void
mouse (int button, int state, int x, int y)
{
}

void
key (unsigned char key, int px, int py)
{
  switch (key) {
    case 'p':	/* Draw points */
      params.drawMode = GL_POINTS;
      break;
    case 't':	/* Draw triangles */
      params.drawMode = GL_TRIANGLES;
      params.polyMode = GL_FILL;
      break;
    case 'l':	/* Draw lines */
      params.drawMode = GL_TRIANGLES;
      params.polyMode = GL_LINE;
      break;
    case '2':	/* Draw 2D gasket */
      params.displayFunc = sierpinski2D;
      break;
    case '3':	/* Draw 3D gasket */
      params.displayFunc = sierpinski3D;
      break;
    case '-':	/* Reduce height (recursion) */
      params.height--;
      if (params.height < 1)
        params.height = 1;
      break;
    case '+':	/* Increase height (recursion) */
      ++params.height;
      break;
    case ' ':	/* Toggle rotation state */
      params.rotating = !params.rotating;
      break;
    case 'r':	/* Reset (and stop) rotation */
      params.rotating = 0;
      params.theta = 0;
      break;
    case '\e':	/* Exit program */
      exit (0);
      break;
  }
  glutPostRedisplay ();
}

void special(int key, int px, int py)
{
}

void reshape (int w, int h)
{
  glViewport(0,0,(GLsizei)w,(GLsizei)h);
}

void
idle ()
{
  if (params.rotating) {
    params.theta += 2;
    if (params.theta > 360) params.theta -= 360;
    glutPostRedisplay ();
  }
}

void
initDisplay ()
{
  glClearColor (0.2, 0.2, 0.4, 0.0);
  glEnable (GL_DEPTH_TEST);
  
  // Setup orthographic projection
  glMatrixMode (GL_PROJECTION);
  glLoadIdentity ();
  glOrtho (-1.0, 1.0, -1.0, 1.0, -1.5, 1.5);
  glMatrixMode (GL_MODELVIEW);

  glPointSize (2.0);
  glLineWidth (2.0);
}

int
main (int argc, char *argv[])
{
  // Setup GLUT
  glutInit (&argc, argv);
  glutInitWindowSize (512, 512);
  glutInitDisplayMode (GLUT_DOUBLE|GLUT_RGB|GLUT_DEPTH);
  glutCreateWindow ("EECE 478 - Example 1");
  
  glutDisplayFunc (display);
  glutReshapeFunc (reshape);
  glutKeyboardFunc (key);
  glutSpecialFunc (special);
  glutMouseFunc (mouse);
  glutIdleFunc (idle);

  initDisplay ();
  
  glutMainLoop ();
  
  return 0;
}


