aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar eugue <eug.sun@gmail.com>2011-01-27 17:31:02 -0500
committerGravatar eugue <eug.sun@gmail.com>2011-01-27 17:31:02 -0500
commit32fdbbb7512576cb8a54556640b18be160d655d1 (patch)
treec776387e432ef11cbe801745f881e515dd0706c4
parent328464219a02c014caf1608a27a898900cad8456 (diff)
parent5fb3ea060025241105dc8e9a174513c112f9a133 (diff)
Merge branch 'biginstall' of github.com:rcoh/SmootLight into mobileapp
-rw-r--r--LightInstallation.py49
-rw-r--r--behaviors/AddPixelEvent.py5
-rw-r--r--behaviors/AllPixels.py9
-rw-r--r--behaviors/AllPixelsLeft.py1
-rw-r--r--behaviors/BehaviorChain.py23
-rw-r--r--behaviors/ColorChangerBehavior.py18
-rw-r--r--behaviors/DebugBehavior.py5
-rw-r--r--behaviors/DecayBehavior.py1
-rw-r--r--behaviors/DimColor.xml8
-rw-r--r--behaviors/EchoBehavior.py2
-rw-r--r--behaviors/Expand.py21
-rw-r--r--behaviors/MITDoors.py20
-rw-r--r--behaviors/ModifyParam.py9
-rw-r--r--behaviors/MoveBehavior.py3
-rw-r--r--behaviors/RandomWalk.py3
-rw-r--r--behaviors/RecursiveDecay.py5
-rw-r--r--behaviors/ResponseMover.py6
-rw-r--r--behaviors/RestrictLocation.py12
-rw-r--r--behaviors/RiseFall.py40
-rw-r--r--behaviors/RiseFall.xml7
-rw-r--r--behaviors/RunningBehavior.py5
-rw-r--r--behaviors/SingleFrame.xml7
-rw-r--r--behaviors/Square.py24
-rw-r--r--behaviors/TimedDie.py15
-rw-r--r--behaviors/Timeout.py16
-rw-r--r--behaviors/XYMove.py24
-rw-r--r--config/6thFloor.xml178
-rw-r--r--inputs/PygameInput.py35
-rw-r--r--inputs/RandomLocs.py3
-rw-r--r--inputs/TCPInput.py15
-rw-r--r--inputs/UDPInput.py10
-rw-r--r--layouts/60StripLayout.xml60
-rw-r--r--layouts/LineLayout.py2
-rw-r--r--layouts/ZigzagLayout.py9
-rw-r--r--operationscore/Behavior.py20
-rw-r--r--operationscore/Input.py15
-rw-r--r--operationscore/PixelAssembler.py2
-rw-r--r--operationscore/PixelEvent.py7
-rw-r--r--operationscore/PixelMapper.py12
-rw-r--r--operationscore/Renderer.py12
-rw-r--r--operationscore/SmootCoreObject.py9
-rw-r--r--operationscore/ThreadedSmootCoreObject.py2
-rw-r--r--pixelcore/Pixel.py49
-rw-r--r--pixelcore/PixelEventManager.py2
-rw-r--r--pixelcore/PixelStrip.py29
-rw-r--r--pixelcore/Screen.py38
-rw-r--r--pixelevents/DecayEvent.py4
-rw-r--r--pixelevents/SingleFrameEvent.py13
-rw-r--r--pixelmappers/GaussianMapper.py12
-rw-r--r--pixelmappers/SimpleMapper.py5
-rw-r--r--renderers/60StripSeq.xml4
-rw-r--r--renderers/IndoorRenderer.py6
-rw-r--r--renderers/PygameRenderer.py7
-rw-r--r--tests/TestComponentRegistry.py3
-rw-r--r--tests/TestConfigLoaders.py3
-rw-r--r--util/ColorOps.py5
-rw-r--r--util/Config.py26
-rw-r--r--util/Geo.py9
-rw-r--r--util/NetworkOps.py1
-rw-r--r--util/PacketComposition.py9
-rw-r--r--util/Search.py6
-rw-r--r--util/TimeOps.py1
62 files changed, 691 insertions, 270 deletions
diff --git a/LightInstallation.py b/LightInstallation.py
index 7f98473..ad1e38d 100644
--- a/LightInstallation.py
+++ b/LightInstallation.py
@@ -89,8 +89,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)
@@ -98,13 +98,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 = []
@@ -114,7 +110,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 *')
@@ -125,14 +120,12 @@ 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:
main_log.error('Failure while initializing ' + className + ' with ' + str(args))
main_log.error(str(inst))
@@ -145,33 +138,30 @@ class LightInstallation(object):
def mainLoop(self):
lastLoopTime = clock.time()
refreshInterval = 30
- runCount = 2000
- while runCount > 0 and not self.dieNow:
- runCount -= 1
+ 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 != []]
- self.screen.timeStep()
+ self.screen.timeStep(loopStart)
[r.render(self.screen, loopStart) for r in self.renderers]
loopElapsed = clock.time()-loopStart
sleepTime = max(0,refreshInterval-loopElapsed)
+ 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']
@@ -180,9 +170,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'])
@@ -190,12 +180,11 @@ 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
diff --git a/behaviors/AddPixelEvent.py b/behaviors/AddPixelEvent.py
index 7f134e1..821f432 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:
@@ -18,6 +21,8 @@ class AddPixelEvent(Behavior):
ret = []
for sensory in sensors:
outDict = {}
+ if not 'Location' in sensory:
+ pdb.set_trace()
outDict[Strings.LOCATION] = sensory[Strings.LOCATION]
settingsDict = dict(self.argDict)
settingsDict['Color'] = sensory['Color']
diff --git a/behaviors/AllPixels.py b/behaviors/AllPixels.py
new file mode 100644
index 0000000..7f66ad6
--- /dev/null
+++ b/behaviors/AllPixels.py
@@ -0,0 +1,9 @@
+from operationscore.Behavior import *
+class AllPixels(Behavior):
+ """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/DebugBehavior.py b/behaviors/DebugBehavior.py
index 34f4106..8f81954 100644
--- a/behaviors/DebugBehavior.py
+++ b/behaviors/DebugBehavior.py
@@ -2,7 +2,10 @@ 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.debug('Sensor Inputs: ' + str(sensorInputs))
+ main_log.error('Sensor Inputs: ' + str(sensorInputs))
return ([], [])
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/DimColor.xml b/behaviors/DimColor.xml
new file mode 100644
index 0000000..58b0673
--- /dev/null
+++ b/behaviors/DimColor.xml
@@ -0,0 +1,8 @@
+<Behavior>
+ <Class>behaviors.ModifyParam</Class>
+ <Args>
+ <ParamType>Sensor</ParamType>
+ <ParamName>Color</ParamName>
+ <ParamOp>[chan*.95 for chan in {val}]</ParamOp>
+ </Args>
+</Behavior>
diff --git a/behaviors/EchoBehavior.py b/behaviors/EchoBehavior.py
index 589c42b..6ef4fcb 100644
--- a/behaviors/EchoBehavior.py
+++ b/behaviors/EchoBehavior.py
@@ -2,6 +2,8 @@ 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:
diff --git a/behaviors/Expand.py b/behaviors/Expand.py
new file mode 100644
index 0000000..323e71f
--- /dev/null
+++ b/behaviors/Expand.py
@@ -0,0 +1,21 @@
+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'])
+ ret.append(data)
+ return (ret, [])
+
diff --git a/behaviors/MITDoors.py b/behaviors/MITDoors.py
new file mode 100644
index 0000000..d602a55
--- /dev/null
+++ b/behaviors/MITDoors.py
@@ -0,0 +1,20 @@
+from operationscore.Behavior import *
+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]}
+ 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'])
+ ret.append(data)
+ return (ret, [])
diff --git a/behaviors/ModifyParam.py b/behaviors/ModifyParam.py
index f589e05..4f45be0 100644
--- a/behaviors/ModifyParam.py
+++ b/behaviors/ModifyParam.py
@@ -2,8 +2,17 @@ from operationscore.Behavior import *
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} exist in some versions"""
+
def processResponse(self, sensorInputs, recursiveInputs):
paramType = self['ParamType']
+ if paramType == None:
+ paramType = 'Sensor'
paramName = self['ParamName']
paramOp = str(self['ParamOp'])
if paramType == 'Sensor':
diff --git a/behaviors/MoveBehavior.py b/behaviors/MoveBehavior.py
index be41b52..e504ca9 100644
--- a/behaviors/MoveBehavior.py
+++ b/behaviors/MoveBehavior.py
@@ -4,6 +4,9 @@ from operationscore.Behavior import *
#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
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..59e353a 100644
--- a/behaviors/ResponseMover.py
+++ b/behaviors/ResponseMover.py
@@ -1,9 +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 = []
diff --git a/behaviors/RestrictLocation.py b/behaviors/RestrictLocation.py
index febc9ed..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',
@@ -25,7 +35,7 @@ class RestrictLocation(Behavior):
def processResponse(self, sensorInputs, recursiveInputs):
ret = []
for data in sensorInputs:
- if not self.locEval(data['Location']):
+ if self.locEval(data['Location']):
(dataOut, recur) = self.paramModifier.immediateProcessInput([data], [])
#behaviors expect lists ^[]
ret += dataOut
diff --git a/behaviors/RiseFall.py b/behaviors/RiseFall.py
new file mode 100644
index 0000000..109cd10
--- /dev/null
+++ b/behaviors/RiseFall.py
@@ -0,0 +1,40 @@
+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']
+ data['Height'] = data['MaxHeight']*math.sin(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/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/SingleFrame.xml b/behaviors/SingleFrame.xml
new file mode 100644
index 0000000..49b5dcf
--- /dev/null
+++ b/behaviors/SingleFrame.xml
@@ -0,0 +1,7 @@
+<Behavior>
+ <Class>behaviors.AddPixelEvent</Class>
+ <Args>
+ <Class>pixelevents.SingleFrameEvent</Class>
+ <z-index>0</z-index>
+ </Args>
+</Behavior>
diff --git a/behaviors/Square.py b/behaviors/Square.py
index a6e9401..9d3223a 100644
--- a/behaviors/Square.py
+++ b/behaviors/Square.py
@@ -1,11 +1,17 @@
from operationscore.Behavior import *
class Square(Behavior):
- 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'] =\
- '{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/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/XYMove.py b/behaviors/XYMove.py
new file mode 100644
index 0000000..11cee96
--- /dev/null
+++ b/behaviors/XYMove.py
@@ -0,0 +1,24 @@
+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:
+ oploc = dict(loc)
+ self.insertStepIfMissing(oploc)
+ oploc['Location'] = Geo.addLocations((oploc['XStep'], oploc['YStep']), oploc['Location'])
+ ret.append(oploc)
+ return (ret, [])
+ def insertStepIfMissing(self, data):
+ if not 'XStep' in data:
+ data['XStep'] = self['XStep']
+ if not 'YStep' in data:
+ data['YStep'] = self['YStep']
+
diff --git a/config/6thFloor.xml b/config/6thFloor.xml
index 9c5f77e..b58ff57 100644
--- a/config/6thFloor.xml
+++ b/config/6thFloor.xml
@@ -28,9 +28,9 @@
</PixelMapper>
</PixelMapperConfiguration>
<RendererConfiguration>
- <Renderer>
+ <!--Renderer>
<InheritsFrom>renderers/60StripSeq.xml</InheritsFrom>
- </Renderer>
+ </Renderer-->
<Renderer>
<InheritsFrom>renderers/Pygame.xml</InheritsFrom>
</Renderer>
@@ -39,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>
@@ -54,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>
@@ -70,15 +84,31 @@
<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>
</Behavior>
+ <Behavior Id="singleframe">
+ <InheritsFrom>behaviors/SingleFrame.xml</InheritsFrom>
+ </Behavior>
<Behavior Id="slowdecay">
<InheritsFrom>behaviors/PixelDecay.xml</InheritsFrom>
<Args>
@@ -86,16 +116,106 @@
</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.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>inpexpanddim</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.DebugBehavior</Class>
<Args>
<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>
@@ -119,7 +239,7 @@
<Args>
<Id>runcolordecay</Id>
<Inputs>
- <Id>pygame</Id>
+ <Id>pygameclick</Id>
</Inputs>
<ChainedBehaviors>
<Id>colorchange</Id>
@@ -127,24 +247,7 @@
<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>
+ <RenderToScreen>False</RenderToScreen>
<Mapper>gaussmap</Mapper>
</Args>
</Behavior>
@@ -155,16 +258,6 @@
</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>
@@ -194,9 +287,8 @@
</Inputs>
<ChainedBehaviors>
<Id>echo</Id>
- <Id>redshift</Id>
<Id>square</Id>
- <Id>slowdecay</Id>
+ <Id>singleframe</Id>
</ChainedBehaviors>
<RenderToScreen>True</RenderToScreen>
</Args>
diff --git a/inputs/PygameInput.py b/inputs/PygameInput.py
index 27b82b0..399a77e 100644
--- a/inputs/PygameInput.py
+++ b/inputs/PygameInput.py
@@ -6,18 +6,27 @@ 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']:
+ 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.')
diff --git a/inputs/RandomLocs.py b/inputs/RandomLocs.py
index d1ce1c7..2719981 100644
--- a/inputs/RandomLocs.py
+++ b/inputs/RandomLocs.py
@@ -4,6 +4,9 @@ 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 time interval. Just a
+ prototype, some assembly required."""
+
def inputInit(self):
self['LastEvent'] = clock.time()
def sensingLoop(self): #TODO: move to params
diff --git a/inputs/TCPInput.py b/inputs/TCPInput.py
index 513b853..17ea7e6 100644
--- a/inputs/TCPInput.py
+++ b/inputs/TCPInput.py
@@ -6,6 +6,10 @@ import logging as main_log
import string
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
@@ -21,25 +25,20 @@ class TCPInput(Input):
def sensingLoop(self):
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
- #dataDict = json.loads(data)
+
+ if self.IS_RESPONDING == 1: # if 'responding', respond to the received data
try:
for datagroup in data.split('\n'):
if datagroup != None and datagroup != '':
dataDict = json.loads(datagroup)
- #print dataDict
self.respond(dataDict)
- #socketDict = {'data':dataDict, 'address':self.address}
- #socketDict = {Strings.LOCATION: (dataDict['x'], dataDict['y'])} # like PygameInput
- #print 'input'
- #self.respond(socketDict)
except Exception as exp:
print str(exp)
else:
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/60StripLayout.xml b/layouts/60StripLayout.xml
index 30f51c5..45ebcb3 100644
--- a/layouts/60StripLayout.xml
+++ b/layouts/60StripLayout.xml
@@ -1,179 +1,179 @@
<PixelConfiguration>
- <PixelStrip Id="strip1.1" originLocation="(0,0)">
+ <PixelStrip Id="strip1.1" originLocation="(0,0)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip1.2" originLocation="(200,0)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip2.1" originLocation="(0,15)">
+ <PixelStrip Id="strip2.1" originLocation="(0,15)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip2.2" originLocation="(200,15)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip3.1" originLocation="(0,30)">
+ <PixelStrip Id="strip3.1" originLocation="(0,30)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip3.2" originLocation="(200,30)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip4.1" originLocation="(0,45)">
+ <PixelStrip Id="strip4.1" originLocation="(0,45)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip4.2" originLocation="(200,45)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip5.1" originLocation="(0,60)">
+ <PixelStrip Id="strip5.1" originLocation="(0,60)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip5.2" originLocation="(200,60)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip6.1" originLocation="(0,75)">
+ <PixelStrip Id="strip6.1" originLocation="(0,75)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip6.2" originLocation="(200,75)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip7.1" originLocation="(0,90)">
+ <PixelStrip Id="strip7.1" originLocation="(0,90)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip7.2" originLocation="(200,90)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip8.1" originLocation="(0,105)">
+ <PixelStrip Id="strip8.1" originLocation="(0,105)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip8.2" originLocation="(200,105)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip9.1" originLocation="(0,120)">
+ <PixelStrip Id="strip9.1" originLocation="(0,120)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip9.2" originLocation="(200,120)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip10.1" originLocation="(0,135)">
+ <PixelStrip Id="strip10.1" originLocation="(0,135)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip10.2" originLocation="(200,135)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip11.1" originLocation="(0,150)">
+ <PixelStrip Id="strip11.1" originLocation="(0,150)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip11.2" originLocation="(200,150)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip12.1" originLocation="(0,165)">
+ <PixelStrip Id="strip12.1" originLocation="(0,165)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip12.2" originLocation="(200,165)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip13.1" originLocation="(0,180)">
+ <PixelStrip Id="strip13.1" originLocation="(0,180)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip13.2" originLocation="(200,180)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip14.1" originLocation="(0,195)">
+ <PixelStrip Id="strip14.1" originLocation="(0,195)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip14.2" originLocation="(200,195)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip15.1" originLocation="(0,210)">
+ <PixelStrip Id="strip15.1" originLocation="(0,210)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip15.2" originLocation="(200,210)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip16.1" originLocation="(400,0)">
+ <PixelStrip Id="strip16.1" originLocation="(400,0)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip16.2" originLocation="(600,0)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip17.1" originLocation="(400,15)">
+ <PixelStrip Id="strip17.1" originLocation="(400,15)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip17.2" originLocation="(600,15)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip18.1" originLocation="(400,30)">
+ <PixelStrip Id="strip18.1" originLocation="(400,30)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip18.2" originLocation="(600,30)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip19.1" originLocation="(400,45)">
+ <PixelStrip Id="strip19.1" originLocation="(400,45)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip19.2" originLocation="(600,45)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip20.1" originLocation="(400,60)">
+ <PixelStrip Id="strip20.1" originLocation="(400,60)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip20.2" originLocation="(600,60)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip21.1" originLocation="(400,75)">
+ <PixelStrip Id="strip21.1" originLocation="(400,75)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip21.2" originLocation="(600,75)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip22.1" originLocation="(400,90)">
+ <PixelStrip Id="strip22.1" originLocation="(400,90)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip22.2" originLocation="(600,90)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip23.1" originLocation="(400,105)">
+ <PixelStrip Id="strip23.1" originLocation="(400,105)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip23.2" originLocation="(600,105)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip24.1" originLocation="(400,120)">
+ <PixelStrip Id="strip24.1" originLocation="(400,120)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip24.2" originLocation="(600,120)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip25.1" originLocation="(400,135)">
+ <PixelStrip Id="strip25.1" originLocation="(400,135)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip25.2" originLocation="(600,135)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip26.1" originLocation="(400,150)">
+ <PixelStrip Id="strip26.1" originLocation="(400,150)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip26.2" originLocation="(600,150)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip27.1" originLocation="(400,165)">
+ <PixelStrip Id="strip27.1" originLocation="(400,165)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip27.2" originLocation="(600,165)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip28.1" originLocation="(400,180)">
+ <PixelStrip Id="strip28.1" originLocation="(400,180)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip28.2" originLocation="(600,180)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip29.1" originLocation="(400,195)">
+ <PixelStrip Id="strip29.1" originLocation="(400,195)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip29.2" originLocation="(600,195)">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
- <PixelStrip Id="strip30.1" originLocation="(400,210)">
+ <PixelStrip Id="strip30.1" originLocation="(400,210)" Reverse="True">
<InheritsFrom>layouts/50PixelStrip.xml</InheritsFrom>
</PixelStrip>
<PixelStrip Id="strip30.2" originLocation="(600,210)">
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 882a290..6424842 100644
--- a/operationscore/Behavior.py
+++ b/operationscore/Behavior.py
@@ -1,17 +1,20 @@
-#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 *
+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([]):
@@ -56,4 +59,5 @@ class Behavior(SmootCoreObject):
self.recursiveResponseQueue)
self.sensorResponseQueue = []
self.recursiveResponseQueue = recursions
+ main_log.debug(self['Id'] + ' Ouputs ' + str(outputs))
return self.addMapperToResponse(outputs)
diff --git a/operationscore/Input.py b/operationscore/Input.py
index 2ee3c3c..d3d5644 100644
--- a/operationscore/Input.py
+++ b/operationscore/Input.py
@@ -1,20 +1,17 @@
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:
- print 'RefreshInterval not defined. Defaulting to .5s.'
self.argDict['RefreshInterval'] = 500
self.parentScope = self.argDict['parentScope']
self.inputInit()
diff --git a/operationscore/PixelAssembler.py b/operationscore/PixelAssembler.py
index 6878f8a..84f3b0b 100644
--- a/operationscore/PixelAssembler.py
+++ b/operationscore/PixelAssembler.py
@@ -23,6 +23,8 @@ class PixelAssembler(SmootCoreObject):
between adjacent pixels must be less than \
pixelToPixelSpacing.')
locations.append(newLocation)
+ if self['Reverse']:
+ locations.reverse()
return locations
def initLayout(self):
pass
diff --git a/operationscore/PixelEvent.py b/operationscore/PixelEvent.py
index c41df17..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
@@ -26,6 +26,5 @@ class PixelEvent(SmootCoreObject):
color = responseDict['Color']
else:
raise Exception('Need Color. Probably')
- pdb.set_trace()
responseDict['PixelEvent'] = StepEvent.generate(300, color)
diff --git a/operationscore/PixelMapper.py b/operationscore/PixelMapper.py
index 1f94fa5..3937973 100644
--- a/operationscore/PixelMapper.py
+++ b/operationscore/PixelMapper.py
@@ -1,6 +1,10 @@
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
@@ -8,7 +12,8 @@ class PixelMapper(SmootCoreObject):
def mapEvent(self, eventLocation, screen):
self.totalCalls += 1
if self.totalCalls % 100 == 0:
- print self['Id'], self.cachehits / float(self.totalCalls)
+ main_log.info('Cache percentage for :', self['Id'], self.cachehits /\
+ float(self.totalCalls))
if eventLocation in self.mem:
self.cachehits += 1
return self.mem[eventLocation]
@@ -17,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 ed88a8c..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.ThreadedSmootCoreObject import *
-class Renderer(ThreadedSmootCoreObject):
+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 7260e56..6ff2e67 100644
--- a/pixelcore/Pixel.py
+++ b/pixelcore/Pixel.py
@@ -2,18 +2,19 @@ import util.ColorOps as color
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,40 +25,46 @@ 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:
- colors.append(color.multiplyColor(eventResult,scale))
+ scaledEvent = color.multiplyColor(eventResult,scale)
+ if (scaledEvent[0] + scaledEvent[1] + scaledEvent[2]) < 5:
+ 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 662b8fe..595ce72 100644
--- a/pixelcore/PixelStrip.py
+++ b/pixelcore/PixelStrip.py
@@ -4,36 +4,21 @@ 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 render(self, surface):
- [l.render(surface) for l in self.pixels]
- #step
- 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 cfadee8..9a81df7 100644
--- a/pixelcore/Screen.py
+++ b/pixelcore/Screen.py
@@ -10,38 +10,40 @@ 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
-
+ self.pixelsSorted = False
+
def addStrip(self, lS):
self.pixelStrips.append(lS)
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."""
+ 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
@@ -49,11 +51,15 @@ class Screen:
#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.
- def timeStep(self):
+ #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):
+ if currentTime == None:
+ currentTime = timeops.time()
tempQueue = list(self.responseQueue)
self.responseQueue = []
for response in tempQueue:
- self.processResponse(response)
+ self.processResponse(response, currentTime)
#public
def respond(self, responseInfo):
@@ -77,9 +83,12 @@ class Screen:
return (0, 0, maxX+100, maxY+100) #TODO: cleaner
#private
- def processResponse(self, responseInfo): #we need to make a new dict for
+ 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:
@@ -89,7 +98,8 @@ class Screen:
#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')
PixelEvent.addPixelEventIfMissing(responseInfo)
- currentTime = timeops.time()
for (pixel, weight) in pixelWeightList:
pixel.processInput(responseInfo['PixelEvent'], 0,weight, currentTime) #TODO: z-index
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 1c6239f..252e31b 100644
--- a/pixelevents/SingleFrameEvent.py
+++ b/pixelevents/SingleFrameEvent.py
@@ -1,8 +1,13 @@
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.rendered = False
- def state(self):
- if !self.rendered:
- return self['Color']
+ self.timeState = -1
+ def state(self, timeDelay):
+ if self.timeState == -1:
+ self.timeState = timeDelay
+ if self.timeState == timeDelay:
+ return self.Color
return None
diff --git a/pixelmappers/GaussianMapper.py b/pixelmappers/GaussianMapper.py
index 686ebcd..883a95d 100644
--- a/pixelmappers/GaussianMapper.py
+++ b/pixelmappers/GaussianMapper.py
@@ -1,9 +1,19 @@
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)
for (x,pixel) in screen.pixelsInRange(x-self.CutoffDist, \
x+self.CutoffDist):
pixelDist = Geo.dist(pixel.location, eventLocation)
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/renderers/60StripSeq.xml b/renderers/60StripSeq.xml
index 3f5255f..49e2c25 100644
--- a/renderers/60StripSeq.xml
+++ b/renderers/60StripSeq.xml
@@ -3,10 +3,6 @@
<Args>
<Id>indoorRenderer</Id>
<PowerSupply>
- <IP>10.32.0.0</IP>
- <PortMapping>{'strip0.1':1, 'strip0.2':2}</PortMapping>
- </PowerSupply>
- <PowerSupply>
<IP>10.32.0.1</IP>
<PortMapping>{'strip1.1':1, 'strip1.2':2}</PortMapping>
</PowerSupply>
diff --git a/renderers/IndoorRenderer.py b/renderers/IndoorRenderer.py
index 76ec172..531a732 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])
+
def render(self, lightSystem, currentTime=timeops.time()):
#try:
for pixelStrip in lightSystem.pixelStrips:
@@ -30,6 +32,4 @@ 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
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/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/util/ColorOps.py b/util/ColorOps.py
index 143444f..037957a 100644
--- a/util/ColorOps.py
+++ b/util/ColorOps.py
@@ -2,13 +2,17 @@ import random
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 +20,6 @@ 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])
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..05ea9fe 100644
--- a/util/Geo.py
+++ b/util/Geo.py
@@ -4,20 +4,25 @@ 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
diff --git a/util/NetworkOps.py b/util/NetworkOps.py
index 6c50c6d..8894b78 100644
--- a/util/NetworkOps.py
+++ b/util/NetworkOps.py
@@ -8,4 +8,3 @@ 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)
diff --git a/util/PacketComposition.py b/util/PacketComposition.py
index c4fcdc3..7b4fe95 100644
--- a/util/PacketComposition.py
+++ b/util/PacketComposition.py
@@ -6,6 +6,7 @@ UNI = 0
import pdb
import util.TimeOps as timeops
argDict = {'flags': 0, 'startcode': 0, '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,6 +28,7 @@ def memoize(f):
cache[x] = f(x)
return cache[x]
return helper
+
@memoize
def cachePacketHeader(port):
packet = bytearray()
@@ -35,11 +38,13 @@ def cachePacketHeader(port):
packet.extend(portOutPacket(subDict))
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,20 @@ 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('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 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