Last active
December 21, 2024 12:43
-
-
Save cyberpunk042/dba9c06d374262a461310d1faf30f492 to your computer and use it in GitHub Desktop.
Axiom Orbits is a creative sandbox game where you build and navigate layered orbit-like structures around a single central “O.” Using two perpendicular “axioms,” you populate each layer’s outer ring of cells with characters, creating intricate patterns. You can then visualize your evolving atomic-like model in 3D, experimenting with various fill…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import curses | |
import logging | |
import math | |
import plotly.graph_objects as go | |
import sys | |
import random | |
logging.basicConfig( | |
filename="layer_axiom_game.log", | |
filemode="w", | |
format="%(asctime)s [%(levelname)s] %(message)s", | |
level=logging.DEBUG | |
) | |
logger = logging.getLogger(__name__) | |
DEFAULT_CHAR = "◦" | |
CENTER_CHAR = "O" | |
data = {} | |
current_layer = 0 | |
current_axiom = 'A' | |
cursor_x, cursor_y = 0, 0 | |
def layer_dimension(layer): | |
return 2*layer + 1 | |
def create_layer_axiom(layer, axiom): | |
dim = layer_dimension(layer) | |
grid = [[DEFAULT_CHAR for _ in range(dim)] for _ in range(dim)] | |
read_only = [[False for _ in range(dim)] for _ in range(dim)] | |
if layer == 0: | |
grid[0][0] = CENTER_CHAR | |
read_only[0][0] = False | |
else: | |
ensure_layer_axiom(layer-1, axiom) | |
prev_grid, prev_read_only = data[(layer-1, axiom)] | |
prev_dim = layer_dimension(layer-1) | |
offset = (dim - prev_dim) // 2 | |
for py in range(prev_dim): | |
for px in range(prev_dim): | |
ch = prev_grid[py][px] | |
if ch == CENTER_CHAR: | |
ch = ' ' | |
grid[py+offset][px+offset] = ch | |
read_only[py+offset][px+offset] = True | |
data[(layer, axiom)] = (grid, read_only) | |
def ensure_layer_axiom(layer, axiom): | |
if (layer, axiom) not in data: | |
create_layer_axiom(layer, axiom) | |
def is_within_bounds(x, y): | |
return (-current_layer <= x <= current_layer and -current_layer <= y <= current_layer) | |
def is_read_only(x, y): | |
grid, ro = data[(current_layer, current_axiom)] | |
center = current_layer | |
gx = x + center | |
gy = y + center | |
return ro[gy][gx] | |
def jump_across(dx, dy): | |
global cursor_x, cursor_y | |
x, y = cursor_x, cursor_y | |
while True: | |
nx, ny = x + dx, y + dy | |
if not is_within_bounds(nx, ny): | |
return False | |
if not is_read_only(nx, ny): | |
cursor_x, cursor_y = nx, ny | |
return True | |
x, y = nx, ny | |
def move_cursor(dx, dy): | |
if jump_across(dx, dy): | |
return | |
def insert_char(ch): | |
grid, read_only = data[(current_layer, current_axiom)] | |
center = current_layer | |
gx = cursor_x + center | |
gy = cursor_y + center | |
if not read_only[gy][gx]: | |
grid[gy][gx] = ch | |
def go_to_layer_axiom(layer, axiom): | |
global current_layer, current_axiom, cursor_x, cursor_y | |
current_layer = layer | |
current_axiom = axiom | |
ensure_layer_axiom(current_layer, current_axiom) | |
cursor_x, cursor_y = -current_layer, -current_layer | |
def get_outer_ring_cells(layer, axiom): | |
grid, ro = data[(layer, axiom)] | |
dim = layer_dimension(layer) | |
center = layer | |
if layer == 0: | |
ch = grid[0][0] | |
return [(0,0,ch)] | |
ring = [] | |
N = layer | |
for y in range(-N, N+1): | |
for x in range(-N, N+1): | |
if max(abs(x),abs(y)) == N: | |
gx = x+center | |
gy = y+center | |
ch = grid[gy][gx] | |
if ch == ' ': | |
continue | |
ring.append((x,y,ch)) | |
return ring | |
def render_3d(filename="matrix_visualization.html"): | |
# A: (x,y,z) = (N*cosθ, N*sinθ, 0) | |
# B: (x,y,z) = (0, N*cosθ, N*sinθ) | |
# C: (x,y,z) = (N*cosθ, 0, N*sinθ) | |
# D: diagonal | |
# x = N*cosθ | |
# y = N*sinθ*(√2/2) | |
# z = N*sinθ*(√2/2) | |
x_data_A, y_data_A, z_data_A, text_data_A = [], [], [], [] | |
x_data_B, y_data_B, z_data_B, text_data_B = [], [], [], [] | |
x_data_C, y_data_C, z_data_C, text_data_C = [], [], [], [] | |
x_data_D, y_data_D, z_data_D, text_data_D = [], [], [], [] | |
max_layer = 0 | |
for (layer, axiom) in data.keys(): | |
if layer > max_layer: | |
max_layer = layer | |
for (layer, axiom) in data.keys(): | |
ring_cells = get_outer_ring_cells(layer, axiom) | |
if len(ring_cells) == 0: | |
if layer == 0 and axiom == 'A': | |
# just center | |
x_data_A.append(0) | |
y_data_A.append(0) | |
z_data_A.append(0) | |
text_data_A.append('O') | |
continue | |
N = layer | |
count = len(ring_cells) | |
ring_cells.sort(key=lambda c: math.atan2(c[1], c[0])) | |
for i, (ox,oy,ch) in enumerate(ring_cells): | |
angle = 2*math.pi*i/count | |
if axiom == 'A': | |
x = N*math.cos(angle) | |
y = N*math.sin(angle) | |
z = 0 | |
x_data_A.append(x) | |
y_data_A.append(y) | |
z_data_A.append(z) | |
text_data_A.append(ch) | |
elif axiom == 'B': | |
x = 0 | |
y = N*math.cos(angle) | |
z = N*math.sin(angle) | |
x_data_B.append(x) | |
y_data_B.append(y) | |
z_data_B.append(z) | |
text_data_B.append(ch) | |
elif axiom == 'C': | |
x = N*math.cos(angle) | |
z = N*math.sin(angle) | |
y = 0 | |
x_data_C.append(x) | |
y_data_C.append(y) | |
z_data_C.append(z) | |
text_data_C.append(ch) | |
else: # D | |
cosθ = math.cos(angle) | |
sinθ = math.sin(angle) | |
x = N*cosθ | |
y = N*sinθ*(math.sqrt(2)/2) | |
z = N*sinθ*(math.sqrt(2)/2) | |
x_data_D.append(x) | |
y_data_D.append(y) | |
z_data_D.append(z) | |
text_data_D.append(ch) | |
fig = go.Figure() | |
fig.add_trace(go.Scatter3d( | |
x=x_data_A, y=y_data_A, z=z_data_A, | |
mode='text', | |
text=text_data_A, | |
textfont=dict(size=12, color='red'), | |
name='A (XY plane)' | |
)) | |
fig.add_trace(go.Scatter3d( | |
x=x_data_B, y=y_data_B, z=z_data_B, | |
mode='text', | |
text=text_data_B, | |
textfont=dict(size=12, color='blue'), | |
name='B (YZ plane)' | |
)) | |
fig.add_trace(go.Scatter3d( | |
x=x_data_C, y=y_data_C, z=z_data_C, | |
mode='text', | |
text=text_data_C, | |
textfont=dict(size=12, color='green'), | |
name='C (XZ plane)' | |
)) | |
fig.add_trace(go.Scatter3d( | |
x=x_data_D, y=y_data_D, z=z_data_D, | |
mode='text', | |
text=text_data_D, | |
textfont=dict(size=12, color='purple'), | |
name='D (Diagonal plane)' | |
)) | |
fig.update_layout( | |
scene=dict( | |
xaxis=dict(title="X", range=[-max_layer, max_layer]), | |
yaxis=dict(title="Y", range=[-max_layer, max_layer]), | |
zaxis=dict(title="Z", range=[-max_layer, max_layer]), | |
camera=dict( | |
center=dict(x=0, y=0, z=0), | |
eye=dict(x=1.25, y=1.25, z=1.25) | |
) | |
), | |
title="3D Visualization (Manual Refresh)", | |
width=1000, height=800 | |
) | |
fig.write_html(filename) | |
def draw_interface(stdscr): | |
stdscr.clear() | |
stdscr.addstr(0,0,f"Layer: {current_layer}, Axiom: {current_axiom}, Pos=({cursor_x},{cursor_y})") | |
stdscr.addstr(1,0,"F1=A(XY), F2=B(YZ), F3=C(XZ), F4=D(Diag) | +/-=layers | Arrows=move | Type=insert | Ctrl+D=exit") | |
stdscr.addstr(2,0,"Refresh matrix_visualization.html manually.") | |
stdscr.addstr(3,0,"A=XY plane, B=YZ plane, C=XZ plane, D=diagonal plane") | |
grid, read_only = data[(current_layer, current_axiom)] | |
dim = layer_dimension(current_layer) | |
center = current_layer | |
VIEW_RADIUS = 5 | |
min_xv = max(cursor_x - VIEW_RADIUS, -current_layer) | |
max_xv = min(cursor_x + VIEW_RADIUS, current_layer) | |
min_yv = max(cursor_y - VIEW_RADIUS, -current_layer) | |
max_yv = min(cursor_y + VIEW_RADIUS, current_layer) | |
offset_line = 5 | |
offset_col = 2 | |
for draw_y in range(min_yv, max_yv+1): | |
row_chars = [] | |
gy = draw_y + center | |
for draw_x in range(min_xv, max_xv+1): | |
gx = draw_x + center | |
ch = grid[gy][gx] | |
display_char = ' ' if read_only[gy][gx] else ch | |
if draw_x == cursor_x and draw_y == cursor_y: | |
if current_layer == 0 and current_axiom == 'A' and cursor_x == 0 and cursor_y == 0: | |
char = display_char | |
else: | |
char = "▮" if display_char != DEFAULT_CHAR else "○" | |
else: | |
char = display_char | |
row_chars.append(char) | |
stdscr.addstr(offset_line+(draw_y - min_yv), offset_col, "".join(row_chars)) | |
stdscr.refresh() | |
def prefill_layers(mode, fillA, fillB, fillC, fillD): | |
# number of layers = max(len(fillA), len(fillB), len(fillC), len(fillD)) | |
max_layers = max(len(fillA), len(fillB), len(fillC), len(fillD)) | |
random.seed(0) | |
axioms = ['A','B','C','D'] | |
fills = {'A': fillA, 'B': fillB, 'C': fillC, 'D': fillD} | |
for layer in range(1, max_layers+1): | |
for axiom in axioms: | |
ensure_layer_axiom(layer, axiom) | |
grid, ro = data[(layer, axiom)] | |
center = layer | |
N = layer | |
ring_coords = [] | |
for y in range(-N, N+1): | |
for x in range(-N, N+1): | |
if max(abs(x),abs(y)) == N: | |
gx = x+center | |
gy = y+center | |
if not ro[gy][gx]: | |
ring_coords.append((gx, gy)) | |
total = len(ring_coords) | |
if total == 0: | |
continue | |
chars_list = fills[axiom] | |
# Determine what char to use for full/partial, or how to pick random | |
if layer <= len(chars_list): | |
base_char = chars_list[layer-1] # single char for this layer | |
else: | |
base_char = None | |
if mode == 'full': | |
# Need a base_char to fill entire ring | |
if base_char is not None: | |
for (gx,gy) in ring_coords: | |
grid[gy][gx] = base_char | |
elif mode == 'partial': | |
# Fill half ring cells with base_char if available | |
if base_char is not None: | |
selected = random.sample(ring_coords, total//2) | |
for (gx,gy) in selected: | |
grid[gy][gx] = base_char | |
# else no fill if no base_char | |
elif mode == 'random': | |
# Partial logic: half ring cells chosen | |
# each chosen cell gets random char from chars_list | |
if len(chars_list) > 0: | |
selected = random.sample(ring_coords, total//2) | |
for (gx,gy) in selected: | |
ch = random.choice(chars_list) | |
grid[gy][gx] = ch | |
# if no chars_list empty, no fill | |
def run(stdscr): | |
global current_layer, current_axiom | |
curses.curs_set(0) | |
stdscr.nodelay(False) | |
stdscr.keypad(True) | |
go_to_layer_axiom(0, 'A') | |
while True: | |
render_3d() | |
draw_interface(stdscr) | |
key = stdscr.getch() | |
if key == -1: | |
continue | |
if key == 4: # Ctrl+D | |
break | |
if key == curses.KEY_F1: | |
go_to_layer_axiom(current_layer, 'A') | |
elif key == curses.KEY_F2: | |
go_to_layer_axiom(current_layer, 'B') | |
elif key == curses.KEY_F3: | |
go_to_layer_axiom(current_layer, 'C') | |
elif key == curses.KEY_F4: | |
go_to_layer_axiom(current_layer, 'D') | |
elif key == ord('+'): | |
go_to_layer_axiom(current_layer+1, current_axiom) | |
elif key == ord('-'): | |
if current_layer > 0: | |
go_to_layer_axiom(current_layer-1, current_axiom) | |
elif key == curses.KEY_LEFT: | |
move_cursor(-1, 0) | |
elif key == curses.KEY_RIGHT: | |
move_cursor(1, 0) | |
elif key == curses.KEY_UP: | |
move_cursor(0, -1) | |
elif key == curses.KEY_DOWN: | |
move_cursor(0, 1) | |
elif 32 <= key < 127: | |
ch = chr(key) | |
insert_char(ch) | |
if __name__ == "__main__": | |
prefill = ('--prefill' in sys.argv) | |
fillA = ['X'] | |
fillB = ['Y'] | |
fillC = ['X'] | |
fillD = ['Y'] | |
mode = 'full' | |
for arg in sys.argv: | |
if arg.startswith('--fillA='): | |
fillA = arg.split('=')[1].split(',') | |
elif arg.startswith('--fillB='): | |
fillB = arg.split('=')[1].split(',') | |
elif arg.startswith('--fillC='): | |
fillC = arg.split('=')[1].split(',') | |
elif arg.startswith('--fillD='): | |
fillD = arg.split('=')[1].split(',') | |
elif arg.startswith('--mode='): | |
mode = arg.split('=')[1] | |
if prefill: | |
prefill_layers(mode, fillA, fillB, fillC, fillD) | |
try: | |
curses.wrapper(run) | |
except KeyboardInterrupt: | |
pass | |
print("Exited.") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import curses | |
import logging | |
import math | |
import plotly.graph_objects as go | |
import sys | |
import random | |
logging.basicConfig( | |
filename="layer_axiom_game.log", | |
filemode="w", | |
format="%(asctime)s [%(levelname)s] %(message)s", | |
level=logging.DEBUG | |
) | |
logger = logging.getLogger(__name__) | |
DEFAULT_CHAR = "◦" | |
CENTER_CHAR = "O" | |
data = {} | |
current_layer = 0 | |
current_axiom = 'A' | |
cursor_x, cursor_y = 0, 0 | |
def layer_dimension(layer): | |
return 2*layer + 1 | |
def create_layer_axiom(layer, axiom): | |
dim = layer_dimension(layer) | |
grid = [[DEFAULT_CHAR for _ in range(dim)] for _ in range(dim)] | |
read_only = [[False for _ in range(dim)] for _ in range(dim)] | |
if layer == 0: | |
grid[0][0] = CENTER_CHAR | |
read_only[0][0] = False | |
else: | |
ensure_layer_axiom(layer-1, axiom) | |
prev_grid, prev_read_only = data[(layer-1, axiom)] | |
prev_dim = layer_dimension(layer-1) | |
offset = (dim - prev_dim) // 2 | |
for py in range(prev_dim): | |
for px in range(prev_dim): | |
ch = prev_grid[py][px] | |
if ch == CENTER_CHAR: | |
ch = ' ' | |
grid[py+offset][px+offset] = ch | |
read_only[py+offset][px+offset] = True | |
data[(layer, axiom)] = (grid, read_only) | |
def ensure_layer_axiom(layer, axiom): | |
if (layer, axiom) not in data: | |
create_layer_axiom(layer, axiom) | |
def is_within_bounds(x, y): | |
return (-current_layer <= x <= current_layer and -current_layer <= y <= current_layer) | |
def is_read_only(x, y): | |
grid, ro = data[(current_layer, current_axiom)] | |
center = current_layer | |
gx = x + center | |
gy = y + center | |
return ro[gy][gx] | |
def jump_across(dx, dy): | |
global cursor_x, cursor_y | |
x, y = cursor_x, cursor_y | |
while True: | |
nx, ny = x + dx, y + dy | |
if not is_within_bounds(nx, ny): | |
return False | |
if not is_read_only(nx, ny): | |
cursor_x, cursor_y = nx, ny | |
return True | |
x, y = nx, ny | |
def move_cursor(dx, dy): | |
if jump_across(dx, dy): | |
return | |
def insert_char(ch): | |
grid, read_only = data[(current_layer, current_axiom)] | |
center = current_layer | |
gx = cursor_x + center | |
gy = cursor_y + center | |
if not read_only[gy][gx]: | |
grid[gy][gx] = ch | |
def go_to_layer_axiom(layer, axiom): | |
global current_layer, current_axiom, cursor_x, cursor_y | |
current_layer = layer | |
current_axiom = axiom | |
ensure_layer_axiom(current_layer, current_axiom) | |
cursor_x, cursor_y = -current_layer, -current_layer | |
def get_outer_ring_cells(layer, axiom): | |
grid, ro = data[(layer, axiom)] | |
dim = layer_dimension(layer) | |
center = layer | |
if layer == 0: | |
ch = grid[0][0] | |
return [(0,0,ch)] | |
ring = [] | |
N = layer | |
for y in range(-N, N+1): | |
for x in range(-N, N+1): | |
if max(abs(x),abs(y)) == N: | |
gx = x+center | |
gy = y+center | |
ch = grid[gy][gx] | |
if ch == ' ': | |
continue | |
ring.append((x,y,ch)) | |
return ring | |
def render_3d(filename="matrix_visualization.html"): | |
# A: (x,y,z) = (N*cosθ, N*sinθ, 0) | |
# B: (x,y,z) = (0, N*cosθ, N*sinθ) | |
# C: (x,y,z) = (N*cosθ, 0, N*sinθ) | |
# D: diagonal | |
# x = N*cosθ | |
# y = N*sinθ*(√2/2) | |
# z = N*sinθ*(√2/2) | |
x_data_A, y_data_A, z_data_A, text_data_A = [], [], [], [] | |
x_data_B, y_data_B, z_data_B, text_data_B = [], [], [], [] | |
x_data_C, y_data_C, z_data_C, text_data_C = [], [], [], [] | |
x_data_D, y_data_D, z_data_D, text_data_D = [], [], [], [] | |
x_data_E, y_data_E, z_data_E, text_data_E = [], [], [], [] | |
x_data_F, y_data_F, z_data_F, text_data_F = [], [], [], [] | |
x_data_H, y_data_H, z_data_H, text_data_H = [], [], [], [] | |
x_data_I, y_data_I, z_data_I, text_data_I = [], [], [], [] | |
x_data_J, y_data_J, z_data_J, text_data_J = [], [], [], [] | |
max_layer = 0 | |
for (layer, axiom) in data.keys(): | |
if layer > max_layer: | |
max_layer = layer | |
for (layer, axiom) in data.keys(): | |
ring_cells = get_outer_ring_cells(layer, axiom) | |
if len(ring_cells) == 0: | |
if layer == 0 and axiom == 'A': | |
# just center | |
x_data_A.append(0) | |
y_data_A.append(0) | |
z_data_A.append(0) | |
text_data_A.append('O') | |
continue | |
N = layer | |
count = len(ring_cells) | |
ring_cells.sort(key=lambda c: math.atan2(c[1], c[0])) | |
for i, (ox,oy,ch) in enumerate(ring_cells): | |
angle = 2*math.pi*i/count | |
if axiom == 'A': | |
x = N*math.cos(angle) | |
y = N*math.sin(angle) | |
z = 0 | |
x_data_A.append(x) | |
y_data_A.append(y) | |
z_data_A.append(z) | |
text_data_A.append(ch) | |
elif axiom == 'B': | |
x = 0 | |
y = N*math.cos(angle) | |
z = N*math.sin(angle) | |
x_data_B.append(x) | |
y_data_B.append(y) | |
z_data_B.append(z) | |
text_data_B.append(ch) | |
elif axiom == 'C': | |
x = N*math.cos(angle) | |
z = N*math.sin(angle) | |
y = 0 | |
x_data_C.append(x) | |
y_data_C.append(y) | |
z_data_C.append(z) | |
text_data_C.append(ch) | |
elif axiom == 'D': | |
cosθ = math.cos(angle) | |
sinθ = math.sin(angle) | |
x = N*cosθ | |
y = N*sinθ*(math.sqrt(2)/2) | |
z = N*sinθ*(math.sqrt(2)/2) | |
x_data_D.append(x) | |
y_data_D.append(y) | |
z_data_D.append(z) | |
text_data_D.append(ch) | |
elif axiom == 'E': | |
cosθ = math.cos(angle) | |
sinθ = math.sin(angle) | |
x = N*sinθ*(math.sqrt(2)/2) | |
y = N*cosθ | |
z = N*sinθ*(math.sqrt(2)/2) | |
x_data_E.append(x) | |
y_data_E.append(y) | |
z_data_E.append(z) | |
text_data_E.append(ch) | |
elif axiom == 'F': | |
cosθ = math.cos(angle) | |
sinθ = math.sin(angle) | |
x = N*sinθ*(math.sqrt(2)/2) | |
y = N*sinθ*(math.sqrt(2)/2) | |
z = N*cosθ | |
x_data_F.append(x) | |
y_data_F.append(y) | |
z_data_F.append(z) | |
text_data_F.append(ch) | |
elif axiom == 'H': | |
cosθ = math.cos(angle) | |
sinθ = math.sin(angle) | |
x = N*cosθ | |
y = N*sinθ*(math.sqrt(2)/2) | |
z = -N*sinθ*(math.sqrt(2)/2) | |
x_data_H.append(x) | |
y_data_H.append(y) | |
z_data_H.append(z) | |
text_data_H.append(ch) | |
elif axiom == 'I': | |
cosθ = math.cos(angle) | |
sinθ = math.sin(angle) | |
x = -N*sinθ*(math.sqrt(2)/2) | |
y = N*cosθ | |
z = N*sinθ*(math.sqrt(2)/2) | |
x_data_I.append(x) | |
y_data_I.append(y) | |
z_data_I.append(z) | |
text_data_I.append(ch) | |
else: # J | |
cosθ = math.cos(angle) | |
sinθ = math.sin(angle) | |
x = -N*sinθ*(math.sqrt(2)/2) | |
y = N*sinθ*(math.sqrt(2)/2) | |
z = N*cosθ | |
x_data_J.append(x) | |
y_data_J.append(y) | |
z_data_J.append(z) | |
text_data_J.append(ch) | |
fig = go.Figure() | |
fig.add_trace(go.Scatter3d( | |
x=x_data_A, y=y_data_A, z=z_data_A, | |
mode='text', | |
text=text_data_A, | |
textfont=dict(size=12, color='red'), | |
name='A (XY plane)' | |
)) | |
fig.add_trace(go.Scatter3d( | |
x=x_data_B, y=y_data_B, z=z_data_B, | |
mode='text', | |
text=text_data_B, | |
textfont=dict(size=12, color='blue'), | |
name='B (YZ plane)' | |
)) | |
fig.add_trace(go.Scatter3d( | |
x=x_data_C, y=y_data_C, z=z_data_C, | |
mode='text', | |
text=text_data_C, | |
textfont=dict(size=12, color='green'), | |
name='C (XZ plane)' | |
)) | |
fig.add_trace(go.Scatter3d( | |
x=x_data_D, y=y_data_D, z=z_data_D, | |
mode='text', | |
text=text_data_D, | |
textfont=dict(size=12, color='purple'), | |
name='D (Diagonal plane Y1)' | |
)) | |
fig.add_trace(go.Scatter3d( | |
x=x_data_E, y=y_data_E, z=z_data_E, | |
mode='text', | |
text=text_data_E, | |
textfont=dict(size=12, color='brown'), | |
name='E (Diagonal plane Y2)' | |
)) | |
fig.add_trace(go.Scatter3d( | |
x=x_data_F, y=y_data_F, z=z_data_F, | |
mode='text', | |
text=text_data_F, | |
textfont=dict(size=12, color='black'), | |
name='F (Diagonal plane Y3)' | |
)) | |
fig.add_trace(go.Scatter3d( | |
x=x_data_H, y=y_data_H, z=z_data_H, | |
mode='text', | |
text=text_data_H, | |
textfont=dict(size=12, color='purple'), | |
name='D (Diagonal plane -Y1)' | |
)) | |
fig.add_trace(go.Scatter3d( | |
x=x_data_I, y=y_data_I, z=z_data_I, | |
mode='text', | |
text=text_data_I, | |
textfont=dict(size=12, color='brown'), | |
name='E (Diagonal plane -Y2)' | |
)) | |
fig.add_trace(go.Scatter3d( | |
x=x_data_J, y=y_data_J, z=z_data_J, | |
mode='text', | |
text=text_data_J, | |
textfont=dict(size=12, color='black'), | |
name='F (Diagonal plane -Y3)' | |
)) | |
fig.update_layout( | |
scene=dict( | |
xaxis=dict(title="X", range=[-max_layer, max_layer]), | |
yaxis=dict(title="Y", range=[-max_layer, max_layer]), | |
zaxis=dict(title="Z", range=[-max_layer, max_layer]), | |
camera=dict( | |
center=dict(x=0, y=0, z=0), | |
eye=dict(x=1.25, y=1.25, z=1.25) | |
) | |
), | |
title="3D Visualization (Manual Refresh)", | |
width=1000, height=800 | |
) | |
fig.write_html(filename) | |
def draw_interface(stdscr): | |
stdscr.clear() | |
stdscr.addstr(0,0,f"Layer: {current_layer}, Axiom: {current_axiom}, Pos=({cursor_x},{cursor_y})") | |
stdscr.addstr(1,0,"F1=A(XY), F2=B(YZ), F3=C(XZ), F4=D(Diag) | +/-=layers | Arrows=move | Type=insert | Ctrl+D=exit") | |
stdscr.addstr(2,0,"Refresh matrix_visualization.html manually.") | |
stdscr.addstr(3,0,"A=XY plane, B=YZ plane, C=XZ plane, D=diagonal plane") | |
grid, read_only = data[(current_layer, current_axiom)] | |
dim = layer_dimension(current_layer) | |
center = current_layer | |
VIEW_RADIUS = 5 | |
min_xv = max(cursor_x - VIEW_RADIUS, -current_layer) | |
max_xv = min(cursor_x + VIEW_RADIUS, current_layer) | |
min_yv = max(cursor_y - VIEW_RADIUS, -current_layer) | |
max_yv = min(cursor_y + VIEW_RADIUS, current_layer) | |
offset_line = 5 | |
offset_col = 2 | |
for draw_y in range(min_yv, max_yv+1): | |
row_chars = [] | |
gy = draw_y + center | |
for draw_x in range(min_xv, max_xv+1): | |
gx = draw_x + center | |
ch = grid[gy][gx] | |
display_char = ' ' if read_only[gy][gx] else ch | |
if draw_x == cursor_x and draw_y == cursor_y: | |
if current_layer == 0 and current_axiom == 'A' and cursor_x == 0 and cursor_y == 0: | |
char = display_char | |
else: | |
char = "▮" if display_char != DEFAULT_CHAR else "○" | |
else: | |
char = display_char | |
row_chars.append(char) | |
stdscr.addstr(offset_line+(draw_y - min_yv), offset_col, "".join(row_chars)) | |
stdscr.refresh() | |
def prefill_layers(mode, fillA, fillB, fillC, fillD, fillE, fillF, fillH, fillI, fillJ): | |
# number of layers = max(len(fillA), len(fillB), len(fillC), len(fillD)) | |
max_layers = max(len(fillA), len(fillB), len(fillC), len(fillD), len(fillE), len(fillF), len(fillH), len(fillI), len(fillJ)) | |
random.seed(0) | |
axioms = ['A','B','C','D','E','F','H','I','J'] | |
fills = {'A': fillA, 'B': fillB, 'C': fillC, 'D': fillD, 'E': fillE, 'F': fillF, 'H': fillH, 'I': fillI, 'J': fillJ} | |
for layer in range(1, max_layers+1): | |
for axiom in axioms: | |
ensure_layer_axiom(layer, axiom) | |
grid, ro = data[(layer, axiom)] | |
center = layer | |
N = layer | |
ring_coords = [] | |
for y in range(-N, N+1): | |
for x in range(-N, N+1): | |
if max(abs(x),abs(y)) == N: | |
gx = x+center | |
gy = y+center | |
if not ro[gy][gx]: | |
ring_coords.append((gx, gy)) | |
total = len(ring_coords) | |
if total == 0: | |
continue | |
chars_list = fills[axiom] | |
# Determine what char to use for full/partial, or how to pick random | |
if layer <= len(chars_list): | |
base_char = chars_list[layer-1] # single char for this layer | |
else: | |
base_char = None | |
######################################## | |
#TODO: replace default char by nothing | |
######################################## | |
if mode == 'full': | |
# Need a base_char to fill entire ring | |
if base_char is not None: | |
for (gx,gy) in ring_coords: | |
grid[gy][gx] = base_char | |
elif mode == 'partial': | |
# Fill half ring cells with base_char if available | |
if base_char is not None: | |
#TODO: replace random select here | |
selected = random.sample(ring_coords, total//2) | |
for (gx,gy) in selected: | |
grid[gy][gx] = base_char | |
# else no fill if no base_char | |
elif mode == 'random': | |
# Partial logic: half ring cells chosen | |
# each chosen cell gets random char from chars_list | |
if len(chars_list) > 0: | |
selected = random.sample(ring_coords, total//2) | |
for (gx,gy) in selected: | |
ch = random.choice(chars_list) | |
grid[gy][gx] = ch | |
# if no chars_list empty, no fill | |
def run(stdscr): | |
global current_layer, current_axiom | |
curses.curs_set(0) | |
stdscr.nodelay(False) | |
stdscr.keypad(True) | |
go_to_layer_axiom(0, 'A') | |
while True: | |
render_3d() | |
draw_interface(stdscr) | |
key = stdscr.getch() | |
if key == -1: | |
continue | |
if key == 4: # Ctrl+D | |
break | |
if key == curses.KEY_F1: | |
go_to_layer_axiom(current_layer, 'A') | |
elif key == curses.KEY_F2: | |
go_to_layer_axiom(current_layer, 'B') | |
elif key == curses.KEY_F3: | |
go_to_layer_axiom(current_layer, 'C') | |
elif key == curses.KEY_F4: | |
go_to_layer_axiom(current_layer, 'D') | |
elif key == curses.KEY_F5: | |
go_to_layer_axiom(current_layer, 'E') | |
elif key == curses.KEY_F6: | |
go_to_layer_axiom(current_layer, 'F') | |
elif key == curses.KEY_F7: | |
go_to_layer_axiom(current_layer, 'H') | |
elif key == curses.KEY_F8: | |
go_to_layer_axiom(current_layer, 'I') | |
elif key == curses.KEY_F9: | |
go_to_layer_axiom(current_layer, 'J') | |
elif key == ord('+'): | |
go_to_layer_axiom(current_layer+1, current_axiom) | |
elif key == ord('-'): | |
if current_layer > 0: | |
go_to_layer_axiom(current_layer-1, current_axiom) | |
elif key == curses.KEY_LEFT: | |
move_cursor(-1, 0) | |
elif key == curses.KEY_RIGHT: | |
move_cursor(1, 0) | |
elif key == curses.KEY_UP: | |
move_cursor(0, -1) | |
elif key == curses.KEY_DOWN: | |
move_cursor(0, 1) | |
elif 32 <= key < 127: | |
ch = chr(key) | |
insert_char(ch) | |
if __name__ == "__main__": | |
prefill = ('--prefill' in sys.argv) | |
fillA = ['B'] | |
fillB = ['B'] | |
fillC = ['C'] | |
fillD = ['D'] | |
fillE = ['E'] | |
fillF = ['F'] | |
fillH = ['H'] | |
fillI = ['I'] | |
fillJ = ['J'] | |
mode = 'full' | |
for arg in sys.argv: | |
if arg.startswith('--fillA='): | |
fillA = arg.split('=')[1].split(',') | |
elif arg.startswith('--fillB='): | |
fillB = arg.split('=')[1].split(',') | |
elif arg.startswith('--fillC='): | |
fillC = arg.split('=')[1].split(',') | |
elif arg.startswith('--fillD='): | |
fillD = arg.split('=')[1].split(',') | |
elif arg.startswith('--fillE='): | |
fillE = arg.split('=')[1].split(',') | |
elif arg.startswith('--fillF='): | |
fillF = arg.split('=')[1].split(',') | |
elif arg.startswith('--fillH='): | |
fillH = arg.split('=')[1].split(',') | |
elif arg.startswith('--fillI='): | |
fillI = arg.split('=')[1].split(',') | |
elif arg.startswith('--fillJ='): | |
fillJ = arg.split('=')[1].split(',') | |
elif arg.startswith('--mode='): | |
mode = arg.split('=')[1] | |
if prefill: | |
prefill_layers(mode, fillA, fillB, fillC, fillD, fillE, fillF, fillH, fillI, fillJ) | |
try: | |
curses.wrapper(run) | |
except KeyboardInterrupt: | |
pass | |
print("Exited.") |
Author
cyberpunk042
commented
Dec 20, 2024
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment