You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
230 lines
6.0 KiB
230 lines
6.0 KiB
#!/usr/bin/python3 |
|
|
|
from math import pi, cos, sin, exp |
|
|
|
import phue, pygame, colorsys |
|
|
|
# === graph data === |
|
|
|
# vertices as screen positions |
|
viewsize = 600 |
|
shrink = 0.9 |
|
vertices = [(viewsize*(1 + shrink*x)/2, viewsize*(1 + shrink*y)/2) for x, y in |
|
[(0, 0)] + [(cos(-n*pi/5), sin(-n*pi/5)) for n in range(10)] + [(0, 0)] |
|
] |
|
|
|
# edges as vertex pairs. FireStar will rest on vertices 6 (A), 5 (B), 7 (C) |
|
vertex_adj = [ |
|
(0, 1), (0, 3), (0, 5), (0, 7), (0, 9), |
|
(2, 8), (2, 6), (6, 10), (10, 4), (4, 8), |
|
(2, 9), (2, 11), (2, 5), |
|
(4, 1), (4, 11), (4, 7), |
|
(6, 3), (6, 11), (6, 9), |
|
(8, 5), (8, 11), (8, 1), |
|
(10, 7), (10, 11), (10, 3), |
|
(9, 3), (9, 5), |
|
(1, 5), (1, 7), |
|
(3, 7) |
|
] |
|
|
|
# cyclic edge adjacencies |
|
cyc_edge_adj = [ |
|
(0, 1), (1, 2), (2, 3), (3, 4), (4, 0), |
|
(5, 10), (10, 11), (11, 12), (12, 6), (6, 5), |
|
(6, 16), (16, 17), (17, 18), (18, 7), (7, 6), |
|
(7, 22), (22, 23), (23, 24), (24, 8), (8, 7), |
|
(8, 13), (13, 14), (14, 15), (15, 9), (9, 8), |
|
(5, 9), (9, 19), (19, 20), (20, 21), (21, 5), |
|
(4, 10), (10, 25), (25, 26), (26, 18), (18, 4), |
|
(3, 15), (15, 29), (29, 28), (28, 22), (22, 3), |
|
(2, 12), (12, 27), (27, 26), (26, 19), (19, 2), |
|
(1, 24), (24, 25), (25, 29), (29, 16), (16, 1), |
|
(0, 21), (21, 28), (28, 27), (27, 13), (13, 0), |
|
(14, 11), (11, 23), (23, 20), (20, 17), (17, 14) |
|
] |
|
|
|
# === pygame setup === |
|
|
|
pygame.init() |
|
screen = pygame.display.set_mode((viewsize, viewsize + 100)) |
|
pygame.display.set_caption('Preview') |
|
font = pygame.font.Font(None, 25) |
|
clock = pygame.time.Clock() |
|
|
|
# === bridge setup and control === |
|
|
|
# --- copied from firestar.py |
|
|
|
b = phue.Bridge('172.18.130.12') |
|
##b = None |
|
Nedges = 30; |
|
edgecode = [None] * Nedges; |
|
|
|
# The edges are numbered as in info/SSDedges.obj; enter the two-letter code for |
|
# the lightstrip inserted through each edge in the block below: |
|
|
|
exec(open("edgecodes.py").read()) |
|
|
|
if b != None: |
|
edges = [ b[code] for code in edgecode ] |
|
|
|
lasthue = 65535 |
|
fullbright = 254 |
|
fullsat = 254 |
|
|
|
# --- control methods |
|
|
|
def set_light_hsv(e, color, hit_bridge): |
|
command = { |
|
'hue' : int(color[0]*lasthue), |
|
'bri' : int(color[2]*fullbright), |
|
'sat' : int(color[1]*fullsat) |
|
} |
|
if hit_bridge and b != None: |
|
b.set_light(edges[e].light_id, command) |
|
rgb_color = tuple(255*c for c in colorsys.hsv_to_rgb(*color)) |
|
pygame.draw.aaline(screen, rgb_color, vertices[vertex_adj[e][0]], vertices[vertex_adj[e][1]]) |
|
|
|
# === interaction parameters === |
|
|
|
frame_rate = 24 |
|
|
|
inductance = 0.001 |
|
|
|
therm_conduct = 0.01 |
|
|
|
drain = False |
|
drain_rate = 0.1 |
|
|
|
# === phase space === |
|
|
|
# heat equation |
|
temp = 30*[0] |
|
new_temp = 30*[0] |
|
|
|
# wave equation |
|
charge = 12*[0] |
|
current = 30*[0] |
|
|
|
last_litness = 30*[float("inf")] |
|
change_threshold = 0.05 |
|
|
|
# === main loop === |
|
|
|
def heat_evolution(): |
|
for e, f in cyc_edge_adj: |
|
flow = therm_conduct*(temp[e] - temp[f]) |
|
new_temp[e] -= flow |
|
new_temp[f] += flow |
|
for e in range(30): |
|
if drain: |
|
new_temp[e] *= 1 - drain_rate |
|
temp[e] = max(new_temp[e], 0) |
|
|
|
def wave_evolution(): |
|
# use verlet integration, first updating the currents and then using the new |
|
# currents to update the charges. |
|
for e in range(30): |
|
u, v = vertex_adj[e] |
|
current[e] += inductance*(charge[u] - charge[v]) |
|
for e in range(30): |
|
u, v = vertex_adj[e] |
|
charge[u] -= current[e] |
|
charge[v] += current[e] |
|
if drain: |
|
for v in range(12): |
|
charge[v] *= 1 - drain_rate |
|
|
|
def set_heat_light(e, i): |
|
litness = 1 - exp(-2*i) |
|
if abs(litness - last_litness[e]) > change_threshold: |
|
hit_bridge = True |
|
last_litness[e] = litness |
|
else: |
|
hit_bridge = False |
|
set_light_hsv(e, (0.167*last_litness[e], 1 - 0.5*last_litness[e]*last_litness[e], last_litness[e]), hit_bridge) |
|
|
|
def set_wave_light(e, i): |
|
litness = 1 - exp(-2*i*i) |
|
hit_bridge = abs(litness - last_litness[e]) > change_threshold |
|
set_light_hsv(e, (0.45 + litness*0.25, 1 - 0.5*litness*litness, litness), hit_bridge) |
|
if hit_bridge: |
|
last_litness[e] = litness |
|
|
|
def handle_heat_events(): |
|
global drain |
|
for event in pygame.event.get(): |
|
if event.type == pygame.KEYDOWN: |
|
if event.key == pygame.K_j: |
|
new_temp[0] += 10 |
|
elif event.key == pygame.K_SPACE: |
|
drain = True |
|
elif event.type == pygame.KEYUP: |
|
if event.key == pygame.K_SPACE: |
|
drain = False |
|
elif event.type == pygame.QUIT: |
|
quit() |
|
|
|
def handle_wave_events(): |
|
global drain |
|
for event in pygame.event.get(): |
|
if event.type == pygame.KEYDOWN: |
|
if event.key == pygame.K_j: |
|
charge[0] += 100 |
|
charge[11] -= 100 |
|
elif event.key == pygame.K_k: |
|
charge[5] += 100 |
|
charge[7] -= 100 |
|
elif event.key == pygame.K_SPACE: |
|
drain = True |
|
elif event.type == pygame.KEYUP: |
|
if event.key == pygame.K_SPACE: |
|
drain = False |
|
elif event.type == pygame.QUIT: |
|
quit() |
|
|
|
def show_data(screen): |
|
energy = sum(temp) |
|
text = font.render('energy', True, (255, 255, 255)) |
|
screen.blit(text, (20, viewsize)) |
|
text = font.render(str(energy), True, (255, 255, 255)) |
|
screen.blit(text, (120, viewsize)) |
|
text = font.render('temp[0]', True, (255, 255, 255)) |
|
screen.blit(text, (20, 30 + viewsize)) |
|
text = font.render(str(temp[0]), True, (255, 255, 255)) |
|
screen.blit(text, (120, 30 + viewsize)) |
|
|
|
if __name__ == '__main__': |
|
# set up background |
|
background = pygame.Surface(screen.get_size()).convert() |
|
background.fill((32, 32, 32)) |
|
##text = font.render('press [j] or [k] to pluck', True, (255, 255, 255)) |
|
text = font.render('press [j] to pluck', True, (255, 255, 255)) |
|
background.blit(text, (340, viewsize)) |
|
text = font.render('hold [space] to drain', True, (255, 255, 255)) |
|
background.blit(text, (340, 30 + viewsize)) |
|
|
|
to_light = 0 |
|
while True: |
|
# handle events |
|
##handle_wave_events() |
|
handle_heat_events() |
|
|
|
# clear screen |
|
screen.blit(background, (0, 0)) |
|
|
|
# show data |
|
show_data(screen) |
|
|
|
# show state |
|
for e in range(30): |
|
##set_wave_light(e, current[e]) |
|
set_heat_light(e, temp[e]) |
|
|
|
# evolve state |
|
##wave_evolution() |
|
heat_evolution() |
|
|
|
# step |
|
pygame.display.flip() |
|
clock.tick(frame_rate)
|
|
|