#!/usr/bin/python import time import random import math from core import ColorPattern, MotionPattern, EventHandler from color import Color, Colors from geometry import key_positions, KeyMatrix, Keys class AlternatingColorPattern(ColorPattern): """Steps through a cycle of colors""" def color(self, t): index = int(round(len(self.colors)*self.phase(t))) - 1 return self.colors[index] class BeatColorPattern(ColorPattern): """Linearly fades through a cycle of colors""" def color(self, t): index = int(len(self.colors)*self.phase(t)) % len(self.colors) foreground = self.colors[index] background = self.colors[(index+1)%len(self.colors)] phase = (self.phase(t) * len(self.colors)) % 1 return Color.blend(foreground, background, transparency=phase) class ThrobColorPattern(ColorPattern): """Non-linearly (sin wave) fades through a cycle of colors""" def color(self, t): index = (2*int(len(self.colors)*self.phase(t))) % len(self.colors) foreground = self.colors[index] background = self.colors[(index+1)%len(self.colors)] phase = (self.phase(t) * len(self.colors)) % 1 intensity = (1 + math.sin(2 * math.pi * phase)) / 2 return Color.blend(foreground, background, transparency=1-intensity) class PulseColorPattern(ColorPattern): """Pulses a color on, then fades to the next color, through a cycle of colors """ def color(self, t): index = (2 * int(len(self.colors)*self.phase(t))) % len(self.colors) foreground = self.colors[index] background = self.colors[(index+1)%len(self.colors)] phase = (self.phase(t) * len(self.colors)) % 1 return Color.blend(foreground, background, transparency=phase) class AngleMotionPattern(MotionPattern): """Applies a phase offset across the keyboard at a specified angle and stretched a specified distance. (Makes for something of a wave effect.) """ def __init__(self, angle=0.0, size=50, *args, **kwargs): super(AngleMotionPattern, self).__init__(*args, **kwargs) self.angle = angle self.size = size def phase_offset(self, x, y): # The pixels for the keyboard geometry are not square; so account for # their shape by doubling the Y value. return (- x * math.cos(self.angle) - 2 * y * math.sin(self.angle)) / self.size # Lighting animations class RandomAnimation(EventHandler): """Light up a randomly selected subset of keys""" tick_rate = 0.1 def __init__(self, foreground=Colors.WHITE, background=Colors.BLACK, quantity=40, *args, **kwargs): super(RandomAnimation, self).__init__(*args, **kwargs) self.foreground = foreground self.background = background self.quantity = quantity self.lit_keys = [] def tick(self): """Handler for the periodic events""" # We don't enforce a strict rate here. If you are typing, the random # updating of keys will occur more frequently. If this is getting # called as part of a group of event handlers where another event # handler has a lower value for tick_rate, this animation will toggle # keys faster. Given the intent of this animation, that isn't a # problem. if len(self.lit_keys) > self.quantity: key = self.lit_keys.pop(0) self.keyboard.set_keys(key, self.background) key = random.choice(list(set(Keys.ALL.split(',')) - set(self.lit_keys))) self.lit_keys.append(key) self.keyboard.set_keys(key, self.foreground) class ColorAnimation(EventHandler): """Given a color pattern, apply that pattern to the keyboard""" def __init__(self, color_pattern=None, keys=Keys.ALL, *args, **kwargs): super(ColorAnimation, self).__init__(*args, **kwargs) self.keys = keys self.color_pattern = color_pattern def tick(self): color = self.color_pattern.color(time.time()) self.keyboard.set_keys(self.keys, color) class MotionAnimation(EventHandler): def __init__(self, color_pattern=None, motion_pattern=None, keys=Keys.ALL, *args, **kwargs): super(MotionAnimation, self).__init__(*args, **kwargs) self.keys = keys.split(',') self.color_pattern = color_pattern self.motion_pattern = motion_pattern # pre-calculate the points we will have to color self.points = [] for key in self.keys: self.points.extend(key_positions[key]) def tick(self): matrix = KeyMatrix() for x,y in self.points: color = self.color_pattern.color(time.time() + self.motion_pattern.t_offset(x, y)) matrix.set_pixel(x, y, color) colors = {} for key in self.keys: color = matrix.get_key(key) colors.setdefault(color, []).append(key) key_colors = [] for color, keys in colors.items(): key_colors.append((','.join(keys), color)) self.keyboard.set_many_keys(key_colors)