Compare commits

..

51 Commits

Author SHA1 Message Date
Fr3deric 44dfe58d61 Merge branch 'balancep0ng-improve-player-joining' 5 years ago
Fr3deric e563e61cfd use balance from x axis 5 years ago
Fr3deric 7f754c032b cancel game when second player joins 5 years ago
Fr3deric bbf4051db1 add function to cancel game 5 years ago
Fr3deric f1bb7a0396 fix crash when left player is bot 5 years ago
Fr3deric d777e5ec97 allow first player on both boards 5 years ago
Fr3deric ac6ad5403c remove --players argument 5 years ago
Fr3deric 497d340503 only show status output when enabled via env var 6 years ago
Fr3deric 08ebd573e3 remove debug output, decrease sleep when waiting 6 years ago
klonfish 1cc28fadcc Python3ified and PEP8ed blpserver 6 years ago
klonfish 6e297fecca Fixed shebang 6 years ago
klonfish c6224c145f Fixed shebangs 7 years ago
informaniac 8d9e1b3b2d refactored input stuff into balance_util module 7 years ago
klonfish 368384481c Added grace period to player detection to allow for accidential stepping off a board 7 years ago
klonfish fbe4fd49ba Reduced minimum weight for player detection to 10 kg 7 years ago
Fr3deric 99ed7b48a1 fixed premature animation restart 7 years ago
Fr3deric c0d6872c5a adapted box color to gamma correction 7 years ago
Fr3deric 3b06c37b79 added twister.py 7 years ago
informaniac b0c20959a1 fixed frame calculation... again 7 years ago
informaniac 7c31f7d5c2 fixed frame calculation 7 years ago
informaniac 59cb9db3b3 frames are now determined by seconds as command line argument 7 years ago
informaniac 67eaf2d05f moved optional argument down to group the mandatory objects on top 7 years ago
informaniac 42941df93c moved blup objects to main function 7 years ago
informaniac df9332777c delay is now a command line argument 7 years ago
informaniac 92432408a5 alive color is generated randomly 7 years ago
informaniac d51becbfc2 game of life animation overhaul 7 years ago
klonfish 22787835b2 Added gamma correction to HD output 7 years ago
klonfish 8ec773cacc Changed DMA channel to 10 since 5 seems to be causing SD card corruption. See https://github.com/jgarff/rpi_ws281x/issues/224 7 years ago
Fr3deric a85cb90347 added discs.py 7 years ago
Fr3deric e498ca8b17 added lines.py 7 years ago
Fr3deric e743f4a47c added checkerboard.py 7 years ago
Fr3deric 43dcd7a620 added curves.py 7 years ago
Fr3deric ab67d72f83 added thimblerig.py 7 years ago
Fr3deric 543aef2467 added dlines.py 7 years ago
Fr3deric ad9b697b05 improved cycle path 7 years ago
Fr3deric 254d21eb91 added movingshapes.py 7 years ago
Fr3deric 815edba13e added faces.py 7 years ago
Fr3deric 1dd2d0a943 added option to specify approximate animation time 7 years ago
Fr3deric 2f8df5467d don't crash in case a frame cannot be sent 7 years ago
Fr3deric 5600f48fb3 replaced 'ignore hidden files/dirs' fature with file extension filter, currently hardcoded to bml 7 years ago
Fr3deric 1c14d6032b ignore hidden files and directories 7 years ago
Fr3deric b899ec0fdc improved animation selection logic, added support for subdirs and symlinks in animation directory 7 years ago
Fr3deric 2073a3e277 improved shape generation, added random to bounceoff 7 years ago
Fr3deric b73f05b294 added collision detection 7 years ago
Fr3deric 626a2bef18 added unrath.py 7 years ago
Fr3deric 93acb5bf0e added blobs.py 7 years ago
Fr3deric 06db0b5ee1 added orbs.py 7 years ago
Fr3deric b494ef2f41 added gif2bml.py 7 years ago
Fr3deric 938f97c136 fixed permissions 7 years ago
Fr3deric 3ebec72f76 Feuerwerk! 7 years ago
Fr3deric 062c2efdb9 added --no-loop option 7 years ago
  1. 130
      blup/balance_util.py
  2. 16
      blup/output.py
  3. 110
      games/balancep0ng.py
  4. 61
      games/gamemenu.py
  5. 9
      games/pong.py
  6. 63
      games/ttrs.py
  7. 126
      generators/blobs.py
  8. 140
      generators/checkerboard.py
  9. 2
      generators/colormarq.py
  10. 2
      generators/colorwischer.py
  11. 107
      generators/curves.py
  12. 114
      generators/discs.py
  13. 141
      generators/dlines.py
  14. 150
      generators/faces.py
  15. 2
      generators/fire.py
  16. 138
      generators/fireworks.py
  17. 147
      generators/gameOfLife.py
  18. 36
      generators/gif2bml.py
  19. 121
      generators/lines.py
  20. 222
      generators/movingshapes.py
  21. 122
      generators/orbs.py
  22. 2
      generators/scroll.py
  23. 232
      generators/thimblerig.py
  24. 124
      generators/twister.py
  25. 133
      generators/unrath.py
  26. 103
      utils/blmplay.py
  27. 6
      utils/blphub.py
  28. 149
      utils/blpserver.py
  29. 31
      utils/miniplayer.py
  30. 26
      wii-pair/daemon.py

130
blup/balance_util.py

@ -0,0 +1,130 @@
"""
this file is part of blup. it provides an easy way
to determine the direction the player is leaning
"""
from enum import Enum
import struct
Direction = Enum('BalanceDirection',
['LEFT', 'RIGHT', 'UP', 'DOWN',
'CENTER', 'QUIT'])
def sanitize_threshold(threshold):
"""
function to make sure the threshold is positive
threshold -- the given (untrusted) treshold
"""
if threshold < 0:
threshold = abs(threshold)
return threshold
class BalanceSocket(object):
def __init__(self, balance_server_socket):
self.socket = balance_server_socket
def close(self):
self.socket.close()
def _get_raw_data(self):
self.socket.send(b'a')
data = self.socket.recv(4)
p0x, p0y, p1x, p1y = struct.unpack('bbbb', data)
return p0x, p0y, p1x, p1y
class BalanceUtil(BalanceSocket):
def __init__(self, balance_server_socket, player_id):
"""
BalanceUtil object
balance_socket -- (ip, port) of the balance server
player_id -- id of the player
"""
super(BalanceUtil, self).__init__(balance_server_socket)
self.player_id = player_id
def _get_raw_player_data(self):
self.socket.send(b'a')
data = self.socket.recv(4)
p0x, p0y, p1x, p1y = struct.unpack('bbbb', data)
if self.player_id == 0:
return p0x, p0y
else:
return p1x, p1y
def get_player_ready(self):
"""
check if someone is on the board
:return: true if someone is on the board
"""
p_x, p_y = self._get_raw_player_data()
return p_x > -128 or p_y > -128
def get_raw_2dir_y(self):
"""
Get raw 2-directional data from the y axis of the board.
e.g. determine how fast the paddle is supposed to move
in balance pong
:return: raw y axis balance value
"""
x_bal, y_bal = self._get_raw_player_data()
return y_bal
def get_2dir_y(self, threshold):
threshold = sanitize_threshold(threshold)
x_bal, y_bal = self._get_raw_player_data()
if x_bal == -128 or y_bal == -128:
return Direction.QUIT
elif abs(y_bal) < threshold:
return Direction.CENTER
elif y_bal > 0:
return Direction.UP
else:
return Direction.DOWN
def get_raw_2dir_x(self):
x_bal, y_bal = self._get_raw_player_data()
return x_bal
def get_2dir_x(self, threshold):
threshold = sanitize_threshold(threshold)
x_bal, y_bal = self._get_raw_player_data()
if x_bal == -128 or y_bal == -128:
return Direction.QUIT
elif abs(x_bal) < threshold:
return Direction.CENTER
elif x_bal > 0:
return Direction.RIGHT
else:
return Direction.LEFT
def get_raw_4dir(self):
return self._get_raw_player_data()
def get_4dir(self, threshold):
"""
Get evaluated 4-directional data of the board
e.g. determine the direction and rotation or drop move
in balance tetris
threshold -- deadzone of the board where nothing is
supposed to happen
:return: Enum Direction value of direction or event
"""
threshold = sanitize_threshold(threshold)
x_bal, y_bal = self._get_raw_player_data()
if x_bal == -128 or y_bal == -128:
return Direction.QUIT
elif abs(x_bal) < threshold and abs(y_bal) < threshold:
return Direction.CENTER
elif abs(x_bal) < abs(y_bal):
if y_bal > 0:
return Direction.UP
else:
return Direction.DOWN
else:
if x_bal > 0:
return Direction.RIGHT
else:
return Direction.LEFT

16
blup/output.py

@ -331,17 +331,18 @@ class BLPOutput(Output):
# #
class BlinkenbuntHDOutput(Output): class BlinkenbuntHDOutput(Output):
moduleRegexDesc = 'bbunthd[:BRIGHTNESS]' moduleRegexDesc = 'bbunthd:BRIGHTNESS[:GAMMA]'
moduleRegex = '^bbunthd(:(?P<brightness>\d+))?$' moduleRegex = '^bbunthd:(?P<brightness>\d+)(:(?P<gamma>\d+(\.\d+)?))?$'
def __init__(self, brightness): def __init__(self, brightness, gamma=1):
import neopixel import neopixel
self.w = 22 self.w = 22
self.h = 16 self.h = 16
self.gamma = gamma
LED_COUNT = self.w * self.h LED_COUNT = self.w * self.h
LED_PIN = 13 LED_PIN = 13
LED_FREQ_HZ = 800000 LED_FREQ_HZ = 800000
LED_DMA = 5 LED_DMA = 10
LED_INVERT = False LED_INVERT = False
LED_PWM = 1 LED_PWM = 1
@ -354,8 +355,9 @@ class BlinkenbuntHDOutput(Output):
@classmethod @classmethod
def fromRegexMatch(cls, regexMatch): def fromRegexMatch(cls, regexMatch):
b = int(regexMatch.groupdict().get('brightness', 50)) b = int(regexMatch.group('brightness'))
return cls(brightness=b) g = float(regexMatch.group('gamma') or 1)
return cls(brightness=b, gamma=g)
def sendFrame(self, frame): def sendFrame(self, frame):
for y in range(self.h): for y in range(self.h):
@ -365,7 +367,7 @@ class BlinkenbuntHDOutput(Output):
else: else:
lednum = x*self.h - y + 15 lednum = x*self.h - y + 15
pix = frame.getPixel(self.w - x - 1, self.h - y - 1) pix = frame.getPixel(self.w - x - 1, self.h - y - 1)
r, g, b = pix r, g, b = map(lambda c: round(((c/255)**self.gamma)*255), pix)
self.strip.setPixelColor(lednum, self._color_cls(g, r, b)) self.strip.setPixelColor(lednum, self._color_cls(g, r, b))
self.strip.show() self.strip.show()

110
games/balancep0ng.py

@ -1,4 +1,4 @@
#!/usr/bin/python3 #!/usr/bin/env python3
import time import time
import argparse import argparse
@ -8,6 +8,7 @@ import os
import configparser import configparser
import socket import socket
import struct import struct
import blup.balance_util
import blup.frame import blup.frame
import blup.output import blup.output
import pong import pong
@ -72,28 +73,14 @@ def convertPixels(dimension, pixels, invert=False):
p.append(row) p.append(row)
return p return p
def get_balance_from_sock(sock, player_id):
print('will balance haben...')
sock.send(b'a')
data = sock.recv(4)
p1x, p1y, p2x, p2y = struct.unpack('bbbb', data)
if player_id == 0:
bal = p1y
elif player_id == 1:
bal = p2y
print('balance id=%s balance=%d' % (player_id, bal))
return bal
class BalanceBoardPlayer(object): class BalanceBoardPlayer(object):
def __init__(self, playground, ownPaddle, wiimote_sock, player_id): def __init__(self, playground, own_paddle, balance_util):
self.__playground = playground self.__playground = playground
self.__ownPaddle = ownPaddle self.__ownPaddle = own_paddle
self.__wiimote = wiimote_sock
self.__ready = False self.__ready = False
self.__playground.addGameTickCallback(self.gametickcb) self.__playground.addGameTickCallback(self.gametickcb)
self.__wiimote_sock = wiimote_sock self.__balance_util = balance_util
self.__player_id = player_id
@property @property
def ownPaddle(self): def ownPaddle(self):
@ -104,11 +91,12 @@ class BalanceBoardPlayer(object):
return true return true
def gametickcb(self): def gametickcb(self):
bal = get_balance_from_sock(self.__wiimote_sock, self.__player_id) # TODO: add CLI option to select axis
bal = self.__balance_util.get_raw_2dir_x()
if bal == -128: if bal == -128:
print("player %d has quit" % (self.__player_id)) print("player %d has quit" % self.__balance_util.player_id)
self.__wiimote_sock.close() self.__balance_util.socket.close()
sys.exit(1) sys.exit(1)
bal = -bal bal = -bal
@ -120,7 +108,7 @@ class BalanceBoardPlayer(object):
bal += MAX_AMPLITUDE bal += MAX_AMPLITUDE
pos = int((bal / (2 * MAX_AMPLITUDE)) * self.__playground.height) pos = int((bal / (2 * MAX_AMPLITUDE)) * self.__playground.height)
print("player %d pos=%d" % (self.__player_id, pos)) print("player %d pos=%d" % (self.__balance_util.player_id, pos))
if self.__ownPaddle.ypos > pos: if self.__ownPaddle.ypos > pos:
self.__ownPaddle.nextMoveUp() self.__ownPaddle.nextMoveUp()
@ -129,7 +117,7 @@ class BalanceBoardPlayer(object):
class B4lancePong(object): class B4lancePong(object):
def __init__(self, out, balanceserver=None, color=True, numplayers=None): def __init__(self, out, balanceserver=None, color=True):
self.__playground = None self.__playground = None
self.__out = out self.__out = out
self.__color = color self.__color = color
@ -137,13 +125,21 @@ class B4lancePong(object):
self.__balanceserver = ('localhost', 4711) self.__balanceserver = ('localhost', 4711)
else: else:
self.__balanceserver = balanceserver self.__balanceserver = balanceserver
self.__numplayers = numplayers
if color: if color:
self.__dimension = blup.frame.FrameDimension(22, 16, 256, 3) self.__dimension = blup.frame.FrameDimension(22, 16, 256, 3)
else: else:
self.__dimension = blup.frame.FrameDimension(18, 8, 2, 1) self.__dimension = blup.frame.FrameDimension(18, 8, 2, 1)
def gametickcb(self):
for i, p in enumerate(self.__players):
if isinstance(p, pong.PongBot):
print('bot in game')
if (isinstance(p, pong.PongBot) and \
self.__bplayers[i].get_player_ready()):
print('second player joined, cancelling current game')
self.__playground.cancel()
def runGame(self): def runGame(self):
print('starting a game...') print('starting a game...')
scoreLeft = 0 scoreLeft = 0
@ -155,26 +151,35 @@ class B4lancePong(object):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(self.__balanceserver) sock.connect(self.__balanceserver)
print('connected!') print('connected!')
self.__bplayers = [
blup.balance_util.BalanceUtil(sock, 0),
blup.balance_util.BalanceUtil(sock, 1)
]
while get_balance_from_sock(sock, 0) == -128: while not (self.__bplayers[0].get_player_ready() or
print('waiting for player...') self.__bplayers[1].get_player_ready()):
time.sleep(1) time.sleep(0.1)
self.__players = [] for i in range(5):
self.__players.append(BalanceBoardPlayer(self.__playground, self.__playground.leftPaddle, sock, 0)) frame = mk_logo_frame(self.__dimension, onePlayer)
self.__out.sendFrame(frame)
if self.__numplayers == 2: print('waiting for second player...')
self.__players.append(BalanceBoardPlayer(self.__playground, self.__playground.rightPaddle, sock, 1)) if (self.__bplayers[0].get_player_ready() and
elif self.__numplayers is None: self.__bplayers[1].get_player_ready()):
for i in range(5): break
frame = mk_logo_frame(self.__dimension, onePlayer) time.sleep(1)
self.__out.sendFrame(frame)
print('waiting for second player...') self.__players = []
if get_balance_from_sock(sock, 1) > -128: for i in [0, 1]:
self.__players.append(BalanceBoardPlayer(self.__playground, self.__playground.rightPaddle, sock, 1)) paddle = self.__playground.leftPaddle if i == 0 else \
break self.__playground.rightPaddle
time.sleep(1) if self.__bplayers[i].get_player_ready():
player = BalanceBoardPlayer(self.__playground, paddle,
self.__bplayers[i])
else:
player = pong.PongBot(self.__playground, paddle, 3)
self.__players.append(player)
frame = mk_logo_frame(self.__dimension, logo) frame = mk_logo_frame(self.__dimension, logo)
self.__out.sendFrame(frame) self.__out.sendFrame(frame)
@ -189,25 +194,25 @@ class B4lancePong(object):
self.__out.sendFrame(frame) self.__out.sendFrame(frame)
time.sleep(1) time.sleep(1)
if len(self.__players) == 1: self.__playground.addGameTickCallback(self.gametickcb)
bot = pong.PongBot(self.__playground, self.__playground.rightPaddle, 3)
self.__players.append(bot)
while max(scoreLeft, scoreRight) < __MAX_SCORE__: while (max(scoreLeft, scoreRight) < __MAX_SCORE__ and
not self.__playground.cancelled):
winner = self.__playground.play() winner = self.__playground.play()
if winner is self.__players[0].ownPaddle: if winner is self.__playground.leftPaddle:
scoreLeft += 1 scoreLeft += 1
else: else:
scoreRight += 1 scoreRight += 1
pong.displayScore(self.__out, self.__dimension, scoreLeft, scoreRight, 3000) pong.displayScore(self.__out, self.__dimension, scoreLeft, scoreRight, 3000)
for i in range(3): if not self.__playground.cancelled:
for i in range(3):
frame = blup.frame.Frame(self.__dimension)
self.__out.sendFrame(frame)
time.sleep(0.5)
pong.displayScore(self.__out, self.__dimension, scoreLeft, scoreRight, 500)
frame = blup.frame.Frame(self.__dimension) frame = blup.frame.Frame(self.__dimension)
self.__out.sendFrame(frame) self.__out.sendFrame(frame)
time.sleep(0.5)
pong.displayScore(self.__out, self.__dimension, scoreLeft, scoreRight, 500)
frame = blup.frame.Frame(self.__dimension)
self.__out.sendFrame(frame)
self.__playground = None self.__playground = None
self.__players = [] self.__players = []
@ -216,8 +221,6 @@ class B4lancePong(object):
if __name__ == '__main__': if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Blinkenbunt Pong!') parser = argparse.ArgumentParser(description='Blinkenbunt Pong!')
parser.add_argument('--players', dest='players', type=int,
help='number of players (autodetect if unspecified)')
parser.add_argument('--balance', dest='balance', type=str, parser.add_argument('--balance', dest='balance', type=str,
metavar='HOST:PORT', help='use balance input') metavar='HOST:PORT', help='use balance input')
parser.add_argument('--out', dest='out', type=str, metavar='OUTPUT', parser.add_argument('--out', dest='out', type=str, metavar='OUTPUT',
@ -227,7 +230,6 @@ if __name__ == '__main__':
out = blup.output.getOutput(args.out) out = blup.output.getOutput(args.out)
bhost, bport = args.balance.split(':') bhost, bport = args.balance.split(':')
p0ng = B4lancePong(out, balanceserver=(bhost, int(bport)), color=True, p0ng = B4lancePong(out, balanceserver=(bhost, int(bport)), color=True)
numplayers=args.players)
p0ng.runGame() p0ng.runGame()

61
games/gamemenu.py

@ -1,4 +1,4 @@
#!/usr/bin/python3 #!/usr/bin/env python3
import sys import sys
import time import time
@ -8,6 +8,7 @@ import argparse
import subprocess import subprocess
import socket import socket
import struct import struct
import blup.balance_util
import blup.frame import blup.frame
import blup.output import blup.output
@ -210,9 +211,8 @@ class TestInput(AbstractInput):
return None return None
class BalanceInput(AbstractInput, threading.Thread): class BalanceInput(AbstractInput, threading.Thread):
def __init__(self, addr, player_id): def __init__(self, balance_util):
self.addr = addr self.balance_util = balance_util
self.player_id = player_id
self.evt = None self.evt = None
self.lastevt = None self.lastevt = None
self.player_present = False self.player_present = False
@ -221,27 +221,13 @@ class BalanceInput(AbstractInput, threading.Thread):
self.start() self.start()
def run(self): def run(self):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
sock.connect(self.addr)
except ConnectionRefusedError:
print('could not connect to balance server', file=sys.stderr)
self.evt = InputEvent.QUIT
return
self.running = True self.running = True
evt = None evt = None
oldevt = None oldevt = None
lastchange = 0 lastchange = 0
while self.running: while self.running:
sock.send(b'a')
data = sock.recv(4)
p0x, p0y, p1x, p1y = struct.unpack('bbbb', data)
if self.player_id == 0:
xbal, ybal = p0x, p0y
elif self.player_id == 1:
xbal, ybal = p1x, p1y
THRESHOLD = 40 THRESHOLD = 40
direction = self.balance_util.get_4dir(THRESHOLD)
MIN_TIMES = { MIN_TIMES = {
InputEvent.LEFT: 0.05, InputEvent.LEFT: 0.05,
InputEvent.RIGHT: 0.05, InputEvent.RIGHT: 0.05,
@ -249,24 +235,21 @@ class BalanceInput(AbstractInput, threading.Thread):
InputEvent.DOWN: 0.05, InputEvent.DOWN: 0.05,
} }
if self.player_present: if self.player_present:
if xbal == -128 or ybal == -128: if direction == blup.balance_util.Direction.QUIT:
self.evt = InputEvent.QUIT self.evt = InputEvent.QUIT
self.player_present = False self.player_present = False
continue continue
elif direction == blup.balance_util.Direction.CENTER:
if abs(xbal) < THRESHOLD and abs(ybal) < THRESHOLD:
evt = None evt = None
else: elif direction == blup.balance_util.Direction.UP:
if abs(xbal) < abs(ybal): evt = InputEvent.UP
if ybal > 0: elif direction == blup.balance_util.Direction.DOWN:
evt = InputEvent.UP evt = InputEvent.DOWN
else: elif direction == blup.balance_util.Direction.RIGHT:
evt = InputEvent.DOWN evt = InputEvent.RIGHT
else: elif direction == blup.balance_util.Direction.LEFT:
if xbal > 0: evt = InputEvent.LEFT
evt = InputEvent.RIGHT
else:
evt = InputEvent.LEFT
if evt != oldevt: if evt != oldevt:
lastchange = time.time() lastchange = time.time()
oldevt = evt oldevt = evt
@ -281,7 +264,7 @@ class BalanceInput(AbstractInput, threading.Thread):
#print('player_id=%d event=%s' % (self.player_id, self.evt)) #print('player_id=%d event=%s' % (self.player_id, self.evt))
time.sleep(0.005) time.sleep(0.005)
else: else:
if xbal != -128 and ybal != -128: if direction != blup.balance_util.Direction.QUIT:
self.evt = InputEvent.STEP_ON self.evt = InputEvent.STEP_ON
self.player_present = True self.player_present = True
continue continue
@ -325,8 +308,14 @@ if __name__ == '__main__':
inp2 = TestInput() inp2 = TestInput()
elif args.balance is not None: elif args.balance is not None:
host, port = args.balance.split(':') host, port = args.balance.split(':')
inp1 = BalanceInput((host, int(port)), 0) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
inp2 = BalanceInput((host, int(port)), 1) try:
sock.connect((host, int(port)))
inp1 = BalanceInput(blup.balance_util.BalanceUtil(sock, 0))
inp2 = BalanceInput(blup.balance_util.BalanceUtil(sock, 1))
except ConnectionRefusedError:
print('could not connect to balance server', file=sys.stderr)
sys.exit(1)
while True: while True:
evt = inp1.get_event() evt = inp1.get_event()

9
games/pong.py

@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/env python3
import time import time
import random import random
@ -183,6 +183,8 @@ class Playground(object):
paddleRight = Paddle(self, width - 1, (height - paddlesize)//2, paddlesize) paddleRight = Paddle(self, width - 1, (height - paddlesize)//2, paddlesize)
self.__paddles = [paddleLeft, paddleRight] self.__paddles = [paddleLeft, paddleRight]
self.cancelled = False
self.__ball = Ball(self, 0, 0, 1, 1) self.__ball = Ball(self, 0, 0, 1, 1)
self.__gameTickCallbacks = [] self.__gameTickCallbacks = []
self.__newRoundCallbacks = [] self.__newRoundCallbacks = []
@ -227,6 +229,9 @@ class Playground(object):
if paddle.containsPoint(x, y): if paddle.containsPoint(x, y):
return paddle return paddle
def cancel(self):
self.cancelled = True
def play(self, serve=None): def play(self, serve=None):
leftPaddle = self.__paddles[0] leftPaddle = self.__paddles[0]
rightPaddle = self.__paddles[1] rightPaddle = self.__paddles[1]
@ -250,7 +255,7 @@ class Playground(object):
callback() callback()
ticks = 0 ticks = 0
while True: while not self.cancelled:
ticks += 1 ticks += 1
time.sleep(0.08) time.sleep(0.08)
if ticks %2 == 0: if ticks %2 == 0:

63
games/ttrs.py

@ -8,6 +8,7 @@ import time
import enum import enum
import math import math
import collections import collections
import blup.balance_util
import blup.frame import blup.frame
import blup.output import blup.output
import random import random
@ -233,55 +234,37 @@ class TestTtrsPlayer(TtrsPlayer):
class BalanceTtrsPlayer(TtrsPlayer, threading.Thread): class BalanceTtrsPlayer(TtrsPlayer, threading.Thread):
def __init__(self, playground, addr, player_id): def __init__(self, playground, balance_util):
self.playground = playground self.playground = playground
self.addr = addr self.balance_util = balance_util
self.player_id = player_id
self.evt = None self.evt = None
self.lastevt = None self.lastevt = None
threading.Thread.__init__(self, daemon=True) threading.Thread.__init__(self, daemon=True)
self.start() self.start()
def run(self): def run(self):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
sock.connect(self.addr)
except ConnectionRefusedError:
print('could not connect to balance server', file=sys.stderr)
self.evt = PlayerEvent.QUIT
return
self.running = True self.running = True
evt = None evt = None
oldevt = None oldevt = None
lastchange = 0 lastchange = 0
while self.running: while self.running:
sock.send(b'a') THRESHOLD = 40
data = sock.recv(4) direction = self.balance_util.get_4dir(THRESHOLD)
p0x, p0y, p1x, p1y = struct.unpack('bbbb', data)
if self.player_id == 0:
xbal, ybal = p0x, p0y
elif self.player_id == 1:
xbal, ybal = p1x, p1y
#print('player_id=%d xbal=%d ybal=%d' % (self.player_id, xbal, ybal)) if direction == blup.balance_util.Direction.QUIT:
if xbal == -128 or ybal == -128:
self.evt = PlayerEvent.QUIT self.evt = PlayerEvent.QUIT
elif direction == blup.balance_util.Direction.LEFT:
THRESHOLD = 40 evt = PlayerEvent.MOVE_LEFT
if abs(xbal) < THRESHOLD and abs(ybal) < THRESHOLD: elif direction == blup.balance_util.Direction.RIGHT:
evt = None evt = PlayerEvent.MOVE_RIGHT
elif direction == blup.balance_util.Direction.UP:
evt = PlayerEvent.ROTATE
elif direction == blup.balance_util.Direction.DOWN:
evt = PlayerEvent.DROP
else: else:
if abs(xbal) < abs(ybal): evt = None
if ybal > 0:
evt = PlayerEvent.ROTATE
else:
evt = PlayerEvent.DROP
else:
if xbal > 0:
evt = PlayerEvent.MOVE_RIGHT
else:
evt = PlayerEvent.MOVE_LEFT
#print('player_id=%d xbal=%d ybal=%d' % (self.player_id, xbal, ybal))
MIN_TIMES = { MIN_TIMES = {
PlayerEvent.MOVE_LEFT: 0.1, PlayerEvent.MOVE_LEFT: 0.1,
PlayerEvent.MOVE_RIGHT: 0.1, PlayerEvent.MOVE_RIGHT: 0.1,
@ -307,7 +290,7 @@ class BalanceTtrsPlayer(TtrsPlayer, threading.Thread):
def get_event(self): def get_event(self):
print('player_id=%d event=%s' % (self.player_id, self.evt)) print('player_id=%d event=%s' % (self.balance_util.player_id, self.evt))
evt = self.evt evt = self.evt
self.evt = None self.evt = None
return evt return evt
@ -481,13 +464,21 @@ if __name__ == '__main__':
seed = random.random() seed = random.random()
games = [] games = []
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
host, port = args.balance.split(':')
try:
sock.connect((host, int(port)))
except ConnectionRefusedError:
print('could not connect to balance server', file=sys.stderr)
sys.exit(1)
def start_game(player_id, xpos): def start_game(player_id, xpos):
pg = Playground(10, 16) pg = Playground(10, 16)
balance_util = blup.balance_util.BalanceUtil(sock, player_id)
if args.pygame: if args.pygame:
player = TestTtrsPlayer(pg) player = TestTtrsPlayer(pg)
elif args.balance is not None: elif args.balance is not None:
host, port = args.balance.split(':') player = BalanceTtrsPlayer(pg, balance_util)
player = BalanceTtrsPlayer(pg, (host, int(port)), player_id)
rnd = random.Random(seed) rnd = random.Random(seed)
game = TtrsGame(pg, player, rnd) game = TtrsGame(pg, player, rnd)
painter.add_game(game, xpos, 0) painter.add_game(game, xpos, 0)

126
generators/blobs.py

@ -0,0 +1,126 @@
#!/usr/bin/env python3
import sys
import random
import argparse
import blup.frame
import blup.output
import blup.animation
import blup.writebml
import colorsys
WIDTH = 22
HEIGHT = 16
DEPTH = 256
def convert_color(c):
return list(map(lambda x: int(round(x*(DEPTH-1))), c))
def get_random_color():
return colorsys.hsv_to_rgb(random.random(), 1, 1)
def get_random_point():
return (random.randint(0, WIDTH-1), random.randint(0, HEIGHT-1))
def cap(x):
return min(1, max(0, x))
def cyc(x):
while x < 0:
x += 1
while x > 1:
x -= 1
return x
class Point:
def __init__(self, pos, color, evolve):
self.pos = pos
self.color = color
self.neighbours = []
h, s, v = colorsys.rgb_to_hsv(*color)
self.evolve = evolve
if 'h' in evolve:
h += (random.random()-.5) * 0.1
if 's' in evolve:
s += (random.random()-.5) * 0.5
if 'v' in evolve:
v += (random.random()-.5) * 0.5
self.ncolor = colorsys.hsv_to_rgb(cyc(h), cap(s), cap(v))
def __eq__(self, other):
return self.pos == other.pos
def __hash__(self):
return self.pos.__hash__()
def get_neighbours(self):
for x in [-1, 0, 1]:
for y in [-1, 0, 1]:
n = (self.pos[0]+x, self.pos[1]+y)
if n[0] >= 0 and n[1] >= 0 and n[0] < WIDTH and \
n[1] < HEIGHT and abs(x+y) < 2:
yield Point(n, self.ncolor, self.evolve)
class Blob:
def __init__(self, pos, color):
e = random.choice('hsv')
if random.random() > 0.8:
e += random.choice('hsv')
p = Point(pos, color, e)
self.points = set([p])
self.candids = set(p.get_neighbours())
self.completed = False
def grow(self):
if len(self.candids) == 0:
self.completed = True
return
newp = random.sample(self.candids, 1)[0]
self.candids.remove(newp)
self.points.add(newp)
self.candids = self.candids.union(set(newp.get_neighbours()) -
self.points - self.candids)
def draw(self, frame):
for p in self.points:
frame.setPixel(p.pos[0], p.pos[1], convert_color(p.color))
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Generate blob animations')
parser.add_argument('-d', '--delay', type=int, default=50)
parser.add_argument('-t', '--time', type=int, default=15)
parser.add_argument('output_file')
args = parser.parse_args()
dim = blup.frame.FrameDimension(WIDTH, HEIGHT, DEPTH, 3)
anim = blup.animation.Animation(dim)
anim.tags['description'] = ' '.join(sys.argv)
t = 0
blobs = [Blob(get_random_point(), get_random_color())]
while t < args.time:
t += args.delay / 1000
frame = blup.animation.AnimationFrame(dim, args.delay)
for b in blobs:
for i in range(4):
b.grow()
b.draw(frame)
if len(blobs[-1].points) > WIDTH*HEIGHT*0.6 and len(blobs) < 3:
blobs.append(Blob(get_random_point(), get_random_color()))
if len(blobs) >=2 and blobs[0].completed and blobs[1].completed:
blobs.remove(blobs[0])
anim.addFrame(frame)
blup.writebml.writeBml(anim, args.output_file)

140
generators/checkerboard.py

@ -0,0 +1,140 @@
#!/usr/bin/env python3
import sys
import random
import math
import argparse
import collections
import blup.frame
import blup.output
import blup.animation
import blup.writebml
import colorsys
WIDTH = 22
HEIGHT = 16
DEPTH = 256
class Point(collections.namedtuple('Point', ['x', 'y'])):
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Point(self.x - other.x, self.y - other.y)
def __mul__(self, other):
if isinstance(other, Point):
raise Exception('not implemented')
else:
return Point(self.x * other, self.y * other)
def __truediv__(self, other):
if isinstance(other, Point):
raise ValueError()
else:
return self * (1/other)
@property
def len(self):
return math.sqrt(self.x**2 + self.y**2)
@property
def n(self):
n = Point(self.y, -self.x)
return n / n.len
@property
def intp(self):
return Point(int(round(self.x)), int(round(self.y)))
def convert_color(c):
return list(map(lambda x: int(round(x*(DEPTH-1))), c))
def get_random_color():
return colorsys.hsv_to_rgb(random.random(), 0.5 + random.random() / 2, 1)
class BlinkingCheckerboard:
def __init__(self, size, fieldsize, h, num_fields, rounds):
self.size, self.fieldsize, self.h, self.num_fields, self.rounds = \
size, fieldsize, h, num_fields, rounds
self.roundtime = 0.5
self.fieldpoints = [
Point(random.randint(0, WIDTH-1), random.randint(0, HEIGHT-1))
]
xm = self.fieldpoints[-1].x % (fieldsize * 2)
ym = self.fieldpoints[-1].y % (fieldsize * 2)
while len(self.fieldpoints) < self.num_fields:
p = random.choice(self.fieldpoints)
o = Point(random.randint(-2, 2), random.randint(-2, 2))
if abs(o.x) + abs(o.y) != 2:
continue
p += o * fieldsize
if p.x < 0 or p.y < 0 or p.x >= size.x or p.y > size.y:
continue
if p not in self.fieldpoints:
self.fieldpoints.append(p)
self.done = False
def update(self, t, dt):
r = t / self.roundtime
if r >= self.rounds + 1:
self.done = True
self.fieldvals = [0] * self.num_fields
else:
self.fieldvals = []
for i in range(self.num_fields):
offs = i / (self.num_fields - 1) * 2 * math.pi
pos = 2 * math.pi * r
self.fieldvals.append(max(math.sin(offs + pos), 0))
if r > self.rounds and r < self.rounds + 1:
if self.fieldvals[-1] < 0.01:
self.fieldvals[-1] = 0
def draw(self, frame):
for i, f in enumerate(self.fieldpoints):
color = colorsys.hsv_to_rgb(self.h, 1, self.fieldvals[i])
for x in range(self.fieldsize):
for y in range(self.fieldsize):
try:
p = f + Point(x, y)
frame.setPixel(p.x, p.y, convert_color(color))
except ValueError:
pass
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Generate animations')
parser.add_argument('-d', '--delay', type=int, default=50)
parser.add_argument('-t', '--time', type=int, default=15)
parser.add_argument('output_file')
args = parser.parse_args()
dim = blup.frame.FrameDimension(WIDTH, HEIGHT, DEPTH, 3)
anim = blup.animation.Animation(dim)
anim.tags['description'] = ' '.join(sys.argv)
t = 0
dt = args.delay / 1000
tt = 0
bc = None
while t < args.time or not bc.done:
t += dt
tt += dt
frame = blup.animation.AnimationFrame(dim, args.delay)
if (bc is None or bc.done) and t < args.time:
tt = 0
bc = BlinkingCheckerboard(
Point(WIDTH, HEIGHT),
2,
random.random(),
random.randint(13, 23),
random.randint(3, 5)
)
bc.update(tt, dt)
bc.draw(frame)
anim.addFrame(frame)
blup.writebml.writeBml(anim, args.output_file)

2
generators/colormarq.py

@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/env python3
import sys import sys

2
generators/colorwischer.py

@ -1,4 +1,4 @@
#!/usr/bin/python3 #!/usr/bin/env python3
import sys import sys
import colorsys import colorsys

107
generators/curves.py

@ -0,0 +1,107 @@
#!/usr/bin/env python3
import sys
import random
import math
import argparse
import collections
import blup.frame
import blup.output
import blup.animation
import blup.writebml
import colorsys
import functools
WIDTH = 22
HEIGHT = 16
DEPTH = 256
class Point(collections.namedtuple('Point', ['x', 'y'])):
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Point(self.x - other.x, self.y - other.y)
def __mul__(self, other):
if isinstance(other, Point):
raise Exception('not implemented')
else:
return Point(self.x * other, self.y * other)
def __truediv__(self, other):
if isinstance(other, Point):
raise ValueError()
else:
return self * (1/other)
@property
def len(self):
return math.sqrt(self.x**2 + self.y**2)
@property
def n(self):
n = Point(self.y, -self.x)
return n / n.len
@property
def intp(self):
return Point(int(round(self.x)), int(round(self.y)))
def convert_color(c):
return list(map(lambda x: int(round(x*(DEPTH-1))), c))
def get_random_color():
return colorsys.hsv_to_rgb(random.random(), 0.5 + random.random() / 2, 1)
class CurveFunction():
def __init__(self, p1, p2):
self.p1, self.p2 = p1, p2
d = p2 - p1
self.pp = p1 + d/2 + d.n * (d.len * 0.8)
def __call__(self, t):
ret = (self.p1 - self.pp*2 + self.p2) * t**2
ret += (self.p1 * -2 + self.pp * 2) * t
ret += self.p1
return ret
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Generate animations')
parser.add_argument('-d', '--delay', type=int, default=50)
parser.add_argument('-t', '--time', type=int, default=15)
parser.add_argument('output_file')
args = parser.parse_args()
dim = blup.frame.FrameDimension(WIDTH, HEIGHT, DEPTH, 3)
anim = blup.animation.Animation(dim)
anim.tags['description'] = ' '.join(sys.argv)
t = 0
dt = args.delay / 1000
while t < args.time:
t += dt
p1 = p2 = None
while p1 == p2 or (p1 - p2).len < max(WIDTH, HEIGHT) / 2:
p1 = Point(random.randint(0, WIDTH-1), random.randint(0, HEIGHT-1))
p2 = Point(random.randint(0, WIDTH-1), random.randint(0, HEIGHT-1))
cf = CurveFunction(p1, p2)
color = convert_color(get_random_color())
tt = 0
points = []
while tt <= 1:
t += dt
tt += 0.025
p = cf(tt).intp
points.append(p)
frame = blup.animation.AnimationFrame(dim, args.delay)
for p in points:
try:
frame.setPixel(p.x, p.y, color)
except ValueError:
pass
anim.addFrame(frame)
blup.writebml.writeBml(anim, args.output_file)

114
generators/discs.py

@ -0,0 +1,114 @@
#!/usr/bin/env python3
import sys
import random
import math
import argparse
import collections
import blup.frame
import blup.output
import blup.animation
import blup.writebml
import colorsys
WIDTH = 22
HEIGHT = 16
DEPTH = 256
class Point(collections.namedtuple('Point', ['x', 'y'])):
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Point(self.x - other.x, self.y - other.y)
def __mul__(self, other):
if isinstance(other, Point):
raise Exception('not implemented')
else:
return Point(self.x * other, self.y * other)
def __truediv__(self, other):
if isinstance(other, Point):
raise ValueError()
else:
return self * (1/other)
@property
def len(self):
return math.sqrt(self.x**2 + self.y**2)
@property
def n(self):
n = Point(self.y, -self.x)
return n / n.len
@property
def intp(self):
return Point(int(round(self.x)), int(round(self.y)))
def convert_color(c):
return list(map(lambda x: int(round(x*(DEPTH-1))), c))
def get_random_color():
return colorsys.hsv_to_rgb(random.random(), 1, 1)
class Disc:
def __init__(self, worldsize, hue):
self.worldsize = worldsize
self.pos = Point(random.randint(0, worldsize.x - 1),
random.randint(0, worldsize.y - 1))
self.hue = hue
self.r = 1
self.speed = min(worldsize.x, worldsize.y) / 2
self.done = False
def update(self, t, dt):
self.r += self.speed * dt
if self.r > max(self.worldsize.x, self.worldsize.y):
self.done = True
def draw(self, frame):
color = colorsys.hsv_to_rgb(self.hue, 1, 1)
for x in range(self.worldsize.x):
for y in range(self.worldsize.y):
p = Point(x, y)
if (p - self.pos).len < self.r:
frame.setPixel(p.x, p.y, convert_color(color))
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Generate animations')
parser.add_argument('-d', '--delay', type=int, default=50)
parser.add_argument('-t', '--time', type=int, default=15)
parser.add_argument('output_file')
args = parser.parse_args()
dim = blup.frame.FrameDimension(WIDTH, HEIGHT, DEPTH, 3)
anim = blup.animation.Animation(dim)
anim.tags['description'] = ' '.join(sys.argv)
discs = []
t = 0
dt = args.delay / 1000
while t < args.time:
t += dt
frame = blup.animation.AnimationFrame(dim, args.delay)
if len(discs) == 0 or discs[-1].r > min(WIDTH, HEIGHT) / 2:
hue = random.random()
if len(discs) > 0:
l = lambda d: hue - d.hue if d.hue < hue else d.hue - hue
while not 0.2 < sorted(map(l, discs))[0] < 0.8:
hue = random.random()
discs.append(Disc(Point(WIDTH, HEIGHT), hue))
for d in discs:
d.update(t, dt)
d.draw(frame)
if len(discs) > 2 and discs[0].done and discs[1].done:
del discs[0]
anim.addFrame(frame)
blup.writebml.writeBml(anim, args.output_file)

141
generators/dlines.py

@ -0,0 +1,141 @@
#!/usr/bin/env python3
import sys
import random
import math
import argparse
import collections
import blup.frame
import blup.output
import blup.animation
import blup.writebml
import colorsys
import functools
WIDTH = 22
HEIGHT = 16
DEPTH = 256
class Point(collections.namedtuple('Point', ['x', 'y'])):
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Point(self.x - other.x, self.y - other.y)
@property
def len(self):
return math.sqrt(self.x**2 + self.y**2)
class PointLenOrder:
def __init__(self, value):
self.value = value
def __eq__(self, other):
return other.value.len == self.value.len
def __lt__(self, other):
return other.value.len < self.value.len
DIRECTIONS = [ Point(x, y) for x in [-1, 1] for y in [-1, 1] ]
def convert_color(c):
return list(map(lambda x: int(round(x*(DEPTH-1))), c))
def get_random_color():
return colorsys.hsv_to_rgb(random.random(), 0.5 + random.random() / 2, 1)
class Line:
def __init__(self, worldsize):
self.worldsize = worldsize
self.line = [Point(random.randint(0, worldsize.x),
random.randint(0, worldsize.y))]
self.slen = 1
self.dir = random.choice(DIRECTIONS)
self.color = get_random_color()
diaglen = int(math.sqrt(worldsize.x**2 + worldsize.y**2))
self.maxlen = random.randint(diaglen, diaglen * 3)
self.fadeout_time = 2 + random.random() * 2
self.fadeout_remaining = None
self.finished = False
def check_bounds(self, p):
return p.x < self.worldsize.x and p.x >= 0 \
and p.y < self.worldsize.y and p.y >= 0
def update(self, t, dt):
if len(self.line) < self.maxlen:
if self.slen >= 3 and random.random() < 0.4:
dirs = list(DIRECTIONS)
dirs.remove(self.line[-1] - self.line[-2])
if self.check_bounds(self.line[-1]):
newp = None
while newp is None or newp == self.line[-2]:
self.dir = random.choice(dirs)
newp = self.line[-1] + self.dir
else:
dirs.sort(key=lambda d: ((self.line[-1]+d) - Point(0, 0)).len)
self.dir = dirs[0]
self.slen = 1
else:
self.slen += 1
self.line.append(self.line[-1] + self.dir)
else:
if self.fadeout_remaining is None:
self.fadeout_remaining = self.fadeout_time
elif self.fadeout_remaining <= 0:
self.finished = True
else:
self.fadeout_remaining -= dt
def draw(self, frame):
if self.fadeout_remaining is not None:
if self.fadeout_remaining > 0:
h, s, v = colorsys.rgb_to_hsv(*self.color)
v = v * (self.fadeout_remaining / self.fadeout_time)
color = colorsys.hsv_to_rgb(h, s, v)
else:
color = (0, 0, 0)
else:
color = self.color
for p in self.line:
if self.check_bounds(p):
frame.setPixel(p.x, p.y, convert_color(color))
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Generate animations')
parser.add_argument('-d', '--delay', type=int, default=50)
parser.add_argument('-t', '--time', type=int, default=15)
parser.add_argument('output_file')
args = parser.parse_args()
dim = blup.frame.FrameDimension(WIDTH, HEIGHT, DEPTH, 3)
anim = blup.animation.Animation(dim)
anim.tags['description'] = ' '.join(sys.argv)
lines = [Line(Point(WIDTH, HEIGHT))]
t = 0
dt = args.delay / 1000
while t < args.time:
t += dt
frame = blup.animation.AnimationFrame(dim, args.delay)
for l in lines:
l.update(t, dt)
l.draw(frame)
if lines[-1].fadeout_remaining is not None:
lines.append(Line(Point(WIDTH, HEIGHT)))
for l in lines:
if l.finished:
lines.remove(l)
anim.addFrame(frame)
blup.writebml.writeBml(anim, args.output_file)

150
generators/faces.py

@ -0,0 +1,150 @@
#!/usr/bin/env python3
import sys
import random
import argparse
import collections
import blup.frame
import blup.output
import blup.animation
import blup.writebml
import colorsys
WIDTH = 22
HEIGHT = 16
DEPTH = 256
SMILEYSIZE = 5
Point = collections.namedtuple('Point', ['x', 'y'])
def convert_color(c):
return list(map(lambda x: int(round(x*(DEPTH-1))), c))
def expand(somepoints, allpoints):
todo = set(somepoints)
ret = set(somepoints)
while len(todo) > 0:
p = todo.pop()
ret.add(p)
adj = { Point(p.x + x, p.y + y) for x in [-1,0,1] for y in [-1,0,1] \
if abs(x) + abs(y) == 1 and p.x + x in range(0, SMILEYSIZE) \
and p.y + y in range(0, SMILEYSIZE) }
adj = (allpoints - ret).intersection(adj)
todo = todo.union(adj)
return ret
def gen_face_shape():
while True:
i = random.randint(0, 2**(SMILEYSIZE * (SMILEYSIZE//2 + 1)))
lpoints = { Point(x, y) for x in range(0, SMILEYSIZE // 2) \
for y in range(SMILEYSIZE) \
if i & 2**(x*SMILEYSIZE + y) > 0 }
mpoints = { Point(SMILEYSIZE//2, y) for y in range(SMILEYSIZE) \
if i & 2**((SMILEYSIZE//2) * SMILEYSIZE + y) > 0 }
rpoints = { Point(SMILEYSIZE - x - 1, y) \
for x in range(0, SMILEYSIZE // 2) \
for y in range(SMILEYSIZE) \
if i & 2**(x*SMILEYSIZE + y) > 0 }
points = set.union(lpoints, mpoints, rpoints)
eyes = { p for p in points if p.y < SMILEYSIZE // 2 and \
p.x != SMILEYSIZE // 2 }
nose = { p for p in points if p.y >= SMILEYSIZE // 3 and \
p.y < SMILEYSIZE // 2 + 1 and p.x == SMILEYSIZE//2 }
mouth = { p for p in points if p.y > SMILEYSIZE // 2 }
nose = expand(nose, points)
mouth = expand(mouth, points)
face = True
if len(eyes.intersection(nose)) > 0:
face = False
if len(eyes.intersection(mouth)) > 0:
face = False
if len(nose.intersection(mouth)) > 0:
face = False
if len(points - eyes - nose - mouth) > 0:
face = False
if len(eyes) == 0:
face = False
if len(mouth) == 0:
face = False
if len(nose) > 0:
nosey = set(map(lambda p: p.y, nose))
if min(nosey) < SMILEYSIZE//3:
face = False
if face:
return points
class Face:
def __init__(self, pos):
self.pos = pos
self.shape = gen_face_shape()
self.lifetime = 0.7
self.fadetime = 0.1 + random.random() / 10
self.age = 0
self.done = False
self.hue = random.random()
def update(self, dt):
self.age += dt
if self.age > self.lifetime:
self.done = True
def draw(self, frame):
if self.done:
return
if self.age < self.fadetime:
v = self.age / self.fadetime
elif self.age > self.lifetime - self.fadetime:
v = (self.lifetime-self.age) / self.fadetime
else:
v = 1
for p in self.shape:
frame.setPixel(
p.x + self.pos.x,
p.y + self.pos.y,
convert_color(colorsys.hsv_to_rgb(self.hue, 1, v))
)
def get_face_pos():
x = random.randint(0, WIDTH - SMILEYSIZE)
y = random.randint(0, HEIGHT - SMILEYSIZE)
return Point(x, y)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Generate Face animations')
parser.add_argument('-d', '--delay', type=int, default=50)
parser.add_argument('-t', '--time', type=int, default=15)
parser.add_argument('output_file')
args = parser.parse_args()
dim = blup.frame.FrameDimension(WIDTH, HEIGHT, DEPTH, 3)
anim = blup.animation.Animation(dim)
anim.tags['description'] = ' '.join(sys.argv)
f = Face(get_face_pos())
t = 0
dt = args.delay / 1000
while f is not None:
t += dt
frame = blup.animation.AnimationFrame(dim, args.delay)
f.update(dt)
f.draw(frame)
if f.done:
if t < args.time:
f = Face(get_face_pos())
else:
f = None
anim.addFrame(frame)
blup.writebml.writeBml(anim, args.output_file)

2
generators/fire.py

@ -1,4 +1,4 @@
#!/usr/bin/python3 #!/usr/bin/env python3
import colorsys import colorsys
import random import random

138
generators/fireworks.py

@ -0,0 +1,138 @@
#!/usr/bin/env python3
import sys
import argparse
import time
import math
import colorsys
import blup.frame
import blup.output
import blup.animation
import blup.writebml
import numpy as np
import random
WIDTH = 22
HEIGHT = 16
DEPTH = 256
G = np.array([0, 9.81])
class Fireball:
def __init__(self, startpos, startspeed, startcolor, lifetime, t0):
self.startpos, self.startspeed, self.startcolor, self.lifetime = \
startpos, startspeed, startcolor, lifetime
self.t0 = t0
self.pos = startpos
self.color = startcolor
self.dead = False
def update_pos(self, t):
t = t - self.t0
if t > self.lifetime:
self.dead = True
return
self.pos = 0.5 * G * t**2 + self.startspeed * t + self.startpos
self.color = self.startcolor * (1 - t/self.lifetime)
if random.randint(1, 10) > 8:
self.color = np.array([0, 0, 0])
def draw(self, frame):
if not self.dead:
frame.setPixel(int(round(self.pos[0])), int(round(self.pos[1])),
tuple(map(lambda x: int(round(x)), self.color)))
class Rocket:
def __init__(self, startpos, startspeed, explode_height, t0, color):
self.startpos, self.startspeed, self.explode_height, self.t0 = \
startpos, startspeed, explode_height, t0
self.color = color
self.pos = None
self.exploded = False
def update(self, t):
t = t - self.t0
if t < 0 or self.exploded:
return
self.pos = 0.5 * G * t**2 + self.startspeed * t + self.startpos
if self.pos[1] <= self.explode_height:
self.exploded = True
self.color = np.array([0, 0, 0])
def draw(self, frame):
if self.pos is None or self.exploded:
return
frame.setPixel(int(round(self.pos[0])), int(round(self.pos[1])),
tuple(map(lambda x: int(round(x)), self.color)))
def getRandomColor(maxval):
return list(map(lambda x: int(round(x*maxval)), colorsys.hsv_to_rgb(random.random(), 1, 1)))
def gen_rockets(num, now=0):
for i in range(num):
yield Rocket(
np.array([random.randint(0, dim.width-1), dim.height-1]),
np.array([random.randint(-8, 8), -20 + random.randint(-2, 2)]),
4 + random.randint(-2, 2),
now + random.randint(0, 40) / 10,
np.array([dim.depth-1] * 3)
)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Generate plasma animations')
parser.add_argument('-d', '--delay', type=int, default=50)
parser.add_argument('-t', '--time', type=int, default=0)
parser.add_argument('-n', '--num-rockets', type=int, default=3)
parser.add_argument('output_file')
args = parser.parse_args()
dim = blup.frame.FrameDimension(WIDTH, HEIGHT, DEPTH, 3)
anim = blup.animation.Animation(dim)
anim.tags['description'] = ' '.join(sys.argv)
rockets = list(gen_rockets(args.num_rockets))
fbs = []
t = 0
while True:
frame = blup.animation.AnimationFrame(dim, args.delay)
for fb in fbs:
fb.update_pos(t)
try:
fb.draw(frame)
except ValueError:
continue
fbs = list(filter(lambda f: not f.dead, fbs))
for r in rockets:
r.update(t)
try:
r.draw(frame)
except ValueError:
continue
if r.exploded:
color = np.array(getRandomColor(dim.depth-1))
for i in range(random.randint(15, 25)):
v = np.array([
random.choice([-1, 1]) * random.randint(0, 25),
random.choice([-1, 1]) * random.randint(0, 25),
])
fbs.append(Fireball(r.pos, v, color, 1.5 + random.randint(-1, 1), t))
rockets = list(filter(lambda r: not r.exploded, rockets))
anim.addFrame(frame)
if len(rockets) == 0 and len(fbs) == 0:
if t < args.time:
frame = blup.animation.AnimationFrame(dim, 700)
anim.addFrame(frame)
rockets = list(gen_rockets(args.num_rockets, t))
else:
break
t += args.delay / 1000
blup.writebml.writeBml(anim, args.output_file)

147
generators/gameOfLife.py

@ -1,10 +1,12 @@
import sys #!/usr/bin/env python3
import ast import ast
import time as t
import random as r import random as r
import blup.animation from blup import frame
import blup.frame from blup import animation
import blup.writebml import blup.writebml
import argparse
import math
r.seed() r.seed()
@ -13,84 +15,95 @@ HEIGHT = 16
COLOR_DEPTH = 256 COLOR_DEPTH = 256
COLORS = 3 COLORS = 3
DELAY = 300
DEAD = 0 DEAD = 0
DEADCOLOR = (0,0,0) DEAD_COLOR = (0, 0, 0)
ALIVE = 1 ALIVE = 1
ALIVECOLOR = (0,0,255) ALIVE_COLOR = (r.randint(0, 255), r.randint(0, 255), r.randint(0, 255))
dimension = blup.frame.FrameDimension(WIDTH, HEIGHT, COLOR_DEPTH, COLORS) def generate_rand_game_board():
animation = blup.animation.Animation(dimension) board = []
for rows in range(HEIGHT):
x_coordinates = []
for cols in range(WIDTH):
x_coordinates.append(r.randint(0, 1))
board.append(x_coordinates)
return board
def generateRandomGameBoard():
gameBoard = []
for i in range(HEIGHT):
xCoordinates = []
for j in range(WIDTH):
xCoordinates.append(r.randint(0, 1))
gameBoard.append(xCoordinates)
return gameBoard
def get_neighbors(x, y): def get_neighbors(x, y):
for rowNumber in range(y-1, y+2): for rowNumber in range(y-1, y+2):
if 0 <= rowNumber < HEIGHT: if 0 <= rowNumber < HEIGHT:
for colNumber in range(x-1, x+2): for colNumber in range(x-1, x+2):
if 0 <= colNumber < WIDTH: if 0 <= colNumber < WIDTH:
if not (y==rowNumber and x==colNumber): if not (y == rowNumber and x == colNumber):
yield gameBoard[rowNumber][colNumber] yield game_board[rowNumber][colNumber]
if __name__ == "__main__":
argument_parser = argparse.ArgumentParser(description="Game of Life bml generator")
argument_parser.add_argument('-o', dest="output_path", type=str, help="output file (mandatory)")
argument_parser.add_argument('-w', dest="world", type=list, default=None, help="pre-constructed world (optional)")
argument_parser.add_argument('-t', dest="secs", type=int, default=90, help="time in seconds (optional)")
argument_parser.add_argument('-d', dest="delay", type=int, default=300, help="delay between frames (optional)")
args = argument_parser.parse_args()
if len(sys.argv) == 1: frames = int(math.floor(args.secs * 1000 / args.delay))
gameBoard = generateRandomGameBoard()
gameBoardIsSanitized = True
elif len(sys.argv) != 2:
print("check args. found "+str(len(sys.argv)))
exit()
if not gameBoardIsSanitized: dimension = frame.FrameDimension(WIDTH, HEIGHT, COLOR_DEPTH, COLORS)
gameBoard = ast.literal_eval(sys.argv[1]) game_of_life_animation = animation.Animation(dimension)
if len(gameBoard) != HEIGHT: if args.output_path is None:
print("len(gameBoard) != "+str(HEIGHT)+". counted "+str(len(gameBoard))) argument_parser.print_help()
exit() exit()
for i in gameBoard: if args.world is None:
if len(i) != WIDTH: game_board = generate_rand_game_board()
print("len(rows) != "+str(WIDTH)+". counted "+str(len(i))) else:
game_board = ast.literal_eval(args.world)
if len(game_board) != HEIGHT:
print("len(gameBoard) != " + str(HEIGHT) + ". counted " + str(len(game_board)))
exit() exit()
for frame in range(500): for i in game_board:
# construct frame if len(i) != WIDTH:
newframe = blup.animation.AnimationFrame(dimension, DELAY) print("len(rows) != "+str(WIDTH)+". counted "+str(len(i)))
for rowIndex in range(HEIGHT): exit()
for colIndex in range(WIDTH):
cellStatus = gameBoard[rowIndex][colIndex] for frame in range(frames):
if cellStatus == ALIVE: # construct frame
newframe.setPixel(colIndex, rowIndex, ALIVECOLOR) new_frame = animation.AnimationFrame(dimension, args.delay)
else: for row_index in range(HEIGHT):
newframe.setPixel(colIndex, rowIndex, DEADCOLOR) for col_index in range(WIDTH):
animation.addFrame(newframe) cell_status = game_board[row_index][col_index]
if cell_status == ALIVE:
# calculate next step new_frame.setPixel(col_index, row_index, ALIVE_COLOR)
gameBoardNextStep = [] else:
lastGameBoard = gameBoard new_frame.setPixel(col_index, row_index, DEAD_COLOR)
for rowIndex in range(HEIGHT): game_of_life_animation.addFrame(new_frame)
newRow = []
for colIndex in range(WIDTH): # calculate next step
neighborList = list(get_neighbors(colIndex, rowIndex)) next_game_board_step = []
aliveNeighbors = neighborList.count(1) previous_game_board_step = game_board
if aliveNeighbors < 2: for row_index in range(HEIGHT):
newRow.append(DEAD) new_row = []
elif aliveNeighbors == 2: for col_index in range(WIDTH):
newRow.append(gameBoard[rowIndex][colIndex]) neighbor_list = list(get_neighbors(col_index, row_index))
elif aliveNeighbors == 3: alive_neighbors = neighbor_list.count(1)
newRow.append(ALIVE) if alive_neighbors < 2:
elif aliveNeighbors >= 4: new_row.append(DEAD)
newRow.append(DEAD) elif alive_neighbors == 2:
gameBoardNextStep.append(newRow) new_row.append(game_board[row_index][col_index])
gameBoard = gameBoardNextStep elif alive_neighbors == 3:
if(gameBoard == lastGameBoard): new_row.append(ALIVE)
for i in range(20): elif alive_neighbors >= 4:
animation.addFrame(newframe) new_row.append(DEAD)
break next_game_board_step.append(new_row)
blup.writebml.writeBml(animation, '/home/pi/animations/gameOfLife.bml') game_board = next_game_board_step
if game_board == previous_game_board_step:
for i in range(20):
game_of_life_animation.addFrame(new_frame)
break
blup.writebml.writeBml(game_of_life_animation, args.output_path)

36
generators/gif2bml.py

@ -0,0 +1,36 @@
#!/usr/bin/env python3
import argparse
import blup.frame
import blup.output
import blup.animation
import blup.writebml
import PIL.Image
DEPTH = 256
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='Convert GIF animations to BML.'
)
parser.add_argument('input_file')
parser.add_argument('output_file')
args = parser.parse_args()
gif = PIL.Image.open(args.input_file)
dim = blup.frame.FrameDimension(gif.width, gif.height, DEPTH, 3)
anim = blup.animation.Animation(dim)
for i in range(gif.n_frames):
gif.seek(i)
gf = gif.convert('RGB')
f = blup.animation.AnimationFrame(dim, gf.info.get('delay', 100))
for x in range(dim.width):
for y in range(dim.height):
f.setPixel(x, y, gf.getpixel((x, y)))
anim.addFrame(f)
blup.writebml.writeBml(anim, args.output_file)

121
generators/lines.py

@ -0,0 +1,121 @@
#!/usr/bin/env python3
import sys
import random
import math
import argparse
import collections
import blup.frame
import blup.output
import blup.animation
import blup.writebml
import colorsys
WIDTH = 22
HEIGHT = 16
DEPTH = 256
class Point(collections.namedtuple('Point', ['x', 'y'])):
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Point(self.x - other.x, self.y - other.y)
def __mul__(self, other):
if isinstance(other, Point):
raise Exception('not implemented')
else:
return Point(self.x * other, self.y * other)
def __truediv__(self, other):
if isinstance(other, Point):
raise ValueError()
else:
return self * (1/other)
@property
def len(self):
return math.sqrt(self.x**2 + self.y**2)
@property
def n(self):
n = Point(self.y, -self.x)
return n / n.len
@property
def intp(self):
return Point(int(round(self.x)), int(round(self.y)))
def convert_color(c):
return list(map(lambda x: int(round(x*(DEPTH-1))), c))
def get_random_color():
return colorsys.hsv_to_rgb(random.random(), 0.5 + random.random() / 2, 1)
class Line:
def __init__(self, size):
self.size = size
r = random.choice([0, 1])
s = random.choice([-1, 1])
if r == 0:
self.pos = Point(size.x if s == -1 else 0,
random.randint(0, size.y - 1))
else:
self.pos = Point(random.randint(0, size.x - 1),
size.y if s == -1 else 0)
self.dir = Point(s if r == 0 else 0, s if r == 1 else 0)
m = max(size.x, size.y)
self.length = random.randint(int(0.4 * m), int(1.1 * m))
self.color = get_random_color()
self.speed = 10
self.done = False
def update(self, t, dt):
self.pos += self.dir * (dt * self.speed)
end = self.pos - self.dir * self.length
if self.dir.x > 0 and end.x > self.size.x \
or self.dir.y > 0 and end.y > self.size.y \
or self.dir.x < 0 and end.x < 0 \
or self.dir.y < 0 and end.y < 0:
self.done = True
def draw(self, frame):
if self.done:
return
for i in range(self.length):
p = self.pos.intp - self.dir * i
try:
frame.setPixel(p.x, p.y, convert_color(self.color))
except ValueError:
pass
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Generate animations')
parser.add_argument('-d', '--delay', type=int, default=50)
parser.add_argument('-t', '--time', type=int, default=15)
parser.add_argument('output_file')
args = parser.parse_args()
dim = blup.frame.FrameDimension(WIDTH, HEIGHT, DEPTH, 3)
anim = blup.animation.Animation(dim)
anim.tags['description'] = ' '.join(sys.argv)
lines = []
t = 0
dt = args.delay / 1000
while t < args.time or len(lines) > 0:
t += dt
frame = blup.animation.AnimationFrame(dim, args.delay)
if len(lines) < 5 and t < args.time:
lines.append(Line(Point(WIDTH, HEIGHT)))
for l in lines:
l.update(t, dt)
l.draw(frame)
lines = list(filter(lambda l: not l.done, lines))
anim.addFrame(frame)
blup.writebml.writeBml(anim, args.output_file)

222
generators/movingshapes.py

@ -0,0 +1,222 @@
#!/usr/bin/env python3
import sys
import random
import argparse
import collections
import blup.frame
import blup.output
import blup.animation
import blup.writebml
import colorsys
import math
WIDTH = 22
HEIGHT = 16
DEPTH = 256
_ALLSHAPES = [
[[0, 1, 0, 1, 0],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[0, 1, 1, 1, 0],
[0, 0, 1, 0, 0]],
[[1, 0, 0, 0, 1],
[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0],
[1, 0, 0, 0, 1],
[0, 1, 1, 1, 0]],
[[1, 0, 1, 0, 1],
[0, 1, 1, 1, 0],
[0, 1, 1, 1, 0],
[0, 1, 0, 1, 0],
[1, 1, 0, 1, 1]],
]
class Point(collections.namedtuple('Point', ['x', 'y'])):
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Point(self.x - other.x, self.y - other.y)
ALLSHAPES = []
for s in _ALLSHAPES:
ALLSHAPES.append(set())
y = 0
for l in s:
x = 0
for i in l:
if i == 1:
ALLSHAPES[-1].add(Point(x, y))
x += 1
y += 1
def convert_color(c):
return list(map(lambda x: int(round(x*(DEPTH-1))), c))
def get_random_color():
return colorsys.hsv_to_rgb(random.random(), 1, 1)
class Shape:
def __init__(self, shape, pos, color):
self.shape, self.pos, self.color = shape, pos, color
self.period = 2
self.fadetime = 0.5 + random.random() / 5
self.t = random.random() * self.period
self.t = 0
self.oldv = 1
self.v = 1
self.newshape = None
def update(self, t, dt):
self.t += dt
if self.t > self.period:
t = self.t - self.period
if t >= self.fadetime:
self.v = 1
self.t = random.random() * self.period
else:
self.v = math.cos(t * (math.pi / self.fadetime)) ** 2
if (self.newshape is not None \
and (self.oldv < self.v or self.v == 0)):
self.shape = self.newshape
self.color = self.newcolor
self.newshape = None
self.oldv = self.v
def change_shape_and_color(self, newshape, newcolor):
self.newshape = newshape
self.newcolor = newcolor
self.t = self.period
def draw(self, frame, origin=Point(0, 0)):
h, s, v = colorsys.rgb_to_hsv(*self.color)
for p in self.shape:
try:
pos = self.pos + p - origin
c = colorsys.hsv_to_rgb(h, s, v * self.v)
frame.setPixel(pos.x, pos.y, convert_color(c))
except ValueError:
pass
class World:
def __init__(self, size, allshapes, gap=2):
self.size, self.allshapes, self.gap = size, allshapes, gap
self.shapes = {}
self.pos = Point(0, 0)
self.cycletime = 60
self.curshape = random.choice(allshapes)
self.curcolor = get_random_color()
self.shapesize = Point(0, 0)
for s in allshapes:
assert min(map(lambda p: p.x, s)) >= 0
assert min(map(lambda p: p.y, s)) >= 0
shapesize = Point(
max(map(lambda p: p.x, s)) + 1,
max(map(lambda p: p.y, s)) + 1,
)
if shapesize.x > self.shapesize.x:
self.shapesize = Point(shapesize.x, self.shapesize.y)
if shapesize.y > self.shapesize.y:
self.shapesize = Point(self.shapesize.x, shapesize.y)
print (self.shapesize)
@property
def shape_space(self):
ret = set()
for x in range(self.size.x):
for y in range(self.size.y):
p = self.pos + Point(x, y)
if (p.x % (self.shapesize.x + self.gap) >= self.gap
and p.y % (self.shapesize.y + self.gap) >= self.gap):
ret.add(p)
return ret
def get_shape_coverage(self, shape):
return { shape.pos + Point(x, y) for x in range(self.shapesize.x) \
for y in range(self.shapesize.y) }
def update(self, t, dt):
self.pos = Point(
int(math.sin((t * math.pi/self.cycletime)*4) * self.size.x),
int(math.cos((t * math.pi/self.cycletime)*5) * self.size.y)
)
space = self.shape_space
for s in self.shapes.values():
space -= self.get_shape_coverage(s)
while len(space) > 0: # and len(self.shapes) < 1:
print('...')
p = space.pop()
newpos = Point(
(p.x // (self.shapesize.x + self.gap)) \
* (self.shapesize.x + self.gap) + self.gap,
(p.y // (self.shapesize.y + self.gap)) \
* (self.shapesize.y + self.gap) + self.gap
)
print(p, newpos)
# TODO improve shape and color selection
newshape = Shape(self.curshape, newpos, self.curcolor)
assert newpos in self.get_shape_coverage(newshape)
print('covered', self.get_shape_coverage(newshape))
print('space before', len(space))
space -= self.get_shape_coverage(newshape)
print('space after ', len(space))
assert newpos not in self.shapes
self.shapes[newpos] = newshape
print('new', len(self.shapes))
#print(space)
newshape = None
newcolor = None
if round(t*1000) % 3000 == 0:
newshape = random.choice(self.allshapes)
self.curshape = newshape
newcolor = get_random_color()
self.curcolor = newcolor
for s in self.shapes.values():
if newshape is not None:
s.change_shape_and_color(newshape, newcolor)
s.update(t, dt)
def draw(self, frame):
for s in self.shapes.values():
s.draw(frame, self.pos)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Generate animations')
parser.add_argument('-d', '--delay', type=int, default=50)
parser.add_argument('-t', '--time', type=int, default=15)
parser.add_argument('output_file')
args = parser.parse_args()
dim = blup.frame.FrameDimension(WIDTH, HEIGHT, DEPTH, 3)
anim = blup.animation.Animation(dim)
anim.tags['description'] = ' '.join(sys.argv)
w = World(Point(WIDTH, HEIGHT), ALLSHAPES)
t = 0
dt = args.delay / 1000
while t < args.time:
t += dt
frame = blup.animation.AnimationFrame(dim, args.delay)
w.update(t, dt)
w.draw(frame)
anim.addFrame(frame)
blup.writebml.writeBml(anim, args.output_file)

122
generators/orbs.py

@ -0,0 +1,122 @@
#!/usr/bin/env python3
import sys
import random
import numpy as np
import argparse
import blup.frame
import blup.output
import blup.animation
import blup.writebml
import colorsys
WIDTH = 22
HEIGHT = 16
DEPTH = 256
def fill_trace(points):
for i in range(len(points)):
yield points[i]
if i == len(points)-1:
break
v = points[i+1] - points[i]
l = int(np.linalg.norm(v))
for j in range(l):
yield points[i] + v * (j+1)/(l+1)
def convert_color(c):
return list(map(lambda x: int(round(x*(DEPTH-1))), c))
def get_random_color():
return colorsys.hsv_to_rgb(random.random(), 1, 1)
class Orb:
def __init__(self, pos, speed, tracelen, color, tracecolor, boxsize):
self.pos = pos
self.speed = speed
self.tracelen = tracelen
self.color = color
self.tracecolor = tracecolor
self.boxsize = boxsize
self.trace = []
def update(self, dt):
newpos = self.pos + self.speed * dt
#newpos = self.pos
for dim in [0, 1]:
if newpos[dim] >= self.boxsize[dim] or newpos[dim] <= 0:
newpos[dim] = 0 if newpos[dim]<0 else self.boxsize[dim]-1
self.speed[dim] *= -1
self.speed += np.array([random.randint(-5, 6), random.randint(-5, 6)])
self.trace.insert(0, self.pos)
self.trace = self.trace[:self.tracelen]
self.pos = newpos
def draw(self, frame):
for i, p in enumerate(fill_trace([self.pos] + self.trace)):
f = (self.tracelen - i) / self.tracelen
try:
c = colorsys.rgb_to_hsv(*self.tracecolor)
s = 0.1 if random.random()>0.9 else c[1]
c = colorsys.hsv_to_rgb(c[0], s, c[2]*f)
frame.setPixel(int(p[0]), int(p[1]), convert_color(c))
except ValueError:
pass
offs = [ np.array([x,y]) for x in [-1,0,1] for y in [-1,0,1] ]
points = []
for o in offs:
p = self.pos.astype('int') + o
if random.random() > 0.2 and not (p == np.array([0,0])).all():
points.append(p)
for p in points:
try:
c = colorsys.rgb_to_hsv(*self.color)
c = colorsys.hsv_to_rgb(c[0], c[1], c[2]*0.2)
frame.setPixel(p[0], p[1], convert_color(c))
except ValueError:
pass
frame.setPixel(int(self.pos[0]), int(self.pos[1]), convert_color(self.color))
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Generate plasma animations')
parser.add_argument('-d', '--delay', type=int, default=50)
parser.add_argument('-t', '--time', type=int, default=15)
parser.add_argument('-n', '--num-orbs', type=int, default=2)
parser.add_argument('output_file')
args = parser.parse_args()
dim = blup.frame.FrameDimension(WIDTH, HEIGHT, DEPTH, 3)
anim = blup.animation.Animation(dim)
anim.tags['description'] = ' '.join(sys.argv)
orbs = []
for i in range(args.num_orbs):
orbs.append(Orb(
np.array([random.randint(0, WIDTH-1),random.randint(0, HEIGHT-1)]),
np.array([8,10]),
20,
get_random_color(),
get_random_color(),
np.array([WIDTH, HEIGHT])
))
t = 0
while t < args.time:
t += args.delay / 1000
frame = blup.animation.AnimationFrame(dim, args.delay)
for orb in orbs:
orb.update(args.delay / 1000)
orb.draw(frame)
anim.addFrame(frame)
blup.writebml.writeBml(anim, args.output_file)

2
generators/scroll.py

@ -1,4 +1,4 @@
#!/usr/bin/python3 #!/usr/bin/env python3
import sys import sys
import colorsys import colorsys

232
generators/thimblerig.py

@ -0,0 +1,232 @@
#!/usr/bin/env python3
import sys
import random
import math
import argparse
import collections
import blup.frame
import blup.output
import blup.animation
import blup.writebml
import colorsys
import functools
WIDTH = 22
HEIGHT = 16
DEPTH = 256
class Point(collections.namedtuple('Point', ['x', 'y'])):
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Point(self.x - other.x, self.y - other.y)
def __mul__(self, other):
if isinstance(other, Point):
raise Exception('not implemented')
else:
return Point(self.x * other, self.y * other)
def __truediv__(self, other):
if isinstance(other, Point):
raise ValueError()
else:
return self * (1/other)
@property
def len(self):
return math.sqrt(self.x**2 + self.y**2)
@property
def n(self):
n = Point(self.y, -self.x)
return n / n.len
@property
def intp(self):
return Point(int(round(self.x)), int(round(self.y)))
def convert_color(c):
return list(map(lambda x: int(round(x*(DEPTH-1))), c))
def get_random_color():
return colorsys.hsv_to_rgb(random.random(), 0.5 + random.random() / 2, 1)
class CurveFunction():
def __init__(self, p1, p2):
self.p1, self.p2 = p1, p2
d = p2 - p1
self.pp = p1 + d/2 + d.n * (d.len * 0.8)
def __call__(self, t):
ret = (self.p1 - self.pp*2 + self.p2) * t**2
ret += (self.p1 * -2 + self.pp * 2) * t
ret += self.p1
return ret
class Box():
def __init__(self, pos, size, hue, swaptime, content=None,
contentcolor=None):
self.pos, self.size, self.hue, self.content, self.contentcolor = \
pos, size, hue, content, contentcolor
self.swaptime = swaptime
self.covered = False
self.swapstart = None
self.newpos = None
self.cf = None
self.colorcycletime = 1
def swap(self, other):
self.newpos = other.pos
other.newpos = self.pos
def update(self, t, dt):
self.hue += 0.05 # dt / self.colorcycletime
if self.hue > 1:
self.hue -= 1
if self.newpos is not None:
if self.cf is None:
self.cf = CurveFunction(self.pos, self.newpos)
self.swapstart = t
elif t - self.swapstart >= self.swaptime:
self.pos = self.newpos
self.newpos = None
self.cf = None
self.swapstart = None
else:
self.pos = (self.cf((t - self.swapstart) / self.swaptime)).intp
def draw(self, frame):
if self.covered:
for x in range(self.size):
for y in range(self.size):
if x == 0 or x == self.size - 1 \
or y == 0 or y == self.size -1:
color = (0.6, 0.6, 0.6)
else:
color = colorsys.hsv_to_rgb(self.hue, 1, 1)
try:
frame.setPixel(self.pos.x + x, self.pos.y + y,
convert_color(color))
except ValueError:
pass
else:
if self.content is not None:
for p in self.content:
try:
frame.setPixel(self.pos.x + p.x + 1,
self.pos.y + p.y + 1,
convert_color(self.contentcolor))
except ValueError:
pass
class Game():
def __init__(self, size, boxsize, gap, content, contentcolor, swaptime):
self.swaptime = swaptime
self.boxes = []
boxbase = Point((size.x - (2*boxsize + gap)) / 2,
(size.y - (2*boxsize + gap)) / 2)
self.n_boxes = 4
for i in range(self.n_boxes):
boxpos = boxbase + Point(
(i % (self.n_boxes/2)) * (boxsize + gap),
(i // (self.n_boxes/2)) * (boxsize + gap)
)
self.boxes.append(Box(
boxpos.intp,
boxsize,
i * (1/self.n_boxes),
swaptime
))
winner = random.randint(0, self.n_boxes - 1)
self.boxes[winner].content = content
self.boxes[winner].contentcolor = contentcolor
self.statetimes = [2, 1, 5, 2, 1, 2]
self.statestarts = [0]
for t in self.statetimes[:-1]:
self.statestarts.append(self.statestarts[-1] + t)
self.done = False
def update(self, t, dt):
state = 0
for i, st in enumerate(self.statestarts):
if st < t:
state = i
else:
break
state_t = t - self.statestarts[state]
if state == 1 or state == 4:
for i, b in enumerate(self.boxes):
if state_t >= i * (self.statetimes[state] / self.n_boxes):
b.covered = (state == 1)
elif state == 2:
if True not in map(lambda b: b.newpos is not None, self.boxes) \
and random.random() > 0.2:
b1 = random.randint(0, self.n_boxes - 1)
b2 = random.randint(0, self.n_boxes - 1)
while b1 == b2:
b2 = random.randint(0, self.n_boxes - 1)
self.boxes[b1].swap(self.boxes[b2])
if state > 0 and state < 5:
for b in self.boxes:
b.update(t, dt)
if state == len(self.statetimes) - 1 and state_t > self.statetimes[-1]:
self.done = True
def draw(self, frame):
for b in self.boxes:
if b.newpos is None:
b.draw(frame)
for b in self.boxes:
if b.newpos is not None:
b.draw(frame)
BOXCONTENT = {
Point(0, 0),
Point(3, 0),
Point(0, 2),
Point(3, 2),
Point(1, 3),
Point(2, 3),
}
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Generate animations')
parser.add_argument('-d', '--delay', type=int, default=50)
parser.add_argument('-t', '--time', type=int, default=15)
parser.add_argument('output_file')
args = parser.parse_args()
dim = blup.frame.FrameDimension(WIDTH, HEIGHT, DEPTH, 3)
anim = blup.animation.Animation(dim)
anim.tags['description'] = ' '.join(sys.argv)
t = 0
dt = args.delay / 1000
gamestart = 0
game = None
while t < args.time or not game.done:
t += dt
frame = blup.animation.AnimationFrame(dim, args.delay)
if (game is None or game.done) and t < args.time:
game = Game(Point(WIDTH, HEIGHT), 6, 2, BOXCONTENT,
get_random_color(), 0.5)
gamestart = t
game.update(t - gamestart, dt)
game.draw(frame)
anim.addFrame(frame)
blup.writebml.writeBml(anim, args.output_file)

124
generators/twister.py

@ -0,0 +1,124 @@
#!/usr/bin/env python3
import sys
import random
import math
import argparse
import collections
import blup.frame
import blup.output
import blup.animation
import blup.writebml
import colorsys
WIDTH = 22
HEIGHT = 16
DEPTH = 256
class Point(collections.namedtuple('Point', ['x', 'y'])):
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Point(self.x - other.x, self.y - other.y)
def __mul__(self, other):
if isinstance(other, Point):
raise Exception('not implemented')
else:
return Point(self.x * other, self.y * other)
def __truediv__(self, other):
if isinstance(other, Point):
raise ValueError()
else:
return self * (1/other)
@property
def len(self):
return math.sqrt(self.x**2 + self.y**2)
@property
def n(self):
n = Point(self.y, -self.x)
return n / n.len
@property
def intp(self):
return Point(int(round(self.x)), int(round(self.y)))
def convert_color(c):
return list(map(lambda x: int(round(x*(DEPTH-1))), c))
def get_random_color():
return colorsys.hsv_to_rgb(random.random(), 0.5 + random.random() / 2, 1)
class Twister:
def __init__(self, worldsize):
self.worldsize = worldsize
self.cycletime = 1.3
self.sat = 0.5 + random.random() / 2
self.hues = [random.random()]
self.hues += [ self.hues[0] + (i+1) * 0.25 for i in range(3) ]
random.shuffle(self.hues)
self.width = worldsize.x // 2.9
def update(self, t, dt):
# inspired by https://www.lexaloffle.com/bbs/?tid=3050
self.rows = []
a0 = t * math.pi / self.cycletime
while len(self.rows) < self.worldsize.y:
y = len(self.rows)
a = a0 - y * math.pi / self.worldsize.y \
* (math.sin(t * math.pi / 10) * 0.7)
ot1 = t * math.pi / 5
ot2 = y * math.pi / (self.worldsize.y * 2)
offs = self.worldsize.x / 2 + math.sin(ot1 + ot2) * self.width / 2
x1 = int(round(offs + self.width * math.sin(a)))
x2 = int(round(offs + self.width * math.sin(a+0.5*math.pi)))
x3 = int(round(offs + self.width * math.sin(a+math.pi)))
x4 = int(round(offs + self.width * math.sin(a+1.5*math.pi)))
row = []
for i, (start, end) in enumerate([(x1, x2), (x2, x3),
(x3, x4), (x4, x1)]):
if start < end:
row += [(x, i, (x - start) / (end - start - 1))
for x in range(start, end - 1)]
self.rows.append(row)
def draw(self, frame):
for y, line in enumerate(self.rows):
for point in line:
x, i, j = point
val = 1 - math.sin(j * math.pi) / 2.3
color = colorsys.hsv_to_rgb(self.hues[i], self.sat, val)
try:
frame.setPixel(x, y, convert_color(color))
except ValueError:
pass
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Generate animations')
parser.add_argument('-d', '--delay', type=int, default=50)
parser.add_argument('-t', '--time', type=int, default=15)
parser.add_argument('output_file')
args = parser.parse_args()
dim = blup.frame.FrameDimension(WIDTH, HEIGHT, DEPTH, 3)
anim = blup.animation.Animation(dim)
anim.tags['description'] = ' '.join(sys.argv)
twister = Twister(Point(WIDTH, HEIGHT))
t = 0
dt = args.delay / 1000
while t < args.time:
t += dt
frame = blup.animation.AnimationFrame(dim, args.delay)
twister.update(t, dt)
twister.draw(frame)
anim.addFrame(frame)
blup.writebml.writeBml(anim, args.output_file)

133
generators/unrath.py

@ -0,0 +1,133 @@
#!/usr/bin/env python3
import sys
import random
import argparse
import blup.frame
import blup.output
import blup.animation
import blup.writebml
import colorsys
WIDTH = 22
HEIGHT = 16
DEPTH = 256
def convert_color(c):
return list(map(lambda x: int(round(x*(DEPTH-1))), c))
def get_random_color():
if random.random() < 0.5:
s = 1
v = random.random()
else:
s = random.random()
v = 1
return colorsys.hsv_to_rgb(random.random(), s, v)
class Unrath:
def __init__(self, world):
self.world = world
self.shape = set([(0, 0)])
shapesize = random.randint(2, 5)
while len(self.shape) < shapesize:
p = random.sample(self.shape, 1)[0]
self.shape.add((p[0]+random.randint(-1, 1),
p[1]+random.randint(-1, 1)))
minx = min(map(lambda p: p[0], self.shape))
miny = min(map(lambda p: p[1], self.shape))
self.shape = {(p[0]+abs(minx), p[1]+abs(miny)) for p in self.shape}
self.shapewidth = max(map(lambda p: p[0], self.shape))
shapeheight = max(map(lambda p: p[1], self.shape))
y = random.randint(0, HEIGHT-shapeheight)
if random.random() > 0.5:
d = 1
x = -self.shapewidth
else:
d = -1
x = WIDTH + self.shapewidth
self.pos = (x, y)
self.finished = False
self.speed = d * random.randint(2, WIDTH/2)
self.color = get_random_color()
def update(self, dt, nocheck=False):
if (self.pos[0] + self.shapewidth < 0 and self.speed < 0) or \
(self.pos[0] > WIDTH and self.speed > 0):
self.finished = True
return
newpos = (self.pos[0] + dt*self.speed, self.pos[1])
newpoints = self.get_points(newpos)
collision = False
for unrath in self.world.unraethe - {self}:
if len(unrath.get_points().intersection(newpoints)) > 0:
collision = True
if abs(unrath.speed) > abs(self.speed):
fast = unrath
slow = self
else:
slow = unrath
fast = self
slow.speed = fast.speed * (1.1 + random.random()/10)
slow.update(dt, True)
if not collision:
self.pos = newpos
def get_points(self, pos=None):
if pos is None:
pos = self.pos
return {(int(pos[0] + s[0]), int(pos[1] + s[1])) for s in self.shape}
def draw(self, frame):
for p in self.get_points():
try:
frame.setPixel(p[0], p[1], convert_color(self.color))
except ValueError:
pass
class World:
def __init__(self, num_unraethe):
self.num_unraethe = num_unraethe
self.unraethe = {}
def update(self, dt):
self.unraethe = set(filter(lambda u: not u.finished, self.unraethe))
if len(self.unraethe) < self.num_unraethe:
self.unraethe.add(Unrath(self))
for u in self.unraethe:
u.update(dt)
def draw(self, frame):
for u in self.unraethe:
u.draw(frame)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Generate Unrath animations')
parser.add_argument('-d', '--delay', type=int, default=50)
parser.add_argument('-t', '--time', type=int, default=15)
parser.add_argument('-n', '--num-unraethe', type=int, default=5)
parser.add_argument('output_file')
args = parser.parse_args()
dim = blup.frame.FrameDimension(WIDTH, HEIGHT, DEPTH, 3)
anim = blup.animation.Animation(dim)
anim.tags['description'] = ' '.join(sys.argv)
t = 0
w = World(args.num_unraethe)
while t < args.time:
t += args.delay / 1000
frame = blup.animation.AnimationFrame(dim, args.delay)
w.update(args.delay / 1000)
w.draw(frame)
anim.addFrame(frame)
blup.writebml.writeBml(anim, args.output_file)

103
utils/blmplay.py

@ -1,4 +1,4 @@
#!/usr/bin/python #! /usr/bin/env python3
import sys import sys
import getopt import getopt
@ -7,62 +7,67 @@ import blup.animation
import blup.output import blup.output
def printUsage(errMsg=None): def printUsage(errMsg=None):
if errMsg is not None: if errMsg is not None:
print('error: %s\n' % (errMsg)) print('error: %s\n' % (errMsg))
print('usage: %s [OPTIONS] FILENAME' % (sys.argv[0])) print('usage: %s [OPTIONS] FILENAME' % (sys.argv[0]))
print('supported options:') print('supported options:')
print(' -o OUTPUT where to output the frames (default: shell)') print(' -o OUTPUT where to output the frames (default: shell)')
print(' --output OUTPUT\n') print(' --no-loop play animation only once')
print(' -h print this text') print(' --output OUTPUT\n')
print(' --help') print(' -h print this text')
print(' --help')
def main(): def main():
try: try:
(opts, args) = getopt.gnu_getopt(sys.argv, 'ho:', ['help', 'output=']) (opts, args) = getopt.gnu_getopt(sys.argv, 'ho:', ['help', 'output=', 'no-loop'])
opts = dict(opts) opts = dict(opts)
except getopt.GetoptError as e: except getopt.GetoptError as e:
printUsage(e.msg) printUsage(e.msg)
sys.exit(1) sys.exit(1)
if '--help' in opts: if '--help' in opts:
printUsage() printUsage()
sys.exit(0) sys.exit(0)
if '-o' in opts: if '-o' in opts:
output = opts['-o'] output = opts['-o']
elif '--output' in opts: elif '--output' in opts:
output = opts['--output'] output = opts['--output']
else: else:
output = 'shell' output = 'shell'
try: loop = '--no-loop' not in opts
out = blup.output.getOutput(output)
except blup.output.IllegalOutputSpecificationError:
print('illegal output specification')
print('available outputs:')
print(blup.output.getOutputDescriptions())
sys.exit(1)
except Exception as e:
print('could not initialize output: %s' % (str(e)))
sys.exit(1)
if len(args) != 2: try:
printUsage() out = blup.output.getOutput(output)
sys.exit(1) except blup.output.IllegalOutputSpecificationError:
print('illegal output specification')
print('available outputs:')
print(blup.output.getOutputDescriptions())
sys.exit(1)
except Exception as e:
print('could not initialize output: %s' % (str(e)))
sys.exit(1)
try: if len(args) != 2:
anim = blup.animation.load(args[1]) printUsage()
except blup.animation.AnimationFileError: sys.exit(1)
print('could not load animation')
sys.exit(1)
player = blup.animation.AnimationPlayer() try:
try: anim = blup.animation.load(args[1])
while True: except blup.animation.AnimationFileError:
player.play(anim, out) print('could not load animation')
except KeyboardInterrupt: sys.exit(1)
sys.exit(0)
player = blup.animation.AnimationPlayer()
try:
while True:
player.play(anim, out)
if not loop:
break
except KeyboardInterrupt:
sys.exit(0)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

6
utils/blphub.py

@ -69,7 +69,11 @@ class BLPHub():
self.__lastFrameTimeout = ( self.__lastFrameTimeout = (
currentTime + self.__streams[stream]['timeout'] currentTime + self.__streams[stream]['timeout']
) )
self.__output.sendFrame(frame) try:
self.__output.sendFrame(frame)
except Exception as e:
# TODO better error handling
print('could not send frame: %s' % (repr(e)))
return True return True
return False return False

149
utils/blpserver.py

@ -1,3 +1,4 @@
#! /usr/bin/env python3
import socket import socket
import getopt import getopt
@ -7,82 +8,84 @@ from blup.BLP import BLPServer
import blup.BLP import blup.BLP
import blup.output import blup.output
def printUsage(errMsg=None): def printUsage(errMsg=None):
if errMsg is not None: if errMsg is not None:
print 'error: %s\n' % (errMsg) print('error: %s\n' % (errMsg))
print 'usage: %s [OPTIONS]' % (sys.argv[0]) print('usage: %s [OPTIONS]' % (sys.argv[0]))
print 'supported options:' print('supported options:')
print ' -a ADDR address to listen for packets (default: 127.0.0.1)' print(' -a ADDR address to listen for packets',
print ' --address ADDR\n' '(default: 127.0.0.1)')
print ' -p PORT port to listen for packets (default: 4242)' print(' --address ADDR\n')
print ' --port PORT\n' print(' -p PORT port to listen for packets (default: 4242)')
print ' -o OUTPUT where to put the recieved frames (default: shell)' print(' --port PORT\n')
print ' --output OUTPUT\n' print(' -o OUTPUT where to put the recieved frames',
print ' --eblp use EBLP instead of BLP\n' '(default: shell)')
print ' --e3blp use E3BLP instead of BLP\n' print(' --output OUTPUT\n')
print ' -h print this text' print(' --eblp use EBLP instead of BLP\n')
print ' --help' print(' --e3blp use E3BLP instead of BLP\n')
print(' -h print this text')
print(' --help')
def main(): def main():
try: try:
(opts, args) = getopt.gnu_getopt(sys.argv, 'ha:p:o:', ['help', 'address=', 'port=', 'output=', 'eblp', 'e3blp']) (opts, args) = getopt.gnu_getopt(sys.argv, 'ha:p:o:',
opts = dict(opts) ['help', 'address=', 'port=',
except getopt.GetoptError as e: 'output=', 'eblp', 'e3blp'])
printUsage(e.msg) opts = dict(opts)
sys.exit(1) except getopt.GetoptError as e:
printUsage(e.msg)
if opts.has_key('--help'): sys.exit(1)
printUsage()
sys.exit(0) if '--help' in opts:
printUsage()
if opts.has_key('-a'): sys.exit(0)
addr = opts['-a']
elif opts.has_key('--address'): if '-a' in opts:
addr = opts['--address'] addr = opts['-a']
else: elif '--address' in opts:
addr = '127.0.0.1' addr = opts['--address']
else:
if opts.has_key('-p'): addr = '127.0.0.1'
port = opts['-p']
elif opts.has_key('--port'): if '-p' in opts:
port = opts['--port'] port = opts['-p']
else: elif '--port' in opts:
port = 4242 port = opts['--port']
else:
if opts.has_key('-o'): port = 4242
output = opts['-o']
elif opts.has_key('--output'): if '-o' in opts:
output = opts['--output'] output = opts['-o']
else: elif '--output' in opts:
output = 'shell' output = opts['--output']
else:
if opts.has_key('--eblp'): output = 'shell'
protocol = blup.BLP.PROTO_EBLP
elif opts.has_key('--e3blp'): if '--eblp' in opts:
protocol = blup.BLP.PROTO_E3BLP protocol = blup.BLP.PROTO_EBLP
else: elif '--e3blp' in opts:
protocol = blup.BLP.PROTO_BLP protocol = blup.BLP.PROTO_E3BLP
else:
if opts.has_key('--eblp') and opts.has_key('--e3blp'): protocol = blup.BLP.PROTO_BLP
printUsage('please only specify one of --eblp or --e3blp')
sys.exit(1) if '--eblp' in opts and '--e3blp' in opts:
printUsage('please only specify one of --eblp or --e3blp')
try: sys.exit(1)
port = int(port)
except ValueError: try:
printUsage('invalid port specified') port = int(port)
sys.exit(1) except ValueError:
printUsage('invalid port specified')
out = blup.output.getOutput(output) sys.exit(1)
srv = BLPServer(output=out, addr=addr, port=port, protocol=protocol)
out = blup.output.getOutput(output)
try: srv = BLPServer(output=out, addr=addr, port=port, protocol=protocol)
srv.serve()
except KeyboardInterrupt: try:
pass srv.serve()
except KeyboardInterrupt:
pass
if __name__ == '__main__': if __name__ == '__main__':
main() main()

31
utils/miniplayer.py

@ -1,4 +1,4 @@
#!/usr/bin/python3 #!/usr/bin/env python3
import threading import threading
import os import os
@ -47,14 +47,17 @@ class MiniPlayer(object):
""" """
def __init__(self, animDir, output): def __init__(self, animDir, animExtensions, output):
self.__animDir = animDir self.__animDir = animDir
self.__animExtensions = animExtensions
self.__output = output self.__output = output
self.loopTime = 10000 self.loopTime = 10000
self.gap = 800 self.gap = 800
self.maxPlayNext = 3 self.maxPlayNext = 3
self.__playNext = [] self.__playNext = []
self.__running = False self.__running = False
self.__anims = {}
self.__anims_remaining = {}
def terminate(self): def terminate(self):
self.__running = False self.__running = False
@ -79,10 +82,23 @@ class MiniPlayer(object):
""" """
if len(self.__playNext) > 0 and os.path.isfile(self.__playNext[0]): if len(self.__playNext) > 0 and os.path.isfile(self.__playNext[0]):
return self.__playNext.pop(0) anim = self.__playNext.pop(0)
else: else:
files = os.listdir(self.__animDir) anims = set()
return random.choice(files) for dp, dns, fns in os.walk(self.__animDir, followlinks=True):
for fn in fns:
root, ext = os.path.splitext(fn)
if ext[1:] in self.__animExtensions:
anims.add(os.path.join(dp, fn))
if anims != self.__anims or len(self.__anims_remaining) == 0:
self.__anims = anims
self.__anims_remaining = set(anims)
anim = random.sample(self.__anims_remaining, 1)[0]
self.__anims_remaining.remove(anim)
print(anim)
return anim
def run(self): def run(self):
""" """
@ -97,8 +113,7 @@ class MiniPlayer(object):
while self.__running: while self.__running:
# begin to load the next animation # begin to load the next animation
filename = os.path.join(self.__animDir, self.getNextFilename()) loader = AnimationLoaderThread(self.getNextFilename())
loader = AnimationLoaderThread(filename)
loader.start() loader.start()
# play the animation in case it had been successfully loaded before # play the animation in case it had been successfully loaded before
@ -172,7 +187,7 @@ def main():
print('%s is not a directory' % (animDir)) print('%s is not a directory' % (animDir))
sys.exit(1) sys.exit(1)
p = MiniPlayer(animDir, out) p = MiniPlayer(animDir, ['bml'], out)
try: try:
p.run() p.run()
except KeyboardInterrupt: except KeyboardInterrupt:

26
wii-pair/daemon.py

@ -15,6 +15,9 @@ known_boards = {'niklas': '00:26:59:34:C8:69',
'fed': '00:23:CC:23:5E:1D', 'fed': '00:23:CC:23:5E:1D',
} }
MIN_WEIGHT = 10
PLAYER_TIMEOUT = 3
class StatusThread(Thread): class StatusThread(Thread):
def __init__(self, t1, t2): def __init__(self, t1, t2):
Thread.__init__(self) Thread.__init__(self)
@ -88,6 +91,7 @@ class WiiThread(Thread):
self.pos_x = None self.pos_x = None
self.pos_y = None self.pos_y = None
self.weight = 0 self.weight = 0
self.timeout = None
def run(self): def run(self):
if self.addr != 'dummy': if self.addr != 'dummy':
@ -108,7 +112,8 @@ class WiiThread(Thread):
while not self.stop and self.board.status == 'Connected': while not self.stop and self.board.status == 'Connected':
self.weight = self.board.mass.totalWeight self.weight = self.board.mass.totalWeight
if self.board.mass.totalWeight > 15: if self.board.mass.totalWeight >= MIN_WEIGHT:
self.timeout = None
m = self.board.mass m = self.board.mass
x_balance = -(m.topLeft+m.bottomLeft) + (m.topRight+m.bottomRight) x_balance = -(m.topLeft+m.bottomLeft) + (m.topRight+m.bottomRight)
x_balance = x_balance/float(m.totalWeight) x_balance = x_balance/float(m.totalWeight)
@ -117,8 +122,12 @@ class WiiThread(Thread):
self.pos_x = x_balance self.pos_x = x_balance
self.pos_y = y_balance self.pos_y = y_balance
else: else:
self.pos_x = None if self.timeout is None:
self.pos_y = None self.timeout = time.time()+PLAYER_TIMEOUT
else:
if time.time() >= self.timeout:
self.pos_x = None
self.pos_y = None
time.sleep(0.1) time.sleep(0.1)
@ -159,9 +168,11 @@ t2.start()
while not t2.connected: while not t2.connected:
time.sleep(0.1) time.sleep(0.1)
tStatus = StatusThread(t1, t2) tStatus = None
tStatus.daemon = True if os.environ.get('DEBUG_STATUS', 0) == '1':
tStatus.start() tStatus = StatusThread(t1, t2)
tStatus.daemon = True
tStatus.start()
wiis = [t1, t2] wiis = [t1, t2]
@ -229,6 +240,7 @@ try:
except (KeyboardInterrupt, SystemExit): except (KeyboardInterrupt, SystemExit):
t1.stop = True t1.stop = True
t2.stop = True t2.stop = True
tStatus.stop = True if tStatus is not None:
tStatus.stop = True
sys.exit() sys.exit()

Loading…
Cancel
Save