aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Russell Cohen <rcoh@mit.edu>2010-12-03 04:20:17 -0500
committerGravatar Russell Cohen <rcoh@mit.edu>2010-12-03 04:20:17 -0500
commit353ab16db64c86122c0fcb9e1852b85c14b354b8 (patch)
tree8904f1cd312974a849ad5bcf0c8520fd20175cda
parent0366f46d3d7e946e254f933888aea4beb4e70658 (diff)
Speed optimizations abound! Caching on parameter validation, Binary
searching for light locations in pixel mappers, actual thread safety for inputs means we don't drop them randomly, anymore. Caching on PixelStates to reduce multi-renderer costs + a short circuit to speed up processing on pixels that are turned off.
-rw-r--r--LightInstallation.py14
-rw-r--r--Util.py57
-rw-r--r--behaviors/ColorChangerBehavior.py4
-rw-r--r--config/LightInstallationConfig.xml16
-rw-r--r--operationscore/Behavior.py3
-rw-r--r--operationscore/Input.py3
-rw-r--r--operationscore/SmootCoreObject.py6
-rw-r--r--pixelcore/Pixel.py8
-rw-r--r--pixelcore/Screen.py17
-rw-r--r--pixelmappers/GaussianMapper.py7
10 files changed, 103 insertions, 32 deletions
diff --git a/LightInstallation.py b/LightInstallation.py
index 849e41f..8f71cf0 100644
--- a/LightInstallation.py
+++ b/LightInstallation.py
@@ -1,14 +1,17 @@
from xml.etree.ElementTree import ElementTree
from pixelcore.Screen import *
from pixelcore.PixelStrip import *
-import pdb, sys, time, Util
+import pdb, sys, time, Util, thread
from pygame.locals import *
#Python class to instantiate and drive a Screen through different patterns,
#and effects.
class LightInstallation:
def __init__(self, configFileName):
+ self.timer = Util.Stopwatch()
+ self.timer.start()
self.inputs = {} #dict of inputs and their bound behaviors, keyed by InputId
self.behaviors = {}
+ self.lock = thread.allocate_lock()
self.behaviorOutputs = {} #key: [list of output destinations]
self.behaviorInputs = {}
self.componentDict = {}
@@ -39,6 +42,8 @@ class LightInstallation:
self.registerComponents(self.inputs)
self.registerComponents(self.behaviors)
#Done initializing. Lets start this thing!
+ self.timer.stop()
+ print 'Initialization done. Time: ', self.timer.elapsed(), 'ms'
self.mainLoop()
def initializeMapper(self, mapperConfig):
self.mapper = self.initializeComponent(mapperConfig)[0] #TODO: support
@@ -82,16 +87,21 @@ class LightInstallation:
#self.screen.allOn()
lastLoopTime = Util.time()
refreshInterval = 30
- while 1:
+ runCount = 1000
+ while runCount > 0:
+ runCount -= 1
loopStart = Util.time()
responses = self.evaluateBehaviors() #inputs are all queued when they
#happen, so we only need to run the behaviors
+ self.timer.start()
[self.screen.respond(response) for response in responses if
response != []]
self.screen.timeStep()
[r.render(self.screen) for r in self.renderers]
loopElapsed = Util.time()-loopStart
sleepTime = max(0,refreshInterval-loopElapsed)
+ self.timer.stop()
+ #print self.timer.elapsed()
if sleepTime > 0:
time.sleep(sleepTime/1000)
#evaluates all the behaviors (including inter-dependencies) and returns a
diff --git a/Util.py b/Util.py
index 377c2e3..ea75b05 100644
--- a/Util.py
+++ b/Util.py
@@ -1,18 +1,21 @@
import pdb
from xml.etree.ElementTree import ElementTree
import math,struct
+from bisect import *
#import json # json.loads() to decode string; json.dumps() to encode data
import socket
import random
from pygame.locals import *
import time as clock
from pixelevents.StepEvent import *
+
VERSION = 0x0001
MAGIC = 0x4adc0104
MOREMAGIC = 0xdeadbeef
DEEPMAGIC = 0xc001d00d
MAGICHASH = 0x69000420
PORTOUT = 0x0108
+classArgsMem = {}
UNI = 0
CONFIG_PATH = 'config/'
kinetDict = {'flags': 0, 'startcode': 0, 'pad':0}
@@ -55,23 +58,38 @@ def randomColor():
def chooseRandomColor(colorList):
return random.choice(colorList)
def loadParamRequirementDict(className):
- return fileToDict(CONFIG_PATH + className)
+ if not className in classArgsMem: #WOO CACHING
+ classArgsMem[className] = fileToDict(CONFIG_PATH + className)
+ return classArgsMem[className]
def loadConfigFile(fileName):
- fileName = CONFIG_PATH + fileName
- if '.params' in fileName:
- return fileToDict(fileName)
- if '.xml' in fileName:
- config = ElementTree()
- config.parse(fileName)
- return config
+ try:
+ fileName = CONFIG_PATH + fileName
+ if '.params' in fileName:
+ return fileToDict(fileName)
+ if '.xml' in fileName:
+ config = ElementTree()
+ config.parse(fileName)
+ return config
+ except:
+ return None
def fileToDict(fileName):
fileText = ''
- with open(fileName) as f:
- for line in f:
- fileText += line.rstrip('\n').lstrip('\t') + ' '
+ try:
+ with open(fileName) as f:
+ for line in f:
+ fileText += line.rstrip('\n').lstrip('\t') + ' '
+ except IOError:
+ return {}
if fileText == '':
return {}
return eval(fileText)
+def find_le(a, 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'
+ return bisect_left(a, x)
def safeColor(c):
return [min(channel,255) for channel in c]
def combineColors(c1,c2):
@@ -186,7 +204,22 @@ def testXMLParse(fileName):
config.parse(fileName)
print generateArgDict(config.find('ChildElement'))
print generateArgDict(config.find('Renderer'))
-
+class Stopwatch:
+ def __init__(self):
+ self.running = False
+ self.startTime = -1
+ self.stopTime = -1
+ def start(self):
+ self.startTime = Util.time()
+ self.running = True
+ def elapsed(self):
+ if self.running:
+ return Util.time()-self.startTime
+ else:
+ return self.stopTime - self.startTime
+ def stop(self):
+ self.stopTime = Util.time()
+ self.running = False
##CONSTANTS##
location = 'Location'
diff --git a/behaviors/ColorChangerBehavior.py b/behaviors/ColorChangerBehavior.py
index ca80eb4..12ef6f4 100644
--- a/behaviors/ColorChangerBehavior.py
+++ b/behaviors/ColorChangerBehavior.py
@@ -7,8 +7,8 @@ class ColorChangerBehavior(Behavior):
for sensory in sensorInputs:
newDict = dict(sensory) #don't run into shallow copy issues
if self['ColorList'] != None:
- newDict['Color'] = Util.randomColor(self['ColorList'])
+ newDict['Color'] = Util.chooseRandomColor(self['ColorList'])
else:
- newDict['Color'] = Util.chooseRandomColor()
+ newDict['Color'] = Util.randomColor()
ret.append(newDict)
return ret
diff --git a/config/LightInstallationConfig.xml b/config/LightInstallationConfig.xml
index aa331af..dfa05d7 100644
--- a/config/LightInstallationConfig.xml
+++ b/config/LightInstallationConfig.xml
@@ -46,7 +46,7 @@
<Id>gaussmap</Id>
<CutoffDist>30</CutoffDist>
<Width>5</Width>
- <Height>.5</Height>
+ <Height>1</Height>
</Args>
</PixelMapper>
</PixelMapperConfiguration>
@@ -77,14 +77,14 @@
<RefreshInterval>100</RefreshInterval>
</Args>
</InputElement>
- <InputElement>
+ <!--<InputElement>
<Class>inputs.PygameInput</Class>
- <Args><!--Passed as a dictionary-->
+ <Args>
<Id>followmouse</Id>
<RefreshInterval>100</RefreshInterval>
<FollowMouse>True</FollowMouse>
</Args>
- </InputElement>
+ </InputElement>-->
<InputElement>
<Class>inputs.UDPInput</Class>
<Args>
@@ -134,7 +134,7 @@
<Id>debug</Id>
<z-index>0</z-index>
<Inputs>
- <Id>UDP</Id>
+ <Id>pygame</Id>
</Inputs>
</Args>
</Behavior>
@@ -161,7 +161,7 @@
<RenderToScreen>True</RenderToScreen>
</Args>
</Behavior>
- <Behavior>
+ <!--<Behavior>
<Class>behaviors.BehaviorChain</Class>
<Args>
<Id>mousechaser</Id>
@@ -175,7 +175,7 @@
</ChainedBehaviors>
<RenderToScreen>True</RenderToScreen>
</Args>
- </Behavior>
+ </Behavior>-->
<Behavior>
<Class>behaviors.RunningBehavior</Class>
<Args>
@@ -183,7 +183,7 @@
<Inputs>
<Id>pygame</Id>
</Inputs>
- <StepSize>1</StepSize>
+ <StepSize>10</StepSize>
<RenderToScreen>False</RenderToScreen>
</Args>
</Behavior>
diff --git a/operationscore/Behavior.py b/operationscore/Behavior.py
index e300f6b..83d2e7d 100644
--- a/operationscore/Behavior.py
+++ b/operationscore/Behavior.py
@@ -56,5 +56,6 @@ class Behavior(SmootCoreObject):
try:
return outputs
except:
- pdb.set_trace()
+ pass
+ #pdb.set_trace()
return outputs
diff --git a/operationscore/Input.py b/operationscore/Input.py
index 67a7bb0..9ee59f8 100644
--- a/operationscore/Input.py
+++ b/operationscore/Input.py
@@ -32,7 +32,10 @@ class Input(threading.Thread):
def respond(self, eventDict):
#if eventDict != []:
#pdb.set_trace()
+ self.parentScope.lock.acquire()
self.parentScope.processResponse(self.argDict, eventDict)
+ self.parentScope.lock.release()
+ time.sleep(.001)
def newEvent(self, event): #Mostly just useful for grabbing events from the
#computer running the sim (key presses, clicks etc.)
self.eventQueue.append(event)
diff --git a/operationscore/SmootCoreObject.py b/operationscore/SmootCoreObject.py
index 84319af..d29e710 100644
--- a/operationscore/SmootCoreObject.py
+++ b/operationscore/SmootCoreObject.py
@@ -21,10 +21,8 @@ class SmootCoreObject:
def __getiter__(self):
return self.argDict.__getiter__()
def validateArgs(self, argFileName):
- try:
- self.validateArgDict(Util.loadParamRequirementDict(argFileName))
- except IOError:
- print 'No Arg Dict found for ' + self.className()
+ self.validateArgDict(Util.loadParamRequirementDict(argFileName))#util
+ #caches for us, woo!
def validateArgDict(self, validationDict):
for item in validationDict:
if not item in self.argDict:
diff --git a/pixelcore/Pixel.py b/pixelcore/Pixel.py
index 8c42581..4c8ec89 100644
--- a/pixelcore/Pixel.py
+++ b/pixelcore/Pixel.py
@@ -12,6 +12,7 @@ class Pixel:
def __init__(self, location):
self.location = location
self.events = {}
+ self.memState = None
def turnOn(self):
self.turnOnFor(-1)
#Turn the light white for 'time' ms. Really only meant for testing. Use
@@ -28,7 +29,13 @@ class Pixel:
self.events = {}
#Combines all PixelEvents currently active and computes the current color of
#the pixel.
+ def invalidateState(self):
+ self.memState = None
def state(self):
+ if self.memState != None:
+ return self.memState
+ if len(self.events) == 0:
+ return (0,0,0)
deadEvents = []
currentTime = Util.time()
resultingColor = (0,0,0)
@@ -40,6 +47,7 @@ class Pixel:
else:
deadEvents.append(eventTime)
[self.events.pop(event) for event in deadEvents]
+ self.memState = tuple(resultingColor)
return tuple(resultingColor)
def __str__(self):
return 'Loc: ' + str(self.location)
diff --git a/pixelcore/Screen.py b/pixelcore/Screen.py
index 6b5c737..71b9b0b 100644
--- a/pixelcore/Screen.py
+++ b/pixelcore/Screen.py
@@ -7,11 +7,24 @@ class Screen:
def __init__(self):
self.responseQueue = []
self.pixelStrips = []
+ self.xSortedPixels = []
+ self.xPixelLocs = []
sizeValid = False
def addStrip(self, lS):
self.pixelStrips.append(lS)
self.sizeValid = False #keep track of whether or not our screen size has
#been invalidated by adding more pixels
+ self.computeXSortedPixels()
+ #Returns (pixelIndex, pixel). Does a binary search.
+ def pixelsInRange(self, minX, maxX):
+ minIndex = Util.find_ge(self.xPixelLocs, minX)
+ maxIndex = Util.find_le(self.xPixelLocs, maxX)+1
+ return self.xSortedPixels[minIndex:maxIndex]
+ def computeXSortedPixels(self):
+ for pixel in self:
+ self.xSortedPixels.append((pixel.location[0], pixel))
+ self.xSortedPixels.sort()
+ self.xPixelLocs = [p[0] for p in self.xSortedPixels]
def render(self, surface):
[lS.render(surface) for lS in self.pixelStrips]
def setMapper(self, mapper):
@@ -29,6 +42,7 @@ class Screen:
self.responseQueue = []
for response in tempQueue:
self.processResponse(response)
+ [p.invalidateState() for p in self]
#public
def respond(self, responseInfo):
self.responseQueue.append(responseInfo)
@@ -53,7 +67,8 @@ class Screen:
#each to prevent interference
#[strip.respond(dict(responseInfo)) for strip in self.pixelStrips]
if type(responseInfo) != type(dict()):
- pdb.set_trace()
+ pass
+ #pdb.set_trace()
pixelWeightList = self.mapper.mapEvent(responseInfo['Location'], self)
Util.addPixelEventIfMissing(responseInfo)
for (pixel, weight) in pixelWeightList:
diff --git a/pixelmappers/GaussianMapper.py b/pixelmappers/GaussianMapper.py
index 552f5c9..1cf9e88 100644
--- a/pixelmappers/GaussianMapper.py
+++ b/pixelmappers/GaussianMapper.py
@@ -3,11 +3,14 @@ import Util
class GaussianMapper(PixelMapper):
def mappingFunction(self, eventLocation, screen):
returnPixels = []
- for pixel in screen:
+ [x,y] = eventLocation
+ for (x,pixel) in screen.pixelsInRange(x-self['CutoffDist'], \
+ x+self['CutoffDist']):
pixelDist = Util.dist(pixel.location, eventLocation)
if pixelDist < self['CutoffDist']:
w = Util.gaussian(pixelDist, self['Height'], 0, self['Width'])
if w>1:
- pdb.set_trace()
+ #pdb.set_trace()
+ pass
returnPixels.append((pixel, w))
return returnPixels