/* voronoi.c  diagram where each object owns space nearest to it  */
/*            voronoi < voronoi.dat file input or mouse input     */
/*            left click to add point to polygon,                 */
/*            right click to end this polygon                     */
/*            expect strange results if polygons overlap          */

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

static int num_obj = 0;
static int num_pts[8]={0,0,0,0,0,0,0,0}; /* max of 8 objects, add colors */
static float x[20][10]; /* each onject max 10 vertices */
static float y[20][10];
static float x_min=0.0, x_max=40.0;
static float y_min=0.0, y_max=40.0;
static int width = 400;
static int height = 400;
static float scale = 10.0;

static GLfloat colors[8][3] =
       {1.0, 0.0, 0.0,  0.0, 1.0, 0.0,  0.0, 0.0, 1.0,
        1.0, 1.0, 0.0,  0.0, 1.0, 1.0,  1.0, 0.0, 1.0,
        0.7, 0.7, 0.7,  0.5, 0.5, 0.7};
static GLfloat dx; /* one pixel in drawing coordinates */
static GLfloat dy; /* one pixel in drawing coordinates */

static void read_geom()
{
  int i, j;
  int pts;
  
  x_min = 0.0;
  y_min = 0.0;
  x_max = 40.0;
  y_max = 40.0;
  
  printf("enter object data, else zero for using mouse.\n");
  scanf("%d", &num_obj);
  printf("%d objects \n", num_obj);
  if(num_obj>8) {printf("8 objects max\n"); exit(0);}
  
  for(i=0; i<num_obj; i++)
  {
    scanf("%d", &pts);
    printf("%d points \n", pts);
    num_pts[i] = pts;
    for(j=0; j<pts; j++)
    {
      scanf(" %f %f \n", &x[i][j], &y[i][j]);
      printf("x[%d][%d]=%f, y[%d][%d]=%f \n",
             i, j, x[i][j], i, j, y[i][j]);
      if(x[i][j]>x_max) x_max = x[i][j];
      if(x[i][j]<x_min) x_min = x[i][j];
      if(y[i][j]>y_max) y_max = y[i][j];
      if(y[i][j]<y_min) y_min = y[i][j];
    }
  }
  printf("x_min=%f, x_max=%f, y_min=%f, y_max=%f\n",
         x_min, x_max, y_min, y_max);
} /* end read_geom */

static double dist(double a1x, double a1y,
                   double a2x, double a2y,
                   double a3x, double a3y)
{
  /* distance from a point to a line segment */
  double d, d1, d2, d3, a1, b1, c1, a2, b2, c2;
  double x, y;
  int out = 0;
  
  a1 = a3y-a2y;
  b1 = a2x-a3x;
  c1 = a2y*(a3x-a2x)-a2x*(a3y-a2y);
  a2 = a2x-a3x;
  b2 = a2y-a3y;
  c2 = a1y*(a3y-a2y)-a1x*(a2x-a3x);
  if(a2x==a3x) x = a2x;
  else
  {
    x = (b1*c2-b2*c1)/(b2*a1-b1*a2);
    if(x>a2x && x>a3x) out = 1;
    if(x<a2x && x<a3x) out = 1;
  }
  if(a2y==a3y) y = a2y;
  else
  {
    y = (a1*c2-a2*c1)/(a2*b1-a1*b2);
    if(y>a2y && y>a3y) out = 1;
    if(y<a2y && y<a3y) out = 1;
  }
  d1 = sqrt((a1x-x)*(a1x-x)+(a1y-y)*(a1y-y));
  d2 = sqrt((a2x-a1x)*(a2x-a1x)+(a2y-a1y)*(a2y-a1y));
  d3 = sqrt((a3x-a1x)*(a3x-a1x)+(a3y-a1y)*(a3y-a1y));
  if(out==0)     d = d1;
  else if(d2<d3) d = d2;
  else           d = d3;
  return d;
} /* end dist */

static void display(void)
{
  int i, j, k, n;
  GLfloat xf, yf, d, d2, d3, dmin;
  
  /* clear window */
  glClear(GL_COLOR_BUFFER_BIT); 
  glLoadIdentity ();
  if(num_obj==0) return;

  /* draw matching colors */
  glPointSize(1.0);
  for(xf=x_min+dx; xf<x_max-dx; xf=xf+dx)
  {
    for(yf=y_min+dy; yf<y_max-dy; yf=yf+dy)
    {
      dmin=1000.0;
      for(i=0; i<num_obj; i++)
      {
        n = num_pts[i];
        for(j=0; j<n; j++)
        {
          d = dist(xf, yf,
                   x[i][j],y[i][j],
                   x[i][(j+1)%n],y[i][(j+1)%n]);
          if(d<dmin) { dmin=d; k=i;}
        }
      }
      glBegin(GL_POINTS);
        glColor3fv(colors[k]);
        glVertex3f(xf, yf, 0.0);
      glEnd();
    }
  }
  
  /* draw border */
  glColor3f(0.0, 0.0, 0.0);
  glLineWidth(2.0);
  glBegin(GL_LINE_LOOP);
    glVertex2f(x[0][0], y[0][0]);
    glVertex2f(x[0][1], y[0][1]);
    glVertex2f(x[0][2], y[0][2]);
    glVertex2f(x[0][3], y[0][3]);
  glEnd();

  /* Draw the points */
  glPointSize(3.0);
  glLineWidth(2.0);
  for(i=1; i<num_obj; i++)
  {
    glColor3f(0.0, 0.0, 0.0);
    glBegin(GL_POINTS);
      for(j=0; j<num_pts[i]; j++)
      {
        glVertex2f(x[i][j], y[i][j]);
      }
    glEnd();
    glColor3f(0.0, 0.0, 0.0);
    glBegin(GL_LINE_LOOP);
      for(j=0; j<num_pts[i]; j++)
      {
        glVertex2f(x[i][j], y[i][j]);
      }
    glEnd();
  }

  glFlush(); 
} /* end display */

static void mouse(int button, int state, int ix, int iy)
{
  float wx, wy;
  int n;
  
  if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
  {
    if(num_obj==0)
    {
      num_pts[0]=4;
      x[0][0]=0.0;          y[0][0]=0.0;
      x[0][1]=0.0;          y[0][1]=height/scale;
      x[0][2]=width/scale;  y[0][2]=height/scale;
      x[0][3]=width/scale;  y[0][3]=0.0;
      num_obj=2;
    }
    /* Translate back to our coordinate system */
    wx = (float)ix/scale;
    wy = (float)(height - 1 - iy)/scale;
    printf("x=%d, y=%d, wx=%g, wy=%g \n", ix, iy, wx, wy);
    n = num_pts[num_obj-1];
    x[num_obj-1][n] = wx;
    y[num_obj-1][n] = wy;
    num_pts[num_obj-1]++;
  glutPostRedisplay();
  }
  if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN)
  {
    if(num_obj!=0) num_obj++;
  }
} /* end mouse */

static void reshape(int w, int h)
{
  width = w;
  height = h;
  /* Set the transformations */
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0.0, (float)width/scale, 0.0, (float)height/scale, -1.0, 1.0);
  glMatrixMode(GL_MODELVIEW);
  glViewport(0, 0, w, h);
  dx = (x_max-x_min)/(float)(width+1);
  dy = (y_max-y_min)/(float)(height+1);
} /* end reshape */

static void init()
{
  /* set clear color to white */
  glClearColor (1.0, 1.0, 1.0, 0.0);
  /* set fill  color to black */
  glColor3f(0.0, 0.0, 0.0);

  glMatrixMode (GL_PROJECTION);
  glLoadIdentity ();
  glOrtho(0.0, (float)width/scale, 0.0, (float)height/scale, -1.0, 1.0);
  glMatrixMode (GL_MODELVIEW);
  glViewport(0, 0, width, height);
} /* end init */

int main(int argc, char* argv[])
{
  read_geom();
  glutInit(&argc,argv);
  glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB);  
  glutInitWindowSize(width, height);
  glutInitWindowPosition(0,0); 
  
  glutCreateWindow(argv[0]); 
  glutDisplayFunc(display);
  glutReshapeFunc(reshape);
  glutMouseFunc(mouse);
  init();
  glutMainLoop();
  return 0;
} /* end main of voronoi.c */

