diff options
Diffstat (limited to 'twinkle.py')
| -rwxr-xr-x | twinkle.py | 241 |
1 files changed, 142 insertions, 99 deletions
@@ -1,112 +1,155 @@ -#!/usr/bin/python2 -"""Simple demo of twinkle lights.""" +"""Rendering classes and helper functions.""" from __future__ import division import color +import random 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 +import math +from show import Show + +class ShootingStar(object): + """Object displayed on the bridge, probably a fish.""" + def __init__(self): + """Create fish with default parameters.""" + self.x = random.random() * BRIDGE_WIDTH + self.y = random.random() * 1.5 + self.width = 5 + self.height = 0.5 + self.color = color.RGB(1, 1, 1) + if random.random() >= 0.5: + self.speed = 8 + else: + self.speed = -8 -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 update(self): + self.x += self.speed + self.speed *= 0.9 + return abs(self.speed) > 1 and self.x < BRIDGE_WIDTH -def flip(): - """Return 0 with 99.9% prob. & 1 with .1% prob.""" - # should be black most of the time - return random.random() < 0.0005 +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.result_color = self.color + self.transitioning_to = self.color + self.step = 0 + self.on = False + self.increasing = False + self.x = x + self.y = y + self.fade_step = 0 -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 + def update_base(self): + """Change color depending on where the fish is.""" + if not self.on and random.random() < 0.0005: + self.transitioning_to = color.RGB(1, 1, 1) + self.step = abs(self.transitioning_to - self.color) / (FRAME_RATE / 2) + self.increasing = True + self.on = True + self.color = transition(self.color, self.transitioning_to, self.step) + elif self.on: + if abs(self.transitioning_to - self.color) <= self.step: + if self.increasing == True: + self.transitioning_to = color.RGB(0, 0, 0) + self.step = abs(self.transitioning_to - self.color) / (FRAME_RATE / 2) + self.increasing = False 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) + self.increasing = False + self.on = False + self.color = transition(self.color, self.transitioning_to, self.step) + + self.result_color = self.color + + def update_star(self, star): + coverage = compute_coverage(star, self.x, self.y) + self.result_color = coverage * star.color + (1 - coverage) * self.result_color + + def blit(self): + self.light.setRGBRaw( + *map(lambda x: clamp(0, x, 1), self.result_color.components) + ) + + + + + def fade_to_black(self, steps): + hue = self.color.to_HSV().h() + sat = self.color.to_HSV().s() + value = self.color.to_HSV().v() + new_color = color.HSV(hue, sat, value - value / steps * self.fade_step).to_RGB() + self.fade_step += 1 + self.light.setRGBRaw(*map(lambda x: clamp(0, x, 1), new_color.components)) + + +class TwinkleShow(Show): + def init(self): + self.lights = [[Light(x, y, self.bridge.get_light(x, y)) + for y in xrange(self.bridge.HEIGHT)] + for x in xrange(self.bridge.WIDTH)] + #self.sun = Sun() + self.stars = set() + self.tick_count = 0 + + + def update(self): + if self.tick_count > 1000: + fade_time = 3 # sec + if self.tick_count > 1000 + fade_time * FRAME_RATE: + self.stop() + return + for col in self.lights: + for light in col: + light.fade_to_black(fade_time * FRAME_RATE) + else: + if random.random() < 0.01: + print "Adding star" + self.stars.add(ShootingStar()) + for col in self.lights: + for light in col: + light.update_base() + + for star in list(self.stars): + if not star.update(): + self.stars.remove(star) + + for x in xrange(int(star.x), int(star.x+star.width+1)): + if x < len(self.lights): + for light in self.lights[x]: + light.update_star(star) + + for col in self.lights: + for light in col: + light.blit() + + self.tick_count += 1 + print self.tick_count -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 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 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) +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 -if __name__ == '__main__': - main() +def clamp(low, x, high): + """Return v such that low <= v <= high and |x - v| is minimal.""" + return max(low, min(x, high)) |
