-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Add a fast PixelMap-like class #7191
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send 8000 you account related emails.
Already on GitHub? Sign in to your account
Conversation
This branch has some unrelated stuff in it, I'll rebase it out before bringing out of draft. |
7c323ba
to
513c116
Compare
.. a fast helper for animations. It is similar to and inspired by the PixelMap helper in Adafruit LED Animation library, but with an extremely fast 'paste' method for setting a series of pixels. This is a common operation for many animations, and can give a substantial speed improvement. It's named `adafruit_pixelmap` so that we can package a compatible version in pure Python for systems that can't fit it in C in flash, or for Blinka. This is a proof of concept and can make a very fast comet animation: ```python import time import adafruit_pixelbuf import adafruti_pixelmap import board import neopixel from supervisor import ticks_ms from adafruit_led_animation.animation.solid import Solid from adafruit_led_animation import color pixel_pin = board.GP0 pixel_num = 96 pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=1, auto_write=False, pixel_order="RGB") evens = adafruit_pixelmap.PixelMap(pixels, tuple(range(0, pixel_num, 2))) odd_indices = tuple((i, i+2) for i in range(1, pixel_num, 4)) print(odd_indices) odds = adafruit_pixelbuf.PixelMap(pixels, odd_indices) assert len(odds) == len(odd_indices) comet_length = 16 comet1 = [color.calculate_intensity(color.GREEN, ((1+i) / comet_length) ** 2.4) for i in range(comet_length)] comet2 = [color.calculate_intensity(color.PURPLE, ((1+i) / comet_length) ** 2.4) for i in range(comet_length)] pos1 = 0 pos2 = 96//4 while True: evens.paste(comet1, pos1, wrap=True, reverse=False, others=0) pos1 = (pos1 + 1) % len(evens) odds.paste(comet2, pos2, wrap=True, reverse=True, others=0) pos2 = (pos2 - 1) % len(odds) pixels.show() m = ticks_ms() if m % 2000 > 1000: time.sleep(.02) ```
I tested this successfully on a NeoTrellis M4 using this modified version of the pixelmap example from the led_animation repo: import board
import neopixel
from adafruit_led_animation.animation.comet import Comet
from adafruit_led_animation.animation.rainbowcomet import RainbowComet
from adafruit_led_animation.animation.rainbowchase import RainbowChase
from adafruit_led_animation.animation.chase import Chase
from adafruit_led_animation.animation.rainbow import Rainbow
from adafruit_led_animation.sequence import AnimationSequence
import adafruit_pixelmap
from adafruit_led_animation.color import PURPLE, JADE, AMBER
pixel_pin = board.NEOPIXEL
pixel_num = 32
pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.5, auto_write=False)
pixel_wing_vertical = adafruit_pixelmap.PixelMap(pixels, ((0, 8, 16, 24), (1, 9, 17, 25), (2, 10, 18, 26), (3, 11, 19, 27), (4, 12, 20, 28), (5, 13, 21, 29), (6, 14, 22, 30), (7, 15, 23, 31)))
pixel_wing_horizontal = adafruit_pixelmap.PixelMap(pixels, ((0, 1, 2, 3, 4, 5, 6, 7), (8, 9, 10, 11, 12, 13, 14, 15), (16, 17, 18, 19, 20, 21, 22, 23), (24, 25, 26, 27, 28, 29, 30, 31)))
comet_h = Comet(
pixel_wing_horizontal, speed=0.1, color=PURPLE, tail_length=3, bounce=True
)
comet_v = Comet(pixel_wing_vertical, speed=0.1, color=AMBER, tail_length=6, bounce=True)
chase_h = Chase(pixel_wing_horizontal, speed=0.1, size=3, spacing=6, color=JADE)
rainbow_chase_v = RainbowChase(
pixel_wing_vertical, speed=0.1, size=3, spacing=2, step=8
)
rainbow_comet_v
8000
= RainbowComet(
pixel_wing_vertical, speed=0.1, tail_length=7, bounce=True
)
rainbow_v = Rainbow(pixel_wing_vertical, speed=0.1, period=2)
rainbow_chase_h = RainbowChase(pixel_wing_horizontal, speed=0.1, size=3, spacing=3)
animations = AnimationSequence(
rainbow_v,
comet_h,
rainbow_comet_v,
chase_h,
rainbow_chase_v,
comet_v,
rainbow_chase_h,
advance_interval=5,
)
while True:
animations.animate() This version does run successfully and the animations appear visually the same to the stock version of the example which utilizes the python PixelMap implementation. In that regards this seems good to me. However I did notice a few differences in the supported features that I wanted to mention because I wasn't sure if this is intended to be a full drop-in replacement for the python implementation or not. This core module does not have the Those were used by the original example that I started from, they feel like variations of the initializer to me. They create an instance in a certain way and return it. For testing I replaced their usage with hardcoded tuples that contain the indexes for my 8x4 grid on the NeoTrellis. I attempted having the python implementation extend the core implementation but that didn't work due to one other difference which is the |
The testing is appreciated! This is not intended to be a 100% replacement for the I chose not to implement everything as a way to keep the code size smaller. I'd prefer if only the part that benefits the most from speed is in C and as much as possible is in Python. So, for instance, those class methods would just shift to being non-class functions in It is worth taking time to decide how to structure this. First I said I wanted to call it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do think it sounds good to have it under _pixelmap
and then having the accompanying python code be adafruit_pixelmap
library with the helper functions that is meant for use by user code and other libraries.
If we do want to go that way, I can work this week on cookie cutting a library for it and migrating the existing code from led_animation and making the changes needed to work with new core module.
Two potential issues I found:
As to the helper code, I'm not sure if a Also I have not done any speed comparisons but will try to do that as well. |
This one should be fixed for sure
This one was deliberately not included initially, but now that I used such slices to e.g., quickly scroll in the 5x5 neopixel bff example code I guess it should be added |
# Conflicts: # shared-module/adafruit_pixelbuf/PixelBuf.c
@gamblor21 do you raise an exception like I'm seeing this when I attempt to access outside of the bounds, and it seems like reasonable / expected behavior to me for the error case.
|
There are at least two things that could be out of range
|
Recommend simply disabling the new module on this board...
|
I had my matrix temporarily mounted in my window. I plan to take it down tomorrow and will do some more testing to find out how to reproduce the error I saw. It was not just an exception but some type of hard fault (or infinite loop as I lost USB). |
The latest commits disable this on the device that was too large, and move it to I think the remaining open items for consideration are:
I have tested the current versions of these successfully on a NeoTrellis M4 as well as Feather S2 TFT + Neopixel Featherwign. running the |
Sorry for the delay in testing, I hope to get to it tomorrow again. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For a pixel string N long the range checks were lettings pixels[n]
go through (and crash) where pixels[n-1]
is the last valid value. Two range checks were inclusive of the top index value.
Other out of bound errors were being caught properly.
Comment on slices: I did figure out how to implement the get slice, I could try to (figure out how to and) push a commit to this branch. I think it is worthwhile in the core. It was a 25% speed increase over doing it in Python. |
Co-authored-by: Mark <56205165+gamblor21@users.noreply.github.com>
Co-authored-by: Mark <56205165+gamblor21@users.noreply.github.com>
@jepler I think you have passed this on, but is it meeting with your approval with the current changes? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The other folks' changes look good, no testing performed
I tried it last night quickly and it worked. Was going to do a little more testing tonight if I have the time but seems good to me. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems fine to merge now. Further testing is good but it would be good to get it out for people to try. Since it's an underscore module, we don't guarantee about the API will remain stable.
LED Animation Library has a class called PixelMap.
This is inspired by PixelMap, a little different, but close! With adafruit/Adafruit_CircuitPython_LED_Animation#103 it can support the following tested animations (I think that's all of 'em):