# rotate a tesseract projected to 3-space # 5/2004 Wolfgang.Urban@schule.at # work in progress... useIDLE = 0 # colors of cubes (timecube,realcube,connectioncube) e_colors = [0,"#8080ff","#ff0000","#70d090"] # delay for animation showdelay = 20 #50 # centric projection distance dist = 7 # background of canvas if necessary canvascolor = "#ffffff" #None # "#rrggbb" # color for selected buttons buttoncolor = "#c080a0" from Tkinter import * import math import copy class HCube: def __init__(self): self.corners = [[-1.0,-1.0,-1.0,-1.0],[ 1.0,-1.0,-1.0,-1.0], [-1.0, 1.0,-1.0,-1.0],[ 1.0, 1.0,-1.0,-1.0], [-1.0,-1.0, 1.0,-1.0],[ 1.0,-1.0, 1.0,-1.0], [-1.0, 1.0, 1.0,-1.0],[ 1.0, 1.0, 1.0,-1.0], [-1.0,-1.0,-1.0, 1.0],[ 1.0,-1.0,-1.0, 1.0], [-1.0, 1.0,-1.0, 1.0],[ 1.0, 1.0,-1.0, 1.0], [-1.0,-1.0, 1.0, 1.0],[ 1.0,-1.0, 1.0, 1.0], [-1.0, 1.0, 1.0, 1.0],[ 1.0, 1.0, 1.0, 1.0]] self.edges = [(0,1),(1,3),(3,2),(2,0), # bottom1 (4,5),(5,7),(7,6),(6,4), # top1 (0,4),(1,5),(3,7),(2,6), # sides1 up (8,9),(9,11),(11,10),(10,8), # bottom2 (12,13),(13,15),(15,14),(14,12), # top2 (8,12),(9,13),(11,15),(10,14), # sides2 up (0,8),(1,9),(3,11),(2,10), # connect 1,2 (4,12),(5,13),(7,15),(6,14)] self.edgecolors=[e_colors[1]]*12+[e_colors[2]]*12+[e_colors[3]]*12 self.points = copy.deepcopy(self.corners) self.perspective=0 def resetcube(self): self.points = copy.deepcopy(self.corners) ## print "cube resetted!" def setproj(self,params): self.ax0,self.ax1,self.ax2 = params ## print "Proj.",self.ax0,self.ax1,self.ax2 def setrot(self,params): self.rot1,self.rot2 = params ## print "Rot",self.rot1,self.rot2 def setangle(self,deg): self.angle = deg ## print "Angle:",self.angle def setperspective(self,value): self.perspective = value ## print "Perspective:",value def rotate(self,direction): w = direction*self.angle*math.pi/180.0 co,si = math.cos(w), math.sin(w) ax1,ax2 = self.rot1,self.rot2 for c in self.points: c[ax1],c[ax2] = co*c[ax1]-si*c[ax2],si*c[ax1]+co*c[ax2] def project(self): ax0,ax1,ax2 = self.ax0,self.ax1,self.ax2 p = [0]*len(self.points) # project points if self.perspective: for i in range(len(p)): f = dist/(dist-self.points[i][ax0]+1) p[i]=(f*self.points[i][ax1],f*self.points[i][ax2],self.points[i][ax0]) else: for i in range(len(p)): p[i]=(self.points[i][ax1],self.points[i][ax2],self.points[i][ax0]) e = [0]*len(self.edges) for i in range(len(e)): a,b = self.edges[i] e[i] = (p[a][0],p[a][1],p[b][0],p[b][1],self.edgecolors[i],(p[a][2]+p[b][2])/2.0) # insert sort for stelle in range(1,len(e)): wert = e[stelle] i = stelle while 1: if i==0: break if e[i-1][5]<=wert[5]: break e[i]=e[i-1] i = i-1 e[i] = wert return e class ProjectionFrame(Frame): def __init__(self,parent,hypercube, hcanvas, axname,**options): Frame.__init__(self,parent,**options) self.hcube = hypercube self.hcanvas = hcanvas Label(self,text="Projection\naxis to plane", font=("Arial",10)).pack(side=TOP,fill=X,padx=5,pady=10) self.projvar = IntVar() self.projvar.set(1) i=1 for p in [(2,0,1),(1,2,0),(0,1,2),(3,0,1),(3,2,0),(3,1,2), (2,0,3),(2,1,3),(1,0,3),(1,2,3),(0,1,3),(0,2,3)]: t = axname[p[0]]+"->"+axname[p[1]]+axname[p[2]] Radiobutton(self,text=t,font=("Courier",8),variable=self.projvar, value=i, indicatoron=0,selectcolor=buttoncolor, command=lambda par=p: self.projjob(par) ).pack(side=TOP,padx=15) if i in [3,6,12]: Label(self,text=" ", font=("Arial",1)).pack(side=TOP,pady=0) i += 1 self.perspvar = IntVar() self.perspvar.set(2) Radiobutton(self,text="parallel",font=("Arial",10),variable=self.perspvar, value=1, indicatoron=0,selectcolor=buttoncolor, command=lambda par=0: self.perspjob(par) ).pack(side=TOP,padx=2,fill=X) Radiobutton(self,text="centric",font=("Arial",10),variable=self.perspvar, value=2, indicatoron=0,selectcolor=buttoncolor, command=lambda par=1: self.perspjob(par) ).pack(side=TOP,padx=2,fill=X) def projjob(self,values): self.hcube.setproj(values) self.hcanvas.show() def perspjob(self,values): self.hcube.setperspective(values) self.hcanvas.show() class RotationFrame(Frame): def __init__(self,parent,hypercube,hcanvas, axname,**options): Frame.__init__(self,parent,**options) self.parent = parent self.hcube = hypercube self.hcanvas = hcanvas Label(self,text="Rotation\nin plane", font=("Arial",10)).pack(side=TOP,fill=X,padx=5,pady=10) self.rotplane = IntVar() self.rotplane.set(3) for value,pars in [(1,(0,1)),(2,(1,2)),(3,(2,0)), (4,(0,3)),(5,(1,3)),(6,(2,3))]: t = axname[pars[0]]+axname[pars[1]] Radiobutton(self,text=t,font=("Arial",10),variable=self.rotplane, value=value, indicatoron=0,selectcolor=buttoncolor, command=lambda par=pars: self.rotjob(par) ).pack(side=TOP,fill=X,padx=5) Label(self,text=" ",font=("Arial",20)).pack(side=TOP,fill=X,padx=5,pady=20) self.killer = Button(self,text="Q\nU\nI\nT",font=("Arial",16), command=self.quit,padx=4,pady=5) self.killer.pack(side=TOP,fill=X,padx=5) def rotjob(self,values): self.hcube.setrot(values) def quit(self): self.hcanvas.setjob(0) self.parent.after(200) ## print "Finished!\n" self.parent.destroy() class CommandFrame(Frame): def __init__(self,parent,hypercube, hcanvas, axname,**options): Frame.__init__(self,parent,**options) self.parent = parent self.hcube = hypercube self.hcanvas = hcanvas self.cubereset = Button(self,text="reset\ncube",font=("Arial",10), command=self.resetjob,padx=4,pady=2) self.cubereset.pack(side=LEFT,fill=Y,pady=1,padx=4) self.tempo = Scale(self,orient=HORIZONTAL, from_=1, to_=15, length=100,showvalue=1,repeatinterval=200, command=self.degreeset) self.tempo.pack(side=LEFT,padx=20,pady=5) Label(self,text=" ",font=("Arial",10)).pack(side=LEFT,fill=X,padx=2,pady=10) self.runback = Button(self,text="<<",font=("Arial",12), command=lambda par=-2: self.turnjob(par),padx=4,pady=4) self.runback.pack(side=LEFT,fill=Y,padx=2,pady=5) self.back = Button(self,text="<",font=("Arial",12), command=lambda par=-1: self.turnjob(par),padx=4,pady=4) self.back.pack(side=LEFT,fill=Y,padx=2,pady=5) self.stop = Button(self,text="stop",font=("Arial",12), command=lambda par=0: self.turnjob(par),padx=4,pady=4) self.stop.pack(side=LEFT,fill=Y,padx=2,pady=5) self.step = Button(self,text=">",font=("Arial",12), command=lambda par=1: self.turnjob(par),padx=4,pady=4) self.step.pack(side=LEFT,fill=Y,padx=2,pady=5) self.run = Button(self,text=">>",font=("Arial",12), command=lambda par=2: self.turnjob(par),padx=4,pady=4) self.run.pack(side=LEFT,fill=Y,padx=2,pady=5) Label(self,text=" ",font=("Arial",10)).pack(side=LEFT,fill=X,padx=5,pady=10) Label(self,text="Hypercube\nby\nWolfgang.Urban@schule.at",font=("Arial",8), fg="#4040ff").pack(side=LEFT,fill=X,padx=5,pady=1) def degreeset(self,event): self.hcube.setangle(self.tempo.get()) def turnjob(self,param): self.hcanvas.setjob(param) def resetjob(self): self.hcube.resetcube() self.hcanvas.setjob(10) self.hcanvas.show() class HyperCanvas(Canvas): def __init__(self,parent,hcube,numx,numy): Canvas.__init__(self) self.running = 0 self.hcube=hcube self.OFFS = 5 self.numx, self.numy = numx, numy self.center = numx/2 self.scale = numx/2.0*0.5 if canvascolor: self.cv = Canvas(parent,width=self.numx+7,height=self.numy+7, background=canvascolor) else: self.cv = Canvas(parent,width=self.numx+7,height=self.numy+7) self.cv.grid(row=1,column=1,padx=3,pady=3) self.lines=[0]*len(hcube.edges) for i in range(len(self.lines)): self.lines[i]=self.cv.create_line(0,0,0,0,width=2) def setjob(self,code): self.job = code ## print "Job:",self.job self.dothejob(code) def update(self): self.cv.update() def stop(self): self.running = 0 def dothejob(self,code): if code==0: self.stop() self.cv.after(100) self.cv.update() elif code==1: self.running=0 self.hcube.rotate(1) self.show() elif code==-1: self.running=0 self.hcube.rotate(-1) self.show() elif code==2: self.running=1 while self.running: self.hcube.rotate(1) self.show() self.after(showdelay) elif code==-2: self.running=1 while self.running: self.hcube.rotate(-1) self.show() self.after(showdelay) elif code==10: self.show() def show(self): ## print "Showing!" lines = self.hcube.project() for i in range(len(lines)): x0,y0 = lines[i][0]*self.scale,lines[i][1]*self.scale x1,y1 = lines[i][2]*self.scale,lines[i][3]*self.scale c = lines[i][4] self.cv.coords(self.lines[i],int(0.5+self.center+x0),int(0.5+self.center-y0), int(0.5+self.center+x1),int(0.5+self.center-y1)) self.cv.itemconfigure(self.lines[i],fill=c) self.cv.update() class CubeShow: def __init__(self,axname): hypercube = HCube() self.root = Tk() self.root.title("A 4D adventure in 3D land projected to a 2D screen") Label(self.root,text="T E S S E R A C T",font=("Arial",24)). \ grid(row=0,column=0,columnspan=3,pady=3) canvas = HyperCanvas(self.root,hypercube,500,500) frame_left = ProjectionFrame(self.root, hypercube, canvas,axname, relief=FLAT, borderwidth=2) frame_left.grid(row=1,column=0,padx=3,pady=3,sticky=N) frame_right = RotationFrame(self.root,hypercube,canvas, axname, relief=FLAT, borderwidth=2) frame_right.grid(row=1,column=2,padx=3,pady=3,sticky=N) frame_bottom = CommandFrame(self.root,hypercube, canvas, axname,relief=SUNKEN, borderwidth=2) frame_bottom.grid(row=2,column=0,columnspan=3,pady=6) self.root.after(250) self.root.update() hypercube.setangle(1) hypercube.resetcube() frame_left.projjob((2,0,1)) # z->xy frame_left.perspjob(1) # centric frame_right.rotjob((2,0)) # rotate in yz frame_bottom.turnjob(0) # stop frame_bottom.turnjob(2) # stop if not useIDLE: self.root.mainloop() x = CubeShow("xyzt") #x = CubeShow("1234")