// bezierS1d - Hip - 2009-12-12
// - crta Bezierovu krivulju kroz zadane toccke
// - pritiskom na desnu tipku misha može se pocceti novi splajn

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

// maksimalni broj toccaka
#define NTMAX 30

// maksimalni broj splajnova
#define NSMAX 6

// polumjer toccke
#define TPOLUMJER 0.15

GLUquadricObj *disk;

int ntocaka = 0; // broj tocaka
int akt = -1; // "aktivna" tocka
int nspl = 0; // broj splajnova
int splBG[NSMAX]; // tabela poccetaka splajnova
int splLEN[NSMAX]; // tabela duljina splajnova

double ktocka[NTMAX][3]; // tabela s tocckama

double udaljenost(double t1[], double t2[]) {
  return sqrt(pow(t1[0] - t2[0], 2.0) +
    pow(t1[1] - t2[1], 2.0) + pow(t1[2] - t2[2], 2.0));
} // udaljenost

void crtajTocku(double t[], double r) {
  glPushMatrix();
    glTranslated(t[0], t[1], t[2]);
    gluDisk(disk, 0.0, r, 16, 2);
  glPopMatrix();
} // crtajTocku

void iscrtaj(void) {
  int i, j;

  glClear(GL_COLOR_BUFFER_BIT);

  if(ntocaka > 1) {
    for(j = 0; j <= nspl; j++) {
      // crtkane linije koje spajaju toccke
      glLineStipple(1, 0x00FF);
      glEnable(GL_LINE_STIPPLE);
      glColor3f(1.0, 1.0, 0.0);
      glBegin(GL_LINE_STRIP);
      for(i = splBG[j]; i < splBG[j] + splLEN[j]; i++)
        glVertex3dv(&ktocka[i][0]);
      glEnd();
      glDisable(GL_LINE_STIPPLE);

      // Bezierova krivulja
      glMap1d(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, splLEN[j], &ktocka[splBG[j]][0]);
      glEnable(GL_MAP1_VERTEX_3);
      glColor3f(1.0, 1.0, 1.0);
      glMapGrid1d(ntocaka * 10, 0.0, 1.0);
      glEvalMesh1(GL_LINE, 0, ntocaka * 10);
      // - alternativni naccin crtanja -
      //glBegin(GL_LINE_STRIP);
      //  for(i = 0; i <= 100; i++) glEvalCoord1f((float)i / 100.0);
      //glEnd();
      glDisable(GL_MAP1_VERTEX_3);
    }
  }

  // crtanje toccaka
  for(i = 0, j = 0; i < ntocaka; i++) { 
    if(i == splBG[j]) {j++; glColor3f(0.0, 1.0, 0.0);} 
    else glColor3f(1.0, 0.0, 0.0);
    if(i == akt) glColor3f(0.0, 0.0, 1.0); 
    crtajTocku(&ktocka[i][0], TPOLUMJER);
  }

   glutSwapBuffers();
} // iscrtaj

void skaliraj(int w, int h) {
  float a = 5.0, b;

  glViewport(0, 0, w, h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  if(w > h) {
    b = a * (float)w / (float)h;
    glOrtho(-b, b, -a, a, -a, a);
  } else {
    b = a * (float)h / (float)w;
    glOrtho(-a, a, -b, b, -a, a);
  }

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
} // skaliraj

void mish(int tipka, int stanje, int x, int yy) {
  GLint viewport[4];
  GLdouble mvmatrix[16], projmatrix[16];
  int y, i, j;
  double wx, wy, wz, w[3], d;

  if(tipka == GLUT_LEFT_BUTTON) {
    if(stanje == GLUT_DOWN) {
      glGetIntegerv(GL_VIEWPORT, viewport);
      glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix);
      glGetDoublev(GL_PROJECTION_MATRIX, projmatrix);

      // viewport[2] - shirina prozora; viewport[3] - visina prozora
      y = viewport[3] - yy - 1; // korekcija ishodishta
      gluUnProject((GLdouble) x, (GLdouble) y, 0.0, 
        mvmatrix, projmatrix, viewport, &wx, &wy, &wz);
      w[0] = wx; w[1] = wy; w[2] = 0.0;

      akt = -1;
      for(i = 0; i < ntocaka; i++) {
        d = udaljenost(ktocka[i], w); // printf("%d udaljenost: %lf\n", i, d);
        if(d <= TPOLUMJER) akt = i;
      }
      if((akt == -1) && (ntocaka < NTMAX)) {
        ktocka[ntocaka][0] = wx;
        ktocka[ntocaka][1] = wy;
        ktocka[ntocaka][2] = 0.0;
        (splLEN[nspl])++;
        ntocaka++;
      }
      glutPostRedisplay();
    }

    if(stanje == GLUT_UP) {
      glGetIntegerv(GL_VIEWPORT, viewport);
      glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix);
      glGetDoublev(GL_PROJECTION_MATRIX, projmatrix);

      // viewport[2] - shirina prozora; viewport[3] - visina prozora
      y = viewport[3] - yy - 1; // korekcija ishodishta
      gluUnProject ((GLdouble) x, (GLdouble) y, 0.0, 
        mvmatrix, projmatrix, viewport, &wx, &wy, &wz);

      //printf ("(%4d, %4d) -> (%f, %f)\n", x, y, wx, wy);
      if(akt >= 0) {
        ktocka[akt][0] = wx; ktocka[akt][1] = wy; ktocka[akt][2] = 0.0;
        akt = -1;
      }
      glutPostRedisplay();
    }
  }

  if((tipka == GLUT_RIGHT_BUTTON) && (stanje == GLUT_DOWN)) {
    nspl++;
    splBG[nspl] = ntocaka - 1; splLEN[nspl] = 1;
    //printf("novi splajn: %d\n", nspl);
    glutPostRedisplay();
  }

  if((tipka == GLUT_MIDDLE_BUTTON) && (stanje == GLUT_UP)) {
    // ispis toccaka
    for(i = 0, j = 0; i < ntocaka; i++) { 
      if(i == splBG[j]) {
        if(j != 0) printf(" %6.2lf %6.2lf\n", ktocka[i][0], ktocka[i][1]);
        printf("spline %d:\n", j++);
      }
      printf(" %6.2lf %6.2lf\n", ktocka[i][0], ktocka[i][1]);
    }
    exit(0);
  }
} // mish

int main(int argc, char** argv) {
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
  glutInitWindowSize(500, 500);
  glutInitWindowPosition(0, 0);
  glutCreateWindow(argv[0]);

  glClearColor(0.0, 0.0, 0.0, 0.0);
  disk = gluNewQuadric();
  splBG[0] = 0;
  splLEN[0] = 0;

  glutDisplayFunc(iscrtaj);
  glutReshapeFunc(skaliraj);
  glutMouseFunc(mish);

  glutMainLoop();
  return 0;
} // main

