184 lines
5.9 KiB
Python
184 lines
5.9 KiB
Python
# SPDX-FileCopyrightText: 2022 Charlyn Gonda for Adafruit Industries
|
|
#
|
|
# SPDX-License-Identifier: MIT
|
|
import time
|
|
import adafruit_dotstar
|
|
|
|
from adafruit_led_animation.animation.rainbowchase import RainbowChase
|
|
from adafruit_led_animation.color import AMBER, JADE, CYAN, GOLD, PINK
|
|
from adafruit_pixel_framebuf import PixelFramebuffer
|
|
|
|
|
|
class Cube():
|
|
def __init__(
|
|
self,
|
|
top_cin,
|
|
top_din,
|
|
side_panels_cin,
|
|
side_panels_din,
|
|
bottom_cin,
|
|
bottom_din):
|
|
|
|
# static numbers
|
|
self.num_pixels = 64*4
|
|
self.num_pixels_topbottom = 64
|
|
self.pixel_width = 8*4
|
|
self.pixel_height = 8
|
|
|
|
# top pixels
|
|
top_pixels = adafruit_dotstar.DotStar(
|
|
top_cin, top_din, self.num_pixels_topbottom,
|
|
brightness=0.03, auto_write=False)
|
|
self.pixel_framebuf_top = PixelFramebuffer(
|
|
top_pixels,
|
|
self.pixel_height,
|
|
self.pixel_height,
|
|
rotation=1,
|
|
alternating=False,
|
|
)
|
|
|
|
# side pixels
|
|
self.side_pixels = adafruit_dotstar.DotStar(
|
|
side_panels_cin, side_panels_din, self.num_pixels,
|
|
brightness=0.03, auto_write=False)
|
|
self.pixel_framebuf_sides = PixelFramebuffer(
|
|
self.side_pixels,
|
|
self.pixel_height,
|
|
self.pixel_width,
|
|
rotation=1,
|
|
alternating=False,
|
|
reverse_y=True,
|
|
reverse_x=False
|
|
)
|
|
self.rainbow_sides = RainbowChase(
|
|
self.side_pixels, speed=0.1, size=3, spacing=6)
|
|
|
|
# bottom pixels
|
|
pixels_bottom = adafruit_dotstar.DotStar(
|
|
bottom_cin, bottom_din, self.num_pixels_topbottom,
|
|
brightness=0.03, auto_write=False)
|
|
self.pixel_framebuf_bottom = PixelFramebuffer(
|
|
pixels_bottom,
|
|
self.pixel_height,
|
|
self.pixel_height,
|
|
rotation=0,
|
|
alternating=False,
|
|
reverse_y=False,
|
|
reverse_x=True
|
|
)
|
|
|
|
# scrolling word state vars
|
|
self.last_color_time = -1
|
|
self.color_wait = 1
|
|
self.word = ''
|
|
self.total_scroll_len = 0
|
|
self.scroll_x_pos = -self.pixel_width
|
|
self.color_idx = 0
|
|
self.color_list = [AMBER, JADE, CYAN, PINK, GOLD]
|
|
self.done_scrolling = False
|
|
|
|
# whether or not the cube is already clear
|
|
self.clear = False
|
|
|
|
# top pixel vars
|
|
self.top_pixel_coords = []
|
|
self.top_pixel_color = CYAN
|
|
|
|
# upside down mode
|
|
self.bottom_last = -1
|
|
self.bottom_wait = 1
|
|
self.bottom_squares = [[0, 0, 8, 8], [
|
|
1, 1, 6, 6], [2, 2, 4, 4], [3, 3, 2, 2]]
|
|
self.bottom_squares_idx = 0
|
|
|
|
def update(self, word, color, coords):
|
|
self.word = word
|
|
self.total_scroll_len = (len(self.word) * 5) + len(self.word)
|
|
self.top_pixel_coords = coords
|
|
self.top_pixel_color = color
|
|
|
|
def scroll_word_and_update_top(self):
|
|
if self.scroll_x_pos >= self.total_scroll_len:
|
|
self.scroll_x_pos = -self.pixel_width
|
|
self.clear_cube()
|
|
self.done_scrolling = True
|
|
else:
|
|
self.done_scrolling = False
|
|
self.clear = False
|
|
|
|
self.__scroll_framebuf(self.word, self.scroll_x_pos, 0)
|
|
self.__display_top_pixels()
|
|
self.scroll_x_pos = self.scroll_x_pos + 1
|
|
|
|
def clear_cube(self, clear_top=False):
|
|
if not self.clear:
|
|
self.pixel_framebuf_sides.fill(0)
|
|
self.pixel_framebuf_sides.display()
|
|
self.side_pixels.fill(0)
|
|
self.side_pixels.show()
|
|
self.pixel_framebuf_bottom.fill(0)
|
|
self.pixel_framebuf_bottom.display()
|
|
if clear_top:
|
|
self.pixel_framebuf_top.fill(0)
|
|
self.pixel_framebuf_top.display()
|
|
self.clear = True
|
|
|
|
def upside_down_mode(self):
|
|
self.clear_cube(True)
|
|
self.rainbow_sides.animate()
|
|
now = time.monotonic()
|
|
self.__bottom_square_animation(now)
|
|
|
|
def waiting_mode(self):
|
|
self.pixel_framebuf_top.pixel(3, 3, CYAN)
|
|
self.pixel_framebuf_top.pixel(4, 4, PINK)
|
|
self.pixel_framebuf_top.display()
|
|
|
|
def __bottom_square_animation(self, now):
|
|
self.pixel_framebuf_bottom.fill(0)
|
|
color_int = self._rgb_to_int(CYAN)
|
|
if now > self.bottom_last + self.bottom_wait:
|
|
self.__coord_wrap()
|
|
self.bottom_last = now
|
|
x, y, w, h = self.bottom_squares[self.bottom_squares_idx]
|
|
self.pixel_framebuf_bottom.rect(x, y, w, h, color_int)
|
|
self.pixel_framebuf_bottom.display()
|
|
|
|
def __coord_wrap(self):
|
|
self.bottom_squares_idx = self.bottom_squares_idx + 1
|
|
if self.bottom_squares_idx >= len(self.bottom_squares):
|
|
self.bottom_squares_idx = 0
|
|
|
|
def __display_top_pixels(self):
|
|
self.pixel_framebuf_top.fill(0)
|
|
|
|
for coord in self.top_pixel_coords:
|
|
x, y = coord.split(":")
|
|
self.pixel_framebuf_top.pixel(int(x), int(y), self.top_pixel_color)
|
|
self.pixel_framebuf_top.display()
|
|
|
|
def __scroll_framebuf(self, word, shift_x, shift_y):
|
|
self.pixel_framebuf_sides.fill(0)
|
|
|
|
color = self.__next_color()
|
|
color_int = self._rgb_to_int(color)
|
|
|
|
# negate x so that the word can be shown from left to right
|
|
self.pixel_framebuf_sides.text(word, -shift_x, shift_y, color_int)
|
|
self.pixel_framebuf_sides.display()
|
|
|
|
def __next_color(self):
|
|
if self.color_idx >= len(self.color_list):
|
|
self.color_idx = 0
|
|
|
|
result = self.color_list[self.color_idx]
|
|
now = time.monotonic()
|
|
if now >= self.last_color_time + self.color_wait:
|
|
self.color_idx = self.color_idx + 1
|
|
self.last_color_time = now
|
|
|
|
return result
|
|
|
|
@staticmethod
|
|
def _rgb_to_int(rgb):
|
|
return rgb[0] << 16 | rgb[1] << 8 | rgb[2]
|