sync with current state
This commit is contained in:
parent
895a744873
commit
8dc0480c18
BIN
apps/PressStart2P-Regular.ttf
Normal file
BIN
apps/PressStart2P-Regular.ttf
Normal file
Binary file not shown.
38
apps/backlight.py
Executable file
38
apps/backlight.py
Executable file
@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
import math
|
||||||
|
import numpy as np
|
||||||
|
# Groesse des Bildschirms bestimmen
|
||||||
|
Nx = int(sys.argv[1])
|
||||||
|
Ny = int(sys.argv[2])
|
||||||
|
|
||||||
|
# Bestimme den Parameter
|
||||||
|
time_ms = 10
|
||||||
|
try:
|
||||||
|
time_ms = int(sys.argv[3])
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Puffer fuer Pixel erstellen und mit 0 initialisieren
|
||||||
|
|
||||||
|
curPixel = 0
|
||||||
|
|
||||||
|
phase = np.random.uniform(0, 2*np.pi, (Ny, Nx))
|
||||||
|
t = 0
|
||||||
|
f_min = 0.08
|
||||||
|
f_max = 0.10
|
||||||
|
f = np.random.uniform(f_min, f_max, (Ny, Nx))
|
||||||
|
while True:
|
||||||
|
t += time_ms/1000.0
|
||||||
|
s = 0.80 +0.2*np.sin(phase+2*np.pi*t*f)
|
||||||
|
img = np.zeros([Ny, Nx, 4])
|
||||||
|
img[:,:,3] = 255*s
|
||||||
|
# Zeige den Puffer an
|
||||||
|
out = img.reshape((Nx*Ny*4,)).astype(np.uint8)
|
||||||
|
os.write(1, out.tobytes())
|
||||||
|
# warte time_ms ms
|
||||||
|
time.sleep(time_ms*0.001)
|
BIN
apps/bimood
Executable file
BIN
apps/bimood
Executable file
Binary file not shown.
6
apps/c-src/99-fbdev.conf
Normal file
6
apps/c-src/99-fbdev.conf
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
Section "Device"
|
||||||
|
Identifier "myfb"
|
||||||
|
Driver "fbdev"
|
||||||
|
Option "fbdev" "/dev/fb0"
|
||||||
|
EndSection
|
||||||
|
|
@ -6,7 +6,7 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <threads.h>
|
#include <pthread.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
int Nx, Ny;
|
int Nx, Ny;
|
||||||
@ -156,7 +156,7 @@ int PFT_HandlePixelflutMessage(uint8_t *buffer, int size, int sock){
|
|||||||
return begin-(char*)buffer;
|
return begin-(char*)buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PFT_HandleClient(void* sock_){
|
void* PFT_HandleClient(void* sock_){
|
||||||
int sock = (int)(size_t)sock_;
|
int sock = (int)(size_t)sock_;
|
||||||
PFT_ClientThreadCount++;
|
PFT_ClientThreadCount++;
|
||||||
uint8_t buf[1024];
|
uint8_t buf[1024];
|
||||||
@ -172,7 +172,7 @@ int PFT_HandleClient(void* sock_){
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int PFT_RunServer(void* _){
|
void* PFT_RunServer(void* _){
|
||||||
int client_sock;
|
int client_sock;
|
||||||
socklen_t addr_len;
|
socklen_t addr_len;
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
@ -220,9 +220,9 @@ int PFT_RunServer(void* _){
|
|||||||
while(1){
|
while(1){
|
||||||
client_sock = accept(server_sock, (struct sockaddr*)&addr, &addr_len);
|
client_sock = accept(server_sock, (struct sockaddr*)&addr, &addr_len);
|
||||||
if(client_sock > 0){
|
if(client_sock > 0){
|
||||||
thrd_t thread;
|
pthread_t thread;
|
||||||
thrd_create(&thread, PFT_HandleClient, (void*)(size_t)client_sock);
|
pthread_create(&thread, NULL, PFT_HandleClient, (void*)(size_t)client_sock);
|
||||||
thrd_detach(thread);
|
pthread_detach(thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
close(server_sock);
|
close(server_sock);
|
||||||
@ -235,8 +235,8 @@ int main(int argc, char **argv) {
|
|||||||
buf = (uint8_t*)malloc(Nx*Ny*3);
|
buf = (uint8_t*)malloc(Nx*Ny*3);
|
||||||
port = atoi(argv[3]);
|
port = atoi(argv[3]);
|
||||||
if (port == 0) port = 4444;
|
if (port == 0) port = 4444;
|
||||||
thrd_t thread;
|
pthread_t thread;
|
||||||
thrd_create(&thread, PFT_RunServer, NULL);
|
pthread_create(&thread, NULL, PFT_RunServer, NULL);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
BIN
apps/cnoise
Executable file
BIN
apps/cnoise
Executable file
Binary file not shown.
61
apps/convergence.py
Executable file
61
apps/convergence.py
Executable file
@ -0,0 +1,61 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import numpy as np
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import colorsys
|
||||||
|
Nx = int(sys.argv[1])
|
||||||
|
Ny = int(sys.argv[2])
|
||||||
|
N = max(Nx, Ny)
|
||||||
|
|
||||||
|
def CpxRandNormal(min, max, N):
|
||||||
|
mu = (min+max)/2
|
||||||
|
sigma = (max-min)/2
|
||||||
|
return np.random.normal(mu, sigma, (N, N))+1.0j*np.random.normal(mu, sigma, (N, N))
|
||||||
|
def CpxRand(min, max, N):
|
||||||
|
return np.random.uniform(min, max, (N, N))+1.0j*np.random.uniform(min, max, (N, N))
|
||||||
|
|
||||||
|
A = CpxRandNormal(-100, 100, N)
|
||||||
|
B = CpxRand(-1, 1, N)*0.1
|
||||||
|
C = CpxRand(-1, 1, N)
|
||||||
|
|
||||||
|
A_ex = np.linalg.inv(np.eye(N)-B)@C
|
||||||
|
|
||||||
|
dir = -1
|
||||||
|
solve = True
|
||||||
|
t = 0
|
||||||
|
while True:
|
||||||
|
t += 0.003
|
||||||
|
if solve:
|
||||||
|
A = 0.9*A+0.1*(B@A+C)
|
||||||
|
else:
|
||||||
|
A += CpxRandNormal(0, 0.008, N)
|
||||||
|
diff = A-A_ex
|
||||||
|
#angles = (np.angle(diff)+np.pi)/(2*np.pi
|
||||||
|
#diff = np.abs(diff)
|
||||||
|
#diff = 1-diff/(1+diff)
|
||||||
|
a = np.abs(np.real(diff))
|
||||||
|
a = a/(a+1)
|
||||||
|
b = np.abs(np.imag(diff))
|
||||||
|
b = b/(b+1)
|
||||||
|
img = np.zeros((Ny, Nx, 3))
|
||||||
|
for x in range(Nx):
|
||||||
|
for y in range(Ny):
|
||||||
|
c = colorsys.hsv_to_rgb((t+0.2+a[x, y]*0.2)%1, 1, 0.5*b[x, y]+0.5)
|
||||||
|
img[y,x,:] = np.array(c)*255
|
||||||
|
#img[:,:,0] = np.minimum(np.abs(diff*255).astype(int), 255)[:Ny,:Nx]
|
||||||
|
out = img.reshape((Nx*Ny*3,)).astype(np.uint8)
|
||||||
|
#A += np.random.uniform(-0.01, 0.01, (N, N))
|
||||||
|
|
||||||
|
#print(len(out))
|
||||||
|
os.write(1, out.tobytes())
|
||||||
|
|
||||||
|
if solve and np.sum(np.abs(diff)) <= 0.001*N*N:
|
||||||
|
solve = False
|
||||||
|
elif not solve and np.sum(np.abs(diff)) >= 1*N*N:
|
||||||
|
solve = True
|
||||||
|
|
||||||
|
#os.write(2, b"frame")
|
||||||
|
#print(angles, diff)
|
||||||
|
|
||||||
|
time.sleep(0.01)
|
110
apps/digi_clock.py
Executable file
110
apps/digi_clock.py
Executable file
@ -0,0 +1,110 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import numpy as np
|
||||||
|
import pygame
|
||||||
|
from datetime import datetime
|
||||||
|
import math
|
||||||
|
|
||||||
|
# Displaygröße
|
||||||
|
Nx = int(sys.argv[1]) # Breite des Displays (z.B. 80)
|
||||||
|
Ny = int(sys.argv[2]) # Höhe des Displays (z.B. 40)
|
||||||
|
|
||||||
|
# Initialize pygame
|
||||||
|
pygame.init()
|
||||||
|
pygame.font.init()
|
||||||
|
|
||||||
|
# Pfad zur Pixelfont im Verzeichnis "apps"
|
||||||
|
font_path = os.path.join('.', 'PressStart2P-Regular.ttf') # Pfad zur Schriftart
|
||||||
|
|
||||||
|
# Schriftart mit optimierter Größe laden (z.B. 16px bis 20px)
|
||||||
|
font_size = 16 # Geeignete Schriftgröße wählen
|
||||||
|
font = pygame.font.Font(font_path, font_size)
|
||||||
|
|
||||||
|
clock = pygame.time.Clock()
|
||||||
|
time_elapsed = 0 # Zeit-Tracker für den Farbverlauf
|
||||||
|
|
||||||
|
# Feste Farbe für den Rand (Türkis)
|
||||||
|
border_color = (64, 224, 208) # RGB-Werte für Türkis
|
||||||
|
|
||||||
|
# Funktion zur Berechnung der RGB-Farbe basierend auf der Zeit und der Position des Zeichens
|
||||||
|
def calculate_color(time_elapsed, position, total_length):
|
||||||
|
"""Berechnet eine RGB-Farbe basierend auf der verstrichenen Zeit und der Position des Zeichens."""
|
||||||
|
frequency = 2 * math.pi / total_length # Frequenz für den Regenbogenverlauf
|
||||||
|
phase_shift = position * frequency # Verschiebung der Phase pro Zeichen
|
||||||
|
|
||||||
|
r = int((math.sin(time_elapsed + phase_shift) + 1) * 127.5)
|
||||||
|
g = int((math.sin(time_elapsed + phase_shift + 2 * math.pi / 3) + 1) * 127.5)
|
||||||
|
b = int((math.sin(time_elapsed + phase_shift + 4 * math.pi / 3) + 1) * 127.5)
|
||||||
|
|
||||||
|
return (r, g, b)
|
||||||
|
|
||||||
|
# Funktion zum Rendern des Rahmens
|
||||||
|
def render_border(display_surface, Nx, Ny, border_color):
|
||||||
|
"""Zeichnet den Rahmen mit einer festen Farbe."""
|
||||||
|
# Obere Linie des Rahmens
|
||||||
|
for x in range(Nx):
|
||||||
|
display_surface.set_at((x, 0), border_color) # Oben
|
||||||
|
|
||||||
|
# Rechte Linie des Rahmens
|
||||||
|
for y in range(1, Ny - 1):
|
||||||
|
display_surface.set_at((Nx - 1, y), border_color) # Rechts
|
||||||
|
|
||||||
|
# Untere Linie des Rahmens
|
||||||
|
for x in range(Nx - 1, -1, -1):
|
||||||
|
display_surface.set_at((x, Ny - 1), border_color) # Unten
|
||||||
|
|
||||||
|
# Linke Linie des Rahmens
|
||||||
|
for y in range(Ny - 2, 0, -1):
|
||||||
|
display_surface.set_at((0, y), border_color) # Links
|
||||||
|
|
||||||
|
# Funktion zum Zeichnen der Uhrzeit
|
||||||
|
def render_time(display_surface, Nx, Ny, font, current_time, time_elapsed):
|
||||||
|
"""Zeichnet die aktuelle Uhrzeit mittig auf das Display."""
|
||||||
|
# Reduzierter Zeichenabstand (85% der Schriftgröße)
|
||||||
|
character_spacing = font_size * 0.95
|
||||||
|
# Breite des gesamten Textes berechnen
|
||||||
|
text_width = len(current_time) * character_spacing
|
||||||
|
|
||||||
|
# Horizontale und vertikale Zentrierung
|
||||||
|
x_offset = (Nx - text_width) // 2
|
||||||
|
y_offset = (Ny - font_size) // 2
|
||||||
|
|
||||||
|
# Zeichnen der Uhrzeit
|
||||||
|
for i, char in enumerate(current_time):
|
||||||
|
# Berechne die Farbe für das aktuelle Zeichen
|
||||||
|
text_color = calculate_color(time_elapsed, i, len(current_time))
|
||||||
|
# Rendern des Zeichens
|
||||||
|
text = font.render(char, True, text_color)
|
||||||
|
text_rect = text.get_rect(topleft=(x_offset + i * character_spacing, y_offset))
|
||||||
|
# Zeichen auf das Display zeichnen
|
||||||
|
display_surface.blit(text, text_rect)
|
||||||
|
|
||||||
|
# Hauptprogramm zur Anzeige der Uhrzeit
|
||||||
|
while True:
|
||||||
|
# Zeit seit Beginn des Programms
|
||||||
|
time_elapsed += clock.get_time() / 1000.0 # in Sekunden
|
||||||
|
|
||||||
|
# Aktuelle Zeit abrufen
|
||||||
|
now = datetime.now()
|
||||||
|
current_time = now.strftime("%H:%M") # Uhrzeit im Format HH:MM
|
||||||
|
|
||||||
|
# Ein leeres Display für die Uhrzeit erstellen
|
||||||
|
display_surface = pygame.Surface((Nx, Ny))
|
||||||
|
display_surface.fill((0, 0, 0)) # Hintergrund schwarz
|
||||||
|
|
||||||
|
# Rahmen zeichnen
|
||||||
|
render_border(display_surface, Nx, Ny, border_color)
|
||||||
|
|
||||||
|
# Uhrzeit zeichnen (Aufruf der neuen Funktion)
|
||||||
|
render_time(display_surface, Nx, Ny, font, current_time, time_elapsed)
|
||||||
|
|
||||||
|
# Sicherstellen, dass das Bild die korrekten Dimensionen hat und der Framebuffer vollständig ist
|
||||||
|
out = pygame.surfarray.array3d(display_surface).transpose((1, 0, 2)).astype(np.uint8).tobytes()
|
||||||
|
|
||||||
|
# Framebuffer zum Server senden
|
||||||
|
os.write(1, out)
|
||||||
|
|
||||||
|
clock.tick(30) # 30 FPS für flüssigen Farbverlauf
|
||||||
|
|
101
apps/doom_fire_psx2.py
Executable file
101
apps/doom_fire_psx2.py
Executable file
@ -0,0 +1,101 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import numpy as np
|
||||||
|
import pygame
|
||||||
|
|
||||||
|
Nx = int(sys.argv[1])
|
||||||
|
Ny = int(sys.argv[2])
|
||||||
|
try:
|
||||||
|
fps = float(sys.argv[3])
|
||||||
|
except (IndexError, ValueError):
|
||||||
|
fps = 15
|
||||||
|
|
||||||
|
#fps = 15
|
||||||
|
|
||||||
|
colors = np.array([
|
||||||
|
0x0, 0x0, 0x0,
|
||||||
|
# 0x07,0x07,0x07,
|
||||||
|
# 0x1F,0x07,0x07,
|
||||||
|
# 0x2F,0x0F,0x07,
|
||||||
|
# 0x47,0x0F,0x07,
|
||||||
|
0x57,0x17,0x07,
|
||||||
|
# 0x67,0x1F,0x07,
|
||||||
|
# 0x77,0x1F,0x07,
|
||||||
|
# 0x8F,0x27,0x07,
|
||||||
|
0x9F,0x2F,0x07,
|
||||||
|
# 0xAF,0x3F,0x07,
|
||||||
|
# 0xBF,0x47,0x07,
|
||||||
|
# 0xC7,0x47,0x07,
|
||||||
|
0xDF,0x4F,0x07,
|
||||||
|
# 0xDF,0x57,0x07,
|
||||||
|
# 0xDF,0x57,0x07,
|
||||||
|
# 0xD7,0x5F,0x07,
|
||||||
|
# 0xD7,0x5F,0x07,
|
||||||
|
# 0xD7,0x67,0x0F,
|
||||||
|
# 0xCF,0x6F,0x0F,
|
||||||
|
# 0xCF,0x77,0x0F,
|
||||||
|
0xCF,0x7F,0x0F,
|
||||||
|
# 0xCF,0x87,0x17,
|
||||||
|
# 0xC7,0x87,0x17,
|
||||||
|
# 0xC7,0x8F,0x17,
|
||||||
|
# 0xC7,0x97,0x1F,
|
||||||
|
# 0xBF,0x9F,0x1F,
|
||||||
|
# 0xBF,0x9F,0x1F,
|
||||||
|
# 0xBF,0xA7,0x27,
|
||||||
|
0xBF,0xA7,0x27,
|
||||||
|
# 0xBF,0xAF,0x2F,
|
||||||
|
# 0xB7,0xAF,0x2F,
|
||||||
|
# 0xB7,0xB7,0x2F,
|
||||||
|
# 0xB7,0xB7,0x37,
|
||||||
|
# 0xCF,0xCF,0x6F,
|
||||||
|
# 0xDF,0xDF,0x9F,
|
||||||
|
# 0xEF,0xEF,0xC7,
|
||||||
|
# 0xFF,0xFF,0xFF
|
||||||
|
], dtype=np.uint8)
|
||||||
|
|
||||||
|
colors = np.reshape(colors, (colors.size//3, 3))
|
||||||
|
|
||||||
|
grid = np.zeros((Ny,Nx), dtype=np.int)
|
||||||
|
gridout = np.zeros((Ny,Nx), dtype=np.int)
|
||||||
|
grid[0,:].fill(colors.shape[0]-1)
|
||||||
|
|
||||||
|
clock = pygame.time.Clock()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def state2red(c):
|
||||||
|
return colors[c, 0]
|
||||||
|
|
||||||
|
def state2green(c):
|
||||||
|
return colors[c, 1]
|
||||||
|
|
||||||
|
def state2blue(c):
|
||||||
|
return colors[c, 2]
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
i = i + 1
|
||||||
|
rnd = np.random.randint(3, size=(Ny, Nx))
|
||||||
|
|
||||||
|
for px in range(Nx):
|
||||||
|
for py in range(1, Ny):
|
||||||
|
#grid[py, px] = max(grid[py-1, px] - 1, 0) # v1
|
||||||
|
#grid[py, px] = max(grid[py-1, px] - (rnd[py, px] & 1), 0) # v2
|
||||||
|
grid[py, (px - rnd[py, px] + 1) % Nx] = max(grid[py-1, px] - (rnd[py, px] & 1), 0) # v3
|
||||||
|
|
||||||
|
#if i % 100 == 0:
|
||||||
|
# grid[0,:].fill(0)
|
||||||
|
#elif (i - (colors.shape[0] +10)) % 100 == 0:
|
||||||
|
# grid[0,:].fill(colors.shape[0]-1)
|
||||||
|
|
||||||
|
gridup = np.flipud(np.fliplr(grid))
|
||||||
|
np.clip(grid + gridup, 0, colors.shape[0]-1, out=gridout)
|
||||||
|
|
||||||
|
#out = np.dstack((state2red(gridout), state2green(gridout), state2blue(gridout), np.full((Ny, Nx), 100, dtype=np.uint8))).astype(np.uint8).tobytes()
|
||||||
|
out = np.dstack((state2red(gridout), state2green(gridout), state2blue(gridout))).astype(np.uint8).tobytes()
|
||||||
|
|
||||||
|
os.write(1, out)
|
||||||
|
clock.tick_busy_loop(fps)
|
BIN
apps/dualmaze
Executable file
BIN
apps/dualmaze
Executable file
Binary file not shown.
68
apps/example2.py
Executable file
68
apps/example2.py
Executable file
@ -0,0 +1,68 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
|
||||||
|
# Groesse des Bildschirms bestimmen
|
||||||
|
Nx = int(sys.argv[1])
|
||||||
|
Ny = int(sys.argv[2])
|
||||||
|
|
||||||
|
# Bestimme den Parameter
|
||||||
|
time_ms = 100
|
||||||
|
try:
|
||||||
|
time_ms = int(sys.argv[3])
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Puffer fuer Pixel erstellen und mit 0 initialisieren
|
||||||
|
buffer = bytearray(b"\x00" * (4 * Nx * Ny))
|
||||||
|
|
||||||
|
curPixel = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# Zufaellige Pixel waeheln
|
||||||
|
# rot
|
||||||
|
x_r = random.randint(0, Nx-1)
|
||||||
|
y_r = random.randint(0, Ny-1)
|
||||||
|
i_r = 4*(x_r+Nx*y_r)
|
||||||
|
# gruen
|
||||||
|
x_g = random.randint(0, Nx-1)
|
||||||
|
y_g = random.randint(0, Ny-1)
|
||||||
|
i_g = 4*(x_g+Nx*y_g)
|
||||||
|
# blau
|
||||||
|
x_b = random.randint(0, Nx-1)
|
||||||
|
y_b = random.randint(0, Ny-1)
|
||||||
|
i_b = 4*(x_b+Nx*y_b)
|
||||||
|
# weiss
|
||||||
|
x_w = random.randint(0, Nx-1)
|
||||||
|
y_w = random.randint(0, Ny-1)
|
||||||
|
i_w = 4*(x_w+Nx*y_w)
|
||||||
|
|
||||||
|
# Pixel in Puffer schreiben
|
||||||
|
# rot
|
||||||
|
buffer[i_r+0] = 0xff # Rotanteil
|
||||||
|
buffer[i_r+1] = 0x00 # Gruenanteil
|
||||||
|
buffer[i_r+2] = 0x00 # Blauanteil
|
||||||
|
buffer[i_r+3] = 0x00
|
||||||
|
# gruen
|
||||||
|
buffer[i_g+0] = 0x00 # Rotanteil
|
||||||
|
buffer[i_g+1] = 0xff # Gruenanteil
|
||||||
|
buffer[i_g+2] = 0x00 # Blauanteil
|
||||||
|
buffer[i_g+3] = 0x00
|
||||||
|
# blau
|
||||||
|
buffer[i_b+0] = 0x00 # Rotanteil
|
||||||
|
buffer[i_b+1] = 0x00 # Gruenanteil
|
||||||
|
buffer[i_b+2] = 0xff # Blauanteil
|
||||||
|
buffer[i_b+3] = 0x00
|
||||||
|
# weiss
|
||||||
|
buffer[i_w+0] = 0x00 # Rotanteil
|
||||||
|
buffer[i_w+1] = 0x00 # Gruenanteil
|
||||||
|
buffer[i_w+2] = 0x00 # Blauanteil
|
||||||
|
buffer[i_w+3] = 0xff
|
||||||
|
|
||||||
|
# Zeige den Puffer an
|
||||||
|
os.write(1, buffer)
|
||||||
|
# warte time_ms ms
|
||||||
|
time.sleep(time_ms*0.001)
|
42
apps/fading_pixels.py
Executable file
42
apps/fading_pixels.py
Executable file
@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import numpy as np
|
||||||
|
import pygame
|
||||||
|
|
||||||
|
Nx = int(sys.argv[1])
|
||||||
|
Ny = int(sys.argv[2])
|
||||||
|
try:
|
||||||
|
fps = float(sys.argv[3])
|
||||||
|
except (IndexError, ValueError):
|
||||||
|
fps = 1.0
|
||||||
|
|
||||||
|
|
||||||
|
def point2idx(x,y):
|
||||||
|
return 3*(x+Nx*y)
|
||||||
|
|
||||||
|
def set_pixel_max(buffer, x, y):
|
||||||
|
idx = point2idx(x,y)
|
||||||
|
for i in range(3):
|
||||||
|
buffer[idx+i] = max(0x00, buffer[idx+i]-1)
|
||||||
|
|
||||||
|
def set_pixel_rand(buffer, x, y):
|
||||||
|
idx = point2idx(x,y)
|
||||||
|
for i in range(3):
|
||||||
|
buffer[idx+i] = np.random.randint(0x00, 0xff)
|
||||||
|
|
||||||
|
clock = pygame.time.Clock()
|
||||||
|
buffer = bytearray(b"\x00"*(3*Nx*Ny))
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
while True:
|
||||||
|
i = i + 1
|
||||||
|
for px in range(Nx):
|
||||||
|
for py in range(Ny):
|
||||||
|
set_pixel_max(buffer, px, py)
|
||||||
|
for j in range(10):
|
||||||
|
set_pixel_rand(buffer, np.random.randint(0, Nx), np.random.randint(0, Ny))
|
||||||
|
if i > 100:
|
||||||
|
os.write(1, buffer)
|
||||||
|
clock.tick_busy_loop(fps)
|
78
apps/fdtd.py
Executable file
78
apps/fdtd.py
Executable file
@ -0,0 +1,78 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# From the Meep tutorial: plotting permittivity and fields of a bent waveguide
|
||||||
|
from __future__ import division
|
||||||
|
|
||||||
|
import meep as mp
|
||||||
|
#import matplotlib.pyplot as plt
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import numpy as np
|
||||||
|
from matplotlib import cm
|
||||||
|
import pygame
|
||||||
|
|
||||||
|
#mp.quiet(True)
|
||||||
|
|
||||||
|
Nx = int(sys.argv[1])
|
||||||
|
Ny = int(sys.argv[2])
|
||||||
|
try:
|
||||||
|
fps = float(sys.argv[3])
|
||||||
|
except (IndexError, ValueError):
|
||||||
|
fps = 40
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cell = mp.Vector3(8,4,0)
|
||||||
|
geometry = [mp.Block(mp.Vector3(12,1,mp.inf),
|
||||||
|
center=mp.Vector3(0,0),
|
||||||
|
material=mp.Medium(epsilon=24))
|
||||||
|
]#,
|
||||||
|
#mp.Block(mp.Vector3(1,5,mp.inf),
|
||||||
|
# center=mp.Vector3(1,1),
|
||||||
|
# material=mp.Medium(epsilon=12))]
|
||||||
|
#pml_layers = [mp.PML(2.0)]
|
||||||
|
resolution = 10
|
||||||
|
|
||||||
|
sources = [mp.Source(mp.ContinuousSource(frequency=0.15, width=20), # wavelength=2*(11**0.5)
|
||||||
|
component=mp.Ez,
|
||||||
|
center=mp.Vector3(0,0),
|
||||||
|
size=mp.Vector3(0,1))
|
||||||
|
]
|
||||||
|
|
||||||
|
sim = mp.Simulation(cell_size=cell,
|
||||||
|
geometry=geometry,
|
||||||
|
sources=sources,
|
||||||
|
resolution=resolution)
|
||||||
|
|
||||||
|
|
||||||
|
buffer = np.empty([80,40])
|
||||||
|
clock = pygame.time.Clock()
|
||||||
|
|
||||||
|
def get_slice(sim):
|
||||||
|
val = sim.get_array(center=mp.Vector3(), size=cell, component=mp.Ez, arr=buffer)
|
||||||
|
#plt.figure()
|
||||||
|
#plt.imshow(val, interpolation='none', cmap='RdBu')
|
||||||
|
#plt.axis('off')
|
||||||
|
#plt.show()
|
||||||
|
out = (val - np.amin(val)) / (np.amax(val) - np.amin(val))
|
||||||
|
out = np.transpose(out) # or switch x and y
|
||||||
|
out = out.ravel()
|
||||||
|
out = 250*cm.RdBu(out)
|
||||||
|
out = out[:,0:3]
|
||||||
|
out = out.ravel()
|
||||||
|
out = out.astype(np.uint8).tobytes()
|
||||||
|
os.write(1, out)
|
||||||
|
clock.tick_busy_loop(fps)
|
||||||
|
|
||||||
|
def plot_dielectric(sim):
|
||||||
|
val = sim.get_array(center=mp.Vector3(), size=cell, component=mp.Dielectric, arr=buffer)
|
||||||
|
#plt.figure()
|
||||||
|
#plt.imshow(val, interpolation='none', cmap='RdBu')
|
||||||
|
#plt.axis('off')
|
||||||
|
#plt.show()
|
||||||
|
#print()
|
||||||
|
|
||||||
|
|
||||||
|
#sim.run(mp.at_beginning(plot_dielectric), mp.at_every(0.6, get_slice), until=200)
|
||||||
|
sim.run(mp.at_every(0.00001, get_slice), until=1000)
|
109
apps/fibonacci-clock.py
Executable file
109
apps/fibonacci-clock.py
Executable file
@ -0,0 +1,109 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from time import strftime
|
||||||
|
import time
|
||||||
|
from math import ceil
|
||||||
|
|
||||||
|
#Nx = int(sys.argv[1])
|
||||||
|
#Ny = int(sys.argv[2])
|
||||||
|
#param = sys.argv[3]
|
||||||
|
Nx = 80
|
||||||
|
Ny = 40
|
||||||
|
|
||||||
|
|
||||||
|
buffer = bytearray(b"\x00"*(3*Nx*Ny))
|
||||||
|
|
||||||
|
def SetPixel(x, y, r, g, b):
|
||||||
|
idx = 3*(x+Nx*y)
|
||||||
|
buffer[idx+0] = r
|
||||||
|
buffer[idx+1] = g
|
||||||
|
buffer[idx+2] = b
|
||||||
|
|
||||||
|
def two(r,g,b):
|
||||||
|
for y in range (1,15):
|
||||||
|
for x in range (9,23):
|
||||||
|
SetPixel(x,y,r,g,b)
|
||||||
|
|
||||||
|
def three(r,g,b):
|
||||||
|
for y in range (17,39):
|
||||||
|
for x in range (9,31):
|
||||||
|
SetPixel(x,y,r,g,b)
|
||||||
|
|
||||||
|
def one(r,g,b):
|
||||||
|
for y in range (1,7):
|
||||||
|
for x in range (25,31):
|
||||||
|
SetPixel(x,y,r,g,b)
|
||||||
|
|
||||||
|
def one_one(r,g,b):
|
||||||
|
for y in range (9,15):
|
||||||
|
for x in range (25,31):
|
||||||
|
SetPixel(x,y,r,g,b)
|
||||||
|
|
||||||
|
def five(r,g,b):
|
||||||
|
for y in range (1,39):
|
||||||
|
for x in range (33,71):
|
||||||
|
SetPixel(x,y,r,g,b)
|
||||||
|
|
||||||
|
def and_gate(hourcol, mincol):
|
||||||
|
if hourcol and mincol:
|
||||||
|
return 'y'
|
||||||
|
if hourcol:
|
||||||
|
return 'r'
|
||||||
|
if mincol:
|
||||||
|
return 'g'
|
||||||
|
if not hourcol and not mincol:
|
||||||
|
return 'b'
|
||||||
|
|
||||||
|
def get_colsquares(hour, minute):
|
||||||
|
coldict = {
|
||||||
|
0: [0, 0, 0, 0, 0],
|
||||||
|
1: [0, 0, 0, 0, 1],
|
||||||
|
2: [0, 0, 0, 1, 1],
|
||||||
|
3: [0, 0, 1, 1, 0],
|
||||||
|
4: [0, 0, 1, 1, 1],
|
||||||
|
5: [0, 1, 1, 0, 0],
|
||||||
|
6: [0, 1, 1, 1, 0],
|
||||||
|
7: [0, 1, 1, 1, 1],
|
||||||
|
8: [1, 1, 0, 0, 0],
|
||||||
|
9: [1, 1, 0, 1, 0],
|
||||||
|
10: [1, 1, 1, 0, 0],
|
||||||
|
11: [1, 1, 1, 1, 0],
|
||||||
|
12: [1, 1, 1, 1, 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
result = []
|
||||||
|
while i < 5:
|
||||||
|
result.append( and_gate(coldict[hour][i], coldict[minute][i]) )
|
||||||
|
i += 1
|
||||||
|
return result
|
||||||
|
|
||||||
|
def gettime():
|
||||||
|
hour = int(strftime('%I'))
|
||||||
|
minute12 = ceil(int(strftime('%M'))/5.0)
|
||||||
|
minute = int(strftime('%M'))
|
||||||
|
|
||||||
|
return hour, minute12, minute
|
||||||
|
|
||||||
|
while True:
|
||||||
|
rgbw = {
|
||||||
|
'r' : (255, 0, 0),
|
||||||
|
'g' : (0, 255, 0),
|
||||||
|
'b' : (0, 0, 255),
|
||||||
|
'y' : (255, 255, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
hour, minute12, minute = gettime()
|
||||||
|
colsq = get_colsquares(hour, minute12)
|
||||||
|
|
||||||
|
|
||||||
|
five(rgbw[colsq[0]][0],rgbw[colsq[0]][1],rgbw[colsq[0]][2])
|
||||||
|
three(rgbw[colsq[1]][0],rgbw[colsq[1]][1],rgbw[colsq[1]][2])
|
||||||
|
two(rgbw[colsq[2]][0],rgbw[colsq[2]][1],rgbw[colsq[2]][2])
|
||||||
|
one_one(rgbw[colsq[3]][0],rgbw[colsq[3]][1],rgbw[colsq[3]][2])
|
||||||
|
one(rgbw[colsq[4]][0],rgbw[colsq[4]][1],rgbw[colsq[4]][2])
|
||||||
|
|
||||||
|
os.write(1, buffer)
|
||||||
|
time.sleep(1)
|
||||||
|
#grid()
|
BIN
apps/flicker
Executable file
BIN
apps/flicker
Executable file
Binary file not shown.
@ -13,7 +13,7 @@ Ny = int(sys.argv[2])
|
|||||||
PixelsPerFrame = 20
|
PixelsPerFrame = 20
|
||||||
NumLines = 7
|
NumLines = 7
|
||||||
LineGenerationRate = 0.2
|
LineGenerationRate = 0.2
|
||||||
time_ms = 10
|
time_ms = 20
|
||||||
try:
|
try:
|
||||||
time_ms = int(sys.argv[3])
|
time_ms = int(sys.argv[3])
|
||||||
except:
|
except:
|
||||||
|
81
apps/pendlum.py
Executable file
81
apps/pendlum.py
Executable file
@ -0,0 +1,81 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import numpy as np
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import colorsys
|
||||||
|
|
||||||
|
Nx = int(sys.argv[1])
|
||||||
|
Ny = int(sys.argv[2])
|
||||||
|
N = 100
|
||||||
|
iterations = 2
|
||||||
|
try:
|
||||||
|
N = int(sys.argv[3])
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def rk4(f, x, dt):
|
||||||
|
k1 = f(x)
|
||||||
|
k2 = f(x+k1*dt/2)
|
||||||
|
k3 = f(x+k2*dt/2)
|
||||||
|
k4 = f(x+k3*dt)
|
||||||
|
return x+dt*(k1+2*k2+2*k3+k4)/6.0
|
||||||
|
dt = 0.08
|
||||||
|
m = 1
|
||||||
|
l = 1
|
||||||
|
g = 1
|
||||||
|
|
||||||
|
def sq(x): return x*x
|
||||||
|
|
||||||
|
def rhs(x):
|
||||||
|
phi1 = x[0]
|
||||||
|
p1 = x[1]
|
||||||
|
phi2 = x[2]
|
||||||
|
p2 = x[3]
|
||||||
|
|
||||||
|
dphi1 = 6/(m*l*l)*(2*p1-3*np.cos(phi1-phi2)*p2)/(16-9*sq(np.cos(phi1-phi2)))
|
||||||
|
dphi2 = 6/(m*l*l)*(8*p2-3*np.cos(phi1-phi2)*p1)/(16-9*sq(np.cos(phi1-phi2)))
|
||||||
|
|
||||||
|
dp1 = -0.5*m*l*l*(dphi1*dphi2*np.sin(phi1-phi2)+3*g/l*np.sin(phi1))
|
||||||
|
dp2 = -0.5*m*l*l*(-dphi1*dphi2*np.sin(phi1-phi2)+g/l*np.sin(phi2))
|
||||||
|
return np.array([dphi1, dp1, dphi2, dp2])
|
||||||
|
|
||||||
|
s = np.array([np.pi+0.1, 0, np.pi-0.1, 0])
|
||||||
|
|
||||||
|
angles = np.linspace(np.pi, np.pi/3, N)+np.random.uniform(-np.pi/6/N, np.pi/6/N, N)
|
||||||
|
angles = np.random.uniform(-np.pi/6, np.pi/3, N)+np.pi
|
||||||
|
states = [np.array([np.pi, 0, a, 0]) for a in angles]
|
||||||
|
|
||||||
|
def clamp(x, min_, max_):
|
||||||
|
return max(min_, min(max_, x))
|
||||||
|
|
||||||
|
buffer = bytearray(b"\x00"*Nx*Ny*3)
|
||||||
|
|
||||||
|
def setpixel(x, y, r, g, b):
|
||||||
|
xi = int(Nx*(x+1.2)/2.4)
|
||||||
|
yi = int(Ny*(y+1.2)/2.4)
|
||||||
|
if xi < 0 or xi >= Nx or yi < 0 or yi >= Ny:
|
||||||
|
return
|
||||||
|
idx = xi+Nx*yi
|
||||||
|
buffer[3*idx+0] = r
|
||||||
|
buffer[3*idx+1] = g
|
||||||
|
buffer[3*idx+2] = b
|
||||||
|
timestep = 0
|
||||||
|
timefac = 0.03
|
||||||
|
while True:
|
||||||
|
timestep += 1
|
||||||
|
for s, i in zip(states, range(len(states))):
|
||||||
|
phi1 = s[0]
|
||||||
|
phi2 = s[2]
|
||||||
|
x1 = np.sin(phi1)*l
|
||||||
|
y1 = np.cos(phi1)*l
|
||||||
|
x2 = np.sin(phi2)*l+x1
|
||||||
|
y2 = np.cos(phi2)*l+y1
|
||||||
|
h = 0.2*i/N+0*timestep*timefac/iterations
|
||||||
|
r, g, b = colorsys.hsv_to_rgb(h%1, 1, 1)
|
||||||
|
setpixel(x2, y2, int(r*255), int(g*255), int(b*255))
|
||||||
|
states[i] = rk4(rhs, s, dt/iterations)
|
||||||
|
if timestep > 10*iterations and timestep % iterations == 0:
|
||||||
|
#time.sleep(0.01)
|
||||||
|
os.write(1, buffer)
|
||||||
|
|
BIN
apps/pixelflut
Executable file
BIN
apps/pixelflut
Executable file
Binary file not shown.
33
apps/plane_wave.py
Executable file
33
apps/plane_wave.py
Executable file
@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import numpy as np
|
||||||
|
import pygame
|
||||||
|
|
||||||
|
Nx = int(sys.argv[1])
|
||||||
|
Ny = int(sys.argv[2])
|
||||||
|
try:
|
||||||
|
frequency = float(sys.argv[3])
|
||||||
|
except (IndexError, ValueError):
|
||||||
|
frequency = 5
|
||||||
|
|
||||||
|
fps = 30
|
||||||
|
amplitude = 105
|
||||||
|
offset = 150
|
||||||
|
t = 0
|
||||||
|
|
||||||
|
x,y = np.meshgrid(np.linspace(0, 2*np.pi * min(1, Nx/Ny), Nx), np.linspace(0, 2*np.pi * min(1, Ny/Nx), Ny))
|
||||||
|
kx = x + y
|
||||||
|
|
||||||
|
clock = pygame.time.Clock()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
t = t + 1e-3
|
||||||
|
R = amplitude * np.sin(kx - 2 * np.pi * frequency * t) + offset
|
||||||
|
G = amplitude * np.sin(kx - 2 * np.pi * frequency * t + 2*np.pi/3) + offset
|
||||||
|
B = amplitude * np.sin(kx - 2 * np.pi * frequency * t + 4*np.pi/3) + offset
|
||||||
|
|
||||||
|
out = np.dstack((R,G,B)).astype(np.uint8).tobytes()
|
||||||
|
os.write(1, out)
|
||||||
|
clock.tick_busy_loop(fps)
|
BIN
apps/predprey
Executable file
BIN
apps/predprey
Executable file
Binary file not shown.
82
apps/quadratic.py
Executable file
82
apps/quadratic.py
Executable file
@ -0,0 +1,82 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import colorsys
|
||||||
|
|
||||||
|
Nx = int(sys.argv[1])
|
||||||
|
Ny = int(sys.argv[2])
|
||||||
|
iterations = 10
|
||||||
|
|
||||||
|
class Slider:
|
||||||
|
def __init__(self, lo, hi, step, pos=0):
|
||||||
|
self.lo = lo
|
||||||
|
self.hi = hi
|
||||||
|
self.stepSize = step
|
||||||
|
self.pos = pos
|
||||||
|
self.dir = 1
|
||||||
|
|
||||||
|
def step(self):
|
||||||
|
if self.pos >= self.hi:
|
||||||
|
self.dir = -1
|
||||||
|
elif self.pos <= self.lo:
|
||||||
|
self.dir = 1
|
||||||
|
|
||||||
|
self.pos += self.dir * self.stepSize
|
||||||
|
return self.pos
|
||||||
|
|
||||||
|
Sa = Slider(-10, 10, 0.1/iterations, np.random.uniform(-2, 2))
|
||||||
|
Sb = Slider(-10, 10, 0.2/iterations, np.random.uniform(-2, 2))
|
||||||
|
Sc = Slider(-10, 10, 0.2/iterations, np.random.uniform(-2, 2))
|
||||||
|
Sd = Slider(-10, 10, 0.1/iterations, np.random.uniform(-2, 2))
|
||||||
|
Se = Slider(-10, 10, 0.2/iterations, np.random.uniform(-2, 2))
|
||||||
|
Sf = Slider(0, 6, 0.05/iterations, 0)
|
||||||
|
|
||||||
|
x, y = np.meshgrid(
|
||||||
|
np.linspace(-4, 4, Nx),
|
||||||
|
np.linspace(-4, 4, Ny))
|
||||||
|
|
||||||
|
color = 1.0
|
||||||
|
|
||||||
|
img = np.zeros( [Ny, Nx, 3] )
|
||||||
|
|
||||||
|
cr = 1.0
|
||||||
|
cg = 0.5
|
||||||
|
cb = 0.25
|
||||||
|
|
||||||
|
step = 0
|
||||||
|
while True:
|
||||||
|
a = Sa.step()
|
||||||
|
b = Sb.step()
|
||||||
|
c = Sc.step()
|
||||||
|
d = Sd.step()
|
||||||
|
e = Se.step()
|
||||||
|
f = Sf.step()
|
||||||
|
|
||||||
|
Sa.pos += b*0.001
|
||||||
|
Sb.pos += c*0.001
|
||||||
|
Sc.pos += d*0.001
|
||||||
|
Sd.pos += e*0.001
|
||||||
|
Se.pos += f*0.001
|
||||||
|
Sf.pos += a*0.001
|
||||||
|
|
||||||
|
curve = (np.abs(a*x**2 + b*x*y + c*y**2 + d*x + e*y + f) <= 1.5)*1
|
||||||
|
|
||||||
|
cr, cg, cb = colorsys.hsv_to_rgb(color, 1, 1)
|
||||||
|
|
||||||
|
img[:,:,0] = np.where(curve > 0, curve*cr*255, img[:,:,0])
|
||||||
|
img[:,:,1] = np.where(curve > 0, curve*cg*255, img[:,:,1])
|
||||||
|
img[:,:,2] = np.where(curve > 0, curve*cb*255, img[:,:,2])
|
||||||
|
|
||||||
|
step += 1
|
||||||
|
if step % iterations == 0:
|
||||||
|
out = img.reshape((Nx*Ny*3,)).astype(np.uint8)
|
||||||
|
os.write(1, out.tobytes())
|
||||||
|
time.sleep(0.01)
|
||||||
|
|
||||||
|
color = (color+0.01/iterations ) % 1
|
||||||
|
|
||||||
|
|
||||||
|
|
71
apps/rps.py
Executable file
71
apps/rps.py
Executable file
@ -0,0 +1,71 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import numpy as np
|
||||||
|
import pygame
|
||||||
|
|
||||||
|
Nx = int(sys.argv[1])
|
||||||
|
Ny = int(sys.argv[2])
|
||||||
|
try:
|
||||||
|
frequency = float(sys.argv[3])
|
||||||
|
except (IndexError, ValueError):
|
||||||
|
frequency = 5
|
||||||
|
|
||||||
|
fps = 10
|
||||||
|
|
||||||
|
nstates = 5
|
||||||
|
colors = np.linspace(0, 255, nstates, dtype=np.uint8)
|
||||||
|
|
||||||
|
red = np.random.choice(colors, size=(Ny,Nx))
|
||||||
|
green = np.zeros((Ny, Nx))
|
||||||
|
blue = np.zeros((Ny, Nx))
|
||||||
|
grid = np.dstack((red, green, blue)).astype(np.uint8)
|
||||||
|
|
||||||
|
|
||||||
|
clock = pygame.time.Clock()
|
||||||
|
|
||||||
|
def clamp(value, min, max):
|
||||||
|
if value > max:
|
||||||
|
return max
|
||||||
|
elif value < min:
|
||||||
|
return min
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
while True:
|
||||||
|
|
||||||
|
for px in range(Nx):
|
||||||
|
for py in range(Ny):
|
||||||
|
statecount = np.zeros(nstates, dtype=np.uint8)
|
||||||
|
for ix in [-1, 0, 1]:
|
||||||
|
for iy in [-1, 0, 1]:
|
||||||
|
|
||||||
|
if (px+ix) >= Nx or (px+ix) <= 0 or (py+iy) >= Ny or (py+iy) <= 0:
|
||||||
|
i = 0
|
||||||
|
else:
|
||||||
|
i = int(grid[py + iy, px + ix, 0] / colors[1])
|
||||||
|
|
||||||
|
#i = int(grid[(px + ix) % Nx, (py + iy) % Ny, 0] / colors[1])
|
||||||
|
#i = int(grid[max(min(px + ix, Nx-1), 0), max(min(py + iy, Ny-1), 0), 0] / colors[1])
|
||||||
|
#print(px, py, max(min(px + ix, Nx-1), 0), max(min(py + iy, Ny-1), 0), file=sys.stderr)
|
||||||
|
statecount[i] = statecount[i] + 1
|
||||||
|
|
||||||
|
i = int(grid[py, px, 0] / colors[1])
|
||||||
|
i_successor = (i+1) % colors.size
|
||||||
|
|
||||||
|
if statecount[i_successor] >= 2: #(2-np.random.randint(0, 2)):
|
||||||
|
grid[py, px, 0] = colors[i_successor]
|
||||||
|
|
||||||
|
# for i in range(Nx*Ny):
|
||||||
|
# c1 = clamp(i % Nx - 1, 0, Nx - 1)
|
||||||
|
# c2 = clamp(int(i / Nx) - 1, 0, Ny - 1)
|
||||||
|
# neighbourhood = grid[c1:(c1+3), c2:(c2+3), 0]
|
||||||
|
# #print(neighbourhood.size, file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
out = grid.tobytes()
|
||||||
|
os.write(1, out)
|
||||||
|
clock.tick_busy_loop(fps)
|
67
apps/sinic.py
Executable file
67
apps/sinic.py
Executable file
@ -0,0 +1,67 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import colorsys
|
||||||
|
|
||||||
|
Nx = int(sys.argv[1])
|
||||||
|
Ny = int(sys.argv[2])
|
||||||
|
iterations = 2
|
||||||
|
|
||||||
|
class Slider:
|
||||||
|
def __init__(self, lo, hi, step, pos=0):
|
||||||
|
self.lo = lo
|
||||||
|
self.hi = hi
|
||||||
|
self.stepSize = step
|
||||||
|
self.pos = pos
|
||||||
|
self.dir = 1
|
||||||
|
|
||||||
|
def step(self):
|
||||||
|
if self.pos >= self.hi:
|
||||||
|
self.dir = -1
|
||||||
|
elif self.pos <= self.lo:
|
||||||
|
self.dir = 1
|
||||||
|
|
||||||
|
self.pos += self.dir * self.stepSize
|
||||||
|
return self.pos
|
||||||
|
|
||||||
|
x, y = np.meshgrid(
|
||||||
|
np.linspace(-2, 2, Nx),
|
||||||
|
np.linspace(-2, 2, Ny))
|
||||||
|
|
||||||
|
img = np.zeros( [Ny, Nx, 3] )
|
||||||
|
|
||||||
|
Sa = Slider(-3, 3, 0.01, np.random.uniform(-1, 1))
|
||||||
|
Sb = Slider(-3, 3, 0.01, np.random.uniform(-1, 1))
|
||||||
|
Sc = Slider(0, 1, 0.005, np.random.uniform(-1, 1))
|
||||||
|
|
||||||
|
Scolor = Slider(0, 1, 0.001/iterations, 0)
|
||||||
|
|
||||||
|
step = 0
|
||||||
|
while True:
|
||||||
|
a = Sa.step()
|
||||||
|
b = Sb.step()
|
||||||
|
c = Sc.step()
|
||||||
|
|
||||||
|
Sa.pos += b*0.001
|
||||||
|
Sb.pos += c*0.001
|
||||||
|
Sc.pos += a*0.001
|
||||||
|
|
||||||
|
curve = (np.clip(np.sin(c) + np.sin(np.sin(a*x) + np.cos(b*y)) - np.cos(np.sin(b*x*y) + np.cos(b*x)), 0, 1))
|
||||||
|
|
||||||
|
cr, cg, cb = colorsys.hsv_to_rgb(Scolor.step(), 1, 1)
|
||||||
|
|
||||||
|
img[:,:,0] = curve*cr*255
|
||||||
|
img[:,:,1] = curve*cg*255
|
||||||
|
img[:,:,2] = curve*cb*255
|
||||||
|
|
||||||
|
step += 1
|
||||||
|
if step % iterations == 0:
|
||||||
|
out = img.reshape((Nx*Ny*3,)).astype(np.uint8)
|
||||||
|
os.write(1, out.tobytes())
|
||||||
|
time.sleep(0.01)
|
||||||
|
|
||||||
|
|
||||||
|
|
68
apps/sinic2.py
Executable file
68
apps/sinic2.py
Executable file
@ -0,0 +1,68 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import colorsys
|
||||||
|
|
||||||
|
Nx = int(sys.argv[1])
|
||||||
|
Ny = int(sys.argv[2])
|
||||||
|
iterations = 2
|
||||||
|
|
||||||
|
class Slider:
|
||||||
|
def __init__(self, lo, hi, step, pos=0):
|
||||||
|
self.lo = lo
|
||||||
|
self.hi = hi
|
||||||
|
self.stepSize = step
|
||||||
|
self.pos = pos
|
||||||
|
self.dir = 1
|
||||||
|
|
||||||
|
def step(self):
|
||||||
|
if self.pos >= self.hi:
|
||||||
|
self.dir = -1
|
||||||
|
elif self.pos <= self.lo:
|
||||||
|
self.dir = 1
|
||||||
|
|
||||||
|
self.pos += self.dir * self.stepSize
|
||||||
|
return self.pos
|
||||||
|
|
||||||
|
x, y = np.meshgrid(
|
||||||
|
np.linspace(-2, 2, Nx),
|
||||||
|
np.linspace(-2, 2, Ny))
|
||||||
|
|
||||||
|
img = np.zeros( [Ny, Nx, 3] )
|
||||||
|
|
||||||
|
Sa = Slider(-3, 3, 0.01, np.random.uniform(-1, 1))
|
||||||
|
Sb = Slider(-3, 3, 0.01, np.random.uniform(-1, 1))
|
||||||
|
Sc = Slider(0, 1, 0.005, np.random.uniform(-1, 1))
|
||||||
|
|
||||||
|
Scolor = Slider(0, 1, 0.001/iterations, 0)
|
||||||
|
|
||||||
|
|
||||||
|
step = 0
|
||||||
|
while True:
|
||||||
|
a = Sa.step()
|
||||||
|
b = Sb.step()
|
||||||
|
c = Sc.step()
|
||||||
|
|
||||||
|
Sa.pos += b*0.001
|
||||||
|
Sb.pos += c*0.001
|
||||||
|
Sc.pos += a*0.001
|
||||||
|
|
||||||
|
curve = np.clip(c+np.sin(a*x*x-b*y*y)-np.sin(a*x+b*y)-np.cos(a*b*x*y), 0, 1)
|
||||||
|
|
||||||
|
cr, cg, cb = colorsys.hsv_to_rgb(Scolor.step(), 1, 1)
|
||||||
|
|
||||||
|
img[:,:,0] = curve*cr*255
|
||||||
|
img[:,:,1] = curve*cg*255
|
||||||
|
img[:,:,2] = curve*cb*255
|
||||||
|
|
||||||
|
step += 1
|
||||||
|
if step % iterations == 0:
|
||||||
|
out = img.reshape((Nx*Ny*3,)).astype(np.uint8)
|
||||||
|
os.write(1, out.tobytes())
|
||||||
|
time.sleep(0.01)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -2,24 +2,33 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
Nx = int(sys.argv[1])
|
Nx = int(sys.argv[1])
|
||||||
Ny = int(sys.argv[2])
|
Ny = int(sys.argv[2])
|
||||||
|
|
||||||
param = 10
|
p1 = 20
|
||||||
|
p2 = 10
|
||||||
try:
|
try:
|
||||||
param = int(sys.argv[3])
|
param = sys.argv[3]
|
||||||
|
p = param.split(",")
|
||||||
|
p1 = int(p[0])
|
||||||
|
p2 = int(p[1])
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
buffera = bytearray(b"\x00" * (3 * Nx * Ny))
|
buffera = bytearray(b"\x00" * (3 * Nx * Ny))
|
||||||
bufferb = bytearray(b"\xFF" * (3 * Nx * Ny))
|
bufferb = bytearray(b"\xFF" * (3 * Nx * Ny))
|
||||||
|
cnt = 0
|
||||||
|
|
||||||
|
os.write(2,b"Usage: Parameter: \"20,10\"\n -> updates every 20*0.01s, every 10th frame is white")
|
||||||
while True:
|
while True:
|
||||||
|
|
||||||
os.write(1, buffera)
|
os.write(1, buffera)
|
||||||
time.sleep(param*0.01)
|
time.sleep(p1*0.01)
|
||||||
os.write(1, bufferb)
|
if cnt == p2-1:
|
||||||
time.sleep(param*0.01)
|
cnt = 0
|
||||||
|
os.write(1, bufferb)
|
||||||
|
time.sleep(p1*0.01)
|
||||||
|
cnt += 1
|
||||||
|
63
apps/swifthohenberg.py
Executable file
63
apps/swifthohenberg.py
Executable file
@ -0,0 +1,63 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import numpy as np
|
||||||
|
Nx = int(sys.argv[1])
|
||||||
|
Ny = int(sys.argv[2])
|
||||||
|
param = sys.argv[3]
|
||||||
|
|
||||||
|
buffer = bytearray(b" "*(3*Nx*Ny))
|
||||||
|
|
||||||
|
|
||||||
|
Lx = 32 #128 #Physikalische Laenge des Gebietes in x-Richtung
|
||||||
|
Ly =32 #128#Physikalische Laenge des Gebietes in y-Richtung
|
||||||
|
|
||||||
|
x, y = np.meshgrid(np.arange(Nx)* Lx/Nx, np.arange(Ny)* Ly/Ny) #x-Array
|
||||||
|
kx, ky = np.meshgrid(np.fft.fftfreq(Nx,Lx/(Nx*2.0*np.pi)), np.fft.fftfreq(Ny,Ly/(Ny*2.0*np.pi)))
|
||||||
|
ksq = kx*kx + ky*ky
|
||||||
|
|
||||||
|
c = 0.0+(np.random.random((Ny,Nx))-0.5)*0.1
|
||||||
|
eps=0.3
|
||||||
|
delta=0.0
|
||||||
|
#eps = 0.1
|
||||||
|
#delta = 1.0
|
||||||
|
|
||||||
|
t = 0.0
|
||||||
|
dt = 0.01
|
||||||
|
T_End = 300000
|
||||||
|
N_t = int(T_End / dt)
|
||||||
|
|
||||||
|
plotEveryNth = 100
|
||||||
|
ck = np.fft.fft2(c) #FFT(c)
|
||||||
|
# Lineare Terme
|
||||||
|
def rhs_lin(ksq):
|
||||||
|
result=eps-(1.0-ksq)**2
|
||||||
|
return result
|
||||||
|
|
||||||
|
Eu=1.0/(1.0-dt*rhs_lin(ksq))
|
||||||
|
i = 0
|
||||||
|
def lerp_sat(a, t, b):
|
||||||
|
v = (1-t)*a+t*b
|
||||||
|
v = int(v)
|
||||||
|
if v < 0:
|
||||||
|
v = 0
|
||||||
|
if v > 255:
|
||||||
|
v = 255
|
||||||
|
return v
|
||||||
|
|
||||||
|
while True:
|
||||||
|
i+= 1
|
||||||
|
ck=Eu*(ck+dt*(delta*np.fft.fft2(np.fft.ifft2(ck)**2)-np.fft.fft2(np.fft.ifft2(ck)**3)))
|
||||||
|
c=np.fft.ifft2(ck)
|
||||||
|
eps = 0.1+0.2*np.cos(i/10000)
|
||||||
|
delta = np.sin(i/10000)
|
||||||
|
if(i % plotEveryNth == 0):
|
||||||
|
myc = c.real
|
||||||
|
myc = (myc-np.min(myc))/(np.max(myc)-np.min(myc))
|
||||||
|
for px in range(Nx):
|
||||||
|
for py in range(Ny):
|
||||||
|
idx = 3*(px+Nx*py)
|
||||||
|
buffer[idx+0] = lerp_sat(0xff, myc[py,px], 0x00)
|
||||||
|
buffer[idx+1] = lerp_sat(0x00, myc[py,px], 0xff)
|
||||||
|
buffer[idx+2] = 0
|
||||||
|
os.write(1, buffer)
|
111
apps/textscroll.py
Executable file
111
apps/textscroll.py
Executable file
@ -0,0 +1,111 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import pygame
|
||||||
|
import paho.mqtt.client as mqtt
|
||||||
|
import random
|
||||||
|
|
||||||
|
class ScrollingTextDisplay:
|
||||||
|
def __init__(self, width: int, height: int):
|
||||||
|
pygame.init()
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
self.screen = pygame.Surface((width, height))
|
||||||
|
self.clock = pygame.time.Clock()
|
||||||
|
|
||||||
|
# Liste der Standardtexte
|
||||||
|
self.default_texts = [
|
||||||
|
"Text per MQTT an: mqtt.chaospott.de Topic: test/topic ", # Hardcodierte IP-Adresse
|
||||||
|
"Willkommen beim MQTT-Display!",
|
||||||
|
"Aktuelle Nachrichten hier empfangen.",
|
||||||
|
"MQTT-Server verbindet...",
|
||||||
|
"Textnachricht anzeigen...",
|
||||||
|
]
|
||||||
|
self.error_text = "MQTT-Server nicht erreichbar!"
|
||||||
|
self.text = self.get_random_default_text() # Zufälligen Standardtext auswählen
|
||||||
|
self.text_x = width
|
||||||
|
|
||||||
|
# Schriftart 'PressStart2P-Regular' aus dem Verzeichnis 'apps' laden
|
||||||
|
self.font = pygame.font.Font("PressStart2P-Regular.ttf", 16) # Schriftgröße anpassen
|
||||||
|
|
||||||
|
# Fest codierte MQTT-Einstellungen
|
||||||
|
self.mqtt_broker = "mqtt.chaospott.de" # Beispiel IP-Adresse des MQTT-Brokers
|
||||||
|
self.topic = "test/topic" # Beispiel Topic
|
||||||
|
self.mqtt_client = mqtt.Client()
|
||||||
|
self.mqtt_client.on_connect = self.on_connect
|
||||||
|
self.mqtt_client.on_message = self.on_message
|
||||||
|
self.mqtt_client.on_disconnect = self.on_disconnect
|
||||||
|
|
||||||
|
# Versuche die Verbindung aufzubauen
|
||||||
|
self.connect_to_mqtt()
|
||||||
|
|
||||||
|
# Zähler für die angezeigte Nachricht
|
||||||
|
self.message_display_count = 0
|
||||||
|
|
||||||
|
def get_random_default_text(self):
|
||||||
|
"""Wählt zufällig einen Standardtext aus der Liste aus."""
|
||||||
|
return random.choice(self.default_texts)
|
||||||
|
|
||||||
|
def connect_to_mqtt(self):
|
||||||
|
try:
|
||||||
|
self.mqtt_client.connect(self.mqtt_broker)
|
||||||
|
self.mqtt_client.loop_start()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Verbindungsfehler zu MQTT-Server: {e}")
|
||||||
|
self.text = self.error_text # Setze Fehlertext für die Anzeige
|
||||||
|
self.text_x = self.width # Text von rechts starten
|
||||||
|
|
||||||
|
def on_connect(self, client, userdata, flags, rc):
|
||||||
|
if rc == 0:
|
||||||
|
client.subscribe(self.topic)
|
||||||
|
self.text = self.get_random_default_text() # Setze einen zufälligen Standardtext
|
||||||
|
else:
|
||||||
|
print(f"Fehler beim Verbinden mit MQTT: Code {rc}")
|
||||||
|
self.text = self.error_text
|
||||||
|
|
||||||
|
def on_disconnect(self, client, userdata, rc):
|
||||||
|
if rc != 0:
|
||||||
|
print("Verbindung zum MQTT-Server verloren. Erneuter Verbindungsversuch...")
|
||||||
|
self.text = self.error_text
|
||||||
|
self.text_x = self.width # Fehlertext von rechts starten
|
||||||
|
# Erneut die Verbindung aufbauen
|
||||||
|
self.connect_to_mqtt()
|
||||||
|
|
||||||
|
def on_message(self, client, userdata, msg):
|
||||||
|
self.text = msg.payload.decode("utf-8") if msg.payload.decode("utf-8") else self.get_random_default_text()
|
||||||
|
self.text_x = self.width # Text von rechts starten
|
||||||
|
self.message_display_count = 3 # Setze den Zähler auf 3
|
||||||
|
|
||||||
|
def draw_text(self):
|
||||||
|
self.screen.fill((0, 0, 0)) # Bildschirm leeren
|
||||||
|
text_surface = self.font.render(self.text, True, (255, 255, 255))
|
||||||
|
text_width = text_surface.get_width() # Breite des Textes ermitteln
|
||||||
|
text_y = (self.height - text_surface.get_height()) // 2 # Y-Position zentrieren
|
||||||
|
self.screen.blit(text_surface, (self.text_x, text_y)) # Text zentriert zeichnen
|
||||||
|
|
||||||
|
def display(self):
|
||||||
|
while True:
|
||||||
|
text_surface = self.font.render(self.text, True, (255, 255, 255))
|
||||||
|
text_width = text_surface.get_width() # Breite des Textes ermitteln
|
||||||
|
|
||||||
|
self.text_x -= 1 # Text nach links verschieben
|
||||||
|
if self.text_x < -text_width: # Wenn der Text komplett verschwunden ist
|
||||||
|
self.text_x = self.width # Text von rechts neu starten
|
||||||
|
# Falls der Text leer ist, setze Standardtext oder Fehlertext
|
||||||
|
if self.message_display_count > 0:
|
||||||
|
self.message_display_count -= 1
|
||||||
|
else:
|
||||||
|
self.text = self.get_random_default_text() # Setze zurück auf zufälligen Standardtext
|
||||||
|
|
||||||
|
self.draw_text()
|
||||||
|
os.write(1, pygame.image.tostring(self.screen, "RGB"))
|
||||||
|
self.clock.tick(30)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
Nx = int(sys.argv[1]) # Breite des Displays
|
||||||
|
Ny = int(sys.argv[2]) # Höhe des Displays
|
||||||
|
|
||||||
|
display = ScrollingTextDisplay(Nx, Ny)
|
||||||
|
display.display()
|
||||||
|
|
23
apps/wget.sh
Executable file
23
apps/wget.sh
Executable file
@ -0,0 +1,23 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#pkill -9 wget
|
||||||
|
if [ ! -d /tmp/video ]; then
|
||||||
|
mkdir /tmp/video
|
||||||
|
fi
|
||||||
|
rm -f /tmp/video/*
|
||||||
|
|
||||||
|
if [[ ${3} =~ .*gif$ ]]; then
|
||||||
|
wget -O "/tmp/video/$(basename ${3})" "${3}" && ffmpeg -loglevel -8 -re -ignore_loop 0 -i "/tmp/video/$(basename ${3})" -pix_fmt rgb24 -vf "scale=(iw*sar)*min(${1}/(iw*sar)\,${2}/ih):ih*min(${1}/(iw*sar)\,${2}/ih), pad=${1}:${2}:(${1}-iw*min(${1}/iw\,${2}/ih))/2:(${2}-ih*min(${1}/iw\,${2}/ih))/2" -sws_flags bicubic -sws_dither bayer -vcodec rawvideo -f image2pipe -
|
||||||
|
elif [[ ${3} =~ .*(jpg|jpeg|png|bmp|tiff|webp)$ ]]; then
|
||||||
|
wget -q -O "/tmp/video/$(basename ${3})" "${3}";
|
||||||
|
ffmpeg -i "/tmp/video/$(basename ${3})" -pix_fmt rgb24 -vf "scale=${1}x${2}" -vcodec rawvideo -f image2pipe - | tee /tmp/video/matrix
|
||||||
|
while true; do
|
||||||
|
cat /tmp/video/matrix
|
||||||
|
sleep 1;
|
||||||
|
done
|
||||||
|
else
|
||||||
|
|
||||||
|
|
||||||
|
wget -O - -q "${3}" | tee "/tmp/video/$(basename ${3})" | ffmpeg -loglevel -8 -f alsa default -re -i - -pix_fmt rgb24 -vf "scale=${1}x${2}" -sws_flags bicubic -sws_dither bayer -vcodec rawvideo -f image2pipe -
|
||||||
|
ffmpeg -loglevel -8 -f alsa default -stream_loop -1 -re -i "/tmp/video/$(basename ${3})" -pix_fmt rgb24 -vf "scale=${1}x${2}" -sws_flags bicubic -sws_dither bayer -vcodec rawvideo -f image2pipe -
|
||||||
|
|
||||||
|
fi
|
75
apps/wolfram.py
Executable file
75
apps/wolfram.py
Executable file
@ -0,0 +1,75 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import numpy as np
|
||||||
|
import pygame
|
||||||
|
import time
|
||||||
|
|
||||||
|
Nx = int(sys.argv[1]) # Breite des Displays
|
||||||
|
Ny = int(sys.argv[2]) # Höhe des Displays
|
||||||
|
fps = 30
|
||||||
|
|
||||||
|
# Liste der Wolfram-Regeln, die angezeigt werden sollen
|
||||||
|
rules = [30, 90, 110, 60, 150, 126, 45, 105, 75, 214]
|
||||||
|
pause_duration = 5 # Dauer der Pause zwischen den Mustern in Sekunden
|
||||||
|
|
||||||
|
# Umwandlung der Regelnummer in eine Binärliste
|
||||||
|
def get_rule_bin(rule_number):
|
||||||
|
return np.array([int(x) for x in np.binary_repr(rule_number, width=8)])
|
||||||
|
|
||||||
|
# Regel für die nächste Generation
|
||||||
|
def next_gen(current_gen, rule_bin):
|
||||||
|
padded = np.pad(current_gen, (1,), mode='constant')
|
||||||
|
new_gen = np.zeros_like(current_gen)
|
||||||
|
|
||||||
|
for i in range(1, len(padded) - 1):
|
||||||
|
neighborhood = padded[i-1:i+2]
|
||||||
|
index = 7 - (neighborhood[0] * 4 + neighborhood[1] * 2 + neighborhood[2])
|
||||||
|
new_gen[i-1] = rule_bin[index]
|
||||||
|
|
||||||
|
return new_gen
|
||||||
|
|
||||||
|
# Funktion, um eine zufällige Farbe zu erzeugen
|
||||||
|
def random_color():
|
||||||
|
return np.random.randint(0, 256, size=3)
|
||||||
|
|
||||||
|
# Haupt-Loop für die Animation
|
||||||
|
clock = pygame.time.Clock()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
for rule_number in rules:
|
||||||
|
rule_bin = get_rule_bin(rule_number)
|
||||||
|
|
||||||
|
# Initialisiere ein leeres Display (alle Zellen sind tot)
|
||||||
|
cells = np.zeros((Ny, Nx), dtype=np.uint8)
|
||||||
|
|
||||||
|
# Setze die mittlere Zelle der ersten Reihe auf 1 (Zelle ist lebendig)
|
||||||
|
cells[0, Nx // 2] = 1
|
||||||
|
|
||||||
|
# Erstelle einen Farbarray für die Zellen
|
||||||
|
color_map = np.zeros((Ny, Nx, 3), dtype=np.uint8)
|
||||||
|
|
||||||
|
# Animation Schleife
|
||||||
|
for row in range(1, Ny):
|
||||||
|
cells[row] = next_gen(cells[row - 1], rule_bin)
|
||||||
|
|
||||||
|
# Setze die Farben für die lebendigen Zellen
|
||||||
|
color_map[row] = np.where(cells[row][:, None], random_color(), 0) # Zufällige Farbe für lebende Zellen
|
||||||
|
|
||||||
|
# Erstelle das Bild aus den Zellen
|
||||||
|
image = np.zeros((Ny, Nx, 3), dtype=np.uint8)
|
||||||
|
for r in range(Ny):
|
||||||
|
for c in range(Nx):
|
||||||
|
if cells[r, c]:
|
||||||
|
image[r, c] = color_map[r, c]
|
||||||
|
|
||||||
|
# Schreibe das Bild in den Output-Stream
|
||||||
|
os.write(1, image.tobytes())
|
||||||
|
|
||||||
|
# Warte für die nächste Frame
|
||||||
|
clock.tick(fps)
|
||||||
|
|
||||||
|
# Pause von 5 Sekunden nach jedem Muster
|
||||||
|
time.sleep(pause_duration)
|
||||||
|
|
@ -1,6 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
export PATH="$PATH:/home/deckensteuerung/.config/bin"
|
export PATH="$PATH:/home/deckensteuerung/.config/bin"
|
||||||
youtube-dl -f 'worst[abr>=96]' --no-progress --output - ${3} | ffmpeg -f alsa default -re -i - -pix_fmt rgb24 -vf "scale=${1}x${2}" -sws_flags bicubic -sws_dither bayer -vcodec rawvideo -f image2pipe -
|
yt-dlp --no-progress --output - ${3} | ffmpeg -f alsa default -re -i - -pix_fmt rgb24 -vf "scale=${1}x${2}" -sws_flags bicubic -sws_dither bayer -vcodec rawvideo -f image2pipe -
|
||||||
|
#youtube-dl -f 'worst[abr>=96]' --no-progress --output - ${3} | ffmpeg -f alsa default -re -i - -pix_fmt rgb24 -vf "scale=${1}x${2}" -sws_flags bicubic -sws_dither bayer -vcodec rawvideo -f image2pipe -
|
||||||
#youtube-dl --no-progress --output - ${3} | ffmpeg -re -i - -pix_fmt rgb24 -vf "scale=${1}x${2}" -sws_flags bicubic -sws_dither bayer -vcodec rawvideo -f image2pipe -
|
#youtube-dl --no-progress --output - ${3} | ffmpeg -re -i - -pix_fmt rgb24 -vf "scale=${1}x${2}" -sws_flags bicubic -sws_dither bayer -vcodec rawvideo -f image2pipe -
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
|
72
config.py
72
config.py
@ -5,7 +5,7 @@ ScreenX = 80
|
|||||||
#height
|
#height
|
||||||
ScreenY = 40
|
ScreenY = 40
|
||||||
|
|
||||||
DefaultBrightness = 1.0
|
DefaultBrightness = 0.6
|
||||||
|
|
||||||
Serial = "/dev/ttyACM0"
|
Serial = "/dev/ttyACM0"
|
||||||
|
|
||||||
@ -15,6 +15,7 @@ NoDataTimeout = 40
|
|||||||
LogLevel = logging.DEBUG
|
LogLevel = logging.DEBUG
|
||||||
|
|
||||||
UseGui = True
|
UseGui = True
|
||||||
|
GuiFPS = 20
|
||||||
GuiScaleFactor = 15
|
GuiScaleFactor = 15
|
||||||
|
|
||||||
WebHost = "0.0.0.0"
|
WebHost = "0.0.0.0"
|
||||||
@ -22,25 +23,52 @@ WebPort = 8000
|
|||||||
|
|
||||||
# first app is always running as default
|
# first app is always running as default
|
||||||
Apps = [
|
Apps = [
|
||||||
{"guiname": "Lines", "name": "lines", "cmd": "apps/lines.py", "persistent": False},
|
|
||||||
|
|
||||||
|
{"name": "backlight", "cmd": "apps/backlight.py", "white": True},
|
||||||
|
|
||||||
|
{"guiname": "Lines", "name": "lines", "cmd": "./lines.py", "persistent": False, "path": "./apps/"},
|
||||||
|
{"guiname": "Wolfram", "name": "wolfram", "cmd": "./wolfram.py", "persistent": False, "path": "./apps/"},
|
||||||
|
{"guiname": "Digi Clock", "name": "digiclock", "cmd": "./digi_clock.py", "persistent": False, "path": "./apps/"},
|
||||||
|
{"guiname": "Text Scroller MQTT", "name": "textscroll", "cmd": "./textscroll.py", "persistent": False, "path": "./apps/"},
|
||||||
{"guiname": "Flicker", "name": "flicker", "cmd": "apps/flicker"},
|
{"guiname": "Flicker", "name": "flicker", "cmd": "apps/flicker"},
|
||||||
|
|
||||||
{"guiname": "Pixelflut", "name": "pixelflut", "cmd": "apps/pixelflut", "persistent": True},
|
{"guiname": "Pixelflut", "name": "pixelflut", "cmd": "apps/pixelflut", "persistent": True},
|
||||||
{"guiname": "Pong", "name": "pong", "cmd": "apps/pong.py"},
|
# {"guiname": "Pong", "name": "pong", "cmd": "apps/pong.py"},
|
||||||
{"guiname": "YoutubeDL", "name": "youtubedl", "cmd": "apps/youtubedl.sh", "persistent": False},
|
{"guiname": "YoutubeDL", "name": "youtubedl", "cmd": "apps/youtubedl.sh", "persistent": False},
|
||||||
{"guiname": "Show Framebuffer", "name": "fbcp", "cmd": ["apps/fbcp", "/dev/fb0"]},
|
# {"guiname": "Show Framebuffer", "name": "fbcp", "cmd": ["apps/fbcp", "/dev/fb0"]},
|
||||||
{"guiname": "Strobo", "name": "strobo", "cmd": "apps/strobo.py"},
|
{"guiname": "Strobo", "name": "strobo", "cmd": "apps/strobo.py"},
|
||||||
{"guiname": "Beispiel", "name": "example", "cmd": "apps/example.py"},
|
# {"guiname": "Beispiel", "name": "example", "cmd": "apps/example.py"},
|
||||||
|
# {"guiname": "Beispiel2", "name": "example2", "cmd": "apps/example2.py", "white": True},
|
||||||
|
{"guiname": "Fibonacci Clock", "name": "fibonacci-clock", "cmd": "apps/fibonacci-clock.py"},
|
||||||
|
{"guiname": "Wget Video/Gif/Images", "name": "wget", "cmd": "apps/wget.sh", "persistent": False},
|
||||||
|
|
||||||
|
|
||||||
# juergen/pixelfoo
|
# juergen/pixelfoo
|
||||||
{"guiname": "Colored noise", "name": "cnoise", "cmd": "apps/cnoise", "persistent": False},
|
{"guiname": "Congress noise", "name": "cnoise", "cmd": "pixelfoo/target/release/cnoise", "persistent": False},
|
||||||
{"guiname": "Dual Moodlight", "name": "bimood", "cmd": "apps/bimood", "persistent": False},
|
{"guiname": "Game of Life", "name": "life", "cmd": "pixelfoo/target/release/life", "persistent": False},
|
||||||
{"guiname": "Maze", "name": "maze", "cmd": "apps/maze", "persistent": False},
|
{"guiname": "Matrix Code", "name": "matrix-code", "cmd": "pixelfoo/target/release/matrix-code", "persistent": False},
|
||||||
{"guiname": "Dual Maze", "name": "dualmaze", "cmd": "apps/dualmaze", "persistent": False, "persistent": False},
|
{"guiname": "Lorenz Attractor", "name": "lorenz", "cmd": "pixelfoo/target/release/lorenz", "persistent": False},
|
||||||
{"guiname": "Predator & Prey", "name": "predprey", "cmd": "apps/predprey", "persistent": False},
|
{"guiname": "Dual Moodlight", "name": "bimood", "cmd": "pixelfoo/target/release/bimood", "persistent": False},
|
||||||
|
{"guiname": "Maze", "name": "maze", "cmd": "pixelfoo/target/release/maze", "persistent": False},
|
||||||
|
{"guiname": "Dual Maze", "name": "dualmaze", "cmd": "pixelfoo/target/release/dualmaze", "persistent": False, "persistent": False},
|
||||||
|
{"guiname": "Predator & Prey", "name": "predprey", "cmd": "pixelfoo/target/release/predprey", "persistent": False},
|
||||||
|
|
||||||
|
# { "guiname": "Beat Saber Ceiling", "name": "beatsaberceiling", "cmd": "./beatsaberceiling.py", "path": "apps/beatsaberceiling" },
|
||||||
|
|
||||||
#{"guiname": "Fibonacci Clock", "name": "fibonacci-clock", "cmd": "apps/fibonacci-clock.py"},
|
# mathpixel
|
||||||
|
{"guiname": "Structure formation", "name": "swifthohenberg", "cmd": "apps/swifthohenberg.py", "persistent": False},
|
||||||
|
{"guiname": "Quadratisch", "name": "quadratic", "cmd": "apps/quadratic.py"},
|
||||||
|
{"guiname": "Pendel", "name": "pendulum", "cmd": "apps/pendlum.py"},
|
||||||
|
{"guiname": "Konvergenz", "name": "convergence", "cmd": "apps/convergence.py"},
|
||||||
|
{"guiname": "Sinic", "name": "sinic", "cmd": "apps/sinic.py"},
|
||||||
|
{"guiname": "Sinic 2", "name": "sinic2", "cmd": "apps/sinic2.py"},
|
||||||
|
|
||||||
|
# pixelthud
|
||||||
|
{"guiname": "Fading Pixels", "name": "fadingpxls", "cmd": "apps/fading_pixels.py", "persistent": False},
|
||||||
|
{"guiname": "Plane Wave", "name": "planewave", "cmd": "apps/plane_wave.py", "persistent": False},
|
||||||
|
{"guiname": "Rock-paper-scissors-spock-lizard", "name": "rps", "cmd": "apps/rps.py", "persistent": False},
|
||||||
|
{"guiname": "Doom Fire", "name": "doomfire", "cmd": "apps/doom_fire_psx2.py", "persistent": False},
|
||||||
|
# {"guiname": "Maxwell FDTD", "name": "fdtd", "cmd": "apps/fdtd.py", "persistent": False},
|
||||||
|
|
||||||
#{"guiname": "Stream", "name": "stream", "cmd": "apps/stream.sh", "persistent": False},
|
#{"guiname": "Stream", "name": "stream", "cmd": "apps/stream.sh", "persistent": False},
|
||||||
#{"guiname": "Wget Video/Gif/Images", "name": "wget", "cmd": "apps/wget.sh", "persistent": False},
|
#{"guiname": "Wget Video/Gif/Images", "name": "wget", "cmd": "apps/wget.sh", "persistent": False},
|
||||||
@ -53,14 +81,14 @@ Apps = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
# load additional apps from config/
|
# load additional apps from config/
|
||||||
import os
|
# import os
|
||||||
import os.path
|
# import os.path
|
||||||
configs = os.listdir("configs/")
|
# configs = os.listdir("configs/")
|
||||||
import importlib
|
# import importlib
|
||||||
for config in configs:
|
# for config in configs:
|
||||||
file, ext = os.path.splitext(os.path.basename(config))
|
# file, ext = os.path.splitext(os.path.basename(config))
|
||||||
if ext != ".py" or file[0] == "_":
|
# if ext != ".py" or file[0] == "_":
|
||||||
continue
|
# continue
|
||||||
modname = "configs."+file
|
# modname = "configs."+file
|
||||||
lib = importlib.import_module(modname)
|
# lib = importlib.import_module(modname)
|
||||||
Apps += lib.Apps
|
# Apps += lib.Apps
|
||||||
|
BIN
filter/test.png
Normal file
BIN
filter/test.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
@ -4,9 +4,10 @@
|
|||||||
<title>Pixelserver Interface</title>
|
<title>Pixelserver Interface</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body style="display: grid; grid-template-columns: auto auto auto;">
|
||||||
<h1>Andreas <i>DEBUG</i> Interface</h1>
|
<h1 style="grid-column: 1 / 4; text-align: center;">Andreas <i>production-ready</i> Interface</h1>
|
||||||
|
|
||||||
|
<div>
|
||||||
<h2>Kommando:</h2>
|
<h2>Kommando:</h2>
|
||||||
<form id='in' onSubmit="return request()">
|
<form id='in' onSubmit="return request()">
|
||||||
<select id="list"></select>
|
<select id="list"></select>
|
||||||
@ -19,17 +20,49 @@
|
|||||||
<input id="brightness" value=1.0 /><br/>
|
<input id="brightness" value=1.0 /><br/>
|
||||||
<button id="sendbrightness">Setzen</button>
|
<button id="sendbrightness">Setzen</button>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
<div style="border-left: solid 1px; padding-left: 10px;">
|
||||||
<h2>Gamma:</h2>
|
<h2>Gamma:</h2>
|
||||||
<form id='gammeform' onSubmit="return setgamma()">
|
<form id='gammeform' onSubmit="return setgamma()">
|
||||||
Rot: <input id="gammar" value=2.8 /><br/>
|
Rot: <input id="gammar" value=2.8 /><br/>
|
||||||
Grün: <input id="gammag" value=2.65 /><br/>
|
Grün: <input id="gammag" value=2.65 /><br/>
|
||||||
Blau: <input id="gammab" value=2.65 /><br/>
|
Blau: <input id="gammab" value=2.65 /><br/>
|
||||||
|
Weiß: <input id="gammaw" value=2.65 /><br/>
|
||||||
<button id="sendgamma">Setzen</button>
|
<button id="sendgamma">Setzen</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<h2>Flip:</h2>
|
||||||
|
<form id="flipform" onSubmit="return setFlip()">
|
||||||
|
<input id="flipx" type="checkbox" /> Flip X <br/>
|
||||||
|
<input id="flipy" type="checkbox" /> Flip Y <br/>
|
||||||
|
<button id="sendflip">Setzen</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<h2>Filterimage:</h2>
|
||||||
|
<form id="filteform" onSubmit="return setFilter()">
|
||||||
|
<input id="filtername" value="test"></intput><br/>
|
||||||
|
<button id="filterflip">Setzten</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<h2>Filter expression:</h2>
|
||||||
|
<form id="filterexprform" onSubmit="return setFilterExpr()">
|
||||||
|
<input id="filterexpr" value="0.5+0.25*sin(x/3+t)"></intput><br/>
|
||||||
|
<button>Setzten</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div style="border-left: solid 1px; padding-left: 10px; width: 600px;">
|
||||||
|
<h2>Crash Log:</h2>
|
||||||
|
<form id="crashlogform" onSubmit="return enableCrashLog()">
|
||||||
|
<button>start</button>
|
||||||
|
</form>
|
||||||
|
<textarea readonly id='crashlogs' style="width: 500px; height: 300px; display: none;"></textarea>
|
||||||
|
|
||||||
<h2>Log:</h2>
|
<h2>Log:</h2>
|
||||||
<textarea readonly id='logs' style="width: 500px; height: 500px;"></textarea>
|
<form id="logform" onSubmit="return enableLog()">
|
||||||
|
<button>start</button>
|
||||||
|
</form>
|
||||||
|
<textarea readonly id='logs' style="width: 500px; height: 300px; display: none;"></textarea>
|
||||||
|
</div>
|
||||||
<script>
|
<script>
|
||||||
function getRaw(from, callback){
|
function getRaw(from, callback){
|
||||||
var xhttp = new XMLHttpRequest();
|
var xhttp = new XMLHttpRequest();
|
||||||
@ -105,7 +138,8 @@
|
|||||||
let r = document.getElementById('gammar').value;
|
let r = document.getElementById('gammar').value;
|
||||||
let g = document.getElementById('gammag').value;
|
let g = document.getElementById('gammag').value;
|
||||||
let b = document.getElementById('gammab').value;
|
let b = document.getElementById('gammab').value;
|
||||||
let url = "/setgamma/" + r+"/"+g+"/"+b;
|
let w = document.getElementById('gammaw').value;
|
||||||
|
let url = "/setgamma/" + r+"/"+g+"/"+b+"/"+w;
|
||||||
getRaw(url, function test(){});
|
getRaw(url, function test(){});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -117,13 +151,61 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setFlip(){
|
||||||
|
let x = document.getElementById('flipx').checked;
|
||||||
|
let y = document.getElementById('flipy').checked;
|
||||||
|
console.log(x);
|
||||||
|
console.log(y);
|
||||||
|
getRaw("/filter/flipx/" + x, function test(){});
|
||||||
|
getRaw("/filter/flipy/" + y, function test(){});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFilter(){
|
||||||
|
let i = document.getElementById('filtername').value;
|
||||||
|
let url = "/filter/img/" + i;
|
||||||
|
getRaw(url, function test(){});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function setFilterExpr(){
|
||||||
|
let expr = document.getElementById('filterexpr').value;
|
||||||
|
let url = "/filter/expr/";
|
||||||
|
post(url, {"expr": expr});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
function updateLog(){
|
function updateLog(){
|
||||||
getRaw("/apps/log", function(text){
|
getRaw("/apps/log", function(text){
|
||||||
document.getElementById('logs').innerText = text;
|
document.getElementById('logs').value = text;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
updateLog();
|
|
||||||
setInterval(updateLog, 1000);
|
function updateCrashLog(){
|
||||||
|
getRaw("/apps/crashlog", function(text){
|
||||||
|
document.getElementById('crashlogs').value = text;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableCrashLog(){
|
||||||
|
document.getElementById("crashlogform").style.display = "none";
|
||||||
|
document.getElementById("crashlogs").style.display = "block";
|
||||||
|
|
||||||
|
updateCrashLog();
|
||||||
|
setInterval(updateCrashLog, 1000);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableLog(){
|
||||||
|
document.getElementById("logform").style.display = "none";
|
||||||
|
document.getElementById("logs").style.display = "block";
|
||||||
|
updateLog();
|
||||||
|
setInterval(updateLog, 1000);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
getJSON("/apps/list", populateForm);
|
getJSON("/apps/list", populateForm);
|
||||||
|
|
||||||
|
252
main.py
252
main.py
@ -12,7 +12,10 @@ import time
|
|||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
|
import numpy as np
|
||||||
|
import string
|
||||||
|
from collections import OrderedDict
|
||||||
|
import scipy.misc
|
||||||
logging.basicConfig(filename='pixelserver.log', level=config.LogLevel)
|
logging.basicConfig(filename='pixelserver.log', level=config.LogLevel)
|
||||||
running = True
|
running = True
|
||||||
|
|
||||||
@ -72,6 +75,16 @@ class LogReader(threading.Thread):
|
|||||||
def stop(self):
|
def stop(self):
|
||||||
self.running = False
|
self.running = False
|
||||||
|
|
||||||
|
class Frame:
|
||||||
|
def __init__(self, buffer, channels=3):
|
||||||
|
self.buffer = buffer
|
||||||
|
self.created = time.time()
|
||||||
|
self.channels = channels
|
||||||
|
def clone(self):
|
||||||
|
f = Frame(self.buffer+0, self.channels)
|
||||||
|
f.created = self.created
|
||||||
|
return f
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
# GUI #
|
# GUI #
|
||||||
########################################################################
|
########################################################################
|
||||||
@ -84,6 +97,7 @@ if config.UseGui:
|
|||||||
self.cv = threading.Condition()
|
self.cv = threading.Condition()
|
||||||
self.datasource = datasource.addListener(self.cv)
|
self.datasource = datasource.addListener(self.cv)
|
||||||
def run(self):
|
def run(self):
|
||||||
|
last_frame = time.time()
|
||||||
logging.info("Starting GUI")
|
logging.info("Starting GUI")
|
||||||
sf = config.GuiScaleFactor
|
sf = config.GuiScaleFactor
|
||||||
screen = pygame.display.set_mode((sf*config.ScreenX, sf*config.ScreenY))
|
screen = pygame.display.set_mode((sf*config.ScreenX, sf*config.ScreenY))
|
||||||
@ -93,14 +107,26 @@ if config.UseGui:
|
|||||||
pass
|
pass
|
||||||
with self.cv:
|
with self.cv:
|
||||||
self.cv.wait()
|
self.cv.wait()
|
||||||
data = self.datasource.getData()
|
frame = self.datasource.getData()
|
||||||
screen.fill((0, 255, 0))
|
screen.fill((0, 0, 0))
|
||||||
for x in range(config.ScreenX):
|
if frame.channels == 3:
|
||||||
for y in range(config.ScreenY):
|
for x in range(config.ScreenX):
|
||||||
i = x+y*config.ScreenX
|
for y in range(config.ScreenY):
|
||||||
color = (data[i*3+0], data[i*3+1], data[i*3+2])
|
color = (frame.buffer[y, x, 0], frame.buffer[y, x, 1], frame.buffer[y, x, 2])
|
||||||
pygame.draw.rect(screen, color, pygame.Rect(sf*x, sf*y, sf, sf))
|
pygame.draw.rect(screen, color, pygame.Rect(sf*x, sf*y, sf, sf))
|
||||||
|
elif frame.channels == 4:
|
||||||
|
for x in range(config.ScreenX):
|
||||||
|
for y in range(config.ScreenY):
|
||||||
|
w = frame.buffer[y, x, 3]//2
|
||||||
|
color = (frame.buffer[y, x, 0]//2+w, frame.buffer[y, x, 1]//2+w, frame.buffer[y, x, 2]//2+w)
|
||||||
|
pygame.draw.rect(screen, color, pygame.Rect(sf*x, sf*y, sf, sf))
|
||||||
|
#logging.debug("Time to gui: "+str(time.time()-frame.created))
|
||||||
pygame.display.flip()
|
pygame.display.flip()
|
||||||
|
if time.time() < last_frame+1/config.GuiFPS:
|
||||||
|
time.sleep(time.time()-(last_frame+1/config.GuiFPS))
|
||||||
|
#time.sleep(0.01)
|
||||||
|
last_frame = time.time()
|
||||||
|
|
||||||
logging.info("Closing GUI")
|
logging.info("Closing GUI")
|
||||||
def join(self):
|
def join(self):
|
||||||
with self.cv:
|
with self.cv:
|
||||||
@ -128,19 +154,26 @@ class SerialWriter(threading.Thread):
|
|||||||
logging.info("Serial Opened")
|
logging.info("Serial Opened")
|
||||||
with self.cv:
|
with self.cv:
|
||||||
self.cv.wait(timeout = 1/30)
|
self.cv.wait(timeout = 1/30)
|
||||||
data = self.datasource.getData()
|
frame = self.datasource.getData()
|
||||||
|
data = frame.buffer.reshape((config.ScreenX*config.ScreenY*frame.channels,)).astype(np.uint8).tobytes()
|
||||||
if self.updateGamma:
|
if self.updateGamma:
|
||||||
buf = bytearray(b"\x00")*3*256
|
buf = bytearray(b"\x00")*4*256
|
||||||
for i in range(256):
|
for i in range(256):
|
||||||
apply = lambda x, g: max(0, min(255, int(math.pow(x/255, g)*255)))
|
apply = lambda x, g: max(0, min(255, int(math.pow(x/255, g)*255)))
|
||||||
buf[i] = apply(i, self.r)
|
buf[i] = apply(i, self.r)
|
||||||
buf[i+256] = apply(i, self.g)
|
buf[i+256] = apply(i, self.g)
|
||||||
buf[i+512] = apply(i, self.b)
|
buf[i+512] = apply(i, self.b)
|
||||||
|
buf[i+512+256] = apply(i, self.w)
|
||||||
ser.write(b"\x02")
|
ser.write(b"\x02")
|
||||||
ser.write(buf)
|
ser.write(buf)
|
||||||
self.updateGamma = False
|
self.updateGamma = False
|
||||||
ser.write(b"\01")
|
if frame.channels == 3:
|
||||||
ser.write(data)
|
ser.write(b"\01")
|
||||||
|
ser.write(data)
|
||||||
|
elif frame.channels == 4:
|
||||||
|
ser.write(b"\03")
|
||||||
|
ser.write(data)
|
||||||
|
logging.debug("Time to gui: "+str(time.time()-frame.created))
|
||||||
ser.flush()
|
ser.flush()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if ser != None:
|
if ser != None:
|
||||||
@ -154,9 +187,9 @@ class SerialWriter(threading.Thread):
|
|||||||
with self.cv:
|
with self.cv:
|
||||||
self.cv.notify_all()
|
self.cv.notify_all()
|
||||||
super().join()
|
super().join()
|
||||||
def setGamma(self, r, g, b):
|
def setGamma(self, r, g, b, w):
|
||||||
with self.cv:
|
with self.cv:
|
||||||
self.r, self.g, self.b = r, g, b
|
self.r, self.g, self.b, self.w = r, g, b, w
|
||||||
self.updateGamma = True
|
self.updateGamma = True
|
||||||
self.cv.notify_all()
|
self.cv.notify_all()
|
||||||
|
|
||||||
@ -164,31 +197,37 @@ class SerialWriter(threading.Thread):
|
|||||||
# App #
|
# App #
|
||||||
########################################################################
|
########################################################################
|
||||||
class App(threading.Thread):
|
class App(threading.Thread):
|
||||||
def __init__(self, cmd, param, listener, is_persistent):
|
def __init__(self, cmd, param, listener, is_persistent, is_white=False, path="."):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
#start app
|
#start app
|
||||||
if type(cmd) != list:
|
if type(cmd) != list:
|
||||||
cmd = [cmd,]
|
cmd = [cmd,]
|
||||||
args = cmd+[str(config.ScreenX), str(config.ScreenY), param]
|
args = cmd+[str(config.ScreenX), str(config.ScreenY), param]
|
||||||
self.app = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
|
self.app = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, cwd=path)
|
||||||
self.last_update = time.time()
|
self.last_update = time.time()
|
||||||
self.cv = threading.Condition()
|
self.cv = threading.Condition()
|
||||||
self.watchdog = WatchDog(lambda: self.isAppTimedOut(), lambda: self.terminateApp())
|
self.watchdog = WatchDog(lambda: self.isAppTimedOut(), lambda: self.terminateApp())
|
||||||
self.watchdog.start()
|
self.watchdog.start()
|
||||||
self.logreader = LogReader(self)
|
self.logreader = LogReader(self)
|
||||||
self.logreader.start()
|
self.logreader.start()
|
||||||
self.datasource = DataSource(b"\x00"*config.ScreenX*config.ScreenY*3)
|
self.datasource = DataSource(Frame(np.zeros((config.ScreenY, config.ScreenX, 3))))
|
||||||
self.running = True
|
self.running = True
|
||||||
self.listener = listener
|
self.listener = listener
|
||||||
self.is_persistent = is_persistent
|
self.is_persistent = is_persistent
|
||||||
|
self.is_white = is_white
|
||||||
def run(self):
|
def run(self):
|
||||||
while running and self.running and self.alive():
|
while running and self.running and self.alive():
|
||||||
oshandle = self.app.stdout.fileno()
|
oshandle = self.app.stdout.fileno()
|
||||||
try:
|
try:
|
||||||
data = os.read(oshandle, config.ScreenX*config.ScreenY*3)
|
bytes = 4 if self.is_white else 3
|
||||||
assert len(data) == config.ScreenX*config.ScreenY*3
|
data = os.read(oshandle, config.ScreenX*config.ScreenY*bytes)
|
||||||
|
assert len(data) == config.ScreenX*config.ScreenY*bytes
|
||||||
|
buffer = np.frombuffer(data, dtype=np.uint8, count=config.ScreenX*config.ScreenY*bytes)
|
||||||
|
buffer = buffer.reshape((config.ScreenY, config.ScreenX, bytes))
|
||||||
|
|
||||||
|
frame = Frame(buffer, channels=bytes)
|
||||||
self.last_update = time.time()
|
self.last_update = time.time()
|
||||||
self.datasource.pushData(data)
|
self.datasource.pushData(frame)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.debug("Exception in App.run")
|
logging.debug("Exception in App.run")
|
||||||
with self.listener:
|
with self.listener:
|
||||||
@ -197,6 +236,8 @@ class App(threading.Thread):
|
|||||||
self.logreader.stop()
|
self.logreader.stop()
|
||||||
self.watchdog.join()
|
self.watchdog.join()
|
||||||
self.logreader.join()
|
self.logreader.join()
|
||||||
|
self.app.wait()
|
||||||
|
logging.debug("App stopped")
|
||||||
def alive(self):
|
def alive(self):
|
||||||
return self.app.poll() == None
|
return self.app.poll() == None
|
||||||
def stop(self):
|
def stop(self):
|
||||||
@ -206,6 +247,8 @@ class App(threading.Thread):
|
|||||||
self.app.stderr.close()
|
self.app.stderr.close()
|
||||||
self.watchdog.stop()
|
self.watchdog.stop()
|
||||||
self.logreader.stop()
|
self.logreader.stop()
|
||||||
|
self.app.wait()
|
||||||
|
logging.debug("App stopped")
|
||||||
def getLog(self):
|
def getLog(self):
|
||||||
return self.logreader.getLog()
|
return self.logreader.getLog()
|
||||||
def terminateApp(self):
|
def terminateApp(self):
|
||||||
@ -220,16 +263,17 @@ class App(threading.Thread):
|
|||||||
class AppRunner(threading.Thread):
|
class AppRunner(threading.Thread):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self.last_crashlog = ""
|
||||||
self.currentApp = -1
|
self.currentApp = -1
|
||||||
self.requestedApp = 0
|
self.requestedApp = 0
|
||||||
self.intensity = config.DefaultBrightness
|
|
||||||
self.app = None
|
self.app = None
|
||||||
self.cv = threading.Condition()
|
self.cv = threading.Condition()
|
||||||
self.param = ""
|
self.param = ""
|
||||||
self.datasource = DataSource(b"\x00"*config.ScreenX*config.ScreenY*3)
|
self.datasource = DataSource(Frame(np.zeros((config.ScreenY, config.ScreenX, 3))))
|
||||||
self.serial = SerialWriter(self.datasource)
|
self.serial = SerialWriter(self.datasource)
|
||||||
self.serial.start()
|
self.serial.start()
|
||||||
self.persistent_apps = {}
|
self.persistent_apps = {}
|
||||||
|
self.filters = OrderedDict()
|
||||||
#start persistent apps
|
#start persistent apps
|
||||||
for app, i in zip(config.Apps, range(len(config.Apps))):
|
for app, i in zip(config.Apps, range(len(config.Apps))):
|
||||||
if app["persistent"]:
|
if app["persistent"]:
|
||||||
@ -245,34 +289,41 @@ class AppRunner(threading.Thread):
|
|||||||
logging.info("Requesting app: "+str(app))
|
logging.info("Requesting app: "+str(app))
|
||||||
def startApp(self, i, param=""):
|
def startApp(self, i, param=""):
|
||||||
app = config.Apps[i]
|
app = config.Apps[i]
|
||||||
newapp = App(app["cmd"], param, self.cv, is_persistent=app["persistent"])
|
newapp = App(app["cmd"], param, self.cv, is_persistent=app["persistent"], is_white=app["white"], path=app["path"])
|
||||||
newapp.datasource.addListener(self.cv)
|
newapp.datasource.addListener(self.cv)
|
||||||
newapp.start()
|
newapp.start()
|
||||||
if app["persistent"]:
|
if app["persistent"]:
|
||||||
self.persistent_apps[self.currentApp] = newapp
|
self.persistent_apps[self.currentApp] = newapp
|
||||||
return newapp
|
return newapp
|
||||||
def updateApp(self):
|
def updateApp(self):
|
||||||
if self.app != None and not self.app.is_persistent:
|
try:
|
||||||
|
if self.app != None and not self.app.is_persistent:
|
||||||
self.app.stop()
|
self.app.stop()
|
||||||
self.currentApp = self.requestedApp
|
self.currentApp = self.requestedApp
|
||||||
if (self.currentApp in self.persistent_apps.keys()
|
if (self.currentApp in self.persistent_apps.keys()
|
||||||
and self.persistent_apps[self.currentApp].alive()):
|
and self.persistent_apps[self.currentApp].alive()):
|
||||||
self.app = self.persistent_apps[self.currentApp]
|
self.app = self.persistent_apps[self.currentApp]
|
||||||
else:
|
else:
|
||||||
self.app = self.startApp(self.requestedApp, self.param)
|
self.app = self.startApp(self.requestedApp, self.param)
|
||||||
|
except FileNotFoundError as e:
|
||||||
|
print(e)
|
||||||
def run(self):
|
def run(self):
|
||||||
logging.info("Starting Apprunner")
|
logging.info("Starting Apprunner")
|
||||||
while running:
|
while running:
|
||||||
with self.cv:
|
with self.cv:
|
||||||
if self.app == None or not self.app.alive():
|
if self.app == None or not self.app.alive():
|
||||||
|
if self.app != None:
|
||||||
|
self.last_crashlog = self.app.getLog()
|
||||||
self.requestedApp = 0
|
self.requestedApp = 0
|
||||||
if self.requestedApp != None:
|
if self.requestedApp != None:
|
||||||
self.updateApp()
|
self.updateApp()
|
||||||
self.requestedApp = None
|
self.requestedApp = None
|
||||||
data = bytearray(self.app.datasource.getData())
|
frame = self.app.datasource.getData().clone()
|
||||||
for i in range(len(data)):
|
#logging.debug("Runner in time: "+str(time.time()-frame.created))
|
||||||
data[i] = int(data[i]*self.intensity)
|
for _, f in self.filters.items():
|
||||||
self.datasource.pushData(data)
|
frame.buffer = f(frame.buffer)
|
||||||
|
#logging.debug("Runner out time: "+str(time.time()-frame.created))
|
||||||
|
self.datasource.pushData(frame)
|
||||||
self.cv.wait()
|
self.cv.wait()
|
||||||
self.serial.join()
|
self.serial.join()
|
||||||
if config.UseGui:
|
if config.UseGui:
|
||||||
@ -282,8 +333,77 @@ class AppRunner(threading.Thread):
|
|||||||
if self.app == None:
|
if self.app == None:
|
||||||
return ""
|
return ""
|
||||||
return self.app.getLog()
|
return self.app.getLog()
|
||||||
def setGamma(self, r, g, b):
|
def setGamma(self, r, g, b, w):
|
||||||
self.serial.setGamma(r, g, b)
|
self.serial.setGamma(r, g, b, w)
|
||||||
|
def setFilter(self, name, filter):
|
||||||
|
self.filters[name] = filter
|
||||||
|
def removeFilter(self, name):
|
||||||
|
if name in self.filters.keys():
|
||||||
|
del self.filters[name]
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# Filter Api #
|
||||||
|
########################################################################
|
||||||
|
def MakeBrightnessFilter(intensity):
|
||||||
|
def filter(img):
|
||||||
|
return (img*intensity).astype(np.uint8)
|
||||||
|
return filter
|
||||||
|
|
||||||
|
def FlipXFilter(intensity):
|
||||||
|
return intensity[:,::-1,:]
|
||||||
|
|
||||||
|
def FlipYFilter(intensity):
|
||||||
|
return intensity[::-1,:,:]
|
||||||
|
|
||||||
|
def MakeBrightnessImageFilter(name):
|
||||||
|
img = scipy.misc.imread("filter/"+name+".png", flatten=True)/255
|
||||||
|
#img = np.transpose(img)
|
||||||
|
def filter(intensity):
|
||||||
|
intensity = intensity.astype(float)
|
||||||
|
for i in range(intensity.shape[2]):
|
||||||
|
intensity[:,:,i] *= img
|
||||||
|
return intensity.astype(np.uint8)
|
||||||
|
return filter
|
||||||
|
|
||||||
|
def strings(str):
|
||||||
|
allowed_chars = string.ascii_letters+string.digits+"+-*/()."
|
||||||
|
i = 0
|
||||||
|
outlist = []
|
||||||
|
while i != len(str):
|
||||||
|
if str[i] not in allowed_chars:
|
||||||
|
raise Exception("Unexpected char "+str[i])
|
||||||
|
if str[i] not in string.ascii_letters:
|
||||||
|
i+=1
|
||||||
|
continue
|
||||||
|
out = ""
|
||||||
|
while i != len(str) and str[i] in string.ascii_letters+string.digits:
|
||||||
|
out += str[i]
|
||||||
|
i+=1
|
||||||
|
outlist.append(out)
|
||||||
|
return outlist
|
||||||
|
|
||||||
|
def eval_safer(expr, x, y, t):
|
||||||
|
symbols = {"x": x, "y": y, "t": t,
|
||||||
|
"sin": np.sin, "cos": np.cos, "exp": np.exp, "tan": np.tan}
|
||||||
|
strs = strings(expr)
|
||||||
|
for s in strs:
|
||||||
|
if s not in symbols.keys():
|
||||||
|
raise Exception("unexpected symbol: "+s)
|
||||||
|
return eval(expr, {}, symbols)
|
||||||
|
|
||||||
|
def MakeBrightnessExprFilter(expr):
|
||||||
|
t0 = time.time()
|
||||||
|
x, y = np.meshgrid(np.arange(config.ScreenX), np.arange(config.ScreenY))
|
||||||
|
eval_safer(expr, 0, 0, 0)
|
||||||
|
def filter(intensity):
|
||||||
|
t = time.time()-t0
|
||||||
|
intensity = intensity.astype(float)
|
||||||
|
filter = 0*x+eval_safer(expr, x, y, t)
|
||||||
|
filter = np.clip(np.nan_to_num(filter), 0, 1)
|
||||||
|
for i in range(intensity.shape[2]):
|
||||||
|
intensity[:,:,i] *= filter
|
||||||
|
return intensity.astype(np.uint8)
|
||||||
|
return filter
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
# Web Api #
|
# Web Api #
|
||||||
@ -336,6 +456,10 @@ def apps_start(name, param):
|
|||||||
def apps_log():
|
def apps_log():
|
||||||
return runner.getLog()
|
return runner.getLog()
|
||||||
|
|
||||||
|
@route("/apps/crashlog")
|
||||||
|
def apps_log():
|
||||||
|
return runner.last_crashlog
|
||||||
|
|
||||||
@route("/apps/running")
|
@route("/apps/running")
|
||||||
def apps_running():
|
def apps_running():
|
||||||
return config.Apps[runner.currentApp]["name"]
|
return config.Apps[runner.currentApp]["name"]
|
||||||
@ -344,22 +468,56 @@ def apps_running():
|
|||||||
def index():
|
def index():
|
||||||
return bottle.static_file("index.html", root='html')
|
return bottle.static_file("index.html", root='html')
|
||||||
|
|
||||||
@route("/setgamma/<r>/<g>/<b>")
|
@route("/setgamma/<r>/<g>/<b>/<w>")
|
||||||
def setGamma(r, g, b):
|
def setGamma(r, g, b, w):
|
||||||
r = float(r)
|
r = float(r)
|
||||||
g = float(g)
|
g = float(g)
|
||||||
b = float(b)
|
b = float(b)
|
||||||
runner.setGamma(r, g, b)
|
w = float(w)
|
||||||
|
runner.setGamma(r, g, b, w)
|
||||||
return "ok"
|
return "ok"
|
||||||
|
|
||||||
@route("/setbrightness/<i>")
|
@route("/setbrightness/<i>")
|
||||||
def setGamma(i):
|
def setIntensity(i):
|
||||||
i = float(i)
|
i = float(i)
|
||||||
if i < 0 or i > 1:
|
if i < 0 or i > 1:
|
||||||
return "bad_value"
|
return "bad_value"
|
||||||
runner.intensity = i
|
runner.setFilter("0_intensity", MakeBrightnessFilter(i))
|
||||||
return "ok"
|
return "ok"
|
||||||
|
|
||||||
|
@route("/filter/flipx/<do>")
|
||||||
|
def flipx(do):
|
||||||
|
if do == "true":
|
||||||
|
runner.setFilter("1_flipx", FlipXFilter)
|
||||||
|
else:
|
||||||
|
runner.removeFilter("1_flipx")
|
||||||
|
return "ok"
|
||||||
|
|
||||||
|
@route("/filter/flipy/<do>")
|
||||||
|
def flipy(do):
|
||||||
|
if do == "true":
|
||||||
|
runner.setFilter("1_flipy", FlipYFilter)
|
||||||
|
else:
|
||||||
|
runner.removeFilter("1_flipy")
|
||||||
|
return "ok"
|
||||||
|
|
||||||
|
@route("/filter/img/<name>")
|
||||||
|
def setfilter(name):
|
||||||
|
if name == "none":
|
||||||
|
runner.removeFilter("3_imgfilter")
|
||||||
|
else:
|
||||||
|
runner.setFilter("3_imgfilter", MakeBrightnessImageFilter(name))
|
||||||
|
return "ok"
|
||||||
|
@post("/filter/expr/")
|
||||||
|
def filter_expr():
|
||||||
|
expr = request.forms.get('expr')
|
||||||
|
if expr == "" or expr == "none":
|
||||||
|
runner.removeFilter("5_brightnessfunction")
|
||||||
|
else:
|
||||||
|
runner.setFilter("5_brightnessfunction", MakeBrightnessExprFilter(expr))
|
||||||
|
return "ok"
|
||||||
|
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
# Startup #
|
# Startup #
|
||||||
########################################################################
|
########################################################################
|
||||||
@ -369,15 +527,21 @@ for app in config.Apps:
|
|||||||
app["persistent"] = False
|
app["persistent"] = False
|
||||||
if "guiname" not in app.keys():
|
if "guiname" not in app.keys():
|
||||||
app["guiname"] = app["name"]
|
app["guiname"] = app["name"]
|
||||||
|
if "white" not in app.keys():
|
||||||
|
app["white"] = False
|
||||||
|
if "path" not in app.keys():
|
||||||
|
app["path"] = "."
|
||||||
cmd = app["cmd"]
|
cmd = app["cmd"]
|
||||||
#remove non existing apps
|
#remove non existing apps
|
||||||
if type(cmd) == str and not os.path.isfile(cmd):
|
#if type(cmd) == str and not os.path.isfile(cmd):
|
||||||
config.Apps.remove(app)
|
#config.Apps.remove(app)
|
||||||
logging.warning("Removed app "+app["name"])
|
#logging.warning("Removed app "+app["name"])
|
||||||
|
|
||||||
|
|
||||||
runner = AppRunner()
|
runner = AppRunner()
|
||||||
runner.start()
|
runner.start()
|
||||||
|
|
||||||
|
#runner.setFilter("5_crazy", MakeBrightnessExprFilter("0.5+0.25*sin(x/3)/x"))
|
||||||
run(host=config.WebHost, port=config.WebPort)
|
run(host=config.WebHost, port=config.WebPort)
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
|
@ -39,17 +39,17 @@ if hash cargo 2>/dev/null; then
|
|||||||
cp -f external/pixelfoo/target/release/dualmaze apps/
|
cp -f external/pixelfoo/target/release/dualmaze apps/
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Getting mathpixel"
|
#echo "Getting mathpixel"
|
||||||
mkdir -p external
|
#mkdir -p external
|
||||||
pushd external
|
#pushd external
|
||||||
if [ ! -d "mathpixel" ]; then
|
#if [ ! -d "mathpixel" ]; then
|
||||||
git clone https://git.chaospott.de/andreas/mathpixel.git
|
# git clone https://git.chaospott.de/andreas/mathpixel.git
|
||||||
fi
|
#fi
|
||||||
pushd mathpixel
|
#pushd mathpixel
|
||||||
git pull
|
#git pull
|
||||||
bash setup.sh
|
#bash setup.sh
|
||||||
popd
|
#popd
|
||||||
popd
|
#popd
|
||||||
|
|
||||||
echo "Getting pixelthud"
|
echo "Getting pixelthud"
|
||||||
mkdir -p external
|
mkdir -p external
|
||||||
|
Loading…
Reference in New Issue
Block a user