-
Notifications
You must be signed in to change notification settings - Fork 12
Raw preset development
If you haven't already mastered basic preset development, you may wish to start there. I will assume you are familiar with that section already.
Raw presets are designed for per-pixel manipulation. This concept can be much more efficient for some preset designs that apply logic on a per-pixel basis, since implementing them using tickers would quickly become unwieldy.
Raw presets are children of the RawPreset
class, which itself is a child of the Preset
class. Because raw presets are rendered
differently than basic presets by the mixer, you cannot use tickers in raw presets.
As with basic presets, raw presets can override the setup()
and parameter_changed()
methods to run initialization code if necessary.
Raw presets should also override the draw()
method, which is called by the mixer each frame. draw()
can call the setp()
method to set pixels to colors. When implementing draw()
, take care to minimize the size and scope of loops if possible in order to get the best performance. Looping over all pixels is possible, but on a typical computer, adding in too many function calls or math operations to this loop can cause performance problems or stuttering. If you find that your preset is not running as quickly as you'd like, see if any of the operations you perform in loops can be extracted from the loop and cached in your preset class (for example, a preset that uses a pixel's (x, y) location to calculate its distance from a certain point could cache that distance, because the pixel locations do not change during runtime). Note that calls to the Scene object such as get_pixel_location()
are already cached, so you do not need to implement your own caching on the return values from those calls.
Let's dissect the code to one of the example presets: Twinkle
import colorsys
import random
from lib.raw_preset import RawPreset
from lib.colors import float_to_uint8
from lib.color_fade import ColorFade
from lib.parameters import FloatParameter, HSVParameter
class Twinkle(RawPreset):
"""Random pixels fade in and out"""
_fading_up = []
_fading_down = []
_time = {}
_fader = None
def setup(self):
random.seed()
self.add_parameter(FloatParameter('birth-rate', 0.15))
self.add_parameter(FloatParameter('fade-up-time', 0.25))
self.add_parameter(FloatParameter('fade-down-time', 2.5))
self.add_parameter(HSVParameter('on-color', (0.15, 1.0, 1.0)))
self.add_parameter(HSVParameter('off-color', (0.0, 0.0, 0.0)))
self._setup_colors()
def parameter_changed(self, parameter):
if str(parameter) == 'on-color':
self._setup_colors()
def _setup_colors(self):
self._up_target_rgb = float_to_uint8(colorsys.hsv_to_rgb(*self.parameter('on-color').get()))
self._down_target_rgb = float_to_uint8(colorsys.hsv_to_rgb(*self.parameter('off-color').get()))
self._fader = ColorFade('hsv', [self.parameter('off-color').get(), self.parameter('on-color').get()])
def reset(self):
self._fading_up = []
self._fading_down = []
self._time = {}
def draw(self, dt):
# Birth
if random.random() > (1.0 - self.parameter('birth-rate').get()):
address = ( random.randint(0, self._max_strand - 1),
random.randint(0, self._max_fixture - 1),
random.randint(0, self._max_pixel - 1))
if address not in self._fading_up:
self._fading_up.append(address)
self._time[address] = dt
# Growth
for address in self._fading_up:
color = self._get_next_color(address, dt)
if color == self._up_target_rgb:
self._fading_up.remove(address)
self._fading_down.append(address)
self._time[address] = dt
self.setp(address, color)
# Decay
for address in self._fading_down:
color = self._get_next_color(address, dt, down=True)
if color == self._down_target_rgb:
self._fading_down.remove(address)
self.setp(address, color)
def _get_next_color(self, address, dt, down=False):
time_target = float(self.parameter('fade-up-time').get()) if down else float(self.parameter('fade-down-time').get())
progress = (dt - self._time[address]) / time_target
if progress > 1.0:
progress = 1.0
elif dt == self._time[address]:
progress = 0.0
if down:
progress = 1.0 - progress
return self._fader.get_color(progress)