diff options
author | rcoh <rcoh@mit.edu> | 2011-02-01 23:14:12 -0500 |
---|---|---|
committer | rcoh <rcoh@mit.edu> | 2011-02-01 23:14:12 -0500 |
commit | 5a97e7548b0ec44f0d61a724903748197e69df7c (patch) | |
tree | 7bb6b8ea7e488949eeb16ae18b0ad2230f1f7c9b | |
parent | 082e4b0c53123dd377da148541f7d98516716862 (diff) | |
parent | 482a94fd48627153b923931d6ff21ebf57fad6f7 (diff) |
Merge branch 'osc' of github.com:rcoh/SmootLight into osc
86 files changed, 2281 insertions, 330 deletions
diff --git a/LightInstallation.py b/LightInstallation.py index 06852e4..41cc925 100755 --- a/LightInstallation.py +++ b/LightInstallation.py @@ -91,8 +91,8 @@ class LightInstallation(object): self.inputs = inputs for inputClass in inputs: inputClass.start() - self.inputBehaviorRegistry[inputClass['Id']] = [] - #empty list is list of bound behaviors + self.inputBehaviorRegistry[inputClass['Id']] = [] #Bound behaviors will be added to this + #list def initializeRenderers(self, rendererConfig): self.renderers = self.initializeComponent(rendererConfig) @@ -100,13 +100,9 @@ class LightInstallation(object): def registerComponents(self, components): for component in components: cid = compReg.registerComponent(component) - main_log.debug(cid + ' registered') - cid = component['Id'] - if cid == None: #TODO: determine if componenent is critical, and if so, die - main_log.error('Components must be registered with Ids. Component not registered') - else: - compReg.registerComponent(component) - main_log.debug(cid + ' registered') + main_log.info(cid + ' registered') + compReg.registerComponent(component) + main_log.info(cid + ' registered') def initializeComponent(self, config): components = [] @@ -116,7 +112,6 @@ class LightInstallation(object): [module,className] = configItem.find('Class').text.split('.') except: main_log.error('Module must have Class element') - main_log.warn('Module without class element. Module not initialized') continue try: exec('from ' + module+'.'+className + ' import *') @@ -127,16 +122,13 @@ class LightInstallation(object): main_log.error(str(inst)) continue args = configGetter.pullArgsFromItem(configItem) - args['parentScope'] = self #TODO: we shouldn't give away scope - #like this, find another way. + args['parentScope'] = self try: new_component = eval(className+'(args)') new_component.addDieListener(self) - components.append(new_component) #TODO: doesn't error - main_log.debug(className + 'initialized with args ' + str(args)) - #right + components.append(new_component) + main_log.info(className + 'initialized with args ' + str(args)) except Exception as inst: - pdb.set_trace() main_log.error('Failure while initializing ' + className + ' with ' + str(args)) main_log.error(str(inst)) @@ -147,11 +139,10 @@ class LightInstallation(object): def mainLoop(self): lastLoopTime = clock.time() - refreshInterval = 30 - while not self.dieNow: + refreshInterval = 30 + while not self.dieNow: #dieNow is set if one of its constituents sends a die request. loopStart = clock.time() - responses = self.evaluateBehaviors() #inputs are all queued when they - #happen, so we only need to run the behaviors + responses = self.evaluateBehaviors() self.timer.start() [self.screen.respond(response) for response in responses if response != []] @@ -162,19 +153,17 @@ class LightInstallation(object): main_log.debug('Loop complete in ' + str(loopElapsed) + 'ms. Sleeping for ' +\ str(sleepTime)) self.timer.stop() - #print self.timer.elapsed() 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): + """Evaluates all the behaviors (including inter-dependencies) and returns a list of responses to + go to the screen""" responses = {} responses['Screen'] = [] #responses to the screen for behavior in self.behaviors: responses[behavior['Id']] = behavior.timeStep() - if behavior['RenderToScreen'] == True: #TODO: this uses extra space, - #we can use less in the future if needbe. + if behavior['RenderToScreen'] == True: responses['Screen'] += responses[behavior['Id']] return responses['Screen'] @@ -183,9 +172,9 @@ class LightInstallation(object): for behavior in self.behaviors: self.addBehavior(behavior) - #Does work needed to add a behavior: currently -- maps behavior inputs into - #the input behavior registry. def addBehavior(self, behavior): + """Does work needed to add a behavior: currently -- maps behavior inputs into the input behavior + registry""" for inputId in behavior.argDict['Inputs']: if inputId in self.inputBehaviorRegistry: #it could be a behavior self.inputBehaviorRegistry[inputId].append(behavior['Id']) @@ -193,19 +182,18 @@ class LightInstallation(object): def processResponse(self,inputDict, responseDict): inputId = inputDict['Id'] boundBehaviorIds = self.inputBehaviorRegistry[inputId] - #TODO: fix this, it crashes because inputs get run before beahviors exist try: [compReg.getComponent(b).addInput(responseDict) for b in boundBehaviorIds] except: pass - #print 'Behaviors not initialized yet. WAIT!' + #Behavior run before loading. Not a big deal. def handleDie(self, caller): self.dieNow = True def main(argv): if len(argv) == 1: - l = LightInstallation('LightInstallationConfig.xml') + l = LightInstallation('config/6thFloor.xml') else: l = LightInstallation(argv[1]) @@ -1,4 +1,4 @@ import cProfile from LightInstallation import main -command = """main(['', 'config/6thFloor.xml'])""" +command = """main(['', 'config/6thFloorOSC.xml'])""" cProfile.runctx(command, globals(), locals(), filename="smootlight.profile") diff --git a/behaviors/Accelerate.xml b/behaviors/Accelerate.xml index f9de077..c78195b 100644 --- a/behaviors/Accelerate.xml +++ b/behaviors/Accelerate.xml @@ -3,6 +3,6 @@ <Args> <ParamType>Sensor</ParamType> <ParamName>StepSize</ParamName> - <ParamOp>{val}*1.1</ParamOp> + <ParamOp>{val}*1.01</ParamOp> </Args> </Behavior> diff --git a/behaviors/AddPixelEvent.py b/behaviors/AddPixelEvent.py index 7f134e1..da3f7c2 100644 --- a/behaviors/AddPixelEvent.py +++ b/behaviors/AddPixelEvent.py @@ -2,6 +2,9 @@ from operationscore.Behavior import * import util.Strings as Strings from logger import main_log class AddPixelEvent(Behavior): + """AddPixelEvent is a behavior to append an arbitrary PixelEvent to a behavior response. The + classname of the PixelEvent should be specified in the Class field of Args. All arguments normally + passed to the PixelEvent should also be specified in Args.""" def behaviorInit(self): [module, className] = self['Class'].split('.') try: diff --git a/behaviors/AllPixels.py b/behaviors/AllPixels.py index e155e55..7f66ad6 100644 --- a/behaviors/AllPixels.py +++ b/behaviors/AllPixels.py @@ -1,6 +1,9 @@ from operationscore.Behavior import * class AllPixels(Behavior): - def processResponse(self, sensorInputs, recursiveInputs): - for sensory in sensorInputs:#TODO: consider replicating the dict - sensory['Location'] = 'True' - return (sensorInputs, recursiveInputs) + """Turns on all Pixels in the installation. Must use SimpleMapper, or other Mapper supporting + conditional pixel locations.""" + + def processResponse(self, sensorInputs, recursiveInputs): + for sensory in sensorInputs:#TODO: consider replicating the dict + sensory['Location'] = 'True' + return (sensorInputs, recursiveInputs) diff --git a/behaviors/AllPixelsLeft.py b/behaviors/AllPixelsLeft.py index b48bfc1..b223156 100644 --- a/behaviors/AllPixelsLeft.py +++ b/behaviors/AllPixelsLeft.py @@ -2,6 +2,7 @@ from operationscore.Behavior import * import util.ComponentRegistry as compReg import pdb class AllPixelsLeft(Behavior): + """Behavior which returns all points left of its input. No Args.""" def processResponse(self, sensorInputs, recursiveInputs): for sensory in sensorInputs: xLoc = sensory['Location'][0] diff --git a/behaviors/BehaviorChain.py b/behaviors/BehaviorChain.py index 39f4402..631ad98 100644 --- a/behaviors/BehaviorChain.py +++ b/behaviors/BehaviorChain.py @@ -3,6 +3,24 @@ import util.ComponentRegistry as compReg from logger import main_log import pdb class BehaviorChain(Behavior): + """BehaviorChain is a class which chains together multiple behavior. BehaviorChain is in itself a + behavior, and behaves and can be used accordingly. BehaviorChain also supports recursive hooks to + be set on its constituent behaviors. ChainedBehaviors should be specified in <Args> as follows: + + <ChainedBehaviors> + <Id>behavior1Id</Id> + <Id>behavior2Id</Id> + </ChainedBehaviors> + + Behaviors may also be appended programmatically via the appendBehavior method. + + Recursive hooks should be specified with Python dict syntax as follows: + + <RecursiveHooks>{'behavior1Id':'hookid'}</RecursiveHooks> + + Behavior Chain manages all recurrences that its constituents propogate. At this point, it does not + support recurrences in its hooks.""" + def behaviorInit(self): self.feedback = {} #dictionary to allow feedback of recursives self.hooks = self['RecursiveHooks'] @@ -27,11 +45,10 @@ class BehaviorChain(Behavior): hookBehavior.immediateProcessInput(recurrence, \ []) if hookRecurrence != []: - main_log.warn('Hook recurrences are not currently supported. Implement it\ -yourself or bug russell') + main_log.warn('Hook recurrences are not currently supported.') self.feedback[behaviorId] = recurrence return (response, []) - + def appendBehavior(behavior): bid = compReg.registerComponent(behavior) #register behavior (will make #a new id if there isn't one) diff --git a/behaviors/ColorChangerBehavior.py b/behaviors/ColorChangerBehavior.py index 2a8d974..e2f8bd3 100644 --- a/behaviors/ColorChangerBehavior.py +++ b/behaviors/ColorChangerBehavior.py @@ -2,12 +2,26 @@ from operationscore.Behavior import * import util.ColorOps as color import pdb class ColorChangerBehavior(Behavior): + """ColorChangerBehavior is a behavior for adding colors to responses. If given no arguments, it + will generate a random color. If it is given a list of colors [as below] it will pick randomly + from them. + + <ColorList> + <Color>(255,0,0)</Color> + <Color>(30,79,200)</Color> + </ColorList> + + ColorList also supports specification of a single color.""" + def processResponse(self, sensorInputs, recursiveInputs): ret = [] for sensory in sensorInputs: - newDict = dict(sensory) #don't run into shallow copy issues + newDict = dict(sensory) if self['ColorList'] != None: - newDict['Color'] = color.chooseRandomColor(self['ColorList']) #TODO: this doesn't work. + if isinstance(self['ColorList'], list): + newDict['Color'] = color.chooseRandomColor(self['ColorList']) #Pick randomly + else: + newDict['Color'] = self['ColorList'] #Unless there is only one else: newDict['Color'] = color.randomColor() ret.append(newDict) diff --git a/behaviors/ControllerOSC.py b/behaviors/ControllerOSC.py new file mode 100644 index 0000000..05d2e7d --- /dev/null +++ b/behaviors/ControllerOSC.py @@ -0,0 +1,62 @@ +from operationscore.Behavior import * +from logger import main_log +#import util.ColorOps as color +import colorsys +from numpy import array +import pdb +import util.ComponentRegistry as compReg + +speedfactor = 15 +vel_decay = .9 + +def constrainLocation(v,c): + if v[0] > c[0]: + v[0] = c[0] + elif v[0]<0: + v[0] = 0 + + if v[1] > c[1]: + v[1] = c[1] + elif v[1]<0: + v[1] = 0 + + return v + +class ControllerOSC(Behavior): + def behaviorInit(self): + self.xy = array((0,0)) + self.v_xy = array((0,0)) + self.v_decay = vel_decay + + self.start_hsv = [0,1,1] + self.dest_hsv = [0,1,1] + self.ssize = compReg.getComponent('Screen').getSize()[-2:] #896 x 310 + + def processResponse(self, sensorInputs, recursiveInputs): + ret = [] + if sensorInputs: + data = sensorInputs[-1]#for data in sensorInputs: + if data['Path'] == '/sixaxis/xy': + #try: + x = data['Value'][0] + y = data['Value'][1] + if y < 0: + self.start_hsv[1] = 1.0+y #s + else: + self.start_hsv[2] = 1.0-y + self.start_hsv[0] = (x+1)/2. + elif data['Path'] == '/sixaxis/lrud': + val=data['Value'] + vy = val[3]-val[2] + vx = val[1]-val[0] + #pdb.set_trace() + #self.v_xy = (val[1]*ssize[0], (1.0-val[0])*ssize[1]) + self.v_xy = array((vx, vy)) * speedfactor + else: + main_log.error('Sensor Inputs: ' + str(sensorInputs)) + self.xy = self.xy + self.v_xy + constrainLocation(self.xy,self.ssize) + self.v_xy *= self.v_decay + ret.append({'Color':[i*255. for i in colorsys.hsv_to_rgb(*self.start_hsv)],'Location':(int(self.xy[0]), int(self.xy[1]))}) + + return (ret, []) diff --git a/behaviors/DebugBehavior.py b/behaviors/DebugBehavior.py index 17383db..8f81954 100644 --- a/behaviors/DebugBehavior.py +++ b/behaviors/DebugBehavior.py @@ -2,6 +2,9 @@ from operationscore.Behavior import * from logger import main_log import pdb class DebugBehavior(Behavior): + """DebugBehavior simply writes all of its inputs to the logs, currently at the ERROR level for + easy visibility. Will be changed to DEBUG or INFO in the future""" + def processResponse(self, sensorInputs, recursiveInputs): if sensorInputs != []: main_log.error('Sensor Inputs: ' + str(sensorInputs)) diff --git a/behaviors/DecayBehavior.py b/behaviors/DecayBehavior.py index c1f6f92..f19ffc8 100644 --- a/behaviors/DecayBehavior.py +++ b/behaviors/DecayBehavior.py @@ -3,6 +3,7 @@ from pixelevents.DecayEvent import * import util.Strings as Strings import pdb class DecayBehavior(Behavior): + """DecayBehavior is obsolete. Use AddPixelEvent instead""" def processResponse(self, sensorInputs, recursiveInputs): ret = [] for sensory in sensorInputs: diff --git a/behaviors/Deccelerate.xml b/behaviors/Deccelerate.xml new file mode 100644 index 0000000..e64e61d --- /dev/null +++ b/behaviors/Deccelerate.xml @@ -0,0 +1,9 @@ + +<Behavior> + <Class>behaviors.ModifyParam</Class> + <Args> + <ParamType>Sensor</ParamType> + <ParamName>StepSize</ParamName> + <ParamOp>{val}*.98</ParamOp> + </Args> +</Behavior> diff --git a/behaviors/DimColor.xml b/behaviors/DimColor.xml new file mode 100644 index 0000000..ef98fee --- /dev/null +++ b/behaviors/DimColor.xml @@ -0,0 +1,8 @@ +<Behavior> + <Class>behaviors.ModifyParam</Class> + <Args> + <ParamType>Sensor</ParamType> + <ParamName>Color</ParamName> + <ParamOp>[chan*.98 for chan in {val}]</ParamOp> + </Args> +</Behavior> diff --git a/behaviors/EchoBehavior.py b/behaviors/EchoBehavior.py index 589c42b..c4af7c0 100644 --- a/behaviors/EchoBehavior.py +++ b/behaviors/EchoBehavior.py @@ -2,11 +2,16 @@ from operationscore.Behavior import * import util.Strings as Strings import pdb class EchoBehavior(Behavior): + """EchoBehavior generates a RED response at all locations specified in sensorInputs. Useful for + debugging""" def processResponse(self, sensorInputs, recursiveInputs): ret = [] for sensory in sensorInputs: outDict = {} outDict[Strings.LOCATION] = sensory[Strings.LOCATION] - outDict['Color'] = (255,0,0) + if self['Color'] != None: + outDict['Color'] = self['Color'] + else: + outDict['Color'] = (255,0,0) ret.append(outDict) return (ret, []) diff --git a/behaviors/Expand.py b/behaviors/Expand.py new file mode 100644 index 0000000..f017c16 --- /dev/null +++ b/behaviors/Expand.py @@ -0,0 +1,22 @@ +from operationscore.Behavior import * +class Expand(Behavior): + """Expand is a behavior that generates a response that grows horizontally starting a location + specifed in input. Required Args: + <ExpandRate>123</ExpandRate> which is the expandrate in units/response""" + + def processResponse(self, sensorInputs, recurs): + ret = [] + for data in sensorInputs: + if not 'Left' in data: #If this is the first time we have seen this input + data['Left'] = data['Location'][0] + data['Right'] = data['Location'][0] + data['ExpandRate'] = self['ExpandRate'] + + data = dict(data) + data['Left'] -= data['ExpandRate'] + data['Right'] += data['ExpandRate'] + data['Location'] = "{x}>" + str(data['Left']) + ", {x}<" +\ + str(data['Right'])+", {y}<50" + ret.append(data) + return (ret, []) + diff --git a/behaviors/ExpandingColorZones.py b/behaviors/ExpandingColorZones.py new file mode 100644 index 0000000..75be761 --- /dev/null +++ b/behaviors/ExpandingColorZones.py @@ -0,0 +1,21 @@ +from operationscore.Behavior import * +from logger import main_log +class ExpandingColorZones(Behavior): + def behaviorInit(self): + self.mapping = {'s001':[(132,0),(255,0,0)], 's002':[(400,0), (0,255,0)], + 's003':[(668,0), + (0,0,255)]} + self.mappingkey = 'data' + def processResponse(self, sensorInputs, recursiveInputs): + ret = [] + for data in sensorInputs: + print data + data = dict(data) + if self.mappingkey in data: + try: + data['Location'], data['Color'] =\ + self.mapping[data[self.mappingkey]] + ret.append(data) + except: + main_log.warn('Bad mapping key. Expanding Color Zones.') + return (ret,[]) diff --git a/behaviors/Flasher.py b/behaviors/Flasher.py new file mode 100644 index 0000000..1d79d41 --- /dev/null +++ b/behaviors/Flasher.py @@ -0,0 +1,41 @@ + +from operationscore.Behavior import * +import util.ColorOps as colorops +import pdb +class Flasher(Behavior): + """Implements a pulsing/flashing behavior. + Jim Salem: jsalem@gmail.com + + Args: + Factor - The speed of flashing. Must be b/w 0 and 1. Default is .95 + """ + def processResponse(self, sensorInputs, recursiveInputs): + ret = [] + for response in sensorInputs: + # Get the multiplier + if self['Factor'] != None: + factor = self['Factor'] + else: + factor = 0.95 + # Initialize the first time + if not 'FireflyStartColor' in response: + response['FireflyValue'] = 1.0 + response['FireflyDir'] = 1 + response['FireflyStartColor'] = response['Color']; + else: + # Update the current value + if response['FireflyDir'] == 1: + response['FireflyValue'] = response['FireflyValue'] * factor + if response['FireflyValue'] <= 0.01: + response['FireflyValue'] = 0.01 + response['FireflyDir'] = 0 + else: + response['FireflyValue'] = response['FireflyValue'] / factor + if response['FireflyValue'] >= 1.0: + response['FireflyValue'] = 1.0 + response['FireflyDir'] = 1 + + # Compute the color + response['Color'] = colorops.multiplyColor(response['FireflyStartColor'], response['FireflyValue']) + ret.append(response) + return (ret, []) #no direct ouput diff --git a/behaviors/MITDoors.py b/behaviors/MITDoors.py new file mode 100644 index 0000000..cee47f0 --- /dev/null +++ b/behaviors/MITDoors.py @@ -0,0 +1,28 @@ +from operationscore.Behavior import * +import math +import util.ComponentRegistry as compReg +class MITDoors(Behavior): + """MITDoors is a case-specific behavior to map keypresses to specific locations. Written for + Kuan 1/26/11 by RCOH""" + + def behaviorInit(self): + self.keymapping = {'q':[2,19], 'w':[22,36], 'e':[37,49], 'r':[52,69], 't':[76,91], 'y':[94,105], + 'u':[106,117], 'i':[123,154], 'o':[158,161], 'p':[164,167], '[':[172,184]} + screenWidth = compReg.getComponent('Screen').getSize()[2] #(minx, miny,maxx, maxy) + maxKey = max([max(self.keymapping[v]) for v in self.keymapping]) + mult = screenWidth / float(maxKey) + for k in self.keymapping: + self.keymapping[k] = [int(val*mult) for val in self.keymapping[k]] + def processResponse(self, sensorInputs, recursiveInputs): + ret = [] + for data in sensorInputs: + key = chr(data['Key']) + if key in self.keymapping: + bounds = self.keymapping[key] + data = dict(data) + data['Left'], data['Right'] = bounds + data['Bottom'] = self['Bottom'] + data['Location'] = (sum(bounds) / 2., self['Bottom']) + data['Oscillate'] = False + ret.append(data) + return (ret, []) diff --git a/behaviors/MobileShakeBehavior.py b/behaviors/MobileShakeBehavior.py new file mode 100644 index 0000000..b05cb5f --- /dev/null +++ b/behaviors/MobileShakeBehavior.py @@ -0,0 +1,26 @@ +from operationscore.Behavior import * +import util.ComponentRegistry as compReg +import util.Strings as Strings + +class MobileShakeBehavior(Behavior): + def behaviorInit(self): + self.mapper = None + + def processResponse(self, sensorInputs, recursiveInputs): + if self.mapper == None: + try: + self.mapper = compReg.getComponent('mobilegaussmap') + except KeyError: + pass + + #print sensorInputs + for sInput in sensorInputs: + if 'Shake' in sInput and sInput['Shake'] == 1: + #print 'increase!' + self.mapper.argDict['Width'] += 30 + #self.mapper.argDict['CutoffDist'] += 20 + sInput['Shake'] = 0 + print 'Width:' + str(compReg.getComponent('mobilegaussmap').argDict['Width']) + #print 'CutoffDist: '+ str(compReg.getComponent('mobilegaussmap').argDict['CutoffDist']) + + return (sensorInputs, recursiveInputs) diff --git a/behaviors/ModifyParam.py b/behaviors/ModifyParam.py index f589e05..0ef3a60 100644 --- a/behaviors/ModifyParam.py +++ b/behaviors/ModifyParam.py @@ -1,9 +1,20 @@ from operationscore.Behavior import * +import math import pdb #Class to perform a given operation on some element of an argDict. Designed to be used a recursive hook, but can serve sensor-based functions as well. Specify ParamType (Sensor or Recurse), ParamName, and ParamOp, (a valid python statement with the old value represented as {val}) class ModifyParam(Behavior): + """ModifyParam is a powerful class to perform an action on a specified key in the Argument + Dictionary of a response. Specify: + <ParamType> -- Sensor or Recurse + <ParamName> -- The name of the parameter you wish to modify + <ParamOp> -- The modification you wish to do. Use {val} to specify the current value of the + parameter in question. Special hooks for {x} and {y} also exist to access the x and y + locations.""" + def processResponse(self, sensorInputs, recursiveInputs): paramType = self['ParamType'] + if paramType == None: + paramType = 'Sensor' paramName = self['ParamName'] paramOp = str(self['ParamOp']) if paramType == 'Sensor': @@ -16,6 +27,9 @@ class ModifyParam(Behavior): if paramName in behaviorInput: #TODO: copy -> modify instead of just #copying paramOp = paramOp.replace('{val}', 'behaviorInput[paramName]') #convert the {val} marker to something we can execute + #TODO: move elsewhere + paramOp = paramOp.replace('{y}', "behaviorInput['Location'][1]") + paramOp = paramOp.replace('{x}', "behaviorInput['Location'][0]") behaviorInput[paramName] = eval(paramOp) if paramType == 'Sensor': #return accordingly return (searchSet, recursiveInputs) diff --git a/behaviors/MoveBehavior.py b/behaviors/MoveBehavior.py new file mode 100644 index 0000000..6e7fc6a --- /dev/null +++ b/behaviors/MoveBehavior.py @@ -0,0 +1,35 @@ +from operationscore.Behavior import * +#import util.ComponentRegistry as compReg +#import util.Geo as Geo +#import util.Strings as Strings + +class MoveBehavior(Behavior): + """Moves current location by the x and y components of sensorInput. Uses recurrences to track + current input. @Author: Euguene""" + + def processResponse(self, sensorInputs, recursiveInputs): + if recursiveInputs: + currRecLocs = recursiveInputs + else: + currRecLocs = [{'Location' : (5, 5), 'Color' : [255, 255, 255]}] + + if sensorInputs: # if input exists, change location + ret = [] + for currRecLoc in currRecLocs: + currDict = dict(currRecLoc) + for sensorInput in sensorInputs: + if 'type' in sensorInput and sensorInput['type'] == 1: + currDict['Shake'] = 0 + currDict['Location'] = (currDict['Location'][0] - sensorInput['x'] * self['XStep'], \ + currDict['Location'][1] + sensorInput['y'] * self['YStep']) + currDict['Color'] = [sensorInput['r'], sensorInput['g'], sensorInput['b']] + elif sensorInput['type'] == 2: + currDict['Shake'] = 1 + #currDict['Force'] = sensorInput['force'] + ret.append(currDict) + #print ret + return (ret, ret) + + else: # if not, return current recursive location. + #print currRecLocs + return (currRecLocs, currRecLocs) diff --git a/behaviors/RandomSetBrightColorBehavior.py b/behaviors/RandomSetBrightColorBehavior.py new file mode 100644 index 0000000..f278858 --- /dev/null +++ b/behaviors/RandomSetBrightColorBehavior.py @@ -0,0 +1,14 @@ +from operationscore.Behavior import * +import util.ColorOps as color +import pdb +import colorsys +import random +class RandomSetBrightColorBehavior(Behavior): + """Sets a random color that is bright.""" + def processResponse(self, sensorInputs, recursiveInputs): + ret = [] + for sensory in sensorInputs: + newDict = dict(sensory) + newDict['Color'] = color.randomBrightColor() + ret.append(newDict) + return (ret, []) diff --git a/behaviors/RandomWalk.py b/behaviors/RandomWalk.py index fd6c2c8..c9049af 100644 --- a/behaviors/RandomWalk.py +++ b/behaviors/RandomWalk.py @@ -5,6 +5,9 @@ import util.Strings as Strings import random import pdb class RandomWalk(Behavior): + """Behavior to move the curent location by a random distance specified by + <StepSize> -- StepSize in units/response""" + def processResponse(self, sensors, recursives): ret = [] s = self['StepSize'] diff --git a/behaviors/RecursiveDecay.py b/behaviors/RecursiveDecay.py index 218813d..0ae21ea 100644 --- a/behaviors/RecursiveDecay.py +++ b/behaviors/RecursiveDecay.py @@ -1,6 +1,11 @@ from operationscore.Behavior import * import pdb class RecursiveDecay(Behavior): + """RecursiveDecay is an event to allow recursive hooks to stop recursing after a certain number + of iterations specified in + <InitialResponseCount> -- Int, number of total responses. + Designed to be used as part of a recursive hook. + """ def processResponse(self, sensorInputs, recursiveInputs): ret = [] for response in sensorInputs: diff --git a/behaviors/ResponseMover.py b/behaviors/ResponseMover.py index e1faccb..3d559df 100644 --- a/behaviors/ResponseMover.py +++ b/behaviors/ResponseMover.py @@ -1,15 +1,11 @@ import pdb from operationscore.Behavior import * import util.ComponentRegistry as compReg -#ResponseMover is a scaffold for behaviors that spawn 'walkers' which act autonomously on input. -#Add a recursive hook to control the movement. class ResponseMover(Behavior): + """ResponseMover is a scaffold for behaviors that spawn 'walkers' which act autonomously on input. + To control the movment, use the behavior as part of a BehaviorChain and add a recursive hook which + modulates the location.""" + def processResponse(self, sensorInputs, recursiveInputs): - newResponses = sensorInputs - ret = [] - for recurInput in recursiveInputs: - outDict = dict(recurInput) - ret.append(outDict) - ret += newResponses - return (ret, ret) + return (recursiveInputs, recursiveInputs+sensorInputs) diff --git a/behaviors/RestrictLocation.py b/behaviors/RestrictLocation.py index f6c26ff..5e12440 100644 --- a/behaviors/RestrictLocation.py +++ b/behaviors/RestrictLocation.py @@ -6,6 +6,16 @@ import util.Strings as Strings import random import pdb class RestrictLocation(Behavior): + """RestrictLocation is a Behavior which does an action -- A ModifyParam, actually, when a certain + location based condition is met. It takes arguments as follows: + + <Action> -- Operation to perform, using ModifyParam syntax. Use {val} to reference the variable + specified by ParamName. + <ParamName> -- the name of the parameter to modify. + <LocationRestriction> -- either a tuple of (xmin,ymin,xmax,ymax) or a python-correct conditional. Use {x} and + {y} to reference x and y. Use < and > to get < and > in XML. EG: + <LocationRestriction>{x}<0 or {x}>800</LocationRestriction>""" + def behaviorInit(self): action = self['Action'] modifyParamArgs = {'ParamType': 'Sensor', diff --git a/behaviors/RiseFall.py b/behaviors/RiseFall.py new file mode 100644 index 0000000..eea2283 --- /dev/null +++ b/behaviors/RiseFall.py @@ -0,0 +1,46 @@ +from operationscore.Behavior import * +import math +import util.TimeOps as timeOps +#Required Args: +#Period (ms), MaxHeight, Width +class RiseFall(Behavior): + """RiseFall is a behavior that creates a rising and falling column of light. Specify: + <MaxHeight> -- the maximum height that it rises to. + <Width> -- the width of the column OR <Left> and <Right> + <Period> -- the period of oscillation in ms + + Designed to be used as part of a recursive hook. + """ + + def processResponse(self, sensorInputs, recurInputs): + ret = [] + for data in sensorInputs: + #first time with behavior: + data = dict(data) + if not 'StartTime' in data: + data['StartTime'] = timeOps.time() + data['Period'] = self['Period'] + data['MaxHeight'] = self['MaxHeight'] #Consider just using += + if not 'Bottom' in data: + data['Bottom'] = data['Location'][1] + if 'Width' in self: #TODO: improve + data['Width'] = self['Width'] + data['Left'] = data['Location'][0]-data['Width']/2. + data['Right'] = data['Location'][0]+data['Width']/2. + currentTime = timeOps.time() + deltaTime = currentTime-data['StartTime'] + #if data['Oscillate'] == True: + data['Height'] = data['MaxHeight']*math.sin(deltaTime/data['Period']*(math.pi*2)) + #else: + # data['Height'] = data['MaxHeight'] + #if (currentTime-data['StartTime']) > data['Period']: + # del data['StartTime'] + + data['Location'] = "{x}>"+str(data['Left']) + ", " +\ + "{x}<"+str(data['Right'])+", {y}<" + str(data['Bottom']) + ",\ + {y}>"+str(data['Bottom']-data['Height']) + + ret.append(data) + return (ret, []) + + diff --git a/behaviors/RiseFall.xml b/behaviors/RiseFall.xml new file mode 100644 index 0000000..eaadd7b --- /dev/null +++ b/behaviors/RiseFall.xml @@ -0,0 +1,7 @@ +<Behavior> + <Class>behaviors.RiseFall</Class> + <Args> + <Period>2000</Period> + <MaxHeight>50</MaxHeight> + </Args> +</Behavior> diff --git a/behaviors/RunningBehavior.py b/behaviors/RunningBehavior.py index 5eb33f7..ab3dc80 100644 --- a/behaviors/RunningBehavior.py +++ b/behaviors/RunningBehavior.py @@ -3,6 +3,11 @@ import util.ComponentRegistry as compReg import util.Geo as Geo import pdb class RunningBehavior(Behavior): + """RunningBehavior is a straightforward behavior that makes a Location run back and forth across + a screen. Specify: + <StepSize> -- the length of movment in units when the response moves. + """ + def processResponse(self, sensorInputs, recursiveInputs): newResponses = sensorInputs ret = [] diff --git a/behaviors/Sink.py b/behaviors/Sink.py new file mode 100644 index 0000000..52d0be2 --- /dev/null +++ b/behaviors/Sink.py @@ -0,0 +1,42 @@ + +from operationscore.Behavior import * +import math +import util.TimeOps as timeOps +#Required Args: +#Period (ms), MaxHeight, Width +class Sink(Behavior): + """RiseFall is a behavior that creates a rising and falling column of light. Specify: + <MaxHeight> -- the maximum height that it rises to. + <Width> -- the width of the column OR <Left> and <Right> + <Period> -- the period of oscillation in ms + + Designed to be used as part of a recursive hook. + """ + + def processResponse(self, sensorInputs, recurInputs): + ret = [] + for data in sensorInputs: + #first time with behavior: + data = dict(data) + if not 'StartTime' in data: + data['StartTime'] = timeOps.time() + data['Period'] = self['Period'] + data['MaxHeight'] = self['MaxHeight'] #Consider just using += + if not 'Bottom' in data: + data['Bottom'] = data['Location'][1] + if 'Width' in self: #TODO: improve + data['Width'] = self['Width'] + data['Left'] = data['Location'][0]-data['Width']/2. + data['Right'] = data['Location'][0]+data['Width']/2. + currentTime = timeOps.time() + deltaTime = currentTime-data['StartTime'] + data['Height'] = data['MaxHeight']*math.cos(deltaTime/data['Period']*(math.pi*2)) + + data['Location'] = "{x}>"+str(data['Left']) + ", " +\ + "{x}<"+str(data['Right'])+", {y}<" + str(data['Bottom']) + ",\ + {y}>"+str(data['Bottom']-data['Height']) + + ret.append(data) + return (ret, []) + + diff --git a/behaviors/SmootWind.py b/behaviors/SmootWind.py new file mode 100755 index 0000000..804183c --- /dev/null +++ b/behaviors/SmootWind.py @@ -0,0 +1,43 @@ +from operationscore.Behavior import *
+import util.ComponentRegistry as compReg
+import random
+
+class SmootWind(Behavior):
+ def behaviorInit(self):
+ self.mapper = None
+ self.xFor = None
+
+ def processResponse(self, sensorInputs, recursiveInputs):
+ if self.mapper == None:
+ try:
+ self.mapper = compReg.getComponent('windgaussmap')
+ except KeyError:
+ pass
+ if self.xFor == None:
+ try:
+ self.xFor = compReg.getComponent('xfor')
+ except KeyError:
+ pass
+
+ for sensory in sensorInputs:
+ print sensory
+ # input[0] is windspeed, [1] is dir
+ if 0 in sensory and 1 in sensory:
+ windSpeed = sensory[0]
+ windDir = sensory[1]
+ #print self.mapper.argDict
+ self.mapper.argDict['Width'] = self.mapper.argDict['Width']+float(windSpeed)*2+20
+ self.xFor.argDict['ParamOp'] = self.xFor.argDict['ParamOp']+float(windSpeed)*3+10*random.random();
+ #print 'Width: ' + str(self.mapper.argDict['Width'])
+ #print 'xFor: ' + str(self.xFor.argDict['ParamOp'])
+
+ elif 'Key' in sensory:
+ if sensory['Key'] == 273:
+ self.mapper.argDict['Width'] = self.mapper.argDict['Width']+10;
+ self.xFor.argDict['ParamOp'] = self.xFor.argDict['ParamOp']+5;
+
+ elif sensory['Key'] == 274:
+ self.mapper.argDict['Width'] = self.mapper.argDict['Width']-10;
+ self.xFor.argDict['ParamOp'] = self.xFor.argDict['ParamOp']-5;
+
+ return (sensorInputs, recursiveInputs)
diff --git a/behaviors/Square.py b/behaviors/Square.py index ecd000c..9d3223a 100644 --- a/behaviors/Square.py +++ b/behaviors/Square.py @@ -1,13 +1,17 @@ from operationscore.Behavior import * class Square(Behavior): - def processResponse(self, sensorInputs, recursiveInputs): - for sensory in sensorInputs:#TODO: consider replicating the dict - if 'Location' in sensory: - xLoc = sensory['Location'][0] - yLoc = sensory['Location'][1] - width = self['Width'] - #sensory['Location'] = 'True' - sensory['Location'] =\ - '{x}<'+str(xLoc+width)+',{x}>'+str(xLoc-width)+\ - ',{y}<'+str(yLoc+width)+',{y}>'+str(yLoc-width) - return (sensorInputs, recursiveInputs) + """Square is a simple behavior that makes a square with side lengths Width*2 around locations in + the sensor input. Specify: + <Width> -- the sidelength/2 + """ + + def processResponse(self, sensorInputs, recursiveInputs): + for sensory in sensorInputs:#TODO: consider replicating the dict + xLoc = sensory['Location'][0] + yLoc = sensory['Location'][1] + width = self['Width'] + #sensory['Location'] = 'True' + sensory['Location'] =\ + '{x}<'+str(xLoc+width)+',{x}>'+str(xLoc-width)+\ + ',{y}<'+str(yLoc+width)+',{y}>'+str(yLoc-width) + return (sensorInputs, recursiveInputs) diff --git a/behaviors/SynchTest.py b/behaviors/SynchTest.py new file mode 100644 index 0000000..e7b8acc --- /dev/null +++ b/behaviors/SynchTest.py @@ -0,0 +1,12 @@ +from operationscore.Behavior import * +from pixelevents.SynchTestEvent import * +import pdb +class SynchTest(Behavior): + def behaviorInit(self): + self.rendered = False + def processResponse(self, sensorInputs, recurs): + if not self.rendered: + self.rendered = True + print 'here1' + return ([{'Location':'True', 'PixelEvent':SynchTestEvent({'Color':(255,0,0)})}], []) + return ([], []) diff --git a/behaviors/TimedDie.py b/behaviors/TimedDie.py new file mode 100644 index 0000000..e75e9dd --- /dev/null +++ b/behaviors/TimedDie.py @@ -0,0 +1,15 @@ +from operationscore.Behavior import * +class Timeout(Behavior): + """Timeout is a behavior designed to be used in recursive hooks to stop responses after a certain + amount of time. It is the Time-version of RecursiveDecay. Specify: + <TimeOut> -- the time in ms that the response will run. + """ + + def processResponse(self, sensorInputs, recur): + ret = [] + for data in sensorInputs: + if not 'StartTime' in data: + data['StartTime'] = timeops.time() + if timeops.time()-data['StartTime'] < self['Timeout']: + ret.append(data) + return (ret, []) diff --git a/behaviors/Timeout.py b/behaviors/Timeout.py new file mode 100644 index 0000000..14d4873 --- /dev/null +++ b/behaviors/Timeout.py @@ -0,0 +1,16 @@ +from operationscore.Behavior import * +import util.TimeOps as timeops +class Timeout(Behavior): + """Timeout is a behavior designed to be used in recursive hooks to stop responses after a certain + amount of time. It is the Time-version of RecursiveDecay. Specify: + <TimeOut> -- the time in ms that the response will run. + """ + + def processResponse(self,sensorInputs, recur): + ret = [] + for data in sensorInputs: + if not 'StartTime' in data: + data['StartTime'] = timeops.time() + if timeops.time()-data['StartTime'] < self['Timeout']: + ret.append(data) + return (ret,[]) diff --git a/behaviors/TouchOSC.py b/behaviors/TouchOSC.py index 1c41b5e..099d5e5 100644 --- a/behaviors/TouchOSC.py +++ b/behaviors/TouchOSC.py @@ -12,10 +12,11 @@ class TouchOSC(Behavior): self.xy = (-1,-1) def processResponse(self, sensorInputs, recursiveInputs): ret = [] - for data in sensorInputs: + if sensorInputs: + data = sensorInputs[-1]#for data in sensorInputs: if data['Path'] == '/1/fader1': try: - self.h = data['Value'][0]*360.0 + self.h = data['Value'][0] except: pdb.set_trace() elif data['Path'] == '/1/fader2': diff --git a/behaviors/XYMove.py b/behaviors/XYMove.py index 8acbba8..11cee96 100644 --- a/behaviors/XYMove.py +++ b/behaviors/XYMove.py @@ -1,6 +1,13 @@ from operationscore.Behavior import * import util.Geo as Geo class XYMove(Behavior): + """XYMove is a behavior designed to be used as a recursive hook to ResponseMover to move pixels by + XStep and YStep. As XStep and YStep are maintained in the responses itself, they can be + modulated to facilitate, acceleration, modulation, bouncing, etc. Specify: + <XStep> -- the starting XStep + <YStep> -- the starting YStep + """ + def processResponse(self, sensor, recurs): ret = [] for loc in sensor: diff --git a/config/6thFloor.xml b/config/6thFloor.xml index 3f734d9..ef195ec 100644 --- a/config/6thFloor.xml +++ b/config/6thFloor.xml @@ -7,7 +7,6 @@ </InstallationConfiguration> <PixelConfiguration> <InheritsFrom>layouts/60StripLayout.xml</InheritsFrom> - <!--<InheritsFrom>layouts/BasicSixStrip.xml</InheritsFrom--> </PixelConfiguration> <PixelMapperConfiguration> <PixelMapper> @@ -40,8 +39,25 @@ <InputElement> <Class>inputs.PygameInput</Class> <Args><!--Passed as a dictionary--> - <Id>pygame</Id> + <Id>pygameclick</Id> <RefreshInterval>10</RefreshInterval> + <Clicks>True</Clicks> + </Args> + </InputElement> + <InputElement> + <Class>inputs.PygameInput</Class> + <Args><!--Passed as a dictionary--> + <Id>pygamekey</Id> + <RefreshInterval>10</RefreshInterval> + <Keyboard>True</Keyboard> + </Args> + </InputElement> + <InputElement> + <Class>inputs.UDPInput</Class> + <Args> + <Id>udp</Id> + <Port>3344</Port> + <RefreshInterval>50</RefreshInterval> </Args> </InputElement> <!--<InputElement> @@ -55,14 +71,11 @@ <InputElement Id="followmouse"> <InheritsFrom>inputs/MouseFollower.xml</InheritsFrom> </InputElement> - <InputElement> - <Class>inputs.RandomLocs</Class> - <Args> - <Id>randomLoc</Id> - </Args> - </InputElement> </InputConfiguration> <BehaviorConfiguration> + <Behavior Id="risefall"> + <InheritsFrom>behaviors/RiseFall.xml</InheritsFrom> + </Behavior> <Behavior> <Class>behaviors.EchoBehavior</Class> <Args> @@ -71,11 +84,24 @@ <RenderToScreen>False</RenderToScreen> </Args> </Behavior> - <Behavior Id="redshift"> - <InheritsFrom>behaviors/RedShift.xml</InheritsFrom> + <Behavior Id="dim"> + <InheritsFrom>behaviors/DimColor.xml</InheritsFrom> + </Behavior> + <Behavior> + <Class>behaviors.Expand</Class> + <Args> + <Id>expand</Id> + <ExpandRate>1</ExpandRate> + </Args> </Behavior> <Behavior Id="colorchange"> <InheritsFrom>behaviors/RandomColor.xml</InheritsFrom> + <Args> + <ColorList> + <Val>(255,0,0)</Val> + <Val>(0,0,255)</Val> + </ColorList> + </Args> </Behavior> <Behavior Id="decay"> <InheritsFrom>behaviors/PixelDecay.xml</InheritsFrom> @@ -121,26 +147,57 @@ <Id>movebounce</Id> <ChainedBehaviors> <Id>xymove</Id> - <Id>ybounce</Id> + <!--Id>ybounce</Id--> <Id>xbounce</Id> + <Id>ysin</Id> + </ChainedBehaviors> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.ModifyParam</Class> + <Args> + <Id>ysin</Id> + <ParamName>YStep</ParamName> + <ParamType>Sensor</ParamType> + <ParamOp>4*math.sin({x}/float(40))</ParamOp> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.BehaviorChain</Class> + <Args> + <Id>expanddie</Id> + <ChainedBehaviors> + <Id>expand</Id> + <Id>dim</Id> + </ChainedBehaviors> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.BehaviorChain</Class> + <Args> + <Id>risefalldie</Id> + <ChainedBehaviors> + <Id>risefall</Id> + <Id>2sec</Id> + <Id>dim</Id> </ChainedBehaviors> </Args> </Behavior> <Behavior> <Class>behaviors.BehaviorChain</Class> <Args> - <Id>xymover</Id> + <Id>doors</Id> <Inputs> - <Id>pygame</Id> + <Id>pygamekey</Id> </Inputs> <ChainedBehaviors> + <Id>mitdoors</Id> <Id>colorchange</Id> <Id>mover</Id> <Id>decay</Id> </ChainedBehaviors> - <RecursiveHooks>{'mover':'movebounce'}</RecursiveHooks> + <RecursiveHooks>{'mover':'risefalldie'}</RecursiveHooks> <RenderToScreen>True</RenderToScreen> - <Mapper>gaussmap</Mapper> </Args> </Behavior> <Behavior> @@ -149,11 +206,26 @@ <Id>debug</Id> <z-index>0</z-index> <Inputs> - <Id>pygame</Id> + <Id>pygamekey</Id> + <Id>udp</Id> </Inputs> </Args> </Behavior> <Behavior> + <Class>behaviors.Timeout</Class> + <Args> + <Id>2sec</Id> + <Timeout>2000</Timeout> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.MITDoors</Class> + <Args> + <Id>mitdoors</Id> + <Bottom>50</Bottom> + </Args> + </Behavior> + <Behavior> <Class>behaviors.AllPixelsLeft</Class> <Args> <Id>pixelsleft</Id> @@ -166,12 +238,6 @@ <Width>20</Width> </Args> </Behavior> - <Behavior> - <Class>behaviors.AllPixels</Class> - <Args> - <Id>allpixels</Id> - </Args> - </Behavior> <Behavior Id="recursivedecay"> <InheritsFrom>behaviors/LoopAndDie.xml</InheritsFrom> <Args> @@ -183,7 +249,7 @@ <Args> <Id>runcolordecay</Id> <Inputs> - <Id>pygame</Id> + <Id>pygameclick</Id> </Inputs> <ChainedBehaviors> <Id>colorchange</Id> @@ -196,39 +262,12 @@ </Args> </Behavior> <Behavior> - <Class>behaviors.BehaviorChain</Class> - <Args> - <Id>randomwalk</Id> - <Inputs> - <Id>pygame</Id> - </Inputs> - <ChainedBehaviors> - <Id>colorchange</Id> - <Id>mover</Id> - <Id>decay</Id> - </ChainedBehaviors> - <RecursiveHooks>{'mover':'redwalk'}</RecursiveHooks> - <RenderToScreen>False</RenderToScreen> - <Mapper>gaussmap</Mapper> - </Args> - </Behavior> - <Behavior> <Class>behaviors.ResponseMover</Class> <Args> <Id>mover</Id> </Args> </Behavior> <Behavior> - <Class>behaviors.BehaviorChain</Class> - <Args> - <Id>redwalk</Id> - <ChainedBehaviors> - <Id>randmovement</Id> - <Id>redshift</Id> - </ChainedBehaviors> - </Args> - </Behavior> - <Behavior> <Class>behaviors.RandomWalk</Class> <Args> <Id>randmovement</Id> @@ -258,7 +297,6 @@ </Inputs> <ChainedBehaviors> <Id>echo</Id> - <Id>redshift</Id> <Id>square</Id> <Id>singleframe</Id> </ChainedBehaviors> diff --git a/config/6thFloorOSC.xml b/config/6thFloorOSC.xml index ce60c74..f834694 100644 --- a/config/6thFloorOSC.xml +++ b/config/6thFloorOSC.xml @@ -29,9 +29,9 @@ </PixelMapper> </PixelMapperConfiguration> <RendererConfiguration> - <Renderer> + <!--Renderer> <InheritsFrom>renderers/60StripSeq.xml</InheritsFrom> - </Renderer> + </Renderer--> <Renderer> <InheritsFrom>renderers/Pygame.xml</InheritsFrom> </Renderer> @@ -41,10 +41,18 @@ <Class>inputs.OSCInput</Class> <Args> <Id>osc</Id> - <Port>12345</Port> + <Port>1234</Port> <RefreshInterval>10</RefreshInterval> </Args> </InputElement> + <!--InputElement> + <Class>inputs.OSCInput</Class> + <Args> + <Id>osc</Id> + <Port>12345</Port> + <RefreshInterval>20</RefreshInterval> + </Args> + </InputElement--> <InputElement> <Class>inputs.PygameInput</Class> <Args><!--Passed as a dictionary--> @@ -52,14 +60,14 @@ <RefreshInterval>10</RefreshInterval> </Args> </InputElement> - <!--<InputElement> + <InputElement> <Class>inputs.TCPInput</Class> <Args> <Id>tcp</Id> <Port>20120</Port> <RefreshInterval>10</RefreshInterval> </Args> - </InputElement>--> + </InputElement> <InputElement Id="followmouse"> <InheritsFrom>inputs/MouseFollower.xml</InheritsFrom> </InputElement> @@ -71,13 +79,64 @@ </InputElement> </InputConfiguration> <BehaviorConfiguration> - <Behavior> - <Class>behaviors.EchoBehavior</Class> + <Behavior> + <Class>behaviors.EchoBehavior</Class> + <Args> + <Id>echo</Id> + <z-index>0</z-index> + <RenderToScreen>False</RenderToScreen> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.BehaviorChain</Class> + <Args> + <Id>moveanddecay</Id> + <Inputs> + <Id>tcp</Id> + </Inputs> + <ChainedBehaviors> + <Id>move</Id> + <Id>decay</Id> + </ChainedBehaviors> + <RenderToScreen>True</RenderToScreen> + <Mapper>gaussmap</Mapper> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.MoveBehavior</Class> + <Args> + <Id>move</Id> + <XStep>3</XStep> + <YStep>3</YStep> + </Args> + </Behavior> + <!--Behavior> + <Class>behaviors.MobileShakeBehavior</Class> + <Args> + <Id>mobileshake</Id> + <SizeMult>3</SizeMult> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.BehaviorChain</Class> + <Args> + <Id>shakeanddecay</Id> + <Inputs> + <Id>tcp</Id> + </Inputs> + <ChainedBehaviors> + <Id>mobileshake</Id> + <Id>slowdecay</Id> + </ChainedBehaviors> + <RenderToScreen>True</RenderToScreen> + <Mapper>simplemap</Mapper> + </Args> + </Behavior--> + <Behavior> <Args> - <Id>echo</Id> - <z-index>0</z-index> - <RenderToScreen>False</RenderToScreen> + <Id>sixaxis</Id> </Args> + <Class>behaviors.ControllerOSC</Class> </Behavior> <Behavior> <Args> @@ -129,7 +188,7 @@ <LocationRestriction>{y}<0 or {y}>200</LocationRestriction> </Args> </Behavior> - <Behavior> + <!--Behavior> <Class>behaviors.BehaviorChain</Class> <Args> <Id>movebounce</Id> @@ -156,8 +215,8 @@ <RenderToScreen>True</RenderToScreen> <Mapper>gaussmap</Mapper> </Args> - </Behavior> - <Behavior> + </Behavior--> + <!--Behavior> <Class>behaviors.DebugBehavior</Class> <Args> <Id>debug</Id> @@ -166,7 +225,7 @@ <Id>pygame</Id> </Inputs> </Args> - </Behavior> + </Behavior--> <Behavior> <Class>behaviors.AllPixelsLeft</Class> <Args> @@ -186,7 +245,7 @@ <Id>allpixels</Id> </Args> </Behavior> - <Behavior Id="recursivedecay"> + <!--Behavior Id="recursivedecay"> <InheritsFrom>behaviors/LoopAndDie.xml</InheritsFrom> <Args> <InitialResponseCount>50</InitialResponseCount> @@ -208,8 +267,8 @@ <RenderToScreen>False</RenderToScreen> <Mapper>gaussmap</Mapper> </Args> - </Behavior> - <Behavior> + </Behavior--> + <!--Behavior> <Class>behaviors.BehaviorChain</Class> <Args> <Id>randomwalk</Id> @@ -261,14 +320,27 @@ <Id>recursivedecay</Id> </ChainedBehaviors> </Args> + </Behavior--> + <Behavior> + <Class>behaviors.BehaviorChain</Class> + <Args> + <Id>SixaxisChase</Id> + <Inputs> + <Id>osc</Id> + </Inputs> + <ChainedBehaviors> + <Id>sixaxis</Id> + <Id>singleframe</Id> + </ChainedBehaviors> + <RenderToScreen>True</RenderToScreen> + <Mapper>gaussmap</Mapper> + </Args> </Behavior> <Behavior> <Class>behaviors.BehaviorChain</Class> <Args> - <Id>mousechaser</Id> + <Id>OSCTouchChase</Id> <Inputs> - <!--Id>followmouse</Id--> - <Id>tcp</Id> <Id>osc</Id> </Inputs> <ChainedBehaviors> diff --git a/config/Demo.xml b/config/Demo.xml new file mode 100644 index 0000000..525e468 --- /dev/null +++ b/config/Demo.xml @@ -0,0 +1,227 @@ +<!---All configuration items contain a "Class" tag specifying the python class they represent, and an "Args" tag specifying the args to be passed in.--> +<LightInstallation> + <InstallationConfiguration> + <Defaults> + <PixelMapper>simplemap</PixelMapper> + </Defaults> + </InstallationConfiguration> + <PixelConfiguration> + <InheritsFrom>layouts/60StripLayout.xml</InheritsFrom> + </PixelConfiguration> + <PixelMapperConfiguration> + <PixelMapper> + <Class>pixelmappers.SimpleMapper</Class> + <Args> + <Id>simplemap</Id> + <CutoffDist>20</CutoffDist> + </Args> + </PixelMapper> + <PixelMapper> + <Class>pixelmappers.GaussianMapper</Class> + <Args> + <Id>gaussmap</Id> + <CutoffDist>30</CutoffDist> + <MinWeight>0.1</MinWeight> + <Width>10</Width> + <Height>1</Height> + </Args> + </PixelMapper> + </PixelMapperConfiguration> + <RendererConfiguration> + <Renderer> + <InheritsFrom>renderers/60StripSeq.xml</InheritsFrom> + </Renderer> + <Renderer> + <InheritsFrom>renderers/Pygame.xml</InheritsFrom> + </Renderer> + </RendererConfiguration> + <InputConfiguration> + <InputElement> + <Class>inputs.PygameInput</Class> + <Args> + <Id>pygameclick</Id> + <RefreshInterval>10</RefreshInterval> + <Clicks>True</Clicks> + </Args> + </InputElement> + <InputElement> + <Class>inputs.PygameInput</Class> + <Args> + <Id>pygamekey</Id> + <RefreshInterval>10</RefreshInterval> + <Keyboard>True</Keyboard> + </Args> + </InputElement> + <InputElement> + <Class>inputs.UDPInput</Class> + <Args> + <Id>udp</Id> + <Port>3344</Port> + <RefreshInterval>50</RefreshInterval> + </Args> + </InputElement> + <!--<InputElement> + <Class>inputs.TCPInput</Class> + <Args> + <Id>tcp</Id> + <Port>20120</Port> + <RefreshInterval>10</RefreshInterval> + </Args> + </InputElement>--> + <InputElement Id="followmouse"> + <InheritsFrom>inputs/MouseFollower.xml</InheritsFrom> + </InputElement> + </InputConfiguration> + <BehaviorConfiguration> + <Behavior Id="colorchange"> + <InheritsFrom>behaviors/RandomColor.xml</InheritsFrom> + <Args> + <ColorList> + <Val>(255,0,0)</Val> + <Val>(0,0,255)</Val> + </ColorList> + </Args> + </Behavior> + <Behavior Id="decay"> + <InheritsFrom>behaviors/PixelDecay.xml</InheritsFrom> + </Behavior> + <Behavior Id="singleframe"> + <InheritsFrom>behaviors/SingleFrame.xml</InheritsFrom> + </Behavior> + <Behavior Id="slowdecay"> + <InheritsFrom>behaviors/PixelDecay.xml</InheritsFrom> + <Args> + <Coefficient>.01</Coefficient> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.XYMove</Class> + <Args> + <Id>xymove</Id> + <XStep>5</XStep> + <YStep>2</YStep> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.RestrictLocation</Class> + <Args> + <Id>xbounce</Id> + <Action>{val}*-1</Action> + <ParamName>XStep</ParamName> + <LocationRestriction>{x}<0 or {x}>800</LocationRestriction> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.RestrictLocation</Class> + <Args> + <Id>ybounce</Id> + <Action>{val}*-1</Action> + <ParamName>YStep</ParamName> + <LocationRestriction>{y}<0 or {y}>200</LocationRestriction> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.BehaviorChain</Class> + <Args> + <Id>movebounce</Id> + <ChainedBehaviors> + <Id>xymove</Id> + <Id>ybounce</Id> + <Id>xbounce</Id> + </ChainedBehaviors> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.ModifyParam</Class> + <Args> + <Id>ysin</Id> + <ParamName>YStep</ParamName> + <ParamType>Sensor</ParamType> + <ParamOp>4*math.sin({x}/float(40))</ParamOp> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.DebugBehavior</Class> + <Args> + <Id>debug</Id> + <z-index>0</z-index> + <Inputs> + <Id>pygamekey</Id> + <Id>udp</Id> + </Inputs> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.Square</Class> + <Args> + <Id>square</Id> + <Width>20</Width> + </Args> + </Behavior> + <Behavior Id="recursivedecay"> + <InheritsFrom>behaviors/LoopAndDie.xml</InheritsFrom> + <Args> + <InitialResponseCount>80</InitialResponseCount> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.BehaviorChain</Class> + <Args> + <Id>runcolordecay</Id> + <Inputs> + <Id>pygameclick</Id> + </Inputs> + <ChainedBehaviors> + <Id>colorchange</Id> + <Id>mover</Id> + <Id>decay</Id> + </ChainedBehaviors> + <RecursiveHooks>{'mover':'movebounce'}</RecursiveHooks> + <RenderToScreen>True</RenderToScreen> + <Mapper>gaussmap</Mapper> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.ResponseMover</Class> + <Args> + <Id>mover</Id> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.RandomWalk</Class> + <Args> + <Id>randmovement</Id> + <StepSize>2</StepSize> + </Args> + </Behavior> + <Behavior Id="accelerate"> + <InheritsFrom>behaviors/Accelerate.xml</InheritsFrom> + </Behavior> + <Behavior> + <Class>behaviors.EchoBehavior</Class> + <Args> + <Id>echo</Id> + <z-index>0</z-index> + <RenderToScreen>False</RenderToScreen> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.BehaviorChain</Class> + <Args> + <Id>mousechaser</Id> + <Inputs> + <Id>followmouse</Id> + </Inputs> + <ChainedBehaviors> + <Id>echo</Id> + <Id>square</Id> + <Id>singleframe</Id> + </ChainedBehaviors> + <RenderToScreen>True</RenderToScreen> + </Args> + </Behavior> + <Behavior Id="running"> + <InheritsFrom>behaviors/RunningBehavior.xml</InheritsFrom> + </Behavior> + </BehaviorConfiguration> +</LightInstallation> diff --git a/config/FireflyDemo.xml b/config/FireflyDemo.xml new file mode 100644 index 0000000..856569e --- /dev/null +++ b/config/FireflyDemo.xml @@ -0,0 +1,163 @@ +<!-- Demo of the flasher behavior. --> +<!-- Bubbles or fireflies move around and fade on and off --> +<!-- Jim Salem 1/28/11 jsalem@gmail.com --> +<!-- Movement is pretty wonky. --> + +<!---All configuration items contain a "Class" tag specifying the python class they represent, and an "Args" tag specifying the args to be passed in.--> +<LightInstallation> + <InstallationConfiguration> + <Defaults> + <PixelMapper>simplemap</PixelMapper> + </Defaults> + </InstallationConfiguration> + <PixelConfiguration> + <InheritsFrom>layouts/60StripLayout.xml</InheritsFrom> + </PixelConfiguration> + <PixelMapperConfiguration> + <PixelMapper> + <Class>pixelmappers.SimpleMapper</Class> + <Args> + <Id>simplemap</Id> + <CutoffDist>20</CutoffDist> + </Args> + </PixelMapper> + <PixelMapper> + <Class>pixelmappers.GaussianMapper</Class> + <Args> + <Id>gaussmap</Id> + <CutoffDist>30</CutoffDist> + <MinWeight>0.1</MinWeight> + <Width>10</Width> + <Height>1</Height> + </Args> + </PixelMapper> + </PixelMapperConfiguration> + <RendererConfiguration> + <Renderer> + <InheritsFrom>renderers/60StripSeq.xml</InheritsFrom> + </Renderer> + <Renderer> + <InheritsFrom>renderers/Pygame.xml</InheritsFrom> + </Renderer> + </RendererConfiguration> + <InputConfiguration> + <InputElement> + <Class>inputs.PygameInput</Class> + <Args> + <Id>pygameclick</Id> + <RefreshInterval>10</RefreshInterval> + <Clicks>True</Clicks> + </Args> + </InputElement> + <InputElement> + <Class>inputs.PygameInput</Class> + <Args> + <Id>pygamekey</Id> + <RefreshInterval>10</RefreshInterval> + <Keyboard>True</Keyboard> + </Args> + </InputElement> + <InputElement> + <Class>inputs.UDPInput</Class> + <Args> + <Id>udp</Id> + <Port>3344</Port> + <RefreshInterval>50</RefreshInterval> + </Args> + </InputElement> + </InputConfiguration> + + <BehaviorConfiguration> + <Behavior> + <Class>behaviors.RandomSetBrightColorBehavior</Class> + <Args> + <Id>setbrightcolor</Id> + </Args> + </Behavior> + <Behavior Id="decay"> + <InheritsFrom>behaviors/PixelDecay.xml</InheritsFrom> + </Behavior> + <Behavior> + <Class>behaviors.RestrictLocation</Class> + <Args> + <Id>xbounce</Id> + <Action>{val}*-1</Action> + <ParamName>XStep</ParamName> + <LocationRestriction>{x}<0 or {x}>800</LocationRestriction> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.RestrictLocation</Class> + <Args> + <Id>ybounce</Id> + <Action>{val}*-1</Action> + <ParamName>YStep</ParamName> + <LocationRestriction>{y}<0 or {y}>200</LocationRestriction> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.Flasher</Class> + <Args> + <Id>flasher</Id> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.BehaviorChain</Class> + <Args> + <Id>flashermovebounce</Id> + <ChainedBehaviors> + <Id>randmovement</Id> + <Id>ybounce</Id> + <Id>xbounce</Id> + <Id>flasher</Id> + </ChainedBehaviors> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.Square</Class> + <Args> + <Id>square</Id> + <Width>15</Width> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.RandomWalk</Class> + <Args> + <Id>randmovement</Id> + <StepSize>20</StepSize> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.BehaviorChain</Class> + <Args> + <Id>runcolordecay</Id> + <Inputs> + <Id>pygameclick</Id> + </Inputs> + <ChainedBehaviors> + <Id>setbrightcolor</Id> + <Id>mover</Id> + <Id>decay</Id> + </ChainedBehaviors> + <RecursiveHooks>{'mover':'flashermovebounce'}</RecursiveHooks> + <RenderToScreen>True</RenderToScreen> + <Mapper>gaussmap</Mapper> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.ResponseMover</Class> + <Args> + <Id>mover</Id> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.EchoBehavior</Class> + <Args> + <Id>echo</Id> + <z-index>0</z-index> + <Color>(90,90,90)</Color> + <RenderToScreen>False</RenderToScreen> + </Args> + </Behavior> + </BehaviorConfiguration> +</LightInstallation> diff --git a/config/HTMLTest.xml b/config/HTMLTest.xml new file mode 100755 index 0000000..159cec4 --- /dev/null +++ b/config/HTMLTest.xml @@ -0,0 +1,58 @@ +<LightInstallation> + <InstallationConfiguration> + <Defaults> + <PixelMapper>simplemap</PixelMapper> + </Defaults> + </InstallationConfiguration> + <PixelConfiguration> + <InheritsFrom>layouts/BasicSixStrip.xml</InheritsFrom> + </PixelConfiguration> + <PixelMapperConfiguration> + <PixelMapper> + <Class>pixelmappers.SimpleMapper</Class> + <Args> + <Id>simplemap</Id> + <CutoffDist>20</CutoffDist> + </Args> + </PixelMapper> + <PixelMapper> + <Class>pixelmappers.GaussianMapper</Class> + <Args> + <Id>gaussmap</Id> + <CutoffDist>30</CutoffDist> + <MinWeight>0.1</MinWeight> + <Width>10</Width> + <Height>1</Height> + </Args> + </PixelMapper> + </PixelMapperConfiguration> + <RendererConfiguration> + <Renderer> + <InheritsFrom>renderers/Pygame.xml</InheritsFrom> + </Renderer> + </RendererConfiguration> + <InputConfiguration> + <InputElement> + <Class>inputs.HTMLInput</Class> + <Args> + <Id>weatherinput</Id> + <Src>'http://sailing.mit.edu/weather/'</Src> + <!--<Regex>'rtWindSpeed = (\d+).*rtWindDir = (\d+)'</Regex>--> + <Regex>'rtWindSpeed = (\d+).*\s.*\s.*rtWindDir = (\d+)'</Regex> + <!--<RefreshInterval>60000</RefreshInterval>--> + </Args> + </InputElement> + </InputConfiguration> + <BehaviorConfiguration> + <Behavior> + <Class>behaviors.EchoBehavior</Class> + <Args> + <Inputs> + <Id>weatherinput</Id> + </Inputs> + <Id>echo</Id> + <RenderToScreen>False</RenderToScreen> + </Args> + </Behavior> + </BehaviorConfiguration> +</LightInstallation> diff --git a/config/Jennifer.xml b/config/Jennifer.xml new file mode 100644 index 0000000..179c462 --- /dev/null +++ b/config/Jennifer.xml @@ -0,0 +1,129 @@ +<!---All configuration items contain a "Class" tag specifying the python class they represent, and an "Args" tag specifying the args to be passed in.--> +<LightInstallation> + <InstallationConfiguration> + <Defaults> + <PixelMapper>simplemap</PixelMapper> + </Defaults> + </InstallationConfiguration> + <PixelConfiguration> + <InheritsFrom>layouts/60StripLayout.xml</InheritsFrom> + </PixelConfiguration> + <PixelMapperConfiguration> + <PixelMapper> + <Class>pixelmappers.SimpleMapper</Class> + <Args> + <Id>simplemap</Id> + <CutoffDist>20</CutoffDist> + </Args> + </PixelMapper> + </PixelMapperConfiguration> + <RendererConfiguration> + <Renderer> + <InheritsFrom>renderers/60StripSeq.xml</InheritsFrom> + </Renderer> + <Renderer> + <InheritsFrom>renderers/Pygame.xml</InheritsFrom> + </Renderer> + </RendererConfiguration> + <InputConfiguration> + <InputElement> + <Class>inputs.PygameInput</Class> + <Args><!--Passed as a dictionary--> + <Id>pygameclick</Id> + <RefreshInterval>10</RefreshInterval> + <Clicks>True</Clicks> + </Args> + </InputElement> + <InputElement> + <Class>inputs.PygameInput</Class> + <Args><!--Passed as a dictionary--> + <Id>pygamekey</Id> + <RefreshInterval>10</RefreshInterval> + <Keyboard>True</Keyboard> + </Args> + </InputElement> + <InputElement> + <Class>inputs.UDPInput</Class> + <Args> + <Id>udp</Id> + <Port>3344</Port> + <RefreshInterval>50</RefreshInterval> + </Args> + </InputElement> + </InputConfiguration> + <BehaviorConfiguration> + <Behavior Id="dim"> + <InheritsFrom>behaviors/DimColor.xml</InheritsFrom> + </Behavior> + <Behavior> + <Class>behaviors.Expand</Class> + <Args> + <Id>expand</Id> + <ExpandRate>8</ExpandRate> + </Args> + </Behavior> + <Behavior Id="colorchange"> + <InheritsFrom>behaviors/RandomColor.xml</InheritsFrom> + <Args> + <ColorList> + <Val>(255,0,0)</Val> + <Val>(0,0,255)</Val> + </ColorList> + </Args> + </Behavior> + <Behavior Id="decay"> + <InheritsFrom>behaviors/PixelDecay.xml</InheritsFrom> + </Behavior> + <Behavior> + <Class>behaviors.BehaviorChain</Class> + <Args> + <Id>expanddie</Id> + <ChainedBehaviors> + <Id>expand</Id> + <Id>decexpand</Id> + <Id>dim</Id> + <Id>8sectimeout</Id> + </ChainedBehaviors> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.ExpandingColorZones</Class> + <Args> + <Id>colorzones</Id> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.BehaviorChain</Class> + <Args> + <Id>colorbars</Id> + <Inputs> + <Id>pygamekey</Id> + <Id>udp</Id> + </Inputs> + <ChainedBehaviors> + <Id>colorzones</Id> + <Id>mover</Id> + <Id>decay</Id> + </ChainedBehaviors> + <RecursiveHooks>{'mover':'expanddie'}</RecursiveHooks> + <RenderToScreen>True</RenderToScreen> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.Timeout</Class> + <Args> + <Id>8sectimeout</Id> + <Timeout>8000</Timeout> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.ResponseMover</Class> + <Args> + <Id>mover</Id> + </Args> + </Behavior> + <Behavior Id="decexpand" ParamName="ExpandRate"> + <InheritsFrom>behaviors/Deccelerate.xml</InheritsFrom> + </Behavior> + </BehaviorConfiguration> +</LightInstallation> diff --git a/config/Kuan.xml b/config/Kuan.xml new file mode 100644 index 0000000..7d76ed4 --- /dev/null +++ b/config/Kuan.xml @@ -0,0 +1,108 @@ +<LightInstallation> + <InstallationConfiguration> + <Defaults> + <PixelMapper>simplemap</PixelMapper> + </Defaults> + </InstallationConfiguration> + <PixelConfiguration> + <InheritsFrom>layouts/60StripLayout.xml</InheritsFrom> + </PixelConfiguration> + <PixelMapperConfiguration> + <PixelMapper> + <Class>pixelmappers.SimpleMapper</Class> + <Args> + <Id>simplemap</Id> + <CutoffDist>20</CutoffDist> + </Args> + </PixelMapper> + </PixelMapperConfiguration> + <RendererConfiguration> + <Renderer> + <InheritsFrom>renderers/60StripSeq.xml</InheritsFrom> + </Renderer> + <Renderer> + <InheritsFrom>renderers/Pygame.xml</InheritsFrom> + </Renderer> + </RendererConfiguration> + <InputConfiguration> + <InputElement> + <Class>inputs.PygameInput</Class> + <Args> + <Id>pygamekey</Id> + <RefreshInterval>10</RefreshInterval> + <Keyboard>True</Keyboard> + </Args> + </InputElement> + </InputConfiguration> + <BehaviorConfiguration> + <Behavior Id="risefall" MaxHeight="200"> + <InheritsFrom>behaviors/RiseFall.xml</InheritsFrom> + </Behavior> + <Behavior Id="dim"> + <InheritsFrom>behaviors/DimColor.xml</InheritsFrom> + </Behavior> + <Behavior Id="colorchange"> + <InheritsFrom>behaviors/RandomColor.xml</InheritsFrom> + </Behavior> + <Behavior Id="decay"> + <InheritsFrom>behaviors/PixelDecay.xml</InheritsFrom> + </Behavior> + <Behavior> + <Class>behaviors.BehaviorChain</Class> + <Args> + <Id>risefalldie</Id> + <ChainedBehaviors> + <Id>risefall</Id> + <Id>2sec</Id> + <Id>dim</Id> + </ChainedBehaviors> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.BehaviorChain</Class> + <Args> + <Id>doors</Id> + <Inputs> + <Id>pygamekey</Id> + </Inputs> + <ChainedBehaviors> + <Id>mitdoors</Id> + <Id>colorchange</Id> + <Id>mover</Id> + <Id>decay</Id> + </ChainedBehaviors> + <RecursiveHooks>{'mover':'risefalldie'}</RecursiveHooks> + <RenderToScreen>True</RenderToScreen> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.Timeout</Class> + <Args> + <Id>2sec</Id> + <Timeout>2000</Timeout> + </Args> + </Behavior> + <!--Behavior> + <Class>behaviors.ModifyParam</Class> + <Args> + <Id>swaposc</Id> + <ParamType>Sensor</ParamType> + <ParamName>Oscillate</ParamName> + <ParamOp>not {val}</ParamOp> + </Args> + </Behavior--> + <Behavior> + <Class>behaviors.MITDoors</Class> + <Args> + <Id>mitdoors</Id> + <Bottom>200</Bottom> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.ResponseMover</Class> + <Args> + <Id>mover</Id> + </Args> + </Behavior> + </BehaviorConfiguration> +</LightInstallation> diff --git a/config/MobileTest.xml b/config/MobileTest.xml new file mode 100644 index 0000000..63c3b47 --- /dev/null +++ b/config/MobileTest.xml @@ -0,0 +1,248 @@ +<!---All configuration items contain a "Class" tag specifying the python class they represent, and an "Args" tag specifying the args to be passed in.--> +<LightInstallation> + <InstallationConfiguration> + <Defaults> + <PixelMapper>simplemap</PixelMapper> + </Defaults> + </InstallationConfiguration> + <PixelConfiguration> + <InheritsFrom>layouts/60StripLayout.xml</InheritsFrom> + </PixelConfiguration> + <PixelMapperConfiguration> + <PixelMapper> + <Class>pixelmappers.SimpleMapper</Class> + <Args> + <Id>simplemap</Id> + <CutoffDist>20</CutoffDist> + </Args> + </PixelMapper> + <PixelMapper> + <Class>pixelmappers.GaussianMapper</Class> + <Args> + <Id>gaussmap</Id> + <CutoffDist>30</CutoffDist> + <MinWeight>0.1</MinWeight> + <Width>10</Width> + <Height>1</Height> + </Args> + </PixelMapper> + <PixelMapper> + <Class>pixelmappers.GaussianMapper</Class> + <Args> + <Id>mobilegaussmap</Id> + <CutoffDist>30</CutoffDist> + <MinWeight>0.1</MinWeight> + <Width>10</Width> + <Height>1</Height> + </Args> + </PixelMapper> + </PixelMapperConfiguration> + <RendererConfiguration> + <Renderer> + <InheritsFrom>renderers/60StripSeq.xml</InheritsFrom> + </Renderer> + <Renderer> + <InheritsFrom>renderers/Pygame.xml</InheritsFrom> + </Renderer> + </RendererConfiguration> + <InputConfiguration> + <InputElement> + <Class>inputs.PygameInput</Class> + <Args><!--Passed as a dictionary--> + <Id>pygame</Id> + <RefreshInterval>10</RefreshInterval> + </Args> + </InputElement> + <InputElement> + <Class>inputs.TCPInput</Class> + <Args> + <Id>tcp</Id> + <Port>20120</Port> + <RefreshInterval>50</RefreshInterval> + </Args> + </InputElement> + <InputElement Id="followmouse"> + <InheritsFrom>inputs/MouseFollower.xml</InheritsFrom> + </InputElement> + <InputElement> + <Class>inputs.RandomLocs</Class> + <Args> + <Id>randomLoc</Id> + </Args> + </InputElement> + </InputConfiguration> + <BehaviorConfiguration> + <Behavior> + <Class>behaviors.EchoBehavior</Class> + <Args> + <Id>echo</Id> + <z-index>0</z-index> + <RenderToScreen>False</RenderToScreen> + </Args> + </Behavior> + <Behavior Id="redshift"> + <InheritsFrom>behaviors/RedShift.xml</InheritsFrom> + </Behavior> + <Behavior Id="colorchange"> + <InheritsFrom>behaviors/RandomColor.xml</InheritsFrom> + </Behavior> + <Behavior Id="decay"> + <InheritsFrom>behaviors/PixelDecay.xml</InheritsFrom> + </Behavior> + <Behavior Id="slowdecay"> + <InheritsFrom>behaviors/PixelDecay.xml</InheritsFrom> + <Args> + <Coefficient>.01</Coefficient> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.DebugBehavior</Class> + <Args> + <Id>debug</Id> + <z-index>0</z-index> + <Inputs> + <Id>pygame</Id> + </Inputs> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.AllPixelsLeft</Class> + <Args> + <Id>pixelsleft</Id> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.Square</Class> + <Args> + <Id>square</Id> + <Width>20</Width> + </Args> + </Behavior> + <Behavior Id="recursivedecay"> + <InheritsFrom>behaviors/LoopAndDie.xml</InheritsFrom> + <Args> + <InitialResponseCount>50</InitialResponseCount> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.BehaviorChain</Class> + <Args> + <Id>runcolordecay</Id> + <Inputs> + <Id>pygame</Id> + </Inputs> + <ChainedBehaviors> + <Id>colorchange</Id> + <Id>running</Id> + <Id>decay</Id> + </ChainedBehaviors> + <RecursiveHooks>{'running':'acceleratedie'}</RecursiveHooks> + <RenderToScreen>True</RenderToScreen> + <Mapper>gaussmap</Mapper> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.BehaviorChain</Class> + <Args> + <Id>randomwalk</Id> + <Inputs> + <Id>pygame</Id> + </Inputs> + <ChainedBehaviors> + <Id>colorchange</Id> + <Id>mover</Id> + <Id>decay</Id> + </ChainedBehaviors> + <RecursiveHooks>{'mover':'redwalk'}</RecursiveHooks> + <RenderToScreen>True</RenderToScreen> + <Mapper>gaussmap</Mapper> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.ResponseMover</Class> + <Args> + <Id>mover</Id> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.BehaviorChain</Class> + <Args> + <Id>redwalk</Id> + <ChainedBehaviors> + <Id>randmovement</Id> + <Id>redshift</Id> + </ChainedBehaviors> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.RandomWalk</Class> + <Args> + <Id>randmovement</Id> + <StepSize>2</StepSize> + </Args> + </Behavior> + <Behavior Id="accelerate"> + <InheritsFrom>behaviors/Accelerate.xml</InheritsFrom> + </Behavior> + <Behavior> + <Class>behaviors.BehaviorChain</Class> + <Args> + <Id>acceleratedie</Id> + <ChainedBehaviors> + <Id>accelerate</Id> + <Id>recursivedecay</Id> + </ChainedBehaviors> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.BehaviorChain</Class> + <Args> + <Id>mousechaser</Id> + <Inputs> + <Id>followmouse</Id> + </Inputs> + <ChainedBehaviors> + <Id>echo</Id> + <Id>redshift</Id> + <Id>square</Id> + <Id>slowdecay</Id> + </ChainedBehaviors> + <RenderToScreen>True</RenderToScreen> + </Args> + </Behavior> + <Behavior Id="running"> + <InheritsFrom>behaviors/RunningBehavior.xml</InheritsFrom> + </Behavior> + <Behavior> + <Class>behaviors.MoveBehavior</Class> + <Args> + <Id>move</Id> + <XStep>3</XStep> + <YStep>3</YStep> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.MobileShakeBehavior</Class> + <Args> + <Id>mobileshake</Id> + <SizeMult>3</SizeMult> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.BehaviorChain</Class> + <Args> + <Id>moveanddecay</Id> + <Inputs> + <Id>tcp</Id> + </Inputs> + <ChainedBehaviors> + <Id>move</Id> + <Id>mobileshake</Id> + <Id>decay</Id> + </ChainedBehaviors> + <RenderToScreen>True</RenderToScreen> + <Mapper>mobilegaussmap</Mapper> + </Args> + </Behavior> + </BehaviorConfiguration> +</LightInstallation> diff --git a/config/SmootWindTest.xml b/config/SmootWindTest.xml new file mode 100755 index 0000000..f6e7a97 --- /dev/null +++ b/config/SmootWindTest.xml @@ -0,0 +1,181 @@ +<!---All configuration items contain a "Class" tag specifying the python class they represent, and an "Args" tag specifying the args to be passed in.-->
+<LightInstallation>
+ <InstallationConfiguration>
+ <Defaults>
+ <PixelMapper>simplemap</PixelMapper>
+ </Defaults>
+ </InstallationConfiguration>
+ <PixelConfiguration>
+ <InheritsFrom>layouts/60StripLayout.xml</InheritsFrom>
+ </PixelConfiguration>
+ <PixelMapperConfiguration>
+ <PixelMapper>
+ <Class>pixelmappers.SimpleMapper</Class>
+ <Args>
+ <Id>simplemap</Id>
+ <CutoffDist>20</CutoffDist>
+ </Args>
+ </PixelMapper>
+ <PixelMapper>
+ <Class>pixelmappers.GaussianMapper</Class>
+ <Args>
+ <Id>gaussmap</Id>
+ <CutoffDist>10</CutoffDist>
+ <MinWeight>0.1</MinWeight>
+ <Width>10</Width>
+ <Height>5</Height>
+ </Args>
+ </PixelMapper>
+ <PixelMapper>
+ <Class>pixelmappers.WindGaussianMapper</Class>
+ <Args>
+ <Id>windgaussmap</Id>
+ <CutoffDist>150</CutoffDist>
+ <MinWeight>0.005</MinWeight>
+ <Width>30</Width>
+ <Height>10</Height>
+ </Args>
+ </PixelMapper>
+ </PixelMapperConfiguration>
+ <RendererConfiguration>
+ <Renderer>
+ <InheritsFrom>renderers/60StripSeq.xml</InheritsFrom>
+ </Renderer>
+ <Renderer>
+ <InheritsFrom>renderers/Pygame.xml</InheritsFrom>
+ </Renderer>
+ </RendererConfiguration>
+ <InputConfiguration>
+ <InputElement>
+ <Class>inputs.PygameInput</Class>
+ <Args><!--Passed as a dictionary-->
+ <Id>pygame</Id>
+ <RefreshInterval>1</RefreshInterval>
+ </Args>
+ </InputElement>
+ <InputElement>
+ <Class>inputs.PygameInput</Class>
+ <Args>
+ <Id>pygamekey</Id>
+ <RefreshInterval>10</RefreshInterval>
+ <Keyboard>True</Keyboard>
+ </Args>
+ </InputElement>
+ <InputElement>
+ <Class>inputs.HTMLInput</Class>
+ <Args>
+ <Id>weatherinput</Id>
+ <Src>'http://sailing.mit.edu/weather/'</Src>
+ <!--<Regex>'rtWindSpeed = (\d+).*rtWindDir = (\d+)'</Regex>-->
+ <Regex>'rtWindSpeed = (\d+).*\s.*\s.*rtWindDir = (\d+)'</Regex>
+ <!--<RefreshInterval>60000</RefreshInterval>-->
+ </Args>
+ </InputElement>
+ <InputElement>
+ <Class>inputs.RandomLocs</Class>
+ <Args>
+ <Id>randomLoc</Id>
+ </Args>
+ </InputElement>
+ </InputConfiguration>
+ <BehaviorConfiguration>
+ <Behavior Id="colorchange">
+ <InheritsFrom>behaviors/RandomColor.xml</InheritsFrom>
+ </Behavior>
+ <Behavior Id="staticcolor">
+ <InheritsFrom>behaviors/RandomColor.xml</InheritsFrom>
+ <Args>
+ <ColorList>
+ <Val>(100,200,255)</Val>
+ <Val>(50,200,255)</Val>
+ <Val>(0,200,255)</Val>
+ <Val>(0,150,255)</Val>
+ </ColorList>
+ </Args>
+ </Behavior>
+ <Behavior Id="decay">
+ <InheritsFrom>behaviors/PixelDecay.xml</InheritsFrom>
+ </Behavior>
+ <Behavior>
+ <Class>behaviors.XYMove</Class>
+ <Args>
+ <Id>xymove</Id>
+ <XStep>0</XStep>
+ <YStep>0</YStep>
+ </Args>
+ </Behavior>
+ <Behavior>
+ <Class>behaviors.BehaviorChain</Class>
+ <Args>
+ <Id>movebounce</Id>
+ <ChainedBehaviors>
+ <Id>xymove</Id>
+ <Id>yfor</Id>
+ <Id>xfor</Id>
+ <Id>20sectimeout</Id>
+ </ChainedBehaviors>
+ </Args>
+ </Behavior>
+ <Behavior>
+ <Class>behaviors.Timeout</Class>
+ <Args>
+ <Id>20sectimeout</Id>
+ <Timeout>20000</Timeout>
+ </Args>
+ </Behavior>
+ <Behavior>
+ <Class>behaviors.ModifyParam</Class>
+ <Args>
+ <Id>yfor</Id>
+ <ParamName>YStep</ParamName>
+ <ParamType>Sensor</ParamType>
+ <ParamOp>2*(2*math.sin({x}/float(100))+math.sin({x}/float(50)))</ParamOp>
+ </Args>
+ </Behavior>
+
+ <Behavior>
+ <Class>behaviors.ModifyParam</Class>
+ <Args>
+ <Id>xfor</Id>
+ <ParamName>XStep</ParamName>
+ <ParamType>Sensor</ParamType>
+ <!--<ParamOp>75</ParamOp>-->
+ <ParamOp>25</ParamOp>
+ </Args>
+ </Behavior>
+
+ <Behavior>
+ <Class>behaviors.SmootWind</Class>
+ <Args>
+ <Id>smootwind</Id>
+ <Inputs>
+ <Id>pygamekey</Id>
+ </Inputs>
+ </Args>
+ </Behavior>
+ <Behavior>
+ <Class>behaviors.ResponseMover</Class>
+ <Args>
+ <Id>mover</Id>
+ </Args>
+ </Behavior>
+ <Behavior>
+ <Class>behaviors.BehaviorChain</Class>
+ <Args>
+ <Id>xymover</Id>
+ <Inputs>
+ <Id>pygame</Id>
+ <Id>randomLoc</Id>
+ </Inputs>
+ <ChainedBehaviors>
+ <Id>staticcolor</Id>
+ <Id>mover</Id>
+ <Id>decay</Id>
+ </ChainedBehaviors>
+ <RecursiveHooks>{'mover':'movebounce'}</RecursiveHooks>
+ <RenderToScreen>True</RenderToScreen>
+ <Mapper>windgaussmap</Mapper>
+ </Args>
+ </Behavior>
+ </BehaviorConfiguration>
+</LightInstallation>
diff --git a/config/SynchTest.xml b/config/SynchTest.xml new file mode 100644 index 0000000..fcb8293 --- /dev/null +++ b/config/SynchTest.xml @@ -0,0 +1,39 @@ +<!---All configuration items contain a "Class" tag specifying the python class they represent, and an "Args" tag specifying the args to be passed in.--> +<LightInstallation> + <InstallationConfiguration> + <Defaults> + <PixelMapper>simplemap</PixelMapper> + </Defaults> + </InstallationConfiguration> + <PixelConfiguration> + <InheritsFrom>layouts/60StripLayout.xml</InheritsFrom> + </PixelConfiguration> + <PixelMapperConfiguration> + <PixelMapper> + <Class>pixelmappers.SimpleMapper</Class> + <Args> + <Id>simplemap</Id> + <CutoffDist>20</CutoffDist> + </Args> + </PixelMapper> + </PixelMapperConfiguration> + <RendererConfiguration> + <Renderer> + <InheritsFrom>renderers/60StripSeq.xml</InheritsFrom> + </Renderer> + <Renderer> + <InheritsFrom>renderers/Pygame.xml</InheritsFrom> + </Renderer> + </RendererConfiguration> + <InputConfiguration> + </InputConfiguration> + <BehaviorConfiguration> + <Behavior> + <Class>behaviors.SynchTest</Class> + <Args> + <Id>synch</Id> + <RenderToScreen>True</RenderToScreen> + </Args> + </Behavior> + </BehaviorConfiguration> +</LightInstallation> diff --git a/inputs/HTMLInput.py b/inputs/HTMLInput.py new file mode 100755 index 0000000..9697a32 --- /dev/null +++ b/inputs/HTMLInput.py @@ -0,0 +1,29 @@ +from operationscore.Input import * +import urllib, re + +""" +HTML Input, which takes 2 arguments: +- 'Src': a URL to a web page, and +- 'Regex': a Regex to parse data out of the web page. +The input parses the source code of the web page according to the regex, and processes the parsed regex groups. +""" +class HTMLInput(Input): + def inputInit(self): + self.src = self.argDict['Src'] + self.regex = self.argDict['Regex'] + + def getHTML(self): + self.sock = urllib.urlopen(self.src); + self.html = self.sock.read() + self.sock.close() + + def sensingLoop(self): + self.getHTML() + self.dataList = [] + + pattern = re.compile(self.regex) + matchObj = pattern.search(self.html) + self.dataList = matchObj.groups() + + self.respond(self.dataList) + diff --git a/inputs/OSCInput.py b/inputs/OSCInput.py index f867fb5..ba1e035 100644 --- a/inputs/OSCInput.py +++ b/inputs/OSCInput.py @@ -7,8 +7,8 @@ class OSCInput(Input): def inputInit(self): HOST = '' # Symbolic name meaning all available interfaces PORT = self['Port'] # Arbitrary non-privileged port - self.server = liblo.Server(PORT) - self.server.add_method(None,None, self.fallback) + self.server = liblo.Server(PORT) + self.server.add_method(None,None, self.fallback) # except liblo.ServerError, err: # main_log.error(str(err)) diff --git a/inputs/PygameInput.py b/inputs/PygameInput.py index 27b82b0..480630c 100644 --- a/inputs/PygameInput.py +++ b/inputs/PygameInput.py @@ -6,18 +6,32 @@ from pygame.locals import * #This class processes input from an already running pygame instance and passes #it to the parent. This class requires an already running pygame instance. class PygameInput(Input): + """PygameInput is an input tied to the PygameDisplay. Specify: + <FollowMouse>True</FollowMouse> to receive an input every frame specifying the current mouse + position. + <Keyboard>True</Keyboard> to grab keystrokes + <Clicks>True</Clicks> to grab clicks. + + NB: If follow mouse is enabled, PygameInput will not return mouse and keypresses. You can, however, + instantiate other PygameInputs in the XML that will capture mouse and keypresses.""" def sensingLoop(self): - #try: - if self['FollowMouse']: - self.respond({Strings.LOCATION: pygame.mouse.get_pos()}) - return - for event in pygame.event.get(): - if event.type is KEYDOWN: - if event.key == 27: - self.die() - self.respond({Strings.LOCATION: (5,5),'Key': event.key}) - if event.type is MOUSEBUTTONDOWN: + if self['FollowMouse']: + self.respond({Strings.LOCATION: pygame.mouse.get_pos()}) + return + for event in pygame.event.get(): + if event.type is KEYDOWN: + if event.key == 27: + self.die() + if self['Keyboard']: + try: + self.respond({'Key': event.key, 'KeyChar': chr(event.key)}) + except: + self.respond({'Key': event.key}) + return + else: + pygame.event.post(event) + if event.type is MOUSEBUTTONDOWN: + if self['Clicks']: self.respond({Strings.LOCATION: pygame.mouse.get_pos()}) - #except: - #raise Exception('Pygame not initialized. Pygame must be \ - #initialized.') + else: + pygame.event.post(event) diff --git a/inputs/RandomLocs.py b/inputs/RandomLocs.py index d1ce1c7..f4182cf 100644 --- a/inputs/RandomLocs.py +++ b/inputs/RandomLocs.py @@ -1,13 +1,16 @@ -import util.TimeOps as clock -import random -import util.Geo as Geo -import util.Strings as Strings -from operationscore.Input import * -class RandomLocs(Input): - def inputInit(self): - self['LastEvent'] = clock.time() - def sensingLoop(self): #TODO: move to params - currentTime = clock.time() - if currentTime - self['LastEvent'] > 2000: - self.respond({Strings.LOCATION: Geo.randomLoc((50,50))}) - self['LastEvent'] = currentTime +import util.TimeOps as clock
+import random
+import util.Geo as Geo
+import util.Strings as Strings
+from operationscore.Input import *
+class RandomLocs(Input):
+ """RandomLocs is an Input that generates RandomLocations at a preset but randomly changing time interval. Just a
+ prototype, some assembly required."""
+
+ def inputInit(self):
+ self['LastEvent'] = clock.time()
+ def sensingLoop(self): #TODO: move to params
+ currentTime = clock.time()
+ if currentTime - self['LastEvent'] > 200+500*random.random():
+ self.respond({Strings.LOCATION: Geo.randomLoc((200,200))})
+ self['LastEvent'] = currentTime
diff --git a/inputs/TCPInput.py b/inputs/TCPInput.py index 5bf06bf..e565649 100644 --- a/inputs/TCPInput.py +++ b/inputs/TCPInput.py @@ -3,7 +3,14 @@ import pdb from operationscore.Input import * import socket, json, time import logging as main_log +import string +from select import select + class TCPInput(Input): + """TCPInput is a input to receive input on a TCP port. In its current incarnation, it parses + json data into python dicts. Warning: contains a bug where init will hang until it receives a + connection. Specify: + <Port> -- Port number to listen on.""" def inputInit(self): self.HOST = '' # Symbolic name meaning all available interfaces self.PORT = self.argDict['Port'] # Arbitrary non-privileged port @@ -14,15 +21,28 @@ class TCPInput(Input): self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.sock.bind((self.HOST, self.PORT)) self.sock.listen(1) - (self.conn, self.address) = self.sock.accept() + + isreadable=select([self.sock],[],[], 0)[0] + self.conn = None + if isreadable: + (self.conn, self.address) = self.sock.accept() def sensingLoop(self): + if self.conn == None: + isreadable=select([self.sock],[],[], 0)[0] + if isreadable: + (self.conn, self.address) = self.sock.accept() + else: + return + data = self.conn.recv(self.BUFFER_SIZE) main_log.debug('Incoming data', data) if not data or 'end' in data: # data end, close socket main_log.debug('End in data') + print 'end of stream' self.IS_RESPONDING = 0 + self.conn.close() self.sock.close() if self.IS_RESPONDING == 1: # if 'responding', respond to the received data @@ -30,20 +50,18 @@ class TCPInput(Input): for datagroup in data.split('\n'): if datagroup != None and datagroup != '': dataDict = json.loads(datagroup) - # socketDict = {'data':dataDict, 'address':self.address} - socketDict = {Strings.LOCATION: (dataDict['x'], dataDict['y'])} # like PygameInput - print 'input' - self.respond(socketDict) + #if dataDict['type'] != 1: + #print dataDict + self.respond(dataDict) except Exception as exp: print str(exp) else: # if not 'responding', don't respond to data and restart socket # * an incomplete hack for now. will be changed if same-type-multi-Input is implemented. - time.sleep(1) + + self.IS_RESPONDING = 1 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.sock.bind((self.HOST, self.PORT)) self.sock.listen(1) (self.conn, self.address) = self.sock.accept() - self.IS_RESPONDING = 1 - diff --git a/inputs/UDPInput.py b/inputs/UDPInput.py index 7d5609e..83e2f77 100644 --- a/inputs/UDPInput.py +++ b/inputs/UDPInput.py @@ -1,13 +1,17 @@ from operationscore.Input import * import socket class UDPInput(Input): + """UDPInput is a barebones UDP Input class. It takes any data it receives and adds it to the + 'data' element of the response dict. It also notes the 'address'. Specify: + <Port> -- the Port to listen on.""" + def inputInit(self): HOST = '' # Symbolic name meaning all available interfaces PORT = self.argDict['Port'] # Arbitrary non-privileged port self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.bind((HOST, PORT)) def sensingLoop(self): - (data,address) = self.sock.recvfrom(1024) - dataDict = {'data':data, 'address':address} - self.respond(dataDict) + (data,address) = self.sock.recvfrom(1024) + dataDict = {'data':data, 'address':address} + self.respond(dataDict) diff --git a/layouts/LineLayout.py b/layouts/LineLayout.py index 34f5e36..1d40105 100644 --- a/layouts/LineLayout.py +++ b/layouts/LineLayout.py @@ -1,5 +1,5 @@ from operationscore.PixelAssembler import * -#Simple layout class that simply makes a line of LEDs class LineLayout(PixelAssembler): + """LineLayout is a layout class that makes a line of LEDs""" def layoutFunc(self, lastLocation): return (lastLocation[0]+self.argDict['spacing'], lastLocation[1]) diff --git a/layouts/ZigzagLayout.py b/layouts/ZigzagLayout.py index 221571c..3fc2ea1 100644 --- a/layouts/ZigzagLayout.py +++ b/layouts/ZigzagLayout.py @@ -10,6 +10,15 @@ import pdb # | # X-X-X-X etc. class ZigzagLayout(PixelAssembler): + """ZigZagLayout is a slightly more complex layout class that makes a zig-Zag Led Pattern + Inheriting classes must specify zigLength, the length in lights of a of a zig + and zig Axis, the direction of the long X axis (X or Y). + EG: zig length = 4, zig Axis = X would give: + X-X-X-X + | + X-X-X-X + | + X-X-X-X etc. def initLayout(self): if not 'zigLength' in self.argDict: raise Exception('zigLength must be defined in argDict') diff --git a/operationscore/Behavior.py b/operationscore/Behavior.py index b3f7342..6424842 100644 --- a/operationscore/Behavior.py +++ b/operationscore/Behavior.py @@ -1,11 +1,3 @@ -#Abstract class for a behavior. On every time step, the behavior is passed the -#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, (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 * @@ -13,6 +5,16 @@ from logger import main_log #timeStep is called on every iteration of the LightInstallation #addInput is called on each individual input received, and the inputs queue class Behavior(SmootCoreObject): + """Abstract class for a behavior. On every time step, the behavior is passed the + 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, (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 AddPixelEvent). + timeStep is called on every iteration of the LightInstallation + addInput is called on each individual input received, and the inputs queue""" def init(self): self.validateArgs('Behavior.params') if type(self['Inputs']) != type([]): diff --git a/operationscore/Input.py b/operationscore/Input.py index 69375d3..d3d5644 100644 --- a/operationscore/Input.py +++ b/operationscore/Input.py @@ -1,16 +1,14 @@ import threading,time from logger import main_log, exception_log from operationscore.ThreadedSmootCoreObject import ThreadedSmootCoreObject -#Abstract class for inputs. Inheriting classes should call "respond" to raise -#their event. Inheriting classes MUST define sensingLoop. Called at the -#interval specified in RefreshInterval while the input is active. For example, if you are writing -#webserver, this is where the loop should go. -#Inheriting classes MAY define inputInit. This is called before the loop -#begins. import pdb class Input(ThreadedSmootCoreObject): - #Event scope is a function pointer the function that will get called when - #an Parent is raised. + """Abstract class for inputs. Inheriting classes should call "respond" to raise + their event. Inheriting classes MUST define sensingLoop. Called at the + interval specified in RefreshInterval while the input is active. For example, if you are writing + webserver, this is where the loop should go. + Inheriting classes MAY define inputInit. This is called before the loop + begins.""" def init(self): self.eventQueue = [] if not 'RefreshInterval' in self.argDict: diff --git a/operationscore/PixelEvent.py b/operationscore/PixelEvent.py index 80d3b9e..5ed6163 100644 --- a/operationscore/PixelEvent.py +++ b/operationscore/PixelEvent.py @@ -1,6 +1,6 @@ -#Class defining a light response. Inheriting classes should define lightState, -#which should return a color, or None if the response is complete. Consider -#requiring a generate event. +"""PixelEvent is a class defining a light response. Inheriting classes should define state, +which should return a color, or None if the response is complete. Consider +requiring a generate event.""" from operationscore.SmootCoreObject import * from pixelevents.StepEvent import * import util.ColorOps as color diff --git a/operationscore/PixelMapper.py b/operationscore/PixelMapper.py index 7e2b0af..3937973 100644 --- a/operationscore/PixelMapper.py +++ b/operationscore/PixelMapper.py @@ -2,6 +2,9 @@ from operationscore.SmootCoreObject import * from logger import main_log import pdb class PixelMapper(SmootCoreObject): + """PixelMapper is the parent class for PixelMappers. Inheriting classes should define + mappingFunction which takes an eventLocation and a screen and returns a list of (weight, pixels). PixelMapper + handles caching automatically.""" def init(self): self.mem = {} #Dictionary of all seen events self.totalCalls = 0 @@ -19,6 +22,7 @@ class PixelMapper(SmootCoreObject): return self.mem[eventLocation] #Takes a Screen and returns a list of tuples #(pixel, weight), with the sum of weights = 1 - #TODO: consider abstracting away from pixels def mappingFunction(self,eventLocation, screen): - pass + """Takes a Screen and event location and returns a list of tuples (pixel,weight) with + sum(weights)=1""" + raise Exception('Mapping function not defined!') diff --git a/operationscore/Renderer.py b/operationscore/Renderer.py index b422304..0861c44 100644 --- a/operationscore/Renderer.py +++ b/operationscore/Renderer.py @@ -1,10 +1,10 @@ -#Renderer abstract class. Doesn't do much now, but might do more later. -#Inheriting classes MUST define render which takes a light system and renders it. -#Inheriting classes may define initRenderer which is called after the dictionary -#is pulled from config. #TODO: multithreaded-rendering from operationscore.SmootCoreObject import * class Renderer(SmootCoreObject): + """Renderer abstract class. Doesn't do much now, but might do more later. + Inheriting classes MUST define render which takes a light system and renders it. + Inheriting classes may define initRenderer which is called after the dictionary + is pulled from config.""" def init(self): self.initRenderer() def render(lightSystem): diff --git a/operationscore/SmootCoreObject.py b/operationscore/SmootCoreObject.py index 8b36f4d..0d32773 100644 --- a/operationscore/SmootCoreObject.py +++ b/operationscore/SmootCoreObject.py @@ -4,6 +4,10 @@ import thread import util.Config as configGetter class SmootCoreObject(object): + """SmootCoreObject is essentially a super-object class which grants us some niceties. It allows + us to use objects as if they are dictionaries -- we use this to store their arguments + convienently -- note that querying for a parameter that does not exist will return None. It + also offers some basic ThreadSafety.""" def __init__(self, argDict, skipValidation = False): self.dieListeners = [] self.argDict = argDict @@ -13,8 +17,6 @@ class SmootCoreObject(object): for key in argDict: setattr(self, key, argDict[key]) self.init() #call init of inheriting class - # self.__setitem__ = self.argDict.__setitem__ - # self.__getitem__ = self.argDict.__getitem__ def init(self): pass @@ -37,7 +39,8 @@ class SmootCoreObject(object): return self.argDict[item] else: return None - + def __contains__(self, item): + return item in self.argDict def __getiter__(self): return self.argDict.__getiter__() diff --git a/operationscore/ThreadedSmootCoreObject.py b/operationscore/ThreadedSmootCoreObject.py index 967ee35..4b5d513 100644 --- a/operationscore/ThreadedSmootCoreObject.py +++ b/operationscore/ThreadedSmootCoreObject.py @@ -4,6 +4,8 @@ import thread import util.Config as configGetter from operationscore.SmootCoreObject import SmootCoreObject class ThreadedSmootCoreObject(SmootCoreObject, threading.Thread): + """ThreadedSmootCoreObject is a version of SmootCoreObject for objects that want to run on their + own thread""" def __init__(self, argDict, skipValidation = False): SmootCoreObject.__init__(self, argDict, skipValidation) threading.Thread.__init__(self) diff --git a/pixelcore/Pixel.py b/pixelcore/Pixel.py index 1fbea2c..4e7cfce 100644 --- a/pixelcore/Pixel.py +++ b/pixelcore/Pixel.py @@ -1,19 +1,21 @@ import util.ColorOps as color +from logger import main_log import pdb from pixelevents.StepEvent import * import util.TimeOps as timeops -#Pixel keeps a queue of events (PixelEvent objects) (actually a dictionary -#keyed by event time). Every time is state is -#requested, it processes all the members of its queue. If a member returns none, -#it is removed from the queue. Otherwise, its value added to the Pixels color -#weighted by z-index. class Pixel: + """Pixel keeps a queue of events (PixelEvent objects) (actually a dictionary + keyed by event time). Every time is state is + requested, it processes all the members of its queue. If a member returns none, + it is removed from the queue. Otherwise, its value added to the Pixels color + weighted by z-index.""" + radius = 2 timeOff = -1 def __init__(self, location): self.location = location - self.events = {} + self.events = [] self.lastRenderTime = timeops.time() self.lastRender = (0,0,0) @@ -24,44 +26,47 @@ class Pixel: #processInput instead. Also, you shouldn't use this anyway. You should be #using the input method on the screen! def turnOnFor(self, time): - event = StepEvent.generate(time, (255,255,255)) #TODO: Move color to + event = StepEvent.generate(time, (255,255,255)) self.processInput(event, 0) - #arg #Add a pixelEvent to the list of active events def processInput(self,pixelEvent,zindex, scale=1,currentTime=None): #consider migrating arg to dict if currentTime == None: currentTime = timeops.time() - self.events[currentTime] = (zindex,scale, pixelEvent) - + self.events.append((currentTime, zindex, scale, pixelEvent)) #TODO: clean this up, maybe? def clearAllEvents(self): - self.events = {} + self.events = [] - #Combines all PixelEvents currently active and computes the current color of - #the pixel. - def state(self, currentTime=timeops.time()): #TODO: this only evaluates at import time, I think + def state(self, currentTime=None): + """Combines all PixelEvents currently active and computes the current color of + the pixel.""" + if currentTime == None: + currentTime = timeops.time() if currentTime-self.lastRenderTime < 5: return self.lastRender - if self.events == {}: + if self.events == []: self.lastRenderTime = currentTime return (0,0,0) deadEvents = [] resultingColor = (0,0,0) colors = [] - for eventTime in self.events: #TODO: right color weighting code - (zindex,scale,event) = self.events[eventTime] - eventResult = event.state(currentTime-eventTime) + for eventObj in self.events: #TODO: right color weighting code + if len(self.events) > 50: + main_log.error('High pixel event count! Investigate!') + eventTime, zindex, scale, pixelEvent = eventObj + eventResult = pixelEvent.state(currentTime-eventTime) if eventResult != None: scaledEvent = color.multiplyColor(eventResult,scale) if (scaledEvent[0] + scaledEvent[1] + scaledEvent[2]) < 5: - deadEvents.append(eventTime) + pass + #deadEvents.append(eventObj) else: colors.append(scaledEvent) else: - deadEvents.append(eventTime) + deadEvents.append(eventObj) resultingColor = color.combineColors(colors) - [self.events.pop(event) for event in deadEvents] + [self.events.remove(event) for event in deadEvents] resultingColor = [int(round(c)) for c in resultingColor] self.lastRender = tuple(resultingColor) self.lastRenderTime = currentTime diff --git a/pixelcore/PixelEventManager.py b/pixelcore/PixelEventManager.py deleted file mode 100644 index 779a0ce..0000000 --- a/pixelcore/PixelEventManager.py +++ /dev/null @@ -1,2 +0,0 @@ -class PixelEventManager(object): - def init(self) diff --git a/pixelcore/PixelStrip.py b/pixelcore/PixelStrip.py index 647991d..29d3b31 100644 --- a/pixelcore/PixelStrip.py +++ b/pixelcore/PixelStrip.py @@ -4,33 +4,17 @@ import util.Geo as Geo from pixelevents.StepEvent import * import math import pdb -#Python class representing a single Pixel strip (usually 50 Pixels) class PixelStrip: + """Python class representing a single Pixel strip (usually 50 Pixels)""" + def __init__(self, layoutEngine): self.initStrip(layoutEngine) self.argDict = layoutEngine.getStripArgs() + def initStrip(self, layoutEngine): pixelLocations = layoutEngine.getPixelLocations() self.pixels = [Pixel(l) for l in pixelLocations] + def __iter__(self): return self.pixels.__iter__() - def allOn(self, time): - [l.turnOnFor(time) for l in self.pixels] #TODO: add test-on method to - #pixels - def respond(self, responseInfo): - location = responseInfo[Strings.LOCATION] - if not 'PixelEvent' in responseInfo: - if 'Color' in responseInfo: - color = responseInfo['Color'] - else: - raise Exception('Need Color. Probably') - responseInfo['PixelEvent'] = StepEvent.generate(300, color) - (dist, pixel) = self.getPixelNearest(location) - pixel.processInput(responseInfo['PixelEvent'], 0) #TODO: z-index - - def getPixelNearest(self, location): - dists = [(Geo.dist(location, pixel.location), pixel) for pixel in self.pixels] - dists.sort() - return dists[0] - #just for now. diff --git a/pixelcore/Screen.py b/pixelcore/Screen.py index a6fc8c4..ada8d4a 100644 --- a/pixelcore/Screen.py +++ b/pixelcore/Screen.py @@ -10,48 +10,49 @@ import itertools import sys import pdb from logger import main_log -#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: + """Class representing a collection of Pixels grouped into PixelStrips. Needs a + PixelMapper, currently set via setMapper by may be migrated into the argDict.""" + def __init__(self): self.responseQueue = [] self.pixelStrips = [] self.xSortedPixels = [] self.xPixelLocs = [] sizeValid = False - - def addStrip(self, lS): - self.pixelStrips.append(lS) + self.pixelsSorted = False + + def addStrip(self, strip): + self.pixelStrips.append(strip) self.sizeValid = False #keep track of whether or not our screen size has + self.pixelsSorted = False #been invalidated by adding more pixels - self.computeXSortedPixels() - #Returns (pixelIndex, pixel). Does a binary search. def pixelsInRange(self, minX, maxX): + """Returns (pixelIndex, pixel). Does a binary search. Sorts first if neccesary.""" + if not self.pixelsSorted: + self.computeXSortedPixels() minIndex = Search.find_ge(self.xPixelLocs, minX) maxIndex = Search.find_le(self.xPixelLocs, maxX)+1 return self.xSortedPixels[minIndex:maxIndex] def computeXSortedPixels(self): + self.xSortedPixels = [] for pixel in self: self.xSortedPixels.append((pixel.location[0], pixel)) self.xSortedPixels.sort() self.xPixelLocs = [p[0] for p in self.xSortedPixels] - - #For debug only - def allOn(self): - [lS.allOn(-1) for lS in self.pixelStrips] - + self.pixelsSorted = True + def __iter__(self): #the iterator of all our pixel strips chained togther 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. #SUBVERTING DESIGN FOR EFFICIENCY 1/24/11, RCOH -- It would be cleaner to store the time on the responses #themselves, however, it is faster to just pass it in. def timeStep(self, currentTime=None): + """Increments time -- This processes all queued responses, adding that to a queue that will + be processed on the next time step.""" if currentTime == None: currentTime = timeops.time() tempQueue = list(self.responseQueue) @@ -64,6 +65,7 @@ class Screen: self.responseQueue.append(responseInfo) def getSize(self): + """Returns the size of the screen in the form: (minx, miny, maxx, maxy)""" if self.sizeValid: return self.size (minX, minY, maxX, maxY) = (sys.maxint,sys.maxint,-sys.maxint,-sys.maxint) @@ -77,23 +79,19 @@ class Screen: maxY = max(y, maxY) self.size = (0,0, maxX, maxY) self.sizeValid = True - return (0, 0, maxX+100, maxY+100) #TODO: cleaner + return (0, 0, maxX, maxY) #private def processResponse(self, responseInfo, currentTime=None): #we need to make a new dict for #each to prevent interference - #[strip.respond(dict(responseInfo)) for strip in self.pixelStrips] if currentTime == None: currentTime = timeops.time() - print 'cachetime fail' if type(responseInfo) != type(dict()): pass if 'Mapper' in responseInfo: mapper = compReg.getComponent(responseInfo['Mapper']) else: mapper = compReg.getComponent(Strings.DEFAULT_MAPPER) - #if type(mapper) != type(PixelMapper): - # raise Exception('No default mapper specified.') pixelWeightList = mapper.mapEvent(responseInfo['Location'], self) main_log.debug('Screen processing response. ' + str(len(pixelWeightList)) + ' events\ generated') diff --git a/pixelevents/DecayEvent.py b/pixelevents/DecayEvent.py index f8d5ef0..8ff423c 100644 --- a/pixelevents/DecayEvent.py +++ b/pixelevents/DecayEvent.py @@ -3,6 +3,10 @@ import math from util.ColorOps import * import util.Geo as Geo class DecayEvent(PixelEvent): + """DecayEvent is a pixel event that can decay either Exponentially or Proportionally. Specify: + <DecayType> -- Exponential or Proportional + <Coefficient> -- Controls the speed of decay.""" + def initEvent(self): self.coefficient = float(abs(self.Coefficient)) if self.DecayType == 'Exponential': diff --git a/pixelevents/SingleFrameEvent.py b/pixelevents/SingleFrameEvent.py index 767f403..252e31b 100644 --- a/pixelevents/SingleFrameEvent.py +++ b/pixelevents/SingleFrameEvent.py @@ -1,5 +1,8 @@ from operationscore.PixelEvent import * class SingleFrameEvent(PixelEvent): + """SingleFrameEvent is a PixelEvent that will only render for the first frame on which it is + queried""" + def initEvent(self): self.timeState = -1 def state(self, timeDelay): diff --git a/pixelevents/SynchTestEvent.py b/pixelevents/SynchTestEvent.py new file mode 100644 index 0000000..3e7ed0c --- /dev/null +++ b/pixelevents/SynchTestEvent.py @@ -0,0 +1,15 @@ +from operationscore.PixelEvent import * +class SynchTestEvent(PixelEvent): + """SynchTestEvent is an event to test the synchronization of the power supplies""" + def initEvent(self): + self.eventstate = 0 + self.cachedDelay = 0 + def state(self, timeDelay): + if timeDelay != self.cachedDelay: + self.eventstate += 1 + self.cachedDelay = timeDelay + color = [0]*3 + color[self.eventstate % 3] = 150 + if self.eventstate > 500: + self.eventstate = 0 + return color diff --git a/pixelmappers/GaussianMapper.py b/pixelmappers/GaussianMapper.py index c82f243..883a95d 100644 --- a/pixelmappers/GaussianMapper.py +++ b/pixelmappers/GaussianMapper.py @@ -1,8 +1,16 @@ from operationscore.PixelMapper import * import util.Geo as Geo class GaussianMapper(PixelMapper): + """GaussianMapper is a PixelMapper which weights pixels around an event proportional to a + gaussian surface. Specify: + <Height> -- The height of the gaussian surface + <Width> -- The width of the gaussian surface + <MinWeight> -- the minimum weight event that can be returned + <CutoffDist> -- the maximum radius considered + """ + def mappingFunction(self, eventLocation, screen): - returnPixels = [] #TODO: consider preallocation and trimming + returnPixels = [] [x,y] = eventLocation potentialPixels = screen.pixelsInRange(x-self.CutoffDist, \ x+self.CutoffDist) diff --git a/pixelmappers/SimpleMapper.py b/pixelmappers/SimpleMapper.py index 19d44c4..1568de3 100644 --- a/pixelmappers/SimpleMapper.py +++ b/pixelmappers/SimpleMapper.py @@ -2,6 +2,10 @@ from operationscore.PixelMapper import * import util.Geo as Geo import sys class SimpleMapper(PixelMapper): + """SimpleMapper is a PixelMapper which maps events to the nearest Pixel. It also supports + strings of the form: + {x}>5, {y}<10, {x}*{y}<{x}, etc. (Conditons, separated by commas. and and or may also be + used).""" def mappingFunction(self, eventLocation, screen): if type(eventLocation) == type(tuple()): bestDist = sys.maxint @@ -19,7 +23,6 @@ class SimpleMapper(PixelMapper): return [] else: #{x}>5,{y}<k - #TODO: we should probably encapsulate this somewhere ret = [] eventLocation = eventLocation.replace('{x}', 'pixel.location[0]') eventLocation = eventLocation.replace('{y}', 'pixel.location[1]') diff --git a/pixelmappers/WindGaussianMapper.py b/pixelmappers/WindGaussianMapper.py new file mode 100755 index 0000000..c5d77ca --- /dev/null +++ b/pixelmappers/WindGaussianMapper.py @@ -0,0 +1,18 @@ +from operationscore.PixelMapper import * +import util.Geo as Geo +import math +class WindGaussianMapper(PixelMapper): + def mappingFunction(self, eventLocation, screen): + returnPixels = [] #TODO: consider preallocation and trimming + [x,y] = eventLocation + potentialPixels = screen.pixelsInRange(x-self.CutoffDist, x) + for (xloc,pixel) in screen.pixelsInRange(x-self.CutoffDist, x): + pixelDistx = math.fabs(pixel.location[0] - x) + pixelDisty = math.fabs(pixel.location[1] - y) + if pixelDistx < self.CutoffDist: + if pixelDisty < 30: + w = Geo.windtrail(pixelDistx, pixelDisty, self.Height, 0, self.Width) + if w > self.MinWeight: + returnPixels.append((pixel, w)) + + return returnPixels diff --git a/renderers/IndoorRenderer.py b/renderers/IndoorRenderer.py index 76ec172..710ca43 100644 --- a/renderers/IndoorRenderer.py +++ b/renderers/IndoorRenderer.py @@ -4,8 +4,9 @@ import util.NetworkOps as network import util.TimeOps as timeops import socket,pdb sock_port = 6038 -#Renderer for a Specific Light System. class IndoorRenderer(Renderer): + """IndoorRenderer is a renderer for a specific Light System""" + def initRenderer(self): self.stripLocations = {} #Dict that stores info necessary to render to #strips @@ -20,6 +21,7 @@ class IndoorRenderer(Renderer): for stripId in stripsInPowerSupply: self.stripLocations[stripId] = (ip, \ stripsInPowerSupply[stripId]) + self.broadSocket = network.getBroadcastSocket(6038) def render(self, lightSystem, currentTime=timeops.time()): #try: for pixelStrip in lightSystem.pixelStrips: @@ -30,6 +32,9 @@ class IndoorRenderer(Renderer): self.sockets[ip] = network.getConnectedSocket(ip,sock_port) packet = composer.composePixelStripPacket(pixelStrip, port, currentTime) self.sockets[ip].send(packet, 0x00) - #except Exception as inst: - # print inst + + synchPacket = composer.composeSynchPacket() + #pdb.set_trace() + #self.broadSocket.sendto(synchPacket, ('10.0.32.255', 6038)) + diff --git a/renderers/PygameRenderer.py b/renderers/PygameRenderer.py index 9582a03..bc50360 100644 --- a/renderers/PygameRenderer.py +++ b/renderers/PygameRenderer.py @@ -4,16 +4,19 @@ import pygame from pygame.locals import * import pdb class PygameRenderer(Renderer): + """PygameRenderer is a renderer which renders the LightSystem to a pygame display""" + def initRenderer(self): pygame.init() self.screen = pygame.display.set_mode((1300,500)) self.background = pygame.Surface(self.screen.get_size()) self.background = self.background.convert() - self.background.fill(Color('Black')) + self.background.fill(Color(0,0,0)) self.stopwatch = timeops.Stopwatch() self.stopwatch.start() + def render(self, lightSystem, currentTime=timeops.time()): - self.background.fill(Color('Black')) + self.background.fill(Color(0,0,0)) #print 'drawing color:',light.color for light in lightSystem: pygame.draw.circle(self.background, light.state(currentTime), light.location, \ @@ -1,2 +1,2 @@ sudo mkdir -p /var/log/smoot_light -sudo chmod 777 /var/log/smoot_light +sudo chmod -R 777 /var/log/smoot_light diff --git a/testosc.py b/testosc.py deleted file mode 100755 index cea03f4..0000000 --- a/testosc.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python - -import liblo, sys - -# create server, listening on port 1234 -try: - server = liblo.Server(12345) -except liblo.ServerError, err: - print str(err) - sys.exit() - -def foo_bar_callback(path, args): - i, f = args - print "received message '%s' with arguments '%d' and '%f'" % (path, i, f) - -def foo_baz_callback(path, args, types, src, data): - print "received message '%s'" % path - print "blob contains %d bytes, user data was '%s'" % (len(args[0]), data) - -def fallback(path, args, types, src): - print "got unknown message '%s' from '%s'" % (path, src.get_url()) - for a, t in zip(args, types): - print "argument of type '%s': %s" % (t, a) - -# register method taking an int and a float -server.add_method("/foo/bar", 'if', foo_bar_callback) - -# register method taking a blob, and passing user data to the callback -server.add_method("/foo/baz", 'b', foo_baz_callback, "blah") - -# register a fallback for unhandled messages -server.add_method(None, None, fallback) - -# loop and dispatch messages every 100ms -while True: - server.recv(100) diff --git a/tests/TestComponentRegistry.py b/tests/TestComponentRegistry.py index 5ada524..9de1a32 100644 --- a/tests/TestComponentRegistry.py +++ b/tests/TestComponentRegistry.py @@ -4,13 +4,16 @@ from operationscore.SmootCoreObject import SmootCoreObject class TestComponentRegistry(unittest.TestCase): def setUp(self): compReg.initRegistry() + def tearDown(self): compReg.clearRegistry() + def test_register_component_id_specified(self): comp = SmootCoreObject({'Id': 'obj1'}) compReg.registerComponent(comp) newcomp = compReg.getComponent('obj1') assert comp == newcomp + def test_register_new_id(self): comp = SmootCoreObject({}) cid =compReg.registerComponent(comp) diff --git a/tests/TestConfigLoaders.py b/tests/TestConfigLoaders.py index 17fc2d9..c79bbf1 100644 --- a/tests/TestConfigLoaders.py +++ b/tests/TestConfigLoaders.py @@ -7,8 +7,10 @@ import xml class TestConfigLoaders(unittest.TestCase): def setUp(self): pass + def tearDown(self): pass + def test_composite(self): parent = ElementTree() overrider = ElementTree() @@ -20,6 +22,7 @@ class TestConfigLoaders(unittest.TestCase): result = ElementTree(result) result.write('tests/testdata/compositeTESTout.xml') assert filecmp.cmp('tests/testdata/compositeTESTout.xml','tests/testdata/compositeTRUTH.xml') + def test_inheritance(self): result = Config.loadConfigFile('tests/testdata/inheritanceTEST.xml') diff --git a/oscserver.py b/tests/testosc.py index 6763f41..6763f41 100755 --- a/oscserver.py +++ b/tests/testosc.py diff --git a/util/ColorOps.py b/util/ColorOps.py index 143444f..4b1162a 100644 --- a/util/ColorOps.py +++ b/util/ColorOps.py @@ -1,14 +1,21 @@ import random +import colorsys from util.TimeOps import Stopwatch def randomColor(): return [random.randint(0,255) for i in range(3)] + def chooseRandomColor(colorList): + """Given a list of colors, pick one at random""" return random.choice(colorList) def safeColor(c): + """Ensures that a color is valid""" c[0] = c[0] if c[0] < 255 else 255 c[1] = c[1] if c[1] < 255 else 255 c[2] = c[2] if c[2] < 255 else 255 + + return c + def combineColors(colors): result = [0,0,0] for c in colors: @@ -16,5 +23,20 @@ def combineColors(colors): result[1] += c[1] result[2] += c[2] return safeColor(result) + def multiplyColor(color, percent): return safeColor([channel*(percent) for channel in color]) + +def floatToIntColor(rgb): + rgb[0] = int(rgb[0]*256 + .5) + rgb[1] = int(rgb[1]*256 + .5) + rgb[2] = int(rgb[2]*256 + .5) + return safeColor(rgb) + +def randomBrightColor(): + hue = random.random() + sat = random.random()/2.0 + .5 + val = 1.0 + hue, sat, val = colorsys.hsv_to_rgb(hue, sat, val) + ret = [hue, sat, val] + return floatToIntColor(ret) diff --git a/util/Config.py b/util/Config.py index c2d8806..6fdb0d4 100644 --- a/util/Config.py +++ b/util/Config.py @@ -8,12 +8,14 @@ from logger import main_log, exception_log classArgsMem = {} CONFIG_PATH = 'config/' DEFAULT_OVERRIDE_MODE = 'Merge' + def loadParamRequirementDict(className): if not className in classArgsMem: #WOO CACHING classArgsMem[className] = fileToDict(CONFIG_PATH + className) return classArgsMem[className] -#Loads a config file. If its an xml file, inheritances are automatically resolved. + def loadConfigFile(fileName): #TODO: error handling etc. + """Loads a config file. If its an xml file, inheritances are automatically resolved.""" try: if '.params' in fileName: return fileToDict(fileName) @@ -26,17 +28,17 @@ def loadConfigFile(fileName): #TODO: error handling etc. main_log.error('Error loading config file ' + fileName)#, inst) TODO: log exception too main_log.error(str(inst)) return None -#Takes an Element or an ElementTree. If it is a tree, it returns its root. Otherwise, just returns -#it def getElement(el): + """Takes an Element or an ElementTree. If it is a tree, it returns its root. Otherwise, just returns + it""" if xml.etree.ElementTree.iselement(el): return el elif el.__class__ == ElementTree: return el.getroot() -#XML tree composition. Returns the resulting tree, but happens in-place in the overriding tree. -def compositeXMLTrees(parentTree, overridingTree): #TODO: break up into sub-methods, change it to -#use .find() - #type checking -- convert ElementTrees to their root elements +def compositeXMLTrees(parentTree, overridingTree): + """XML tree composition. Returns the resulting tree, but happens in-place in the overriding + tree.""" + #TODO: break up into sub-methods, change it to use .find() if parentTree == None: return overridingTree if overridingTree == None: @@ -75,8 +77,10 @@ def compositeXMLTrees(parentTree, overridingTree): #TODO: break up into sub-meth overrideItems.insert(-1, child) overrideItems.remove(item) return overridingTree + def findElementsByTag(tag, eList): return [el for el in eList if el.tag == tag] + def fileToDict(fileName): fileText = '' try: @@ -95,8 +99,9 @@ def fileToDict(fileName): except: exception_log.info(fileName + ' is not a well formed python dict. Parsing failed') return eval(fileText) -#parses arguments into python objects if possible, otherwise leaves as strings + def pullArgsFromItem(parentNode): + """Parses arguments into python objects if possible, otherwise leaves as strings""" attribArgs = {} for arg in parentNode.attrib: #automatically pull attributes into the argdict attribArgs[arg] = attemptEval(parentNode.attrib[arg]) @@ -112,6 +117,7 @@ def attemptEval(val): except (NameError, SyntaxError): val = str(val) return val + def generateArgDict(parentNode, recurse=False): args = {} for arg in parentNode.getchildren(): @@ -131,13 +137,13 @@ def generateArgDict(parentNode, recurse=False): if len(args.keys()) == 1 and recurse: return args[args.keys()[0]] return args -#In place resolution of document inheritances. Doesn't return anything. def resolveDocumentInheritances(el): + """In place resolution of document inheritances. Doesn't return anything.""" abstractMembers = Search.parental_tree_search(el, '.getchildren()', '.tag==\'InheritsFrom\'') for subel in abstractMembers: subel = resolveInheritance(subel) -#In place resolution of inheritence. Doesn't return anything. def resolveInheritance(el): + """In place resolution of inheritence. Doesn't return anything.""" parentClass = el.find('InheritsFrom') if parentClass != None: parentTree = loadConfigFile(parentClass.text) diff --git a/util/Geo.py b/util/Geo.py index be3e93e..0dde80b 100644 --- a/util/Geo.py +++ b/util/Geo.py @@ -4,20 +4,31 @@ from bisect import * import random 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 + def addLocations(l1,l2): return tuple([l1[i]+l2[i] for i in range(len(l1))]) + 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((l1[0]-l2[0])**2+(l1[1]-l2[1])**2) - #return math.sqrt(sum([(l1[i]-l2[i])**2 for i in range(len(l1))])) + return math.sqrt((l1[0]-l2[0])**2+(l1[1]-l2[1])**2) #For speed + def randomLoc(boundingBox): #TODO: make less shitty loc = [] loc.append(random.randint(0, boundingBox[0])) loc.append(random.randint(0, boundingBox[1])) return tuple(loc) + def approxexp(x): + """Approximates exp with a 3 term Taylor Series.""" return 1+x+x**2/2+x**3/6 + +def windtrail(x,y,height,center,width): + a=height + b=center + c=width + return a*((math.exp(-((x-b))/(c)))**2)*(math.exp(-((y))/(0.2*c)))**2 diff --git a/util/NetworkOps.py b/util/NetworkOps.py index 6c50c6d..3ece763 100644 --- a/util/NetworkOps.py +++ b/util/NetworkOps.py @@ -8,4 +8,9 @@ def getConnectedSocket(ip,port): except Exception as inst: main_log.error('Network down. All network based renderers and sensors will not function.', inst) - print (ip, port) + +def getBroadcastSocket(port): + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) + return s diff --git a/util/PacketComposition.py b/util/PacketComposition.py index c4fcdc3..75ef917 100644 --- a/util/PacketComposition.py +++ b/util/PacketComposition.py @@ -5,7 +5,8 @@ PORTOUT = 0x0108 UNI = 0 import pdb import util.TimeOps as timeops -argDict = {'flags': 0, 'startcode': 0, 'pad':0} +argDict = {'flags': 0, 'startcode': 0x0fff, 'pad':0} + def composePixelStripData(pixelStrip,currentTime=timeops.time()): packet = bytearray() for light in pixelStrip: @@ -19,6 +20,7 @@ def composePixelStripData(pixelStrip,currentTime=timeops.time()): #color = pixelStrip.pixels[i].state() #packet[i:i+2] = color # return bytearray(packet) + cache = {} def memoize(f): def helper(x): @@ -26,20 +28,23 @@ def memoize(f): cache[x] = f(x) return cache[x] return helper + @memoize def cachePacketHeader(port): packet = bytearray() subDict = dict(argDict) - subDict['len'] = 38500 #I have no idea why this works. + subDict['len'] = 150 #I have no idea why this works. subDict['port'] = port packet.extend(portOutPacket(subDict)) - packet.append(0x0) +# packet.append(0x0) return packet + def composePixelStripPacket(pixelStrip,port, currentTime): packet = bytearray(cachePacketHeader(port)) data = composePixelStripData(pixelStrip, currentTime) packet.extend(data) return packet + def packheader(): header = bytearray() header.extend(struct.pack('L', MAGIC)) @@ -47,18 +52,29 @@ def packheader(): header.extend(struct.pack('H', PORTOUT)) header.extend(struct.pack('L', 0)) return header + def portOut(): header = packheader() header.extend(struct.pack('L', UNI)) return header + def portOutPayload(argDict): payload = bytearray() payload.extend(struct.pack('B', argDict['port'])) + payload.extend(struct.pack('B',0)) payload.extend(struct.pack('H', argDict['flags'])) - #payload.append(0x00) #somepadding? lolwtf. payload.extend(struct.pack('H', argDict['len'])) payload.extend(struct.pack('H', argDict['startcode'])) return payload +def composeSynchPacket(): + header = bytearray() + header.extend(struct.pack('L', MAGIC)) + header.extend(struct.pack('H', VERSION)) + header.extend(struct.pack('H', 0x0109)) + header.extend(struct.pack('L', 0)) + header.extend(struct.pack('L', 0)) + return header + def portOutPacket(payloadArgs): packet = bytearray() packet.extend(portOut()) diff --git a/util/Search.py b/util/Search.py index f7e4b81..1499e23 100644 --- a/util/Search.py +++ b/util/Search.py @@ -1,13 +1,13 @@ from bisect import * def find_le(a, x): - 'Find rightmost value less than or equal to x' + """Find rightmost value less than or equal to x""" return bisect_right(a, x)-1 def find_ge(a, x): - 'Find leftmost value greater than x' + """Find leftmost value greater than x""" return bisect_left(a, x) -#returns parents of nodes that meed a given condition def parental_tree_search(root, childrenstr, conditionstr): + """Returns parents of nodes that meed a given condition""" ret = [] queue = [root] while queue: diff --git a/util/TimeOps.py b/util/TimeOps.py index dcd5038..841efab 100644 --- a/util/TimeOps.py +++ b/util/TimeOps.py @@ -1,6 +1,7 @@ import time as clock def time(): return clock.time()*1000 #all times in MS + class Stopwatch: def __init__(self): self.running = False |