Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Heartbeat #11

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
*.pyc
dmx_setup.json
Stats

# Virtualenv
.Python
bin
pip-selfcheck.json
lib
python2.7
86 changes: 62 additions & 24 deletions data/geom.txt
Original file line number Diff line number Diff line change
@@ -1,24 +1,62 @@
1 2,5 6
2 1,3,6 5,7
3 2,4,7 6,8
4 3,8 7
5 6,1,9 2,10
6 5,7,2,10 1,9,3,11
7 6,8,3,11 2,10,4,12
8 7,4,12 3,11
9 10,5,13 6,14
10 9,11,6,14 5,13,7,15
11 10,12,7,15 6,14,8,16
12 11,8,16 7,15
13 14,9,17 10,18
14 13,15,10,18 9,17,11,19
15 14,16,11,19 10,18,12,20
16 15,12,20 11,19
17 18,13,21 14,22
18 17,19,14,22 13,21,15,23
19 18,20,15,23 14,22,16,24
20 19,16,24 15,23
21 22,17 18
22 21,23,18 17,19
23 22,24,19 18,20
24 23,20 19
1 2,5,84 85,86,87,4,6,7
2 1,3,7 84,85,86,87,5,6,8
3 2,87,8 84,85,86,2,87,8
4 44,5 45,11,12,6
5 1,4,6 44,45,11,12,7,2,84
6 5,12,7 4,44,45,11,13,1,2
7 2,6,13,8 1,5,3,12,16,17,14,9,18
8 3,7,9,10 2,3,13,14,17,9,16,18
9 8,14,10 7,13,16,17,18
10 88,8,9 14
11 12,15,45 4,5,6,44,15,16
12 6,11,13 4,5,44,45,15,16
13 7,12,16 6,11,15,17,18,14,9,8
14 9,18 8,7,13,16,17,10
15 11,16,20 12,13,17,21
16 13,15,17 11,12,21,7,8,9
17 16,18,21 7,8,9,13,15,20,22,23
18 14,17,23 7,8,9,13,16,21,22
19 15,20,25,26 21,28,27
20 15,21 16,17,26,27,28
21 17,20,28,29,22 15,16,26,27,30,31
22 21,23,31 17,18,29,30
23 18,22 17,21
24 25,32 26,33,35,36
25 24,26 32,33,35,36
26 25,27,33 32,35,36
27 26,28,34 21,29,30,33
28 21,27,29 26,30,34
29 21,28,30 27,31,34
30 29,31,34 21,27,28
31 22,30 21,29,34
32 24,35,39 25,26,40,41
33 26,34,36 25,24,32,35,27,37
34 30,27,33,37 26,28,29,36
35 32,36,41 24,25,26,39,40
36 33,35,37 24,25,26,32,34,41,42,43
37 34,36,43,38 33,35,41,42
38 37,43 42,58
39 32,40,52 35,41
40 39,41,56 32,35,42,57,58
41 35,40,42 36,37,32,39,57,58
42 41,43,58 35,36,37,40,56,57,38
43 37,42,38 33,36,41
44 4,45 5,6,11,12
45 44,11 4,5,6,12
50 51,52
51 50,52 24,32,39
52 39,50,51,53 24,33,40,56
53 52,54,56 39,40
54 53,55,57 56,58
55 54 57,58
56 40,53,57 39,52,42,52,54
57 54,56,58 40,41,42,55
58 42,57 40,41,56,55,38
80 83 89
83 80,89 84,85
84 1,85 4,5,2,3,86,87,83
85 84,86,89 1,2,3,87,83
86 85,87 1,2,3
87 86,3,88 8,10
88 87,10 3,8
89 83,85 80,84
185 changes: 185 additions & 0 deletions deployments/sheep2017/shows/Heartbeat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# Heartbeat
#
# Show creates Heartbeats
#
# TODO: Control background color, beat color and beat rate by Touch OSC
#

import time
from random import randint, choice
from color import RGB, HSV

# import sheep
import shows.geom


# Converts a 0-1536 color into rgb on a wheel by keeping one of the rgb channels off
MAX_COLOR = 1536
BEAT_START_CELL = 13
# BEAT_START_CELL = 40
BG_DARKNESS = 0.6

def Wheel(color):
color = color % MAX_COLOR # just in case color is out of bounds
channel = color / 255
value = color % 255

if channel == 0:
r = 255
g = value
b = 0
elif channel == 1:
r = 255 - value
g = 255
b = 0
elif channel == 2:
r = 0
g = 255
b = value
elif channel == 3:
r = 0
g = 255 - value
b = 255
elif channel == 4:
r = value
g = 0
b = 255
else:
r = 255
g = 0
b = 255 - value

return RGB(r, g, b)

# Interpolates between colors. Fract = 1 is all color 2
def morph_color(color1, color2, fract):
morph_h = color1.h + ((color2.h - color1.h) * fract)
morph_s = color1.s + ((color2.s - color1.s) * fract)
morph_v = color1.v + ((color2.v - color1.v) * fract)

return HSV(morph_h, morph_s, morph_v)


class Fader(object):
def __init__(self, sheep, cell, duration):
self.sheep = sheep
self.cell = cell
self.duration = duration
self.start_time = time.time()

def draw_fader(self, fore_color, back_color):
progress = (time.time() - self.start_time) / self.duration
if progress < 1.0:
adj_color = morph_color(fore_color, back_color, progress)
self.sheep.set_cell(self.cell, adj_color)


class Path(object):
def __init__(self, sheep, duration):
self.sheep = sheep
self.duration = duration
self.faders = [] # List that holds fader objects
self.heads = [] # coordinate list of growing heads
self.color = Wheel(randint(0, MAX_COLOR))
# self.color = RGB(255, 0, 0)

# Plant first head
# new_head = choice(self.sheep.all_cells())
new_head = BEAT_START_CELL
self.heads.append(new_head)
new_fader = Fader(self.sheep, new_head, self.duration)
self.faders.append(new_fader)

def draw_path(self, background):
for f in self.faders:
f.draw_fader(self.color, background)

def path_alive(self):
return not all(time.time() > (f.start_time + f.duration) for f in self.faders)

def move_path(self):
new_heads = [] # temporary list to hold new heads

for h in self.heads:
# neighbors = set(self.sheep.edge_neighbors(h) + self.sheep.vertex_neighbors(h))
neighbors = self.sheep.edge_neighbors(h)
for cell in neighbors:
if self.is_empty(cell):
new_head = cell
new_heads.append(new_head)
new_fader = Fader(self.sheep, new_head, self.duration)
self.faders.append(new_fader)

self.heads.extend(new_heads)

def is_empty(self, cell):
return not cell in [f.cell for f in self.faders]


class Heartbeat(object):
def __init__(self, sheep_sides):
self.name = "Heartbeat"
self.sheep = sheep_sides.both
self.heartbeats = [] # List that holds Path objects
self.speed = 0.05
self.beat_frequency = 100 # BPM
self.last_beat = 0
self.beat_duration = 2

self.last_osc = time.time()
self.OSC = False # Is Touch OSC working?
self.noOSCcolor = randint(0,MAX_COLOR) # Default color if no Touch OSC
self.OSCcolor = Wheel(self.noOSCcolor)

def set_param(self, name, val):
# name will be 'colorR', 'colorG', 'colorB'
rgb255 = int(val * 0xff)
if name == 'colorR':
self.OSCcolor.r = rgb255
self.last_osc = time.time()
self.OSC = True
elif name == 'colorG':
self.OSCcolor.g = rgb255
self.last_osc = time.time()
self.OSC = True
elif name == 'colorB':
self.OSCcolor.b = rgb255
self.last_osc = time.time()
self.OSC = True

def next_frame(self):
while True:
if time.time() > (self.last_beat + (60.0 / self.beat_frequency)):
# Time for a new beat
new_path = Path(self.sheep, self.beat_duration)
self.heartbeats.append(new_path)
self.last_beat = time.time()
# self.short_beat_pending = True

# Pick the background color - either random or Touch OSC
if self.OSC: # Which color to use?
background = self.OSCcolor.copy()
else:
background = morph_color(Wheel(self.noOSCcolor), RGB(0,0,0), BG_DARKNESS)
# background = RGB(255, 255, 255)

# Draw Starburst

for p in self.heartbeats:
p.draw_path(background)
p.move_path()
for p in self.heartbeats:
if p.path_alive() == False:
self.heartbeats.remove(p)

# Change background color

if time.time() - self.last_osc > 10: # 2 minutes
self.OSC = False

if self.OSC == False:
self.noOSCcolor += 1
if self.noOSCcolor > MAX_COLOR:
self.noOSCcolor -= MAX_COLOR

yield self.speed