# with this edition we retire ch.py nee MH and start fixing it
# edited by GF and later by Matt Hoffman with rearrangements
# whose philosophy is reposition 4vertex as being drawn,
# while we usually reassociate and apply rotation after being drawn.
# pyNewTorus.py by Stan Blank, Wayne City, IL
# Credit to George K. Francis for the excellent original program
# continued by GF 18nov12

# om (for angle omega) represents time in the homotopy
global om; om = 0
global dom; dom = .05
global xmult; xmult=1 # rotation angle multiplier
global ymult; ymult=1
global zmult; zmult=2

global dangle; dangle = 15
#compare with dangle=90
global dgap; dgap = dangle

from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
from math import *
import sys, numpy

svn = sqrt(2)/2.
deg = pi/180
def C(u): return cos(u*deg)
def S(u): return sin(u*deg)

global wd
global ht
global mouseX
global mouseY
global aff
global nrange
nrange = 3.0

 aff = [1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]

def drawvert(th,ta, xmat, ymat, zmat):
#parametric sphere equations
#n0= svn*C(th+ta)
#n1= svn*S(th+ta)
#n2= svn* C(th-ta)
#n3= svn*S(th-ta)
n0= .5*(C(th) + S(th))
n1= .5*(C(th) - S(th))
n2= .5*(C(ta) + S(ta))
n3= .5*(C(ta) - S(ta))
vert=numpy.array([n0,n1,n2,n3])

# this is the numpy way of applyig matrix multiplation
vert=numpy.dot(xmat,vert)
vert=numpy.dot(ymat,vert)
vert=numpy.dot(zmat,vert)

#glColor3f(S(th),C(ta),S(th*ta))
glColor3f(1.,1.,1.)
# clifford torus
denominator = 1-vert[3]
glVertex3f(vert[0]/denominator, vert[1]/denominator, vert[2]/denominator)

def drawsurf():
global om #needed because it is changed in the function
global dom
matc=C(xmult*om); mats=S(xmult*om)
xmat=numpy.array([[matc,0,0,-mats],[0,1,0,0],[0,0,1,0],[mats,0,0,matc]])
matc=C(ymult*om); mats=S(ymult*om)
ymat=numpy.array([[1,0,0,0],[0,matc,0,-mats],[0,0,1,0],[0,mats,0,matc]])
matc=C(zmult*om); mats=S(zmult*om)
zmat=numpy.array([[1,0,0,0],[0,1,0,0],[0,0,matc,-mats],[0,0,mats,matc]])
om += dom
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
for th in range(0 ,360+dangle,dangle):
for ta in range (0,360+dangle, dangle):
drawvert(th,ta, xmat, ymat, zmat)
drawvert(th+dgap,ta, xmat, ymat, zmat)
glEnd()
wd = 800
ht = 800
mouseX = wd/2
mouseY = ht/2

brake = 500.

def display():
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glMatrixMode(GL_MODELVIEW)
glMultMatrixf(aff)
drawsurf()
glutSwapBuffers()

#typical keyboard callback
def keyboard(key, x, y):
if key == chr(27) or key == 'q':
sys.exit(0)
glutPostRedisplay()

#adjust to resizing of the window
def reshape(width, height):
global wd
global ht
glClearColor(0.0, 0.0, 0.0, 0.0)
if height == 0:
height = 1
wd = width
ht = height
glViewport(0,0,wd,ht)
glMatrixMode(GL_PROJECTION)
if wd<=ht:
glOrtho(-nrange,nrange,-nrange*ht/wd,nrange*ht/wd,-nrange,nrange)
else:
glOrtho(-nrange*wd/ht,nrange*wd/ht,-nrange,nrange,-nrange,nrange)
glMatrixMode(GL_MODELVIEW)

#Note that we must declare the globals again
def chaptrack():
global mouseX
global mouseY
global wd
global ht
global aff
dx = (mouseX-wd/2)/brake
if dx*dx < .1:
dx =0
dy = (mouseY-ht/2)/brake;
if dy*dy < .1:
dy =0
glMatrixMode(GL_TEXTURE)
glPushMatrix()
glRotatef(dx,0,1.0,0.0)
glRotatef(dy,1.0,0.0,0.0)
glMultMatrixf(aff)
aff = glGetFloatv(GL_TEXTURE_MATRIX)
glPopMatrix()

def idle():
chaptrack()
glutPostRedisplay()

def mousemotion(x,y):
global mouseX
global mouseY
mouseX = x
mouseY = y

def init():
glEnable(GL_DEPTH_TEST)

def main() :
global wd
global ht
glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE)
glutInitWindowPosition(50, 50)
glutInitWindowSize(wd, ht)
glutInit([])
glutCreateWindow("illiTorus")
glutKeyboardFunc(keyboard)
glutDisplayFunc(display)
glutIdleFunc(idle)
glutReshapeFunc(reshape)
glutPassiveMotionFunc(mousemotion)

init()
glutMainLoop()

if __name__=="__main__":
main()