/* unproject.c  selecting (picking) a 3D object          */
/*              press left mouse button to select object */ 
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <GL/glut.h>

/* fixed display list for this example */
typedef float vec[3];
typedef struct {vec coord; vec color;} points;
static int width = 500;
static int height = 500;
static int selected = -1;
static points display_list[4] = {
       {  0.4,   0.4,   -1.0, 1.0, 0.0, 0.0},
       { -0.4,  -0.4,   -1.0, 0.0, 0.0, 1.0},
       { 40.0, -40.0, -100.0, 1.0, 1.0, 0.0},
       {-40.0,  40.0, -100.0, 0.0, 1.0, 0.0}};


static void printstring(float x, float y, char *string);
static void display(void);
static void reshape(int w, int h);
static GLdouble dist(GLdouble a1x, GLdouble a1y, GLdouble a1z,
                     GLdouble a2x, GLdouble a2y, GLdouble a2z,
                     GLdouble a3x, GLdouble a3y, GLdouble a3z);
static void mouse(int button, int state, int x, int y) ;

static void printstring(float x, float y, char *string)
{
  int len, i;
   
  glPushMatrix();
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluOrtho2D(0.0, (GLfloat)width, 0.0, (GLfloat)height);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
   
   glRasterPos2f(x, y);
   len = (int) strlen(string);
   for (i = 0; i < len; i++)
      glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, string[i]);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective (45.0, (GLfloat) width/(GLfloat) height, 1.0, 100.0);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glPopMatrix();
} /* end printstring */

static void display(void)
{
  char redstr[50];
  int i;
  
  glClear(GL_COLOR_BUFFER_BIT);
  glPointSize(10.0);
  glBegin(GL_POINTS);
    for(i=0; i<4; i++) /* loop through display list */
    {
      if(i==selected) glColor3f(1.0, 1.0, 1.0);
      else glColor3f(display_list[i].color[0],
                     display_list[i].color[1],
                     display_list[i].color[2]);
      glVertex3f(display_list[i].coord[0],
                 display_list[i].coord[1],
                 display_list[i].coord[2]);
    }
  glEnd();
  glColor3f(1.0, 1.0, 1.0);       /* label points */
  sprintf(redstr, "(%f,%f,%f)\n",
          display_list[0].coord[0],
          display_list[0].coord[1],
          display_list[0].coord[2]);
  printstring(300.0, 480.0, redstr);
  sprintf(redstr, "(%f,%f,%f)\n",
          display_list[1].coord[0],
          display_list[1].coord[1],
          display_list[1].coord[2]);
  printstring( 20.0,  10.0, redstr);
  sprintf(redstr, "(%f,%f,%f)\n",
          display_list[2].coord[0],
          display_list[2].coord[1],
          display_list[2].coord[2]);
  printstring(260.0,  10.0, redstr);
  sprintf(redstr, "(%f,%f,%f)\n",
          display_list[3].coord[0],
          display_list[3].coord[1],
          display_list[3].coord[2]);
  printstring( 20.0, 480.0, redstr);

  glFlush();
}

static void reshape(int w, int h)
{
  glViewport (0, 0, w, h);
  width = w;
  height = h;
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective (45.0, (GLfloat) w/(GLfloat) h, 1.0, 100.0);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

static GLdouble dist(GLdouble a1x, GLdouble a1y, GLdouble a1z,
                     GLdouble a2x, GLdouble a2y, GLdouble a2z,
                     GLdouble a3x, GLdouble a3y, GLdouble a3z)
{
  GLdouble d, ax, ay, az, bx, by, bz, cx, cy, cz;
  
  ax = a2x-a1x;      ay = a2y-a1y;      az = a2z-a1z;
  bx = a1x-a3x;      by = a1y-a3y;      bz = a1z-a3z;
  cx = ay*bz-az*by;  cy = ax*bz-az*bx;  cz = ax*by-ay*bx;
  d =  sqrt(cx*cx+cy*cy+cz*cz)/sqrt(ax*ax+ay*ay+az*az);
  return d;
}

static void mouse(int button, int state, int x, int y) 
{
  int i;
  GLint viewport[4];
  GLdouble mvmatrix[16], projmatrix[16];
  GLint realy;  /*  OpenGL y coordinate position  */
  GLdouble wx0, wy0, wz0;  /*  returned world x, y, z coords  */
  GLdouble wx1, wy1, wz1;  /*  returned world x, y, z coords  */
  GLdouble close, d;
  
  if(button==GLUT_LEFT_BUTTON  && state == GLUT_DOWN)
  {
    glGetIntegerv (GL_VIEWPORT, viewport);
    glGetDoublev (GL_MODELVIEW_MATRIX, mvmatrix);
    glGetDoublev (GL_PROJECTION_MATRIX, projmatrix);
    /*  note viewport[3] is height of window in pixels  */
    realy = viewport[3] - (GLint) y - 1;
    printf ("Coordinates at cursor are (%4d, %4d)\n", x, realy);
    gluUnProject((GLdouble) x, (GLdouble) realy, 0.0, 
                 mvmatrix, projmatrix, viewport, &wx0, &wy0, &wz0); 
    printf ("World coords at z=0.0 are (%f, %f, %f)\n", 
            wx0, wy0, wz0);
    gluUnProject((GLdouble) x, (GLdouble) realy, 1.0, 
                 mvmatrix, projmatrix, viewport, &wx1, &wy1, &wz1); 
    printf ("World coords at z=1.0 are (%f, %f, %f)\n", 
            wx1, wy1, wz1);
    selected = -1;
    close = 10000.0;
    for(i=0; i<4; i++)
    {
      d = dist(wx0, wy0, wz0, wx1, wy1, wz1, 
               (double)display_list[i].coord[0],
               (double)display_list[i].coord[1],
               (double)display_list[i].coord[2]);
      printf("distance, d to point %d is %g\n", i, d);
      if(d<close) { close = d; selected = i; }
    }
    printf("selected = %d \n", selected);
    if(close>0.5) selected = -1; /* not close enough */
    glutPostRedisplay();
  }
}

int main(int argc, char* argv[])
{
   glutInit(&argc, argv);
   glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);
   glutInitWindowSize (width, height); 
   glutInitWindowPosition (100, 100);
   glutCreateWindow (argv[0]);
   glutDisplayFunc(display); 
   glutReshapeFunc(reshape); 
   glutMouseFunc(mouse);
   glutMainLoop();
   return 0;
}

