from visual import *
from random import random

import time,sys,thread

spinSphereW = display(title='Spin Sphere', width=720, height=600, x=20, y=20, center=(0,0,0), background=(.05,.08,.08), visible=1)
spinSphereW.fov = pi/900.0
spinSphereW.lights = [vector(.5,.5,.5), vector(-.5,-.5,-.5)]

spinSphereW.up = (0,0,1)
spinSphereW.forward = (0.0,1.0,-1.0)
spinSphereW.range = 1.414
spinSphereW.autoscale = 0

spinSphereW.select()


equatorLine = curve(x=cos(arange(0,2.1*pi,.1565)), y=sin(arange(0,2.1*pi,.1565)), color=(.5,.5,.5))

##zAxis = []
##for x in arange(-1.4,1.4+.1,1.4):
##    zAxis.append(x*norm(vector(0.0,0.0,1.0)))
##zAxisLine = curve(pos=zAxis, color=(.8,.8,.8))
   


class Tracer:
    tracerLength = 32
    
    tracerPoints = []
    tracerCurve = curve()

    def __init__(self, tracerLength):
        self.tracerLength = tracerLength
        
        tracerPoints = [ None ] * tracerLength              # a vector of null elements, nSpinsX long
        for x in range(tracerLength):
            tracerPoints[x] = [0.0] * 3                     # a vector of zeros nSpinsY long

        self.tracerPoints = tracerPoints
        self.tracerCurve.pos = self.tracerPoints

        
    def stepTracer(self, updatePosition):
        del self.tracerPoints[0]
        self.tracerPoints.append(updatePosition)
        self.tracerCurve.pos = self.tracerPoints

    def delTracer(self):
        for i in range(self.tracerLength):
            self.tracerPoints[i] = [0.0,0.0,0.0]  
        self.tracerCurve.pos = self.tracerPoints

    def getTracer(self):
        return self.tracerPoints




class Spin:
    tracerLength = 32

    tracer = Tracer(tracerLength)
    point = sphere(visible=0)
    vector = arrow(visible=0)
    
    def __init__(self, tracerLength):
        self.tracerLength = tracerLength

        self.tracer = Tracer(tracerLength)
        self.point = sphere(visible=0)
        self.vector = arrow(visible=0)
                
    def delTracer(self):
        self.tracer.delTracer()

    def getTracer(self):
        return self.tracer



 
class VisualSphere:
    visualArray = []

    tracerLength = 32

    toggleTracersVar = 1
    togglePointsVar = 0
    toggleVectorsVar = 0

    toggleAxisVar = 1

    spinWaveAxis = []           
    spinWaveAxisLine = curve(color=(.7,.7,.9))
    

    
    def __init__(self, nSpinsX, nSpinsY, tracerLength):
        self.nSpinsX = nSpinsX
        self.nSpinsY = nSpinsY

        visualArray = [ None ] * nSpinsX                               # a vector of null elements, nSpinsX long
        for x in range(nSpinsX):
            visualArray[x] = [0] * nSpinsY                             # a vector of zeros nSpinsY long
            for y in range(nSpinsY):
                visualArray[x][y] = Spin(tracerLength)
               
        self.visualArray = visualArray



    def addTracers(self,numericArray):
        for x in range(self.nSpinsX):
            for y in range(self.nSpinsY):
                self.visualArray[x][y].tracer.tracerCurve = curve(color=(1-((x+y)%2),.3*(x+y+2.0)/((x+1.0)*(y+1.0)),((x+y)%2)))

    def updateTracers(self,numericArray):
        for x in range(self.nSpinsX):
            for y in range(self.nSpinsY):
                self.visualArray[x][y].getTracer().stepTracer(numericArray[x][y].spin)
                
    def resetTracers(self):
        for x in range(self.nSpinsX):
            for y in range(self.nSpinsY):
                self.visualArray[x][y].delTracer()
                
    def toggleTracers(self):
        self.toggleTracersVar = (self.toggleTracersVar+1)%2
        
        for x in range(self.nSpinsX):
            for y in range(self.nSpinsY):
                self.visualArray[x][y].tracer.tracerCurve.visible = self.toggleTracersVar             


##    def changeTracerLength(self, tracerLength):
##        for x in range(self.nSpinsX):
##            for y in range(self.nSpinsY):
##                self.visualArray[x][y].tracer.




    def addPoints(self,numericArray):
        for x in range(self.nSpinsX):
            for y in range(self.nSpinsY):
                self.visualArray[x][y].point = sphere(pos=(0.0,0.0,0.0), color=(1-((x+y)%2),.3*(x+y+2)/((x+1)*(y+1)),((x+y)%2)), radius=0.03, visible=self.togglePointsVar)

    def updatePoints(self,numericArray):
        for x in range(self.nSpinsX):
            for y in range(self.nSpinsY):
                self.visualArray[x][y].point.pos = numericArray[x][y].spin

    def togglePoints(self):
        self.togglePointsVar = (self.togglePointsVar+1)%2
        
        for x in range(self.nSpinsX):
            for y in range(self.nSpinsY):
                self.visualArray[x][y].point.visible = self.togglePointsVar



    def addVectors(self,numericArray):
        for x in range(self.nSpinsX):
            for y in range(self.nSpinsY):
                self.visualArray[x][y].vector = arrow(pos=(0.0,0.0,0.0), axis=numericArray[x][y].spin, color=(1-((x+y)%2),.3*(x+y+2.0)/((x+1.0)*(y+1.0)),((x+y)%2)), shaftwidth=0.025, visible=self.toggleVectorsVar)

    def updateVectors(self,numericArray):
        for x in range(self.nSpinsX):
            for y in range(self.nSpinsY):
                self.visualArray[x][y].vector.axis = numericArray[x][y].spin

    def toggleVectors(self):
        self.toggleVectorsVar = (self.toggleVectorsVar+1)%2
        
        for x in range(self.nSpinsX):
            for y in range(self.nSpinsY):
                self.visualArray[x][y].vector.visible = self.toggleVectorsVar



 
    def updateAxis(self, axis):
        self.spinWaveAxis = [-1.5*axis,1.5*axis]
        self.spinWaveAxisLine.pos = self.spinWaveAxis

        
    def updateViewAxis(self, axis):
        spinSphereW.forward = axis


    def toggleAxis(self):
        self.spinWaveAxis.visible = (self.toggleAxisVar+1)%2


                
##    def resetVisuals(self):
##        for x in range(self.nSpinsX):
##            for y in range(self.nSpinsY):
##                self.visualArray[x][y].tracer.visible = 0
##                self.visualArray[x][y].point.visible = 0
##                self.visualArray[x][y].vector.visible = 0

        

     
    def getArray(self):                         # handy, since we dont want to have to know the name of the object's actual variable!
        return self.visualArray





























