/*---------------------------------------------------------------------------
Author: Christopher Huyler
Date: 02/10/02
Description:  Project one, a simple animation
This animation simulates flies chasing after an apple.  You can move the
apple to another place by clicking the left mouse button.  You can move
the apple around by holding the left button down.  To distract the flies,
press and hold the right mouse button and wave it around.   */
//---------------------------------------------------------------------------
#pragma hdrstop

#include "gl/glut.h"
#include <stdlib.h>
#include <math.h>
//---------------------------------------------------------------------------
#pragma package(smart_init)

//globals for the rectangle definition
GLint coords[200];                      // flee coordinates
GLint xfocus=250,                       // x coordinate for flie focus
      yfocus=250,                       // y coordinate for flie focus
      rfocus=250,                       // radius of focus point
      applex=250,                       // x coordinate for apple
      appley=250;                       // y coordinate for apple
GLint speed =2;                         // speed of flies
GLint incr;                             // iterator

// display callback
void display(void) {
  GLfloat x,y,angle;
  GLfloat angleToRadians = 3.14159265358979/180.0;
  glClear(GL_COLOR_BUFFER_BIT); //clear background

  glColor3f(1.0,0.0,0.0);       // draw apple as a circle
  glBegin(GL_TRIANGLE_FAN);
    glVertex2f(applex,appley);
    for(angle=0.0; angle<=360.0; angle+=15) {
        x=cos(angle*angleToRadians)*12;
        y=sin(angle*angleToRadians)*12;
        glVertex2f(applex+x,appley+y);
    }
  glEnd();

  glColor3f(.4,.4,.1);      // draw stem as a sequence of lines
  glBegin(GL_LINES);
    glVertex2f(applex,appley+12);
    glVertex2f(applex+1,appley+15);
    glVertex2f(applex+1,appley+15);
    glVertex2f(applex+4,appley+18);
  glEnd();

  glColor3f(0.0,.5,0.0);    // draw the leaves as polygons
  glBegin(GL_POLYGON);
    glVertex2f(applex,appley+12);
    glVertex2f(applex+6,appley+14);
    glVertex2f(applex+10,appley+12);
    glVertex2f(applex+4,appley+10);
    glVertex2f(applex,appley+12);
    glVertex2f(applex-6,appley+14);
    glVertex2f(applex-6,appley+18);
    glVertex2f(applex-2,appley+16);
  glEnd();

  glColor3f(0.0,0.0,0.0);
  for(incr=0;incr<200;incr+=2) {// draw each flee as a '*'
    glRasterPos2i(coords[incr],coords[incr+1]);
    glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_10, '*');
  }


  glutSwapBuffers();
  glFlush(); //always a good idea to push what's left to display
} //display

// move apple with mouse and update focus
void appleMotion(int x, int y) {
    applex = x;
    appley = 500 - y;
    xfocus = x;
    yfocus = 500 - y;
    rfocus = 25;
}

// follow mouse and update focus
void followMotion(int x, int y) {
    xfocus = x;
    yfocus = 500 - y;
    rfocus = 25;
}

// stop following mouse and go back to apple
void stopFollow() {
    xfocus = applex;
    yfocus = appley;
    rfocus = 25;
}

// mouse callback
void mouse(int button, int state, int x, int y) {
    if (button == GLUT_LEFT_BUTTON)     // left button moves apple
        switch(state) {
            case GLUT_DOWN : appleMotion(x,y);
                             glutMotionFunc(appleMotion);
                             break;
            case GLUT_UP   : glutMotionFunc(NULL);
                             break;
        }
    if (button == GLUT_RIGHT_BUTTON)    // right button follows mouse
        switch(state) {
            case GLUT_DOWN : followMotion(x,y);
                             glutMotionFunc(followMotion);
                             break;
            case GLUT_UP   : glutMotionFunc(NULL);
                             stopFollow();
                             break;
        }
}

// idle callback
void idle(void) {
  GLint i, distance,            // distance from focus
           xdist,               // x component of distance
           ydist;               // y component of distance
  int focused = 0;              // number of flies inside focus
  for(i=0; i<200; i+=2)
  {// change postion of each fly

    // get distance from focus point
    xdist = abs(xfocus - coords[i]);
    ydist = abs(yfocus - coords[i+1]);
    distance = (xdist == 0)? ydist : sqrt(xdist*xdist + ydist*ydist);

    // determine next direction
    if (distance < rfocus) {     // inside bounding sphere
        coords[i] += (rand()%3 - 1)*speed;
        coords[i+1] += (rand()%3 - 1)*speed;
        focused++;               // track # of flies inside sphere


    }
    else {                       // outside bounding sphere
        if (coords[i] > xfocus+rfocus/2)
            coords[i] += (rand()%2 - 1)*speed;
        else {
            if (coords[i] < xfocus-rfocus/2)
                coords[i] += (rand()%2)*speed;
            else
                coords[i] += (rand()%3 - 1)*speed;
        }
        if (coords[i+1] > yfocus+rfocus/2)
            coords[i+1] += (rand()%2 - 1)*speed;
        else {
            if (coords[i+1] < yfocus-rfocus/2)
                coords[i+1] += (rand()%2)*speed;
            else
                coords[i+1] += (rand()%3 - 1)*speed;
        }
    }
  }
  rfocus = (focused>66)?50:rfocus; // increase sphere if 2/3 of flies
                                   // have arrived in sphere
  glutPostRedisplay();             //invoke the display callback
}

// menu callback to change flee speed
void speedMenu(int id) {
    speed = (GLint) id;
}


void init(void) {
  //local init function to set user defined vars
  for(incr=0;incr<200;incr++)
    coords[incr]=rand()%500;
  glClearColor(1.0,1.0,1.0,0.0); //background is white
  glColor3f(0.0,0.0,0.0);        //flies are black
  gluOrtho2D(0.0,500.0,0.0,500.0);//2D projection with canvas size of 640X480
}//init

int main(int argc, char** argv) {
  //initialization
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
  glutInitWindowSize(500, 500);
  glutInitWindowPosition(100,100);
  glutCreateWindow("Flies");

  //register the callback functions
  glutDisplayFunc(display);
  glutMouseFunc(mouse);
  glutCreateMenu(speedMenu);
    glutAddMenuEntry("1x",1);
    glutAddMenuEntry("2x",2);
    glutAddMenuEntry("3x",3);
    glutAddMenuEntry("4x",4);
  glutAttachMenu(GLUT_MIDDLE_BUTTON);
  glutIdleFunc(idle);
  //init
  init();
  //enter event processing loop
  glutMainLoop();
  return 0;
}//main


