diff options
-rw-r--r-- | LightInstallation.py | 7 | ||||
-rw-r--r-- | Util.py | 7 | ||||
-rw-r--r-- | __init__.py | 0 | ||||
-rw-r--r-- | config/.LightInstallationConfig.xml.swp | bin | 20480 -> 0 bytes | |||
-rw-r--r-- | config/DecayBehavior.params | 3 | ||||
-rw-r--r-- | config/LightInstallationConfig.xml | 9 | ||||
-rw-r--r-- | config/MouseFollowerDemo.xml | 138 | ||||
-rw-r--r-- | config/PixelAssembler.params | 6 | ||||
-rw-r--r-- | operationscore/PixelEvent.py | 2 | ||||
-rw-r--r-- | operationscore/PixelMapper.py | 17 | ||||
-rw-r--r-- | pixelcore/Pixel.py | 13 | ||||
-rw-r--r-- | pixelcore/PixelStrip.py | 2 | ||||
-rw-r--r-- | pixelcore/Screen.py | 8 | ||||
-rw-r--r-- | pixelmappers/SimpleMapper.py | 14 | ||||
-rw-r--r-- | pixelmappers/__init__.py | 0 |
15 files changed, 219 insertions, 7 deletions
diff --git a/LightInstallation.py b/LightInstallation.py index 4efa17b..6866271 100644 --- a/LightInstallation.py +++ b/LightInstallation.py @@ -24,17 +24,24 @@ class LightInstallation: pixelConfig = config.find('PixelConfiguration') inputConfig = config.find('InputConfiguration') behaviorConfig = config.find('BehaviorConfiguration') + mapperConfig = config.find('PixelMapperConfiguration') #inits self.initializeScreen(pixelConfig) self.initializeRenderers(rendererConfig) self.initializeInputs(inputConfig) self.initializeBehaviors(behaviorConfig) + self.initializeMapper(mapperConfig) + + self.screen.setMapper(self.mapper) #registration in dict self.registerComponents(self.renderers) self.registerComponents(self.inputs) self.registerComponents(self.behaviors) #Done initializing. Lets start this thing! self.mainLoop() + def initializeMapper(self, mapperConfig): + self.mapper = self.initializeComponent(mapperConfig)[0] #TODO: support + #multiple mappers def initializeScreen(self, layoutConfig): pixelAssemblers = self.initializeComponent(layoutConfig) [self.addPixelStrip(l) for l in pixelAssemblers] @@ -23,6 +23,13 @@ def getComponentById(cid): return componentDict[cid] else: return None +def addPixelEventIfMissing(responseDict): + if not 'PixelEvent' in responseDict: + if 'Color' in responseDict: + color = responseDict['Color'] + else: + raise Exception('Need Color. Probably') + responseDict['PixelEvent'] = StepEvent.generate(300, color) def dist(l1, l2): return math.sqrt(sum([(l1[i]-l2[i])**2 for i in range(len(l1))])) def time(): diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/__init__.py diff --git a/config/.LightInstallationConfig.xml.swp b/config/.LightInstallationConfig.xml.swp Binary files differdeleted file mode 100644 index 05e30ce..0000000 --- a/config/.LightInstallationConfig.xml.swp +++ /dev/null diff --git a/config/DecayBehavior.params b/config/DecayBehavior.params new file mode 100644 index 0000000..67b7dcf --- /dev/null +++ b/config/DecayBehavior.params @@ -0,0 +1,3 @@ +{'DecayType': 'Decay type missing. Specify Exponential or Proportional.', + 'Coefficient':'Coeffienct missing. Coefficient for decay type. E^(-ct) if + exponential, c/t if proportional.'} diff --git a/config/LightInstallationConfig.xml b/config/LightInstallationConfig.xml index 3f73e0c..713646e 100644 --- a/config/LightInstallationConfig.xml +++ b/config/LightInstallationConfig.xml @@ -1,7 +1,6 @@ <!---All configuration items contain a "Class" tag specifying the python class they represent, and an "Args" tag specifying the args to be passed in.--> <LightInstallation> <PixelConfiguration> - <PixelStrip> <Class>layouts.ZigzagLayout</Class><!--Name of Layout Class, imported dynamically via a eval('import ' + name)--> @@ -35,6 +34,14 @@ </Args> </PixelStrip> </PixelConfiguration> + <PixelMapperConfiguration> + <PixelMapper> + <Class>pixelmappers.SimpleMapper</Class> + <Args> + <Id>simplemap</Id> + </Args> + </PixelMapper> + </PixelMapperConfiguration> <RendererConfiguration> <Renderer> <Class>renderers.PygameRenderer</Class> diff --git a/config/MouseFollowerDemo.xml b/config/MouseFollowerDemo.xml new file mode 100644 index 0000000..9e9f6e5 --- /dev/null +++ b/config/MouseFollowerDemo.xml @@ -0,0 +1,138 @@ +<!---All configuration items contain a "Class" tag specifying the python class they represent, and an "Args" tag specifying the args to be passed in.--> +<LightInstallation> + <LayoutConfiguration> + + <PixelStrip> + <Class>layouts.ZigzagLayout</Class><!--Name of Layout Class, + imported dynamically via a eval('import ' + name)--> + <Args><!--Any args the layout class needs. This go as + elements of a dictionary that gets passed to the Layout machinery--> + <Id>strip1</Id> + <zigLength>25</zigLength> + <zigAxis>X</zigAxis> + <yDirection>-1</yDirection> + <pixelToPixelSpacing>12</pixelToPixelSpacing> + <spacing>12</spacing> <!--we can space at any value less the + l2lspacing--> + <numPixels>50</numPixels> + <originLocation>(10,10)</originLocation> + </Args> + </PixelStrip> + <PixelStrip> + <Class>layouts.ZigzagLayout</Class><!--Name of Layout Class, + imported dynamically via a eval('import ' + name)--> + <Args><!--Any args the layout class needs. This go as + elements of a dictionary that gets passed to the Layout machinery--> + <Id>strip2</Id> + <zigLength>25</zigLength> + <zigAxis>X</zigAxis> + <yDirection>1</yDirection> + <pixelToPixelSpacing>12</pixelToPixelSpacing> + <spacing>12</spacing> <!--we can space at any value less the + l2lspacing--> + <numPixels>50</numPixels> + <originLocation>(10,30)</originLocation> + </Args> + </PixelStrip> + </LayoutConfiguration> + <RendererConfiguration> + <Renderer> + <Class>renderers.PygameRenderer</Class> + <Args> + <displaySize>(1300,50)</displaySize> + </Args> + </Renderer> + <Renderer> + <Class>renderers.IndoorRenderer</Class> + <Args> + <PowerSupply> + <IP>10.1.218.72</IP> + <PortMapping>{'strip1':1, 'strip2':2}</PortMapping> + </PowerSupply> + </Args> + </Renderer> + </RendererConfiguration> + <InputConfiguration> + <InputElement> + <Class>inputs.PygameInput</Class> + <Args><!--Passed as a dictionary--> + <Id>pygame</Id> + <RefreshInterval>100</RefreshInterval> + </Args> + </InputElement> + <InputElement> + <Class>inputs.PygameInput</Class> + <Args><!--Passed as a dictionary--> + <Id>followmouse</Id> + <FollowMouse>True</FollowMouse> + <RefreshInterval>100</RefreshInterval> + </Args> + </InputElement> + <InputElement> + <Class>inputs.UDPInput</Class> + <Args> + <Id>UDP</Id> + <Port>6038</Port> + <RefreshInterval>100</RefreshInterval> + </Args> + </InputElement> + </InputConfiguration> + <BehaviorConfiguration> + <Behavior> + <Class>behaviors.EchoBehavior</Class> + <Args> + <Id>echo</Id> + <z-index>0</z-index> + <RenderToScreen>False</RenderToScreen> + <Inputs> + </Inputs> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.ColorChangerBehavior</Class> + <Args> + <Id>color</Id> + <z-index>0</z-index> + <RenderToScreen>False</RenderToScreen> + <Inputs> + </Inputs> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.DecayBehavior</Class> + <Args> + <Id>decay</Id> + <DecayType>Exponential</DecayType> + <Coefficient>.01</Coefficient> + <z-index>0</z-index> + <RenderToScreen>False</RenderToScreen> + <Inputs> + </Inputs> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.DebugBehavior</Class> + <Args> + <Id>debug</Id> + <z-index>0</z-index> + <Inputs> + <Id>UDP</Id> + </Inputs> + </Args> + </Behavior> + <Behavior> + <Class>behaviors.BehaviorChain</Class> + <Args> + <Inputs> + <Id>followmouse</Id> + </Inputs> + <ChainedBehaviors> + <Id>echo</Id> + <Id>color</Id> + <Id>decay</Id> + </ChainedBehaviors> + <RenderToScreen>True</RenderToScreen> + </Args> + </Behavior> + </BehaviorConfiguration> +</LightInstallation> diff --git a/config/PixelAssembler.params b/config/PixelAssembler.params new file mode 100644 index 0000000..1b1dee5 --- /dev/null +++ b/config/PixelAssembler.params @@ -0,0 +1,6 @@ +{'pixelToPixelSpacing':'pixelToPixel spacing not defined in argDict. This is the +length of wire between 2 adjacent LEDs. Common values are 4 or 12. +Specified in config XML.', 'numPixels': 'numPixels not defined in +argDict. Common value: 50.', 'originLocation':'originLocation not +defined in argDict. Values should be a string in the form (x,y). This +should be specified in the config XML.'} diff --git a/operationscore/PixelEvent.py b/operationscore/PixelEvent.py index 07669cd..8567d93 100644 --- a/operationscore/PixelEvent.py +++ b/operationscore/PixelEvent.py @@ -8,6 +8,8 @@ class PixelEvent(SmootCoreObject): self.initEvent() def initEvent(self): pass + def scale(c): + self['Color'] *= c def state(self,timeDelay): pass diff --git a/operationscore/PixelMapper.py b/operationscore/PixelMapper.py new file mode 100644 index 0000000..bbbfcf4 --- /dev/null +++ b/operationscore/PixelMapper.py @@ -0,0 +1,17 @@ +from operationscore.SmootCoreObject import * +import Util +import pdb +class PixelMapper(SmootCoreObject): + def init(self): + self.mem = {} #Dictionary of all seen events + def mapEvent(self, eventLocation, screen): + if eventLocation in self.mem: + return self.mem[eventLocation] + else: + self.mem[eventLocation] = self.mappingFunction(eventLocation, screen) + 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 diff --git a/pixelcore/Pixel.py b/pixelcore/Pixel.py index a71dba5..8c42581 100644 --- a/pixelcore/Pixel.py +++ b/pixelcore/Pixel.py @@ -3,26 +3,31 @@ import pdb from pixelevents.StepEvent import * #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 cue. If a member returns none, +#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: radius = 2 timeOff = -1 - def __init__(self, location, color): + def __init__(self, location): self.location = location - self.color = color self.events = {} def turnOn(self): self.turnOnFor(-1) + #Turn the light white for 'time' ms. Really only meant for testing. Use + #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,0,255)) #TODO: Move color to + event = StepEvent.generate(time, (255,255,255)) #TODO: Move color to self.processInput(event, 0) #arg + #Add a pixelEvent to the list of active events def processInput(self,pixelEvent,zindex): #consider migrating arg to dict self.events[Util.time()] = (zindex, pixelEvent) def clearAllEvents(self): self.events = {} + #Combines all PixelEvents currently active and computes the current color of + #the pixel. def state(self): deadEvents = [] currentTime = Util.time() diff --git a/pixelcore/PixelStrip.py b/pixelcore/PixelStrip.py index 45d2c8b..c82a87a 100644 --- a/pixelcore/PixelStrip.py +++ b/pixelcore/PixelStrip.py @@ -11,7 +11,7 @@ class PixelStrip: self.argDict = layoutEngine.getStripArgs() def initStrip(self, layoutEngine): pixelLocations = layoutEngine.getPixelLocations() - self.pixels = [Pixel(l, (0,0,0)) for l in pixelLocations] + self.pixels = [Pixel(l) for l in pixelLocations] def __iter__(self): return self.pixels.__iter__() def render(self, surface): diff --git a/pixelcore/Screen.py b/pixelcore/Screen.py index 9806daa..561dc21 100644 --- a/pixelcore/Screen.py +++ b/pixelcore/Screen.py @@ -9,6 +9,8 @@ class Screen: self.pixelStrips.append(lS) def render(self, surface): [lS.render(surface) for lS in self.pixelStrips] + def setMapper(self, mapper): + self.mapper = mapper def allOn(self): [lS.allOn(-1) for lS in self.pixelStrips] def __iter__(self): #the iterator of all our pixel strips chained togther @@ -27,5 +29,9 @@ class Screen: #private def processResponse(self, responseInfo): #we need to make a new dict for #each to prevent interference - [strip.respond(dict(responseInfo)) for strip in self.pixelStrips] + #[strip.respond(dict(responseInfo)) for strip in self.pixelStrips] + pixelWeightList = self.mapper.mapEvent(responseInfo['Location'], self) + Util.addPixelEventIfMissing(responseInfo) + for (pixel, weight) in pixelWeightList: #TODO: weighting + pixel.processInput(responseInfo['PixelEvent'], 0) #TODO: z-index diff --git a/pixelmappers/SimpleMapper.py b/pixelmappers/SimpleMapper.py new file mode 100644 index 0000000..7d730f1 --- /dev/null +++ b/pixelmappers/SimpleMapper.py @@ -0,0 +1,14 @@ +from operationscore.PixelMapper import * +import Util +class SimpleMapper(PixelMapper): + def mappingFunction(self, eventLocation, screen): + bestDist = 10**10 #don't kill me, I'm lazy + bestPixel = None + for pixel in screen: + pixelDist = Util.dist(pixel.location, eventLocation) + if pixelDist < bestDist: + bestPixel = pixel + bestDist = pixelDist + return [(bestPixel,1)] + + diff --git a/pixelmappers/__init__.py b/pixelmappers/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/pixelmappers/__init__.py |