# 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):
      glBegin(GL_QUAD_STRIP)
      for ta in range (0,360+dangle, dangle):
         drawvert(th,ta, xmat, ymat, zmat)
         drawvert(th+dgap,ta, xmat, ymat, zmat)
      glEnd()
#assign initial window and mouse settings
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)
   glLoadIdentity()
   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)
   glLoadIdentity()
   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)
   glLoadIdentity()

#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()
   glLoadIdentity()
   glRotatef(dx,0,1.0,0.0)
   glRotatef(dy,1.0,0.0,0.0)
   glMultMatrixf(aff)
   aff = glGetFloatv(GL_TEXTURE_MATRIX)
   glPopMatrix()

#traditional idle
def idle():
   chaptrack()
   glutPostRedisplay()

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

def init():
   glEnable(GL_DEPTH_TEST)
   glShadeModel(GL_SMOOTH)

#Traditional main subroutine
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()