From e3e00bac3d417e392586927a4f7efb947341e92a Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Sat, 28 Sep 2019 16:42:50 +0200 Subject: [PATCH 1/7] Enter graph data and test pygame --- interactions.py | 76 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 interactions.py diff --git a/interactions.py b/interactions.py new file mode 100644 index 0000000..4f5ebd2 --- /dev/null +++ b/interactions.py @@ -0,0 +1,76 @@ +from math import pi, cos, sin + +import 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 +edges = [ + (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, 9), (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) +] + +# === phase space === + +litness = 30*[0] + +# === main loop === + +if __name__ == '__main__': + # set up display and clock + pygame.display.init() + screen = pygame.display.set_mode((viewsize, viewsize)) + pygame.display.set_caption('Preview') + clock = pygame.time.Clock() + + to_light = 0 + while True: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + quit() + + litness[to_light] = 1. + for e in range(30): + pygame.draw.aaline( + screen, + tuple(255*c for c in colorsys.hsv_to_rgb(e/30., litness[e], 0.2 + 0.8*litness[e])), + vertices[edges[e][0]], + vertices[edges[e][1]], + ) + litness[e] *= 0.9 + + # step + to_light = (to_light + 1) % 30 + pygame.display.flip() + clock.tick(24) From 36077fd872791d95272540e572914e07c6afe2e1 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Sat, 28 Sep 2019 20:56:25 +0200 Subject: [PATCH 2/7] Mock up wave equation example Light strip outputs aren't hooked up yet --- interactions.py | 76 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 11 deletions(-) diff --git a/interactions.py b/interactions.py index 4f5ebd2..0e388d4 100644 --- a/interactions.py +++ b/interactions.py @@ -1,4 +1,4 @@ -from math import pi, cos, sin +from math import pi, cos, sin, exp import pygame, colorsys @@ -11,7 +11,7 @@ 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 +# edges as vertex pairs. FireStar will rest on vertices 6 (A), 5 (B), 7 (C) edges = [ (0, 1), (0, 3), (0, 5), (0, 7), (0, 9), (2, 8), (2, 6), (6, 10), (10, 4), (4, 8), @@ -41,36 +41,90 @@ cyc_edge_adj = [ (14, 11), (11, 23), (23, 20), (20, 17), (17, 14) ] +# === interaction parameters === + +frame_rate = 24 +inductance = 0.001 +drain = False +drain_rate = 0.1 + # === phase space === -litness = 30*[0] +charge = 12*[0] +current = 30*[0] # === main loop === if __name__ == '__main__': # set up display and clock - pygame.display.init() - screen = pygame.display.set_mode((viewsize, viewsize)) + 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() + # set up background + background = pygame.Surface(screen.get_size()).convert() + background.fill((32, 32, 32)) + to_light = 0 while True: + # handle events for event in pygame.event.get(): - if event.type == pygame.QUIT: + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_j: + charge[0] += 10 + charge[11] -= 10 + elif event.key == pygame.K_k: + charge[0] += 100 + charge[11] -= 100 + elif event.key == pygame.K_SPACE: + drain = True + pygame.draw.aaline(screen, (255, 255, 255), (0, 0), (viewsize, viewsize)) + elif event.type == pygame.KEYUP: + if event.key == pygame.K_SPACE: + drain = False + elif event.type == pygame.QUIT: quit() - litness[to_light] = 1. + # print data + energy = 0.5*(sum(q*q for q in charge) + inductance*sum(i*i for i in current)) + screen.blit(background, (0, 0)) + 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('charge[0]', True, (255, 255, 255)) + screen.blit(text, (20, 30 + viewsize)) + text = font.render(str(charge[0]), True, (255, 255, 255)) + screen.blit(text, (120, 30 + viewsize)) + + # show state for e in range(30): + litness = 1 - exp(-2*current[e]*current[e]) + color = colorsys.hsv_to_rgb(0.45 + litness*0.25, 1 - 0.5*litness*litness, litness) + ##color = colorsys.hsv_to_rgb(litness*0.167, 1 - 0.5*litness*litness, litness) pygame.draw.aaline( screen, - tuple(255*c for c in colorsys.hsv_to_rgb(e/30., litness[e], 0.2 + 0.8*litness[e])), + tuple(255*c for c in color), vertices[edges[e][0]], vertices[edges[e][1]], ) - litness[e] *= 0.9 + + # evolve state. use verlet integration, first updating the currents and then + # using the new currents to update the charges. + for e in range(30): + u, v = edges[e] + ##current[e] *= 1 - resistance + current[e] += inductance*(charge[u] - charge[v]) + for e in range(30): + u, v = edges[e] + charge[u] -= current[e] + charge[v] += current[e] + if drain: + for v in range(12): + charge[v] *= 1 - drain_rate # step - to_light = (to_light + 1) % 30 pygame.display.flip() - clock.tick(24) + clock.tick(frame_rate) From 217bdbec67c0ebe142fe364c3b2353d79d70afa5 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Sat, 28 Sep 2019 21:18:05 +0200 Subject: [PATCH 3/7] Separate things into functions a bit --- interactions.py | 73 +++++++++++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 26 deletions(-) diff --git a/interactions.py b/interactions.py index 0e388d4..fe23672 100644 --- a/interactions.py +++ b/interactions.py @@ -44,7 +44,11 @@ cyc_edge_adj = [ # === interaction parameters === frame_rate = 24 + inductance = 0.001 + +therm_conduct = 0.001 + drain = False drain_rate = 0.1 @@ -55,6 +59,39 @@ current = 30*[0] # === main loop === +def heat_evolution(): + for e, f in cyc_edge_adj: + flow = therm_conduct*(current[e] - current[f]) + current[e] -= flow + current[f] += flow + if drain: + current[v] *= 1 - drain_rate + +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 = edges[e] + current[e] += inductance*(charge[u] - charge[v]) + for e in range(30): + u, v = edges[e] + charge[u] -= current[e] + charge[v] += current[e] + if drain: + for v in range(12): + charge[v] *= 1 - drain_rate + +def show_data(screen): + energy = 0.5*(sum(q*q for q in charge) + inductance*sum(i*i for i in current)) + 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('charge[0]', True, (255, 255, 255)) + screen.blit(text, (20, 30 + viewsize)) + text = font.render(str(charge[0]), True, (255, 255, 255)) + screen.blit(text, (120, 30 + viewsize)) + if __name__ == '__main__': # set up display and clock pygame.init() @@ -73,11 +110,11 @@ if __name__ == '__main__': for event in pygame.event.get(): if event.type == pygame.KEYDOWN: if event.key == pygame.K_j: - charge[0] += 10 - charge[11] -= 10 - elif event.key == pygame.K_k: 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 pygame.draw.aaline(screen, (255, 255, 255), (0, 0), (viewsize, viewsize)) @@ -87,17 +124,11 @@ if __name__ == '__main__': elif event.type == pygame.QUIT: quit() - # print data - energy = 0.5*(sum(q*q for q in charge) + inductance*sum(i*i for i in current)) + # clear screen screen.blit(background, (0, 0)) - 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('charge[0]', True, (255, 255, 255)) - screen.blit(text, (20, 30 + viewsize)) - text = font.render(str(charge[0]), True, (255, 255, 255)) - screen.blit(text, (120, 30 + viewsize)) + + # show data + show_data(screen) # show state for e in range(30): @@ -111,19 +142,9 @@ if __name__ == '__main__': vertices[edges[e][1]], ) - # evolve state. use verlet integration, first updating the currents and then - # using the new currents to update the charges. - for e in range(30): - u, v = edges[e] - ##current[e] *= 1 - resistance - current[e] += inductance*(charge[u] - charge[v]) - for e in range(30): - u, v = edges[e] - charge[u] -= current[e] - charge[v] += current[e] - if drain: - for v in range(12): - charge[v] *= 1 - drain_rate + # evolve state + wave_evolution() + ##heat_evolution() # step pygame.display.flip() From 9955a827a7442d86a16ff0c7641e6002e74406b8 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Sat, 28 Sep 2019 21:21:46 +0200 Subject: [PATCH 4/7] Show controls --- interactions.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/interactions.py b/interactions.py index fe23672..e5c8084 100644 --- a/interactions.py +++ b/interactions.py @@ -103,6 +103,10 @@ 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)) + 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: From 7b96b8e97441b9cc5d7dbe0cc61f6e56320aa36b Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Sat, 28 Sep 2019 21:53:54 +0200 Subject: [PATCH 5/7] Hook up the lights? --- interactions.py | 70 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 19 deletions(-) diff --git a/interactions.py b/interactions.py index e5c8084..a35830d 100644 --- a/interactions.py +++ b/interactions.py @@ -1,6 +1,8 @@ +#!/usr/bin/python3 + from math import pi, cos, sin, exp -import pygame, colorsys +import phue, pygame, colorsys # === graph data === @@ -12,7 +14,7 @@ vertices = [(viewsize*(1 + shrink*x)/2, viewsize*(1 + shrink*y)/2) for x, y in ] # edges as vertex pairs. FireStar will rest on vertices 6 (A), 5 (B), 7 (C) -edges = [ +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), @@ -41,6 +43,50 @@ cyc_edge_adj = [ (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(edge, color): + if b != None: + b.set_light( + edges[edge].light_id, + { + 'hue' : color[0]*lasthue, + 'bri' : color[2]*fullbright, + 'sat' : color[1]*fullsat + } + ) + rgb_color = tuple(255*c for c in colorsys.hsv_to_rgb(*color)) + pygame.draw.aaline(screen, rgb_color, vertices[vertex_adj[edge][0]], vertices[vertex_adj[edge][1]]) + # === interaction parameters === frame_rate = 24 @@ -71,10 +117,10 @@ 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 = edges[e] + u, v = vertex_adj[e] current[e] += inductance*(charge[u] - charge[v]) for e in range(30): - u, v = edges[e] + u, v = vertex_adj[e] charge[u] -= current[e] charge[v] += current[e] if drain: @@ -93,13 +139,6 @@ def show_data(screen): screen.blit(text, (120, 30 + viewsize)) if __name__ == '__main__': - # set up display and clock - 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() - # set up background background = pygame.Surface(screen.get_size()).convert() background.fill((32, 32, 32)) @@ -137,14 +176,7 @@ if __name__ == '__main__': # show state for e in range(30): litness = 1 - exp(-2*current[e]*current[e]) - color = colorsys.hsv_to_rgb(0.45 + litness*0.25, 1 - 0.5*litness*litness, litness) - ##color = colorsys.hsv_to_rgb(litness*0.167, 1 - 0.5*litness*litness, litness) - pygame.draw.aaline( - screen, - tuple(255*c for c in color), - vertices[edges[e][0]], - vertices[edges[e][1]], - ) + set_light_hsv(e, (0.45 + litness*0.25, 1 - 0.5*litness*litness, litness)) # evolve state wave_evolution() From add9bda0807a4ba3b96b70a782570e5436e3315d Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Sat, 28 Sep 2019 22:16:42 +0200 Subject: [PATCH 6/7] Try to debug out-of-range index Seems to be in the `color` tuple in set_light_hsv, but doesn't show up in debugging. --- interactions.py | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/interactions.py b/interactions.py index a35830d..f9cab9c 100644 --- a/interactions.py +++ b/interactions.py @@ -74,18 +74,16 @@ fullsat = 254 # --- control methods -def set_light_hsv(edge, color): - if b != None: - b.set_light( - edges[edge].light_id, - { - 'hue' : color[0]*lasthue, - 'bri' : color[2]*fullbright, - 'sat' : color[1]*fullsat - } - ) +def set_light_hsv(e, color, hit_bridge): + command = { + 'hue' : color[0]*lasthue, + 'bri' : color[2]*fullbright, + 'sat' : 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[edge][0]], vertices[vertex_adj[edge][1]]) + pygame.draw.aaline(screen, rgb_color, vertices[vertex_adj[e][0]], vertices[vertex_adj[e][1]]) # === interaction parameters === @@ -103,6 +101,9 @@ drain_rate = 0.1 charge = 12*[0] current = 30*[0] +last_litness = 30*[float("inf")] +change_threshold = 0.05 + # === main loop === def heat_evolution(): @@ -127,6 +128,13 @@ def wave_evolution(): for v in range(12): charge[v] *= 1 - drain_rate +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 show_data(screen): energy = 0.5*(sum(q*q for q in charge) + inductance*sum(i*i for i in current)) text = font.render('energy', True, (255, 255, 255)) @@ -175,8 +183,7 @@ if __name__ == '__main__': # show state for e in range(30): - litness = 1 - exp(-2*current[e]*current[e]) - set_light_hsv(e, (0.45 + litness*0.25, 1 - 0.5*litness*litness, litness)) + set_wave_light(e, current[e]) # evolve state wave_evolution() From 4d132ab857e34363f1cdeb523d2b73f3f4d89e86 Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Sat, 28 Sep 2019 16:33:34 -0400 Subject: [PATCH 7/7] Tweaks to get it to actually run --- interactions.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interactions.py b/interactions.py index f9cab9c..4331426 100644 --- a/interactions.py +++ b/interactions.py @@ -76,9 +76,9 @@ fullsat = 254 def set_light_hsv(e, color, hit_bridge): command = { - 'hue' : color[0]*lasthue, - 'bri' : color[2]*fullbright, - 'sat' : color[1]*fullsat + '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) @@ -87,7 +87,7 @@ def set_light_hsv(e, color, hit_bridge): # === interaction parameters === -frame_rate = 24 +frame_rate = 2 inductance = 0.001