diff options
| -rw-r--r-- | .gitignore | 3 | ||||
| -rwxr-xr-x | bridge.py | 29 | ||||
| -rwxr-xr-x | brige.py | 100 | ||||
| -rw-r--r-- | constants.py | 22 | ||||
| -rw-r--r-- | fish_render.py | 44 | ||||
| -rw-r--r-- | sunrise.py | 127 | ||||
| -rwxr-xr-x | twinkle.py | 112 | ||||
| -rw-r--r-- | webapp/app/__init__.py | 11 | ||||
| -rw-r--r-- | webapp/app/templates/index.html | 55 | ||||
| -rw-r--r-- | webapp/app/views.py | 37 | ||||
| -rwxr-xr-x | webapp/run.py | 7 |
11 files changed, 526 insertions, 21 deletions
@@ -1,2 +1,5 @@ __pycache__ *.pyc + +webapp/packages +webapp/tmp @@ -1,18 +1,30 @@ #!/usr/bin/python2 """Simple demo of oscillating colors.""" -from __future__ import division, unicode_literals +from __future__ import division, print_function import color import constants -from DummyRig import DummyRig import fish_render +import sunrise import time +import random try: import lumiversepython except ImportError: have_lumiversepython = False + from DummyRig import DummyRig else: have_lumiversepython = True + + + +#fish_render = sunrise + + + + + + def get_all_lights(rig): """Return a list of all panels.""" return [ @@ -27,11 +39,20 @@ def get_all_lights(rig): def demo(lights, rig): """Do some light things.""" - fish = fish_render.Fish(0, 0, 2, 1, color.red, 1 / constants.FRAME_RATE) + #fish = fish_render.Fish(0, 0, 15, 1.5, color.black, 0.05 / constants.FRAME_RATE) + #fish = fish_render.Fish(0, 0, 23, 1.5, color.RGB(193 / 255, 68 / 255, 227 / 255), 0.05 / constants.FRAME_RATE) + fish = fish_render.Fish(0, 0, 23, 1.5, color.black, 0.05 / constants.FRAME_RATE) bridge = fish_render.Bridge(lights, fish, rig) + current_time = time.time() + count = 0 while True: bridge.update() - time.sleep(1 / constants.FRAME_RATE) + if count % constants.FRAME_RATE == 0: + time_delta = time.time() - current_time + print(time_delta) + current_time = time.time() + #time.sleep(1 / constants.FRAME_RATE) + count += 1 def main(): diff --git a/brige.py b/brige.py new file mode 100755 index 0000000..314f9db --- /dev/null +++ b/brige.py @@ -0,0 +1,100 @@ +#!/usr/bin/python2 +from __future__ import division, print_function +import color +import constants +import curses +import fish_render +import time +import random +try: + import lumiversepython +except ImportError: + have_lumiversepython = False + from DummyRig import DummyRig +else: + have_lumiversepython = True + +def get_all_lights(rig): + """Return a list of all panels.""" + return [ + [ + fish_render.Light( + x, int(y == 'top'), + rig.select('$side={y}[$sequence={x}]'.format(y=y, x=x)) + ) for y in ('top', 'bot') + ] for x in xrange(1, 200) + ] + + +def colorSection(lights, color, *ranges): + for start, end in ranges: + map(lambda l: map(lambda x: x.light.setRGBRaw(*color), l), lights[start:end]) + +def onSection(lights, *ranges): + colorSection(lights, (1, 1, 1), *ranges) + +def offSection(lights, *ranges): + colorSection(lights, (0, 0, 0), *ranges) + +def mapKey(key): + if key == ord('a'): + return (0, 20) + if key == ord('o'): + return (20, 40) + if key == ord('e'): + return (40, 60) + if key == ord('u'): + return (60, 80) + if key == ord('i'): + return (80, 100) + if key == ord('d'): + return (100, 120) + if key == ord('h'): + return (120, 140) + if key == ord('t'): + return (140, 160) + if key == ord('n'): + return (160, 180) + if key == ord('s'): + return (180, 200) + return (0, 0) + +def demo(lights, rig, screen): + """Do some light things.""" + sections = [] + while True: + k = screen.getch() + offSection(lights, *sections) + r = mapKey(k) + try: + i = sections.index(r) + except ValueError: + sections.append(r) + else: + del sections[i] + onSection(lights, *sections) + rig.updateOnce() + +def main(): + """Run show.""" + if have_lumiversepython: + rig = lumiversepython.Rig('/home/teacher/Lumiverse/PBridge.rig.json') + else: + rig = DummyRig(200, 2) + rig.init() + stdscr = curses.initscr() + stdscr.keypad(1) + curses.noecho() + + lights = get_all_lights(rig) + try: + demo(lights, rig, stdscr) + except BaseException: + curses.nocbreak() + stdscr.keypad(0) + curses.echo() + curses.endwin() + raise + +if __name__ == '__main__': + main() diff --git a/constants.py b/constants.py index e19c127..1685c5a 100644 --- a/constants.py +++ b/constants.py @@ -1,10 +1,22 @@ """Constants that may be tweaked in the future.""" from __future__ import division, unicode_literals -FRAME_RATE = 10 #44 +FRAME_RATE = 22 MIN_VALUE = 0 MAX_VALUE = 1 -MAX_TIME = 4 -MIN_TIME = 0.3 -MAX_HUE = 169 / 360 -MIN_HUE = 250 / 360 +MAX_TIME = 2 +MIN_TIME = 0.1 +MIN_HUE = 207 / 360 +MAX_HUE = 250 / 360 +MAX_SATURATION = 1 +MIN_SATURATION = 0.5 +MIN_VALUE = 0.3 +MAX_VALUE = 1 + +# shallow +MIN_HUE = 169 / 360 +MAX_HUE = 250 / 360 +MAX_SATURATION = 1 +MIN_SATURATION = 0.5 +MIN_VALUE = 0.3 +MAX_VALUE = 1 diff --git a/fish_render.py b/fish_render.py index 4924016..18aecac 100644 --- a/fish_render.py +++ b/fish_render.py @@ -2,6 +2,9 @@ from __future__ import division, unicode_literals import color import random +import constants +from constants import * +import math class Fish(object): """Object displayed on the bridge, probably a fish.""" @@ -18,6 +21,12 @@ class Fish(object): def update(self): """Swim gently to the other side of the bridge.""" self.x += self.step + if self.x > 200: + self.x = 0 + self.y = 0.25 + 0.25 * math.sin(math.pi * self.x / 10) + #theta = random.random() * math.pi / 2 - math.pi / 4 + #self.y += 0.01 * math.sin(theta) + #print(self.y) class Light(object): @@ -33,7 +42,7 @@ class Light(object): def update(self, fish): """Change color depending on where the fish is.""" - if abs(self.transitioning_to - self.color) < self.step: + if abs(self.transitioning_to - self.color) <= self.step: self.transitioning_to = pick_HSV().to_RGB() self.step = (random_change() * abs(self.color - self.transitioning_to)) @@ -41,7 +50,13 @@ class Light(object): self.color, self.transitioning_to, self.step ) coverage = compute_coverage(fish, self.x, self.y) + #coverage *= coverage new_color = coverage * fish.color + (1 - coverage) * new_background + #new_color = new_background + self.color = new_background + if self.x == 30 and self.y == 0: + pass + #print(self.color.r(), self.color.g(), self.color.b()) self.light.setRGBRaw( *map(lambda x: clamp(0, x, 1), new_color.components) ) @@ -61,24 +76,28 @@ class Bridge(object): for y, light in enumerate(light_col): self.fish.update() light.update(self.fish) - self.rig.updateOnce() + self.rig.updateOnce() def compute_coverage(fish, x, y): """Calculate the coverage of a fish over a box.""" - min_x = max(x, fish.x) - max_x = min(x+1, fish.x + fish.width) - min_y = max(y, fish.y) - max_y = min(y+1, fish.y + fish.height) + middle_of_fish = fish.x + fish.width / 2 + middle_of_panel = x + 0.5 + diff = abs(middle_of_fish - middle_of_panel) / (fish.width / 2) + scale = 1 - diff + min_x = min(max(x, fish.x), x + 1) + max_x = max(min(x+1, fish.x + fish.width), x) + min_y = min(max(y, fish.y), y + 1) + max_y = max(min(y+1, fish.y + fish.height), y) coverage = (max_x - min_x) * (max_y - min_y) - return coverage + return coverage * scale def pick_HSV(): """Return a random color in an appropriate interval.""" - hue = random.random() * (MAX_HUE - MIN_HUE) + MIN_HUE - saturation = random.random() - value = random.random() + hue = random.random() * (constants.MAX_HUE - constants.MIN_HUE) + constants.MIN_HUE + saturation = random.random() * (MAX_SATURATION - MIN_SATURATION) + MIN_SATURATION + value = random.random() * (MAX_VALUE - MIN_VALUE) + MIN_VALUE return color.HSV(hue, saturation, value) @@ -88,12 +107,13 @@ def transition(start, end, step): return start diff_vector = end - start step_vector = diff_vector / abs(diff_vector) * step - return step_vector + start + result = step_vector + start + return result def random_change(): """Return a random amount that a light should change each frame.""" - min_time = int(MIN_TIME * FRAME_RATE) + min_time = int(constants.MIN_TIME * FRAME_RATE) max_time = int(MAX_TIME * FRAME_RATE) return 1 / random.randint(min_time, max_time) diff --git a/sunrise.py b/sunrise.py new file mode 100644 index 0000000..68a423c --- /dev/null +++ b/sunrise.py @@ -0,0 +1,127 @@ +"""Rendering classes and helper functions.""" +from __future__ import division +import color +import random +import constants +from constants import * +import math + +class Fish(object): + """Object displayed on the bridge, probably a fish.""" + def __init__(self, x, y, width, height, c, step): + """Create fish with default parameters.""" + self.x = x + self.y = y + self.x = 75 + self.y = -2 + self.width = width + self.width = 75 + self.height = height + self.height = 2 + self.color = c + self.color = color.RGB(1, 0.5, 0) + self.speed = step + self.step = step + self.wait = 0 + + def update(self): + """Swim gently to the other side of the bridge.""" + #self.x += self.step + #if self.x > 200: + # self.x = 0 + #self.y = 0.25 + 0.25 * math.sin(math.pi * self.x / 10) + #theta = random.random() * math.pi / 2 - math.pi / 4 + #self.y += 0.01 * math.sin(theta) + #print(self.y) + self.y += 0.000005 + if self.y >= 1: + self.y = 1 + self.wait += 1 + if self.wait >= 200: + self.wait = 0 + self.y = -2 + + +class Light(object): + """Represent a bridge light and its state.""" + def __init__(self, x, y, light): + """Store all facts about a light.""" + self.light = light + self.color = color.black + self.transitioning_to = self.color + self.step = 0 + self.x = x + self.y = y + + def update(self, fish): + """Change color depending on where the fish is.""" + if self.y == 1: + new_color = color.RGB(0.5, 0.5, 0.5) + else: + new_color = color.RGB(0.5, 0.5, 0) + coverage = compute_coverage(fish, self.x, self.y) + ccolor = new_color * (1 - coverage) + fish.color * coverage + self.light.setRGBRaw( + *map(lambda x: clamp(0, x, 1), ccolor.components) + ) + + +class Bridge(object): + """Store objects on the bridge.""" + def __init__(self, lights, fish, rig): + """Initialize with lights and fish.""" + self.lights = lights + self.fish = fish + self.rig = rig + + def update(self): + """Compute fish coverages and push lights to bridge.""" + for x, light_col in enumerate(self.lights): + for y, light in enumerate(light_col): + self.fish.update() + light.update(self.fish) + self.rig.updateOnce() + + +def compute_coverage(fish, x, y): + """Calculate the coverage of a fish over a box.""" + middle_of_fish = fish.x + fish.width / 2 + middle_of_panel = x + 0.5 + diff = abs(middle_of_fish - middle_of_panel) / (fish.width / 2) + scale = 1 - diff + min_x = min(max(x, fish.x), x + 1) + max_x = max(min(x+1, fish.x + fish.width), x) + min_y = min(max(y, fish.y), y + 1) + max_y = max(min(y+1, fish.y + fish.height), y) + coverage = (max_x - min_x) * (max_y - min_y) + return coverage * scale + + +def pick_HSV(): + """Return a random color in an appropriate interval.""" + hue = random.random() * (constants.MAX_HUE - constants.MIN_HUE) + constants.MIN_HUE + saturation = random.random() * (MAX_SATURATION - MIN_SATURATION) + MIN_SATURATION + value = random.random() * (MAX_VALUE - MIN_VALUE) + MIN_VALUE + return color.HSV(hue, saturation, value) + + +def transition(start, end, step): + """Return a vector equal to (end - start) * step + start.""" + if start == end: + return start + diff_vector = end - start + step_vector = diff_vector / abs(diff_vector) * step + result = step_vector + start + return result + + +def random_change(): + """Return a random amount that a light should change each frame.""" + min_time = int(constants.MIN_TIME * FRAME_RATE) + max_time = int(MAX_TIME * FRAME_RATE) + return 1 / random.randint(min_time, max_time) + + +def clamp(low, x, high): + """Return v such that low <= v <= high and |x - v| is minimal.""" + return max(low, min(x, high)) diff --git a/twinkle.py b/twinkle.py new file mode 100755 index 0000000..a2cb3d2 --- /dev/null +++ b/twinkle.py @@ -0,0 +1,112 @@ +#!/usr/bin/python2 +"""Simple demo of twinkle lights.""" +from __future__ import division +import color +import constants +#from DummyRig import DummyRig +from fish_render import * +import fish_render +from constants import * +import time +import random +try: + import lumiversepython +except ImportError: + have_lumiversepython = False +else: + have_lumiversepython = True + +def get_all_lights(rig): + """Return a list of all panels.""" + return [ + [ + fish_render.Light( + x, int(y == 'top'), + rig.select('$side={y}[$sequence={x}]'.format(y=y, x=x)) + ) for y in ('top', 'bot') + ] for x in xrange(1, 200) + ] + +def flip(): + """Return 0 with 99.9% prob. & 1 with .1% prob.""" + # should be black most of the time + return random.random() < 0.0005 + + +def twinkle(lights, rig): + """Try to do some light things.""" + light_status = [ + { + 'is_increasing': True, + 'is_on': False, + 'change': random_change(), + 'light': light, + 'intensity': 0, + } + for light_col in lights for light in light_col + ] + shooting_stars = list() + while True: + if random.random() < 1: + shooting_stars.append([random.randint(1, 180), random.random() < 0.5, 0]) + for light_s in light_status: + is_increasing = light_s['is_increasing'] + is_on = light_s['is_on'] + change = light_s['change'] + light = light_s['light'] + intensity = light_s['intensity'] + # R and G are either 0 or 1 + if not is_on: + if flip(): + is_on = True + is_increasing = True + change = 1.5 / FRAME_RATE + light_s['is_on'] = is_on + light_s['is_increasing'] = is_increasing + light_s['change'] = change + else: + continue + intensity = clamp(0, intensity + change * (int(is_increasing) * 2 - 1), 1) + light.light.setRGBRaw(intensity, intensity, intensity) + light_s['intensity'] = intensity + if intensity == 0: + light_s['is_on'] = False + if intensity == 1: + light_s['is_increasing'] = False + for i, star in enumerate(shooting_stars): + star[2] += 1 + if star[2] > 10: + for dx in range(-6, 7): + light_status[star[0] + dx]['light'].light.setRGBRaw(0, 0, 0) + del shooting_stars[i] + continue + star[0] += int(star[1]) * 2 - 1 + for dx in range(-6, 7): + intensity = 1 - 1 / 6 * abs(dx) + light_status[star[0] + dx]['light'].light.setRGBRaw(intensity, intensity, intensity) + rig.updateOnce() + time.sleep(1 / FRAME_RATE) + +def sunrise(lights, rig): + #rig.select('$side={y}[$sequence={x}]'.format(y=y, x=x)) + rig.select("$side=top[$panel=27|$panel=28]") + rig.updateOnce() + time.sleep(1) + + +def main(): + """Run show.""" + if have_lumiversepython: + rig = lumiversepython.Rig('/home/teacher/Lumiverse/PBridge.rig.json') + else: + rig = DummyRig(200, 2) + rig.init() + lights = get_all_lights(rig) + #for light in lights: + # light.setRGBRaw(0, 0, 0) + rig.updateOnce() + time.sleep(3) + twinkle(lights, rig) + +if __name__ == '__main__': + main() diff --git a/webapp/app/__init__.py b/webapp/app/__init__.py new file mode 100644 index 0000000..aa2e264 --- /dev/null +++ b/webapp/app/__init__.py @@ -0,0 +1,11 @@ +from flask import Flask +import socketio +import eventlet +import eventlet.wsgi + +sio = socketio.Server(async_mode='threading') +app = Flask(__name__) +app.wsgi_app = socketio.Middleware(sio, app.wsgi_app) +from app import views + + diff --git a/webapp/app/templates/index.html b/webapp/app/templates/index.html new file mode 100644 index 0000000..b0d9bb3 --- /dev/null +++ b/webapp/app/templates/index.html @@ -0,0 +1,55 @@ +<html> + <head> + <script type="text/javascript" src="//code.jquery.com/jquery-2.1.4.min.js"></script> + <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.5/socket.io.min.js"></script> + + <script> + var SPAWN_URL = "/spawn"; + + function creature(name, socket) { + var self = this; + self.name = name; + self.socket = socket; + + self.render = function($parent) { + var $a = $('<a>'); + $a.text(self.name); + $a.click(self.click.bind(self)); + + var $li = $('<li>'); + $li.append($a); + $parent.append($li); + }; + + self.click = function() { + self.socket.emit('spawn', {name:self.name}); + } + } + + $(document).ready(function() { + var namespace = '/spawn'; + var socket = io.connect('http://' + document.domain + ':' + location.port + namespace); + + socket.on('connect', function() { + console.log('connected!'); + }); + + socket.on('new creature', function(msg) { + console.log('new!'); + var c = new creature(msg.name, socket); + c.render($('#creatures')); + }); + + socket.on('disconnect', function() { + console.log('disconnected'); + }); + }); + + </script> + <title>{{ title }}</title> + </head> + <body> + <h1>Ocean Fun Times</h1> + <ul id="creatures"></ul> + </body> +</html> diff --git a/webapp/app/views.py b/webapp/app/views.py new file mode 100644 index 0000000..4aa2c05 --- /dev/null +++ b/webapp/app/views.py @@ -0,0 +1,37 @@ +from flask import render_template, request +from app import app, sio +import time + +import threading + +thread = None + +@app.route('/') +@app.route('/index') +def index(): + return render_template('index.html', + title='F16 Group1') + +connections = set() + +@sio.on('connect', namespace='/spawn') +def connect(sid, environ): + print("connect ", sid) + connections.add(sid) + +@sio.on('spawn', namespace='/spawn') +def message(sid, data): + print("message ", data) + +@sio.on('disconnect', namespace='/spawn') +def disconnect(sid): + print('disconnect', sid) + connections.remove(sid) + + + +def add_creature(): + time.sleep(5) + sio.emit('new creature', {'name': 'fish1'}, namespace='/spawn') + +thread = sio.start_background_task(add_creature) diff --git a/webapp/run.py b/webapp/run.py new file mode 100755 index 0000000..b5c8c6f --- /dev/null +++ b/webapp/run.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python + +import sys +sys.path.append('packages') + +from app import app +app.run(threaded=True, host='', port=8000) |
