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..83a2aa3 --- /dev/null +++ b/adafruit_led_animation/animation/__init__.py @@ -0,0 +1,199 @@ +# 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 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 adafruit_led_animation import NANOS_PER_SECOND, monotonic_ns + + +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..fe6ec10 --- /dev/null +++ b/adafruit_led_animation/animation/blink.py @@ -0,0 +1,64 @@ +# 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` +================================================================================ + +Blink animation 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 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..bd7a308 --- /dev/null +++ b/adafruit_led_animation/animation/chase.py @@ -0,0 +1,146 @@ +# 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` +================================================================================ + +Theatre chase animation 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 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..42b590e --- /dev/null +++ b/adafruit_led_animation/animation/colorcycle.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.colorcycle` +================================================================================ + +Color cycle animation 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 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..4476128 --- /dev/null +++ b/adafruit_led_animation/animation/comet.py @@ -0,0 +1,146 @@ +# 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` +================================================================================ + +Comet animation 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 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..1b3aa33 --- /dev/null +++ b/adafruit_led_animation/animation/pulse.py @@ -0,0 +1,85 @@ +# 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` +================================================================================ + +Pulse animation 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 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/animation/rainbow.py b/adafruit_led_animation/animation/rainbow.py new file mode 100644 index 0000000..c102dd5 --- /dev/null +++ b/adafruit_led_animation/animation/rainbow.py @@ -0,0 +1,144 @@ +# 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.rainbow` +================================================================================ + +Rainbow animation 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 adafruit_led_animation.animation import Animation +from adafruit_led_animation.color import BLACK, colorwheel +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" + + +class Rainbow(Animation): + """ + The classic rainbow color wheel. + + :param pixel_object: The initialised LED object. + :param float speed: Animation refresh rate in seconds, e.g. ``0.1``. + :param float period: Period to cycle the rainbow over in seconds. Default 5. + :param float step: Color wheel step. Default 1. + :param str name: Name of animation (optional, useful for sequences and debugging). + :param bool precompute_rainbow: Whether to precompute the rainbow. Uses more memory. + (default True). + """ + + # pylint: disable=too-many-arguments + def __init__( + self, pixel_object, speed, period=5, step=1, name=None, precompute_rainbow=True + ): + super().__init__(pixel_object, speed, BLACK, name=name) + self._period = period + self._step = step + self._wheel_index = 0 + self.colors = None + self._generator = self._color_wheel_generator() + if precompute_rainbow: + self.generate_rainbow() + + def generate_rainbow(self): + """Generates the rainbow.""" + self.colors = [] + i = 0 + while i < 256: + self.colors.append(colorwheel(int(i))) + i += self._step + + cycle_complete_supported = True + + def _color_wheel_generator(self): + period = int(self._period * NANOS_PER_SECOND) + + num_pixels = len(self.pixel_object) + last_update = monotonic_ns() + cycle_position = 0 + last_pos = 0 + while True: + cycle_completed = False + now = monotonic_ns() + time_since_last_draw = now - last_update + last_update = now + pos = cycle_position = (cycle_position + time_since_last_draw) % period + if pos < last_pos: + cycle_completed = True + last_pos = pos + wheel_index = int((pos / period) * len(self.colors)) + + if self.colors: + self._draw_precomputed(num_pixels, wheel_index) + else: + wheel_index = int((pos / period) * 256) + self.pixel_object[:] = [ + colorwheel((i + wheel_index) % 255) for i in range(num_pixels) + ] + self._wheel_index = wheel_index + self.show() + if cycle_completed: + self.cycle_complete() + yield + + def _draw_precomputed(self, num_pixels, wheel_index): + for i in range(0, num_pixels, len(self.colors)): + num = len(self.colors) + if i + len(self.colors) > num_pixels: + num = num_pixels - i + if wheel_index + num > len(self.colors): + colors_left = len(self.colors) - wheel_index + self.pixel_object[i : i + colors_left] = self.colors[wheel_index:] + self.pixel_object[i + colors_left : i + num] = self.colors[ + : num - colors_left + ] + else: + self.pixel_object[i : i + num] = self.colors[ + wheel_index : wheel_index + num + ] + + def draw(self): + next(self._generator) + + def reset(self): + """ + Resets the animation. + """ + self._generator = self._color_wheel_generator() diff --git a/adafruit_led_animation/animation/rainbowchase.py b/adafruit_led_animation/animation/rainbowchase.py new file mode 100644 index 0000000..8394fdb --- /dev/null +++ b/adafruit_led_animation/animation/rainbowchase.py @@ -0,0 +1,85 @@ +# 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` +================================================================================ + +Rainbow chase animation 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 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/rainbow.py b/adafruit_led_animation/animation/rainbowcomet.py similarity index 52% rename from adafruit_led_animation/rainbow.py rename to adafruit_led_animation/animation/rainbowcomet.py index 78500b1..beb667d 100644 --- a/adafruit_led_animation/rainbow.py +++ b/adafruit_led_animation/animation/rainbowcomet.py @@ -21,10 +21,10 @@ # 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.rainbowcomet` ================================================================================ -Rainbow animations for CircuitPython helper library for LED animations. +Rainbow comet for CircuitPython helper library for LED animations. * Author(s): Roy Hooper, Kattni Rembor @@ -41,103 +41,11 @@ * Adafruit CircuitPython firmware for the supported boards: https://circuitpython.org/downloads -""" - -from adafruit_led_animation.animation import Animation, Chase, Comet -from adafruit_led_animation.color import BLACK, colorwheel -from . import NANOS_PER_SECOND, monotonic_ns - -__version__ = "0.0.0-auto.0" -__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_LED_Animation.git" - - -class Rainbow(Animation): - """ - The classic rainbow color wheel. - - :param pixel_object: The initialised LED object. - :param float speed: Animation refresh rate in seconds, e.g. ``0.1``. - :param period: Period to cycle the rainbow over. Default 5. - """ - - # pylint: disable=too-many-arguments - def __init__(self, pixel_object, speed, period=5, name=None): - super().__init__(pixel_object, speed, BLACK, name=name) - self._period = period - self._generator = self._color_wheel_generator() - - cycle_complete_supported = True - - def _color_wheel_generator(self): - period = int(self._period * NANOS_PER_SECOND) - - last_update = monotonic_ns() - cycle_position = 0 - last_pos = 0 - while True: - cycle_completed = False - now = monotonic_ns() - time_since_last_draw = now - last_update - last_update = now - pos = cycle_position = (cycle_position + time_since_last_draw) % period - if pos < last_pos: - cycle_completed = True - last_pos = pos - wheel_index = int((pos / period) * 256) - self.pixel_object[:] = [ - colorwheel((i + wheel_index) % 255) - for i, _ in enumerate(self.pixel_object) - ] - self.show() - if cycle_completed: - self.cycle_complete() - yield - - def draw(self): - next(self._generator) - - def reset(self): - """ - 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() +from adafruit_led_animation.animation.comet import Comet +from adafruit_led_animation.color import colorwheel, BLACK class RainbowComet(Comet): diff --git a/adafruit_led_animation/animation/rainbowsparkle.py b/adafruit_led_animation/animation/rainbowsparkle.py new file mode 100644 index 0000000..c6be5e0 --- /dev/null +++ b/adafruit_led_animation/animation/rainbowsparkle.py @@ -0,0 +1,118 @@ +# 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.rainbowsparkle` +================================================================================ + +Rainbow sparkle 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 + +""" + +import random +from adafruit_led_animation.animation.rainbow import Rainbow + + +class RainbowSparkle(Rainbow): + """Rainbow sparkle animation. + + :param pixel_object: The initialised LED object. + :param float speed: Animation refresh rate in seconds, e.g. ``0.1``. + :param float period: Period to cycle the rainbow over in seconds. Default 5. + :param int num_sparkles: The number of sparkles to display. Defaults to 1/20 of the pixel + object length. + :param float step: Color wheel step. Default 1. + :param str name: Name of animation (optional, useful for sequences and debugging). + :param float background_brightness: The brightness of the background rainbow. Defaults to + ``0.2`` or 20 percent. + :param bool precompute_rainbow: Whether to precompute the rainbow. Uses more memory. + (default True). + """ + + # pylint: disable=too-many-arguments + def __init__( + self, + pixel_object, + speed, + period=5, + num_sparkles=None, + step=1, + name=None, + background_brightness=0.2, + ): + self._num_sparkles = num_sparkles + if num_sparkles is None: + self._num_sparkles = max(1, int(len(pixel_object) / 20)) + self._sparkle_duration = 2 + self._background_brightness = background_brightness + self._bright_colors = None + super().__init__( + pixel_object=pixel_object, + speed=speed, + period=period, + step=step, + name=name, + precompute_rainbow=True, + ) + + def generate_rainbow(self): + super().generate_rainbow() + self._bright_colors = self.colors[:] + for i, color in enumerate(self.colors): + if isinstance(self.colors[i], int): + self.colors[i] = ( + int(self._background_brightness * ((color & 0xFF0000) >> 16)), + int(self._background_brightness * ((color & 0xFF00) >> 8)), + int(self._background_brightness * (color & 0xFF)), + ) + else: + self.colors[i] = ( + int(self._background_brightness * color[0]), + int(self._background_brightness * color[1]), + int(self._background_brightness * color[2]), + ) + + def show(self): + pixels = [ + random.randint(0, len(self.pixel_object) - 1) + for n in range(self._num_sparkles) + ] + for pixel in pixels: + self.pixel_object[pixel] = self._bright_colors[ + (self._wheel_index + pixel) % len(self._bright_colors) + ] + super().show() diff --git a/adafruit_led_animation/animation/solid.py b/adafruit_led_animation/animation/solid.py new file mode 100644 index 0000000..7fa90e6 --- /dev/null +++ b/adafruit_led_animation/animation/solid.py @@ -0,0 +1,62 @@ +# 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` +================================================================================ + +Solid animation 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 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..81c57a4 --- /dev/null +++ b/adafruit_led_animation/animation/sparkle.py @@ -0,0 +1,94 @@ +# 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 animation 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 + +""" + +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 67% rename from adafruit_led_animation/sparkle.py rename to adafruit_led_animation/animation/sparklepulse.py index 8ac5b36..26ab1e9 100644 --- a/adafruit_led_animation/sparkle.py +++ b/adafruit_led_animation/animation/sparklepulse.py @@ -21,13 +21,12 @@ # 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.sparklepulse` ================================================================================ -Sparkle animations for CircuitPython helper library for LED animations. +Sparkle-pulse animation for CircuitPython helper library for LED animations. - -* Author(s): Roy Hooper, Kattni Rembor +* Author(s): Roy Hooper, dmolavi Implementation Notes -------------------- @@ -42,58 +41,12 @@ * Adafruit CircuitPython firmware for the supported boards: https://circuitpython.org/downloads + """ import random +from adafruit_led_animation import NANOS_PER_SECOND, monotonic_ns 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 0cdb0d2..daead03 100644 --- a/adafruit_led_animation/color.py +++ b/adafruit_led_animation/color.py @@ -45,7 +45,11 @@ try: - from _pixelbuf import colorwheel # pylint: disable=unused-import + 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 except ImportError: # Ensure we have a wheel if not built in def colorwheel(pos): diff --git a/docs/api.rst b/docs/api.rst index 61f7163..b4aa897 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -19,8 +19,38 @@ .. automodule:: adafruit_led_animation.sequence :members: -.. automodule:: adafruit_led_animation.rainbow +.. automodule:: adafruit_led_animation.animation.blink :members: -.. automodule:: adafruit_led_animation.sparkle +.. automodule:: adafruit_led_animation.animation.solid + :members: + +.. automodule:: adafruit_led_animation.animation.colorcycle + :members: + +.. automodule:: adafruit_led_animation.animation.chase + :members: + +.. automodule:: adafruit_led_animation.animation.comet + :members: + +.. automodule:: adafruit_led_animation.animation.pulse + :members: + +.. automodule:: adafruit_led_animation.animation.rainbow + :members: + +.. automodule:: adafruit_led_animation.animation.sparkle + :members: + +.. automodule:: adafruit_led_animation.animation.rainbowchase + :members: + +.. automodule:: adafruit_led_animation.animation.rainbowcomet + :members: + +.. automodule:: adafruit_led_animation.animation.rainbowsparkle + :members: + +.. automodule:: adafruit_led_animation.animation.sparklepulse :members: diff --git a/docs/conf.py b/docs/conf.py index ec64647..fec636b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -21,7 +21,7 @@ # Uncomment the below if you use native CircuitPython modules such as # digitalio, micropython and busio. List the modules you use. Without it, the # autodoc module docs will fail to generate with a warning. -autodoc_mock_imports = ["led_animation"] +autodoc_mock_imports = [] intersphinx_mapping = { @@ -39,8 +39,8 @@ # General information about the project. project = "LED_Animation Library" -copyright = "2017 Adam Patt" -author = "Adam Patt" +copyright = "2018 Roy Hoopert" +author = "Roy Hooper" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/examples/led_animation_all_animations.py b/examples/led_animation_all_animations.py index 8ec5b20..954ff69 100644 --- a/examples/led_animation_all_animations.py +++ b/examples/led_animation_all_animations.py @@ -9,11 +9,20 @@ import board import neopixel -import adafruit_led_animation.rainbow -import adafruit_led_animation.sparkle -from adafruit_led_animation import animation +import adafruit_led_animation.animation.blink as blink_animation +import adafruit_led_animation.animation.sparklepulse as sparklepulse_animation +import adafruit_led_animation.animation.comet as comet_animation +import adafruit_led_animation.animation.chase as chase_animation +import adafruit_led_animation.animation.pulse as pulse_animation +import adafruit_led_animation.animation.sparkle as sparkle_animation +import adafruit_led_animation.animation.rainbowchase as rainbowchase_animation +import adafruit_led_animation.animation.rainbowsparkle as rainbowsparkle_animation +import adafruit_led_animation.animation.rainbowcomet as rainbowcomet_animation +import adafruit_led_animation.animation.solid as solid_animation +import adafruit_led_animation.animation.colorcycle as colorcycle_animation +import adafruit_led_animation.animation.rainbow as rainbow_animation from adafruit_led_animation.sequence import AnimationSequence -from adafruit_led_animation.color import PURPLE, WHITE, AMBER, JADE +from adafruit_led_animation.color import PURPLE, WHITE, AMBER, JADE, MAGENTA, ORANGE # Update to match the pin connected to your NeoPixels pixel_pin = board.D6 @@ -22,36 +31,43 @@ pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.2, auto_write=False) -blink = animation.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( - pixels, speed=0.1, color=PURPLE, num_sparkles=10 +blink = blink_animation.Blink(pixels, speed=0.5, color=JADE) +colorcycle = colorcycle_animation.ColorCycle( + pixels, speed=0.4, colors=[MAGENTA, ORANGE] ) -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( +comet = comet_animation.Comet( + pixels, speed=0.01, color=PURPLE, tail_length=10, bounce=True +) +chase = chase_animation.Chase(pixels, speed=0.1, size=3, spacing=6, color=WHITE) +pulse = pulse_animation.Pulse(pixels, speed=0.1, period=3, color=AMBER) +sparkle = sparkle_animation.Sparkle(pixels, speed=0.1, color=PURPLE, num_sparkles=10) +solid = solid_animation.Solid(pixels, color=JADE) +rainbow = rainbow_animation.Rainbow(pixels, speed=0.1, period=2) +sparkle_pulse = sparklepulse_animation.SparklePulse( pixels, speed=0.1, period=3, color=JADE ) -rainbow_comet = adafruit_led_animation.rainbow.RainbowComet( +rainbow_comet = rainbowcomet_animation.RainbowComet( pixels, speed=0.1, tail_length=7, bounce=True ) -rainbow_chase = adafruit_led_animation.rainbow.RainbowChase( +rainbow_chase = rainbowchase_animation.RainbowChase( pixels, speed=0.1, size=3, spacing=2, wheel_step=8 ) +rainbow_sparkle = rainbowsparkle_animation.RainbowSparkle( + pixels, speed=0.1, num_sparkles=15 +) animations = AnimationSequence( comet, blink, + rainbow_sparkle, chase, pulse, sparkle, - solid, rainbow, - sparkle_pulse, + solid, rainbow_comet, + sparkle_pulse, rainbow_chase, advance_interval=5, auto_clear=True, diff --git a/examples/led_animation_gridmap.py b/examples/led_animation_gridmap.py index 2931aab..d696751 100644 --- a/examples/led_animation_gridmap.py +++ b/examples/led_animation_gridmap.py @@ -10,9 +10,12 @@ import board import neopixel -import adafruit_led_animation.rainbow -import adafruit_led_animation.sequence -from adafruit_led_animation import animation +import adafruit_led_animation.animation.comet as comet_animation +import adafruit_led_animation.animation.rainbowcomet as rainbowcomet_animation +import adafruit_led_animation.animation.rainbowchase as rainbowchase_animation +import adafruit_led_animation.animation.chase as chase_animation +import adafruit_led_animation.animation.rainbow as rainbow_animation +from adafruit_led_animation.sequence import AnimationSequence from adafruit_led_animation import helper from adafruit_led_animation.color import PURPLE, JADE, AMBER @@ -26,29 +29,27 @@ pixels, 8, 4, helper.horizontal_strip_gridmap(8, alternating=False) ) -comet_h = animation.Comet( +comet_h = comet_animation.Comet( pixel_wing_horizontal, speed=0.1, color=PURPLE, tail_length=3, bounce=True ) -comet_v = animation.Comet( +comet_v = comet_animation.Comet( pixel_wing_vertical, speed=0.1, color=AMBER, tail_length=6, bounce=True ) -chase_h = animation.Chase( +chase_h = chase_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 = rainbowchase_animation.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 = rainbowcomet_animation.RainbowComet( pixel_wing_vertical, speed=0.1, tail_length=7, bounce=True ) -rainbow_v = adafruit_led_animation.rainbow.Rainbow( - pixel_wing_vertical, speed=0.1, period=2 -) -rainbow_chase_h = adafruit_led_animation.rainbow.RainbowChase( +rainbow_v = rainbow_animation.Rainbow(pixel_wing_vertical, speed=0.1, period=2) +rainbow_chase_h = rainbowchase_animation.RainbowChase( pixel_wing_horizontal, speed=0.1, size=3, spacing=3 ) -animations = adafruit_led_animation.sequence.AnimationSequence( +animations = AnimationSequence( rainbow_v, comet_h, rainbow_comet_v, diff --git a/examples/led_animation_simpletest.py b/examples/led_animation_simpletest.py index bb53d95..686b7f8 100644 --- a/examples/led_animation_simpletest.py +++ b/examples/led_animation_simpletest.py @@ -9,7 +9,8 @@ """ import board import neopixel -from adafruit_led_animation.animation import Comet, Chase +import adafruit_led_animation.animation.comet as comet_animation +import adafruit_led_animation.animation.chase as chase_animation from adafruit_led_animation.sequence import AnimationSequence from adafruit_led_animation.color import PURPLE, WHITE @@ -20,8 +21,10 @@ pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.2, auto_write=False) -comet = Comet(pixels, speed=0.01, color=PURPLE, tail_length=10, bounce=True) -chase = Chase(pixels, speed=0.1, size=3, spacing=6, color=WHITE) +comet = comet_animation.Comet( + pixels, speed=0.01, color=PURPLE, tail_length=10, bounce=True +) +chase = chase_animation.Chase(pixels, speed=0.1, size=3, spacing=6, color=WHITE) animations = AnimationSequence(comet, chase, advance_interval=5)