Changeset - f77dfa8e82b9
[Not reviewed]
default
0 2 0
Drew Perttula - 6 years ago 2019-06-06 11:59:44
drewp@bigasterisk.com
new 'image' effect for animating light colors
Ignore-this: 8df08111173bd96ff8d8093d4243c3ba
2 files changed with 36 insertions and 2 deletions:
0 comments (0 inline, 0 general)
light9/effect/effecteval.py
Show inline comments
 
from rdflib import Literal, URIRef
 
from light9.namespaces import L9, DEV
 
from webcolors import rgb_to_hex, hex_to_rgb
 
from colorsys import hsv_to_rgb
 
import math
 
from noise import pnoise1
 
import logging
 
from light9.effect.settings import DeviceSettings
 
from light9.effect.scale import scale
 
from typing import Dict, Tuple, Any
 
from PIL import Image
 
import random
 
random.seed(0)
 

	
 
log = logging.getLogger('effecteval')
 
log.info("reload effecteval")
 

	
 

	
 
def literalColor(rnorm, gnorm, bnorm):
 
    return Literal(
 
        rgb_to_hex([int(rnorm * 255),
 
                    int(gnorm * 255),
 
                    int(bnorm * 255)]))
 
@@ -376,12 +377,36 @@ def effect_lightning(effectSettings, str
 
        L9['device/backlight2'], L9['device/backlight3'],
 
        L9['device/backlight4'], L9['device/backlight5'], L9['device/down2'],
 
        L9['device/down3'], L9['device/down4'], L9['device/hexLow3'],
 
        L9['device/hexLow5'], L9['device/postL1'], L9['device/postR1']
 
    ]
 
    out = {}
 
    col = rgb_to_hex([int(255 * strength)] * 3)
 
    for i, dev in enumerate(devs):
 
        n = noise(songTime * 8 + i * 6.543)
 
        if n > .4:
 
            out[(dev, L9['color'])] = col
 
    return out
 

	
 
def sample(img, x, y, repeat=False):
 
    if 0 <= x < img.width:
 
        return img.getpixel((x, y))
 
    elif not repeat:
 
        return (0, 0, 0)
 
    else:
 
        return img.getpixel((x % img.width, y))
 

	
 
def effect_image(effectSettings, strength, songTime, noteTime):
 
    out = {}
 
    imgPath = f'cur/anim/{effectSettings[L9["image"]]}'
 
    t_offset = effectSettings.get(L9['tOffset'], 0)
 
    pxPerSec = effectSettings.get(L9['pxPerSec'], 30)
 
    img = Image.open(imgPath)
 
    x = (noteTime * pxPerSec)
 

	
 
    scl = effectSettings.get(L9['strength'], 1)
 
    for dev, y in [(L9['theater/skyline/device/strip1'], 0),
 
                   (L9['theater/skyline/device/strip2'], 1),
 
                   (L9['theater/skyline/device/strip3'], 2)]:
 
        color = sample(img, x, y, effectSettings.get(L9['repeat'], False))
 
        out[(dev, L9['color'])] = scale(rgb_to_hex(color), scl)
 
    return out
light9/effect/sequencer.py
Show inline comments
 
@@ -3,24 +3,25 @@ copies from effectloop.py, which this sh
 
'''
 

	
 
from louie import dispatcher
 
from rdflib import URIRef
 
from twisted.internet import reactor
 
from twisted.internet import defer
 
from twisted.internet.defer import Deferred, inlineCallbacks
 
from twisted.internet.inotify import INotify
 
from twisted.python.filepath import FilePath
 
import cyclone.sse
 
import logging, bisect, time
 
import traceback
 
from decimal import Decimal
 
from typing import Any, Callable, Dict, List, Tuple, cast, Union
 

	
 
from light9.ascoltami.musictime_client import MusicTime
 
from light9.effect import effecteval
 
from light9.effect.settings import DeviceSettings
 
from light9.effect.simple_outputs import SimpleOutputs
 
from light9.namespaces import L9, RDF
 
from light9.newtypes import DeviceUri, DeviceAttr, NoteUri, Curve, Song
 
from rdfdb.syncedgraph import SyncedGraph
 
from standardservice.scalessetup import gatherProcessStats
 

	
 
from greplin import scales
 
@@ -38,37 +39,44 @@ updateStats = scales.collection(
 
    scales.PmfStat('updateLoopLatency', recalcPeriod=1),
 
    scales.DoubleStat('updateLoopLatencyGoal'),
 
    scales.RecentFpsStat('updateFps'),
 
    scales.DoubleStat('goalFps'),
 
)
 
compileStats = scales.collection(
 
    '/compile',
 
    scales.PmfStat('graph', recalcPeriod=1),
 
    scales.PmfStat('song', recalcPeriod=1),
 
)
 

	
 

	
 
def pyType(n):
 
    ret = n.toPython()
 
    if isinstance(ret, Decimal):
 
        return float(ret)
 
    return ret
 

	
 

	
 
class Note(object):
 

	
 
    def __init__(self, graph: SyncedGraph, uri: NoteUri, effectevalModule,
 
                 simpleOutputs):
 
        g = self.graph = graph
 
        self.uri = uri
 
        self.effectEval = effectevalModule.EffectEval(
 
            graph, g.value(uri, L9['effectClass']), simpleOutputs)
 
        self.baseEffectSettings: Dict[URIRef, Any] = {}  # {effectAttr: value}
 
        for s in g.objects(uri, L9['setting']):
 
            settingValues = dict(g.predicate_objects(s))
 
            ea = settingValues[L9['effectAttr']]
 
            self.baseEffectSettings[ea] = settingValues[L9['value']]
 
            self.baseEffectSettings[ea] = pyType(settingValues[L9['value']])
 

	
 
        def floatVal(s, p):
 
            return float(g.value(s, p).toPython())
 

	
 
        originTime = floatVal(uri, L9['originTime'])
 
        self.points: List[Tuple[float, float]] = []
 
        for curve in g.objects(uri, L9['curve']):
 
            self.points.extend(
 
                self.getCurvePoints(curve, L9['strength'], originTime))
 
        self.points.sort()
 

	
 
    def getCurvePoints(self, curve: Curve, attr,
 
@@ -103,25 +111,25 @@ class Note(object):
 

	
 
    def outputSettings(
 
            self,
 
            t: float) -> Tuple[List[Tuple[DeviceUri, DeviceAttr, float]], Dict]:
 
        """
 
        list of (device, attr, value), and a report for web
 
        """
 
        report = {
 
            'note': str(self.uri),
 
            'effectClass': self.effectEval.effect,
 
        }
 
        effectSettings: Dict[DeviceAttr, Union[float, str]] = dict(
 
            (DeviceAttr(da), v.toPython())
 
            (DeviceAttr(da), v)
 
            for da, v in self.baseEffectSettings.items())
 
        effectSettings[L9['strength']] = self.evalCurve(t)
 

	
 
        def prettyFormat(x: Union[float, str]):
 
            if isinstance(x, float):
 
                return round(x, 4)
 
            return x
 

	
 
        report['effectSettings'] = dict(
 
            (str(k), prettyFormat(v))
 
            for k, v in sorted(effectSettings.items()))
 
        report['nonZero'] = cast(float, effectSettings[L9['strength']]) > 0
 
@@ -225,24 +233,25 @@ class Sequencer(object):
 
            updateStats.updateLoopLatency = took
 

	
 
            if not self.lastLoopSucceeded:
 
                log.info('Sequencer.update is working')
 
                self.lastLoopSucceeded = True
 
        
 
            delay = max(0, 1 / self.fps - took)
 
            reactor.callLater(delay, self.updateLoop)
 

	
 
    @updateStats.updateFps.rate()
 
    @inlineCallbacks
 
    def update(self) -> Deferred:
 
        
 
        with updateStats.s0_getMusic.time():
 
            musicState = self.music.getLatest()
 
            if not musicState.get('song') or not isinstance(
 
                    musicState.get('t'), float):
 
                return defer.succeed(0.0)
 
            song = Song(URIRef(musicState['song']))
 
            dispatcher.send('state',
 
                            update={
 
                                'song': str(song),
 
                                't': musicState['t']
 
                            })
 

	
0 comments (0 inline, 0 general)