simplify and clean up pixelmap, add ability to use pixel numbers without a list wrapping them

This commit is contained in:
Roy Hooper 2020-06-07 13:09:11 -04:00
parent baa8095da7
commit 968a7beed4
3 changed files with 64 additions and 101 deletions

View file

@ -74,9 +74,6 @@ class Pulse(Animation):
""" """
Resets the animation. Resets the animation.
""" """
white = len(self.pixel_object[0]) > 3 and isinstance(
self.pixel_object[0][-1], int
)
dotstar = len(self.pixel_object[0]) == 4 and isinstance( dotstar = len(self.pixel_object[0]) == 4 and isinstance(
self.pixel_object[0][-1], float self.pixel_object[0][-1], float
) )
@ -84,6 +81,4 @@ class Pulse(Animation):
pulse_generator, pulse_generator,
) )
self._generator = pulse_generator( self._generator = pulse_generator(self._period, self, dotstar_pwm=dotstar)
self._period, self, white, dotstar_pwm=dotstar
)

View file

@ -74,14 +74,11 @@ class SparklePulse(Sparkle):
self._max_intensity = max_intensity self._max_intensity = max_intensity
self._min_intensity = min_intensity self._min_intensity = min_intensity
self._period = period self._period = period
white = len(pixel_object) == 4 and isinstance(pixel_object[0][-1], int)
dotstar = len(pixel_object) == 4 and isinstance(pixel_object[0][-1], float) dotstar = len(pixel_object) == 4 and isinstance(pixel_object[0][-1], float)
super().__init__( super().__init__(
pixel_object, speed=speed, color=color, num_sparkles=1, name=name pixel_object, speed=speed, color=color, num_sparkles=1, name=name
) )
self._generator = pulse_generator( self._generator = pulse_generator(self._period, self, dotstar_pwm=dotstar)
self._period, self, white, dotstar_pwm=dotstar
)
def draw(self): def draw(self):
self._sparkle_color = next(self._generator) self._sparkle_color = next(self._generator)

View file

@ -45,6 +45,7 @@ Implementation Notes
import math import math
from . import NANOS_PER_SECOND, monotonic_ns from . import NANOS_PER_SECOND, monotonic_ns
from .color import calculate_intensity
class PixelMap: class PixelMap:
@ -69,7 +70,7 @@ class PixelMap:
pixel_wing_horizontal[0] = (255, 255, 0) pixel_wing_horizontal[0] = (255, 255, 0)
pixel_wing_horizontal.show() pixel_wing_horizontal.show()
To use with individual pixels: To use with groups of individual pixels:
.. code-block:: python .. code-block:: python
@ -92,24 +93,59 @@ class PixelMap:
pixel_wing_vertical[0] = (255, 255, 0) pixel_wing_vertical[0] = (255, 255, 0)
pixel_wing_vertical.show() pixel_wing_vertical.show()
To use with individual pixels:
.. code-block:: python
import board
import neopixel
import time
from adafruit_led_animation.helper import PixelMap
pixels = neopixel.NeoPixel(board.D6, 8, auto_write=False)
pixel_map = PixelMap(pixels, [
0, 7, 1, 6, 2, 5, 3, 4
], individual_pixels=True)
n = 0
while True:
pixel_map[n] = AMBER
pixel_map.show()
n = n + 1
if n > 7:
n = 0
pixel_map.fill(0)
time.sleep(0.25)
""" """
def __init__(self, strip, pixel_ranges, individual_pixels=False): def __init__(self, strip, pixel_ranges, individual_pixels=False):
self._pixels = strip self._pixels = strip
self._ranges = pixel_ranges self._ranges = pixel_ranges
self.n = len(self._ranges) self.n = len(self._ranges)
if self.n == 0:
raise (ValueError("A PixelMap must have at least one pixel defined"))
self._individual_pixels = individual_pixels self._individual_pixels = individual_pixels
self._expand_ranges()
def _expand_ranges(self):
if not self._individual_pixels:
self._ranges = [
[n for n in range(start, end)] for start, end in self._ranges
]
return
if isinstance(self._ranges[0], int):
self._ranges = [[n] for n in self._ranges]
def __repr__(self): def __repr__(self):
return "[" + ", ".join([str(x) for x in self]) + "]" return "[" + ", ".join([str(self[x]) for x in range(self.n)]) + "]"
def _set_pixels(self, index, val): def _set_pixels(self, index, val):
if self._individual_pixels: for pixel in self._ranges[index]:
for pixel in self._ranges[index]: self._pixels[pixel] = val
self._pixels[pixel] = val
else:
range_start, range_stop = self._ranges[index]
self._pixels[range_start:range_stop] = [val] * (range_stop - range_start)
def __setitem__(self, index, val): def __setitem__(self, index, val):
if isinstance(index, slice): if isinstance(index, slice):
@ -124,7 +160,7 @@ class PixelMap:
else: else:
self._set_pixels(index, val) self._set_pixels(index, val)
if not self._pixels.auto_write: if self._pixels.auto_write:
self.show() self.show()
def __getitem__(self, index): def __getitem__(self, index):
@ -160,13 +196,9 @@ class PixelMap:
:param color: Color to fill all pixels referenced by this PixelMap definition with. :param color: Color to fill all pixels referenced by this PixelMap definition with.
""" """
if self._individual_pixels: for pixels in self._ranges:
for pixels in self._ranges: for pixel in pixels:
for pixel in pixels: self._pixels[pixel] = color
self._pixels[pixel] = color
else:
for start, stop in self._ranges:
self._pixels[start:stop] = [color] * (stop - start)
def show(self): def show(self):
""" """
@ -273,7 +305,7 @@ def horizontal_strip_gridmap(width, alternating=True):
return mapper return mapper
class PixelSubset: class PixelSubset(PixelMap):
""" """
PixelSubset lets you work with a subset of a pixel object. PixelSubset lets you work with a subset of a pixel object.
@ -295,78 +327,18 @@ class PixelSubset:
""" """
def __init__(self, pixel_object, start, end): def __init__(self, pixel_object, start, end):
self._pixels = pixel_object super().__init__(
self._start = start pixel_object,
self._end = end pixel_ranges=[[n] for n in range(start, end)],
self.n = self._end - self._start individual_pixels=True,
)
def __repr__(self):
return "[" + ", ".join([str(x) for x in self]) + "]"
def __setitem__(self, index, val):
if isinstance(index, slice):
start, stop, step = index.indices(self.n)
self._pixels[start + self._start : stop + self._start : step] = val
else:
self._pixels[index + self._start] = val
if not self._pixels.auto_write:
self.show()
def __getitem__(self, index):
if isinstance(index, slice):
start, stop, step = index.indices(self.n)
return self._pixels[start + self._start : stop + self._start : step]
if index < 0:
index += len(self)
if index >= self.n or index < 0:
raise IndexError
return self._pixels[index]
def __len__(self):
return self.n
@property
def brightness(self):
"""
brightness from the underlying strip.
"""
return self._pixels.brightness
@brightness.setter
def brightness(self, brightness):
self._pixels.brightness = min(max(brightness, 0.0), 1.0)
def fill(self, color):
"""
Fill the used pixel ranges with color.
"""
self._pixels[self._start : self._end] = [color] * (self.n)
def show(self):
"""
Shows the pixels on the underlying strip.
"""
self._pixels.show()
@property
def auto_write(self):
"""
auto_write from the underlying strip.
"""
return self._pixels.auto_write
@auto_write.setter
def auto_write(self, value):
self._pixels.auto_write = value
def pulse_generator(period: float, animation_object, white=False, dotstar_pwm=False): def pulse_generator(period: float, animation_object, dotstar_pwm=False):
""" """
Generates a sequence of colors for a pulse, based on the time period specified. Generates a sequence of colors for a pulse, based on the time period specified.
:param period: Pulse duration in seconds. :param period: Pulse duration in seconds.
:param animation_object: An animation object to interact with. :param animation_object: An animation object to interact with.
:param white: Whether the pixel strip has a white pixel.
:param dotstar_pwm: Whether to use the dostar per pixel PWM value for brightness control. :param dotstar_pwm: Whether to use the dostar per pixel PWM value for brightness control.
""" """
period = int(period * NANOS_PER_SECOND) period = int(period * NANOS_PER_SECOND)
@ -376,7 +348,6 @@ def pulse_generator(period: float, animation_object, white=False, dotstar_pwm=Fa
cycle_position = 0 cycle_position = 0
last_pos = 0 last_pos = 0
while True: while True:
fill_color = list(animation_object.color)
now = monotonic_ns() now = monotonic_ns()
time_since_last_draw = now - last_update time_since_last_draw = now - last_update
last_update = now last_update = now
@ -388,12 +359,12 @@ def pulse_generator(period: float, animation_object, white=False, dotstar_pwm=Fa
pos = period - pos pos = period - pos
intensity = pos / half_period intensity = pos / half_period
if dotstar_pwm: if dotstar_pwm:
fill_color = (fill_color[0], fill_color[1], fill_color[2], intensity) fill_color = (
animation_object.color[0],
animation_object.color[1],
animation_object.color[2],
intensity,
)
yield fill_color yield fill_color
continue continue
if white and len(fill_color) == 4: yield calculate_intensity(animation_object.color, intensity)
fill_color[3] = int(fill_color[3] * intensity)
fill_color[0] = int(fill_color[0] * intensity)
fill_color[1] = int(fill_color[1] * intensity)
fill_color[2] = int(fill_color[2] * intensity)
yield fill_color