#! /usr/bin/python # -*- coding: utf-8 -*- # kochenspecker.py # Version 2.2, September 2009 # Author: Jan-Åke Larsson ######################################################################## # This program draws three-dimensional sets of vectors and lets you # try coloring them according to the rule "Of the two vectors in an # orthonormal set of three, two should be red and one should be green" ######################################################################## # Copyright (C) Jan-Åke Larsson 2003, 2008, 2009 ######################################################################## # Usage: Run it using python (with the wxPython extension), usually by # typing 'python kochenspecker.py' or make it executable, put it in # your PATH and run it. Left-click colors a vector green, right-click # or shift-left-click colors it red. # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # Resolution (you may change this) resolution=700 #----------------------------------------------------------------------- from math import sqrt,cos,sin,tan,pi from pprint import saferepr from string import replace import re import wx ######################################################################## # Vector algebra ######################################################################## class Vector(list): def __init__(self,values,extra=''): self.green=False self.red=False self.set=False self.showgc=False self.extra=extra self.ON=[] self.dotvectors=[] self.dotvectors2=[] self.gcvectors=[] list.__init__(self,values) def __add__(self,other): return Vector(map(lambda x,y:x+y,self,other)) def __mul__(self,other): if type(other)==Vector or type(other)==list: return sum(map(lambda x,y: x*y, self,other)) else: return Vector(map(lambda x: other*x, self)) def __rmul__(self,other): return self*other def __neg__(self): return -1*self def __sub__(self,other): return self+(-other) def cross(self,other): return Vector([self[1]*other[2]-self[2]*other[1], self[2]*other[0]-self[0]*other[2], self[0]*other[1]-self[1]*other[0]]) def norm(self): return sqrt(self*self) def normalized(self): return 1/self.norm()*self def outward(self): if self*ScreenNormal>0: return self else: return -self def rotate(self,start,stop): # start and stop should both be normalized # self=xX+zZ # result=x(cosvX+sinvY)+zZ sinvZ=start.cross(stop) sinvZsinvZ=sinvZ*sinvZ if sinvZsinvZ<1e-7: return self cosv=start*stop zZ=(self*sinvZ)/sinvZsinvZ*sinvZ xX=self-zZ xsinvY=sinvZ.cross(xX) return cosv*xX+xsinvY+zZ def mirr_y(self): return Vector([self[0],-self[1],self[2]]) def __str__(self): return "("+",".join(map(lambda x:"%.4f"%x,self))+")" def greatc_basis(self): x=self.cross([0,0,1]) if x.norm()<1e-6: x=self.cross([1,0,0]) return x.normalized(),self.cross(x.normalized()) def draw(self,dc): size=0.06 #if self.extra: # size=0.02 # if self.suggested: # color=self.suggested if not self.dotvectors: a,b=self.greatc_basis() for i in range(0,360,5): self.dotvectors.append(self +size*(cos(i*pi/180)*a +sin(i*pi/180)*b)) self.dotvectors2.append(-self +size*(cos(i*pi/180)*a +sin(i*pi/180)*b)) coords=[] if self*ScreenNormal>0: for v in self.dotvectors: coords.append(project(v)) else: for v in self.dotvectors2: coords.append(project(v)) dc.SetPen(wx.BLACK_PEN) dc.SetBrush(wx.BLACK_BRUSH) if not self.set and frame.Panel.options.Hints.GetValue(): if self.green or self.red: dc.DrawPolygon(coords) coords=coords[0:18]+coords[54:36:-1] #if self.green and self.red: # dc.SetBrush(wx.CROSSYELLOW_BRUSH) #elif self.green: # dc.SetBrush(wx.CROSSGREEN_BRUSH) #elif self.red: # dc.SetBrush(wx.CROSSRED_BRUSH) if self.green and self.red: dc.SetBrush(wx.YELLOW_BRUSH) elif self.green: dc.SetBrush(wx.GREEN_BRUSH) elif self.red: dc.SetBrush(wx.RED_BRUSH) dc.DrawPolygon(coords) def drawgc(self,dc): if not self.gcvectors: a,b=self.greatc_basis() for i in range(0,365,5): self.gcvectors.append(cos(i*pi/180)*a +sin(i*pi/180)*b) coords=[] endcoords=[] for v in self.gcvectors: if v*ScreenNormal>=0: coords.append(project(v)) else: if not endcoords: endcoords=coords coords=[] if coords+endcoords: if self.set: dc.SetPen(wx.WIDERED_PEN) else: dc.SetPen(wx.WIDEDOTTEDRED_PEN) dc.DrawLines(coords+endcoords) def suggestcolor(self,suggested,setcolor=False): change=False if suggested=='green' and not self.green: self.green=True change=True elif suggested=='red' and not self.red: self.red=True change=True if not setcolor: setcolor=frame.Panel.options.Automatic.GetValue() if setcolor and not self.set: self.set=True change=True if change and self.set: if self.green: if frame.Panel.options.GC.GetValue(): self.showgc=True for v in self.ON: v.suggestcolor('red') # "elif": prohibit excessive yellow coloring elif self.red: for v in self.ON: if v.set and v.red: for w in v.ON: if w in self.ON: w.suggestcolor('green') if frame.Panel.options.GC.GetValue(): w.showgc=True def setcolor(self,color): if color=='green' and self.red: color='red' elif color=='red' and self.green: color='green' self.suggestcolor(color,True) def setsuggested(self): self.suggestcolor(None,True) def getsuggestions(self): # Fetch suggestion from ON list. if not self.set: self.green=False self.red=False for v in self.ON: if v.set: if v.green: self.suggestcolor('red') elif v.red: for w in v.ON: if w.set and w.red and w in self.ON: self.suggestcolor('green') def clear(self): self.green=False self.red=False self.set=False self.showgc=False self.getsuggestions() # Check ON vectors if _their_ suggestions disappear. If # green suggestion disappears, check vectors in _its_ # greatcircle. for u in self.ON: wasgreen=u.green u.getsuggestions() if wasgreen and not u.green: u.showgc=False for v in u.ON: v.getsuggestions() def setgcdots(self): if self.green: for v in self.ON: v.setsuggested() def showONgc(self): if self.red: for v in self.ON: if v.red: for u in v.ON: if u in self.ON: u.showgc=True class Vectorlist(list): def append(self,item): if type(item)==list: item=Vector(item) item=item.normalized() item.tag="tag"+str(len(self)) found=False for v in self: if abs(v*item)>1-1E-7: found=True break if not found: for v in self: if abs(item*v)<1E-7: v.ON.append(item) item.ON.append(v) list.append(self,item) def extend(self,vectorlist): for i in vectorlist: self.append(i) def draw(self,dc): for v in self: if v.showgc and v.green: v.drawgc(dc) for v in self: v.draw(dc) def clear(self): for v in self: v.green=False v.red=False v.set=False v.showgc=False def setsuggested(self): for v in self: v.setsuggested() origo=Vector([0,0,0]) x_hat=Vector([1,0,0]) y_hat=Vector([0,1,0]) z_hat=Vector([0,0,1]) ######################################################################## # Vector sets ######################################################################## def test(): test=Vectorlist() test.append([0,0,1]) test.append([0,1,0]) a=Vector([sin(pi/6),0,cos(pi/6)]) for i in range(-10,10): v=a*cos(i*pi/2/10)+y_hat*sin(i*pi/2/10) test.append(v) u=v.cross([0,0,1]) test.append(u) test.append(v.cross(u)) if abs(i)==2: #print v for j in range(1,5): w=-cos(j*pi/2/5)*v+sin(j*pi/2/5)*u test.append(w) s=w.cross([0,0,1]) test.append(s) test.append(s.cross(w)) if j==2: #print v for k in range(1,5): t=-cos(k*pi/2/5)*w+sin(k*pi/2/5)*s test.append(t) p=t.cross([0,0,1]) test.append(p) test.append(p.cross(t)) v=Vector([-1,2,1]) test.append(v) test.append(v.cross([1,0,0])) test.append(x_hat.cross(v.cross([1,0,0]))) return test def test(): test=Vectorlist() test.append([0,0,1]) test.append([0,1,0]) a=Vector([sin(pi/8),0,cos(pi/8)]) test.append(a) test.append(a.cross(y_hat)) cosphi=cos(pi/10)#sqrt(3.0/4+sqrt(9.0/16-1.0/2/cos(pi/10)/cos(pi/10))) #print cosphi,1/2/cos(pi/10)/cos(pi/10) b=a*cosphi+y_hat*sqrt(1-cosphi**2) test.append(b) c=b.cross([0,0,-1]).normalized() test.append(c) test.append(b.cross(c)) bb=(1/sqrt(2)/cos(pi/8)/cos(pi/10)) d=b*bb+c*sqrt(1-bb*bb) test.append(d) e=d.cross([0,0,1]) test.append(e) test.append(e.cross(d)) bp=Vector([b[0],-b[1],b[2]]) cp=Vector([c[0],-c[1],c[2]]) test.append(bp) test.append(cp) test.append(bp.cross(cp)) #print b,c,d return test def test4(): test=Vectorlist() test.append([0,0,1]) # test.append([1,0,1]) test.append([0,1,0]) test.append([-1,0,1]) # test.append([1,1,1]) test.append([-1,1,0]) test.append([1,1,-2]) # test.append([0,2,1]) test.append([1,0,0]) test.append([0,-1,2]) # test.append([-1,sqrt(2),1]) ## test.append([0,-1,0]) # test.append([1,-1,1]) test.append([-1,-1,0]) test.append([1,-1,-2]) # test.append([0,-2,1]) test.append([0,1,2]) test.append([-1,-sqrt(2),1]) return test def peres(): #### Peres 33-vector set # There are 33 vectors in this set. # There are also 24 "unmatched" pairs, # and 16 triads. # # Values are chosen so that we get a unit cube # That is, a factor 1/sqrt(2) to those given in Peres book values = [-1/sqrt(2), 0, 1/sqrt(2)] peres=Vectorlist() for x in values: for y in values: peres.append([x,y,1]) peres.append([x,1,y]) peres.append([1,x,y]) peres.append([0,1,1]) peres.append([0,-1,1]) peres.append([1,0,1]) peres.append([-1,0,1]) peres.append([1,1,0]) peres.append([-1,1,0]) return peres def conwaykochen(): #### Conway and Kochen # There are 31 vectors in this set. # There are also 20 "unmatched" pairs, # and 17 triads. values = [-1.0/2.0, 0, 1.0/2.0] conwaykochen=Vectorlist() for x in values: for y in values: conwaykochen.append([-x,y,1]) if x!= 1.0/2.0: conwaykochen.append([x,1,y]) conwaykochen.append([-1,x,y]) conwaykochen.append([0,1,1]) conwaykochen.append([0,-1,1]) conwaykochen.append([1,0,1]) conwaykochen.append([-1,0,1]) conwaykochen.append([1,1,0]) conwaykochen.append([-1,1,0]) conwaykochen.append([1,1,1]) conwaykochen.append([-1,-1,1]) conwaykochen.append([1,-1,1]) conwaykochen.append([-1,1,1]) return conwaykochen def schutte(): #### Schütte # There are 33 vectors in this set. # There are also 16 "unmatched" pairs, # and 20 triads. values = [-1.0/2.0, 0, 1.0/2.0] schutte=Vectorlist() for x in values: for y in values: schutte.append([x,y,1]) if x==0 or y!=0: schutte.append([x,1,y]) schutte.append([1,x,y]) schutte.append([0,1,1]) schutte.append([0,-1,1]) schutte.append([1,0,1]) schutte.append([-1,0,1]) schutte.append([1,1,0]) schutte.append([-1,1,0]) schutte.append([1,1,1]) schutte.append([-1,-1,1]) schutte.append([1,-1,1]) schutte.append([-1,1,1]) return schutte def ks_10(): # Solving the equation # cos(pi/10)^2*sin(phi)^4+sin(pi/10)^2 # -cos(pi/10)^2*sin(phi)^2*cos(phi)^2=0 # gives # sin(phi)^2=1/2/(10+2*sqrt(5))*(5+sqrt(5)+-sqrt(-50+26*sqrt(5))) x=sqrt(1/2.0/(10+2*sqrt(5))*(5+sqrt(5)-sqrt(-50+26*sqrt(5)))) # x=sqrt(1/2.0/(10+2*sqrt(5))*(5+sqrt(5)+sqrt(-50+26*sqrt(5)))) ks_10 = Vectorlist() ks_10.append([0,0,1]) ks_10.append([0,-sin(pi/10),cos(pi/10)]) ks_10.append([1,0,0]) ks_10.append([0,cos(pi/10),sin(pi/10)]) ks_10.append([-x*sqrt(1-x**2)*cos(pi/10), -cos(pi/10)*sin(pi/10)*(1-x**2), -sin(pi/10)**2-x**2*cos(pi/10)**2]) ks_10.append([ x*sqrt(1-x**2)*cos(pi/10), -cos(pi/10)*sin(pi/10)*(1-x**2), -sin(pi/10)**2-x**2*cos(pi/10)**2]) ks_10.append([-sqrt(1-x**2)*sin(pi/10),-x, 0]) ks_10.append([-sqrt(1-x**2)*sin(pi/10), x, 0]) ks_10.append([-x,-sqrt(1-x**2)*sin(pi/10),sqrt(1-x**2)*cos(pi/10)]) ks_10.append([ x,-sqrt(1-x**2)*sin(pi/10),sqrt(1-x**2)*cos(pi/10)]) return ks_10 def ks_41(): ks_41=Vectorlist() start=Vector([0,0,1]) stop=Vector([0,-sin(pi/10),cos(pi/10)]) ks=ks_10() for i in range(5): ks_41.extend(ks) ks=map(lambda x:x.rotate(start,stop),ks) #print "ks",len(ks_41) return ks_41 def ks_117(): ks_117=Vectorlist() ks=ks_41() for i in range(3): ks_117.extend(ks) ks=map(lambda v:Vector([v[1],v[2],v[0]]),ks) return ks_117 def new_12(): new=Vectorlist() new.append([0,0,1]) new.append([1,0,0]) # alpha=22.5*pi/180 a=Vector([0,-sin(alpha),cos(alpha)]) new.append(a) new.append([0,cos(alpha),sin(alpha)]) new.append([1,cos(alpha),sin(alpha)]) new.append([-1,cos(alpha),sin(alpha)]) cosphi=sqrt(1.0/2+sqrt(1.0/4-tan(alpha)*tan(alpha))) sinphi=sqrt(1.0/2-sqrt(1.0/4-tan(alpha)*tan(alpha))) e=cosphi*a+sinphi*x_hat new.append(e) f=z_hat.cross(e) new.append(f) new.append(e.cross(f)) e=cosphi*a-sinphi*x_hat new.append(e) f=z_hat.cross(e) new.append(f) new.append(e.cross(f)) #print len(new) return new def new_41(): new_ninety=Vectorlist() new=new_12() start=Vector([0,0,1]) stop=Vector([0,-sin(pi/8),cos(pi/8)]) for i in range(4): new_ninety.extend(new) new=map(lambda x:x.rotate(start,stop),new) #print len(new_ninety) return new_ninety def new_111(): new_111=Vectorlist() new=new_41() for i in range(3): new_111.extend(new) new=map(lambda v:Vector([v[1],v[2],v[0]]),new) #print len(new_111) return new_111 ######################################################################## # Drawing ######################################################################## scale=0.9 def project(vector): x=(1.0+scale*ScreenX*vector)*resolution/2 y=(1.0-scale*ScreenY*vector)*resolution/2 return x,y def iproject(x,y=None): if y==None: (x,y)=x v=(2.0*x/resolution-1)/scale*ScreenX-(2.0*y/resolution-1)/scale*ScreenY if v.norm()>1: return origo return (v+sqrt(1-v.norm()*v.norm())*ScreenNormal).normalized() class Grid(list): def __init__(self): self.bgcoords=[] for i in range(0,360,5): self.bgcoords.append(project(cos(i*pi/180)*ScreenX +sin(i*pi/180)*ScreenY)) self.gridvectors=[] # Latitude lines for i in range(15,180,15): item=[] for j in range(0,365,5): item.append(cos(i*pi/180)*z_hat +sin(i*pi/180)*(cos(j*pi/180)*x_hat +sin(j*pi/180)*y_hat)) self.gridvectors.append(item) # Longitude lines for j in range(0,180,15): item=[] for i in range(0,365,5): item.append(cos(i*pi/180)*z_hat +sin(i*pi/180)*(cos(j*pi/180)*x_hat +sin(j*pi/180)*y_hat)) self.gridvectors.append(item) def draw(self,dc): dc.SetBrush(wx.WHITE_BRUSH) dc.SetPen(wx.BLACK_PEN) dc.DrawPolygon(self.bgcoords) dc.SetPen(wx.BLUE_PEN) for item in self.gridvectors: coords=[] endcoords=[] for v in item: if v*ScreenNormal>=0: coords.append(project(v)) else: if not endcoords: endcoords=coords coords=[] if coords+endcoords: dc.DrawLines(coords+endcoords) ######################################################################## # export to postscript ######################################################################## def export(): global exportno filename=replace(exportbutton.get(), "Export to\n", "") left=project(Vector([1,-1,0]))[0]-3*resolution/200 right=project(Vector([-1,1,0]))[0]+3*resolution/200 top=project(Vector([-1,-1,1]))[1]-3*resolution/200 bottom=project(Vector([1,1,-1]))[1]+3*resolution/200 canvas.postscript(colormode='color',file=filename, x=left,y=top,width=right-left,height=bottom-top) exportno=exportno+1 exportbutton.set("Export to\nKochen-Specker"+saferepr(exportno)+".eps") def clearexportno(): global exportno exportno=1 exportbutton.set("Export to\nKochen-Specker"+saferepr(exportno)+".eps") ######################################################################## # main() ######################################################################## ScreenNormal=None class DataPanel(wx.Panel): def __init__(self,parent): wx.Panel.__init__(self,parent,size=(resolution,resolution)) #self.SetStatusText=parent.SetStatusText wx.EVT_PAINT(self, self.OnPaint) wx.EVT_MOUSE_EVENTS(self, self.OnMouse) self.irotate=wx.Timer(self) wx.EVT_TIMER(self, self.irotate.GetId(), self.IRotate) self.vectors=peres() self.exportFile="Peres1.eps" self.proof=[] self.loadedproof=[] self.Reset() self.grid=Grid() self.MouseDownAt=None self.irotating=False def Redraw(self,dc=None,setbg=True): if dc==None: dc=wx.BufferedDC(wx.ClientDC(self)) if setbg: dc.SetBackground(wx.Brush(wx.Colour(220,218,213))) dc.Clear() self.grid.draw(dc) self.vectors.draw(dc) def OnPaint(self, event=None): self.Redraw(wx.AutoBufferedPaintDC(self)) def Clear(self): self.vectors.clear() self.proof=["rotate %f %f %f %f %f %f"% (ScreenNormal[0],ScreenNormal[1],ScreenNormal[2], ScreenX[0],ScreenX[1],ScreenX[2])] self.Redraw() def IRotate(self,event=None): global ScreenNormal, ScreenX, ScreenY self.irotating=True while app.Pending(): app.Yield() self.irotating=False if (not self.loadedproof or not self.loadedproof[0][0:6]=="rotate"): return step=0.03 while (len(self.loadedproof)>1 and self.loadedproof[1][0:6]=="rotate"): self.loadedproof.pop(0) item=self.loadedproof[0].split() NewNormal=Vector([float(item[1]),float(item[2]),float(item[3])]) NewX=Vector([float(item[4]),float(item[5]),float(item[6])]) RotN=NewNormal-ScreenNormal RotX=NewX-ScreenX if RotN.norm()step: RotN=step/RotN.norm()*RotN if RotX.norm()>step: RotX=step/RotX.norm()*RotX ScreenNormal=(ScreenNormal+RotN).normalized() ScreenX=(ScreenX+RotX).normalized() ScreenX=(ScreenX-(ScreenX*ScreenNormal)*ScreenNormal).normalized() self.proof.append("rotate %f %f %f %f %f %f"% (ScreenNormal[0],ScreenNormal[1],ScreenNormal[2], ScreenX[0],ScreenX[1],ScreenX[2])) ScreenY=ScreenNormal.cross(ScreenX) self.Redraw() self.irotate.Start(1,wx.TIMER_ONE_SHOT) def Reset(self): global ScreenNormal, ScreenX, ScreenY # The ScreenNormal is the one projected onto the origin phi=37.5 theta=45 NewNormal=Vector([sin(phi*pi/180)*sin(theta*pi/180), cos(phi*pi/180)*sin(theta*pi/180), cos(theta*pi/180)]) NewX=Vector([-cos(phi*pi/180), sin(phi*pi/180), 0]) if not ScreenNormal: ScreenNormal=NewNormal ScreenX=NewX ScreenY=ScreenNormal.cross(ScreenX) else: self.loadedproof.insert(0,"rotate %f %f %f %f %f %f"% (NewNormal[0],NewNormal[1],NewNormal[2], NewX[0],NewX[1],NewX[2])) self.IRotate() def OnMouse(self, event): global ScreenNormal, ScreenX, ScreenY action=None v=iproject(event.GetPosition()) if event.ButtonDown(): self.MouseDownAt=iproject(event.GetPosition()) if self.loadedproof: if self.loadedproof[0][0:6]=="rotate": if not self.irotating: self.IRotate() else: item=self.loadedproof.pop(0).split() action=item[0] v=Vector([float(item[1]),float(item[2]),float(item[3])]) for u in self.vectors: if v*u>sqrt(1-0.06*0.06): break else: for u in self.vectors: if v*u.outward()>sqrt(1-0.06*0.06): if event.LeftDown() and not event.ShiftDown(): if not u.set: action="setgreen" else: action="clear" elif event.RightDown() or (event.LeftDown() and event.ShiftDown()): if not u.set: action="setred" elif u.green and u.showgc: action="setgcdots" elif u.green: action="showgc" else: action="showONgc" break if not action: for u in self.vectors: if abs(v*u)<.02 and u.green: if u.set and u.showgc: if event.LeftDown() and not event.ShiftDown(): action="!showgc" elif event.RightDown() or (event.LeftDown() and event.ShiftDown()): action="setgcdots" else: action="setgreen" break self.MouseDownAt=v elif event.Dragging() and v.norm()>0: ScreenNormal=ScreenNormal.rotate(v,self.MouseDownAt) ScreenX=ScreenX.rotate(v,self.MouseDownAt) ScreenY=ScreenNormal.cross(ScreenX) self.proof.append("rotate %f %f %f %f %f %f"% (ScreenNormal[0],ScreenNormal[1],ScreenNormal[2], ScreenX[0],ScreenX[1],ScreenX[2])) self.Redraw() self.MouseDownAt=iproject(event.GetPosition()) if action: if action=="setgreen": u.setcolor('green') elif action=="setred": u.setcolor('red') elif action=="clear": u.clear() elif action=="setgcdots": u.setgcdots() elif action=="showgc": u.showgc=True elif action=="!showgc": u.showgc=False elif action=="showONgc": u.showONgc() self.Redraw() self.proof.append("%s %f %f %f"%(action,u[0],u[1],u[2])) class ButtonPanel(wx.Panel): def __init__(self, parent, *args, **kwargs): wx.Panel.__init__(self, parent, *args, **kwargs) self.parent = parent self.names=["Peres", "Conway+Kochen", "Schütte", "KS 18 degrees", "KS 5*18 degrees", "KS 117 vectors", #"New 22.5 degrees", #"New 4*22.5 degrees", #"New 111 vectors", #"Test", "Custom", ] self.onload=wx.Timer(self) self.sets=[peres()] # If I throw all the sets in at startup, there will be a delay # until the program is responsive. A timer will conveniently # wait until the main loop has finished displaying stuff and # will only then calculate the rest of the vectors. handler=lambda event: self.sets.extend([conwaykochen(), schutte(), ks_10(), ks_41(), ks_117(), #new_12(), #new_41(), #new_111(), #test(), Vectorlist(), ]) wx.EVT_TIMER(self, self.onload.GetId(),handler) self.onload.Start(1,wx.TIMER_ONE_SHOT) self.Choices = wx.RadioBox(self,choices=self.names, style=wx.VERTICAL, label="Avaliable sets") self.Choices.EnableItem(len(self.names)-1,False) #self.Choices.Check(0) self.Choices.Bind(wx.EVT_RADIOBOX, self.Choose ) self.Automatic = wx.CheckBox(self, label="Automatic",) self.Automatic.SetValue(False) self.Automatic.Bind(wx.EVT_CHECKBOX, self.CheckAuto ) self.Hints = wx.CheckBox(self, label="Hints") self.Hints.SetValue(True) #self.Hints.Disable() self.Hints.Bind(wx.EVT_CHECKBOX, self.parent.Redraw ) self.GC = wx.CheckBox(self, label="Greatcircles") self.GC.SetValue(True) self.GC.Bind(wx.EVT_CHECKBOX, self.parent.Redraw ) #MsgBtn = wx.Button(self, label="Send Message") #MsgBtn.Bind(wx.EVT_BUTTON, self.OnMsgBtn ) Subsizer = wx.StaticBox(self,wx.VERTICAL,label="Options") Subsizer = wx.StaticBoxSizer(Subsizer,wx.VERTICAL) Subsizer.Add(self.Automatic, 0, wx.ALIGN_LEFT|wx.ALL, 5) Subsizer.Add(self.Hints, 0, wx.ALIGN_LEFT|wx.ALL, 5) Subsizer.Add(self.GC, 0, wx.ALIGN_LEFT|wx.ALL, 5) Clear=wx.Button(self,label="Clear") Clear.Bind(wx.EVT_BUTTON, self.parent.Clear ) Reset=wx.Button(self,label="Reset Orientation") Reset.Bind(wx.EVT_BUTTON, self.parent.Reset ) Sizer = wx.BoxSizer(wx.VERTICAL) Sizer.Add(self.Choices, 0, wx.ALIGN_CENTER|wx.ALL, 5) Sizer.Add(Subsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5) Sizer.Add(Clear, 0, wx.ALIGN_CENTER|wx.ALL, 5) Sizer.Add(Reset, 0, wx.ALIGN_CENTER|wx.ALL, 5) self.SetSizerAndFit(Sizer) def CheckAuto(self,event=None): if self.Automatic.GetValue(): self.Hints.Disable() self.parent.data.vectors.setsuggested() else: self.Hints.Enable() self.parent.Redraw() def Choose(self,event=None): self.parent.data.vectors=self.sets[self.Choices.GetSelection()] self.parent.data.exportFile=self.names[self.Choices.GetSelection()]+"1.eps" self.parent.Clear() self.parent.Redraw() class FullPanel(wx.Panel): """This Panel hold two simple buttons, but doesn't really do anything.""" def __init__(self, parent, *args, **kwargs): """Create the DemoPanel.""" wx.Panel.__init__(self, parent, *args, **kwargs) self.parent = parent self.data = DataPanel(self) self.options = ButtonPanel(self) Sizer = wx.BoxSizer(wx.HORIZONTAL) Sizer.Add(self.data, 0, wx.ALIGN_CENTER|wx.ALL, 5) Sizer.Add(self.options, 0, wx.ALIGN_CENTER|wx.ALL, 5) self.SetSizerAndFit(Sizer) def Redraw(self, event=None): self.data.Redraw() def Clear(self,event=None): self.data.Clear() def Reset(self,event=None): self.data.Reset() class MyFrame(wx.Frame): """Main Frame holding the Panel.""" def __init__(self, *args, **kwargs): wx.Frame.__init__(self, *args, **kwargs) # Build the menu bar MenuBar = wx.MenuBar() FileMenu = wx.Menu() item = FileMenu.Append(wx.ID_EXIT, text="&Quit") self.Bind(wx.EVT_MENU, self.OnQuit, item) item = FileMenu.Append(0,text="&Open proof\tCtrl-O") self.Bind(wx.EVT_MENU, self.LoadProof, item) item = FileMenu.Append(1,text="&Save proof\tCtrl-S") self.Bind(wx.EVT_MENU, self.SaveProof, item) item = FileMenu.Append(2,text="Open custom &vector list\tCtrl-V") self.Bind(wx.EVT_MENU, self.LoadCustomVectorset, item) item = FileMenu.Append(3,text="&Print image\tCtrl-P") self.Bind(wx.EVT_MENU, self.OnDoPrint, item) item = FileMenu.Append(4,text="Page setup") self.Bind(wx.EVT_MENU, self.OnPageSetup, item) item = FileMenu.Append(5,text="&Export EPS\tCtrl-E") self.Bind(wx.EVT_MENU, self.OnExport, item) MenuBar.Append(FileMenu, "&File") self.SetMenuBar(MenuBar) # Add the Widget Panel self.Panel = FullPanel(self) self.Fit() self.printData = wx.PrintData() self.printData.SetPaperId(wx.PAPER_A4) self.printData.SetPrintMode(wx.PRINT_MODE_PRINTER) self.exportPrintData = wx.PrintData() self.exportPrintData.SetPaperId(wx.PAPER_A4) self.exportPrintData.SetPrintMode(wx.PRINT_MODE_FILE) def OnQuit(self, event=None): """Exit application.""" self.Close() def LoadProof(self, event=None): dlg=wx.FileDialog(self,message="Choose a file containing a KS proof", style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST|wx.FD_CHANGE_DIR) if dlg.ShowModal()==wx.ID_OK: proof=open(dlg.GetFilename()) self.Panel.data.loadedproof=proof.readlines() proof.close() name=self.Panel.data.loadedproof.pop(0) for i in range(len(self.Panel.options.names)): # Remove newline if self.Panel.options.names[i]==name[:-1]: self.Panel.options.Choices.SetSelection(i) self.Panel.options.Choose() if self.Panel.data.loadedproof[0][:6]=="rotate": self.Panel.data.IRotate() def SaveProof(self, event=None): dlg=wx.FileDialog(self,message="Save KS proof to a file", style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.FD_CHANGE_DIR) if dlg.ShowModal()==wx.ID_OK: proof=open(dlg.GetFilename(),"w") proof.write(self.Panel.options.names[self.Panel.options.Choices.GetSelection()]+"\n") for i in self.Panel.data.proof: proof.write(i+"\n") proof.close() def LoadCustomVectorset(self, event=None): dlg=wx.FileDialog(self,message="Choose a file containing a custom vector list", style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST|wx.FD_CHANGE_DIR) if dlg.ShowModal()==wx.ID_OK: name=dlg.GetFilename() customvectorset=open(dlg.GetFilename()) data=customvectorset.readlines() customvectorset.close() vectors=Vectorlist() for i in data: i=i.strip("\n") try: coords=map(float,i.split()) if len(coords)!=3: raise ValueError vectors.append(coords) except: if i and i==data[0].strip("\n"): name=i self.Panel.options.names[-1]=name self.Panel.options.Choices.SetItemLabel(len(self.Panel.options.names)-1,name) self.Panel.options.Choices.EnableItem(len(self.Panel.options.names)-1,True) self.Panel.options.sets[-1]=vectors def OnPageSetup(self, evt): psdd = wx.PageSetupDialogData(self.printData) psdd.CalculatePaperSizeFromId() dlg = wx.PageSetupDialog(self, psdd) dlg.ShowModal() # this makes a copy of the wx.PrintData instead of just saving # a reference to the one inside the PrintDialogData that will # be destroyed when the dialog is destroyed self.printData = wx.PrintData( dlg.GetPageSetupData().GetPrintData() ) dlg.Destroy() def OnDoPrint(self, event): pdd = wx.PrintDialogData(self.printData) printer = wx.Printer(pdd) printout = MyPrintout(self.Panel.data) if not printer.Print(self, printout, True): wx.MessageBox("""There was a problem printing. Perhaps your current printer is not set correctly?""", "Printing", wx.OK) else: self.printData = wx.PrintData( printer.GetPrintDialogData().GetPrintData() ) printout.Destroy() def OnExport(self, event): def incr(match): return str(int(match.group())+1) dlg=wx.FileDialog(self,message="Export image to EPS file", style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT|wx.FD_CHANGE_DIR, defaultFile=self.Panel.data.exportFile) if dlg.ShowModal()==wx.ID_OK: name=dlg.GetFilename() self.exportPrintData.SetFilename(name) dc = wx.PostScriptDC(self.exportPrintData) dc.StartDoc("") dc.ResetBoundingBox() self.Panel.data.Redraw(dc, False) llx=dc.MinX() lly=dc.MinY() urx=dc.MaxX() ury=dc.MaxY() dc.EndDoc() f=open(name,"r") lines=f.readlines() f.close() lines.insert(5,"%%%%BoundingBox: %d %d %d %d\n"%(llx-1,ury-(urx-llx),urx+1,ury+2)) f=open(name,"w") for i in lines: f.write(i) f.close() p = re.compile(r'\d+') self.Panel.data.exportFile=p.sub(incr,name) class MyPrintout(wx.Printout): def __init__(self, canvas): wx.Printout.__init__(self) self.canvas = canvas def HasPage(self, page): if page == 1: return True else: return False def GetPageInfo(self): return (1, 1, 1, 1) def OnPrintPage(self, page): dc = self.GetDC() # Get the size of the DC in pixels (w, h) = dc.GetSizeTuple() # Calculate a suitable scaling factor actualScale = float(w)*.8 / resolution # Calculate the position on the DC for centering the graphic posX = (w - (resolution * actualScale)) / 2.0 posY = (h - (resolution * actualScale)) / 2.0 # Set the scale and origin dc.SetUserScale(actualScale, actualScale) dc.SetDeviceOrigin(int(posX), int(posY)) #------------------------------------------- self.canvas.Redraw(dc, False) return True if __name__ == '__main__': app = wx.App() wx.BLUE_PEN=wx.Pen(wx.Colour(0,0,255)) wx.WIDERED_PEN=wx.Pen(wx.Colour(255,0,0),width=3) wx.WIDEDOTTEDRED_PEN=wx.Pen(wx.Colour(255,0,0),width=3,style=wx.SHORT_DASH) wx.CROSSGREEN_BRUSH=wx.Brush(wx.Colour(0,255,0),style=wx.CROSSDIAG_HATCH) wx.CROSSRED_BRUSH=wx.Brush(wx.Colour(255,0,0),style=wx.CROSSDIAG_HATCH ) wx.YELLOW_BRUSH=wx.Brush(wx.Colour(255,255,0)) wx.CROSSYELLOW_BRUSH=wx.Brush(wx.Colour(255,255,0),style=wx.CROSSDIAG_HATCH) frame = MyFrame(None, title="The Kochen-Specker paradox") frame.Show() app.MainLoop()