/*****************************************************************
  sphererand.cpp
  by Dave Pape
  18 Feb 2003

 This program demonstrates different ways to pick random points
 on the surface of a sphere.  By drawing several thousand of these
 points, it shows the distribution patterns of the different
 methods.

 The methods are:
    . 'cube' - pick a random point in the cube (-1,-1,-1) - (1,1,1),
     and then normalize it so that it lies on the surface of the
     sphere
    . 'latlon' - pick a random latitude and longitude, and compute
     the corresponding X/Y/Z coordinates
    . 'cyl' - pick a random point using a cylindrical map projection.
     i.e. pick a random Y coordinate and a random longitude

  Each time the spacebar is hit, the program will cycle to using 
  the next randomPoint function.

*****************************************************************/
#include <stdlib.h>
#include <math.h>
#include <unistd.h>
#include <stdio.h>
#include <GL/glut.h>
#include <GL/gl.h>
#include <dms/dms.h>

#define NUM_POINTS 250000

using namespace dms;

void drawEverything(void);

void key(unsigned char k, int x, int y);
void specialkey(int k, int x, int y);


PerspCamera camera;
QuadricObject sphere;
float xRotation=0, yRotation=0;

int mode = 0;


int main(int argc, char *argv[])
    {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
    glutInitWindowSize(512,512);
    glutCreateWindow(argv[0]);
    
    glutDisplayFunc(drawEverything);
    glutKeyboardFunc(key);
    glutSpecialFunc(specialkey);
    
    camera.setPosition(0, 0, 3);
    sphere.makeSphere(0.99, 128, 64);
    
    glutMainLoop();
    return 0;
    }


/*************** CODE OF INTEREST ******************************/
/* Pick a point using a 'cube' distribution.  Pick random X, Y */
/* and Z values in the range -1 to 1, and then normalize the   */
/* resulting vector.  A normalized vector is guaranteed to lie */
/* on the surface of the unit sphere, more or less by          */
/* definition.                                                 */
/*                                                             */
Vector3 randomPointCube(void)
    {
    Vector3 val;
    val[0] = drand48() * 2.0 - 1.0;
    val[1] = drand48() * 2.0 - 1.0;
    val[2] = drand48() * 2.0 - 1.0;
    val.normalize();
    return val;
    }


/*************** CODE OF INTEREST ******************************/
/* Pick a point by choosing a random latitude and longitude,   */
/* and then converting that into its (X,Y,Z) coordinates.      */
/*                                                             */
Vector3 randomPointLatLon(void)
    {
    Vector3 val;
    float lat, lon;
    lat = drand48() * M_PI - M_PI_2;
    lon = drand48() * M_PI * 2.0;
    val[0] = sin(lon) * cos(lat);
    val[1] = sin(lat);
    val[2] = cos(lon) * cos(lat);
    return val;
    }


/*************** CODE OF INTEREST ******************************/
/* Pick a point by choosing a random cylindrical position -    */
/* this consists of a random Y value and a random longitude.   */
/* The X & Z values are calculated by taking the arcsin of the */
/* Y value for the latitude of the point, and plugging it into */
/* the same latitude/longitude formulae as above.              */
/*                                                             */
Vector3 randomPointCyl(void)
    {
    Vector3 val;
    float lon, y;
    lon = drand48() * M_PI * 2.0;
    y = drand48() * 2.0 - 1.0;
    val[0] = sin(lon) * cos(asin(y));
    val[1] = y;
    val[2] = cos(lon) * cos(asin(y));
    return val;
    }


/*************** CODE OF INTEREST ******************************/
/* Draw a set of random points on the sphere.  This function   */
/* uses the desired randomPoint function to generate each      */
/* position, and draws them as a set of GL points.             */
/*                                                             */
void drawRandomPoints(void)
    {
    Vector3 point;
    srand48(1);
    glColor3f(1, 1, 0.3);
    glBegin(GL_POINTS);
    for (int i=0; i < NUM_POINTS; i++)
        {
        if (mode == 0)
            point = randomPointCube();
        else if (mode == 1)
            point = randomPointLatLon();
        else if (mode == 2)
            point = randomPointCyl();
        glVertex3fv(point.vec);
        }
    glEnd();
    }


void drawEverything(void)
    {
    glClearColor(0.6, 0.6, 0.6, 0.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
    
    camera.apply();
    Color::Blue.apply();
    sphere.drawAll();
    
    glRotatef(xRotation, 1, 0, 0);
    glRotatef(yRotation, 0, 1, 0);
    drawRandomPoints();

    glutSwapBuffers();

    checkGLError("end-of-frame");
    }


void key(unsigned char k, int x, int y)
    {
    if (k == 27)
        exit(0);
    else if (k == ' ')
        mode = (mode+1) % 3;
    glutPostRedisplay();
    }


void specialkey(int k, int x, int y)
    {
    if (k == GLUT_KEY_LEFT)
        yRotation += 3.0;
    else if (k == GLUT_KEY_RIGHT)
        yRotation -= 3.0;
    else if (k == GLUT_KEY_UP)
        xRotation -= 2.0;
    else if (k == GLUT_KEY_DOWN)
        xRotation += 2.0;
    else if (k == GLUT_KEY_HOME)
        camera.moveForward(0.25);
    else if (k == GLUT_KEY_END)
        camera.moveForward(-0.25);
    else if (k == GLUT_KEY_PAGE_UP)
        camera.zoom(-1);
    else if (k == GLUT_KEY_PAGE_DOWN)
        camera.zoom(1);
    glutPostRedisplay();
    }

