def __init__( self, screensize=(800, 600), fullscreen=False, caption="Topology Viewer", particleTypes=None, initialTopology=None, laws=None, simCyclesPerRedraw=None, border=100, extraDrawing=None, showGrid=True, transparency=None, position=None, ): """x.__init__(...) initializes x; see x.__class__.__doc__ for signature""" tracker = _cat.coordinatingassistanttracker.getcat() display = OpenGLDisplay(width=screensize[0], height=screensize[1], fullscreen=fullscreen, title=caption) display.activate() OpenGLDisplay.setDisplayService(display, tracker) super(TopologyViewer3D, self).__init__() self.particle = None self.particleTypes = {"-": Particle3D}
def initialiseComponent(self): # listen to shutdown events ogl_display = OpenGLDisplay.getDisplayService()[0] self.link( (ogl_display, "signal"), (self, "control") ) # create board self.boardvis = CheckersBoard(position=(0,0,-15)).activate() self.interactor_comms = {} self.board = {} for i in range(8): self.board[i] = {} for j in range(8): self.board[i][j] = None # create black pieces self.blackPieces = [] self.blackInteractors = [] for i in range(8): for j in range(3): if (i+j) %2 == 0: x = float(i)-3.5 y = float(j)-3.5 piece = CheckersPiece(position=(x, y, -15), colour=(0.6,0,0)).activate() self.blackPieces.append(piece) interactor = CheckersInteractor(target=piece, colour='B').activate() self.blackInteractors.append(interactor) intcomms = self.addOutbox("interactor_comms") self.interactor_comms[id(interactor)] = intcomms self.link( (self, intcomms), (interactor, "inbox")) self.link( (interactor, "outbox"), (self, "inbox")) self.board[i][j] = 'B' # create white pieces self.whitePieces = [] self.whiteInteractors = [] for i in range(8): for j in range(5,8): if (i+j) %2 == 0: x = float(i)-3.5 y = float(j)-3.5 piece = CheckersPiece(position=(x, y, -15), colour=(0,0,0.6)).activate() self.whitePieces.append(piece) interactor = CheckersInteractor(target=piece, colour='B').activate() self.whiteInteractors.append(interactor) intcomms = self.addOutbox("interactor_comms") self.interactor_comms[id(interactor)] = intcomms self.link( (self, intcomms), (interactor, "inbox")) self.link( (interactor, "outbox"), (self, "inbox")) self.board[i][j] = 'W' return 1
def initUIComponents(self): # listen to shutdown events ogl_display = OpenGLDisplay().getDisplayService()[0] self.link((ogl_display, "signal"), (self, "control")) # init mover self.mover = WheelMover(radius=15, center=(0, 0, -25), steps=500, slots=40).activate() self.link((self, "mover_signal"), (self.mover, "notify")) self.link((self, "mover_switch"), (self.mover, "switch")) self.background = SkyGrassBackground(size=(5000, 5000, 0), position=(0, 0, -90)).activate() # create & link nav buttons self.up_button = ArrowButton(size=(1, 1, 0.3), position=(7, 5, -15), msg="UP").activate() self.down_button = ArrowButton(size=(1, 1, 0.3), position=(7, -5, -15), rotation=(0, 0, 180), msg="DOWN").activate() self.link((self.up_button, "outbox"), (self, "nav")) self.link((self.down_button, "outbox"), (self, "nav")) # init info display self.infoticker = Ticker(text_height=21, render_right=250, render_bottom=500, background_colour=(250, 250, 200), text_colour=(0, 0, 0), outline_colour=(255, 255, 255)).activate() self.tickerwrapper = PygameWrapper(wrap=self.infoticker, size=(2.4, 4.0, 0.3)).activate() self.hideinfo_button = Button(caption="Hide", fontsize=30).activate() infocontents = { self.tickerwrapper: { "position": (0, 0, 0) }, self.hideinfo_button: { "position": (0, -2.4, 0) }, } self.infocontainer = Container(contents=infocontents, position=(-10, 10, -16)).activate() infopath = LinearPath([(-10, 10, -16), (-3, 0, -8)], 100) self.infomover = PathMover(infopath, False).activate() self.link((self.infomover, "outbox"), (self.infocontainer, "position")) self.link((self, "infomover_commands"), (self.infomover, "inbox")) self.link((self, "torrent_info"), (self.infoticker, "inbox")) self.link((self.hideinfo_button, "outbox"), (self, "hide_info")) self.send("Stop", "infomover_commands")
def __init__(self, screensize=(800, 600), fullscreen=False, caption="Topology Viewer", particleTypes=None, initialTopology=None, laws=None, simCyclesPerRedraw=None, border=100, extraDrawing=None, showGrid=True, transparency=None, position=None): """x.__init__(...) initializes x; see x.__class__.__doc__ for signature""" tracker = _cat.coordinatingassistanttracker.getcat() display = OpenGLDisplay(width=screensize[0], height=screensize[1], fullscreen=fullscreen, title=caption) display.activate() OpenGLDisplay.setDisplayService(display, tracker) super(TopologyViewer3D, self).__init__() self.particle = None self.particleTypes = {"-": Particle3D}
from Kamaelia.Codec.Dirac import DiracDecoder from Kamaelia.File.ReadFileAdaptor import ReadFileAdaptor from Kamaelia.Util.RateFilter import MessageRateLimit from VideoSurface import VideoSurface from PixFormatConversion import ToRGB_interleaved from Kamaelia.UI.PygameDisplay import PygameDisplay from Kamaelia.UI.OpenGL.OpenGLDisplay import OpenGLDisplay from Kamaelia.UI.OpenGL.PygameWrapper import PygameWrapper from Kamaelia.UI.OpenGL.SkyGrassBackground import SkyGrassBackground from Kamaelia.UI.OpenGL.Movement import SimpleRotator file = "../../../Code/Python/Kamaelia/Examples/VideoCodecs/Dirac/snowboard-jump-352x288x75.0.5.4.drc" framerate = 15 # override pygame display service ogl_display = OpenGLDisplay.getDisplayService() PygameDisplay.setDisplayService(ogl_display[0]) SkyGrassBackground(size=(5000, 5000, 0), position=(0, 0, -100)).activate() screen = VideoSurface() screen__in_scene = PygameWrapper(wrap=screen, position=(0, 0, -5), rotation=(-30, 15, 3)).activate() rotator = SimpleRotator(amount=(0.0, 0.0, 0.5)).activate() rotator.link((rotator, "outbox"), (screen__in_scene, "rel_rotation")) Pipeline(ReadFileAdaptor(file, readmode="bitrate", bitrate=300000 * 8 / 5), DiracDecoder(), ToRGB_interleaved(), MessageRateLimit(framerate, buffer=15), screen).run()
def initialiseComponent(self): # listen to shutdown events ogl_display = OpenGLDisplay.getDisplayService()[0] self.link((ogl_display, "signal"), (self, "control")) # create board self.boardvis = CheckersBoard(position=(0, 0, -15)).activate() self.interactor_comms = {} self.board = {} for i in range(8): self.board[i] = {} for j in range(8): self.board[i][j] = None # create black pieces self.blackPieces = [] self.blackInteractors = [] for i in range(8): for j in range(3): if (i + j) % 2 == 0: x = float(i) - 3.5 y = float(j) - 3.5 piece = CheckersPiece(position=(x, y, -15), colour=(0.6, 0, 0)).activate() self.blackPieces.append(piece) interactor = CheckersInteractor(target=piece, colour='B').activate() self.blackInteractors.append(interactor) intcomms = self.addOutbox("interactor_comms") self.interactor_comms[id(interactor)] = intcomms self.link((self, intcomms), (interactor, "inbox")) self.link((interactor, "outbox"), (self, "inbox")) self.board[i][j] = 'B' # create white pieces self.whitePieces = [] self.whiteInteractors = [] for i in range(8): for j in range(5, 8): if (i + j) % 2 == 0: x = float(i) - 3.5 y = float(j) - 3.5 piece = CheckersPiece(position=(x, y, -15), colour=(0, 0, 0.6)).activate() self.whitePieces.append(piece) interactor = CheckersInteractor(target=piece, colour='B').activate() self.whiteInteractors.append(interactor) intcomms = self.addOutbox("interactor_comms") self.interactor_comms[id(interactor)] = intcomms self.link((self, intcomms), (interactor, "inbox")) self.link((interactor, "outbox"), (self, "inbox")) self.board[i][j] = 'W' return 1
if (to[0] < 0 or to[0] > 7 or to[1] < 0 or to[1] > 7 or to[0] + to[1]) % 2 != 0 or self.board[to[0]][ to[1]] is not None: self.send("INVALID", self.interactor_comms[objectid]) else: self.board[fr[0]][fr[1]] = None self.board[to[0]][to[1]] = colour self.send("ACK", self.interactor_comms[objectid]) while self.dataReady("control"): cmsg = self.recv("control") if isinstance(cmsg, Axon.Ipc.shutdownMicroprocess): # dirty way to terminate program sys.exit(0) return 1 if __name__ == '__main__': # initialise display, change point of view ogl_display = OpenGLDisplay(viewerposition=(0, -10, 0), lookat=(0, 0, -15), limit_fps=100) ogl_display.activate() OpenGLDisplay.setDisplayService(ogl_display) Checkers().activate() Axon.Scheduler.scheduler.run.runThreads() # Licensed to the BBC under a Contributor Agreement: THF
def __init__(self, screensize = (800,600), fullscreen = False, caption = "3D Topology Viewer", particleTypes = None, initialTopology = None, laws = None, simCyclesPerRedraw = 1, border = 0): """x.__init__(...) initializes x; see x.__class__.__doc__ for signature""" super(TopologyViewer3D, self).__init__() glutInit(sys.argv) tracker = _cat.coordinatingassistanttracker.getcat() try: self.display = tracker.retrieveService("ogl_display")[0] except KeyError: self.display = OpenGLDisplay(width=screensize[0], height=screensize[1],fullscreen=fullscreen, title=caption) self.display.activate() OpenGLDisplay.setDisplayService(self.display, tracker) self.display = OpenGLDisplay.getDisplayService()[0] self.link((self,"display_signal"), (self.display,"notify")) self.link((self.display,"signal"), (self,"control")) self.border = border if particleTypes == None: self.particleTypes = {"-":CuboidParticle3D, "cuboid":CuboidParticle3D, "sphere":SphereParticle3D, "teapot":TeapotParticle3D} else: self.particleTypes = particleTypes if initialTopology == None: initialTopology = ([],[]) self.initialNodes = list(initialTopology[0]) self.initialBonds = list(initialTopology[1]) self.hitParticles = [] self.multiSelectMode = False self.selectedParticles = [] self.grabbed = False self.rotationMode = False if laws==None: self.laws = Kamaelia.Support.Particles.SimpleLaws(bondLength=2) else: self.laws = laws self.physics = ParticleSystem(self.laws, [], 0) self.biggestRadius = 0 # Do interaction self.simCyclesPerRedraw = simCyclesPerRedraw self.lastIdleTime = time.time() # Tell if new node is added; if true, new id needs adding to OpenGLDisplay list self.isNewNode = False # For hierarchy structure self.maxLevel = 0 self.currentLevel = 0 self.previousParentParticleID = self.currentParentParticleID = '' self.viewerOldPos = Vector() self.levelViewerPos = {} # The Physics particle system of current display level for display self.currentDisplayedPhysics = ParticleSystem(self.laws, [], 0) # For double click self.lastClickPos = (0,0) self.lastClickTime = time.time() self.dClickRes = 0.3
def __init__(self, screensize=(800, 600), fullscreen=False, caption="3D Topology Viewer", particleTypes=None, initialTopology=None, laws=None, simCyclesPerRedraw=1, border=0): """x.__init__(...) initializes x; see x.__class__.__doc__ for signature""" super(TopologyViewer3D, self).__init__() glutInit(sys.argv) tracker = _cat.coordinatingassistanttracker.getcat() try: self.display = tracker.retrieveService("ogl_display")[0] except KeyError: self.display = OpenGLDisplay(width=screensize[0], height=screensize[1], fullscreen=fullscreen, title=caption) self.display.activate() OpenGLDisplay.setDisplayService(self.display, tracker) self.display = OpenGLDisplay.getDisplayService()[0] self.link((self, "display_signal"), (self.display, "notify")) self.link((self.display, "signal"), (self, "control")) self.border = border if particleTypes == None: self.particleTypes = { "-": CuboidParticle3D, "cuboid": CuboidParticle3D, "sphere": SphereParticle3D, "teapot": TeapotParticle3D } else: self.particleTypes = particleTypes if initialTopology == None: initialTopology = ([], []) self.initialNodes = list(initialTopology[0]) self.initialBonds = list(initialTopology[1]) self.hitParticles = [] self.multiSelectMode = False self.selectedParticles = [] self.grabbed = False self.rotationMode = False if laws == None: self.laws = Kamaelia.Support.Particles.SimpleLaws(bondLength=2) else: self.laws = laws self.physics = ParticleSystem(self.laws, [], 0) self.biggestRadius = 0 # Do interaction self.simCyclesPerRedraw = simCyclesPerRedraw self.lastIdleTime = time.time() # Tell if new node is added; if true, new id needs adding to OpenGLDisplay list self.isNewNode = False # For hierarchy structure self.maxLevel = 0 self.currentLevel = 0 self.previousParentParticleID = self.currentParentParticleID = '' self.viewerOldPos = Vector() self.levelViewerPos = {} # The Physics particle system of current display level for display self.currentDisplayedPhysics = ParticleSystem(self.laws, [], 0) # For double click self.lastClickPos = (0, 0) self.lastClickTime = time.time() self.dClickRes = 0.3
from Kamaelia.Apps.Whiteboard.SmartBoard import SmartBoard from Kamaelia.Apps.Whiteboard.Webcam import Webcam #from Webcam import Webcam from Webcam import VideoCaptureSource #from BlankCanvas import BlankCanvas if __name__=="__main__": width = 1024 height = 768 top = 0 left = 0 colours_order = [ "black", "red", "orange", "yellow", "green", "turquoise", "blue", "purple", "darkgrey", "lightgrey" ] ogl_display = OpenGLDisplay(title="Kamaelia Whiteboard",width=width,height=height,background_colour=(255,255,255)) ogl_display.activate() OpenGLDisplay.setDisplayService(ogl_display) ogl_display = OpenGLDisplay.getDisplayService() PygameDisplay.setDisplayService(ogl_display[0]) if (0): #PygameDisplay.setDisplayService(ogl_display) CANVAS = Canvas( position=(left,top+32),size=(1200,(900-(32+15))),notepad="Test" ).activate() #(replace width with 'width' and height with 'height-(32+15)' PAINTER = Painter().activate() CANVAS_WRAPPER = PygameWrapper(wrap=CANVAS, position=(0,0,-10), rotation=(0,0,0)).activate() ERASER = Eraser(left,top).activate() PALETTE = buildPalette( cols=colours, order=colours_order, topleft=(left+64,top), size=32 ).activate() CLEAR = ClearPage(left+(64*5)+32*len(colours),top).activate() #PALETTE_WRAPPER = PygameWrapper(wrap=PALETTE, position=(4,1,-10), rotation=(-20,15,3)).activate()
from Kamaelia.Chassis.Pipeline import Pipeline from Kamaelia.UI.OpenGL.OpenGLDisplay import OpenGLDisplay from Kamaelia.UI.OpenGL.PygameWrapper import PygameWrapper from Kamaelia.UI.OpenGL.MatchedTranslationInteractor import MatchedTranslationInteractor from Kamaelia.UI.PygameDisplay import PygameDisplay from Kamaelia.UI.Pygame.Button import Button from Kamaelia.UI.Pygame.Text import Textbox, TextDisplayer from Kamaelia.UI.Pygame.VideoSurface import VideoSurface from Kamaelia.Codec.Dirac import DiracDecoder from Kamaelia.Util.RateFilter import MessageRateLimit from Kamaelia.File.ReadFileAdaptor import ReadFileAdaptor from Kamaelia.Video.PixFormatConversion import ToRGB_interleaved # override pygame display service ogl_display = OpenGLDisplay.getDisplayService(fullscreen=True) PygameDisplay.setDisplayService(ogl_display[0]) READER = Textbox(size=(400, 300), text_height=30).activate() WRITER = TextDisplayer(size=(400, 300), text_height=30).activate() SCREEN = VideoSurface().activate() Pipeline( ReadFileAdaptor("TestMaterial/TrainWindow.drc", readmode="bitrate", bitrate=1000000), DiracDecoder(), MessageRateLimit(10), ToRGB_interleaved(), SCREEN,
from Kamaelia.Chassis.Pipeline import Pipeline from Kamaelia.UI.OpenGL.OpenGLDisplay import OpenGLDisplay from Kamaelia.UI.OpenGL.PygameWrapper import PygameWrapper from Kamaelia.UI.OpenGL.MatchedTranslationInteractor import MatchedTranslationInteractor from Kamaelia.UI.PygameDisplay import PygameDisplay from Kamaelia.UI.Pygame.Button import Button from Kamaelia.UI.Pygame.Text import Textbox, TextDisplayer from Kamaelia.UI.Pygame.VideoSurface import VideoSurface from Kamaelia.Codec.Dirac import DiracDecoder from Kamaelia.Util.RateFilter import MessageRateLimit from Kamaelia.File.ReadFileAdaptor import ReadFileAdaptor from Kamaelia.Video.PixFormatConversion import ToRGB_interleaved # override pygame display service ogl_display = OpenGLDisplay.getDisplayService(fullscreen=True) PygameDisplay.setDisplayService(ogl_display[0]) READER = Textbox(size = (400, 300),text_height=30).activate() WRITER = TextDisplayer(size = (400, 300),text_height=30).activate() SCREEN = VideoSurface().activate() Pipeline( ReadFileAdaptor("TestMaterial/TrainWindow.drc", readmode="bitrate", bitrate = 1000000), DiracDecoder(), MessageRateLimit(10), ToRGB_interleaved(), SCREEN,
def main(self): while 1: self.capture_one() self.send(self.snapshot, "outbox") time.sleep(self.delay) from Kamaelia.UI.OpenGL.OpenGLDisplay import OpenGLDisplay from Kamaelia.UI.PygameDisplay import PygameDisplay from Kamaelia.UI.OpenGL.SkyGrassBackground import SkyGrassBackground from Kamaelia.UI.OpenGL.PygameWrapper import PygameWrapper from Kamaelia.UI.OpenGL.MatchedTranslationInteractor import MatchedTranslationInteractor ogl_display = OpenGLDisplay.getDisplayService() PygameDisplay.setDisplayService(ogl_display[0]) # SkyGrassBackground(size=(5000,5000,0), position=(0,0,-100)).activate() screen = VideoSurface().activate() screen_in_scene = PygameWrapper(wrap=screen, position=(0, 0,-8), rotation=(-30,15,3)).activate() i1 = MatchedTranslationInteractor(target=screen_in_scene).activate() Pipeline( VideoCaptureSource(), PureTransformer(lambda F : \ {"rgb" : pygame.image.tostring(F, "RGB"), "size" : (352, 288), "pixformat" : "RGB_interleaved", }),
class TopologyViewer3D(Axon.AdaptiveCommsComponent.AdaptiveCommsComponent): """\ TopologyViewer3D(...) -> new TopologyViewer3D component. A component that takes incoming topology (change) data and displays it live using pygame OpenGL. A simple physics model assists with visual layout. Particle types, appearance and physics interactions can be customised. """ Inboxes = { "inbox" : "Topology (change) data describing an Axon system", "control" : "Shutdown signalling", "alphacontrol" : "Alpha (transparency) of the image (value 0..255)", "callback": "for the response after a displayrequest", "events" : "Place where we recieve events from the outside world", "displaycontrol" : "Replies from Pygame Display service", } Outboxes = { "signal" : "NOT USED", "outbox" : "Notification and topology output", "display_signal" : "Requests to Pygame Display service", } def __init__(self, screensize = (800,600), fullscreen = False, caption = "Topology Viewer", particleTypes = None, initialTopology = None, laws = None, simCyclesPerRedraw = 1, border = 0, extraDrawing = None, showGrid = True, transparency = None, position = None): """x.__init__(...) initializes x; see x.__class__.__doc__ for signature""" super(TopologyViewer3D, self).__init__() glutInit(sys.argv) tracker = _cat.coordinatingassistanttracker.getcat() self.display = OpenGLDisplay(width=screensize[0], height=screensize[1],fullscreen=fullscreen, title=caption) self.display.activate() OpenGLDisplay.setDisplayService(self.display, tracker) self.link((self,"display_signal"), (self.display,"notify")) self.link((self.display,"signal"), (self,"control")) self.border = border if particleTypes == None: self.particleTypes = {"-":CuboidParticle3D, "cuboid":CuboidParticle3D, "sphere":SphereParticle3D, "teapot":TeapotParticle3D} else: self.particleTypes = particleTypes self.hitParticles = [] self.multiSelectMode = False self.selectedParticles = [] self.grabbed = False self.rotationMode = False if laws==None: self.laws = Kamaelia.Support.Particles.SimpleLaws(bondLength=2) else: self.laws = laws self.physics = ParticleSystemX(self.laws, [], 0) self.biggestRadius = 0 # Do interaction self.simCyclesPerRedraw = simCyclesPerRedraw self.lastIdleTime = time.time() # Tell if new node is added; if true, new id needs adding to OpenGLDisplay list self.isNewNode = False # For hierarchy structure self.maxLevel = 0 self.currentLevel = 0 self.currentParentParticleID = '' self.viewerOldPos = Vector() self.levelViewerPos = {} self.currentDisplayedPhysics = ParticleSystemX(self.laws, [], 0) # For double click self.lastClickPos = (0,0) self.lastClickTime = time.time() self.dClickRes = 0.3 def main(self): """\ """ # create display request for itself self.size = Vector(0,0,0) disprequest = { "OGL_DISPLAYREQUEST" : True, "objectid" : id(self), "callback" : (self,"callback"), "events" : (self, "events"), "size": self.size } # send display request self.send(disprequest, "display_signal") # wait for response on displayrequest and get identifier of the viewer while not self.dataReady("callback"): yield 1 self.identifier = self.recv("callback") self.addListenEvents( [pygame.MOUSEBUTTONDOWN, pygame.MOUSEBUTTONUP, pygame.MOUSEMOTION, pygame.KEYDOWN, pygame.KEYUP ]) pygame.key.set_repeat(100,100) while True: # process incoming messages if self.dataReady("inbox"): message = self.recv("inbox") self.doCommand(message) #print message # wait for response on displayrequest and get identifier of the particle if self.isNewNode: while not self.dataReady("callback"): yield 1 self.physics.particles[-1].identifier = self.recv("callback") self.isNewNode = False else: self.lastIdleTime = 0 yield 1 if self.lastIdleTime + 1.0 < time.time(): #print [particle.pos for particle in self.physics.particles] avoidedList = [] avoidedList.extend(self.hitParticles) avoidedList.extend(self.selectedParticles) #self.currentDisplayedPhysics.particleDict[ident].breakAllBonds() self.currentDisplayedPhysics.particles = [] if self.physics.particles != []: for particle in self.physics.particles: if self.currentParentParticleID == '': if ':' not in particle.ID: self.currentDisplayedPhysics.add( particle ) particle.oldpos = particle.initialpos elif particle.ID.find(self.currentParentParticleID) == 0 and particle.ID.count(':') == self.currentLevel: self.currentDisplayedPhysics.add( particle ) particle.oldpos = particle.initialpos self.currentDisplayedPhysics.run(self.simCyclesPerRedraw, avoidedList=avoidedList) #print [particle.pos for particle in self.physics.particles] # Draw particles if new or updated for particle in self.currentDisplayedPhysics.particles: if particle.needRedraw: self.drawParticles(particle) #particle.needRedraw = False self.handleEvents() # Perform transformation for particle in self.currentDisplayedPhysics.particles: transform_update = particle.applyTransforms() if transform_update is not None: self.send(transform_update, "display_signal") #print transform_update #print [particle.pos for particle in self.physics.particles] self.lastIdleTime = time.time() else: yield 1 if self.dataReady("control"): msg = self.recv("control") if isinstance(msg, Axon.Ipc.shutdownMicroprocess): self.quit(msg) def quit(self,msg=Axon.Ipc.shutdownMicroprocess()): print 'Shut down...' self.send(msg, "signal") self.scheduler.stop() def draw(self): """\ Invoke draw() and save its commands to a newly generated displaylist. The displaylist name is then sent to the display service via a "DISPLAYLIST_UPDATE" request. """ pass def drawParticles(self, *particles): for particle in particles: # display list id displaylist = glGenLists(1) # draw object to its displaylist glNewList(displaylist, GL_COMPILE) particle.draw() glEndList() #print displaylist dl_update = { "DISPLAYLIST_UPDATE": True, "objectid": id(particle), "displaylist": displaylist } self.send(dl_update, "display_signal") #print particle def addListenEvents(self, events): """\ Sends listening request for pygame events to the display service. The events parameter is expected to be a list of pygame event constants. """ for event in events: self.send({"ADDLISTENEVENT":event, "objectid":id(self)}, "display_signal") def removeListenEvents(self, events): """\ Sends stop listening request for pygame events to the display service. The events parameter is expected to be a list of pygame event constants. """ for event in events: self.send({"REMOVELISTENEVENT":event, "objectid":id(self)}, "display_signal") def handleEvents(self): """ Handle events. """ while self.dataReady("events"): event = self.recv("events") if event.type == pygame.MOUSEBUTTONDOWN or pygame.MOUSEMOTION and self.grabbed: if not self.rotationMode: for particle in self.hitParticles: p1 = Vector(*particle.pos).copy() p1.x += 10 p2 = Vector(*particle.pos).copy() p2.y += 10 #z = Intersect.ray_Plane(Vector(0,0,0), event.direction, [Vector(*particle.pos)-self.display.viewerposition, p1, p2]) z = Intersect.ray_Plane(Vector(0,0,0), event.direction, [Vector(*particle.pos)-Vector(0,0,self.display.viewerposition.z), p1-Vector(0,0,self.display.viewerposition.z), p2-Vector(0,0,self.display.viewerposition.z)]) newpoint = event.direction * z if event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: # Handle double click clickPos = event.pos currentTime = time.time() elapsedTime = currentTime - self.lastClickTime if clickPos == self.lastClickPos and elapsedTime<self.dClickRes: if self.currentLevel < self.maxLevel and len(self.selectedParticles) == 1: hasChildParticles = False for particle in self.physics.particles: if particle.ID.find(self.selectedParticles[0].ID) == 0 and particle.ID != self.selectedParticles[0].ID: hasChildParticles = True break if hasChildParticles: self.currentParentParticleID = self.selectedParticles[0].ID self.gotoDisplayLevel(1) else: print 'Warning: The particle you double-clicked has no children!' else: if self.currentLevel == self.maxLevel: print "Warning: max hierarchy level has reached!" if len(self.selectedParticles) != 1: print "Tips: To extend a node, please double-click the node you want to extend" else: if not self.rotationMode: for particle in self.currentDisplayedPhysics.particles: if particle.identifier in event.hitobjects: #particle.oldpos = particle.oldpos - self.display.viewerposition self.grabbed = True #particle.scaling = Vector(0.9,0.9,0.9) self.hitParticles.append(particle) self.selectParticle(particle) #print str(id(particle))+'hit' #print self.hitParticles # If click places other than particles in non multiSelectMode, deselect all if not self.hitParticles and not self.multiSelectMode: self.deselectAll() self.lastClickPos = clickPos self.lastClickTime = currentTime if event.button == 3: if self.currentLevel > 0: items = self.currentParentParticleID.split(':') items.pop() self.currentParentParticleID = ':'.join(items) self.gotoDisplayLevel(-1) else: print "Warning: The first hierarchy level has reached!" if event.button == 4: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles for particle in particles: posVector = Vector(*particle.pos) posVector.z -= 1 particle.pos = posVector.toTuple() if event.button == 5: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles for particle in particles: posVector = Vector(*particle.pos) posVector.z += 1 particle.pos = posVector.toTuple() if event.type == pygame.MOUSEBUTTONUP: if event.button == 1: for particle in self.hitParticles: self.grabbed = False particle.oldpoint = None #particle.scaling = Vector(1,1,1) self.hitParticles.pop(self.hitParticles.index(particle)) #print self.hitParticles if event.type == pygame.MOUSEMOTION: if not self.rotationMode and self.grabbed: for particle in self.hitParticles: try: if particle.oldpoint is not None: #print particle.pos diff = newpoint-particle.oldpoint amount = (diff.x, diff.y) particle.pos = (Vector(*particle.pos)+Vector(*amount)).toTuple() except NameError: pass # Redraw the link so that the link can move with the particle for p in particle.bondedFrom: p.needRedraw = True elif self.rotationMode: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAnglex = float(event.rel[1])*math.pi/180 dAngley = -float(event.rel[0])*math.pi/180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.z*relativePosVector.z+relativePosVector.y*relativePosVector.y)**0.5 newAnglex = (math.atan2(relativePosVector.z,relativePosVector.y)+dAnglex) particle.pos = (posVector.x, radius*math.cos(newAnglex)+centrePoint.y, radius*math.sin(newAnglex)+centrePoint.z) posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.z*relativePosVector.z+relativePosVector.x*relativePosVector.x)**0.5 newAngley = (math.atan2(relativePosVector.z,relativePosVector.x)+dAngley) particle.pos = (radius*math.cos(newAngley)+centrePoint.x, posVector.y, radius*math.sin(newAngley)+centrePoint.z) particle.drotation.y = float(event.rel[0]) particle.drotation.x = float(event.rel[1]) particle.drotation %= 360 try: for particle in self.hitParticles: particle.oldpoint = newpoint except NameError: pass # Keyboard events handling if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: self.quit() elif event.key == pygame.K_BACKSPACE: if self.currentLevel > 0: items = self.currentParentParticleID.split(':') items.pop() self.currentParentParticleID = ':'.join(items) self.gotoDisplayLevel(-1) else: print "Warning: The first hierarchy level has reached!" elif event.key == pygame.K_RETURN: if self.currentLevel < self.maxLevel and len(self.selectedParticles) == 1: hasChildParticles = False for particle in self.physics.particles: if particle.ID.find(self.selectedParticles[0].ID) == 0 and particle.ID != self.selectedParticles[0].ID: hasChildParticles = True break if hasChildParticles: self.currentParentParticleID = self.selectedParticles[0].ID self.gotoDisplayLevel(1) else: print 'Warning: The particle you double-clicked has no children!' else: if self.currentLevel == self.maxLevel: print "Warning: max hierarchy level has reached!" if len(self.selectedParticles) != 1: print "Tips: To extend a node, please click to select the node (only one) you want to extend first." elif event.key == pygame.K_LSHIFT or event.key == pygame.K_RSHIFT: self.multiSelectMode = True elif event.key == pygame.K_LCTRL or event.key == pygame.K_RCTRL: self.rotationMode = True elif event.key == pygame.K_PAGEUP: self.display.viewerposition.z -= 0.5 elif event.key == pygame.K_PAGEDOWN: self.display.viewerposition.z += 0.5 elif event.key == pygame.K_w: self.display.viewerposition.y += 0.5 elif event.key == pygame.K_s: self.display.viewerposition.y -= 0.5 elif event.key == pygame.K_a: self.display.viewerposition.x -= 0.5 elif event.key == pygame.K_d: self.display.viewerposition.x += 0.5 elif event.key == pygame.K_UP: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAngle = -20*math.pi/180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.z*relativePosVector.z+relativePosVector.y*relativePosVector.y)**0.5 newAngle = (math.atan2(relativePosVector.z,relativePosVector.y)+dAngle) particle.pos = (posVector.x, radius*math.cos(newAngle)+centrePoint.y, radius*math.sin(newAngle)+centrePoint.z) particle.drotation = Vector(dAngle*180/math.pi,0,0) elif event.key == pygame.K_DOWN: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAngle = 20*math.pi/180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.z*relativePosVector.z+relativePosVector.y*relativePosVector.y)**0.5 newAngle = (math.atan2(relativePosVector.z,relativePosVector.y)+dAngle) particle.pos = (posVector.x, radius*math.cos(newAngle)+centrePoint.y, radius*math.sin(newAngle)+centrePoint.z) particle.drotation = Vector(dAngle*180/math.pi,0,0) elif event.key == pygame.K_LEFT: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAngle = 20*math.pi/180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.z*relativePosVector.z+relativePosVector.x*relativePosVector.x)**0.5 newAngle = (math.atan2(relativePosVector.z,relativePosVector.x)+dAngle) particle.pos = (radius*math.cos(newAngle)+centrePoint.x, posVector.y, radius*math.sin(newAngle)+centrePoint.z) particle.drotation = Vector(0,-dAngle*180/math.pi,0) elif event.key == pygame.K_RIGHT: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAngle = -20*math.pi/180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.z*relativePosVector.z+relativePosVector.x*relativePosVector.x)**0.5 newAngle = (math.atan2(relativePosVector.z,relativePosVector.x)+dAngle) particle.pos = (radius*math.cos(newAngle)+centrePoint.x, posVector.y, radius*math.sin(newAngle)+centrePoint.z) particle.drotation = Vector(0,-dAngle*180/math.pi,0) elif event.key == pygame.K_COMMA: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAngle = 20*math.pi/180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.x*relativePosVector.x+relativePosVector.y*relativePosVector.y)**0.5 newAngle = (math.atan2(relativePosVector.y,relativePosVector.x)+dAngle) particle.pos = (radius*math.cos(newAngle)+centrePoint.x, radius*math.sin(newAngle)+centrePoint.y, posVector.z) particle.drotation = Vector(0,0,dAngle*180/math.pi) elif event.key == pygame.K_PERIOD: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAngle = -20*math.pi/180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.x*relativePosVector.x+relativePosVector.y*relativePosVector.y)**0.5 newAngle = (math.atan2(relativePosVector.y,relativePosVector.x)+dAngle) particle.pos = (radius*math.cos(newAngle)+centrePoint.x, radius*math.sin(newAngle)+centrePoint.y, posVector.z) particle.drotation = Vector(0,0,dAngle*180/math.pi) #print self.display.viewerposition # Scroll if self.display.viewerposition changes if self.display.viewerposition.copy() != self.viewerOldPos: self.scroll() self.viewerOldPos = self.display.viewerposition.copy() # for particle in self.currentDisplayedPhysics.particles: # particle.oldpoint = None if event.type == pygame.KEYUP: if event.key == pygame.K_LSHIFT or event.key == pygame.K_RSHIFT: self.multiSelectMode = False elif event.key == pygame.K_LCTRL or event.key == pygame.K_RCTRL: self.rotationMode = False def scroll( self ): # Scroll the surface by resetting gluLookAt glMatrixMode(GL_PROJECTION) glLoadIdentity() self.display.setProjection() def gotoDisplayLevel( self, dlevel): # Save current level's viewer position self.levelViewerPos[self.currentLevel] = self.display.viewerposition.copy() # Display next level self.currentLevel += dlevel # Reset viewer position to previous try: self.display.viewerposition = self.levelViewerPos[self.currentLevel].copy() except KeyError: self.display.viewerposition = self.levelViewerPos[self.currentLevel] = Vector() # Remove current displayed particles for particle in self.currentDisplayedPhysics.particles: self.display.ogl_displaylists.pop(id(particle)) self.display.ogl_transforms.pop(id(particle)) self.currentDisplayedPhysics.removeByID(*self.currentDisplayedPhysics.particleDict.keys()) def doCommand(self, msg): """\ Proceses a topology command tuple: [ "ADD", "NODE", <id>, <name>, <positionSpec>, <particle type> ] [ "DEL", "NODE", <id> ] [ "ADD", "LINK", <id from>, <id to> ] [ "DEL", "LINK", <id from>, <id to> ] [ "DEL", "ALL" ] [ "GET", "ALL" ] """ #print 'doCommand' if len(msg) >= 2: cmd = msg[0].upper(), msg[1].upper() if cmd == ("ADD", "NODE") and len(msg) == 6: if msg[2] in [p.ID for p in self.physics.particles]: print "Node exists, please use a new node ID!" else: if self.particleTypes.has_key(msg[5]): #print 'ADD NODE begin' ptype = self.particleTypes[msg[5]] ident = msg[2] name = msg[3] posSpec = msg[4] pos = self._generatePos(posSpec) #print pos particle = ptype(position = pos, ID=ident, name=name) particle.originaltype = msg[5] #self.particles.append(particle) #print self.particles[0] self.addParticle(particle) self.isNewNode = True #print id(particle) #print 'ADD NODE end' elif cmd == ("DEL", "NODE") and len(msg) == 3: ident = msg[2] self.removeParticle(ident) elif cmd == ("ADD", "LINK") and len(msg) == 4: src = msg[2] dst = msg[3] self.makeBond(src, dst) elif cmd == ("DEL", "LINK") and len(msg) == 4: src = msg[2] dst = msg[3] self.breakBond(src, dst) elif cmd == ("DEL", "ALL") and len(msg) == 2: self.removeParticle(*self.physics.particleDict.keys()) self.currentLevel = 0 self.currentParentParticleID = '' elif cmd == ("GET", "ALL") and len(msg) == 2: topology = [("DEL","ALL")] topology.extend(self.getTopology()) self.send( ("TOPOLOGY", topology), "outbox" ) elif cmd == ("UPDATE_NAME", "NODE") and len(msg) == 4: node_id = msg[2] new_name = msg[3] self.updateParticleLabel(node_id, new_name) self.send( ("UPDATE_NAME", "NODE", node_id, new_name), "outbox" ) elif cmd == ("GET_NAME", "NODE") and len(msg) == 3: node_id = msg[2] name = self.getParticleLabel(node_id) self.send( ("GET_NAME", "NODE", node_id, name), "outbox" ) else: print "Command Error: please check your command format!" else: print "Command Error: not enough parameters!" def _generatePos(self, posSpec): """\ generateXY(posSpec) -> (x,y,z) or raises ValueError posSpec == "randompos" or "auto" -> random (x,y,z) within the surface (specified border distance in from the edege) posSpec == "(XXX,YYY,ZZZ)" -> specified x,y,z (positive or negative integers) """ posSpec = posSpec.lower() if posSpec == "randompos" or posSpec == "auto" : # FIXME: need to consider camera/ viewer setting zLim = self.display.nearPlaneDist, self.display.farPlaneDist z = -1*random.randrange(int((zLim[1]-zLim[0])/20)+self.border,int((zLim[1]-zLim[0])/8)-self.border,1) yLim = z*math.tan(self.display.perspectiveAngle*math.pi/360.0), -z*math.tan(self.display.perspectiveAngle*math.pi/360.0) xLim = yLim[0]*self.display.aspectRatio, yLim[1]*self.display.aspectRatio y = random.randrange(int(yLim[0])+self.border,int(yLim[1])-self.border,1) x = random.randrange(int(xLim[0])+self.border,int(xLim[1])-self.border,1) #print x,y,z return x,y,z else: match = re.match("^([+-]?\d+),([+-]?\d+),([+-]?\d+)$", posSpec) if match: x = int(match.group(1)) y = int(match.group(2)) z = int(match.group(3)) return x,y,z raise ValueError("Unrecognised position specification") def addParticle(self, *particles): """Add particles to the system""" for p in particles: if p.radius > self.biggestRadius: self.biggestRadius = p.radius pLevel = p.ID.count(':') if self.maxLevel < pLevel: self.maxLevel = pLevel # create display request for every particle added disprequest = { "OGL_DISPLAYREQUEST" : True, "objectid" : id(p), "callback" : (self,"callback"), "events" : (self, "events"), "size": p.size } # send display request self.send(disprequest, "display_signal") self.physics.add( *particles ) def removeParticle(self, *ids): """\ Remove particle(s) specified by their ids. Also breaks any bonds to/from that particle. """ for ident in ids: self.physics.particleDict[ident].breakAllBonds() try: self.display.ogl_objects.remove(id(self.physics.particleDict[ident])) self.display.ogl_names.pop(id(self.physics.particleDict[ident])) self.display.ogl_displaylists.pop(id(self.physics.particleDict[ident])) self.display.ogl_transforms.pop(id(self.physics.particleDict[ident])) except KeyError: pass # if self.selected == self.physics.particleDict[id]: # self.selectParticle(None) self.physics.removeByID(*ids) for ident in ids: try: self.currentDisplayedPhysics.removeByID(ident) except KeyError: pass #print self.currentDisplayedPhysics.particles #print self.physics.particles def selectParticle(self, particle): """Select the specified particle.""" if self.multiSelectMode: if particle not in self.selectedParticles: particle.select() self.selectedParticles.append(particle) self.send( "('SELECT', 'NODE', '"+particle.name+"')", "outbox" ) else: particle.deselect() self.selectedParticles.remove(particle) self.send( "('DESELECT', 'NODE', '"+particle.name+"')", "outbox" ) else: self.deselectAll() self.selectedParticles = [] particle.select() self.selectedParticles.append(particle) self.send( "('SELECT', 'NODE', '"+particle.name+"')", "outbox" ) def deselectAll(self): """Deselect all particles.""" for particle in self.selectedParticles: particle.deselect() self.selectedParticles = [] def makeBond(self, source, dest): """Make a bond from source to destination particle, specified by IDs""" self.physics.particleDict[source].makeBond(self.physics.particleDict, dest) self.physics.particleDict[source].needRedraw = True def breakBond(self, source, dest): """Break a bond from source to destination particle, specified by IDs""" self.physics.particleDict[source].breakBond(self.physics.particleDict, dest) self.physics.particleDict[source].needRedraw = True def updateParticleLabel(self, node_id, new_name): """\ updateParticleLabel(node_id, new_name) -> updates the given nodes name & visual label if it exists node_id - an id for an already existing node new_name - a string (may include spaces) defining the new node name """ for p in self.physics.particles: if p.ID == node_id: p.set_label(new_name) p.needRedraw = True return def getParticleLabel(self, node_id): """\ getParticleLabel(node_id) -> particle's name Returns the name/label of the specified particle. """ for p in self.physics.particles: if p.ID == node_id: return p.name def getTopology(self): """getTopology() -> list of command tuples that would build the current topology""" topology = [] # first, enumerate the particles for particle in self.physics.particles: topology.append( ( "ADD","NODE", particle.ID, particle.name, "random", particle.originaltype ) ) # now enumerate the linkages for particle in self.physics.particles: for dst in particle.getBondedTo(): topology.append( ( "ADD","LINK", particle.ID, dst.ID ) ) return topology
class TopologyViewer3D(Axon.AdaptiveCommsComponent.AdaptiveCommsComponent): """\ TopologyViewer3D(...) -> new TopologyViewer3D component. A component that takes incoming topology (change) data and displays it live using pygame OpenGL. A simple physics model assists with visual layout. Particle types, appearance and physics interactions can be customised. """ Inboxes = { "inbox": "Topology (change) data describing an Axon system", "control": "Shutdown signalling", "alphacontrol": "Alpha (transparency) of the image (value 0..255)", "callback": "for the response after a displayrequest", "events": "Place where we recieve events from the outside world", "displaycontrol": "Replies from Pygame Display service", } Outboxes = { "signal": "NOT USED", "outbox": "Notification and topology output", "display_signal": "Requests to Pygame Display service", } def __init__(self, screensize=(800, 600), fullscreen=False, caption="Topology Viewer", particleTypes=None, initialTopology=None, laws=None, simCyclesPerRedraw=1, border=0, extraDrawing=None, showGrid=True, transparency=None, position=None): """x.__init__(...) initializes x; see x.__class__.__doc__ for signature""" super(TopologyViewer3D, self).__init__() glutInit(sys.argv) tracker = _cat.coordinatingassistanttracker.getcat() self.display = OpenGLDisplay(width=screensize[0], height=screensize[1], fullscreen=fullscreen, title=caption) self.display.activate() OpenGLDisplay.setDisplayService(self.display, tracker) self.link((self, "display_signal"), (self.display, "notify")) self.link((self.display, "signal"), (self, "control")) self.border = border if particleTypes == None: self.particleTypes = { "-": CuboidParticle3D, "cuboid": CuboidParticle3D, "sphere": SphereParticle3D, "teapot": TeapotParticle3D } else: self.particleTypes = particleTypes self.hitParticles = [] self.multiSelectMode = False self.selectedParticles = [] self.grabbed = False self.rotationMode = False if laws == None: self.laws = Kamaelia.Support.Particles.SimpleLaws(bondLength=2) else: self.laws = laws self.physics = ParticleSystemX(self.laws, [], 0) self.biggestRadius = 0 # Do interaction self.simCyclesPerRedraw = simCyclesPerRedraw self.lastIdleTime = time.time() # Tell if new node is added; if true, new id needs adding to OpenGLDisplay list self.isNewNode = False # For hierarchy structure self.maxLevel = 0 self.currentLevel = 0 self.currentParentParticleID = '' self.viewerOldPos = Vector() self.levelViewerPos = {} self.currentDisplayedPhysics = ParticleSystemX(self.laws, [], 0) # For double click self.lastClickPos = (0, 0) self.lastClickTime = time.time() self.dClickRes = 0.3 def main(self): """\ """ # create display request for itself self.size = Vector(0, 0, 0) disprequest = { "OGL_DISPLAYREQUEST": True, "objectid": id(self), "callback": (self, "callback"), "events": (self, "events"), "size": self.size } # send display request self.send(disprequest, "display_signal") # wait for response on displayrequest and get identifier of the viewer while not self.dataReady("callback"): yield 1 self.identifier = self.recv("callback") self.addListenEvents([ pygame.MOUSEBUTTONDOWN, pygame.MOUSEBUTTONUP, pygame.MOUSEMOTION, pygame.KEYDOWN, pygame.KEYUP ]) pygame.key.set_repeat(100, 100) while True: # process incoming messages if self.dataReady("inbox"): message = self.recv("inbox") self.doCommand(message) #print message # wait for response on displayrequest and get identifier of the particle if self.isNewNode: while not self.dataReady("callback"): yield 1 self.physics.particles[-1].identifier = self.recv( "callback") self.isNewNode = False else: self.lastIdleTime = 0 yield 1 if self.lastIdleTime + 1.0 < time.time(): #print [particle.pos for particle in self.physics.particles] avoidedList = [] avoidedList.extend(self.hitParticles) avoidedList.extend(self.selectedParticles) #self.currentDisplayedPhysics.particleDict[ident].breakAllBonds() self.currentDisplayedPhysics.particles = [] if self.physics.particles != []: for particle in self.physics.particles: if self.currentParentParticleID == '': if ':' not in particle.ID: self.currentDisplayedPhysics.add(particle) particle.oldpos = particle.initialpos elif particle.ID.find( self.currentParentParticleID ) == 0 and particle.ID.count(':') == self.currentLevel: self.currentDisplayedPhysics.add(particle) particle.oldpos = particle.initialpos self.currentDisplayedPhysics.run(self.simCyclesPerRedraw, avoidedList=avoidedList) #print [particle.pos for particle in self.physics.particles] # Draw particles if new or updated for particle in self.currentDisplayedPhysics.particles: if particle.needRedraw: self.drawParticles(particle) #particle.needRedraw = False self.handleEvents() # Perform transformation for particle in self.currentDisplayedPhysics.particles: transform_update = particle.applyTransforms() if transform_update is not None: self.send(transform_update, "display_signal") #print transform_update #print [particle.pos for particle in self.physics.particles] self.lastIdleTime = time.time() else: yield 1 if self.dataReady("control"): msg = self.recv("control") if isinstance(msg, Axon.Ipc.shutdownMicroprocess): self.quit(msg) def quit(self, msg=Axon.Ipc.shutdownMicroprocess()): print 'Shut down...' self.send(msg, "signal") self.scheduler.stop() def draw(self): """\ Invoke draw() and save its commands to a newly generated displaylist. The displaylist name is then sent to the display service via a "DISPLAYLIST_UPDATE" request. """ pass def drawParticles(self, *particles): for particle in particles: # display list id displaylist = glGenLists(1) # draw object to its displaylist glNewList(displaylist, GL_COMPILE) particle.draw() glEndList() #print displaylist dl_update = { "DISPLAYLIST_UPDATE": True, "objectid": id(particle), "displaylist": displaylist } self.send(dl_update, "display_signal") #print particle def addListenEvents(self, events): """\ Sends listening request for pygame events to the display service. The events parameter is expected to be a list of pygame event constants. """ for event in events: self.send({ "ADDLISTENEVENT": event, "objectid": id(self) }, "display_signal") def removeListenEvents(self, events): """\ Sends stop listening request for pygame events to the display service. The events parameter is expected to be a list of pygame event constants. """ for event in events: self.send({ "REMOVELISTENEVENT": event, "objectid": id(self) }, "display_signal") def handleEvents(self): """ Handle events. """ while self.dataReady("events"): event = self.recv("events") if event.type == pygame.MOUSEBUTTONDOWN or pygame.MOUSEMOTION and self.grabbed: if not self.rotationMode: for particle in self.hitParticles: p1 = Vector(*particle.pos).copy() p1.x += 10 p2 = Vector(*particle.pos).copy() p2.y += 10 #z = Intersect.ray_Plane(Vector(0,0,0), event.direction, [Vector(*particle.pos)-self.display.viewerposition, p1, p2]) z = Intersect.ray_Plane(Vector( 0, 0, 0), event.direction, [ Vector(*particle.pos) - Vector(0, 0, self.display.viewerposition.z), p1 - Vector(0, 0, self.display.viewerposition.z), p2 - Vector(0, 0, self.display.viewerposition.z) ]) newpoint = event.direction * z if event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: # Handle double click clickPos = event.pos currentTime = time.time() elapsedTime = currentTime - self.lastClickTime if clickPos == self.lastClickPos and elapsedTime < self.dClickRes: if self.currentLevel < self.maxLevel and len( self.selectedParticles) == 1: hasChildParticles = False for particle in self.physics.particles: if particle.ID.find( self.selectedParticles[0].ID ) == 0 and particle.ID != self.selectedParticles[ 0].ID: hasChildParticles = True break if hasChildParticles: self.currentParentParticleID = self.selectedParticles[ 0].ID self.gotoDisplayLevel(1) else: print 'Warning: The particle you double-clicked has no children!' else: if self.currentLevel == self.maxLevel: print "Warning: max hierarchy level has reached!" if len(self.selectedParticles) != 1: print "Tips: To extend a node, please double-click the node you want to extend" else: if not self.rotationMode: for particle in self.currentDisplayedPhysics.particles: if particle.identifier in event.hitobjects: #particle.oldpos = particle.oldpos - self.display.viewerposition self.grabbed = True #particle.scaling = Vector(0.9,0.9,0.9) self.hitParticles.append(particle) self.selectParticle(particle) #print str(id(particle))+'hit' #print self.hitParticles # If click places other than particles in non multiSelectMode, deselect all if not self.hitParticles and not self.multiSelectMode: self.deselectAll() self.lastClickPos = clickPos self.lastClickTime = currentTime if event.button == 3: if self.currentLevel > 0: items = self.currentParentParticleID.split(':') items.pop() self.currentParentParticleID = ':'.join(items) self.gotoDisplayLevel(-1) else: print "Warning: The first hierarchy level has reached!" if event.button == 4: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles for particle in particles: posVector = Vector(*particle.pos) posVector.z -= 1 particle.pos = posVector.toTuple() if event.button == 5: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles for particle in particles: posVector = Vector(*particle.pos) posVector.z += 1 particle.pos = posVector.toTuple() if event.type == pygame.MOUSEBUTTONUP: if event.button == 1: for particle in self.hitParticles: self.grabbed = False particle.oldpoint = None #particle.scaling = Vector(1,1,1) self.hitParticles.pop( self.hitParticles.index(particle)) #print self.hitParticles if event.type == pygame.MOUSEMOTION: if not self.rotationMode and self.grabbed: for particle in self.hitParticles: try: if particle.oldpoint is not None: #print particle.pos diff = newpoint - particle.oldpoint amount = (diff.x, diff.y) particle.pos = (Vector(*particle.pos) + Vector(*amount)).toTuple() except NameError: pass # Redraw the link so that the link can move with the particle for p in particle.bondedFrom: p.needRedraw = True elif self.rotationMode: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAnglex = float(event.rel[1]) * math.pi / 180 dAngley = -float(event.rel[0]) * math.pi / 180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = ( relativePosVector.z * relativePosVector.z + relativePosVector.y * relativePosVector.y)**0.5 newAnglex = (math.atan2(relativePosVector.z, relativePosVector.y) + dAnglex) particle.pos = (posVector.x, radius * math.cos(newAnglex) + centrePoint.y, radius * math.sin(newAnglex) + centrePoint.z) posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = ( relativePosVector.z * relativePosVector.z + relativePosVector.x * relativePosVector.x)**0.5 newAngley = (math.atan2(relativePosVector.z, relativePosVector.x) + dAngley) particle.pos = (radius * math.cos(newAngley) + centrePoint.x, posVector.y, radius * math.sin(newAngley) + centrePoint.z) particle.drotation.y = float(event.rel[0]) particle.drotation.x = float(event.rel[1]) particle.drotation %= 360 try: for particle in self.hitParticles: particle.oldpoint = newpoint except NameError: pass # Keyboard events handling if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: self.quit() elif event.key == pygame.K_BACKSPACE: if self.currentLevel > 0: items = self.currentParentParticleID.split(':') items.pop() self.currentParentParticleID = ':'.join(items) self.gotoDisplayLevel(-1) else: print "Warning: The first hierarchy level has reached!" elif event.key == pygame.K_RETURN: if self.currentLevel < self.maxLevel and len( self.selectedParticles) == 1: hasChildParticles = False for particle in self.physics.particles: if particle.ID.find( self.selectedParticles[0].ID ) == 0 and particle.ID != self.selectedParticles[ 0].ID: hasChildParticles = True break if hasChildParticles: self.currentParentParticleID = self.selectedParticles[ 0].ID self.gotoDisplayLevel(1) else: print 'Warning: The particle you double-clicked has no children!' else: if self.currentLevel == self.maxLevel: print "Warning: max hierarchy level has reached!" if len(self.selectedParticles) != 1: print "Tips: To extend a node, please click to select the node (only one) you want to extend first." elif event.key == pygame.K_LSHIFT or event.key == pygame.K_RSHIFT: self.multiSelectMode = True elif event.key == pygame.K_LCTRL or event.key == pygame.K_RCTRL: self.rotationMode = True elif event.key == pygame.K_PAGEUP: self.display.viewerposition.z -= 0.5 elif event.key == pygame.K_PAGEDOWN: self.display.viewerposition.z += 0.5 elif event.key == pygame.K_w: self.display.viewerposition.y += 0.5 elif event.key == pygame.K_s: self.display.viewerposition.y -= 0.5 elif event.key == pygame.K_a: self.display.viewerposition.x -= 0.5 elif event.key == pygame.K_d: self.display.viewerposition.x += 0.5 elif event.key == pygame.K_UP: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAngle = -20 * math.pi / 180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = ( relativePosVector.z * relativePosVector.z + relativePosVector.y * relativePosVector.y)**0.5 newAngle = (math.atan2(relativePosVector.z, relativePosVector.y) + dAngle) particle.pos = (posVector.x, radius * math.cos(newAngle) + centrePoint.y, radius * math.sin(newAngle) + centrePoint.z) particle.drotation = Vector(dAngle * 180 / math.pi, 0, 0) elif event.key == pygame.K_DOWN: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAngle = 20 * math.pi / 180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = ( relativePosVector.z * relativePosVector.z + relativePosVector.y * relativePosVector.y)**0.5 newAngle = (math.atan2(relativePosVector.z, relativePosVector.y) + dAngle) particle.pos = (posVector.x, radius * math.cos(newAngle) + centrePoint.y, radius * math.sin(newAngle) + centrePoint.z) particle.drotation = Vector(dAngle * 180 / math.pi, 0, 0) elif event.key == pygame.K_LEFT: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAngle = 20 * math.pi / 180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = ( relativePosVector.z * relativePosVector.z + relativePosVector.x * relativePosVector.x)**0.5 newAngle = (math.atan2(relativePosVector.z, relativePosVector.x) + dAngle) particle.pos = (radius * math.cos(newAngle) + centrePoint.x, posVector.y, radius * math.sin(newAngle) + centrePoint.z) particle.drotation = Vector(0, -dAngle * 180 / math.pi, 0) elif event.key == pygame.K_RIGHT: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAngle = -20 * math.pi / 180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = ( relativePosVector.z * relativePosVector.z + relativePosVector.x * relativePosVector.x)**0.5 newAngle = (math.atan2(relativePosVector.z, relativePosVector.x) + dAngle) particle.pos = (radius * math.cos(newAngle) + centrePoint.x, posVector.y, radius * math.sin(newAngle) + centrePoint.z) particle.drotation = Vector(0, -dAngle * 180 / math.pi, 0) elif event.key == pygame.K_COMMA: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAngle = 20 * math.pi / 180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = ( relativePosVector.x * relativePosVector.x + relativePosVector.y * relativePosVector.y)**0.5 newAngle = (math.atan2(relativePosVector.y, relativePosVector.x) + dAngle) particle.pos = (radius * math.cos(newAngle) + centrePoint.x, radius * math.sin(newAngle) + centrePoint.y, posVector.z) particle.drotation = Vector(0, 0, dAngle * 180 / math.pi) elif event.key == pygame.K_PERIOD: if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) dAngle = -20 * math.pi / 180 for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = ( relativePosVector.x * relativePosVector.x + relativePosVector.y * relativePosVector.y)**0.5 newAngle = (math.atan2(relativePosVector.y, relativePosVector.x) + dAngle) particle.pos = (radius * math.cos(newAngle) + centrePoint.x, radius * math.sin(newAngle) + centrePoint.y, posVector.z) particle.drotation = Vector(0, 0, dAngle * 180 / math.pi) #print self.display.viewerposition # Scroll if self.display.viewerposition changes if self.display.viewerposition.copy() != self.viewerOldPos: self.scroll() self.viewerOldPos = self.display.viewerposition.copy() # for particle in self.currentDisplayedPhysics.particles: # particle.oldpoint = None if event.type == pygame.KEYUP: if event.key == pygame.K_LSHIFT or event.key == pygame.K_RSHIFT: self.multiSelectMode = False elif event.key == pygame.K_LCTRL or event.key == pygame.K_RCTRL: self.rotationMode = False def scroll(self): # Scroll the surface by resetting gluLookAt glMatrixMode(GL_PROJECTION) glLoadIdentity() self.display.setProjection() def gotoDisplayLevel(self, dlevel): # Save current level's viewer position self.levelViewerPos[ self.currentLevel] = self.display.viewerposition.copy() # Display next level self.currentLevel += dlevel # Reset viewer position to previous try: self.display.viewerposition = self.levelViewerPos[ self.currentLevel].copy() except KeyError: self.display.viewerposition = self.levelViewerPos[ self.currentLevel] = Vector() # Remove current displayed particles for particle in self.currentDisplayedPhysics.particles: self.display.ogl_displaylists.pop(id(particle)) self.display.ogl_transforms.pop(id(particle)) self.currentDisplayedPhysics.removeByID( *self.currentDisplayedPhysics.particleDict.keys()) def doCommand(self, msg): """\ Proceses a topology command tuple: [ "ADD", "NODE", <id>, <name>, <positionSpec>, <particle type> ] [ "DEL", "NODE", <id> ] [ "ADD", "LINK", <id from>, <id to> ] [ "DEL", "LINK", <id from>, <id to> ] [ "DEL", "ALL" ] [ "GET", "ALL" ] """ #print 'doCommand' if len(msg) >= 2: cmd = msg[0].upper(), msg[1].upper() if cmd == ("ADD", "NODE") and len(msg) == 6: if msg[2] in [p.ID for p in self.physics.particles]: print "Node exists, please use a new node ID!" else: if self.particleTypes.has_key(msg[5]): #print 'ADD NODE begin' ptype = self.particleTypes[msg[5]] ident = msg[2] name = msg[3] posSpec = msg[4] pos = self._generatePos(posSpec) #print pos particle = ptype(position=pos, ID=ident, name=name) particle.originaltype = msg[5] #self.particles.append(particle) #print self.particles[0] self.addParticle(particle) self.isNewNode = True #print id(particle) #print 'ADD NODE end' elif cmd == ("DEL", "NODE") and len(msg) == 3: ident = msg[2] self.removeParticle(ident) elif cmd == ("ADD", "LINK") and len(msg) == 4: src = msg[2] dst = msg[3] self.makeBond(src, dst) elif cmd == ("DEL", "LINK") and len(msg) == 4: src = msg[2] dst = msg[3] self.breakBond(src, dst) elif cmd == ("DEL", "ALL") and len(msg) == 2: self.removeParticle(*self.physics.particleDict.keys()) self.currentLevel = 0 self.currentParentParticleID = '' elif cmd == ("GET", "ALL") and len(msg) == 2: topology = [("DEL", "ALL")] topology.extend(self.getTopology()) self.send(("TOPOLOGY", topology), "outbox") elif cmd == ("UPDATE_NAME", "NODE") and len(msg) == 4: node_id = msg[2] new_name = msg[3] self.updateParticleLabel(node_id, new_name) self.send(("UPDATE_NAME", "NODE", node_id, new_name), "outbox") elif cmd == ("GET_NAME", "NODE") and len(msg) == 3: node_id = msg[2] name = self.getParticleLabel(node_id) self.send(("GET_NAME", "NODE", node_id, name), "outbox") else: print "Command Error: please check your command format!" else: print "Command Error: not enough parameters!" def _generatePos(self, posSpec): """\ generateXY(posSpec) -> (x,y,z) or raises ValueError posSpec == "randompos" or "auto" -> random (x,y,z) within the surface (specified border distance in from the edege) posSpec == "(XXX,YYY,ZZZ)" -> specified x,y,z (positive or negative integers) """ posSpec = posSpec.lower() if posSpec == "randompos" or posSpec == "auto": # FIXME: need to consider camera/ viewer setting zLim = self.display.nearPlaneDist, self.display.farPlaneDist z = -1 * random.randrange( int((zLim[1] - zLim[0]) / 20) + self.border, int((zLim[1] - zLim[0]) / 8) - self.border, 1) yLim = z * math.tan( self.display.perspectiveAngle * math.pi / 360.0), -z * math.tan( self.display.perspectiveAngle * math.pi / 360.0) xLim = yLim[0] * self.display.aspectRatio, yLim[ 1] * self.display.aspectRatio y = random.randrange( int(yLim[0]) + self.border, int(yLim[1]) - self.border, 1) x = random.randrange( int(xLim[0]) + self.border, int(xLim[1]) - self.border, 1) #print x,y,z return x, y, z else: match = re.match("^([+-]?\d+),([+-]?\d+),([+-]?\d+)$", posSpec) if match: x = int(match.group(1)) y = int(match.group(2)) z = int(match.group(3)) return x, y, z raise ValueError("Unrecognised position specification") def addParticle(self, *particles): """Add particles to the system""" for p in particles: if p.radius > self.biggestRadius: self.biggestRadius = p.radius pLevel = p.ID.count(':') if self.maxLevel < pLevel: self.maxLevel = pLevel # create display request for every particle added disprequest = { "OGL_DISPLAYREQUEST": True, "objectid": id(p), "callback": (self, "callback"), "events": (self, "events"), "size": p.size } # send display request self.send(disprequest, "display_signal") self.physics.add(*particles) def removeParticle(self, *ids): """\ Remove particle(s) specified by their ids. Also breaks any bonds to/from that particle. """ for ident in ids: self.physics.particleDict[ident].breakAllBonds() try: self.display.ogl_objects.remove( id(self.physics.particleDict[ident])) self.display.ogl_names.pop(id( self.physics.particleDict[ident])) self.display.ogl_displaylists.pop( id(self.physics.particleDict[ident])) self.display.ogl_transforms.pop( id(self.physics.particleDict[ident])) except KeyError: pass # if self.selected == self.physics.particleDict[id]: # self.selectParticle(None) self.physics.removeByID(*ids) for ident in ids: try: self.currentDisplayedPhysics.removeByID(ident) except KeyError: pass #print self.currentDisplayedPhysics.particles #print self.physics.particles def selectParticle(self, particle): """Select the specified particle.""" if self.multiSelectMode: if particle not in self.selectedParticles: particle.select() self.selectedParticles.append(particle) self.send("('SELECT', 'NODE', '" + particle.name + "')", "outbox") else: particle.deselect() self.selectedParticles.remove(particle) self.send("('DESELECT', 'NODE', '" + particle.name + "')", "outbox") else: self.deselectAll() self.selectedParticles = [] particle.select() self.selectedParticles.append(particle) self.send("('SELECT', 'NODE', '" + particle.name + "')", "outbox") def deselectAll(self): """Deselect all particles.""" for particle in self.selectedParticles: particle.deselect() self.selectedParticles = [] def makeBond(self, source, dest): """Make a bond from source to destination particle, specified by IDs""" self.physics.particleDict[source].makeBond(self.physics.particleDict, dest) self.physics.particleDict[source].needRedraw = True def breakBond(self, source, dest): """Break a bond from source to destination particle, specified by IDs""" self.physics.particleDict[source].breakBond(self.physics.particleDict, dest) self.physics.particleDict[source].needRedraw = True def updateParticleLabel(self, node_id, new_name): """\ updateParticleLabel(node_id, new_name) -> updates the given nodes name & visual label if it exists node_id - an id for an already existing node new_name - a string (may include spaces) defining the new node name """ for p in self.physics.particles: if p.ID == node_id: p.set_label(new_name) p.needRedraw = True return def getParticleLabel(self, node_id): """\ getParticleLabel(node_id) -> particle's name Returns the name/label of the specified particle. """ for p in self.physics.particles: if p.ID == node_id: return p.name def getTopology(self): """getTopology() -> list of command tuples that would build the current topology""" topology = [] # first, enumerate the particles for particle in self.physics.particles: topology.append(("ADD", "NODE", particle.ID, particle.name, "random", particle.originaltype)) # now enumerate the linkages for particle in self.physics.particles: for dst in particle.getBondedTo(): topology.append(("ADD", "LINK", particle.ID, dst.ID)) return topology
def rotate(angle, vector): cos = math.cos(angle) sin = math.sin(angle) return (cos * vector[0] + sin * vector[1], -sin * vector[0] + cos * vector[1]) def dist(vector): return (vector[0] * vector[0] + vector[1] * vector[1])**0.5 if __name__ == '__main__': import Axon from Kamaelia.UI.OpenGL.OpenGLDisplay import OpenGLDisplay from Kamaelia.UI.OpenGL.SimpleRotationInteractor import SimpleRotationInteractor display = OpenGLDisplay(background_colour=(0.75, 0.75, 1.0)).activate() OpenGLDisplay.setDisplayService(display) FOLD = Simple3dFold(position=(0, 0, -22), size=(15, 15, 2), rotation=(-30, 0, 0), radius=1.0, segments=15).activate() # SimpleRotationInteractor(target=FOLD).activate() print "Grab close to a corner and drag!" Axon.Scheduler.scheduler.run.runThreads()
def left90(vector): return (-vector[1],vector[0]) def right90(vector): return (vector[1],-vector[0]) def rotate(angle, vector): cos = math.cos(angle) sin = math.sin(angle) return ( cos*vector[0]+sin*vector[1], -sin*vector[0]+cos*vector[1] ) def dist(vector): return (vector[0]*vector[0] + vector[1]*vector[1])**0.5 if __name__ == '__main__': import Axon from Kamaelia.UI.OpenGL.OpenGLDisplay import OpenGLDisplay from Kamaelia.UI.OpenGL.SimpleRotationInteractor import SimpleRotationInteractor display = OpenGLDisplay(background_colour=(0.75, 0.75, 1.0)).activate() OpenGLDisplay.setDisplayService(display) FOLD = Simple3dFold(position=(0,0,-22), size=(15,15,2), rotation=(-30,0,0),radius=1.0,segments=15).activate() # SimpleRotationInteractor(target=FOLD).activate() print "Grab close to a corner and drag!" Axon.Scheduler.scheduler.run.runThreads()
class TopologyViewer3D(Axon.Component.component): """\ TopologyViewer3D(...) -> new TopologyViewer3D component. A component that takes incoming topology (change) data and displays it live using pygame OpenGL. A simple physics model assists with visual layout. Particle types, appearance and physics interactions can be customised. Keyword arguments (in order): - screensize -- (width,height) of the display area (default = (800,600)) - fullscreen -- True to start up in fullscreen mode (default = False) - caption -- Caption for the pygame window (default = "3D Topology Viewer") - particleTypes -- dict("type" -> klass) mapping types of particle to classes used to render them (default = {"-":CuboidParticle3D}) - initialTopology -- (nodes,bonds) where bonds=list((src,dst)) starting state for the topology (default=([],[])) - laws -- Physics laws to apply between particles (default = SimpleLaws(bondlength=2)) - simCyclesPerRedraw -- number of physics sim cycles to run between each redraw (default=1) - border -- Minimum distance from edge of display area that new particles appear (default=0) """ Inboxes = { "inbox": "Topology (change) data describing an Axon system", "control": "Shutdown signalling", "callback": "for the response after a displayrequest", "events": "Place where we recieve events from the outside world", } Outboxes = { "signal": "Control signalling", "outbox": "Notification and topology output", "display_signal": "Requests to Pygame Display service", } def __init__(self, screensize=(800, 600), fullscreen=False, caption="3D Topology Viewer", particleTypes=None, initialTopology=None, laws=None, simCyclesPerRedraw=1, border=0): """x.__init__(...) initializes x; see x.__class__.__doc__ for signature""" super(TopologyViewer3D, self).__init__() glutInit(sys.argv) tracker = _cat.coordinatingassistanttracker.getcat() try: self.display = tracker.retrieveService("ogl_display")[0] except KeyError: self.display = OpenGLDisplay(width=screensize[0], height=screensize[1], fullscreen=fullscreen, title=caption) self.display.activate() OpenGLDisplay.setDisplayService(self.display, tracker) self.display = OpenGLDisplay.getDisplayService()[0] self.link((self, "display_signal"), (self.display, "notify")) self.link((self.display, "signal"), (self, "control")) self.border = border if particleTypes == None: self.particleTypes = { "-": CuboidParticle3D, "cuboid": CuboidParticle3D, "sphere": SphereParticle3D, "teapot": TeapotParticle3D } else: self.particleTypes = particleTypes if initialTopology == None: initialTopology = ([], []) self.initialNodes = list(initialTopology[0]) self.initialBonds = list(initialTopology[1]) self.hitParticles = [] self.multiSelectMode = False self.selectedParticles = [] self.grabbed = False self.rotationMode = False if laws == None: self.laws = Kamaelia.Support.Particles.SimpleLaws(bondLength=2) else: self.laws = laws self.physics = ParticleSystem(self.laws, [], 0) self.biggestRadius = 0 # Do interaction self.simCyclesPerRedraw = simCyclesPerRedraw self.lastIdleTime = time.time() # Tell if new node is added; if true, new id needs adding to OpenGLDisplay list self.isNewNode = False # For hierarchy structure self.maxLevel = 0 self.currentLevel = 0 self.previousParentParticleID = self.currentParentParticleID = '' self.viewerOldPos = Vector() self.levelViewerPos = {} # The Physics particle system of current display level for display self.currentDisplayedPhysics = ParticleSystem(self.laws, [], 0) # For double click self.lastClickPos = (0, 0) self.lastClickTime = time.time() self.dClickRes = 0.3 def initialiseComponent(self): """Initialises.""" self.addListenEvents([ pygame.MOUSEBUTTONDOWN, pygame.MOUSEBUTTONUP, pygame.MOUSEMOTION, pygame.KEYDOWN, pygame.KEYUP ]) # For key holding handling pygame.key.set_repeat(100, 100) for node in self.initialNodes: self.addParticle(*node) for source, dest in self.initialBonds: self.makeBond(source, dest) def main(self): """Main loop.""" # Make display request for event listening purpose self.size = Vector(0, 0, 0) disprequest = { "OGL_DISPLAYREQUEST": True, "objectid": id(self), "callback": (self, "callback"), "events": (self, "events"), "size": self.size } # send display request self.send(disprequest, "display_signal") # Wait for response on displayrequest and get identifier of the viewer while not self.dataReady("callback"): yield 1 self.identifier = self.recv("callback") self.initialiseComponent() while True: # Process incoming messages if self.dataReady("inbox"): message = self.recv("inbox") self.doCommand(message) # Wait for response on displayrequest and get identifier of the particle if self.isNewNode: while not self.dataReady("callback"): yield 1 self.physics.particles[-1].identifier = self.recv( "callback") self.isNewNode = False else: self.lastIdleTime = 0 yield 1 if self.lastIdleTime + 1.0 < time.time(): #Freeze selected particles so that they are not subject to the physics law for particle in self.selectedParticles: particle.freeze() # Do interaction between particles self.currentDisplayedPhysics.run(self.simCyclesPerRedraw) # Unfreeze selected particles for particle in self.selectedParticles: particle.unFreeze() # Draw particles if new or updated for particle in self.currentDisplayedPhysics.particles: if particle.needRedraw: self.drawParticles(particle) self.handleEvents() # Perform transformation for particle in self.currentDisplayedPhysics.particles: transform_update = particle.applyTransforms() if transform_update is not None: self.send(transform_update, "display_signal") self.lastIdleTime = time.time() else: yield 1 if self.dataReady("control"): msg = self.recv("control") if isinstance(msg, Axon.Ipc.shutdownMicroprocess): self.quit(msg) def quit(self, msg=Axon.Ipc.shutdownMicroprocess()): """Cause termination.""" print('Shut down...') self.send(msg, "signal") self.scheduler.stop() def draw(self): """\ Dummy method reserved for future use Invoke draw() and save its commands to a newly generated displaylist. The displaylist name is then sent to the display service via a "DISPLAYLIST_UPDATE" request. """ pass def drawParticles(self, *particles): """\ Sends particles drawing opengl command to the display service. """ for particle in particles: # Display list id displaylist = glGenLists(1) # Draw object to its displaylist glNewList(displaylist, GL_COMPILE) particle.draw() glEndList() # Send displaylist dl_update = { "DISPLAYLIST_UPDATE": True, "objectid": id(particle), "displaylist": displaylist } self.send(dl_update, "display_signal") def addListenEvents(self, events): """\ Sends listening request for pygame events to the display service. The events parameter is expected to be a list of pygame event constants. """ for event in events: self.send({ "ADDLISTENEVENT": event, "objectid": id(self) }, "display_signal") def removeListenEvents(self, events): """\ Sends stop listening request for pygame events to the display service. The events parameter is expected to be a list of pygame event constants. """ for event in events: self.send({ "REMOVELISTENEVENT": event, "objectid": id(self) }, "display_signal") def handleEvents(self): """Handle events.""" while self.dataReady("events"): event = self.recv("events") if event.type == pygame.MOUSEBUTTONDOWN or event.type == pygame.MOUSEMOTION or event.type == pygame.MOUSEBUTTONUP: self.handleMouseEvents(event) elif event.type == pygame.KEYDOWN or event.type == pygame.KEYUP: self.handleKeyEvents(event) # Scroll if self.display.viewerposition changes if self.display.viewerposition.copy() != self.viewerOldPos: self.scroll() self.viewerOldPos = self.display.viewerposition.copy() def handleMouseEvents(self, event): """Handle mouse events.""" if event.type == pygame.MOUSEBUTTONDOWN or pygame.MOUSEMOTION and self.grabbed: if not self.rotationMode: for particle in self.hitParticles: p1 = Vector(*particle.pos).copy() p1.x += 10 p2 = Vector(*particle.pos).copy() p2.y += 10 # Get the position of mouse z = Intersect.ray_Plane(Vector(0, 0, 0), event.direction, [ Vector(*particle.pos) - Vector(0, 0, self.display.viewerposition.z), p1 - Vector(0, 0, self.display.viewerposition.z), p2 - Vector(0, 0, self.display.viewerposition.z) ]) newpoint = event.direction * z if event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: # Handle double click clickPos = event.pos currentTime = time.time() elapsedTime = currentTime - self.lastClickTime # If it's a double-click if clickPos == self.lastClickPos and elapsedTime < self.dClickRes: self.gotoDisplayLevel(1) else: # Single click if not self.rotationMode: # Select particle for particle in self.currentDisplayedPhysics.particles: if particle.identifier in event.hitobjects: self.grabbed = True self.hitParticles.append(particle) self.selectParticle(particle) # If click places other than particles in non multiSelectMode, deselect all if not self.hitParticles and not self.multiSelectMode: self.deselectAll() self.lastClickPos = clickPos self.lastClickTime = currentTime elif event.button == 3: # Right-clicked self.gotoDisplayLevel(-1) elif event.button == 4: # Scrolled-up: zoom out if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles for particle in particles: posVector = Vector(*particle.pos) posVector.z -= 1 particle.pos = posVector.toTuple() elif event.button == 5: # Scrolled-down: zoom in if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles for particle in particles: posVector = Vector(*particle.pos) posVector.z += 1 particle.pos = posVector.toTuple() if event.type == pygame.MOUSEBUTTONUP: if event.button == 1: for particle in self.hitParticles: self.grabbed = False particle.oldpoint = None self.hitParticles.pop(self.hitParticles.index(particle)) if event.type == pygame.MOUSEMOTION: if not self.rotationMode and self.grabbed: # Drag particles for particle in self.hitParticles: try: if particle.oldpoint is not None: diff = newpoint - particle.oldpoint amount = (diff.x, diff.y) particle.pos = (Vector(*particle.pos) + Vector(*amount)).toTuple() except NameError: pass # Redraw the link so that the link can move with the particle for p in particle.bondedFrom: p.needRedraw = True elif self.rotationMode: # Rotate particles dAnglex = float(event.rel[1]) dAngley = -float(event.rel[0]) self.rotateParticles(self.selectedParticles, (dAnglex, dAngley, 0)) try: for particle in self.hitParticles: particle.oldpoint = newpoint except NameError: pass def handleKeyEvents(self, event): """Handle keyboard events.""" if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: self.quit() elif event.key == pygame.K_BACKSPACE: self.gotoDisplayLevel(-1) elif event.key == pygame.K_RETURN: self.gotoDisplayLevel(1) elif event.key == pygame.K_LSHIFT or event.key == pygame.K_RSHIFT: self.multiSelectMode = True elif event.key == pygame.K_LCTRL or event.key == pygame.K_RCTRL: self.rotationMode = True # Change viewer position elif event.key == pygame.K_PAGEUP: self.display.viewerposition.z -= 0.5 elif event.key == pygame.K_PAGEDOWN: self.display.viewerposition.z += 0.5 elif event.key == pygame.K_w: self.display.viewerposition.y += 0.5 elif event.key == pygame.K_s: self.display.viewerposition.y -= 0.5 elif event.key == pygame.K_a: self.display.viewerposition.x -= 0.5 elif event.key == pygame.K_d: self.display.viewerposition.x += 0.5 # Rotate particles elif event.key == pygame.K_UP: self.rotateParticles(self.selectedParticles, (-20, 0, 0)) elif event.key == pygame.K_DOWN: self.rotateParticles(self.selectedParticles, (20, 0, 0)) elif event.key == pygame.K_LEFT: self.rotateParticles(self.selectedParticles, (0, 20, 0)) elif event.key == pygame.K_RIGHT: self.rotateParticles(self.selectedParticles, (0, -20, 0)) elif event.key == pygame.K_COMMA: self.rotateParticles(self.selectedParticles, (0, 0, 20)) elif event.key == pygame.K_PERIOD: self.rotateParticles(self.selectedParticles, (0, 0, -20)) # Key exit (release) handling elif event.type == pygame.KEYUP: if event.key == pygame.K_LSHIFT or event.key == pygame.K_RSHIFT: # Return to normal mode from multiSelectMode self.multiSelectMode = False elif event.key == pygame.K_LCTRL or event.key == pygame.K_RCTRL: # Return to normal mode from rotationMode self.rotationMode = False def scroll(self): """Scroll the surface by resetting gluLookAt.""" glMatrixMode(GL_PROJECTION) glLoadIdentity() self.display.setProjection() def rotateParticles(self, particles, dAngle): """\ Rotate the particles around their common centre dAngle degree. Particles is a list; dAngle is a triple tuple of degree. If particles are given an empty list, rotate all particles instead. """ if particles == []: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) if dAngle[0] != 0: # Rotate around x axis for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.z * relativePosVector.z + relativePosVector.y * relativePosVector.y)**0.5 newAngle = ( math.atan2(relativePosVector.z, relativePosVector.y) + dAngle[0] * math.pi / 180) particle.pos = (posVector.x, radius * math.cos(newAngle) + centrePoint.y, radius * math.sin(newAngle) + centrePoint.z) particle.drotation += Vector(dAngle[0], 0, 0) if dAngle[1] != 0: # Rotate around y axis for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.z * relativePosVector.z + relativePosVector.x * relativePosVector.x)**0.5 newAngle = ( math.atan2(relativePosVector.z, relativePosVector.x) + dAngle[1] * math.pi / 180) particle.pos = (radius * math.cos(newAngle) + centrePoint.x, posVector.y, radius * math.sin(newAngle) + centrePoint.z) particle.drotation += Vector(0, -dAngle[1], 0) if dAngle[2] != 0: # Rotate around z axis for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.x * relativePosVector.x + relativePosVector.y * relativePosVector.y)**0.5 newAngle = ( math.atan2(relativePosVector.y, relativePosVector.x) + dAngle[2] * math.pi / 180) particle.pos = (radius * math.cos(newAngle) + centrePoint.x, radius * math.sin(newAngle) + centrePoint.y, posVector.z) particle.drotation += Vector(0, 0, dAngle[2]) # An angle keeps the same with when it minus muptiple 360 particle.drotation %= 360 def gotoDisplayLevel(self, dlevel): """Switch to another display level.""" isValid = False if self.currentLevel + dlevel > self.maxLevel: print("Warning: max hierarchy level has reached!") elif self.currentLevel + dlevel < 0: print("Warning: The first hierarchy level has reached!") else: if dlevel < 0: # Go to the last dlevel level self.previousParentParticleID = self.currentParentParticleID items = self.currentParentParticleID.split(':') for _ in xrange(-dlevel): items.pop() self.currentParentParticleID = ':'.join(items) isValid = True if dlevel == 1: # It only makes sense if dlevel == 1 when go to next dlevel level if len(self.selectedParticles) == 1: hasChildParticles = False for particle in self.physics.particles: if particle.ID.find( self.selectedParticles[0].ID ) == 0 and particle.ID != self.selectedParticles[0].ID: hasChildParticles = True break if hasChildParticles: self.previousParentParticleID = self.currentParentParticleID self.currentParentParticleID = self.selectedParticles[ 0].ID isValid = True else: print( 'Warning: The particle you double-clicked has no children!' ) else: print( "Tips: To extend a node, please double-click the node you want to extend" ) # Show the specified display level if valid if isValid: # Save current level's viewer position self.levelViewerPos[ self.currentLevel, self. previousParentParticleID] = self.display.viewerposition.copy() # Deselect all self.deselectAll() # Display next level self.currentLevel += dlevel # Reset viewer position to previous try: self.display.viewerposition = self.levelViewerPos[ self.currentLevel, self.currentParentParticleID].copy() except KeyError: self.display.viewerposition = self.levelViewerPos[ self.currentLevel, self.currentParentParticleID] = Vector() # Remove current displayed particles for particle in self.currentDisplayedPhysics.particles: self.display.ogl_displaylists.pop(id(particle)) self.display.ogl_transforms.pop(id(particle)) self.currentDisplayedPhysics.removeByID( *self.currentDisplayedPhysics.particleDict.keys()) # Add current level's particles to self.currentDisplayedPhysics.particles for display self.currentDisplayedPhysics.particles = [] if self.physics.particles != []: for particle in self.physics.particles: if self.currentParentParticleID == '': # If no parent, it's the top level if ':' not in particle.ID: self.currentDisplayedPhysics.add(particle) particle.oldpos = particle.initialpos # The child particles of self.currentParentParticleID elif particle.ID.find( self.currentParentParticleID ) == 0 and particle.ID.count(':') == self.currentLevel: self.currentDisplayedPhysics.add(particle) particle.oldpos = particle.initialpos def doCommand(self, msg): """\ Proceses a topology command tuple: [ "ADD", "NODE", <id>, <name>, <positionSpec>, <particle type> ] [ "DEL", "NODE", <id> ] [ "ADD", "LINK", <id from>, <id to> ] [ "DEL", "LINK", <id from>, <id to> ] [ "DEL", "ALL" ] [ "GET", "ALL" ] """ if len(msg) >= 2: cmd = msg[0].upper(), msg[1].upper() # Add default arguments when they are not provided if cmd == ("ADD", "NODE"): if len(msg) == 4: msg += ['randompos', '-'] elif len(msg) == 5: msg += ['-'] if cmd == ("ADD", "NODE") and len(msg) == 6: if msg[2] in [p.ID for p in self.physics.particles]: print("Node exists, please use a new node ID!") else: if (msg[5] in self.particleTypes): ptype = self.particleTypes[msg[5]] ident = msg[2] name = msg[3] posSpec = msg[4] pos = self._generatePos(posSpec) particle = ptype(position=pos, ID=ident, name=name) particle.originaltype = msg[5] self.addParticle(particle) self.isNewNode = True elif cmd == ("DEL", "NODE") and len(msg) == 3: ident = msg[2] self.removeParticle(ident) elif cmd == ("ADD", "LINK") and len(msg) == 4: src = msg[2] dst = msg[3] self.makeBond(src, dst) elif cmd == ("DEL", "LINK") and len(msg) == 4: src = msg[2] dst = msg[3] self.breakBond(src, dst) elif cmd == ("DEL", "ALL") and len(msg) == 2: self.removeParticle(*self.physics.particleDict.keys()) self.currentLevel = 0 self.currentParentParticleID = '' elif cmd == ("GET", "ALL") and len(msg) == 2: topology = [("DEL", "ALL")] topology.extend(self.getTopology()) self.send(("TOPOLOGY", topology), "outbox") elif cmd == ("UPDATE_NAME", "NODE") and len(msg) == 4: node_id = msg[2] new_name = msg[3] self.updateParticleLabel(node_id, new_name) self.send(("UPDATE_NAME", "NODE", node_id, new_name), "outbox") elif cmd == ("GET_NAME", "NODE") and len(msg) == 3: node_id = msg[2] name = self.getParticleLabel(node_id) self.send(("GET_NAME", "NODE", node_id, name), "outbox") else: print("Command Error: please check your command format!") else: print("Command Error: not enough parameters!") def _generatePos(self, posSpec): """\ generateXY(posSpec) -> (x,y,z) or raises ValueError posSpec == "randompos" or "auto" -> random (x,y,z) within the surface (specified border distance in from the edege) posSpec == "(XXX,YYY,ZZZ)" -> specified x,y,z (positive or negative integers) spaces are allowed within the tuple, but quotation is needed in this case. E.g., " ( 0 , 0 , -10 ) " """ posSpec = posSpec.lower() if posSpec == "randompos" or posSpec == "auto": zLim = self.display.nearPlaneDist, self.display.farPlaneDist z = -1 * random.randrange( int((zLim[1] - zLim[0]) / 20) + self.border, int((zLim[1] - zLim[0]) / 8) - self.border, 1) yLim = z * math.tan( self.display.perspectiveAngle * math.pi / 360.0), -z * math.tan( self.display.perspectiveAngle * math.pi / 360.0) xLim = yLim[0] * self.display.aspectRatio, yLim[ 1] * self.display.aspectRatio y = random.randrange( int(yLim[0]) + self.border, int(yLim[1]) - self.border, 1) x = random.randrange( int(xLim[0]) + self.border, int(xLim[1]) - self.border, 1) # Apply camera/ viewer transformation x += self.display.viewerposition.x y += self.display.viewerposition.y z += self.display.viewerposition.z return x, y, z else: # given specified position posSpec = posSpec.strip() # Use triple tuple format for position match = re.match( "^\( *([+-]?\d+) *, *([+-]?\d+) *, *([+-]?\d+) *\)$", posSpec) if match: x = int(match.group(1)) y = int(match.group(2)) z = int(match.group(3)) return x, y, z raise ValueError("Unrecognised position specification") def addParticle(self, *particles): """Add particles to the system""" for p in particles: if p.radius > self.biggestRadius: self.biggestRadius = p.radius pLevel = p.ID.count(':') if self.maxLevel < pLevel: self.maxLevel = pLevel # Make display request for every particle added disprequest = { "OGL_DISPLAYREQUEST": True, "objectid": id(p), "callback": (self, "callback"), "events": (self, "events"), "size": p.size } # Send display request self.send(disprequest, "display_signal") self.physics.add(*particles) # Add new particles to self.currentDisplayedPhysics for particle in particles: if self.currentParentParticleID == '': # If no parent, it's the top level if ':' not in particle.ID: self.currentDisplayedPhysics.add(particle) particle.oldpos = particle.initialpos # The child particles of self.currentParentParticleID elif particle.ID.find( self.currentParentParticleID) == 0 and particle.ID.count( ':') == self.currentLevel: self.currentDisplayedPhysics.add(particle) particle.oldpos = particle.initialpos def removeParticle(self, *ids): """\ Remove particle(s) specified by their ids. Also breaks any bonds to/from that particle. """ for ident in ids: self.physics.particleDict[ident].breakAllBonds() try: self.display.ogl_objects.remove( id(self.physics.particleDict[ident])) self.display.ogl_names.pop(id( self.physics.particleDict[ident])) self.display.ogl_displaylists.pop( id(self.physics.particleDict[ident])) self.display.ogl_transforms.pop( id(self.physics.particleDict[ident])) except KeyError: pass self.physics.removeByID(*ids) for ident in ids: try: self.currentDisplayedPhysics.removeByID(ident) except KeyError: pass def selectParticle(self, particle): """Select the specified particle.""" if self.multiSelectMode: if particle not in self.selectedParticles: particle.select() self.selectedParticles.append(particle) self.send("('SELECT', 'NODE', '" + particle.name + "')", "outbox") else: particle.deselect() self.selectedParticles.remove(particle) self.send("('DESELECT', 'NODE', '" + particle.name + "')", "outbox") else: self.deselectAll() self.selectedParticles = [] particle.select() self.selectedParticles.append(particle) self.send("('SELECT', 'NODE', '" + particle.name + "')", "outbox") def deselectAll(self): """Deselect all particles.""" for particle in self.selectedParticles: particle.deselect() self.selectedParticles = [] def makeBond(self, source, dest): """Make a bond from source to destination particle, specified by IDs""" self.physics.particleDict[source].makeBond(self.physics.particleDict, dest) self.physics.particleDict[source].needRedraw = True def breakBond(self, source, dest): """Break a bond from source to destination particle, specified by IDs""" self.physics.particleDict[source].breakBond(self.physics.particleDict, dest) self.physics.particleDict[source].needRedraw = True def updateParticleLabel(self, node_id, new_name): """\ updateParticleLabel(node_id, new_name) -> updates the given nodes name & visual label if it exists node_id - an id for an already existing node new_name - a string (may include spaces) defining the new node name """ for p in self.physics.particles: if p.ID == node_id: p.set_label(new_name) p.needRedraw = True return def getParticleLabel(self, node_id): """\ getParticleLabel(node_id) -> particle's name Returns the name/label of the specified particle. """ for p in self.physics.particles: if p.ID == node_id: return p.name def getTopology(self): """getTopology() -> list of command tuples that would build the current topology""" topology = [] # first, enumerate the particles for particle in self.physics.particles: topology.append(("ADD", "NODE", particle.ID, particle.name, "random", particle.originaltype)) # now enumerate the linkages for particle in self.physics.particles: for dst in particle.getBondedTo(): topology.append(("ADD", "LINK", particle.ID, dst.ID)) return topology
class TopologyViewer3D(Axon.Component.component): """\ TopologyViewer3D(...) -> new TopologyViewer3D component. A component that takes incoming topology (change) data and displays it live using pygame OpenGL. A simple physics model assists with visual layout. Particle types, appearance and physics interactions can be customised. Keyword arguments (in order): - screensize -- (width,height) of the display area (default = (800,600)) - fullscreen -- True to start up in fullscreen mode (default = False) - caption -- Caption for the pygame window (default = "3D Topology Viewer") - particleTypes -- dict("type" -> klass) mapping types of particle to classes used to render them (default = {"-":CuboidParticle3D}) - initialTopology -- (nodes,bonds) where bonds=list((src,dst)) starting state for the topology (default=([],[])) - laws -- Physics laws to apply between particles (default = SimpleLaws(bondlength=2)) - simCyclesPerRedraw -- number of physics sim cycles to run between each redraw (default=1) - border -- Minimum distance from edge of display area that new particles appear (default=0) """ Inboxes = { "inbox" : "Topology (change) data describing an Axon system", "control" : "Shutdown signalling", "callback" : "for the response after a displayrequest", "events" : "Place where we recieve events from the outside world", } Outboxes = { "signal" : "Control signalling", "outbox" : "Notification and topology output", "display_signal" : "Requests to Pygame Display service", } def __init__(self, screensize = (800,600), fullscreen = False, caption = "3D Topology Viewer", particleTypes = None, initialTopology = None, laws = None, simCyclesPerRedraw = 1, border = 0): """x.__init__(...) initializes x; see x.__class__.__doc__ for signature""" super(TopologyViewer3D, self).__init__() glutInit(sys.argv) tracker = _cat.coordinatingassistanttracker.getcat() try: self.display = tracker.retrieveService("ogl_display")[0] except KeyError: self.display = OpenGLDisplay(width=screensize[0], height=screensize[1],fullscreen=fullscreen, title=caption) self.display.activate() OpenGLDisplay.setDisplayService(self.display, tracker) self.display = OpenGLDisplay.getDisplayService()[0] self.link((self,"display_signal"), (self.display,"notify")) self.link((self.display,"signal"), (self,"control")) self.border = border if particleTypes == None: self.particleTypes = {"-":CuboidParticle3D, "cuboid":CuboidParticle3D, "sphere":SphereParticle3D, "teapot":TeapotParticle3D} else: self.particleTypes = particleTypes if initialTopology == None: initialTopology = ([],[]) self.initialNodes = list(initialTopology[0]) self.initialBonds = list(initialTopology[1]) self.hitParticles = [] self.multiSelectMode = False self.selectedParticles = [] self.grabbed = False self.rotationMode = False if laws==None: self.laws = Kamaelia.Support.Particles.SimpleLaws(bondLength=2) else: self.laws = laws self.physics = ParticleSystem(self.laws, [], 0) self.biggestRadius = 0 # Do interaction self.simCyclesPerRedraw = simCyclesPerRedraw self.lastIdleTime = time.time() # Tell if new node is added; if true, new id needs adding to OpenGLDisplay list self.isNewNode = False # For hierarchy structure self.maxLevel = 0 self.currentLevel = 0 self.previousParentParticleID = self.currentParentParticleID = '' self.viewerOldPos = Vector() self.levelViewerPos = {} # The Physics particle system of current display level for display self.currentDisplayedPhysics = ParticleSystem(self.laws, [], 0) # For double click self.lastClickPos = (0,0) self.lastClickTime = time.time() self.dClickRes = 0.3 def initialiseComponent(self): """Initialises.""" self.addListenEvents( [pygame.MOUSEBUTTONDOWN, pygame.MOUSEBUTTONUP, pygame.MOUSEMOTION, pygame.KEYDOWN, pygame.KEYUP ]) # For key holding handling pygame.key.set_repeat(100,100) for node in self.initialNodes: self.addParticle(*node) for source,dest in self.initialBonds: self.makeBond(source, dest) def main(self): """Main loop.""" # Make display request for event listening purpose self.size = Vector(0,0,0) disprequest = { "OGL_DISPLAYREQUEST" : True, "objectid" : id(self), "callback" : (self,"callback"), "events" : (self, "events"), "size": self.size } # send display request self.send(disprequest, "display_signal") # Wait for response on displayrequest and get identifier of the viewer while not self.dataReady("callback"): yield 1 self.identifier = self.recv("callback") self.initialiseComponent() while True: # Process incoming messages if self.dataReady("inbox"): message = self.recv("inbox") self.doCommand(message) # Wait for response on displayrequest and get identifier of the particle if self.isNewNode: while not self.dataReady("callback"): yield 1 self.physics.particles[-1].identifier = self.recv("callback") self.isNewNode = False else: self.lastIdleTime = 0 yield 1 if self.lastIdleTime + 1.0 < time.time(): #Freeze selected particles so that they are not subject to the physics law for particle in self.selectedParticles: particle.freeze() # Do interaction between particles self.currentDisplayedPhysics.run(self.simCyclesPerRedraw) # Unfreeze selected particles for particle in self.selectedParticles: particle.unFreeze() # Draw particles if new or updated for particle in self.currentDisplayedPhysics.particles: if particle.needRedraw: self.drawParticles(particle) self.handleEvents() # Perform transformation for particle in self.currentDisplayedPhysics.particles: transform_update = particle.applyTransforms() if transform_update is not None: self.send(transform_update, "display_signal") self.lastIdleTime = time.time() else: yield 1 if self.dataReady("control"): msg = self.recv("control") if isinstance(msg, Axon.Ipc.shutdownMicroprocess): self.quit(msg) def quit(self,msg=Axon.Ipc.shutdownMicroprocess()): """Cause termination.""" print ('Shut down...') self.send(msg, "signal") self.scheduler.stop() def draw(self): """\ Dummy method reserved for future use Invoke draw() and save its commands to a newly generated displaylist. The displaylist name is then sent to the display service via a "DISPLAYLIST_UPDATE" request. """ pass def drawParticles(self, *particles): """\ Sends particles drawing opengl command to the display service. """ for particle in particles: # Display list id displaylist = glGenLists(1) # Draw object to its displaylist glNewList(displaylist, GL_COMPILE) particle.draw() glEndList() # Send displaylist dl_update = { "DISPLAYLIST_UPDATE": True, "objectid": id(particle), "displaylist": displaylist } self.send(dl_update, "display_signal") def addListenEvents(self, events): """\ Sends listening request for pygame events to the display service. The events parameter is expected to be a list of pygame event constants. """ for event in events: self.send({"ADDLISTENEVENT":event, "objectid":id(self)}, "display_signal") def removeListenEvents(self, events): """\ Sends stop listening request for pygame events to the display service. The events parameter is expected to be a list of pygame event constants. """ for event in events: self.send({"REMOVELISTENEVENT":event, "objectid":id(self)}, "display_signal") def handleEvents(self): """Handle events.""" while self.dataReady("events"): event = self.recv("events") if event.type == pygame.MOUSEBUTTONDOWN or event.type == pygame.MOUSEMOTION or event.type == pygame.MOUSEBUTTONUP: self.handleMouseEvents(event) elif event.type == pygame.KEYDOWN or event.type == pygame.KEYUP: self.handleKeyEvents(event) # Scroll if self.display.viewerposition changes if self.display.viewerposition.copy() != self.viewerOldPos: self.scroll() self.viewerOldPos = self.display.viewerposition.copy() def handleMouseEvents(self, event): """Handle mouse events.""" if event.type == pygame.MOUSEBUTTONDOWN or pygame.MOUSEMOTION and self.grabbed: if not self.rotationMode: for particle in self.hitParticles: p1 = Vector(*particle.pos).copy() p1.x += 10 p2 = Vector(*particle.pos).copy() p2.y += 10 # Get the position of mouse z = Intersect.ray_Plane(Vector(0,0,0), event.direction, [Vector(*particle.pos)-Vector(0,0,self.display.viewerposition.z), p1-Vector(0,0,self.display.viewerposition.z), p2-Vector(0,0,self.display.viewerposition.z)]) newpoint = event.direction * z if event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: # Handle double click clickPos = event.pos currentTime = time.time() elapsedTime = currentTime - self.lastClickTime # If it's a double-click if clickPos == self.lastClickPos and elapsedTime<self.dClickRes: self.gotoDisplayLevel(1) else: # Single click if not self.rotationMode: # Select particle for particle in self.currentDisplayedPhysics.particles: if particle.identifier in event.hitobjects: self.grabbed = True self.hitParticles.append(particle) self.selectParticle(particle) # If click places other than particles in non multiSelectMode, deselect all if not self.hitParticles and not self.multiSelectMode: self.deselectAll() self.lastClickPos = clickPos self.lastClickTime = currentTime elif event.button == 3: # Right-clicked self.gotoDisplayLevel(-1) elif event.button == 4: # Scrolled-up: zoom out if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles for particle in particles: posVector = Vector(*particle.pos) posVector.z -= 1 particle.pos = posVector.toTuple() elif event.button == 5: # Scrolled-down: zoom in if self.selectedParticles: particles = self.selectedParticles else: particles = self.currentDisplayedPhysics.particles for particle in particles: posVector = Vector(*particle.pos) posVector.z += 1 particle.pos = posVector.toTuple() if event.type == pygame.MOUSEBUTTONUP: if event.button == 1: for particle in self.hitParticles: self.grabbed = False particle.oldpoint = None self.hitParticles.pop(self.hitParticles.index(particle)) if event.type == pygame.MOUSEMOTION: if not self.rotationMode and self.grabbed: # Drag particles for particle in self.hitParticles: try: if particle.oldpoint is not None: diff = newpoint-particle.oldpoint amount = (diff.x, diff.y) particle.pos = (Vector(*particle.pos)+Vector(*amount)).toTuple() except NameError: pass # Redraw the link so that the link can move with the particle for p in particle.bondedFrom: p.needRedraw = True elif self.rotationMode: # Rotate particles dAnglex = float(event.rel[1]) dAngley = -float(event.rel[0]) self.rotateParticles(self.selectedParticles, (dAnglex,dAngley,0)) try: for particle in self.hitParticles: particle.oldpoint = newpoint except NameError: pass def handleKeyEvents(self, event): """Handle keyboard events.""" if event.type == pygame.KEYDOWN: if event.key == pygame.K_ESCAPE: self.quit() elif event.key == pygame.K_BACKSPACE: self.gotoDisplayLevel(-1) elif event.key == pygame.K_RETURN: self.gotoDisplayLevel(1) elif event.key == pygame.K_LSHIFT or event.key == pygame.K_RSHIFT: self.multiSelectMode = True elif event.key == pygame.K_LCTRL or event.key == pygame.K_RCTRL: self.rotationMode = True # Change viewer position elif event.key == pygame.K_PAGEUP: self.display.viewerposition.z -= 0.5 elif event.key == pygame.K_PAGEDOWN: self.display.viewerposition.z += 0.5 elif event.key == pygame.K_w: self.display.viewerposition.y += 0.5 elif event.key == pygame.K_s: self.display.viewerposition.y -= 0.5 elif event.key == pygame.K_a: self.display.viewerposition.x -= 0.5 elif event.key == pygame.K_d: self.display.viewerposition.x += 0.5 # Rotate particles elif event.key == pygame.K_UP: self.rotateParticles(self.selectedParticles, (-20,0,0)) elif event.key == pygame.K_DOWN: self.rotateParticles(self.selectedParticles, (20,0,0)) elif event.key == pygame.K_LEFT: self.rotateParticles(self.selectedParticles, (0,20,0)) elif event.key == pygame.K_RIGHT: self.rotateParticles(self.selectedParticles, (0,-20,0)) elif event.key == pygame.K_COMMA: self.rotateParticles(self.selectedParticles, (0,0,20)) elif event.key == pygame.K_PERIOD: self.rotateParticles(self.selectedParticles, (0,0,-20)) # Key exit (release) handling elif event.type == pygame.KEYUP: if event.key == pygame.K_LSHIFT or event.key == pygame.K_RSHIFT: # Return to normal mode from multiSelectMode self.multiSelectMode = False elif event.key == pygame.K_LCTRL or event.key == pygame.K_RCTRL: # Return to normal mode from rotationMode self.rotationMode = False def scroll( self ): """Scroll the surface by resetting gluLookAt.""" glMatrixMode(GL_PROJECTION) glLoadIdentity() self.display.setProjection() def rotateParticles( self, particles, dAngle ): """\ Rotate the particles around their common centre dAngle degree. Particles is a list; dAngle is a triple tuple of degree. If particles are given an empty list, rotate all particles instead. """ if particles == []: particles = self.currentDisplayedPhysics.particles centrePoint = Vector() for particle in particles: posVector = Vector(*particle.pos) centrePoint += posVector centrePoint /= len(particles) if dAngle[0] != 0: # Rotate around x axis for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.z*relativePosVector.z+relativePosVector.y*relativePosVector.y)**0.5 newAngle = (math.atan2(relativePosVector.z,relativePosVector.y)+dAngle[0]*math.pi/180) particle.pos = (posVector.x, radius*math.cos(newAngle)+centrePoint.y, radius*math.sin(newAngle)+centrePoint.z) particle.drotation += Vector(dAngle[0],0,0) if dAngle[1] != 0: # Rotate around y axis for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.z*relativePosVector.z+relativePosVector.x*relativePosVector.x)**0.5 newAngle = (math.atan2(relativePosVector.z,relativePosVector.x)+dAngle[1]*math.pi/180) particle.pos = (radius*math.cos(newAngle)+centrePoint.x, posVector.y, radius*math.sin(newAngle)+centrePoint.z) particle.drotation += Vector(0,-dAngle[1],0) if dAngle[2] != 0: # Rotate around z axis for particle in particles: posVector = Vector(*particle.pos) relativePosVector = posVector - centrePoint radius = (relativePosVector.x*relativePosVector.x+relativePosVector.y*relativePosVector.y)**0.5 newAngle = (math.atan2(relativePosVector.y,relativePosVector.x)+dAngle[2]*math.pi/180) particle.pos = (radius*math.cos(newAngle)+centrePoint.x, radius*math.sin(newAngle)+centrePoint.y, posVector.z) particle.drotation += Vector(0,0,dAngle[2]) # An angle keeps the same with when it minus muptiple 360 particle.drotation %= 360 def gotoDisplayLevel( self, dlevel): """Switch to another display level.""" isValid = False if self.currentLevel + dlevel > self.maxLevel: print ("Warning: max hierarchy level has reached!") elif self.currentLevel + dlevel < 0: print ("Warning: The first hierarchy level has reached!") else: if dlevel < 0: # Go to the last dlevel level self.previousParentParticleID = self.currentParentParticleID items = self.currentParentParticleID.split(':') for _ in xrange(-dlevel): items.pop() self.currentParentParticleID = ':'.join(items) isValid = True if dlevel == 1: # It only makes sense if dlevel == 1 when go to next dlevel level if len(self.selectedParticles) == 1: hasChildParticles = False for particle in self.physics.particles: if particle.ID.find(self.selectedParticles[0].ID) == 0 and particle.ID != self.selectedParticles[0].ID: hasChildParticles = True break if hasChildParticles: self.previousParentParticleID = self.currentParentParticleID self.currentParentParticleID = self.selectedParticles[0].ID isValid = True else: print ('Warning: The particle you double-clicked has no children!') else: print ("Tips: To extend a node, please double-click the node you want to extend") # Show the specified display level if valid if isValid: # Save current level's viewer position self.levelViewerPos[self.currentLevel, self.previousParentParticleID] = self.display.viewerposition.copy() # Deselect all self.deselectAll() # Display next level self.currentLevel += dlevel # Reset viewer position to previous try: self.display.viewerposition = self.levelViewerPos[self.currentLevel, self.currentParentParticleID].copy() except KeyError: self.display.viewerposition = self.levelViewerPos[self.currentLevel, self.currentParentParticleID] = Vector() # Remove current displayed particles for particle in self.currentDisplayedPhysics.particles: self.display.ogl_displaylists.pop(id(particle)) self.display.ogl_transforms.pop(id(particle)) self.currentDisplayedPhysics.removeByID(*self.currentDisplayedPhysics.particleDict.keys()) # Add current level's particles to self.currentDisplayedPhysics.particles for display self.currentDisplayedPhysics.particles = [] if self.physics.particles != []: for particle in self.physics.particles: if self.currentParentParticleID == '': # If no parent, it's the top level if ':' not in particle.ID: self.currentDisplayedPhysics.add( particle ) particle.oldpos = particle.initialpos # The child particles of self.currentParentParticleID elif particle.ID.find(self.currentParentParticleID) == 0 and particle.ID.count(':') == self.currentLevel: self.currentDisplayedPhysics.add( particle ) particle.oldpos = particle.initialpos def doCommand(self, msg): """\ Proceses a topology command tuple: [ "ADD", "NODE", <id>, <name>, <positionSpec>, <particle type> ] [ "DEL", "NODE", <id> ] [ "ADD", "LINK", <id from>, <id to> ] [ "DEL", "LINK", <id from>, <id to> ] [ "DEL", "ALL" ] [ "GET", "ALL" ] """ if len(msg) >= 2: cmd = msg[0].upper(), msg[1].upper() # Add default arguments when they are not provided if cmd == ("ADD", "NODE"): if len(msg) == 4: msg += ['randompos', '-'] elif len(msg) == 5: msg += ['-'] if cmd == ("ADD", "NODE") and len(msg) == 6: if msg[2] in [p.ID for p in self.physics.particles]: print ("Node exists, please use a new node ID!") else: if ( msg[5] in self.particleTypes ): ptype = self.particleTypes[msg[5]] ident = msg[2] name = msg[3] posSpec = msg[4] pos = self._generatePos(posSpec) particle = ptype(position = pos, ID=ident, name=name) particle.originaltype = msg[5] self.addParticle(particle) self.isNewNode = True elif cmd == ("DEL", "NODE") and len(msg) == 3: ident = msg[2] self.removeParticle(ident) elif cmd == ("ADD", "LINK") and len(msg) == 4: src = msg[2] dst = msg[3] self.makeBond(src, dst) elif cmd == ("DEL", "LINK") and len(msg) == 4: src = msg[2] dst = msg[3] self.breakBond(src, dst) elif cmd == ("DEL", "ALL") and len(msg) == 2: self.removeParticle(*self.physics.particleDict.keys()) self.currentLevel = 0 self.currentParentParticleID = '' elif cmd == ("GET", "ALL") and len(msg) == 2: topology = [("DEL","ALL")] topology.extend(self.getTopology()) self.send( ("TOPOLOGY", topology), "outbox" ) elif cmd == ("UPDATE_NAME", "NODE") and len(msg) == 4: node_id = msg[2] new_name = msg[3] self.updateParticleLabel(node_id, new_name) self.send( ("UPDATE_NAME", "NODE", node_id, new_name), "outbox" ) elif cmd == ("GET_NAME", "NODE") and len(msg) == 3: node_id = msg[2] name = self.getParticleLabel(node_id) self.send( ("GET_NAME", "NODE", node_id, name), "outbox" ) else: print ("Command Error: please check your command format!") else: print ("Command Error: not enough parameters!") def _generatePos(self, posSpec): """\ generateXY(posSpec) -> (x,y,z) or raises ValueError posSpec == "randompos" or "auto" -> random (x,y,z) within the surface (specified border distance in from the edege) posSpec == "(XXX,YYY,ZZZ)" -> specified x,y,z (positive or negative integers) spaces are allowed within the tuple, but quotation is needed in this case. E.g., " ( 0 , 0 , -10 ) " """ posSpec = posSpec.lower() if posSpec == "randompos" or posSpec == "auto" : zLim = self.display.nearPlaneDist, self.display.farPlaneDist z = -1*random.randrange(int((zLim[1]-zLim[0])/20)+self.border,int((zLim[1]-zLim[0])/8)-self.border,1) yLim = z*math.tan(self.display.perspectiveAngle*math.pi/360.0), -z*math.tan(self.display.perspectiveAngle*math.pi/360.0) xLim = yLim[0]*self.display.aspectRatio, yLim[1]*self.display.aspectRatio y = random.randrange(int(yLim[0])+self.border,int(yLim[1])-self.border,1) x = random.randrange(int(xLim[0])+self.border,int(xLim[1])-self.border,1) # Apply camera/ viewer transformation x += self.display.viewerposition.x y += self.display.viewerposition.y z += self.display.viewerposition.z return x,y,z else: # given specified position posSpec = posSpec.strip() # Use triple tuple format for position match = re.match("^\( *([+-]?\d+) *, *([+-]?\d+) *, *([+-]?\d+) *\)$", posSpec) if match: x = int(match.group(1)) y = int(match.group(2)) z = int(match.group(3)) return x,y,z raise ValueError("Unrecognised position specification") def addParticle(self, *particles): """Add particles to the system""" for p in particles: if p.radius > self.biggestRadius: self.biggestRadius = p.radius pLevel = p.ID.count(':') if self.maxLevel < pLevel: self.maxLevel = pLevel # Make display request for every particle added disprequest = { "OGL_DISPLAYREQUEST" : True, "objectid" : id(p), "callback" : (self,"callback"), "events" : (self, "events"), "size": p.size } # Send display request self.send(disprequest, "display_signal") self.physics.add( *particles ) # Add new particles to self.currentDisplayedPhysics for particle in particles: if self.currentParentParticleID == '': # If no parent, it's the top level if ':' not in particle.ID: self.currentDisplayedPhysics.add( particle ) particle.oldpos = particle.initialpos # The child particles of self.currentParentParticleID elif particle.ID.find(self.currentParentParticleID) == 0 and particle.ID.count(':') == self.currentLevel: self.currentDisplayedPhysics.add( particle ) particle.oldpos = particle.initialpos def removeParticle(self, *ids): """\ Remove particle(s) specified by their ids. Also breaks any bonds to/from that particle. """ for ident in ids: self.physics.particleDict[ident].breakAllBonds() try: self.display.ogl_objects.remove(id(self.physics.particleDict[ident])) self.display.ogl_names.pop(id(self.physics.particleDict[ident])) self.display.ogl_displaylists.pop(id(self.physics.particleDict[ident])) self.display.ogl_transforms.pop(id(self.physics.particleDict[ident])) except KeyError: pass self.physics.removeByID(*ids) for ident in ids: try: self.currentDisplayedPhysics.removeByID(ident) except KeyError: pass def selectParticle(self, particle): """Select the specified particle.""" if self.multiSelectMode: if particle not in self.selectedParticles: particle.select() self.selectedParticles.append(particle) self.send( "('SELECT', 'NODE', '"+particle.name+"')", "outbox" ) else: particle.deselect() self.selectedParticles.remove(particle) self.send( "('DESELECT', 'NODE', '"+particle.name+"')", "outbox" ) else: self.deselectAll() self.selectedParticles = [] particle.select() self.selectedParticles.append(particle) self.send( "('SELECT', 'NODE', '"+particle.name+"')", "outbox" ) def deselectAll(self): """Deselect all particles.""" for particle in self.selectedParticles: particle.deselect() self.selectedParticles = [] def makeBond(self, source, dest): """Make a bond from source to destination particle, specified by IDs""" self.physics.particleDict[source].makeBond(self.physics.particleDict, dest) self.physics.particleDict[source].needRedraw = True def breakBond(self, source, dest): """Break a bond from source to destination particle, specified by IDs""" self.physics.particleDict[source].breakBond(self.physics.particleDict, dest) self.physics.particleDict[source].needRedraw = True def updateParticleLabel(self, node_id, new_name): """\ updateParticleLabel(node_id, new_name) -> updates the given nodes name & visual label if it exists node_id - an id for an already existing node new_name - a string (may include spaces) defining the new node name """ for p in self.physics.particles: if p.ID == node_id: p.set_label(new_name) p.needRedraw = True return def getParticleLabel(self, node_id): """\ getParticleLabel(node_id) -> particle's name Returns the name/label of the specified particle. """ for p in self.physics.particles: if p.ID == node_id: return p.name def getTopology(self): """getTopology() -> list of command tuples that would build the current topology""" topology = [] # first, enumerate the particles for particle in self.physics.particles: topology.append( ( "ADD","NODE", particle.ID, particle.name, "random", particle.originaltype ) ) # now enumerate the linkages for particle in self.physics.particles: for dst in particle.getBondedTo(): topology.append( ( "ADD","LINK", particle.ID, dst.ID ) ) return topology
def hideInfo(self): self.send("Backward", "infomover_commands") self.send("Play", "infomover_commands") if __name__ == "__main__": from Kamaelia.Chassis.Graphline import Graphline from Kamaelia.Util.Console import ConsoleReader from Kamaelia.UI.PygameDisplay import PygameDisplay from Kamaelia.UI.OpenGL.OpenGLDisplay import OpenGLDisplay from Kamaelia.Protocol.HTTP.HTTPClient import SimpleHTTPClient from Kamaelia.Protocol.Torrent.TorrentPatron import TorrentPatron ogl_display = OpenGLDisplay(limit_fps=100).activate() OpenGLDisplay.setDisplayService(ogl_display) # override pygame display service PygameDisplay.setDisplayService(ogl_display) Graphline( reader = ConsoleReader(prompt="Enter torrent location:", eol=""), httpclient = SimpleHTTPClient(), gui = TorrentOpenGLGUI(), backend = TorrentPatron(), linkages = { ("gui", "outbox") : ("backend", "inbox"), ("reader", "outbox") : ("gui", "torrent_url"), ("gui", "fetcher") : ("httpclient", "inbox"), ("httpclient", "outbox") : ("gui", "torrent_file"), ("backend", "outbox"): ("gui", "inbox") }
self.send(infostring, "torrent_info") def hideInfo(self): self.send("Backward", "infomover_commands") self.send("Play", "infomover_commands") if __name__ == "__main__": from Kamaelia.Chassis.Graphline import Graphline from Kamaelia.Util.Console import ConsoleReader from Kamaelia.UI.PygameDisplay import PygameDisplay from Kamaelia.UI.OpenGL.OpenGLDisplay import OpenGLDisplay from Kamaelia.Protocol.HTTP.HTTPClient import SimpleHTTPClient from Kamaelia.Protocol.Torrent.TorrentPatron import TorrentPatron ogl_display = OpenGLDisplay(limit_fps=100).activate() OpenGLDisplay.setDisplayService(ogl_display) # override pygame display service PygameDisplay.setDisplayService(ogl_display) Graphline(reader=ConsoleReader(prompt="Enter torrent location:", eol=""), httpclient=SimpleHTTPClient(), gui=TorrentOpenGLGUI(), backend=TorrentPatron(), linkages={ ("gui", "outbox"): ("backend", "inbox"), ("reader", "outbox"): ("gui", "torrent_url"), ("gui", "fetcher"): ("httpclient", "inbox"), ("httpclient", "outbox"): ("gui", "torrent_file"), ("backend", "outbox"): ("gui", "inbox") }).run()