aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar rcoh <rcoh@mit.edu>2011-02-01 23:14:12 -0500
committerGravatar rcoh <rcoh@mit.edu>2011-02-01 23:14:12 -0500
commit5a97e7548b0ec44f0d61a724903748197e69df7c (patch)
tree7bb6b8ea7e488949eeb16ae18b0ad2230f1f7c9b
parent082e4b0c53123dd377da148541f7d98516716862 (diff)
parent482a94fd48627153b923931d6ff21ebf57fad6f7 (diff)
Merge branch 'osc' of github.com:rcoh/SmootLight into osc
-rwxr-xr-xLightInstallation.py48
-rw-r--r--Profile.py2
-rw-r--r--behaviors/Accelerate.xml2
-rw-r--r--behaviors/AddPixelEvent.py3
-rw-r--r--behaviors/AllPixels.py11
-rw-r--r--behaviors/AllPixelsLeft.py1
-rw-r--r--behaviors/BehaviorChain.py23
-rw-r--r--behaviors/ColorChangerBehavior.py18
-rw-r--r--behaviors/ControllerOSC.py62
-rw-r--r--behaviors/DebugBehavior.py3
-rw-r--r--behaviors/DecayBehavior.py1
-rw-r--r--behaviors/Deccelerate.xml9
-rw-r--r--behaviors/DimColor.xml8
-rw-r--r--behaviors/EchoBehavior.py7
-rw-r--r--behaviors/Expand.py22
-rw-r--r--behaviors/ExpandingColorZones.py21
-rw-r--r--behaviors/Flasher.py41
-rw-r--r--behaviors/MITDoors.py28
-rw-r--r--behaviors/MobileShakeBehavior.py26
-rw-r--r--behaviors/ModifyParam.py14
-rw-r--r--behaviors/MoveBehavior.py35
-rw-r--r--behaviors/RandomSetBrightColorBehavior.py14
-rw-r--r--behaviors/RandomWalk.py3
-rw-r--r--behaviors/RecursiveDecay.py5
-rw-r--r--behaviors/ResponseMover.py14
-rw-r--r--behaviors/RestrictLocation.py10
-rw-r--r--behaviors/RiseFall.py46
-rw-r--r--behaviors/RiseFall.xml7
-rw-r--r--behaviors/RunningBehavior.py5
-rw-r--r--behaviors/Sink.py42
-rwxr-xr-xbehaviors/SmootWind.py43
-rw-r--r--behaviors/Square.py26
-rw-r--r--behaviors/SynchTest.py12
-rw-r--r--behaviors/TimedDie.py15
-rw-r--r--behaviors/Timeout.py16
-rw-r--r--behaviors/TouchOSC.py5
-rw-r--r--behaviors/XYMove.py7
-rw-r--r--config/6thFloor.xml140
-rw-r--r--config/6thFloorOSC.xml112
-rw-r--r--config/Demo.xml227
-rw-r--r--config/FireflyDemo.xml163
-rwxr-xr-xconfig/HTMLTest.xml58
-rw-r--r--config/Jennifer.xml129
-rw-r--r--config/Kuan.xml108
-rw-r--r--config/MobileTest.xml248
-rwxr-xr-xconfig/SmootWindTest.xml181
-rw-r--r--config/SynchTest.xml39
-rwxr-xr-xinputs/HTMLInput.py29
-rw-r--r--inputs/OSCInput.py4
-rw-r--r--inputs/PygameInput.py40
-rw-r--r--inputs/RandomLocs.py29
-rw-r--r--inputs/TCPInput.py34
-rw-r--r--inputs/UDPInput.py10
-rw-r--r--layouts/LineLayout.py2
-rw-r--r--layouts/ZigzagLayout.py9
-rw-r--r--operationscore/Behavior.py18
-rw-r--r--operationscore/Input.py14
-rw-r--r--operationscore/PixelEvent.py6
-rw-r--r--operationscore/PixelMapper.py8
-rw-r--r--operationscore/Renderer.py8
-rw-r--r--operationscore/SmootCoreObject.py9
-rw-r--r--operationscore/ThreadedSmootCoreObject.py2
-rw-r--r--pixelcore/Pixel.py47
-rw-r--r--pixelcore/PixelEventManager.py2
-rw-r--r--pixelcore/PixelStrip.py24
-rw-r--r--pixelcore/Screen.py38
-rw-r--r--pixelevents/DecayEvent.py4
-rw-r--r--pixelevents/SingleFrameEvent.py3
-rw-r--r--pixelevents/SynchTestEvent.py15
-rw-r--r--pixelmappers/GaussianMapper.py10
-rw-r--r--pixelmappers/SimpleMapper.py5
-rwxr-xr-xpixelmappers/WindGaussianMapper.py18
-rw-r--r--renderers/IndoorRenderer.py11
-rw-r--r--renderers/PygameRenderer.py7
-rwxr-xr-xsetup.sh2
-rwxr-xr-xtestosc.py36
-rw-r--r--tests/TestComponentRegistry.py3
-rw-r--r--tests/TestConfigLoaders.py3
-rwxr-xr-xtests/testosc.py (renamed from oscserver.py)0
-rw-r--r--util/ColorOps.py22
-rw-r--r--util/Config.py26
-rw-r--r--util/Geo.py15
-rw-r--r--util/NetworkOps.py7
-rw-r--r--util/PacketComposition.py24
-rw-r--r--util/Search.py6
-rw-r--r--util/TimeOps.py1
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])
diff --git a/Profile.py b/Profile.py
index 2f180c9..2f796b2 100644
--- a/Profile.py
+++ b/Profile.py
@@ -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 &lt; and &gt; to get < and > in XML. EG:
+ <LocationRestriction>{x}&lt;0 or {x}&gt;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}&lt;0 or {y}&gt;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}&lt;0 or {x}&gt;800</LocationRestriction>
+ </Args>
+ </Behavior>
+ <Behavior>
+ <Class>behaviors.RestrictLocation</Class>
+ <Args>
+ <Id>ybounce</Id>
+ <Action>{val}*-1</Action>
+ <ParamName>YStep</ParamName>
+ <LocationRestriction>{y}&lt;0 or {y}&gt;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}&lt;0 or {x}&gt;800</LocationRestriction>
+ </Args>
+ </Behavior>
+ <Behavior>
+ <Class>behaviors.RestrictLocation</Class>
+ <Args>
+ <Id>ybounce</Id>
+ <Action>{val}*-1</Action>
+ <ParamName>YStep</ParamName>
+ <LocationRestriction>{y}&lt;0 or {y}&gt;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, \
diff --git a/setup.sh b/setup.sh
index 8f5f6dc..97ad691 100755
--- a/setup.sh
+++ b/setup.sh
@@ -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