##	/*
##	 *	Simple trackball-like motion adapted (ripped off) from projtex.c
##	 *	(written by David Yu and David Blythe).	 See the SIGGRAPH '96
##	 *	Advanced OpenGL course notes.
##	 */

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *

from math import sqrt, cos

class struct: pass ## globals
GBL = struct();

GLTB_TIME_EPSILON = 10

GBL.gltb_lasttime = 0;
GBL.gltb_lastposition = [0, 0, 0];

GBL.gltb_angle = 0.0
GBL.gltb_axis = [0, 0, 0];
GBL.gltb_transform = None;

GBL.gltb_width	= 0
GBL.gltb_height = 0

GBL.gltb_button = -1;
GBL.gltb_tracking = GL_FALSE;
GBL.gltb_animate = GL_TRUE;

def _gltbPointToVector(x, y, width, height) :
	v = [0, 0, 0]
	## /* project x, y onto a hemi-sphere centered within width, height. */
	v[0] = (2.0 * x - width) / width;
	v[1] = (height - 2.0 * y) / height;
	d = sqrt(v[0] * v[0] + v[1] * v[1]);
	if d >= 1.0 : d = 1.0
	v[2] = cos((3.14159265 / 2.0) * d);
	a = 1.0 / sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
	v[0] *= a;
	v[1] *= a;
	v[2] *= a;
	return v

NULL = None
def _gltbAnimate() :
	glutPostRedisplay();

def _gltbStartMotion(x, y, button, time) :
	assert(GBL.gltb_button != -1);
	GBL.gltb_tracking = GL_TRUE;
	GBL.gltb_lasttime = time;
	GBL.gltb_lastposition = _gltbPointToVector(x, y, GBL.gltb_width, GBL.gltb_height);

def _gltbStopMotion(button, time) :
	assert(GBL.gltb_button != -1);

	GBL.gltb_tracking = GL_FALSE;

	if (time - GBL.gltb_lasttime < GLTB_TIME_EPSILON and GBL.gltb_animate) :
		glutIdleFunc(_gltbAnimate);
	else :
		GBL.gltb_angle = 0;
		if (GBL.gltb_animate) :
			glutIdleFunc(NULL);

def gltbAnimate(animate) :
	GBL.gltb_animate = animate;

def gltbInit(button) :
	GBL.gltb_button = button;
	GBL.gltb_angle = 0.0;
	## /* put the identity in the trackball transform */
	glPushMatrix();
	glLoadIdentity();
	GBL.gltb_transform = glGetFloatv(GL_MODELVIEW_MATRIX)
	glPopMatrix();

def gltbMatrix() :
	assert(GBL.gltb_button != -1);

	glPushMatrix();
	glLoadIdentity();
	glRotatef(GBL.gltb_angle, GBL.gltb_axis[0], GBL.gltb_axis[1], GBL.gltb_axis[2]);
	glMultMatrixf(GBL.gltb_transform);
	GBL.gltb_transform = glGetFloatv(GL_MODELVIEW_MATRIX)
	glPopMatrix();

	glMultMatrixf(GBL.gltb_transform);

def gltbReshape(width, height) :
	assert(GBL.gltb_button != -1);
	GBL.gltb_width	= width;
	GBL.gltb_height = height;

def gltbMouse(button, state, x, y) :
	assert(GBL.gltb_button != -1);

	if (state == GLUT_DOWN and button == GBL.gltb_button) :
		_gltbStartMotion(x, y, button, glutGet(GLUT_ELAPSED_TIME));
	elif (state == GLUT_UP and button == GBL.gltb_button) :
		_gltbStopMotion(button, glutGet(GLUT_ELAPSED_TIME));

def gltbMotion(x, y) :
	assert(GBL.gltb_button != -1);

	if (GBL.gltb_tracking == GL_FALSE) :
		return;
	current_position = _gltbPointToVector(x, y, GBL.gltb_width, GBL.gltb_height)

	##/* calculate the angle to rotate by (directly proportional to the
	##   length of the mouse movement) */
	dx = current_position[0] - GBL.gltb_lastposition[0];
	dy = current_position[1] - GBL.gltb_lastposition[1];
	dz = current_position[2] - GBL.gltb_lastposition[2];
	GBL.gltb_angle = 90.0 * sqrt(dx * dx + dy * dy + dz * dz);

	## /* calculate the axis of rotation (cross product) */
	GBL.gltb_axis[0] = GBL.gltb_lastposition[1] * current_position[2] - \
		GBL.gltb_lastposition[2] * current_position[1];
	GBL.gltb_axis[1] = GBL.gltb_lastposition[2] * current_position[0] - \
		GBL.gltb_lastposition[0] * current_position[2];
	GBL.gltb_axis[2] = GBL.gltb_lastposition[0] * current_position[1] - \
		GBL.gltb_lastposition[1] * current_position[0];

	## /* XXX - constrain motion */
	GBL.gltb_axis[2] = 0;

	## /* reset for next time */
	GBL.gltb_lasttime = glutGet(GLUT_ELAPSED_TIME);
	GBL.gltb_lastposition[0] = current_position[0];
	GBL.gltb_lastposition[1] = current_position[1];
	GBL.gltb_lastposition[2] = current_position[2];

	## /* remember to draw new position */
	glutPostRedisplay();
