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 <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <threads.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int Nx, Ny;
|
||||
@ -156,7 +156,7 @@ int PFT_HandlePixelflutMessage(uint8_t *buffer, int size, int sock){
|
||||
return begin-(char*)buffer;
|
||||
}
|
||||
|
||||
int PFT_HandleClient(void* sock_){
|
||||
void* PFT_HandleClient(void* sock_){
|
||||
int sock = (int)(size_t)sock_;
|
||||
PFT_ClientThreadCount++;
|
||||
uint8_t buf[1024];
|
||||
@ -172,7 +172,7 @@ int PFT_HandleClient(void* sock_){
|
||||
}
|
||||
|
||||
|
||||
int PFT_RunServer(void* _){
|
||||
void* PFT_RunServer(void* _){
|
||||
int client_sock;
|
||||
socklen_t addr_len;
|
||||
struct sockaddr_in addr;
|
||||
@ -220,9 +220,9 @@ int PFT_RunServer(void* _){
|
||||
while(1){
|
||||
client_sock = accept(server_sock, (struct sockaddr*)&addr, &addr_len);
|
||||
if(client_sock > 0){
|
||||
thrd_t thread;
|
||||
thrd_create(&thread, PFT_HandleClient, (void*)(size_t)client_sock);
|
||||
thrd_detach(thread);
|
||||
pthread_t thread;
|
||||
pthread_create(&thread, NULL, PFT_HandleClient, (void*)(size_t)client_sock);
|
||||
pthread_detach(thread);
|
||||
}
|
||||
}
|
||||
close(server_sock);
|
||||
@ -235,8 +235,8 @@ int main(int argc, char **argv) {
|
||||
buf = (uint8_t*)malloc(Nx*Ny*3);
|
||||
port = atoi(argv[3]);
|
||||
if (port == 0) port = 4444;
|
||||
thrd_t thread;
|
||||
thrd_create(&thread, PFT_RunServer, NULL);
|
||||
pthread_t thread;
|
||||
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
|
||||
NumLines = 7
|
||||
LineGenerationRate = 0.2
|
||||
time_ms = 10
|
||||
time_ms = 20
|
||||
try:
|
||||
time_ms = int(sys.argv[3])
|
||||
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 sys
|
||||
import time
|
||||
|
||||
|
||||
Nx = int(sys.argv[1])
|
||||
Ny = int(sys.argv[2])
|
||||
|
||||
param = 10
|
||||
|
||||
p1 = 20
|
||||
p2 = 10
|
||||
try:
|
||||
param = int(sys.argv[3])
|
||||
param = sys.argv[3]
|
||||
p = param.split(",")
|
||||
p1 = int(p[0])
|
||||
p2 = int(p[1])
|
||||
except:
|
||||
pass
|
||||
|
||||
buffera = bytearray(b"\x00" * (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:
|
||||
|
||||
os.write(1, buffera)
|
||||
time.sleep(param*0.01)
|
||||
time.sleep(p1*0.01)
|
||||
if cnt == p2-1:
|
||||
cnt = 0
|
||||
os.write(1, bufferb)
|
||||
time.sleep(param*0.01)
|
||||
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
|
||||
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 -
|
||||
|
||||
exit 0
|
||||
|
72
config.py
72
config.py
@ -5,7 +5,7 @@ ScreenX = 80
|
||||
#height
|
||||
ScreenY = 40
|
||||
|
||||
DefaultBrightness = 1.0
|
||||
DefaultBrightness = 0.6
|
||||
|
||||
Serial = "/dev/ttyACM0"
|
||||
|
||||
@ -15,6 +15,7 @@ NoDataTimeout = 40
|
||||
LogLevel = logging.DEBUG
|
||||
|
||||
UseGui = True
|
||||
GuiFPS = 20
|
||||
GuiScaleFactor = 15
|
||||
|
||||
WebHost = "0.0.0.0"
|
||||
@ -22,25 +23,52 @@ WebPort = 8000
|
||||
|
||||
# first app is always running as default
|
||||
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": "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": "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": "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
|
||||
{"guiname": "Colored noise", "name": "cnoise", "cmd": "apps/cnoise", "persistent": False},
|
||||
{"guiname": "Dual Moodlight", "name": "bimood", "cmd": "apps/bimood", "persistent": False},
|
||||
{"guiname": "Maze", "name": "maze", "cmd": "apps/maze", "persistent": False},
|
||||
{"guiname": "Dual Maze", "name": "dualmaze", "cmd": "apps/dualmaze", "persistent": False, "persistent": False},
|
||||
{"guiname": "Predator & Prey", "name": "predprey", "cmd": "apps/predprey", "persistent": False},
|
||||
{"guiname": "Congress noise", "name": "cnoise", "cmd": "pixelfoo/target/release/cnoise", "persistent": False},
|
||||
{"guiname": "Game of Life", "name": "life", "cmd": "pixelfoo/target/release/life", "persistent": False},
|
||||
{"guiname": "Matrix Code", "name": "matrix-code", "cmd": "pixelfoo/target/release/matrix-code", "persistent": False},
|
||||
{"guiname": "Lorenz Attractor", "name": "lorenz", "cmd": "pixelfoo/target/release/lorenz", "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": "Wget Video/Gif/Images", "name": "wget", "cmd": "apps/wget.sh", "persistent": False},
|
||||
@ -53,14 +81,14 @@ Apps = [
|
||||
]
|
||||
|
||||
# load additional apps from config/
|
||||
import os
|
||||
import os.path
|
||||
configs = os.listdir("configs/")
|
||||
import importlib
|
||||
for config in configs:
|
||||
file, ext = os.path.splitext(os.path.basename(config))
|
||||
if ext != ".py" or file[0] == "_":
|
||||
continue
|
||||
modname = "configs."+file
|
||||
lib = importlib.import_module(modname)
|
||||
Apps += lib.Apps
|
||||
# import os
|
||||
# import os.path
|
||||
# configs = os.listdir("configs/")
|
||||
# import importlib
|
||||
# for config in configs:
|
||||
# file, ext = os.path.splitext(os.path.basename(config))
|
||||
# if ext != ".py" or file[0] == "_":
|
||||
# continue
|
||||
# modname = "configs."+file
|
||||
# lib = importlib.import_module(modname)
|
||||
# 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>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Andreas <i>DEBUG</i> Interface</h1>
|
||||
<body style="display: grid; grid-template-columns: auto auto auto;">
|
||||
<h1 style="grid-column: 1 / 4; text-align: center;">Andreas <i>production-ready</i> Interface</h1>
|
||||
|
||||
<div>
|
||||
<h2>Kommando:</h2>
|
||||
<form id='in' onSubmit="return request()">
|
||||
<select id="list"></select>
|
||||
@ -19,17 +20,49 @@
|
||||
<input id="brightness" value=1.0 /><br/>
|
||||
<button id="sendbrightness">Setzen</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<div style="border-left: solid 1px; padding-left: 10px;">
|
||||
<h2>Gamma:</h2>
|
||||
<form id='gammeform' onSubmit="return setgamma()">
|
||||
Rot: <input id="gammar" value=2.8 /><br/>
|
||||
Grün: <input id="gammag" 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>
|
||||
</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>
|
||||
<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>
|
||||
function getRaw(from, callback){
|
||||
var xhttp = new XMLHttpRequest();
|
||||
@ -105,7 +138,8 @@
|
||||
let r = document.getElementById('gammar').value;
|
||||
let g = document.getElementById('gammag').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(){});
|
||||
return false;
|
||||
}
|
||||
@ -117,13 +151,61 @@
|
||||
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(){
|
||||
getRaw("/apps/log", function(text){
|
||||
document.getElementById('logs').innerText = text;
|
||||
document.getElementById('logs').value = text;
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
228
main.py
228
main.py
@ -12,7 +12,10 @@ import time
|
||||
import sys
|
||||
import logging
|
||||
import math
|
||||
|
||||
import numpy as np
|
||||
import string
|
||||
from collections import OrderedDict
|
||||
import scipy.misc
|
||||
logging.basicConfig(filename='pixelserver.log', level=config.LogLevel)
|
||||
running = True
|
||||
|
||||
@ -72,6 +75,16 @@ class LogReader(threading.Thread):
|
||||
def stop(self):
|
||||
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 #
|
||||
########################################################################
|
||||
@ -84,6 +97,7 @@ if config.UseGui:
|
||||
self.cv = threading.Condition()
|
||||
self.datasource = datasource.addListener(self.cv)
|
||||
def run(self):
|
||||
last_frame = time.time()
|
||||
logging.info("Starting GUI")
|
||||
sf = config.GuiScaleFactor
|
||||
screen = pygame.display.set_mode((sf*config.ScreenX, sf*config.ScreenY))
|
||||
@ -93,14 +107,26 @@ if config.UseGui:
|
||||
pass
|
||||
with self.cv:
|
||||
self.cv.wait()
|
||||
data = self.datasource.getData()
|
||||
screen.fill((0, 255, 0))
|
||||
frame = self.datasource.getData()
|
||||
screen.fill((0, 0, 0))
|
||||
if frame.channels == 3:
|
||||
for x in range(config.ScreenX):
|
||||
for y in range(config.ScreenY):
|
||||
i = x+y*config.ScreenX
|
||||
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))
|
||||
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()
|
||||
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")
|
||||
def join(self):
|
||||
with self.cv:
|
||||
@ -128,19 +154,26 @@ class SerialWriter(threading.Thread):
|
||||
logging.info("Serial Opened")
|
||||
with self.cv:
|
||||
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:
|
||||
buf = bytearray(b"\x00")*3*256
|
||||
buf = bytearray(b"\x00")*4*256
|
||||
for i in range(256):
|
||||
apply = lambda x, g: max(0, min(255, int(math.pow(x/255, g)*255)))
|
||||
buf[i] = apply(i, self.r)
|
||||
buf[i+256] = apply(i, self.g)
|
||||
buf[i+512] = apply(i, self.b)
|
||||
buf[i+512+256] = apply(i, self.w)
|
||||
ser.write(b"\x02")
|
||||
ser.write(buf)
|
||||
self.updateGamma = False
|
||||
if frame.channels == 3:
|
||||
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()
|
||||
except Exception as e:
|
||||
if ser != None:
|
||||
@ -154,9 +187,9 @@ class SerialWriter(threading.Thread):
|
||||
with self.cv:
|
||||
self.cv.notify_all()
|
||||
super().join()
|
||||
def setGamma(self, r, g, b):
|
||||
def setGamma(self, r, g, b, w):
|
||||
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.cv.notify_all()
|
||||
|
||||
@ -164,31 +197,37 @@ class SerialWriter(threading.Thread):
|
||||
# App #
|
||||
########################################################################
|
||||
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__()
|
||||
#start app
|
||||
if type(cmd) != list:
|
||||
cmd = [cmd,]
|
||||
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.cv = threading.Condition()
|
||||
self.watchdog = WatchDog(lambda: self.isAppTimedOut(), lambda: self.terminateApp())
|
||||
self.watchdog.start()
|
||||
self.logreader = LogReader(self)
|
||||
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.listener = listener
|
||||
self.is_persistent = is_persistent
|
||||
self.is_white = is_white
|
||||
def run(self):
|
||||
while running and self.running and self.alive():
|
||||
oshandle = self.app.stdout.fileno()
|
||||
try:
|
||||
data = os.read(oshandle, config.ScreenX*config.ScreenY*3)
|
||||
assert len(data) == config.ScreenX*config.ScreenY*3
|
||||
bytes = 4 if self.is_white else 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.datasource.pushData(data)
|
||||
self.datasource.pushData(frame)
|
||||
except Exception as e:
|
||||
logging.debug("Exception in App.run")
|
||||
with self.listener:
|
||||
@ -197,6 +236,8 @@ class App(threading.Thread):
|
||||
self.logreader.stop()
|
||||
self.watchdog.join()
|
||||
self.logreader.join()
|
||||
self.app.wait()
|
||||
logging.debug("App stopped")
|
||||
def alive(self):
|
||||
return self.app.poll() == None
|
||||
def stop(self):
|
||||
@ -206,6 +247,8 @@ class App(threading.Thread):
|
||||
self.app.stderr.close()
|
||||
self.watchdog.stop()
|
||||
self.logreader.stop()
|
||||
self.app.wait()
|
||||
logging.debug("App stopped")
|
||||
def getLog(self):
|
||||
return self.logreader.getLog()
|
||||
def terminateApp(self):
|
||||
@ -220,16 +263,17 @@ class App(threading.Thread):
|
||||
class AppRunner(threading.Thread):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.last_crashlog = ""
|
||||
self.currentApp = -1
|
||||
self.requestedApp = 0
|
||||
self.intensity = config.DefaultBrightness
|
||||
self.app = None
|
||||
self.cv = threading.Condition()
|
||||
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.start()
|
||||
self.persistent_apps = {}
|
||||
self.filters = OrderedDict()
|
||||
#start persistent apps
|
||||
for app, i in zip(config.Apps, range(len(config.Apps))):
|
||||
if app["persistent"]:
|
||||
@ -245,13 +289,14 @@ class AppRunner(threading.Thread):
|
||||
logging.info("Requesting app: "+str(app))
|
||||
def startApp(self, i, param=""):
|
||||
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.start()
|
||||
if app["persistent"]:
|
||||
self.persistent_apps[self.currentApp] = newapp
|
||||
return newapp
|
||||
def updateApp(self):
|
||||
try:
|
||||
if self.app != None and not self.app.is_persistent:
|
||||
self.app.stop()
|
||||
self.currentApp = self.requestedApp
|
||||
@ -260,19 +305,25 @@ class AppRunner(threading.Thread):
|
||||
self.app = self.persistent_apps[self.currentApp]
|
||||
else:
|
||||
self.app = self.startApp(self.requestedApp, self.param)
|
||||
except FileNotFoundError as e:
|
||||
print(e)
|
||||
def run(self):
|
||||
logging.info("Starting Apprunner")
|
||||
while running:
|
||||
with self.cv:
|
||||
if self.app == None or not self.app.alive():
|
||||
if self.app != None:
|
||||
self.last_crashlog = self.app.getLog()
|
||||
self.requestedApp = 0
|
||||
if self.requestedApp != None:
|
||||
self.updateApp()
|
||||
self.requestedApp = None
|
||||
data = bytearray(self.app.datasource.getData())
|
||||
for i in range(len(data)):
|
||||
data[i] = int(data[i]*self.intensity)
|
||||
self.datasource.pushData(data)
|
||||
frame = self.app.datasource.getData().clone()
|
||||
#logging.debug("Runner in time: "+str(time.time()-frame.created))
|
||||
for _, f in self.filters.items():
|
||||
frame.buffer = f(frame.buffer)
|
||||
#logging.debug("Runner out time: "+str(time.time()-frame.created))
|
||||
self.datasource.pushData(frame)
|
||||
self.cv.wait()
|
||||
self.serial.join()
|
||||
if config.UseGui:
|
||||
@ -282,8 +333,77 @@ class AppRunner(threading.Thread):
|
||||
if self.app == None:
|
||||
return ""
|
||||
return self.app.getLog()
|
||||
def setGamma(self, r, g, b):
|
||||
self.serial.setGamma(r, g, b)
|
||||
def setGamma(self, r, g, b, w):
|
||||
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 #
|
||||
@ -336,6 +456,10 @@ def apps_start(name, param):
|
||||
def apps_log():
|
||||
return runner.getLog()
|
||||
|
||||
@route("/apps/crashlog")
|
||||
def apps_log():
|
||||
return runner.last_crashlog
|
||||
|
||||
@route("/apps/running")
|
||||
def apps_running():
|
||||
return config.Apps[runner.currentApp]["name"]
|
||||
@ -344,22 +468,56 @@ def apps_running():
|
||||
def index():
|
||||
return bottle.static_file("index.html", root='html')
|
||||
|
||||
@route("/setgamma/<r>/<g>/<b>")
|
||||
def setGamma(r, g, b):
|
||||
@route("/setgamma/<r>/<g>/<b>/<w>")
|
||||
def setGamma(r, g, b, w):
|
||||
r = float(r)
|
||||
g = float(g)
|
||||
b = float(b)
|
||||
runner.setGamma(r, g, b)
|
||||
w = float(w)
|
||||
runner.setGamma(r, g, b, w)
|
||||
return "ok"
|
||||
|
||||
@route("/setbrightness/<i>")
|
||||
def setGamma(i):
|
||||
def setIntensity(i):
|
||||
i = float(i)
|
||||
if i < 0 or i > 1:
|
||||
return "bad_value"
|
||||
runner.intensity = i
|
||||
runner.setFilter("0_intensity", MakeBrightnessFilter(i))
|
||||
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 #
|
||||
########################################################################
|
||||
@ -369,15 +527,21 @@ for app in config.Apps:
|
||||
app["persistent"] = False
|
||||
if "guiname" not in app.keys():
|
||||
app["guiname"] = app["name"]
|
||||
if "white" not in app.keys():
|
||||
app["white"] = False
|
||||
if "path" not in app.keys():
|
||||
app["path"] = "."
|
||||
cmd = app["cmd"]
|
||||
#remove non existing apps
|
||||
if type(cmd) == str and not os.path.isfile(cmd):
|
||||
config.Apps.remove(app)
|
||||
logging.warning("Removed app "+app["name"])
|
||||
#if type(cmd) == str and not os.path.isfile(cmd):
|
||||
#config.Apps.remove(app)
|
||||
#logging.warning("Removed app "+app["name"])
|
||||
|
||||
|
||||
runner = AppRunner()
|
||||
runner.start()
|
||||
|
||||
#runner.setFilter("5_crazy", MakeBrightnessExprFilter("0.5+0.25*sin(x/3)/x"))
|
||||
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/
|
||||
fi
|
||||
|
||||
echo "Getting mathpixel"
|
||||
mkdir -p external
|
||||
pushd external
|
||||
if [ ! -d "mathpixel" ]; then
|
||||
git clone https://git.chaospott.de/andreas/mathpixel.git
|
||||
fi
|
||||
pushd mathpixel
|
||||
git pull
|
||||
bash setup.sh
|
||||
popd
|
||||
popd
|
||||
#echo "Getting mathpixel"
|
||||
#mkdir -p external
|
||||
#pushd external
|
||||
#if [ ! -d "mathpixel" ]; then
|
||||
# git clone https://git.chaospott.de/andreas/mathpixel.git
|
||||
#fi
|
||||
#pushd mathpixel
|
||||
#git pull
|
||||
#bash setup.sh
|
||||
#popd
|
||||
#popd
|
||||
|
||||
echo "Getting pixelthud"
|
||||
mkdir -p external
|
||||
|
Loading…
Reference in New Issue
Block a user