diff --git a/adafruit_led_animation/animation.py b/adafruit_led_animation/animation.py deleted file mode 100644 index 51a7e9d..0000000 --- a/adafruit_led_animation/animation.py +++ /dev/null @@ -1,504 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2019-2020 Roy Hooper -# Copyright (c) 2020 Kattni Rembor for Adafruit Industries -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -""" -`adafruit_led_animation.animation` -================================================================================ - -Animation base class, and basic animations for CircuitPython helper library for LED animations. - -* Author(s): Roy Hooper, Kattni Rembor - -Implementation Notes --------------------- - -**Hardware:** - -* `Adafruit NeoPixels `_ -* `Adafruit DotStars `_ - -**Software and Dependencies:** - -* Adafruit CircuitPython firmware for the supported boards: - https://circuitpython.org/downloads - -""" - -from math import ceil -from . import NANOS_PER_SECOND, monotonic_ns -from .color import BLACK, RAINBOW - -__version__ = "0.0.0-auto.0" -__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_LED_Animation.git" - - -class Animation: - # pylint: disable=too-many-instance-attributes - """ - Base class for animations. - """ - cycle_complete_supported = False - - # pylint: disable=too-many-arguments - def __init__(self, pixel_object, speed, color, peers=None, paused=False, name=None): - self.pixel_object = pixel_object - self.pixel_object.auto_write = False - self.peers = peers if peers else [] - """A sequence of animations to trigger .draw() on when this animation draws.""" - self._speed_ns = 0 - self._color = None - self._paused = paused - self._next_update = monotonic_ns() - self._time_left_at_pause = 0 - self._also_notify = [] - self.speed = speed # sets _speed_ns - self.color = color # Triggers _recompute_color - self.name = name - self.notify_cycles = 1 - """Number of cycles to trigger additional cycle_done notifications after""" - self.draw_count = 0 - """Number of animation frames drawn.""" - self.cycle_count = 0 - """Number of animation cycles completed.""" - - def __str__(self): - return "<%s: %s>" % (self.__class__.__name__, self.name) - - def animate(self): - """ - Call animate() from your code's main loop. It will draw the animation draw() at intervals - configured by the speed property (set from init). - - :return: True if the animation draw cycle was triggered, otherwise False. - """ - if self._paused: - return False - - now = monotonic_ns() - if now < self._next_update: - return False - - self.draw() - self.draw_count += 1 - - # Draw related animations together - if self.peers: - for peer in self.peers: - peer.draw() - - self._next_update = now + self._speed_ns - return True - - def draw(self): - """ - Animation subclasses must implement draw() to render the animation sequence. - Draw must call show(). - """ - raise NotImplementedError() - - def show(self): - """ - Displays the updated pixels. Called during animates with changes. - """ - self.pixel_object.show() - - def freeze(self): - """ - Stops the animation until resumed. - """ - self._paused = True - self._time_left_at_pause = max(0, monotonic_ns() - self._next_update) - - def resume(self): - """ - Resumes the animation. - """ - self._next_update = monotonic_ns() + self._time_left_at_pause - self._time_left_at_pause = 0 - self._paused = False - - def fill(self, color): - """ - Fills the pixel object with a color. - """ - self.pixel_object.fill(color) - - @property - def color(self): - """ - The current color. - """ - return self._color - - @color.setter - def color(self, color): - if self._color == color: - return - if isinstance(color, int): - color = (color >> 16 & 0xFF, color >> 8 & 0xFF, color & 0xFF) - self._color = color - self._recompute_color(color) - - @property - def speed(self): - """ - The animation speed in fractional seconds. - """ - return self._speed_ns / NANOS_PER_SECOND - - @speed.setter - def speed(self, seconds): - self._speed_ns = int(seconds * NANOS_PER_SECOND) - - def _recompute_color(self, color): - """ - Called if the color is changed, which includes at initialization. - Override as needed. - """ - - def cycle_complete(self): - """ - Called by some animations when they complete an animation cycle. - Animations that support cycle complete notifications will have X property set to False. - Override as needed. - """ - self.cycle_count += 1 - if self.cycle_count % self.notify_cycles == 0: - for callback in self._also_notify: - callback(self) - - def add_cycle_complete_receiver(self, callback): - """ - Adds an additional callback when the cycle completes. - - :param callback: Additional callback to trigger when a cycle completes. The callback - is passed the animation object instance. - """ - self._also_notify.append(callback) - - def reset(self): - """ - Resets the animation sequence. - """ - - -class ColorCycle(Animation): - """ - Animate a sequence of one or more colors, cycling at the specified speed. - - :param pixel_object: The initialised LED object. - :param float speed: Animation speed in seconds, e.g. ``0.1``. - :param colors: A list of colors to cycle through in ``(r, g, b)`` tuple, or ``0x000000`` hex - format. Defaults to a rainbow color cycle. - """ - - def __init__(self, pixel_object, speed, colors=RAINBOW, name=None): - self.colors = colors - super().__init__(pixel_object, speed, colors[0], name=name) - self._generator = self._color_generator() - next(self._generator) - - cycle_complete_supported = True - - def draw(self): - self.pixel_object.fill(self.color) - self.show() - next(self._generator) - - def _color_generator(self): - index = 0 - while True: - self._color = self.colors[index] - yield - index = (index + 1) % len(self.colors) - if index == 0: - self.cycle_complete() - - def reset(self): - """ - Resets to the first color. - """ - self._generator = self._color_generator() - - -class Blink(ColorCycle): - """ - Blink a color on and off. - - :param pixel_object: The initialised LED object. - :param float speed: Animation speed in seconds, e.g. ``0.1``. - :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. - """ - - def __init__(self, pixel_object, speed, color, name=None): - super().__init__(pixel_object, speed, [color, BLACK], name=name) - - def _recompute_color(self, color): - self.colors = [color, BLACK] - - -class Solid(ColorCycle): - """ - A solid color. - - :param pixel_object: The initialised LED object. - :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. - """ - - def __init__(self, pixel_object, color, name=None): - super().__init__(pixel_object, speed=1, colors=[color], name=name) - - def _recompute_color(self, color): - self.colors = [color] - - -class Comet(Animation): - """ - A comet animation. - - :param pixel_object: The initialised LED object. - :param float speed: Animation speed in seconds, e.g. ``0.1``. - :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. - :param int tail_length: The length of the comet. Defaults to 10. Cannot exceed the number of - pixels present in the pixel object, e.g. if the strip is 30 pixels - long, the ``tail_length`` cannot exceed 30 pixels. - :param bool reverse: Animates the comet in the reverse order. Defaults to ``False``. - :param bool bounce: Comet will bounce back and forth. Defaults to ``True``. - """ - - # pylint: disable=too-many-arguments - def __init__( - self, - pixel_object, - speed, - color, - tail_length=10, - reverse=False, - bounce=False, - name=None, - ): - self._tail_length = tail_length + 1 - self._color_step = 0.9 / tail_length - self._color_offset = 0.1 - self._comet_colors = None - self._reverse_comet_colors = None - self._initial_reverse = reverse - self.reverse = reverse - self.bounce = bounce - self._computed_color = color - self._generator = self._comet_generator() - super().__init__(pixel_object, speed, color, name=name) - - cycle_complete_supported = True - - def _recompute_color(self, color): - pass - - def __recompute_color(self, color): - self._comet_colors = [BLACK] + [ - [ - int(color[rgb] * ((n * self._color_step) + self._color_offset)) - for rgb in range(len(color)) - ] - for n in range(self._tail_length - 1) - ] - self._reverse_comet_colors = list(reversed(self._comet_colors)) - self._computed_color = color - - def _get_range(self, num_pixels): - if self.reverse: - return range(num_pixels, -self._tail_length - 1, -1) - return range(-self._tail_length, num_pixels + 1) - - def _comet_generator(self): - num_pixels = len(self.pixel_object) - cycle_passes = 0 - while True: - if self._color != self._computed_color or not self._comet_colors: - self.__recompute_color(self._color) - colors = self._reverse_comet_colors if self.reverse else self._comet_colors - for start in self._get_range(num_pixels): - - if start + self._tail_length < num_pixels: - end = self._tail_length - else: - end = num_pixels - start - if start <= 0: - num_visible = self._tail_length + start - self.pixel_object[0:num_visible] = colors[ - self._tail_length - num_visible : - ] - else: - self.pixel_object[start : start + end] = colors[0:end] - self.show() - yield - cycle_passes += 1 - if self.bounce: - self.reverse = not self.reverse - if not self.bounce or cycle_passes == 2: - self.cycle_complete() - cycle_passes = 0 - - def draw(self): - next(self._generator) - - def reset(self): - """ - Resets to the first color. - """ - self._generator = self._comet_generator() - self.reverse = self._initial_reverse - - -class Pulse(Animation): - """ - Pulse all pixels a single color. - - :param pixel_object: The initialised LED object. - :param float speed: Animation refresh rate in seconds, e.g. ``0.1``. - :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. - :param period: Period to pulse the LEDs over. Default 5. - """ - - # pylint: disable=too-many-arguments - def __init__(self, pixel_object, speed, color, period=5, name=None): - super().__init__(pixel_object, speed, color, name=name) - self._period = period - self._generator = None - self.reset() - - cycle_complete_supported = True - - def draw(self): - color = next(self._generator) - self.fill(color) - self.show() - - def reset(self): - """ - Resets the animation. - """ - white = len(self.pixel_object[0]) > 3 and isinstance( - self.pixel_object[0][-1], int - ) - from adafruit_led_animation.helper import ( # pylint: disable=import-outside-toplevel - pulse_generator, - ) - - self._generator = pulse_generator(self._period, self, white) - - -class Chase(Animation): - """ - Chase pixels in one direction in a single color, like a theater marquee sign. - - :param pixel_object: The initialised LED object. - :param float speed: Animation speed rate in seconds, e.g. ``0.1``. - :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. - :param size: Number of pixels to turn on in a row. - :param spacing: Number of pixels to turn off in a row. - :param reverse: Reverse direction of movement. - """ - - # pylint: disable=too-many-arguments - def __init__( - self, pixel_object, speed, color, size=2, spacing=3, reverse=False, name=None - ): - self._size = size - self._spacing = spacing - self._repeat_width = size + spacing - self._num_repeats = ceil(len(pixel_object) / self._repeat_width) - self._overflow = len(pixel_object) % self._repeat_width - self._direction = 1 if not reverse else -1 - self._reverse = reverse - self._offset = 0 - - def _resetter(): - self._offset = 0 - self._reverse = reverse - self._direction = 1 if not reverse else -1 - - self._reset = _resetter - - super().__init__(pixel_object, speed, color, name=name) - - cycle_complete_supported = True - - @property - def reverse(self): - """ - Whether the animation is reversed - """ - return self._reverse - - @reverse.setter - def reverse(self, value): - self._reverse = value - self._direction = -1 if self._reverse else 1 - - def draw(self): - def bar_colors(): - bar_no = 0 - for i in range(self._offset, 0, -1): - if i > self._spacing: - yield self.bar_color(bar_no, i) - else: - yield self.space_color(bar_no, i) - bar_no = 1 - while True: - for bar_pixel in range(self._size): - yield self.bar_color(bar_no, bar_pixel) - for space_pixel in range(self._spacing): - yield self.space_color(bar_no, space_pixel) - bar_no += 1 - - colorgen = bar_colors() - self.pixel_object[:] = [next(colorgen) for _ in self.pixel_object] - self.show() - - if self.draw_count % len(self.pixel_object) == 0: - self.cycle_complete() - self._offset = (self._offset + self._direction) % self._repeat_width - - def bar_color(self, n, pixel_no=0): # pylint: disable=unused-argument - """ - Generate the color for the n'th bar_color in the Chase - - :param n: The pixel group to get the color for - :param pixel_no: Which pixel in the group to get the color for - """ - return self.color - - def space_color(self, n, pixel_no=0): # pylint: disable=unused-argument,no-self-use - """ - Generate the spacing color for the n'th bar_color in the Chase - - :param n: The pixel group to get the spacing color for - :param pixel_no: Which pixel in the group to get the spacing color for - """ - return 0 - - def reset(self): - """ - Reset the animation. - """ - self._reset() diff --git a/adafruit_led_animation/animation/__init__.py b/adafruit_led_animation/animation/__init__.py new file mode 100644 index 0000000..b54f76c --- /dev/null +++ b/adafruit_led_animation/animation/__init__.py @@ -0,0 +1,201 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019-2020 Roy Hooper +# Copyright (c) 2020 Kattni Rembor for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`adafruit_led_animation.animation` +================================================================================ + +Animation base class, and basic animations for CircuitPython helper library for LED animations. + +* Author(s): Roy Hooper, Kattni Rembor + +Implementation Notes +-------------------- + +**Hardware:** + +* `Adafruit NeoPixels `_ +* `Adafruit DotStars `_ + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://circuitpython.org/downloads + +""" + +__version__ = "0.0.0-auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_LED_Animation.git" + +from time import monotonic_ns + +from adafruit_led_animation import NANOS_PER_SECOND + + +class Animation: + # pylint: disable=too-many-instance-attributes + """ + Base class for animations. + """ + cycle_complete_supported = False + + # pylint: disable=too-many-arguments + def __init__(self, pixel_object, speed, color, peers=None, paused=False, name=None): + self.pixel_object = pixel_object + self.pixel_object.auto_write = False + self.peers = peers if peers else [] + """A sequence of animations to trigger .draw() on when this animation draws.""" + self._speed_ns = 0 + self._color = None + self._paused = paused + self._next_update = monotonic_ns() + self._time_left_at_pause = 0 + self._also_notify = [] + self.speed = speed # sets _speed_ns + self.color = color # Triggers _recompute_color + self.name = name + self.notify_cycles = 1 + """Number of cycles to trigger additional cycle_done notifications after""" + self.draw_count = 0 + """Number of animation frames drawn.""" + self.cycle_count = 0 + """Number of animation cycles completed.""" + + def __str__(self): + return "<%s: %s>" % (self.__class__.__name__, self.name) + + def animate(self): + """ + Call animate() from your code's main loop. It will draw the animation draw() at intervals + configured by the speed property (set from init). + + :return: True if the animation draw cycle was triggered, otherwise False. + """ + if self._paused: + return False + + now = monotonic_ns() + if now < self._next_update: + return False + + self.draw() + self.draw_count += 1 + + # Draw related animations together + if self.peers: + for peer in self.peers: + peer.draw() + + self._next_update = now + self._speed_ns + return True + + def draw(self): + """ + Animation subclasses must implement draw() to render the animation sequence. + Draw must call show(). + """ + raise NotImplementedError() + + def show(self): + """ + Displays the updated pixels. Called during animates with changes. + """ + self.pixel_object.show() + + def freeze(self): + """ + Stops the animation until resumed. + """ + self._paused = True + self._time_left_at_pause = max(0, monotonic_ns() - self._next_update) + + def resume(self): + """ + Resumes the animation. + """ + self._next_update = monotonic_ns() + self._time_left_at_pause + self._time_left_at_pause = 0 + self._paused = False + + def fill(self, color): + """ + Fills the pixel object with a color. + """ + self.pixel_object.fill(color) + + @property + def color(self): + """ + The current color. + """ + return self._color + + @color.setter + def color(self, color): + if self._color == color: + return + if isinstance(color, int): + color = (color >> 16 & 0xFF, color >> 8 & 0xFF, color & 0xFF) + self._color = color + self._recompute_color(color) + + @property + def speed(self): + """ + The animation speed in fractional seconds. + """ + return self._speed_ns / NANOS_PER_SECOND + + @speed.setter + def speed(self, seconds): + self._speed_ns = int(seconds * NANOS_PER_SECOND) + + def _recompute_color(self, color): + """ + Called if the color is changed, which includes at initialization. + Override as needed. + """ + + def cycle_complete(self): + """ + Called by some animations when they complete an animation cycle. + Animations that support cycle complete notifications will have X property set to False. + Override as needed. + """ + self.cycle_count += 1 + if self.cycle_count % self.notify_cycles == 0: + for callback in self._also_notify: + callback(self) + + def add_cycle_complete_receiver(self, callback): + """ + Adds an additional callback when the cycle completes. + + :param callback: Additional callback to trigger when a cycle completes. The callback + is passed the animation object instance. + """ + self._also_notify.append(callback) + + def reset(self): + """ + Resets the animation sequence. + """ diff --git a/adafruit_led_animation/animation/blink.py b/adafruit_led_animation/animation/blink.py new file mode 100644 index 0000000..0a3257f --- /dev/null +++ b/adafruit_led_animation/animation/blink.py @@ -0,0 +1,50 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019-2020 Roy Hooper +# Copyright (c) 2020 Kattni Rembor for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`adafruit_led_animation.animation.blink` +================================================================================ + +TODO + +* Author(s): Roy Hooper, Kattni Rembor + +""" + +from adafruit_led_animation.animation.colorcycle import ColorCycle +from adafruit_led_animation.color import BLACK + + +class Blink(ColorCycle): + """ + Blink a color on and off. + + :param pixel_object: The initialised LED object. + :param float speed: Animation speed in seconds, e.g. ``0.1``. + :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. + """ + + def __init__(self, pixel_object, speed, color, name=None): + super().__init__(pixel_object, speed, [color, BLACK], name=name) + + def _recompute_color(self, color): + self.colors = [color, BLACK] diff --git a/adafruit_led_animation/animation/chase.py b/adafruit_led_animation/animation/chase.py new file mode 100644 index 0000000..f689d28 --- /dev/null +++ b/adafruit_led_animation/animation/chase.py @@ -0,0 +1,132 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019-2020 Roy Hooper +# Copyright (c) 2020 Kattni Rembor for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`adafruit_led_animation.animation.chase` +================================================================================ + +TODO + +* Author(s): Roy Hooper, Kattni Rembor + +""" + +from math import ceil + +from adafruit_led_animation.animation import Animation + + +class Chase(Animation): + """ + Chase pixels in one direction in a single color, like a theater marquee sign. + + :param pixel_object: The initialised LED object. + :param float speed: Animation speed rate in seconds, e.g. ``0.1``. + :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. + :param size: Number of pixels to turn on in a row. + :param spacing: Number of pixels to turn off in a row. + :param reverse: Reverse direction of movement. + """ + + # pylint: disable=too-many-arguments + def __init__( + self, pixel_object, speed, color, size=2, spacing=3, reverse=False, name=None + ): + self._size = size + self._spacing = spacing + self._repeat_width = size + spacing + self._num_repeats = ceil(len(pixel_object) / self._repeat_width) + self._overflow = len(pixel_object) % self._repeat_width + self._direction = 1 if not reverse else -1 + self._reverse = reverse + self._offset = 0 + + def _resetter(): + self._offset = 0 + self._reverse = reverse + self._direction = 1 if not reverse else -1 + + self._reset = _resetter + + super().__init__(pixel_object, speed, color, name=name) + + cycle_complete_supported = True + + @property + def reverse(self): + """ + Whether the animation is reversed + """ + return self._reverse + + @reverse.setter + def reverse(self, value): + self._reverse = value + self._direction = -1 if self._reverse else 1 + + def draw(self): + def bar_colors(): + bar_no = 0 + for i in range(self._offset, 0, -1): + if i > self._spacing: + yield self.bar_color(bar_no, i) + else: + yield self.space_color(bar_no, i) + bar_no = 1 + while True: + for bar_pixel in range(self._size): + yield self.bar_color(bar_no, bar_pixel) + for space_pixel in range(self._spacing): + yield self.space_color(bar_no, space_pixel) + bar_no += 1 + + colorgen = bar_colors() + self.pixel_object[:] = [next(colorgen) for _ in self.pixel_object] + self.show() + + if self.draw_count % len(self.pixel_object) == 0: + self.cycle_complete() + self._offset = (self._offset + self._direction) % self._repeat_width + + def bar_color(self, n, pixel_no=0): # pylint: disable=unused-argument + """ + Generate the color for the n'th bar_color in the Chase + + :param n: The pixel group to get the color for + :param pixel_no: Which pixel in the group to get the color for + """ + return self.color + + def space_color(self, n, pixel_no=0): # pylint: disable=unused-argument,no-self-use + """ + Generate the spacing color for the n'th bar_color in the Chase + + :param n: The pixel group to get the spacing color for + :param pixel_no: Which pixel in the group to get the spacing color for + """ + return 0 + + def reset(self): + """ + Reset the animation. + """ + self._reset() diff --git a/adafruit_led_animation/animation/colorcycle.py b/adafruit_led_animation/animation/colorcycle.py new file mode 100644 index 0000000..785192d --- /dev/null +++ b/adafruit_led_animation/animation/colorcycle.py @@ -0,0 +1,73 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019-2020 Roy Hooper +# Copyright (c) 2020 Kattni Rembor for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`adafruit_led_animation.animation.colorcycle` +================================================================================ + +TODO + +* Author(s): Roy Hooper, Kattni Rembor + +""" + +from adafruit_led_animation.animation import Animation +from adafruit_led_animation.color import RAINBOW + + +class ColorCycle(Animation): + """ + Animate a sequence of one or more colors, cycling at the specified speed. + + :param pixel_object: The initialised LED object. + :param float speed: Animation speed in seconds, e.g. ``0.1``. + :param colors: A list of colors to cycle through in ``(r, g, b)`` tuple, or ``0x000000`` hex + format. Defaults to a rainbow color cycle. + """ + + def __init__(self, pixel_object, speed, colors=RAINBOW, name=None): + self.colors = colors + super().__init__(pixel_object, speed, colors[0], name=name) + self._generator = self._color_generator() + next(self._generator) + + cycle_complete_supported = True + + def draw(self): + self.pixel_object.fill(self.color) + self.show() + next(self._generator) + + def _color_generator(self): + index = 0 + while True: + self._color = self.colors[index] + yield + index = (index + 1) % len(self.colors) + if index == 0: + self.cycle_complete() + + def reset(self): + """ + Resets to the first color. + """ + self._generator = self._color_generator() diff --git a/adafruit_led_animation/animation/comet.py b/adafruit_led_animation/animation/comet.py new file mode 100644 index 0000000..f558917 --- /dev/null +++ b/adafruit_led_animation/animation/comet.py @@ -0,0 +1,132 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019-2020 Roy Hooper +# Copyright (c) 2020 Kattni Rembor for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`adafruit_led_animation.animation.comet` +================================================================================ + +TODO + +* Author(s): Roy Hooper, Kattni Rembor + +""" + +from adafruit_led_animation.animation import Animation +from adafruit_led_animation.color import BLACK + + +class Comet(Animation): + """ + A comet animation. + + :param pixel_object: The initialised LED object. + :param float speed: Animation speed in seconds, e.g. ``0.1``. + :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. + :param int tail_length: The length of the comet. Defaults to 10. Cannot exceed the number of + pixels present in the pixel object, e.g. if the strip is 30 pixels + long, the ``tail_length`` cannot exceed 30 pixels. + :param bool reverse: Animates the comet in the reverse order. Defaults to ``False``. + :param bool bounce: Comet will bounce back and forth. Defaults to ``True``. + """ + + # pylint: disable=too-many-arguments + def __init__( + self, + pixel_object, + speed, + color, + tail_length=10, + reverse=False, + bounce=False, + name=None, + ): + self._tail_length = tail_length + 1 + self._color_step = 0.9 / tail_length + self._color_offset = 0.1 + self._comet_colors = None + self._reverse_comet_colors = None + self._initial_reverse = reverse + self.reverse = reverse + self.bounce = bounce + self._computed_color = color + self._generator = self._comet_generator() + super().__init__(pixel_object, speed, color, name=name) + + cycle_complete_supported = True + + def _recompute_color(self, color): + pass + + def __recompute_color(self, color): + self._comet_colors = [BLACK] + [ + [ + int(color[rgb] * ((n * self._color_step) + self._color_offset)) + for rgb in range(len(color)) + ] + for n in range(self._tail_length - 1) + ] + self._reverse_comet_colors = list(reversed(self._comet_colors)) + self._computed_color = color + + def _get_range(self, num_pixels): + if self.reverse: + return range(num_pixels, -self._tail_length - 1, -1) + return range(-self._tail_length, num_pixels + 1) + + def _comet_generator(self): + num_pixels = len(self.pixel_object) + cycle_passes = 0 + while True: + if self._color != self._computed_color or not self._comet_colors: + self.__recompute_color(self._color) + colors = self._reverse_comet_colors if self.reverse else self._comet_colors + for start in self._get_range(num_pixels): + + if start + self._tail_length < num_pixels: + end = self._tail_length + else: + end = num_pixels - start + if start <= 0: + num_visible = self._tail_length + start + self.pixel_object[0:num_visible] = colors[ + self._tail_length - num_visible : + ] + else: + self.pixel_object[start : start + end] = colors[0:end] + self.show() + yield + cycle_passes += 1 + if self.bounce: + self.reverse = not self.reverse + if not self.bounce or cycle_passes == 2: + self.cycle_complete() + cycle_passes = 0 + + def draw(self): + next(self._generator) + + def reset(self): + """ + Resets to the first color. + """ + self._generator = self._comet_generator() + self.reverse = self._initial_reverse diff --git a/adafruit_led_animation/animation/pulse.py b/adafruit_led_animation/animation/pulse.py new file mode 100644 index 0000000..a3bcc98 --- /dev/null +++ b/adafruit_led_animation/animation/pulse.py @@ -0,0 +1,71 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019-2020 Roy Hooper +# Copyright (c) 2020 Kattni Rembor for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`adafruit_led_animation.animation.pulse` +================================================================================ + +TODO + +* Author(s): Roy Hooper, Kattni Rembor + +""" + +from adafruit_led_animation.animation import Animation + + +class Pulse(Animation): + """ + Pulse all pixels a single color. + + :param pixel_object: The initialised LED object. + :param float speed: Animation refresh rate in seconds, e.g. ``0.1``. + :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. + :param period: Period to pulse the LEDs over. Default 5. + """ + + # pylint: disable=too-many-arguments + def __init__(self, pixel_object, speed, color, period=5, name=None): + super().__init__(pixel_object, speed, color, name=name) + self._period = period + self._generator = None + self.reset() + + cycle_complete_supported = True + + def draw(self): + color = next(self._generator) + self.fill(color) + self.show() + + def reset(self): + """ + Resets the animation. + """ + white = len(self.pixel_object[0]) > 3 and isinstance( + self.pixel_object[0][-1], int + ) + from adafruit_led_animation.helper import ( # pylint: disable=import-outside-toplevel + pulse_generator, + ) + + self._generator = pulse_generator(self._period, self, white) diff --git a/adafruit_led_animation/rainbow.py b/adafruit_led_animation/animation/rainbow.py similarity index 50% rename from adafruit_led_animation/rainbow.py rename to adafruit_led_animation/animation/rainbow.py index 78500b1..76cf021 100644 --- a/adafruit_led_animation/rainbow.py +++ b/adafruit_led_animation/animation/rainbow.py @@ -21,10 +21,11 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. """ -`adafruit_led_animation.rainbow` +`adafruit_led_animation.animation.rainbow` ================================================================================ Rainbow animations for CircuitPython helper library for LED animations. +TODO * Author(s): Roy Hooper, Kattni Rembor @@ -43,9 +44,9 @@ Implementation Notes """ -from adafruit_led_animation.animation import Animation, Chase, Comet +from adafruit_led_animation.animation import Animation from adafruit_led_animation.color import BLACK, colorwheel -from . import NANOS_PER_SECOND, monotonic_ns +from adafruit_led_animation import NANOS_PER_SECOND, monotonic_ns __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_LED_Animation.git" @@ -101,93 +102,3 @@ class Rainbow(Animation): Resets the animation. """ self._generator = self._color_wheel_generator() - - -class RainbowChase(Chase): - """ - Chase pixels in one direction, like a theater marquee but with rainbows! - - :param pixel_object: The initialised LED object. - :param float speed: Animation speed rate in seconds, e.g. ``0.1``. - :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. - :param size: Number of pixels to turn on in a row. - :param spacing: Number of pixels to turn off in a row. - :param reverse: Reverse direction of movement. - :param wheel_step: How many colors to skip in `colorwheel` per bar (default 8) - """ - - # pylint: disable=too-many-arguments - def __init__( - self, - pixel_object, - speed, - size=2, - spacing=3, - reverse=False, - name=None, - wheel_step=8, - ): - self._num_colors = 256 // wheel_step - self._colors = [colorwheel(n % 256) for n in range(0, 512, wheel_step)] - self._color_idx = 0 - super().__init__(pixel_object, speed, 0, size, spacing, reverse, name) - - def bar_color(self, n, pixel_no=0): - return self._colors[self._color_idx - n] - - def cycle_complete(self): - self._color_idx = (self._color_idx + self._direction) % len(self._colors) - super().cycle_complete() - - -class RainbowComet(Comet): - """ - A rainbow comet animation. - - :param pixel_object: The initialised LED object. - :param float speed: Animation speed in seconds, e.g. ``0.1``. - :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. - :param int tail_length: The length of the comet. Defaults to 10. Cannot exceed the number of - pixels present in the pixel object, e.g. if the strip is 30 pixels - long, the ``tail_length`` cannot exceed 30 pixels. - :param bool reverse: Animates the comet in the reverse order. Defaults to ``False``. - :param bool bounce: Comet will bounce back and forth. Defaults to ``True``. - :param int colorwheel_offset: Offset from start of colorwheel (0-255). - """ - - # pylint: disable=too-many-arguments - def __init__( - self, - pixel_object, - speed, - tail_length=10, - reverse=False, - bounce=False, - colorwheel_offset=0, - name=None, - ): - self._colorwheel_is_tuple = isinstance(colorwheel(0), tuple) - self._colorwheel_offset = colorwheel_offset - - super().__init__(pixel_object, speed, 0, tail_length, reverse, bounce, name) - - def _calc_brightness(self, n, color): - brightness = (n * self._color_step) + self._color_offset - if not self._colorwheel_is_tuple: - color = (color & 0xFF, ((color & 0xFF00) >> 8), (color >> 16)) - return [int(i * brightness) for i in color] - - def __recompute_color(self, color): - factor = int(256 / self._tail_length) - self._comet_colors = [BLACK] + [ - self._calc_brightness( - n, - colorwheel( - int((n * factor) + self._color_offset + self._colorwheel_offset) - % 256 - ), - ) - for n in range(self._tail_length - 1) - ] - self._reverse_comet_colors = list(reversed(self._comet_colors)) - self._computed_color = color diff --git a/adafruit_led_animation/animation/rainbowchase.py b/adafruit_led_animation/animation/rainbowchase.py new file mode 100644 index 0000000..bc54472 --- /dev/null +++ b/adafruit_led_animation/animation/rainbowchase.py @@ -0,0 +1,71 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019-2020 Roy Hooper +# Copyright (c) 2020 Kattni Rembor for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`adafruit_led_animation.animation.rainbowchase` +================================================================================ + +TODO + +* Author(s): Roy Hooper, Kattni Rembor + +""" + +from adafruit_led_animation.color import colorwheel +from adafruit_led_animation.animation.chase import Chase + + +class RainbowChase(Chase): + """ + Chase pixels in one direction, like a theater marquee but with rainbows! + + :param pixel_object: The initialised LED object. + :param float speed: Animation speed rate in seconds, e.g. ``0.1``. + :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. + :param size: Number of pixels to turn on in a row. + :param spacing: Number of pixels to turn off in a row. + :param reverse: Reverse direction of movement. + :param wheel_step: How many colors to skip in `colorwheel` per bar (default 8) + """ + + # pylint: disable=too-many-arguments + def __init__( + self, + pixel_object, + speed, + size=2, + spacing=3, + reverse=False, + name=None, + wheel_step=8, + ): + self._num_colors = 256 // wheel_step + self._colors = [colorwheel(n % 256) for n in range(0, 512, wheel_step)] + self._color_idx = 0 + super().__init__(pixel_object, speed, 0, size, spacing, reverse, name) + + def bar_color(self, n, pixel_no=0): + return self._colors[self._color_idx - n] + + def cycle_complete(self): + self._color_idx = (self._color_idx + self._direction) % len(self._colors) + super().cycle_complete() diff --git a/adafruit_led_animation/animation/rainbowcomet.py b/adafruit_led_animation/animation/rainbowcomet.py new file mode 100644 index 0000000..88de529 --- /dev/null +++ b/adafruit_led_animation/animation/rainbowcomet.py @@ -0,0 +1,87 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019-2020 Roy Hooper +# Copyright (c) 2020 Kattni Rembor for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`adafruit_led_animation.animation.rainbowcomet` +================================================================================ + +TODO + +* Author(s): Roy Hooper, Kattni Rembor + +""" + +from adafruit_led_animation.animation.comet import Comet +from adafruit_led_animation.color import colorwheel, BLACK + + +class RainbowComet(Comet): + """ + A rainbow comet animation. + + :param pixel_object: The initialised LED object. + :param float speed: Animation speed in seconds, e.g. ``0.1``. + :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. + :param int tail_length: The length of the comet. Defaults to 10. Cannot exceed the number of + pixels present in the pixel object, e.g. if the strip is 30 pixels + long, the ``tail_length`` cannot exceed 30 pixels. + :param bool reverse: Animates the comet in the reverse order. Defaults to ``False``. + :param bool bounce: Comet will bounce back and forth. Defaults to ``True``. + :param int colorwheel_offset: Offset from start of colorwheel (0-255). + """ + + # pylint: disable=too-many-arguments + def __init__( + self, + pixel_object, + speed, + tail_length=10, + reverse=False, + bounce=False, + colorwheel_offset=0, + name=None, + ): + self._colorwheel_is_tuple = isinstance(colorwheel(0), tuple) + self._colorwheel_offset = colorwheel_offset + + super().__init__(pixel_object, speed, 0, tail_length, reverse, bounce, name) + + def _calc_brightness(self, n, color): + brightness = (n * self._color_step) + self._color_offset + if not self._colorwheel_is_tuple: + color = (color & 0xFF, ((color & 0xFF00) >> 8), (color >> 16)) + return [int(i * brightness) for i in color] + + def __recompute_color(self, color): + factor = int(256 / self._tail_length) + self._comet_colors = [BLACK] + [ + self._calc_brightness( + n, + colorwheel( + int((n * factor) + self._color_offset + self._colorwheel_offset) + % 256 + ), + ) + for n in range(self._tail_length - 1) + ] + self._reverse_comet_colors = list(reversed(self._comet_colors)) + self._computed_color = color diff --git a/adafruit_led_animation/animation/solid.py b/adafruit_led_animation/animation/solid.py new file mode 100644 index 0000000..10aa56f --- /dev/null +++ b/adafruit_led_animation/animation/solid.py @@ -0,0 +1,48 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019-2020 Roy Hooper +# Copyright (c) 2020 Kattni Rembor for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`adafruit_led_animation.animation.solid` +================================================================================ + +TODO + +* Author(s): Roy Hooper, Kattni Rembor + +""" + +from adafruit_led_animation.animation.colorcycle import ColorCycle + + +class Solid(ColorCycle): + """ + A solid color. + + :param pixel_object: The initialised LED object. + :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. + """ + + def __init__(self, pixel_object, color, name=None): + super().__init__(pixel_object, speed=1, colors=[color], name=name) + + def _recompute_color(self, color): + self.colors = [color] diff --git a/adafruit_led_animation/animation/sparkle.py b/adafruit_led_animation/animation/sparkle.py new file mode 100644 index 0000000..c1ed704 --- /dev/null +++ b/adafruit_led_animation/animation/sparkle.py @@ -0,0 +1,95 @@ +# The MIT License (MIT) +# +# Copyright (c) 2019-2020 Roy Hooper +# Copyright (c) 2020 Kattni Rembor for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`adafruit_led_animation.animation.sparkle` +================================================================================ + +Sparkle animations for CircuitPython helper library for LED animations. +TODO + +* Author(s): Roy Hooper, Kattni Rembor + +Implementation Notes +-------------------- + +**Hardware:** + +* `Adafruit NeoPixels `_ +* `Adafruit DotStars `_ + +**Software and Dependencies:** + +* Adafruit CircuitPython firmware for the supported boards: + https://circuitpython.org/downloads + +""" + +import random +from adafruit_led_animation.animation import Animation + +__version__ = "0.0.0-auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_LED_Animation.git" + + +class Sparkle(Animation): + """ + Sparkle animation of a single color. + + :param pixel_object: The initialised LED object. + :param float speed: Animation speed in seconds, e.g. ``0.1``. + :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. + :param num_sparkles: Number of sparkles to generate per animation cycle. + """ + + # pylint: disable=too-many-arguments + def __init__(self, pixel_object, speed, color, num_sparkles=1, name=None): + if len(pixel_object) < 2: + raise ValueError("Sparkle needs at least 2 pixels") + self._half_color = None + self._dim_color = None + self._num_sparkles = num_sparkles + super().__init__(pixel_object, speed, color, name=name) + + def _recompute_color(self, color): + half_color = tuple(color[rgb] // 4 for rgb in range(len(color))) + dim_color = tuple(color[rgb] // 10 for rgb in range(len(color))) + for pixel in range(len(self.pixel_object)): + if self.pixel_object[pixel] == self._half_color: + self.pixel_object[pixel] = half_color + elif self.pixel_object[pixel] == self._dim_color: + self.pixel_object[pixel] = dim_color + self._half_color = half_color + self._dim_color = dim_color + + def draw(self): + pixels = [ + random.randint(0, (len(self.pixel_object) - 2)) + for n in range(self._num_sparkles) + ] + for pixel in pixels: + self.pixel_object[pixel] = self._color + self.show() + for pixel in pixels: + self.pixel_object[pixel] = self._half_color + self.pixel_object[pixel + 1] = self._dim_color + self.show() diff --git a/adafruit_led_animation/sparkle.py b/adafruit_led_animation/animation/sparklepulse.py similarity index 62% rename from adafruit_led_animation/sparkle.py rename to adafruit_led_animation/animation/sparklepulse.py index 8ac5b36..dcd4740 100644 --- a/adafruit_led_animation/sparkle.py +++ b/adafruit_led_animation/animation/sparklepulse.py @@ -21,79 +21,20 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. """ -`adafruit_led_animation.sparkle` +`adafruit_led_animation.animation.sparklepluse` ================================================================================ -Sparkle animations for CircuitPython helper library for LED animations. +TODO - -* Author(s): Roy Hooper, Kattni Rembor - -Implementation Notes --------------------- - -**Hardware:** - -* `Adafruit NeoPixels `_ -* `Adafruit DotStars `_ - -**Software and Dependencies:** - -* Adafruit CircuitPython firmware for the supported boards: - https://circuitpython.org/downloads +* Author(s): TODO """ import random +from time import monotonic_ns + +from adafruit_led_animation import NANOS_PER_SECOND from adafruit_led_animation.animation import Animation -from . import NANOS_PER_SECOND, monotonic_ns - -__version__ = "0.0.0-auto.0" -__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_LED_Animation.git" - - -class Sparkle(Animation): - """ - Sparkle animation of a single color. - - :param pixel_object: The initialised LED object. - :param float speed: Animation speed in seconds, e.g. ``0.1``. - :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. - :param num_sparkles: Number of sparkles to generate per animation cycle. - """ - - # pylint: disable=too-many-arguments - def __init__(self, pixel_object, speed, color, num_sparkles=1, name=None): - if len(pixel_object) < 2: - raise ValueError("Sparkle needs at least 2 pixels") - self._half_color = None - self._dim_color = None - self._num_sparkles = num_sparkles - super().__init__(pixel_object, speed, color, name=name) - - def _recompute_color(self, color): - half_color = tuple(color[rgb] // 4 for rgb in range(len(color))) - dim_color = tuple(color[rgb] // 10 for rgb in range(len(color))) - for pixel in range(len(self.pixel_object)): - if self.pixel_object[pixel] == self._half_color: - self.pixel_object[pixel] = half_color - elif self.pixel_object[pixel] == self._dim_color: - self.pixel_object[pixel] = dim_color - self._half_color = half_color - self._dim_color = dim_color - - def draw(self): - pixels = [ - random.randint(0, (len(self.pixel_object) - 2)) - for n in range(self._num_sparkles) - ] - for pixel in pixels: - self.pixel_object[pixel] = self._color - self.show() - for pixel in pixels: - self.pixel_object[pixel] = self._half_color - self.pixel_object[pixel + 1] = self._dim_color - self.show() class SparklePulse(Animation): diff --git a/adafruit_led_animation/color.py b/adafruit_led_animation/color.py index 28203ee..daead03 100644 --- a/adafruit_led_animation/color.py +++ b/adafruit_led_animation/color.py @@ -45,8 +45,8 @@ RAINBOW = (RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE) try: - # Backwards compat for 5.3.0 and prior try: + # Backwards compat for 5.3.0 and prior from _pixelbuf import colorwheel # pylint: disable=unused-import except ImportError: from _pixelbuf import wheel as colorwheel # pylint: disable=unused-import diff --git a/examples/led_animation_all_animations.py b/examples/led_animation_all_animations.py index 8ec5b20..e78d188 100644 --- a/examples/led_animation_all_animations.py +++ b/examples/led_animation_all_animations.py @@ -9,8 +9,8 @@ This example does not work on SAMD21 (M0) boards. import board import neopixel -import adafruit_led_animation.rainbow -import adafruit_led_animation.sparkle +import adafruit_led_animation.animation.blink +import adafruit_led_animation.animation.sparklepulse from adafruit_led_animation import animation from adafruit_led_animation.sequence import AnimationSequence from adafruit_led_animation.color import PURPLE, WHITE, AMBER, JADE @@ -22,22 +22,22 @@ pixel_num = 32 pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.2, auto_write=False) -blink = animation.Blink(pixels, speed=0.5, color=JADE) +blink = adafruit_led_animation.animation.animation.blink.Blink(pixels, speed=0.5, color=JADE) comet = animation.Comet(pixels, speed=0.01, color=PURPLE, tail_length=10, bounce=True) chase = animation.Chase(pixels, speed=0.1, size=3, spacing=6, color=WHITE) pulse = animation.Pulse(pixels, speed=0.1, period=3, color=AMBER) -sparkle = adafruit_led_animation.sparkle.Sparkle( +sparkle = adafruit_led_animation.animation.animation.sparkle.Sparkle( pixels, speed=0.1, color=PURPLE, num_sparkles=10 ) -solid = animation.Solid(pixels, color=JADE) -rainbow = adafruit_led_animation.rainbow.Rainbow(pixels, speed=0.1, period=2) -sparkle_pulse = adafruit_led_animation.sparkle.SparklePulse( +solid = adafruit_led_animation.animation.animation.solid.Solid(pixels, color=JADE) +rainbow = adafruit_led_animation.animation.animation.rainbow.Rainbow(pixels, speed=0.1, period=2) +sparkle_pulse = adafruit_led_animation.animation.animation.sparklepulse.SparklePulse( pixels, speed=0.1, period=3, color=JADE ) -rainbow_comet = adafruit_led_animation.rainbow.RainbowComet( +rainbow_comet = adafruit_led_animation.animation.animation.rainbowcomet.RainbowComet( pixels, speed=0.1, tail_length=7, bounce=True ) -rainbow_chase = adafruit_led_animation.rainbow.RainbowChase( +rainbow_chase = adafruit_led_animation.animation.animation.rainbowchase.RainbowChase( pixels, speed=0.1, size=3, spacing=2, wheel_step=8 ) diff --git a/examples/led_animation_gridmap.py b/examples/led_animation_gridmap.py index 2931aab..336c5a2 100644 --- a/examples/led_animation_gridmap.py +++ b/examples/led_animation_gridmap.py @@ -10,7 +10,7 @@ This example does not work on SAMD21 (M0) boards. import board import neopixel -import adafruit_led_animation.rainbow +import adafruit_led_animation.animation.rainbow import adafruit_led_animation.sequence from adafruit_led_animation import animation from adafruit_led_animation import helper @@ -35,16 +35,16 @@ comet_v = animation.Comet( chase_h = animation.Chase( pixel_wing_horizontal, speed=0.1, size=3, spacing=6, color=JADE ) -rainbow_chase_v = adafruit_led_animation.rainbow.RainbowChase( +rainbow_chase_v = adafruit_led_animation.animation.animation.rainbowchase.RainbowChase( pixel_wing_vertical, speed=0.1, size=3, spacing=2, wheel_step=8 ) -rainbow_comet_v = adafruit_led_animation.rainbow.RainbowComet( +rainbow_comet_v = adafruit_led_animation.animation.animation.rainbowcomet.RainbowComet( pixel_wing_vertical, speed=0.1, tail_length=7, bounce=True ) -rainbow_v = adafruit_led_animation.rainbow.Rainbow( +rainbow_v = adafruit_led_animation.animation.animation.rainbow.Rainbow( pixel_wing_vertical, speed=0.1, period=2 ) -rainbow_chase_h = adafruit_led_animation.rainbow.RainbowChase( +rainbow_chase_h = adafruit_led_animation.animation.animation.rainbowchase.RainbowChase( pixel_wing_horizontal, speed=0.1, size=3, spacing=3 )