From cf1f2224b3625b01a6aa7db221403849b308b3bc Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Mon, 29 Nov 2010 00:00:26 -0500 Subject: Making recursive behaviors work. Some bugs existed before. Adding running behavior which makes a signal bounce back and forth. --- LightInstallation.py | 10 ++++---- Util.py | 11 +++++++++ behaviors/BehaviorChain.py | 10 +++++++- behaviors/RunningBehavior.py | 21 +++++++++++++++++ config/LightInstallationConfig.xml | 48 +++++++++++++++++++++----------------- docs/designDocs.tex | 3 +++ inputs/TCPInput.py | 2 -- inputs/UDPInput.py | 3 --- operationscore/Behavior.py | 42 ++++++++++++++++++++++++--------- operationscore/PixelEvent.py | 7 ++++-- pixelcore/Screen.py | 30 +++++++++++++++++++++--- 11 files changed, 138 insertions(+), 49 deletions(-) create mode 100644 behaviors/RunningBehavior.py diff --git a/LightInstallation.py b/LightInstallation.py index 6866271..7d8b7b0 100644 --- a/LightInstallation.py +++ b/LightInstallation.py @@ -14,10 +14,11 @@ class LightInstallation: self.componentDict = {} self.inputBehaviorRegistry = {} #inputid -> behaviors listening to that #input - #give Util a pointer to our componentRegistry so that everyone can use + #give Util a pointer to our componentRegistry and screen so that everyone can use #it Util.setComponentDict(self.componentDict) self.screen = Screen() + Util.setScreen(self.screen) config = Util.loadConfigFile(configFileName) #read configs from xml rendererConfig = config.find('RendererConfiguration') @@ -59,9 +60,8 @@ class LightInstallation: self.renderers = self.initializeComponent(rendererConfig) def registerComponents(self, components): for component in components: - try: - cid = component['Id'] - except: + cid = component['Id'] + if cid == None: raise Exception('Components must have Ids!') self.componentDict[cid] = component def initializeComponent(self, config): @@ -114,7 +114,7 @@ class LightInstallation: if inputId in self.inputBehaviorRegistry: #it could be a behavior self.inputBehaviorRegistry[inputId].append(behavior['Id']) def processResponse(self,inputDict, responseDict): - #pdb.set_trace() + print inputDict, responseDict inputId = inputDict['Id'] boundBehaviorIds = self.inputBehaviorRegistry[inputId] [self.componentDict[b].addInput(responseDict) for b in boundBehaviorIds] diff --git a/Util.py b/Util.py index a398d7c..48ad3c5 100644 --- a/Util.py +++ b/Util.py @@ -6,6 +6,7 @@ import socket import random from pygame.locals import * import time as clock +from pixelevents.StepEvent import * VERSION = 0x0001 MAGIC = 0x4adc0104 MOREMAGIC = 0xdeadbeef @@ -16,6 +17,16 @@ UNI = 0 CONFIG_PATH = 'config/' kinetDict = {'flags': 0, 'startcode': 0, 'pad':0} componentDict = {} +#Only for rough estimates. Kindof lazy on specifics. +def pointWithinBoundingBox(point, bb): #this could be in 4 lines, but I'm lazy. + return sum([(point[i % 2] <= bb[i]) == (i>1) for i in range(4)]) == 4 +print pointWithinBoundingBox((118,21), (10,8,298,42)) +def addLocations(l1,l2): + return tuple([l1[i]+l2[i] for i in range(len(l1))]) +def setScreen(screen): + globals()["screen"] = screen +def getScreen(): + return screen def setComponentDict(componentDictRef): globals()["componentDict"] = componentDictRef def getComponentById(cid): diff --git a/behaviors/BehaviorChain.py b/behaviors/BehaviorChain.py index 15f7d74..8bf97bb 100644 --- a/behaviors/BehaviorChain.py +++ b/behaviors/BehaviorChain.py @@ -2,9 +2,17 @@ from operationscore.Behavior import * import Util import pdb class BehaviorChain(Behavior): + def behaviorInit(self): + self.feedback = {} #dictionary to allow feedback of recursives def processResponse(self, sensorInputs, recursiveInputs): response = sensorInputs for behaviorId in self['ChainedBehaviors']: behavior = Util.getComponentById(behaviorId) - response = behavior.immediateProcessInput(response) + if behaviorId in self.feedback: + recurrence = self.feedback[behaviorId] + else: + recurrence = [] + (response,recurrence) = behavior.immediateProcessInput(response,\ + recurrence) + self.feedback[behaviorId] = recurrence return response diff --git a/behaviors/RunningBehavior.py b/behaviors/RunningBehavior.py new file mode 100644 index 0000000..255ce69 --- /dev/null +++ b/behaviors/RunningBehavior.py @@ -0,0 +1,21 @@ +from operationscore.Behavior import * +import pdb +import Util +class RunningBehavior(Behavior): + def processResponse(self, sensorInputs, recursiveInputs): + newResponses = sensorInputs + ret = [] + ret += newResponses + for recurInput in recursiveInputs: + outDict = dict(recurInput) + if not 'Dir' in outDict: + outDict['Dir'] = 1 #to the right + outDict['Location']= Util.addLocations(outDict['Location'], + (self['StepSize']*outDict['Dir'],0)) + if not Util.pointWithinBoundingBox(outDict['Location'], \ + Util.getScreen().getSize()): + outDict['Dir'] *= -1 + ret.append(outDict) + ret += newResponses + return (ret, ret) + diff --git a/config/LightInstallationConfig.xml b/config/LightInstallationConfig.xml index 713646e..441b7e4 100644 --- a/config/LightInstallationConfig.xml +++ b/config/LightInstallationConfig.xml @@ -14,21 +14,18 @@ 12 50 - (10,10) + (10,20) - layouts.ZigzagLayout - + layouts.ZigzagLayout + strip2 25 X 1 12 - 12 + 12 50 (10,30) @@ -46,18 +43,20 @@ renderers.PygameRenderer + pygamerender (1300,50) - + @@ -67,14 +66,6 @@ 100 - - inputs.PygameInput - - followmouse - True - 100 - - inputs.UDPInput @@ -98,10 +89,11 @@ behaviors.ColorChangerBehavior - color + colorchange 0 False + pygame @@ -110,7 +102,7 @@ decay Exponential - .01 + .005 0 False @@ -130,16 +122,28 @@ behaviors.BehaviorChain + runcolordecay - followmouse + pygame - echo - color + colorchange + running decay True + + behaviors.RunningBehavior + + running + + pygame + + 10 + False + + diff --git a/docs/designDocs.tex b/docs/designDocs.tex index 327c280..0690d3f 100644 --- a/docs/designDocs.tex +++ b/docs/designDocs.tex @@ -179,4 +179,7 @@ argument. This will be passed in via recursive inputs.} \subsection{Color} For color, use a tuple of (R,G,B) 0-255 for each. Colors can be easily manipulated with members of the Util class. + \subsection{Locations} + Locations are stored (x,y), in whatever unit you light system is + in. (Whatever unit you use when you define spacing). \end{document} diff --git a/inputs/TCPInput.py b/inputs/TCPInput.py index 72d8742..01b6a99 100644 --- a/inputs/TCPInput.py +++ b/inputs/TCPInput.py @@ -15,8 +15,6 @@ class TCPInput(Input.Input): def handle(self): # get data from the TCP socket connected to the client self.data = self.request.recv(1024).strip() - print "%s wrote:" % self.client_address[0] - print self.data pydict = json.loads(self.data) # decode and add to queue self.responseQueue.append(pydict) diff --git a/inputs/UDPInput.py b/inputs/UDPInput.py index b0d6c93..5b83792 100644 --- a/inputs/UDPInput.py +++ b/inputs/UDPInput.py @@ -7,11 +7,8 @@ class UDPInput(Input): PORT = self.argDict['Port'] # Arbitrary non-privileged port self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.bind((HOST, PORT)) - print 'UDPINIT' def sensingLoop(self): - print 'udploop' (data,address) = self.sock.recvfrom(1024) dataDict = {'data':data, 'address':address} - print 'LOLOLOLOL' self.respond(dataDict) diff --git a/operationscore/Behavior.py b/operationscore/Behavior.py index 198c4b2..8db2d88 100644 --- a/operationscore/Behavior.py +++ b/operationscore/Behavior.py @@ -2,11 +2,11 @@ #inputs from all sensors it is bound to as well as any recursive inputs that it #spawned during the last time step. Inheriting classes MUST define #processResponse. processResponse should return a list of dictionaries which -#define the properties of the light response. They must give a location and -#color. They may define a function pointer which defines a custom mapping. -#[More on this later. Bug Russell if you want to do it]. -#recursiveResponse to queue a input on the next iteration with a dictionary -#argument. This will be passed in via recursive inputs. +#define the properties of the light response, (outputs, recursions). They must give a location and +#color. They may define a PixelEvent to more closely control the outgoing +#data, however, this is normally handled by routing the event to a behavior +#specifically designed to do this (like DecayBehavior). + import pdb from operationscore.SmootCoreObject import * #timeStep is called on every iteration of the LightInstallation @@ -19,23 +19,43 @@ class Behavior(SmootCoreObject): self.recursiveResponseQueue = [] self.sensorResponseQueue = [] self.outGoingQueue = [] + self.behaviorInit() + def behaviorInit(self): + pass def processResponse(self, sensorInputs, recursiveInputs): pass def addInput(self, sensorInput): self.sensorResponseQueue.append(sensorInput) #used for behavior chaining - def immediateProcessInput(self, sensorInputs): - return self.processResponse(sensorInputs, []) + def immediateProcessInput(self, sensorInputs, recursiveInputs=[]): + try: + (output,recursions) = self.processResponse(sensorInputs, \ + recursiveInputs) + if type(output) != type([]): + output = [output] + return (output, recursions) + except: + return (self.processResponse(sensorInputs, recursiveInputs),[]) def addInputs(self, sensorInputs): + print sensorInputs if type(sensorInputs) == type([]): [self.addInput(sensorInput) for sensorInput in sensorInputs] else: self.addInput(sensorInputs) - def recursiveReponse(self, args): - self.responseQueue.append(args) def timeStep(self): responses = self.processResponse(self.sensorResponseQueue, \ self.recursiveResponseQueue) + if type(responses) == type(tuple()) and len(responses) == 2: + (outputs, recursions) = responses + else: + outputs = responses + recursions = [] self.sensorResponseQueue = [] - self.recursiveResponseQueue = [] - return responses + self.recursiveResponseQueue = recursions + if type(outputs) != type([]): + outputs = [outputs] + try: + return outputs + except: + pdb.set_trace() + return outputs diff --git a/operationscore/PixelEvent.py b/operationscore/PixelEvent.py index 8567d93..66b6fdf 100644 --- a/operationscore/PixelEvent.py +++ b/operationscore/PixelEvent.py @@ -8,8 +8,11 @@ class PixelEvent(SmootCoreObject): self.initEvent() def initEvent(self): pass - def scale(c): - self['Color'] *= c + #Returns a new PixelEvent, but with a response scaled by c. + def scale(self,c): + newDict = dict(self.argDict) + newDict['Color'] = Util.multiplyColor(newDict['Color'], c) + return self.__class__(newDict) def state(self,timeDelay): pass diff --git a/pixelcore/Screen.py b/pixelcore/Screen.py index 561dc21..6b5c737 100644 --- a/pixelcore/Screen.py +++ b/pixelcore/Screen.py @@ -1,12 +1,17 @@ from pixelcore.Pixel import * from pixelcore.PixelStrip import * import itertools +#Class representing a collection of Pixels grouped into PixelStrips. Needs a +#PixelMapper, currently set via setMapper by may be migrated into the argDict. class Screen: def __init__(self): self.responseQueue = [] self.pixelStrips = [] + sizeValid = False def addStrip(self, lS): self.pixelStrips.append(lS) + self.sizeValid = False #keep track of whether or not our screen size has + #been invalidated by adding more pixels def render(self, surface): [lS.render(surface) for lS in self.pixelStrips] def setMapper(self, mapper): @@ -14,7 +19,8 @@ class Screen: def allOn(self): [lS.allOn(-1) for lS in self.pixelStrips] def __iter__(self): #the iterator of all our pixel strips chained togther - return itertools.chain(*[strip.__iter__() for strip in self.pixelStrips]) + return itertools.chain(*[strip.__iter__() for strip in \ + self.pixelStrips]) #the * operator breaks the list into args #increment time -- This processes all queued responses. Responses generated #during this period are added to the queue that will be processed on the next #time step. @@ -26,12 +32,30 @@ class Screen: #public def respond(self, responseInfo): self.responseQueue.append(responseInfo) + def getSize(self): + if self.sizeValid: + return self.size + (minX, minY, maxX, maxY) = (10**10,10**10,-10**10,-10*10) #TODO: don't + #be lazy + for light in self: + (x,y) = light.location + + minX = min(x, minX) + maxX = max(x, maxX) + + minY = min(y, minY) + maxY = max(y, maxY) + self.size = (minX, minY, maxX, maxY) + self.sizeValid = True + return (minX, minY, maxX, maxY) #private def processResponse(self, responseInfo): #we need to make a new dict for #each to prevent interference #[strip.respond(dict(responseInfo)) for strip in self.pixelStrips] + if type(responseInfo) != type(dict()): + pdb.set_trace() pixelWeightList = self.mapper.mapEvent(responseInfo['Location'], self) Util.addPixelEventIfMissing(responseInfo) - for (pixel, weight) in pixelWeightList: #TODO: weighting - pixel.processInput(responseInfo['PixelEvent'], 0) #TODO: z-index + for (pixel, weight) in pixelWeightList: + pixel.processInput(responseInfo['PixelEvent'].scale(weight), 0) #TODO: z-index -- cgit v1.2.3