[71] | 1 | #!/usr/bin/python |
---|
| 2 | |
---|
| 3 | import time |
---|
| 4 | import random |
---|
| 5 | import math |
---|
| 6 | |
---|
| 7 | from core import ColorPattern, MotionPattern, EventHandler |
---|
| 8 | from color import Color, Colors |
---|
| 9 | from geometry import key_positions, KeyMatrix, Keys |
---|
| 10 | |
---|
| 11 | |
---|
| 12 | class AlternatingColorPattern(ColorPattern): |
---|
| 13 | """Steps through a cycle of colors""" |
---|
| 14 | def color(self, t): |
---|
| 15 | index = int(round(len(self.colors)*self.phase(t))) - 1 |
---|
| 16 | return self.colors[index] |
---|
| 17 | |
---|
| 18 | |
---|
| 19 | class BeatColorPattern(ColorPattern): |
---|
| 20 | """Linearly fades through a cycle of colors""" |
---|
| 21 | def color(self, t): |
---|
| 22 | index = int(len(self.colors)*self.phase(t)) % len(self.colors) |
---|
| 23 | foreground = self.colors[index] |
---|
| 24 | background = self.colors[(index+1)%len(self.colors)] |
---|
| 25 | phase = (self.phase(t) * len(self.colors)) % 1 |
---|
| 26 | return Color.blend(foreground, background, transparency=phase) |
---|
| 27 | |
---|
| 28 | |
---|
| 29 | class ThrobColorPattern(ColorPattern): |
---|
| 30 | """Non-linearly (sin wave) fades through a cycle of colors""" |
---|
| 31 | def color(self, t): |
---|
| 32 | index = (2*int(len(self.colors)*self.phase(t))) % len(self.colors) |
---|
| 33 | foreground = self.colors[index] |
---|
| 34 | background = self.colors[(index+1)%len(self.colors)] |
---|
| 35 | phase = (self.phase(t) * len(self.colors)) % 1 |
---|
| 36 | intensity = (1 + math.sin(2 * math.pi * phase)) / 2 |
---|
| 37 | return Color.blend(foreground, background, transparency=1-intensity) |
---|
| 38 | |
---|
| 39 | |
---|
| 40 | class PulseColorPattern(ColorPattern): |
---|
| 41 | """Pulses a color on, then fades to the next color, through a cycle of |
---|
| 42 | colors |
---|
| 43 | """ |
---|
| 44 | def color(self, t): |
---|
| 45 | index = (2 * int(len(self.colors)*self.phase(t))) % len(self.colors) |
---|
| 46 | foreground = self.colors[index] |
---|
| 47 | background = self.colors[(index+1)%len(self.colors)] |
---|
| 48 | phase = (self.phase(t) * len(self.colors)) % 1 |
---|
| 49 | return Color.blend(foreground, background, |
---|
| 50 | transparency=phase) |
---|
| 51 | |
---|
| 52 | |
---|
| 53 | class AngleMotionPattern(MotionPattern): |
---|
| 54 | """Applies a phase offset across the keyboard at a specified angle and |
---|
| 55 | stretched a specified distance. (Makes for something of a wave effect.) |
---|
| 56 | """ |
---|
| 57 | def __init__(self, angle=0.0, size=50, *args, **kwargs): |
---|
| 58 | super(AngleMotionPattern, self).__init__(*args, **kwargs) |
---|
| 59 | self.angle = angle |
---|
| 60 | self.size = size |
---|
| 61 | |
---|
| 62 | def phase_offset(self, x, y): |
---|
| 63 | # The pixels for the keyboard geometry are not square; so account for |
---|
| 64 | # their shape by doubling the Y value. |
---|
| 65 | return (- x * math.cos(self.angle) - 2 * y * math.sin(self.angle)) / self.size |
---|
| 66 | |
---|
| 67 | |
---|
| 68 | # Lighting animations |
---|
| 69 | class RandomAnimation(EventHandler): |
---|
| 70 | """Light up a randomly selected subset of keys""" |
---|
| 71 | tick_rate = 0.1 |
---|
| 72 | |
---|
| 73 | def __init__(self, foreground=Colors.WHITE, background=Colors.BLACK, |
---|
| 74 | quantity=40, *args, **kwargs): |
---|
| 75 | super(RandomAnimation, self).__init__(*args, **kwargs) |
---|
| 76 | self.foreground = foreground |
---|
| 77 | self.background = background |
---|
| 78 | self.quantity = quantity |
---|
| 79 | self.lit_keys = [] |
---|
| 80 | |
---|
| 81 | def tick(self): |
---|
| 82 | """Handler for the periodic events""" |
---|
| 83 | # We don't enforce a strict rate here. If you are typing, the random |
---|
| 84 | # updating of keys will occur more frequently. If this is getting |
---|
| 85 | # called as part of a group of event handlers where another event |
---|
| 86 | # handler has a lower value for tick_rate, this animation will toggle |
---|
| 87 | # keys faster. Given the intent of this animation, that isn't a |
---|
| 88 | # problem. |
---|
| 89 | if len(self.lit_keys) > self.quantity: |
---|
| 90 | key = self.lit_keys.pop(0) |
---|
| 91 | self.keyboard.set_keys(key, self.background) |
---|
| 92 | key = random.choice(list(set(Keys.ALL.split(',')) - set(self.lit_keys))) |
---|
| 93 | self.lit_keys.append(key) |
---|
| 94 | self.keyboard.set_keys(key, self.foreground) |
---|
| 95 | |
---|
| 96 | |
---|
| 97 | class ColorAnimation(EventHandler): |
---|
| 98 | """Given a color pattern, apply that pattern to the keyboard""" |
---|
| 99 | def __init__(self, color_pattern=None, keys=Keys.ALL, *args, **kwargs): |
---|
| 100 | super(ColorAnimation, self).__init__(*args, **kwargs) |
---|
| 101 | self.keys = keys |
---|
| 102 | self.color_pattern = color_pattern |
---|
| 103 | |
---|
| 104 | def tick(self): |
---|
| 105 | color = self.color_pattern.color(time.time()) |
---|
| 106 | self.keyboard.set_keys(self.keys, color) |
---|
| 107 | |
---|
| 108 | |
---|
| 109 | class MotionAnimation(EventHandler): |
---|
| 110 | def __init__(self, color_pattern=None, motion_pattern=None, keys=Keys.ALL, |
---|
| 111 | *args, **kwargs): |
---|
| 112 | super(MotionAnimation, self).__init__(*args, **kwargs) |
---|
| 113 | self.keys = keys.split(',') |
---|
| 114 | self.color_pattern = color_pattern |
---|
| 115 | self.motion_pattern = motion_pattern |
---|
| 116 | |
---|
| 117 | # pre-calculate the points we will have to color |
---|
| 118 | self.points = [] |
---|
| 119 | for key in self.keys: |
---|
| 120 | self.points.extend(key_positions[key]) |
---|
| 121 | |
---|
| 122 | def tick(self): |
---|
| 123 | matrix = KeyMatrix() |
---|
| 124 | for x,y in self.points: |
---|
| 125 | color = self.color_pattern.color(time.time() + self.motion_pattern.t_offset(x, y)) |
---|
| 126 | matrix.set_pixel(x, y, color) |
---|
| 127 | colors = {} |
---|
| 128 | for key in self.keys: |
---|
| 129 | color = matrix.get_key(key) |
---|
| 130 | colors.setdefault(color, []).append(key) |
---|
| 131 | key_colors = [] |
---|
| 132 | for color, keys in colors.items(): |
---|
| 133 | key_colors.append((','.join(keys), color)) |
---|
| 134 | self.keyboard.set_many_keys(key_colors) |
---|