From 0366f46d3d7e946e254f933888aea4beb4e70658 Mon Sep 17 00:00:00 2001 From: Russell Cohen Date: Wed, 1 Dec 2010 19:46:14 -0500 Subject: Added support for RecursiveHooks, RecursiveBehaviors and GaussianMapper supporting Gaussian based pixel mapping. --- LightInstallation.py | 9 +++++-- Util.py | 7 +++++ behaviors/BehaviorChain.py | 9 +++++++ behaviors/ColorChangerBehavior.py | 6 +++-- behaviors/DecayBehavior.py | 2 +- behaviors/RecursiveDecay.py | 12 +++++++++ config/GaussianMapper.params | 3 +++ config/LightInstallationConfig.xml | 52 ++++++++++++++++++++++++++++++++++---- config/RecursiveDecay.params | 2 ++ operationscore/Behavior.py | 1 - operationscore/SmootCoreObject.py | 10 +++++++- pixelmappers/GaussianMapper.py | 13 ++++++++++ 12 files changed, 114 insertions(+), 12 deletions(-) create mode 100644 behaviors/RecursiveDecay.py create mode 100644 config/GaussianMapper.params create mode 100644 config/RecursiveDecay.params create mode 100644 pixelmappers/GaussianMapper.py diff --git a/LightInstallation.py b/LightInstallation.py index 7d8b7b0..849e41f 100644 --- a/LightInstallation.py +++ b/LightInstallation.py @@ -80,14 +80,20 @@ class LightInstallation: return True def mainLoop(self): #self.screen.allOn() + lastLoopTime = Util.time() + refreshInterval = 30 while 1: - time.sleep(.1) + loopStart = Util.time() responses = self.evaluateBehaviors() #inputs are all queued when they #happen, so we only need to run the behaviors [self.screen.respond(response) for response in responses if response != []] self.screen.timeStep() [r.render(self.screen) for r in self.renderers] + loopElapsed = Util.time()-loopStart + sleepTime = max(0,refreshInterval-loopElapsed) + if sleepTime > 0: + time.sleep(sleepTime/1000) #evaluates all the behaviors (including inter-dependencies) and returns a #list of responses to go to the screen. def evaluateBehaviors(self): @@ -114,7 +120,6 @@ class LightInstallation: if inputId in self.inputBehaviorRegistry: #it could be a behavior self.inputBehaviorRegistry[inputId].append(behavior['Id']) def processResponse(self,inputDict, responseDict): - 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 48ad3c5..377c2e3 100644 --- a/Util.py +++ b/Util.py @@ -41,12 +41,19 @@ def addPixelEventIfMissing(responseDict): else: raise Exception('Need Color. Probably') responseDict['PixelEvent'] = StepEvent.generate(300, color) +def gaussian(x,height,center,width): + a=height + b=center + c=width + return a*math.exp(-((x-b)**2)/(2*c**2)) def dist(l1, l2): return math.sqrt(sum([(l1[i]-l2[i])**2 for i in range(len(l1))])) def time(): return clock.time()*1000 def randomColor(): return [random.randint(0,255) for i in range(3)] +def chooseRandomColor(colorList): + return random.choice(colorList) def loadParamRequirementDict(className): return fileToDict(CONFIG_PATH + className) def loadConfigFile(fileName): diff --git a/behaviors/BehaviorChain.py b/behaviors/BehaviorChain.py index 8bf97bb..8b5cb1d 100644 --- a/behaviors/BehaviorChain.py +++ b/behaviors/BehaviorChain.py @@ -4,6 +4,9 @@ import pdb class BehaviorChain(Behavior): def behaviorInit(self): self.feedback = {} #dictionary to allow feedback of recursives + self.hooks = self['RecursiveHooks'] + if self.hooks == None: + self.hooks = {} def processResponse(self, sensorInputs, recursiveInputs): response = sensorInputs for behaviorId in self['ChainedBehaviors']: @@ -14,5 +17,11 @@ class BehaviorChain(Behavior): recurrence = [] (response,recurrence) = behavior.immediateProcessInput(response,\ recurrence) + + if behaviorId in self.hooks: #process recursive hook if there is one + hookBehavior = Util.getComponentById(self.hooks[behaviorId]) + (response, recurrence) = \ + hookBehavior.immediateProcessInput(response, + recurrence) self.feedback[behaviorId] = recurrence return response diff --git a/behaviors/ColorChangerBehavior.py b/behaviors/ColorChangerBehavior.py index 8ecc5f2..ca80eb4 100644 --- a/behaviors/ColorChangerBehavior.py +++ b/behaviors/ColorChangerBehavior.py @@ -6,7 +6,9 @@ class ColorChangerBehavior(Behavior): ret = [] for sensory in sensorInputs: newDict = dict(sensory) #don't run into shallow copy issues - #TODO: support for PixelEvents - newDict['Color'] = Util.randomColor() + if self['ColorList'] != None: + newDict['Color'] = Util.randomColor(self['ColorList']) + else: + newDict['Color'] = Util.chooseRandomColor() ret.append(newDict) return ret diff --git a/behaviors/DecayBehavior.py b/behaviors/DecayBehavior.py index 7956cbc..edc833d 100644 --- a/behaviors/DecayBehavior.py +++ b/behaviors/DecayBehavior.py @@ -11,4 +11,4 @@ class DecayBehavior(Behavior): outDict['PixelEvent'] = \ DecayEvent.generate(self['DecayType'],self['Coefficient'], sensory['Color']) ret.append(outDict) - return ret + return (ret, recursiveInputs) diff --git a/behaviors/RecursiveDecay.py b/behaviors/RecursiveDecay.py new file mode 100644 index 0000000..b119b85 --- /dev/null +++ b/behaviors/RecursiveDecay.py @@ -0,0 +1,12 @@ +from operationscore.Behavior import * +class RecursiveDecay(Behavior): + def processResponse(self, sensorInputs, recursiveInputs): + ret = [] + for response in recursiveInputs: + if not 'ResponsesLeft' in response: + response['ResponsesLeft'] = self['InitialResponseCount'] + else: + response['ResponsesLeft'] -= 1 + if response['ResponsesLeft'] > 0: + ret.append(response) + return (sensorInputs, ret) #no direct ouput diff --git a/config/GaussianMapper.params b/config/GaussianMapper.params new file mode 100644 index 0000000..288ec1d --- /dev/null +++ b/config/GaussianMapper.params @@ -0,0 +1,3 @@ +{'CutoffDist':'CutoffDist, the distance at which a light will not be turned +on','Height':'The height of the gaussian','Width':'The width of the +gaussian'} diff --git a/config/LightInstallationConfig.xml b/config/LightInstallationConfig.xml index 441b7e4..aa331af 100644 --- a/config/LightInstallationConfig.xml +++ b/config/LightInstallationConfig.xml @@ -31,13 +31,24 @@ - + + + + pixelmappers.GaussianMapper + + gaussmap + 30 + 5 + .5 + + @@ -47,7 +58,7 @@ (1300,50) - + @@ -66,6 +77,14 @@ 100 + + inputs.PygameInput + + followmouse + 100 + True + + inputs.UDPInput @@ -102,7 +121,7 @@ decay Exponential - .005 + .001 0 False @@ -119,6 +138,13 @@ + + behaviors.RecursiveDecay + + recursivedecay + 20000 + + behaviors.BehaviorChain @@ -131,6 +157,22 @@ running decay + {'running':'recursivedecay'} + True + + + + behaviors.BehaviorChain + + mousechaser + + followmouse + + + echo + colorchange + decay + True @@ -141,7 +183,7 @@ pygame - 10 + 1 False diff --git a/config/RecursiveDecay.params b/config/RecursiveDecay.params new file mode 100644 index 0000000..fe9d553 --- /dev/null +++ b/config/RecursiveDecay.params @@ -0,0 +1,2 @@ +{'InitialResponseCount':'Initial response count, the number of times a +recursive response can occur, must be specified.'} diff --git a/operationscore/Behavior.py b/operationscore/Behavior.py index 8db2d88..e300f6b 100644 --- a/operationscore/Behavior.py +++ b/operationscore/Behavior.py @@ -37,7 +37,6 @@ class Behavior(SmootCoreObject): except: return (self.processResponse(sensorInputs, recursiveInputs),[]) def addInputs(self, sensorInputs): - print sensorInputs if type(sensorInputs) == type([]): [self.addInput(sensorInput) for sensorInput in sensorInputs] else: diff --git a/operationscore/SmootCoreObject.py b/operationscore/SmootCoreObject.py index 2901ef6..84319af 100644 --- a/operationscore/SmootCoreObject.py +++ b/operationscore/SmootCoreObject.py @@ -3,9 +3,14 @@ import pdb class SmootCoreObject: def __init__(self, argDict): self.argDict = argDict + self.validateArgs(self.className()+'.params') self.init() #call init of inheriting class + # self.__setitem__ = self.argDict.__setitem__ + # self.__getitem__ = self.argDict.__getitem__ def init(self): pass + def className(self): + return str(self.__class__).split('.')[-1] def __setitem__(self,k, item): self.argDict[k] = item def __getitem__(self, item): @@ -16,7 +21,10 @@ class SmootCoreObject: def __getiter__(self): return self.argDict.__getiter__() def validateArgs(self, argFileName): - self.validateArgDict(Util.loadParamRequirementDict(argFileName)) + try: + self.validateArgDict(Util.loadParamRequirementDict(argFileName)) + except IOError: + print 'No Arg Dict found for ' + self.className() def validateArgDict(self, validationDict): for item in validationDict: if not item in self.argDict: diff --git a/pixelmappers/GaussianMapper.py b/pixelmappers/GaussianMapper.py new file mode 100644 index 0000000..552f5c9 --- /dev/null +++ b/pixelmappers/GaussianMapper.py @@ -0,0 +1,13 @@ +from operationscore.PixelMapper import * +import Util +class GaussianMapper(PixelMapper): + def mappingFunction(self, eventLocation, screen): + returnPixels = [] + for pixel in screen: + pixelDist = Util.dist(pixel.location, eventLocation) + if pixelDist < self['CutoffDist']: + w = Util.gaussian(pixelDist, self['Height'], 0, self['Width']) + if w>1: + pdb.set_trace() + returnPixels.append((pixel, w)) + return returnPixels -- cgit v1.2.3