fluepdot-scripts/images.py

233 lines
7.1 KiB
Python
Raw Permalink Normal View History

"""
This file contains animations, stillframes and transitions that can be imported elsewhere.
When running this file it will randomly select a transition and a frame to transition to indefinitly.
"""
# requires: python-fluepdot, python-dotenv
import os
from math import sqrt
from dotenv import load_dotenv
from fluepdot import Fluepdot, Mode
from random import choice, sample
from itertools import product
from time import sleep
from typing import Generator
DELAY = 5 # hold time for completed animations/frames
ANIMATION_DELAY = 0.1 # delay between frames of animations
flipdot_logo = [
" XXXXXX ",
" XX X ",
" X X ",
" X X ",
" X XX ",
"X X",
"X X X X",
"X X X X X",
"X X XXX X",
"X XXX X X X",
"X X X X X",
" X X XXX X ",
" X X ",
" X X ",
" XX XX ",
" XXXXXX ",
]
def checkerboard(len_x: int, len_y: int, invert: bool = False) -> list[list[bool]]:
return [[(j + i + invert) % 2 == 0 for i in range(len_x)] for j in range(len_y)]
def droplet(fd: Fluepdot):
max_x, max_y = fd.get_size()
fd.set_mode(Mode.DIFFERENTIAL)
frame = [[False for _ in range(max_x)] for _ in range(max_y)]
cx, cy = max_x // 2 - .5, max_y // 2 - .5
r = 0
while r < cx + 32:
for y in range(max_y):
for x in range(int(cx - r - 2), int(cx + r + 3)):
if not (0 <= x < max_x and 0 <= y < max_y):
continue
frame[y][x] = False
dist = ((x - cx) ** 2 + (y - cy) ** 2) ** .5
for l in range(5):
if r - 1 - (l ** 1.4) * 4 < dist < r + 1 - (l ** 1.4) * 4:
frame[y][x] = True
break
r += 2
if r > 36:
fd_logo_width = len(flipdot_logo[0])
s = int(cx + 1 - fd_logo_width // 2)
e = s + fd_logo_width
for y in range(max_y):
for x in range(s, e):
frame[y][x] = flipdot_logo[y][x - s] == "X" or frame[y][x]
sleep(ANIMATION_DELAY)
fd.post_frame(frame)
def fd_logo(fd: Fluepdot) -> list[list[bool]]:
fd.post_text("flipdot e.V.", x=7, y=0, font="fixed_7x14")
original_frame = fd.get_frame()
frame = []
for lrow, frow in zip(flipdot_logo, original_frame):
new_row = frow[:-(len(lrow) + 2)] + lrow + frow[-2:]
frame.append(new_row)
return [[c == "X" for c in row] for row in frame]
def kurhacken(fd: Fluepdot) -> list[list[bool]]:
fd.post_text("Kurhecken", x=14, y=-2, font="fixed_10x20")
frame = [[c == "X" for c in row] for row in fd.get_frame()]
fd.set_mode(Mode.DIFFERENTIAL)
for x, y in [(56, 4), (57, 4), (60, 4), (61, 4), (56, 3), (57, 3), (60, 3), (61, 3)]:
frame[y][x] = True
return frame
def event37c3(fd: Fluepdot) -> list[list[bool]]:
fd.post_text("37c3Unlocked", x=-1, y=-1, font="DejaVuSerif16")
frame = [[c == "X" for c in row] for row in fd.get_frame()]
return frame
def event38c3(fd: Fluepdot) -> list[list[bool]]:
fd.post_text("38C3 Illegal Instructions", font="DejaVuSerif16")
frame = [[c == "X" for c in row] for row in fd.get_frame()]
return frame
def hackumenta(fd: Fluepdot) -> list[list[bool]]:
fd.post_text("Hackumenta", x=3, y=-1, font="DejaVuSerif16")
frame = [[c == "X" for c in row] for row in fd.get_frame()]
return frame
def wave(len_x: int, len_y: int) -> Generator[list[list[bool]], None, None]:
center_x = len_x // 2
center_y = len_y // 2
for r in range(max(len_x, len_y)+5):
arr = [[False for _ in range(len_x)] for _ in range(len_y)]
for y in range(len_y):
for x in range(len_x):
for i in [j for j in range(r, 0, -4)][:5]:
dist = sqrt((x - center_x) ** 2 + (y - center_y) ** 2)
adist = int(dist)
if adist == i or adist == i+1:
arr[y][x] = True
yield arr
def propagate_wave(fd: Fluepdot, delay: float = ANIMATION_DELAY) -> None:
x, y = fd.get_size()
wave_func = wave(x, y)
for w in wave_func:
fd.post_frame(w)
sleep(delay)
def wipe_to(fd: Fluepdot, frame: list[list[bool]]):
fd.set_mode(Mode.DIFFERENTIAL)
current = [[c == "X" for c in row] for row in fd.get_frame()]
for i in range(0, len(current[0])):
for j in range(len(current)):
current[j][i] = frame[j][i]
if i + 1 < len(current[0]):
current[j][i + 1] = True
sleep(ANIMATION_DELAY)
fd.post_frame(current)
def dither_to(fd: Fluepdot, frame: list[list[bool]]):
fd.set_mode(Mode.DIFFERENTIAL)
max_x, max_y = fd.get_size()
current = [[c == "X" for c in row] for row in fd.get_frame()]
coords = [(x, y) for x, y in product(range(max_x), range(max_y)) if current[y][x] != frame[y][x]]
while coords:
sleep(ANIMATION_DELAY)
x, y = choice(coords)
coords.remove((x, y))
if frame[y][x]:
r = fd.set_pixel(x, y)
if r.status_code != 200:
print(r.text)
else:
r = fd.unset_pixel(x, y)
if r.status_code != 200:
print(r.text)
def push_to(fd: Fluepdot, frame: list[list[bool]]):
fd.set_mode(Mode.DIFFERENTIAL)
current = [[c == "X" for c in row] for row in fd.get_frame()]
while True:
sleep(max(ANIMATION_DELAY, 1))
for crow, frow in zip(current, frame):
for _ in range(3):
crow.insert(0, frow.pop(-1))
crow.pop(-1)
if frow:
continue
else:
break
else:
fd.post_frame(current)
continue
break
def roll_to(fd: Fluepdot, frame: list[list[bool]]):
fd.set_mode(Mode.DIFFERENTIAL)
current = [[c == "X" for c in row] for row in fd.get_frame()]
while frame:
sleep(max(ANIMATION_DELAY, 1.5))
current.insert(0, frame.pop())
current.pop()
fd.post_frame(current)
if __name__ == "__main__":
load_dotenv()
hostname = os.getenv("DOTS_HOST")
fd = Fluepdot(f"http://{hostname}")
fd.clear()
animations = [
droplet,
propagate_wave
]
transitions = [
wipe_to,
dither_to,
push_to,
roll_to,
]
stillframes = [
kurhacken(fd), # Text endpoint
checkerboard(*fd.get_size(), invert=False), # Frame endpoint
[[True] * 115] * 16,
[[False] * 115] * 16,
#event37c3(fd), # Text endpoint
event38c3(fd), # Text endpoint
hackumenta(fd),
fd_logo(fd), # Text endpoint
]
#for i, f in enumerate(stillframes):
# print(i, len(f), [len(g) for g in f])
current = stillframes[1]
while True:
sf = sample(stillframes, k=1)[0]
tr = choice(transitions)
if sf == current:
print("Skipping same frame")
continue
print(tr.__name__)
current = sf
tr(fd, sf)
sleep(DELAY)