Framework-LED-Matrix 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cli.py +669 -0
- framework_led_matrix/__init__.py +0 -0
- framework_led_matrix/apps/__init__.py +0 -0
- framework_led_matrix/apps/background_runner.py +125 -0
- framework_led_matrix/apps/runtime.py +185 -0
- framework_led_matrix/core/__init__.py +0 -0
- framework_led_matrix/core/led_commands.py +406 -0
- framework_led_matrix/core/math_engine.py +294 -0
- framework_led_matrix/simulations/BihamMiddletonLevineTrafficModel.py +238 -0
- framework_led_matrix/simulations/HardyPomeauPazzis.py +241 -0
- framework_led_matrix/simulations/__init__.py +0 -0
- framework_led_matrix/simulations/inner_totalistic.py +47 -0
- framework_led_matrix/simulations/outer_totalistic.py +112 -0
- framework_led_matrix/utils/__init__.py +0 -0
- framework_led_matrix/utils/anagrams.py +39 -0
- framework_led_matrix/utils/text_rendering.py +281 -0
- framework_led_matrix-0.1.1.dist-info/METADATA +159 -0
- framework_led_matrix-0.1.1.dist-info/RECORD +22 -0
- framework_led_matrix-0.1.1.dist-info/WHEEL +5 -0
- framework_led_matrix-0.1.1.dist-info/entry_points.txt +2 -0
- framework_led_matrix-0.1.1.dist-info/licenses/LICENSE +674 -0
- framework_led_matrix-0.1.1.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Background Runner (Screensaver) for LED Matrix.
|
|
4
|
+
Cycles through various scenes randomly.
|
|
5
|
+
"""
|
|
6
|
+
import time
|
|
7
|
+
import random
|
|
8
|
+
import sys
|
|
9
|
+
from typing import List
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
from framework_led_matrix.core.led_commands import log, reset_modules
|
|
13
|
+
from framework_led_matrix.apps.runtime import (
|
|
14
|
+
game_of_life_totalistic_sim,
|
|
15
|
+
run_outer_totalistic_simulation,
|
|
16
|
+
run_inner_totalistic_simulation,
|
|
17
|
+
run_bml_simulation,
|
|
18
|
+
run_draw_anagram_on_matrix,
|
|
19
|
+
run_math_funs_game_of_life,
|
|
20
|
+
show_random_graphs,
|
|
21
|
+
random_greyscale_animation
|
|
22
|
+
)
|
|
23
|
+
from framework_led_matrix.simulations.HardyPomeauPazzis import run_hpp_simulation
|
|
24
|
+
except ImportError as e:
|
|
25
|
+
print(f"Background Runner Import Error: {e}")
|
|
26
|
+
sys.exit(1)
|
|
27
|
+
|
|
28
|
+
def scene_game_of_life():
|
|
29
|
+
steps = random.randint(100, 300)
|
|
30
|
+
initial_board = None
|
|
31
|
+
log(f"[BG] Starting Game of Life for {steps} steps...")
|
|
32
|
+
game_of_life_totalistic_sim(initial_board, timesteps=steps, delay_sec=0.05, which='both')
|
|
33
|
+
|
|
34
|
+
def scene_outer_totalistic():
|
|
35
|
+
b_rule = [random.randint(1, 8) for _ in range(random.randint(1, 3))]
|
|
36
|
+
s_rule = [random.randint(1, 8) for _ in range(random.randint(1, 3))]
|
|
37
|
+
steps = random.randint(100, 200)
|
|
38
|
+
log(f"[BG] Starting Outer-Totalistic (B{b_rule}/S{s_rule}) for {steps} steps...")
|
|
39
|
+
run_outer_totalistic_simulation(None, b_rule, s_rule, timesteps=steps, delay_sec=0.05)
|
|
40
|
+
|
|
41
|
+
def scene_inner_totalistic():
|
|
42
|
+
rule = random.randint(100, 1000)
|
|
43
|
+
steps = random.randint(100, 200)
|
|
44
|
+
log(f"[BG] Starting Inner-Totalistic Rule {rule} for {steps} steps...")
|
|
45
|
+
run_inner_totalistic_simulation(None, rule, timesteps=steps, delay_sec=0.05)
|
|
46
|
+
|
|
47
|
+
def scene_bml_traffic():
|
|
48
|
+
density = random.uniform(0.3, 0.45)
|
|
49
|
+
steps = random.randint(400, 800)
|
|
50
|
+
log(f"[BG] Starting BML Traffic (density={density:.2f}) for {steps} steps...")
|
|
51
|
+
run_bml_simulation(density, timesteps=steps, delay_sec=0.02)
|
|
52
|
+
|
|
53
|
+
def scene_hpp_gas():
|
|
54
|
+
density = random.uniform(0.4, 0.6)
|
|
55
|
+
steps = random.randint(300, 600)
|
|
56
|
+
log(f"[BG] Starting HPP Lattice Gas (density={density:.2f}) for {steps} steps...")
|
|
57
|
+
run_hpp_simulation(None, density, steps, 0.03, 'both')
|
|
58
|
+
|
|
59
|
+
def scene_anagrams():
|
|
60
|
+
words = random.randint(2, 4)
|
|
61
|
+
log(f"[BG] Drawing anagrams for {words} random words...")
|
|
62
|
+
run_draw_anagram_on_matrix(words, 'both')
|
|
63
|
+
|
|
64
|
+
def scene_math_gof():
|
|
65
|
+
steps = random.randint(50, 150)
|
|
66
|
+
log(f"[BG] Starting Math-GoL for {steps} steps...")
|
|
67
|
+
run_math_funs_game_of_life(steps, 0.05)
|
|
68
|
+
|
|
69
|
+
def scene_math_graphs():
|
|
70
|
+
num = random.randint(3, 7)
|
|
71
|
+
log(f"[BG] Showing {num} random math graphs...")
|
|
72
|
+
show_random_graphs(num, 3.0, 'both')
|
|
73
|
+
|
|
74
|
+
def scene_random_noise():
|
|
75
|
+
duration = random.randint(5, 15)
|
|
76
|
+
log(f"[BG] Showing random noise for {duration} seconds...")
|
|
77
|
+
random_greyscale_animation(animate=True, duration_sec=duration)
|
|
78
|
+
|
|
79
|
+
SCENES = [
|
|
80
|
+
scene_game_of_life,
|
|
81
|
+
scene_outer_totalistic,
|
|
82
|
+
scene_inner_totalistic,
|
|
83
|
+
scene_bml_traffic,
|
|
84
|
+
scene_hpp_gas,
|
|
85
|
+
scene_anagrams,
|
|
86
|
+
scene_math_gof,
|
|
87
|
+
scene_math_graphs,
|
|
88
|
+
scene_random_noise
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
def run_background_mode():
|
|
92
|
+
"""
|
|
93
|
+
Main loop for background runner.
|
|
94
|
+
"""
|
|
95
|
+
log("--- Starting Background Runner ---")
|
|
96
|
+
log("Press Ctrl+C to stop.")
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
while True:
|
|
100
|
+
# Pick a random scene
|
|
101
|
+
scene = random.choice(SCENES)
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
scene()
|
|
105
|
+
# Brief pause between scenes
|
|
106
|
+
time.sleep(1.0)
|
|
107
|
+
reset_modules()
|
|
108
|
+
except KeyboardInterrupt:
|
|
109
|
+
raise
|
|
110
|
+
except Exception as e:
|
|
111
|
+
log(f"[BG] Error in scene {scene.__name__}: {e}")
|
|
112
|
+
# Log traceback for debugging if needed
|
|
113
|
+
# import traceback
|
|
114
|
+
# traceback.print_exc()
|
|
115
|
+
time.sleep(2.0)
|
|
116
|
+
reset_modules()
|
|
117
|
+
|
|
118
|
+
except KeyboardInterrupt:
|
|
119
|
+
log("\nBackground runner stopped by user.")
|
|
120
|
+
finally:
|
|
121
|
+
reset_modules()
|
|
122
|
+
log("Modules reset.")
|
|
123
|
+
|
|
124
|
+
if __name__ == "__main__":
|
|
125
|
+
run_background_mode()
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
from framework_led_matrix.core.led_commands import log, draw_greyscale_on_board, set_led, clear_graph, start_animation, stop_animation, draw_matrix_on_board, reset_modules, WIDTH, HEIGHT, coordinates_to_matrix
|
|
2
|
+
from framework_led_matrix.simulations.BihamMiddletonLevineTrafficModel import run_bml
|
|
3
|
+
from framework_led_matrix.utils.anagrams import draw_anagram_on_matrix, anagrams, ensure_nltk_words
|
|
4
|
+
# Note: 'words' corpus is imported inside anagrams.py or needs to be available
|
|
5
|
+
from framework_led_matrix.utils.text_rendering import draw_text_vertical
|
|
6
|
+
from framework_led_matrix.core.math_engine import MATH_OPERATIONS, pick_largest_graph
|
|
7
|
+
from framework_led_matrix.simulations.inner_totalistic import run_totalistic_ca
|
|
8
|
+
from framework_led_matrix.simulations.outer_totalistic import run_outer_totalistic_ca, game_of_life_rules, STARTING_STATES_GOF
|
|
9
|
+
from typing import List, Optional
|
|
10
|
+
from framework_led_matrix.simulations.HardyPomeauPazzis import run_hpp_simulation, create_hpp_board_np
|
|
11
|
+
import random
|
|
12
|
+
import time
|
|
13
|
+
import numpy as np
|
|
14
|
+
import nltk
|
|
15
|
+
from nltk.corpus import words
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def run_hpp_with_math(density: float = 0.3, timesteps: int = 500, delay_sec: float = 0.1, graphs_count: int = 5):
|
|
19
|
+
log("HPP: Running HPP simulation with math function graphs.")
|
|
20
|
+
for func in random.sample(MATH_OPERATIONS, graphs_count):
|
|
21
|
+
func = pick_largest_graph(func)
|
|
22
|
+
draw_matrix_on_board(func)
|
|
23
|
+
func = create_hpp_board_np(initial_state=np.array(func))
|
|
24
|
+
time.sleep(3)
|
|
25
|
+
run_hpp_simulation(
|
|
26
|
+
initial_state=func,
|
|
27
|
+
density=density,
|
|
28
|
+
timesteps=timesteps,
|
|
29
|
+
delay_sec=delay_sec,
|
|
30
|
+
which='both'
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
def run_outer_totalistic_simulation(initial_state: Optional[List[List[int]]] = None, b_rule: Optional[List[int]] = None, s_rule: Optional[List[int]] = None, timesteps: int = 100, delay_sec: float = 0.1, oscilation_max_steps: int = 20, still_board_max_steps: int = 10, empty_board_max_steps: int = 5):
|
|
34
|
+
# normalize defaults to avoid mutable default arguments and satisfy type annotations
|
|
35
|
+
if b_rule is None:
|
|
36
|
+
b_rule = [3]
|
|
37
|
+
if s_rule is None:
|
|
38
|
+
s_rule = [2,3]
|
|
39
|
+
|
|
40
|
+
initial_state_np = np.random.randint(0, 2, size=(HEIGHT, WIDTH), dtype=int) if initial_state is None else np.array(initial_state, dtype=int)
|
|
41
|
+
log(f"Running Outer-Totalistic CA: B{b_rule}/S{s_rule} for {timesteps} steps.")
|
|
42
|
+
all_generations = run_outer_totalistic_ca(initial_state_np, timesteps, b_rule, s_rule)
|
|
43
|
+
oscilation_counter = 0
|
|
44
|
+
still_board_counter = 0
|
|
45
|
+
empty_board_counter = 0
|
|
46
|
+
for t in range(all_generations.shape[0]):
|
|
47
|
+
frame_np = all_generations[t]
|
|
48
|
+
frame_list = frame_np.tolist()
|
|
49
|
+
draw_matrix_on_board(frame_list, which='both')
|
|
50
|
+
time.sleep(delay_sec)
|
|
51
|
+
#oscilation
|
|
52
|
+
if all_generations[t].tolist() == all_generations[t-2].tolist() and t >=2:
|
|
53
|
+
oscilation_counter +=1
|
|
54
|
+
if oscilation_counter >= oscilation_max_steps:
|
|
55
|
+
log(f"oscillation at step {t-oscilation_max_steps}. Ending simulation.")
|
|
56
|
+
break
|
|
57
|
+
else:
|
|
58
|
+
oscilation_counter = 0
|
|
59
|
+
|
|
60
|
+
# still life
|
|
61
|
+
if np.all(frame_np == 0):
|
|
62
|
+
still_board_counter += 1
|
|
63
|
+
if still_board_counter >= still_board_max_steps:
|
|
64
|
+
log(f"still life detected at step {t-still_board_max_steps}. Ending simulation.")
|
|
65
|
+
break
|
|
66
|
+
else:
|
|
67
|
+
still_board_counter = 0
|
|
68
|
+
|
|
69
|
+
# empty board
|
|
70
|
+
if np.all(frame_np == 0):
|
|
71
|
+
empty_board_counter += 1
|
|
72
|
+
if empty_board_counter >= empty_board_max_steps:
|
|
73
|
+
log(f"empty board detected at step {t-empty_board_max_steps}. Ending simulation.")
|
|
74
|
+
break
|
|
75
|
+
else:
|
|
76
|
+
empty_board_counter = 0
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def game_of_life_totalistic_sim(initial_board: Optional[List[List[int]]] = None, generations: int = 200, delay_sec: float = 0.1, which: str = 'both'):
|
|
80
|
+
initial_state_np = np.array(initial_board, dtype=int) if initial_board is not None else np.random.randint(0, 2, size=(HEIGHT, WIDTH), dtype=int)
|
|
81
|
+
b_rule = game_of_life_rules['Original']['B']
|
|
82
|
+
s_rule = game_of_life_rules['Original']['S']
|
|
83
|
+
run_outer_totalistic_simulation(initial_state_np.tolist(), b_rule, s_rule, generations, delay_sec)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def run_bml_simulation(density: float = 0.3, timesteps: int = 100, delay_sec: float = 0.1):
|
|
87
|
+
run_bml(density=density, steps=timesteps, delay_sec=delay_sec)
|
|
88
|
+
|
|
89
|
+
def run_inner_totalistic_simulation(initial_state: Optional[List[List[int]]] = None, rule_number: int = 777, timesteps: int = 200, delay_sec: float = 0.1):
|
|
90
|
+
initial_state_np = np.array(initial_state, dtype=int) if initial_state is not None else np.random.randint(0, 2, size=(HEIGHT, WIDTH), dtype=int)
|
|
91
|
+
log(f"Running Inner-Totalistic CA: rule={rule_number} for {timesteps} steps.")
|
|
92
|
+
all_generations = run_totalistic_ca(initial_state_np, timesteps, rule_number)
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
for t in range(all_generations.shape[0]):
|
|
96
|
+
frame_np = all_generations[t]
|
|
97
|
+
frame_list = frame_np.tolist()
|
|
98
|
+
draw_matrix_on_board(frame_list, which='both')
|
|
99
|
+
time.sleep(delay_sec)
|
|
100
|
+
|
|
101
|
+
except KeyboardInterrupt:
|
|
102
|
+
log("Animation stopped by user.")
|
|
103
|
+
clear_graph()
|
|
104
|
+
|
|
105
|
+
def random_greyscale_animation(animate: bool = True, duration_seconds: int = 10):
|
|
106
|
+
log(f"random_greyscale_animation: start animate={animate} duration_seconds={duration_seconds}")
|
|
107
|
+
matrix = [[0 for _ in range(WIDTH)] for _ in range(HEIGHT)]
|
|
108
|
+
if animate:
|
|
109
|
+
log("random_greyscale_animation: starting hardware animation")
|
|
110
|
+
start_animation()
|
|
111
|
+
else:
|
|
112
|
+
log("random_greyscale_animation: ensuring animation stopped")
|
|
113
|
+
stop_animation()
|
|
114
|
+
|
|
115
|
+
timeout_start = time.time()
|
|
116
|
+
frames = 0
|
|
117
|
+
while time.time() < timeout_start + duration_seconds:
|
|
118
|
+
frames += 1
|
|
119
|
+
# fill matrix with random brightness
|
|
120
|
+
for row in range(HEIGHT):
|
|
121
|
+
for col in range(WIDTH):
|
|
122
|
+
brightness = int(random.triangular(0, 255, 0))
|
|
123
|
+
set_led(matrix, row, col, brightness)
|
|
124
|
+
draw_greyscale_on_board(matrix, which='both')
|
|
125
|
+
# log periodically to avoid overwhelming logs
|
|
126
|
+
if frames % 20 == 0:
|
|
127
|
+
remaining = max(0, timeout_start + duration_seconds - time.time())
|
|
128
|
+
log(f"random_greyscale_animation: frames={frames} time_remaining={remaining:.1f}s")
|
|
129
|
+
log(f"random_greyscale_animation: completed frames={frames}")
|
|
130
|
+
stop_animation()
|
|
131
|
+
clear_graph()
|
|
132
|
+
log("random_greyscale_animation: stopped animation and cleared display")
|
|
133
|
+
|
|
134
|
+
def run_anagrams_game_of_life(word_limit: int = 4, generations: int = 100, delay_sec: float = 0.1, which: str = 'both'):
|
|
135
|
+
log("runtime.py: run_anagrams_game_of_life() entry")
|
|
136
|
+
ensure_nltk_words()
|
|
137
|
+
eligible_words = [word for word in words.words() if len(word) <= 7]
|
|
138
|
+
actual_limit = min(word_limit, len(eligible_words))
|
|
139
|
+
for word in random.sample(eligible_words, actual_limit):
|
|
140
|
+
ana_lst = anagrams(word)
|
|
141
|
+
ana_lst.add(word)
|
|
142
|
+
ana_lst = list(ana_lst)
|
|
143
|
+
for w in ana_lst:
|
|
144
|
+
log(f"runtime.py: run_anagrams_game_of_life() rendering word '{w}'")
|
|
145
|
+
matrix = draw_text_vertical(w, which=which)
|
|
146
|
+
time.sleep(2)
|
|
147
|
+
start_animation()
|
|
148
|
+
time.sleep(3)
|
|
149
|
+
stop_animation()
|
|
150
|
+
draw_text_vertical(w, which=which)
|
|
151
|
+
game_of_life_totalistic_sim(initial_board=matrix, generations=generations, delay_sec=delay_sec, which=which)
|
|
152
|
+
|
|
153
|
+
def run_draw_anagram_on_matrix(word_limit: int = 3, which: str = 'both'):
|
|
154
|
+
log("runtime.py: run_draw_anagram_on_matrix() entry")
|
|
155
|
+
ensure_nltk_words()
|
|
156
|
+
eligible_words = [word for word in words.words() if len(word) <= 7]
|
|
157
|
+
actual_limit = min(word_limit, len(eligible_words))
|
|
158
|
+
for word in random.sample(eligible_words, actual_limit):
|
|
159
|
+
draw_anagram_on_matrix(word, which=which, animate=True)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def run_math_funs_game_of_life(generations: int = 100, delay_sec: float = 0.1):
|
|
163
|
+
for func in random.sample(MATH_OPERATIONS, len(MATH_OPERATIONS)):
|
|
164
|
+
func = pick_largest_graph(func)
|
|
165
|
+
draw_matrix_on_board(func)
|
|
166
|
+
time.sleep(2)
|
|
167
|
+
game_of_life_totalistic_sim(initial_board=func, generations=generations, delay_sec=delay_sec, which='both')
|
|
168
|
+
|
|
169
|
+
def show_random_graphs(num_graphs: int = 5, delay_sec: float = 2.0, which: str = 'both'):
|
|
170
|
+
log(f"show_random_graphs: start num_graphs={num_graphs} delay_sec={delay_sec} which={which}")
|
|
171
|
+
for i in range(num_graphs):
|
|
172
|
+
func = random.choice(MATH_OPERATIONS)
|
|
173
|
+
log(f"show_random_graphs: graph {i+1}/{num_graphs}, selected function {func.__name__}")
|
|
174
|
+
graph_matrix = pick_largest_graph(func)
|
|
175
|
+
draw_matrix_on_board(graph_matrix, which=which)
|
|
176
|
+
time.sleep(delay_sec)
|
|
177
|
+
log("show_random_graphs: completed all graphs, clearing display")
|
|
178
|
+
clear_graph()
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
if __name__ == "__main__":
|
|
182
|
+
try:
|
|
183
|
+
run_math_funs_game_of_life(generations=300, delay_sec=0.001)
|
|
184
|
+
finally:
|
|
185
|
+
reset_modules()
|
|
File without changes
|