commit
19eceac3f9
44 changed files with 5846 additions and 0 deletions
@ -0,0 +1,152 @@
@@ -0,0 +1,152 @@
|
||||
|
||||
SERVER_URL="http://volta:8080/do_request"; |
||||
UPLOAD_URL="http://volta:8080/do_upload"; |
||||
|
||||
function refreshStatus() { |
||||
|
||||
$.ajax({ |
||||
url: SERVER_URL, |
||||
dataType: "json", |
||||
type: "POST", |
||||
data: JSON.stringify({"cmd": "getcurrentfilename"}), |
||||
success: function(data) { |
||||
$.blayerwebif.newFilename = data.currentfilename; |
||||
if($.blayerwebif.oldFilename == $.blayerwebif.newFilename) |
||||
return; |
||||
$.blayerwebif.oldFilename = $.blayerwebif.newFilename; |
||||
|
||||
$("#tagtable").empty(); |
||||
$("#tagtable").append("<tr><td>filename</td><td>"+$.blayerwebif.newFilename+"</td></tr"); |
||||
$.ajax({ |
||||
url: SERVER_URL, |
||||
dataType: "json", |
||||
type: "POST", |
||||
data: JSON.stringify({"cmd": "getcurrentfileinfo"}), |
||||
success: function(data) { |
||||
$.each(data.currentfileinfo, function(key, value) { |
||||
$("#tagtable").append("<tr><td>"+key+"</td><td>"+value+"</td></tr"); |
||||
}); |
||||
} |
||||
}); |
||||
|
||||
} |
||||
}); |
||||
|
||||
|
||||
|
||||
|
||||
}; |
||||
|
||||
function refreshFilenames() { |
||||
$.ajax({ |
||||
url: SERVER_URL, |
||||
dataType: "json", |
||||
type: "POST", |
||||
data: JSON.stringify({"cmd": "getallfilenames"}), |
||||
success: function(data) { |
||||
$("#filenames").empty(); |
||||
$.each(data.allfilenames, function(key, value) { |
||||
//alert("<li><a href=\"#\" onClick=\"playNext(\\\""+value+"\\\")\">"+value+"</a></li>");
|
||||
$("#filenames").append("<li><a href=\"#\" class=\"playNextLink\">"+value+"</a></li>"); |
||||
|
||||
}); |
||||
$("#animationcount").text(data.allfilenames.length); |
||||
$(".playNextLink").click(function() { |
||||
linkNode = this; |
||||
$.ajax({ |
||||
url: SERVER_URL, |
||||
dataType: "json", |
||||
type: "POST", |
||||
data: JSON.stringify({"cmd": "playnext", "filename": $(this).text()}), |
||||
success: function(data) { |
||||
if(data.playnext == "ok") |
||||
item = $("<span class=\"notificationtext\">queued...</span>"); |
||||
else |
||||
item = $("<span class=\"notificationtext\">queue full :(</span>"); |
||||
|
||||
$(linkNode).after(item); |
||||
$(item).delay(1000).fadeOut("slow"); |
||||
} |
||||
}); |
||||
}); |
||||
}, |
||||
}); |
||||
} |
||||
|
||||
function refreshStatusPeriodic() { |
||||
refreshStatus(); |
||||
setTimeout(refreshStatusPeriodic, 1000); |
||||
} |
||||
|
||||
$(document).ready(function() { |
||||
$.blayerwebif = {}; |
||||
$.blayerwebif.oldFilename = ""; |
||||
|
||||
setTimeout(refreshStatusPeriodic, 1000); |
||||
refreshFilenames(); |
||||
refreshStatus(); |
||||
|
||||
$("#uploadbutton").click(function() { |
||||
$.ajax({ |
||||
url: UPLOAD_URL, |
||||
type: "POST", |
||||
beforeSend: function() { |
||||
$("#uploadform").after("<span class=\"notificationtext\" id=\"uploadingnotification\">uploading...</span>"); |
||||
}, |
||||
success: function() { |
||||
$("#uploadingnotification").text("done! :)").delay(1000).fadeOut("slow"); |
||||
refreshFilenames(); |
||||
}, |
||||
error: function() { |
||||
$("#uploadingnotification").text("failed! :(").delay(1000).fadeOut("slow"); |
||||
}, |
||||
contentType: false, |
||||
cache: false, |
||||
processData: false, |
||||
data: new FormData($("#uploadform")[0]) |
||||
}); |
||||
}); |
||||
|
||||
$("#next").click(function() { |
||||
$.ajax({ |
||||
url: SERVER_URL, |
||||
dataType: "json", |
||||
type: "POST", |
||||
data: JSON.stringify({"cmd": "next"}), |
||||
success: function() { |
||||
refreshStatus(); |
||||
} |
||||
}); |
||||
}); |
||||
|
||||
$("#pause").click(function() { |
||||
button = this; |
||||
$.ajax({ |
||||
url: SERVER_URL, |
||||
dataType: "json", |
||||
type: "POST", |
||||
data: JSON.stringify({"cmd": "togglepaused"}), |
||||
success: function(data) { |
||||
refreshStatus(); |
||||
|
||||
if(data.paused) |
||||
$(button).text("play"); |
||||
else |
||||
$(button).text("pause"); |
||||
} |
||||
}); |
||||
}); |
||||
|
||||
$("#filtertext").keyup(function() { |
||||
keyword = $("#filtertext").val().toLowerCase(); |
||||
$("#filenames li").each(function(key, value) { |
||||
|
||||
if($(value).children("a").text().toLowerCase().indexOf(keyword) >= 0) |
||||
$(value).show(); |
||||
else |
||||
$(value).hide(); |
||||
}); |
||||
}); |
||||
}); |
||||
|
||||
|
@ -0,0 +1,53 @@
@@ -0,0 +1,53 @@
|
||||
<html> |
||||
<head> |
||||
<title>blayer web interface</title> |
||||
<script src="jquery.js" type="text/javascript"></script> |
||||
<script src="blayerwebif.js" type="text/javascript"></script> |
||||
|
||||
<link rel="stylesheet" href="style.css" /> |
||||
</head> |
||||
<body> |
||||
|
||||
<div id="containerleft"> |
||||
<div class="box"> |
||||
<p class="boxhead">control</p> |
||||
|
||||
<p class="controlbuttons"> |
||||
<a href="#" id="next">next</a> |
||||
<a href="#" id="pause">play / pause</a> |
||||
</p> |
||||
</div> |
||||
|
||||
<div class="box"> |
||||
<p class="boxhead">upload</p> |
||||
<form action="#" method="post" id="uploadform"> |
||||
<input type="file" name="file" /> |
||||
<input type="button" value="upload" id="uploadbutton" /> |
||||
</form> |
||||
</div> |
||||
|
||||
<div class="box"> |
||||
<p class="boxhead">current animation</p> |
||||
|
||||
<!--<p><span class="label">Filename:</span><span id="filename"></span></p>--> |
||||
|
||||
<table id="tagtable"> |
||||
</table> |
||||
|
||||
</div> |
||||
</div> |
||||
|
||||
<div id="containerleft"> |
||||
<div class="box"> |
||||
<p class="boxhead">animation database</p> |
||||
<p><span id="animationcount">###</span> animations in total</p> |
||||
|
||||
<p>filter: <input type="text" id="filtertext" /></p> |
||||
|
||||
<ul id="filenames"> |
||||
</ul> |
||||
|
||||
</div> |
||||
</div> |
||||
|
||||
</body></html> |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,87 @@
@@ -0,0 +1,87 @@
|
||||
body { |
||||
background-color: #2f2f2f; |
||||
font-family: "lucida grande",tahoma, verdana, sans-serif; |
||||
font-size: 0.8em; |
||||
color: #cccccc; |
||||
} |
||||
|
||||
h1 { |
||||
font-size: 1.5em; |
||||
} |
||||
|
||||
#containerleft { |
||||
width: 46%; |
||||
margin-left: auto; |
||||
padding: 20px; |
||||
float: left; |
||||
} |
||||
#containerright { |
||||
width: 46%; |
||||
padding: 20px; |
||||
float: left; |
||||
} |
||||
|
||||
div.box { |
||||
border-radius: 10px; |
||||
border-color: #000000; |
||||
border-width: 1px; |
||||
border-style: solid; |
||||
/*width: 50%;*/ |
||||
background-color: #3d3d3d; |
||||
margin: auto; |
||||
margin-bottom: 30px; |
||||
padding: 10px; |
||||
box-shadow: 3px 3px 3px 3px #272727; |
||||
} |
||||
|
||||
p.boxhead { |
||||
color: #006483; |
||||
font-weight: bold; |
||||
margin-top: 0px; |
||||
} |
||||
|
||||
#tagtable td:first-child { |
||||
font-style: italic; |
||||
padding-right: 30px; |
||||
} |
||||
#tagtable td { |
||||
font-size: 0.8em; |
||||
} |
||||
|
||||
#filenames a, #filenames a:visited { |
||||
text-decoration: none; |
||||
color: #cccccc; |
||||
} |
||||
|
||||
.notificationtext { |
||||
padding-left: 20px; |
||||
color: #ffffff; |
||||
font-size: 0.7em; |
||||
} |
||||
|
||||
.controlbuttons { |
||||
padding: 10px; |
||||
} |
||||
.controlbuttons a, .controlbutton a:visited { |
||||
padding: 5px; |
||||
border: 1px solid #cccccc; |
||||
border-radius: 4px; |
||||
color: #cccccc; |
||||
text-decoration: none; |
||||
} |
||||
|
||||
.controlbuttons a:hover { |
||||
background: #006483; |
||||
} |
||||
|
||||
.label { |
||||
font-style: italic; |
||||
} |
||||
|
||||
.clear { |
||||
clear:both; |
||||
width: 0px; |
||||
height: 0px; |
||||
} |
||||
|
||||
|
@ -0,0 +1,17 @@
@@ -0,0 +1,17 @@
|
||||
[control] |
||||
bind_host = 0.0.0.0 |
||||
bind_port = 4243 |
||||
|
||||
[player] |
||||
animation_dir = /var/tmp/blup2/ |
||||
playlist = /var/tmp/blup2/playlist.txt |
||||
shuffle = true |
||||
autorefresh = true |
||||
looptime = 3 |
||||
maxplaynext = 3 |
||||
|
||||
[output] |
||||
output = blp:localhost:4242 |
||||
#output = blp:bastel0:4242 |
||||
#output = 'shell' |
||||
|
@ -0,0 +1,428 @@
@@ -0,0 +1,428 @@
|
||||
#!/usr/bin/python |
||||
|
||||
import threading |
||||
import random |
||||
import os.path |
||||
import os |
||||
import time |
||||
import socket |
||||
import asyncore |
||||
import asynchat |
||||
import cmd |
||||
import ConfigParser |
||||
import sys |
||||
import json |
||||
|
||||
import blup.animation as Animation |
||||
import blup.output as BlupOutput |
||||
import blup.frame as Frame |
||||
|
||||
|
||||
CONFIG_DEFAULTS = { |
||||
'output': 'shell', |
||||
'playlist': '', |
||||
'animation_dir': '.', |
||||
'enabled': False, |
||||
'shuffle': False, |
||||
'autorefresh': False |
||||
} |
||||
|
||||
def find(f, list): |
||||
for x in list: |
||||
if f(x): |
||||
return x |
||||
|
||||
class AnimationInfo(): |
||||
def __init__(self, filename): |
||||
self.__filename = filename |
||||
self.__tags = None |
||||
self.__valid = None |
||||
|
||||
@property |
||||
def filesize(self): |
||||
try: |
||||
statinfo = os.stat(self.__filename) |
||||
return statinfo.st_size |
||||
except OSError: |
||||
return -1 |
||||
|
||||
@property |
||||
def mtime(self): |
||||
try: |
||||
statinfo = os.stat(self.__filename) |
||||
return statinfo.st_mtime |
||||
except OSError: |
||||
return -1 |
||||
|
||||
@property |
||||
def tags(self): |
||||
return self.__tags |
||||
|
||||
@property |
||||
def valid(self): |
||||
if self.__valid == None: |
||||
try: |
||||
self.check() |
||||
except Animation.AnimationFileError: |
||||
pass |
||||
return self.__valid |
||||
|
||||
@property |
||||
def filename(self): |
||||
return self.__filename |
||||
|
||||
def check(self): |
||||
self.__valid = False |
||||
self.__tags = None |
||||
|
||||
anim = Animation.load(self.__filename) |
||||
|
||||
self.__valid = True |
||||
self.__tags = anim.tags |
||||
|
||||
|
||||
class AnimationDatabase(): |
||||
def __init__(self, animDir): |
||||
self.__animDir = animDir |
||||
self.__animInfos = [] |
||||
self.__lock = threading.Lock() |
||||
|
||||
def update(self): |
||||
with self.__lock: |
||||
oldAnimInfos = self.__animInfos |
||||
animInfos = [] |
||||
|
||||
for (dirpath, dirnames, filenames) in os.walk(self.__animDir): |
||||
files = map(lambda f: os.path.join(dirpath, f), filenames) |
||||
files = map(lambda f: os.path.relpath(os.path.join(dirpath, f), self.__animDir), filenames) |
||||
animInfos.extend(map(lambda f: AnimationInfo(f), files)) |
||||
#animInfos = filter(lambda a: a.valid, animInfos) |
||||
|
||||
newAnimInfos = [] |
||||
for animInfo in animInfos: |
||||
oldAnimInfo = find(lambda a: a.filename == animInfo.filename, oldAnimInfos) |
||||
if oldAnimInfo is not None and oldAnimInfo.mtime == animInfo.mtime: |
||||
newAnimInfos.append(oldAnimInfo) |
||||
else: |
||||
if animInfo.valid: |
||||
newAnimInfos.append(animInfo) |
||||
|
||||
self.__animInfos = newAnimInfos |
||||
|
||||
def findAnimation(self, filename): |
||||
with self.__lock: |
||||
animInfo = find(lambda a: a.filename == filename, self.__animInfos) |
||||
if animInfo is not None: |
||||
return animInfo |
||||
else: |
||||
raise Exception('animation not found!') |
||||
|
||||
def containsAnimation(self, filename): |
||||
with self.__lock: |
||||
animInfo = find(lambda a: a.filename == filename, self.__animInfos) |
||||
return (animInfo is not None) |
||||
|
||||
def getAllFilenames(self): |
||||
with self.__lock: |
||||
return map(lambda a: a.filename, self.__animInfos) |
||||
|
||||
|
||||
#class Playlist(list): |
||||
# def __init__(self, animationDatabase, filename=None): |
||||
# self.__animDb = animationDatabase |
||||
# self.__filename = filename |
||||
# if filename == None: |
||||
# self.append(self.__animDb.getAllFilenames()) |
||||
# else: |
||||
# f = open(filename, 'r') |
||||
# lines = f.read().split('\n') |
||||
# f.close() |
||||
# |
||||
# for line in lines: |
||||
# if self.__animDb.containsAnimation(line): |
||||
# self.append(line) |
||||
# |
||||
# def refresh(self): |
||||
# #f = open(filename, 'r') |
||||
# #lines = f.read().split('\n') |
||||
# #f.close() |
||||
# pass |
||||
# |
||||
# #for line in lines: |
||||
# # if self.__animDb.containsAnimation(line): |
||||
# # self.append(line) |
||||
|
||||
|
||||
|
||||
class PlayerThread(threading.Thread): |
||||
def __init__(self, database, output): |
||||
threading.Thread.__init__(self) |
||||
self.__playlist = [] |
||||
self.database = database |
||||
self.__playNext = [] |
||||
self.maxPlayNext = 3 |
||||
self.__output = output |
||||
self.shuffle = False |
||||
self.autoRefresh = True |
||||
self.loopTime = 10 |
||||
self.gap = 800 |
||||
self.__running = False |
||||
self.__player = Animation.AnimationPlayer() |
||||
self.__currentIndex = 0 |
||||
self.__currentFile = '' |
||||
self.__paused = False |
||||
|
||||
@property |
||||
def currentFile(self): |
||||
if not self.__paused: |
||||
return self.__currentFile |
||||
else: |
||||
return "" |
||||
|
||||
@property |
||||
def currentFileInfo(self): |
||||
if not self.__paused: |
||||
return self.database.findAnimation(self.__currentFile) |
||||
else: |
||||
# TODO: mhh... |
||||
return None |
||||
|
||||
@property |
||||
def paused(self): |
||||
return self.__paused |
||||
|
||||
def playNext(self, next): |
||||
if len(self.__playNext) < self.maxPlayNext: |
||||
self.__playNext.append(next) |
||||
else: |
||||
raise Exception |
||||
|
||||
def next(self): |
||||
self.__player.stop() |
||||
|
||||
def togglePaused(self): |
||||
self.__paused = not self.__paused |
||||
if self.__paused: |
||||
self.__player.stop() |
||||
|
||||
def refreshPlaylist(self): |
||||
self.database.update() |
||||
self.__playlist = self.database.getAllFilenames() |
||||
|
||||
def loadPlaylistFile(self): |
||||
raise Exception('not yet implemented :(') |
||||
|
||||
def terminate(self): |
||||
self.__player.stop() |
||||
self.__running = False |
||||
|
||||
def run(self): |
||||
self.__running = True |
||||
while self.__running: |
||||
anim = None |
||||
if self.autoRefresh: # and self.__currentIndex == len(self.__playlist) - 1: |
||||
self.refreshPlaylist() |
||||
|
||||
if len(self.__playNext) == 0: |
||||
if len(self.__playlist) == 0: |
||||
print 'busywait!!' |
||||
continue |
||||
|
||||
if self.shuffle: |
||||
self.__currentFile = self.__playlist[random.randint(0, len(self.__playlist) - 1)] |
||||
else: |
||||
if self.__currentIndex >= len(self.__playlist): |
||||
self.__currentIndex = 0 |
||||
self.__currentFile = self.__playlist[self.__currentIndex] |
||||
self.__currentIndex += 1 |
||||
else: |
||||
self.__currentFile = self.__playNext.pop(0) |
||||
anim = Animation.load(self.__currentFile) |
||||
|
||||
count = 1 |
||||
if anim.tags.has_key('loop') and anim.tags['loop'].lower() in ['yes', 'true']: |
||||
if anim.duration < self.loopTime * 1000: |
||||
count = (self.loopTime * 1000) / anim.duration |
||||
|
||||
|
||||
print 'playing:', anim |
||||
print 'tags:', anim.tags |
||||
self.__player.play(anim, self.__output, count=count) |
||||
print 'elapsed: ', self.__player.elapsed |
||||
|
||||
if self.__paused: |
||||
# TODO: use correct frame size |
||||
self.__output.sendFrame(Frame.Frame(18,8)) |
||||
while self.__paused: |
||||
time.sleep(1) |
||||
print 'busywait!!' |
||||
|
||||
if self.gap > 0 and self.__running: |
||||
# TODO: use correct frame size |
||||
self.__output.sendFrame(Frame.Frame(18,8)) |
||||
time.sleep(self.gap / 1000.0) |
||||
|
||||
|
||||
class PlayerControlClientHandler(asynchat.async_chat): |
||||
def __init__(self, sock, player): |
||||
asynchat.async_chat.__init__(self, sock=sock) |
||||
self.set_terminator('\n') |
||||
self.__buffer = '' |
||||
self.__player = player |
||||
|
||||
def collect_incoming_data(self, data): |
||||
self.__buffer += data |
||||
|
||||
def sendPacket(self, data): |
||||
self.send(json.dumps(data) + '\n') |
||||
|
||||
def found_terminator(self): |
||||
try: |
||||
data = json.loads(self.__buffer) |
||||
except ValueError: |
||||
self.sendPacket({'error': 'invalid json'}) |
||||
return |
||||
finally: |
||||
self.__buffer = '' |
||||
|
||||
try: |
||||
cmd = data['cmd'] |
||||
except (TypeError, KeyError): |
||||
self.sendPacket({'error': 'no command given'}) |
||||
return |
||||
|
||||
if cmd == 'refresh': |
||||
self.__player.refreshPlaylist() |
||||
self.sendPacket({'refresh': 'ok'}) |
||||
elif cmd == 'next': |
||||
self.__player.next() |
||||
self.sendPacket({'next': 'ok'}) |
||||
elif cmd == 'togglepaused': |
||||
self.__player.togglePaused() |
||||
self.sendPacket({'togglepaused': 'ok', 'paused': self.__player.paused}) |
||||
elif cmd == 'getpaused': |
||||
self.sendPacket({'paused': self.__player.paused}) |
||||
elif cmd == 'playnext': |
||||
if data.has_key('filename') and type(data['filename']) in [str, unicode]: |
||||
try: |
||||
self.__player.playNext(data['filename']) |
||||
except Exception: |
||||
self.sendPacket({'error':'too many animations queued'}) |
||||
else: |
||||
self.sendPacket({'playnext':'ok'}) |
||||
else: |
||||
self.sendPacket({'error': 'no or invalid animation file'}) |
||||
elif cmd == 'getallfilenames': |
||||
self.sendPacket({'allfilenames': self.__player.database.getAllFilenames()}) |
||||
elif cmd == 'getcurrentfileinfo': |
||||
if not self.__player.paused: |
||||
info = self.__player.currentFileInfo.tags |
||||
else: |
||||
info = {} |
||||
self.sendPacket({'currentfileinfo': info}) |
||||
elif cmd == 'getcurrentfilename': |
||||
self.sendPacket({'currentfilename': self.__player.currentFile}) |
||||
else: |
||||
self.sendPacket({'error': 'unknown command'}) |
||||
|
||||
|
||||
class PlayerControlServer(asyncore.dispatcher): |
||||
def __init__(self, host, port, player): |
||||
asyncore.dispatcher.__init__(self) |
||||
self.create_socket(socket.AF_INET, socket.SOCK_STREAM) |
||||
self.set_reuse_addr() |
||||
self.bind((host, port)) |
||||
self.listen(5) |
||||
self.__player = player |
||||
|
||||
def handle_accept(self): |
||||
pair = self.accept() |
||||
if pair is not None: |
||||
sock, addr = pair |
||||
print 'control connetcion from', addr |
||||
PlayerControlClientHandler(sock, self.__player) |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# |
||||
# main |
||||
# |
||||
|
||||
cfg = ConfigParser.SafeConfigParser(CONFIG_DEFAULTS) |
||||
cfg.read('/var/tmp/blup2/blayer.cfg') |
||||
|
||||
try: |
||||
out = BlupOutput.getOutput(cfg.get('output', 'output')) |
||||
except ConfigParser.NoSectionError: |
||||
sys.err.write('config error: missing \'output\' section') |
||||
except ConfigParser.NoOptionError: |
||||
sys.err.write('config error: missing \'output\' option in \'output\' section') |
||||
|
||||
try: |
||||
animation_dir = cfg.get('player', 'animation_dir') |
||||
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): |
||||
animation_dir = '.' |
||||
|
||||
db = AnimationDatabase(animation_dir) |
||||
|
||||
print 'updating database...' |
||||
db.update() |
||||
print 'done with updating database...' |
||||
print 'updating database (again)...' |
||||
db.update() |
||||
print 'done with updating database...' |
||||
#print db.getAllFilenames() |
||||
|
||||
#try: |
||||
# playlist = Playlist(db, cfg.get('player', 'playlist')) |
||||
# print 'file-playluist:', playlist |
||||
#except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): |
||||
# playlist = Playlist(db) |
||||
# print 'db-playluist:', playlist |
||||
|
||||
blayer = PlayerThread(db, out) |
||||
blayer.refreshPlaylist() |
||||
#sys.exit(0) |
||||
|
||||
try: |
||||
blayer.loopTime = cfg.getint('player', 'looptime') |
||||
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): |
||||
pass |
||||
|
||||
try: |
||||
blayer.shuffle = cfg.getboolean('player', 'shuffle') |
||||
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): |
||||
pass |
||||
|
||||
try: |
||||
blayer.autoRefresh = cfg.getboolean('player', 'autorefresh') |
||||
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): |
||||
pass |
||||
|
||||
try: |
||||
blayer.maxPlayNext = cfg.getint('player', 'maxplaynext') |
||||
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): |
||||
pass |
||||
|
||||
blayer.start() |
||||
|
||||
try: |
||||
cser = PlayerControlServer(cfg.get('control', 'bind_host'), cfg.getint('control', 'bind_port'), blayer) |
||||
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): |
||||
cser = None |
||||
|
||||
|
||||
try: |
||||
|
||||
if cser is not None: |
||||
asyncore.loop() |
||||
else: |
||||
blayer.join() |
||||
except KeyboardInterrupt: |
||||
print 'nawk!!' |
||||
blayer.terminate() |
||||
|
||||
|
||||
|
@ -0,0 +1,379 @@
@@ -0,0 +1,379 @@
|
||||
#!/usr/bin/python |
||||
|
||||
import threading |
||||
import random |
||||
import os.path |
||||
import os |
||||
import time |
||||
import socket |
||||
import asyncore |
||||
import asynchat |
||||
import cmd |
||||
import ConfigParser |
||||
import sys |
||||
|
||||
import blup.animation as Animation |
||||
import blup.output as BlupOutput |
||||
import blup.frame as Frame |
||||
|
||||
|
||||
CONFIG_DEFAULTS = { |
||||
'output': 'shell', |
||||
'playlist': '', |
||||
'animation_dir': '.', |
||||
'enabled': False, |
||||
'shuffle': False, |
||||
'autorefresh': False |
||||
} |
||||
|
||||
def find(f, list): |
||||
for x in list: |
||||
if f(x): |
||||
return x |
||||
|
||||
class AnimationInfo(): |
||||
def __init__(self, filename): |
||||
self.__filename = filename |
||||
self.__tags = None |
||||
self.__valid = None |
||||
|
||||
@property |
||||
def filesize(self): |
||||
try: |
||||
statinfo = os.stat(self.__filename) |
||||
return statinfo.st_size |
||||
except OSError: |
||||
return -1 |
||||
|
||||
@property |
||||
def mtime(self): |
||||
try: |
||||
statinfo = os.stat(self.__filename) |
||||
return statinfo.st_mtime |
||||
except OSError: |
||||
return -1 |
||||
|
||||
@property |
||||
def tags(self): |
||||
return self.__tags |
||||
|
||||
@property |
||||
def valid(self): |
||||
if self.__valid == None: |
||||
try: |
||||
self.check() |
||||
except Animation.AnimationFileError: |
||||
pass |
||||
return self.__valid |
||||
|
||||
@property |
||||
def filename(self): |
||||
return self.__filename |
||||
|
||||
def check(self): |
||||
self.__valid = False |
||||
self.__tags = None |
||||
|
||||
anim = Animation.load(self.__filename) |
||||
|
||||
self.__valid = True |
||||
self.__tags = anim.tags |
||||
|
||||
|
||||
class AnimationDatabase(): |
||||
def __init__(self, animDir): |
||||
self.__animDir = animDir |
||||
self.__animInfos = [] |
||||
self.__lock = threading.Lock() |
||||
|
||||
def update(self): |
||||
with self.__lock: |
||||
oldAnimInfos = self.__animInfos |
||||
animInfos = [] |
||||
|
||||
for (dirpath, dirnames, filenames) in os.walk(self.__animDir): |
||||
files = map(lambda f: os.path.join(dirpath, f), filenames) |
||||
files = map(lambda f: os.path.relpath(os.path.join(dirpath, f), self.__animDir), filenames) |
||||
animInfos.extend(map(lambda f: AnimationInfo(f), files)) |
||||
#animInfos = filter(lambda a: a.valid, animInfos) |
||||
|
||||
newAnimInfos = [] |
||||
for animInfo in animInfos: |
||||
oldAnimInfo = find(lambda a: a.filename == animInfo.filename, oldAnimInfos) |
||||
if oldAnimInfo is not None and oldAnimInfo.mtime == animInfo.mtime: |
||||
newAnimInfos.append(oldAnimInfo) |
||||
else: |
||||
if animInfo.valid: |
||||
newAnimInfos.append(animInfo) |
||||
|
||||
self.__animInfos = newAnimInfos |
||||
|
||||
def findAnimation(self, filename): |
||||
with self.__lock: |
||||
animInfo = find(lambda a: a.filename == filename, self.__animInfos) |
||||
if animInfo is not None: |
||||
return animInfo |
||||
else: |
||||
raise Exception('animation not found!') |
||||
|
||||
def containsAnimation(self, filename): |
||||
with self.__lock: |
||||
animInfo = find(lambda a: a.filename == filename, self.__animInfos) |
||||
return (animInfo is not None) |
||||
|
||||
def getAllFilenames(self): |
||||
with self.__lock: |
||||
return map(lambda a: a.filename, self.__animInfos) |
||||
|
||||
|
||||
#class Playlist(list): |
||||
# def __init__(self, animationDatabase, filename=None): |
||||
# self.__animDb = animationDatabase |
||||
# self.__filename = filename |
||||
# if filename == None: |
||||
# self.append(self.__animDb.getAllFilenames()) |
||||
# else: |
||||
# f = open(filename, 'r') |
||||
# lines = f.read().split('\n') |
||||
# f.close() |
||||
# |
||||
# for line in lines: |
||||
# if self.__animDb.containsAnimation(line): |
||||
# self.append(line) |
||||
# |
||||
# def refresh(self): |
||||
# #f = open(filename, 'r') |
||||
# #lines = f.read().split('\n') |
||||
# #f.close() |
||||
# pass |
||||
# |
||||
# #for line in lines: |
||||
# # if self.__animDb.containsAnimation(line): |
||||
# # self.append(line) |
||||
|
||||
|
||||
|
||||
class PlayerThread(threading.Thread): |
||||
def __init__(self, database, playlist, output): |
||||
threading.Thread.__init__(self) |
||||
self.__playlist = playlist |
||||
self.__database = database |
||||
self.__playNext = [] |
||||
self.maxPlayNext = 3 |
||||
self.__output = output |
||||
self.shuffle = False |
||||
self.autoRefresh = True |
||||
self.loopTime = 10 |
||||
self.gap = 800 |
||||
self.__running = False |
||||
self.__player = Animation.AnimationPlayer() |
||||
self.__currentIndex = 0 |
||||
self.__currentFile = '' |
||||
|
||||
@property |
||||
def currentFile(self): |
||||
return self.__currentFile |
||||
|
||||
def playNext(self, next): |
||||
if len(self.__playNext) < self.__maxPlayNext: |
||||
self.__playNext.append(next) |
||||
else: |
||||
raise Exception |
||||
|
||||
def refreshPlaylist(self): |
||||
self.__database.update() |
||||
self.__playlist = self.__database.getAllFilenames() |
||||
|
||||
def loadPlaylistFile(self): |
||||
raise Exception('not yet implemented :(') |
||||
|
||||
def terminate(self): |
||||
self.__player.stop() |
||||
self.__running = False |
||||
|
||||
def run(self): |
||||
self.__running = True |
||||
while self.__running: |
||||
anim = None |
||||
if self.autoRefresh: # and self.__currentIndex == len(self.__playlist) - 1: |
||||
self.refreshPlaylist() |
||||
|
||||
if len(self.__playNext) == 0: |
||||
if len(self.__playlist) == 0: |
||||
print 'busywait!!' |
||||
continue |
||||
|
||||
if self.shuffle: |
||||
self.__currentFile = self.__playlist[random.randint(0, len(self.__playlist) - 1)] |
||||
else: |
||||
if self.__currentIndex >= len(self.__playlist): |
||||
self.__currentIndex = 0 |
||||
self.__currentFile = self.__playlist[self.__currentIndex] |
||||
self.__currentIndex += 1 |
||||
else: |
||||
self.__currentFile = self.__playNext.pop(0) |
||||
anim = Animation.load(self.__currentFile) |
||||
|
||||
count = 1 |
||||
if anim.tags.has_key('loop') and anim.tags['loop'].lower() in ['yes', 'true']: |
||||
if anim.duration < self.loopTime * 1000: |
||||
count = (self.loopTime * 1000) / anim.duration |
||||
|
||||
|
||||
print 'playing:', anim |
||||
print 'tags:', anim.tags |
||||
self.__player.play(anim, self.__output, count=count) |
||||
|
||||
if self.gap > 0 and self.__running: |
||||
self.__output.sendFrame(Frame.Frame(18,8)) |
||||
time.sleep(self.gap / 1000.0) |
||||
|
||||
|
||||
class PlayerControlClientHandler(asynchat.async_chat): |
||||
def __init__(self, sock, player): |
||||
asynchat.async_chat.__init__(self, sock=sock) |
||||
self.set_terminator('\n') |
||||
self.__buffer = '' |
||||
self.__player = player |
||||
|
||||
def collect_incoming_data(self, data): |
||||
self.__buffer += data |
||||
|
||||
def found_terminator(self): |
||||
args = self.__buffer.split(' ') |
||||
args = filter(lambda a: len(a) > 0, args) |
||||
if len(args) == 0: |
||||
return |
||||
self.__buffer = '' |
||||
cmd = args.pop(0) |
||||
|
||||
if cmd == 'refresh': |
||||
self.__playlist.refresh() |
||||
self.send('ok\n') |
||||
elif cmd == 'status': |
||||
self.send('playing %s\n' % (self.__player.getCurrentFile())) |
||||
elif cmd == 'getplaylist': |
||||
plist = '\n'.join(self.__playlist.getPlaylist()) |
||||
self.send(plist + '\n') |
||||
elif cmd == 'playnext' and len(args) == 1: |
||||
try: |
||||
self.__player.playNext(args[0]) |
||||
self.send('ok\n') |
||||
except Exception: |
||||
self.send('error\n') |
||||
elif cmd == 'loadplaylist' and len(args) == 1: |
||||
try: |
||||
if args[0] == '.': |
||||
self.__playlist.loadDir() |
||||
else: |
||||
self.__playlist.loadFile(args[0]) |
||||
self.send('ok\n') |
||||
except: |
||||
self.send('error\n') |
||||
elif cmd == 'quit': |
||||
self.send('bye\n') |
||||
self.close() |
||||
else: |
||||
self.send('unknown command\n') |
||||
|
||||
|
||||
class PlayerControlServer(asyncore.dispatcher): |
||||
def __init__(self, host, port, player, playlist): |
||||
asyncore.dispatcher.__init__(self) |
||||
self.create_socket(socket.AF_INET, socket.SOCK_STREAM) |
||||
self.set_reuse_addr() |
||||
self.bind((host, port)) |
||||
self.listen(5) |
||||
self.__player = player |
||||
self.__playlist = playlist |
||||
|
||||
def handle_accept(self): |
||||
pair = self.accept() |
||||
if pair is not None: |
||||
sock, addr = pair |
||||
print 'control connetcion from', addr |
||||
PlayerControlClientHandler(sock, self.__player, self.__playlist) |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# |
||||
# main |
||||
# |
||||
|
||||
cfg = ConfigParser.SafeConfigParser(CONFIG_DEFAULTS) |
||||
cfg.read('/var/tmp/blup2/blayer.cfg') |
||||
|
||||
try: |
||||
out = BlupOutput.getOutput(cfg.get('output', 'output')) |
||||
except ConfigParser.NoSectionError: |
||||
sys.err.write('config error: missing \'output\' section') |
||||
except ConfigParser.NoOptionError: |
||||
sys.err.write('config error: missing \'output\' option in \'output\' section') |
||||
|
||||
try: |
||||
animation_dir = cfg.get('player', 'animation_dir') |
||||
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): |
||||
animation_dir = '.' |
||||
|
||||
db = AnimationDatabase(animation_dir) |
||||
|
||||
print 'updating database...' |
||||
db.update() |
||||
print 'done with updating database...' |
||||
print 'updating database (again)...' |
||||
db.update() |
||||
print 'done with updating database...' |
||||
#print db.getAllFilenames() |
||||
|
||||
try: |
||||
playlist = Playlist(db, cfg.get('player', 'playlist')) |
||||
print 'file-playluist:', playlist |
||||
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): |
||||
playlist = Playlist(db) |
||||
print 'db-playluist:', playlist |
||||
|
||||
blayer = PlayerThread(db, playlist, out) |
||||
#sys.exit(0) |
||||
|
||||
try: |
||||
blayer.setLoopTime(cfg.getint('player', 'looptime')) |
||||
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): |
||||
pass |
||||
|
||||
try: |
||||
blayer.setShuffle(cfg.getboolean('player', 'shuffle')) |
||||
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): |
||||
pass |
||||
|
||||
try: |
||||
blayer.setAutoRefresh(cfg.getboolean('player', 'autorefresh')) |
||||
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): |
||||
pass |
||||
|
||||
try: |
||||
blayer.setMaxPlayNext(cfg.getint('player', 'maxplaynext')) |
||||
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): |
||||
pass |
||||
|
||||
blayer.start() |
||||
|
||||
try: |
||||
cser = PlayerControlServer(cfg.get('control', 'bind_host'), cfg.getint('control', 'bind_port'), blayer, playlist) |
||||
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): |
||||
cser = None |
||||
|
||||
|
||||
try: |
||||
|
||||
if cser is not None: |
||||
asyncore.loop() |
||||
else: |
||||
blayer.join() |
||||
except KeyboardInterrupt: |
||||
print 'nawk!!' |
||||
blayer.terminate() |
||||
|
||||
|
||||
|
@ -0,0 +1,68 @@
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/python |
||||
|
||||
import sys |
||||
import getopt |
||||
|
||||
import blup.animation |
||||
import blup.output |
||||
|
||||
def printUsage(errMsg=None): |
||||
if errMsg is not None: |
||||
print 'error: %s\n' % (errMsg) |
||||
print 'usage: %s [OPTIONS] FILENAME' % (sys.argv[0]) |
||||
print 'supported options:' |
||||
print ' -o OUTPUT where to output the frames (default: shell)' |
||||
print ' --output OUTPUT\n' |
||||
print ' -h print this text' |
||||
print ' --help' |
||||
|
||||
def main(): |
||||
try: |
||||
(opts, args) = getopt.gnu_getopt(sys.argv, 'ho:', ['help', 'output=']) |
||||
opts = dict(opts) |
||||
except getopt.GetoptError as e: |
||||
printUsage(e.msg) |
||||
sys.exit(1) |
||||
|
||||
if opts.has_key('--help'): |
||||
printUsage() |
||||
sys.exit(0) |
||||
|
||||
if opts.has_key('-o'): |
||||
output = opts['-o'] |
||||
elif opts.has_key('--output'): |
||||
output = opts['--output'] |
||||
else: |
||||
output = 'shell' |
||||
|
||||
try: |
||||
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: |
||||
printUsage() |
||||
sys.exit(1) |
||||
|
||||
try: |
||||
anim = blup.animation.load(args[1]) |
||||
except blup.animation.AnimationFileError: |
||||
print 'could not load animation' |
||||
sys.exit(1) |
||||
|
||||
player = blup.animation.AnimationPlayer() |
||||
try: |
||||
while True: |
||||
player.play(anim, out) |
||||
except KeyboardInterrupt: |
||||
sys.exit(0) |
||||
|
||||
if __name__ == "__main__": |
||||
main() |
||||
|
@ -0,0 +1,167 @@
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env python3 |
||||
|
||||
import threading |
||||
import socket |
||||
import time |
||||
import getopt |
||||
import sys |
||||
|
||||
import blup.BLP |
||||
import blup.output |
||||
|
||||
|
||||
class BLPStreamReceiverThread(threading.Thread): |
||||
def __init__(self, addr, port, hub, protocol=blup.BLP.PROTO_BLP): |
||||
threading.Thread.__init__(self) |
||||
self.__addr = addr |
||||
self.__port = port |
||||
self.__hub = hub |
||||
self.__protocol = protocol |
||||
self.__running = False |
||||
|
||||
def run(self): |
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
||||
sock.bind((self.__addr, self.__port)) |
||||
sock.settimeout(1) |
||||
|
||||
self.__running = True |
||||
while self.__running: |
||||
try: |
||||
data, addr = sock.recvfrom(2048) |
||||
except socket.timeout: |
||||
continue |
||||
|
||||
try: |
||||
frame = blup.BLP.blp2frame(data, protocol=self.__protocol) |
||||
except ValueError: |
||||
print('got an invalid frame from', addr) |
||||
else: |
||||
self.__hub.sendFrame(frame, self) |
||||
|
||||
def terminate(self): |
||||
self.__running = False |
||||
|
||||
class BLPHub(): |
||||
def __init__(self, output): |
||||
self.__output = output |
||||
self.__streams = {} |
||||
self.__lock = threading.Lock() |
||||
self.__lastFrameTimeout = -1 |
||||
self.__lastFramePriority = -1 |
||||
|
||||
def sendFrame(self, frame, stream): |
||||
with self.__lock: |
||||
if stream not in self.__streams.keys(): |
||||
return False |
||||
|
||||
priority = self.__streams[stream]['priority'] |
||||
currentTime = int(time.time() * 1000) |
||||
|
||||
accept = False |
||||
if priority >= self.__lastFramePriority: |
||||
accept = True |
||||
else: |
||||
if currentTime > self.__lastFrameTimeout: |
||||
accept = True |
||||
|
||||
if accept: |
||||
self.__lastFramePriority = priority |
||||
self.__lastFrameTimeout = ( |
||||
currentTime + self.__streams[stream]['timeout'] |
||||
) |
||||
self.__output.sendFrame(frame) |
||||
return True |
||||
|
||||
return False |
||||
|
||||
def addStream(self, stream, priority, timeout): |
||||
if stream not in self.__streams.keys(): |
||||
self.__streams[stream] = {'priority': priority, 'timeout': timeout} |
||||
|
||||
def printUsage(errMsg=None): |
||||
if errMsg is not None: |
||||
print('error: %s\n' % (errMsg)) |
||||
print('usage: %s [OPTIONS] INPUTSTREAM ...' % (sys.argv[0])) |
||||
print('where INPUTSTREAM is given as HOST:PORT:PRIORITY:TIMEOUT') |
||||
print('supported options:') |
||||
print(' -o OUTPUT where to put the received frames') |
||||
print(' --eblp use EBLP instead of BLP') |
||||
print(' --e3blp use E3BLP instead of BLP') |
||||
print(' -h print this text') |
||||
print(' --help') |
||||
|
||||
def main(): |
||||
(opts, args) = getopt.gnu_getopt(sys.argv, 'hs:o:', |
||||
['help', 'eblp', 'e3blp']) |
||||
opts = dict(opts) |
||||
|
||||
if '--help' in opts or '-h' in opts: |
||||
printUsage() |
||||
sys.exit(0) |
||||
|
||||
if '-o' in opts: |
||||
output = opts['-o'] |
||||
else: |
||||
output = 'shell' |
||||
|
||||
if '--eblp' in opts: |
||||
protocol = blup.BLP.PROTO_EBLP |
||||
elif '--e3blp' in opts: |
||||
protocol = blup.BLP.PROTO_E3BLP |
||||
else: |
||||
protocol = blup.BLP.PROTO_BLP |
||||
|
||||
if '--eblp' in opts and '--e3blp' in opts: |
||||
printUsage('please only specify one of --eblp or --e3blp') |
||||
sys.exit(1) |
||||
|
||||
streams = [] |
||||
for arg in args[1:]: |
||||
try: |
||||
(host, port, priority, timeout) = arg.split(':') |
||||
port = int(port) |
||||
priority = int(priority) |
||||
timeout = int(timeout) |
||||
if port <= 0 or timeout < 0: |
||||
raise ValueError |
||||
except ValueError as e: |
||||
printUsage('illegal stream specification') |
||||
sys.exit(1) |
||||
streams.append((host, port, priority, timeout)) |
||||
|
||||
if len(streams) == 0: |
||||
printUsage('please specify at least one stream') |
||||
sys.exit(1) |
||||
|
||||
try: |
||||
out = blup.output.getOutput(output) |
||||
except ValueError: |
||||
printUsage('could not initialize output') |
||||
sys.exit(1) |
||||
|
||||
hub = BLPHub(out) |
||||
|
||||
streamThreads = [] |
||||
firstThread = None |
||||
for (host, port, priority, timeout) in streams: |
||||
stream = BLPStreamReceiverThread(host, port, hub, protocol) |
||||
hub.addStream(stream, priority, timeout) |
||||
if firstThread is None: |
||||
firstThread = stream |
||||
else: |
||||
streamThreads.append(stream) |
||||
stream.start() |
||||
|
||||
try: |
||||
firstThread.run() |
||||
except KeyboardInterrupt: |
||||
if len(streamThreads) > 0: |
||||
print('waiting for listeners to terminate...') |
||||
for stream in streamThreads: |
||||
stream.terminate() |
||||
for stream in streamThreads: |
||||
stream.join() |
||||
|
||||
if __name__ == '__main__': |
||||
main() |
||||
|
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash |
||||
|
||||
python blphub.py -o blp:bastel0:4242 localhost:4242:1:1000 localhost:42421:2:3500 |
||||
|
@ -0,0 +1,88 @@
@@ -0,0 +1,88 @@
|
||||
|
||||
import socket |
||||
import getopt |
||||
import sys |
||||
|
||||
from blup.BLP import BLPServer |
||||
import blup.BLP |
||||
import blup.output |
||||
|
||||
|
||||
def printUsage(errMsg=None): |
||||
if errMsg is not None: |
||||
print 'error: %s\n' % (errMsg) |
||||
print 'usage: %s [OPTIONS]' % (sys.argv[0]) |
||||
print 'supported options:' |
||||
print ' -a ADDR address to listen for packets (default: 127.0.0.1)' |
||||
print ' --address ADDR\n' |
||||
print ' -p PORT port to listen for packets (default: 4242)' |
||||
print ' --port PORT\n' |
||||
print ' -o OUTPUT where to put the recieved frames (default: shell)' |
||||
print ' --output OUTPUT\n' |
||||
print ' --eblp use EBLP instead of BLP\n' |
||||
print ' --e3blp use E3BLP instead of BLP\n' |
||||
print ' -h print this text' |
||||
print ' --help' |
||||
|
||||
|
||||
def main(): |
||||
try: |
||||
(opts, args) = getopt.gnu_getopt(sys.argv, 'ha:p:o:', ['help', 'address=', 'port=', 'output=', 'eblp', 'e3blp']) |
||||
opts = dict(opts) |
||||
except getopt.GetoptError as e: |
||||
printUsage(e.msg) |
||||
sys.exit(1) |
||||
|
||||
if opts.has_key('--help'): |
||||
printUsage() |
||||
sys.exit(0) |
||||
|
||||
if opts.has_key('-a'): |
||||
addr = opts['-a'] |
||||
elif opts.has_key('--address'): |
||||
addr = opts['--address'] |
||||
else: |
||||
addr = '127.0.0.1' |
||||
|
||||
if opts.has_key('-p'): |
||||
port = opts['-p'] |
||||
elif opts.has_key('--port'): |
||||
port = opts['--port'] |
||||
else: |
||||
port = 4242 |
||||
|
||||
if opts.has_key('-o'): |
||||
output = opts['-o'] |
||||
elif opts.has_key('--output'): |
||||
output = opts['--output'] |
||||
else: |
||||
output = 'shell' |
||||
|
||||
if opts.has_key('--eblp'): |
||||
protocol = blup.BLP.PROTO_EBLP |
||||
elif opts.has_key('--e3blp'): |
||||
protocol = blup.BLP.PROTO_E3BLP |
||||
else: |
||||
protocol = blup.BLP.PROTO_BLP |
||||
|
||||
if opts.has_key('--eblp') and opts.has_key('--e3blp'): |
||||
printUsage('please only specify one of --eblp or --e3blp') |
||||
sys.exit(1) |
||||
|
||||
try: |
||||
port = int(port) |
||||
except ValueError: |
||||
printUsage('invalid port specified') |
||||
sys.exit(1) |
||||
|
||||
out = blup.output.getOutput(output) |
||||
srv = BLPServer(output=out, addr=addr, port=port, protocol=protocol) |
||||
|
||||
try: |
||||
srv.serve() |
||||
except KeyboardInterrupt: |
||||
pass |
||||
|
||||
if __name__ == '__main__': |
||||
main() |
||||
|
@ -0,0 +1,174 @@
@@ -0,0 +1,174 @@
|
||||
""" |
||||
This is a BLP and EBLP implementation as described here: |
||||
https://wiki.blinkenarea.org/index.php/BlinkenlightsProtocolEnglish |
||||
|
||||
""" |
||||
|
||||
import threading |
||||
import socket |
||||
import struct |
||||
|
||||
from .frame import Frame |
||||
from .frame import FrameDimension |
||||
|
||||
BLP_MAGIC = b'\xde\xad\xbe\xef' |
||||
EBLP_MAGIC = b'\xfe\xed\xbe\xef' |
||||
E3BLP_MAGIC = b'\xca\xfe\xbe\xef' |
||||
|
||||
PROTO_BLP = 0 |
||||
PROTO_EBLP = 1 |
||||
PROTO_E3BLP = 2 |
||||
PROTOCOLS = [PROTO_BLP, PROTO_EBLP, PROTO_E3BLP] |
||||
|
||||
def frame2blp(frame, protocol=PROTO_BLP, depth=16): |
||||
if protocol not in PROTOCOLS: |
||||
raise ValueError('Unknown protocol: %d' % (protocol)) |
||||
if frame.depth != 2 and protocol == PROTO_BLP: |
||||
raise ValueError('BLP does not support greyscale.') |
||||
if frame.channels != 3 and protocol == PROTO_E3BLP: |
||||
raise ValueError('E3BLP does only support 3-channel frames.') |
||||
|
||||
(w, h) = frame.size |
||||
|
||||
# magic value |
||||
if protocol == PROTO_BLP: |
||||
packet = BLP_MAGIC |
||||
elif protocol == PROTO_EBLP: |
||||
packet = EBLP_MAGIC |
||||
elif protocol == PROTO_E3BLP: |
||||
packet = E3BLP_MAGIC |
||||
|
||||
# frame number, not yet implemented |
||||
packet += b'\x00\x00\x00\x00' |
||||
|
||||
# frame dimension |
||||
packet += struct.pack('>HH', w, h) |
||||
|
||||
for y in range(h): |
||||
for x in range(w): |
||||
if protocol == PROTO_BLP: |
||||
if frame.getPixel(x, y) == 1: |
||||
packet += b'\x01' |
||||
else: |
||||
packet += b'\x00' |
||||
elif protocol == PROTO_EBLP: |
||||
packet += struct.pack('B', frame.getPixel(x, y)) |
||||
elif protocol == PROTO_E3BLP: |
||||
(r,g,b) = frame.getPixel(x, y) |
||||
packet += struct.pack('BBB', r, g, b) |
||||
|
||||
return packet |
||||
|
||||
def blp2frame(packet, protocol=PROTO_BLP, depth=None): |
||||
if protocol not in PROTOCOLS: |
||||
raise ValueError('Unknown protocol: %d' % (protocol)) |
||||
if len(packet) < 13: |
||||
raise ValueError('packet is too short') |
||||
if ( (packet[0:4] != BLP_MAGIC and protocol == PROTO_BLP) or |
||||
(packet[0:4] != EBLP_MAGIC and protocol == PROTO_EBLP) or |
||||
(packet[0:4] != E3BLP_MAGIC and protocol == PROTO_E3BLP) ): |
||||
raise ValueError('MAGIC does not match') |
||||
|
||||
w = (packet[8]<<8) + packet[9] |
||||
h = (packet[10]<<8) + packet[11] |
||||
|
||||
if ( (protocol == PROTO_E3BLP and len(packet) != (w*h*3 + 12)) or |
||||
(protocol in [PROTO_BLP, PROTO_EBLP] and len(packet) != (w*h + 12)) ): |
||||
print(len(packet)) |
||||
raise ValueError('packet size does not match') |
||||
|
||||
if protocol == PROTO_BLP: |
||||
frm = Frame(FrameDimension(w, h, 2, 1)) |
||||
elif protocol == PROTO_EBLP: |
||||
if depth is None: |
||||
depth = 16 |
||||
frm = Frame(FrameDimension(w, h, depth, 1)) |
||||
elif protocol == PROTO_E3BLP: |
||||
if depth is None: |
||||
depth = 8 |
||||
frm = Frame(FrameDimension(w, h, depth, 3)) |
||||
|
||||
pixels = [] |
||||
for y in range(h): |
||||
row = [] |
||||
for x in range(w): |
||||
if protocol == PROTO_BLP: |
||||
if packet[12 + y*w + x] == '\x01': |
||||
#frm.setPixel(x, y, 1) |
||||
row.append(1) |
||||
else: |
||||
#frm.setPixel(x, y, 0) |
||||
row.append(0) |
||||
elif protocol == PROTO_EBLP: |
||||
row.append(packet[12 + y*w + x]) |
||||
elif protocol == PROTO_E3BLP: |
||||
pos = 12 + (y*w + x) * 3 |
||||
r = packet[pos] |
||||
g = packet[pos + 1] |
||||
b = packet[pos + 2] |
||||
row.append((r,g,b)) |
||||
pixels.append(row) |
||||
frm.pixels = pixels |
||||
|
||||
return frm |
||||
|
||||
class BLPServer: |
||||
def __init__(self, addr='127.0.0.1', port=4242, output=None, |
||||
protocol=PROTO_BLP): |
||||
if protocol not in PROTOCOLS: |
||||
raise ValueError('Unknown protocol: %d' % (protocol)) |
||||
|
||||
self.__addr = addr |
||||
self.__port = port |
||||
self.__output = output |
||||
self.__sock = None |
||||
self.__protocol = protocol |
||||
|
||||
if not output: |
||||
raise ValueError('serving without output isn\'t supported, yet') |
||||
|
||||
def bind(self): |
||||
self.__sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
||||
self.__sock.bind((self.__addr, self.__port)) |
||||
|
||||
def handle(self): |
||||
if self.__sock is None: |
||||
return |
||||
|
||||
data, addr = self.__sock.recvfrom(1024) |
||||
try: |
||||
frame = blp2frame(data, protocol=self.__protocol) |
||||
except ValueError as e: |
||||
print('got an invalid frame from', addr) |
||||
print(e) |
||||
return |
||||
else: |
||||
self.__output.sendFrame(frame) |
||||
|
||||
def serve(self): |
||||
self.bind() |
||||
while True: |
||||
self.handle() |
||||
|
||||
class BLPServerThread(threading.Thread, BLPServer): |
||||
def __init__(self, addr, port, output, protocol=PROTO_BLP): |
||||
if protocol not in PROTOCOLS: |
||||
raise ValueError('Unknown protocol: %d' % (protocol)) |
||||
|
||||
threading.Thread.__init__(self) |
||||
BLPServer.__init__(self, addr, port, output, protocol) |
||||
self.__running = False |
||||
|
||||
def run(self): |
||||
self.bind() |
||||
self.__running = True |
||||
while self.__running: self.handle() |
||||
|
||||
def serve(self): |
||||
self.start() |
||||
|
||||
def terminate(self): |
||||
self.__running = False |
||||
|
||||
|
||||
|
@ -0,0 +1,344 @@
@@ -0,0 +1,344 @@
|
||||
""" |
||||
This module is part of the 'blup' package and defines an animation class along |
||||
with the necessary functions to read animations from files. |
||||
|
||||
""" |
||||
|
||||
import re |
||||
import time |
||||
import xml.etree.ElementTree as ET |
||||
|
||||
from frame import Frame |
||||
from frame import FrameDimension |
||||
|
||||
|
||||
class AnimationFileError(Exception): |
||||
def __init__(self, value): |
||||
self.__value = value |
||||
def __str__(self): |
||||
return repr(self.__value) |
||||
|
||||
class AnimationFrame(Frame): |
||||
""" This class constitutes a single frame of an animation. """ |
||||
|
||||
def __init__(self, dimension, delay=100): |
||||
""" Initialize the frame with given dimension and delay. """ |
||||
Frame.__init__(self, dimension) |
||||
self.delay = int(delay) |
||||
|
||||
class AnimationIterator(object): |
||||
""" |
||||
This iterator can be used to loop over the animation's frames using |
||||
python's 'for' statement. |
||||
|
||||
""" |
||||
|
||||
def __init__(self, animation): |
||||
""" Initialize the iterator. """ |
||||
self.__animation = animation |
||||
self.__pos = 0 |
||||
|
||||
def next(self): |
||||
""" Return the next frame (if available). """ |
||||
if self.__pos >= len(self.__animation): |
||||
raise StopIteration |
||||
else: |
||||
self.__pos += 1 |
||||
return self.__animation[self.__pos - 1] |
||||
|
||||
class Animation(object): |
||||
""" This class constitutes an animation. """ |
||||
|
||||
def __init__(self, dimension): |
||||
""" Initialize the animation (without frames). """ |
||||
self.__dimension = dimension |
||||
self.__frames = [] |
||||
self.tags = {} |
||||
|
||||
|
||||
@property |
||||
def dimension(self): |
||||
return self.__dimension |
||||
|
||||
@property |
||||
def width(self): |
||||
return self.__dimension.width |
||||
@property |
||||
def height(self): |
||||
return self.__dimension.height |
||||
@property |
||||
def depth(self): |
||||
return self.__dimension.depth |
||||
@property |
||||
def channels(self): |
||||
return self.__dimension.channels |
||||
|
||||
@property |
||||
def duration(self): |
||||
""" Compute the duration of the animation. """ |
||||
return sum(map(lambda f: f.delay, self.__frames)) |
||||
|
||||
def addFrame(self, frame): |
||||
""" Append a frame to the animation. """ |
||||
if not isinstance(frame, Frame): |
||||
raise ValueError('frame is no \'Frame\' instance') |
||||
|
||||
if self.dimension != frame.dimension: |
||||
raise ValueError(('frame dimension %s does not match animation ' + |
||||
'dimension %s' ) |
||||
% (str(self.dimension), str(frame.dimension))) |
||||
|
||||
self.__frames.append(frame) |
||||
|
||||
def __iter__(self): |
||||
""" Return an iterator to loop over the animation's frames. """ |
||||
return AnimationIterator(self) |
||||
|
||||
def __len__(self): |
||||
""" Return the number of frames. """ |
||||
return len(self.__frames) |
||||
|
||||
def __getitem__(self, i): |
||||
""" Retrieve a certain frame from the animation. """ |
||||
if i < 0 or i >= len(self): |
||||
raise IndexError('frame index out of bounds') |
||||
return self.__frames[i] |
||||
|
||||
def __delitem__(self, i): |
||||
""" Delete a certain frame from the animation. """ |
||||
if i < 0 or i >= len(self): |
||||
raise IndexError('frame index out of bounds') |
||||
del self.__frames[i] |
||||
|
||||
|
||||
class AnimationPlayer(object): |
||||
""" |
||||
This class can be used to play an Animation using an output class that |
||||
implements the 'sendFrame()' function. |
||||
|
||||
""" |
||||
|
||||
def __init__(self): |
||||
""" Initialize the player. """ |
||||
self.__playing = False |
||||
self.__elapsed = 0 |
||||
|
||||
@property |
||||
def elapsed(self): |
||||
return self.__elapsed |
||||
|
||||
def play(self, animation, output, count=1): |
||||
""" Play the animation 'count' times on 'output'. """ |
||||
self.__playing = True |
||||
self.__elapsed = 0 |
||||
for i in range(count): |
||||
for frame in animation: |
||||
output.sendFrame(frame) |
||||
|
||||
delay = frame.delay / 1000.0 |
||||
time.sleep(delay) |
||||
self.__elapsed += delay |
||||
if not self.__playing: |
||||
break |
||||
if not self.__playing: |
||||
break |
||||
|
||||
def stop(self): |
||||
""" Stop the player, if playing. """ |
||||
self.__playing = False |
||||
|
||||
def loadBlm(filename): |
||||
""" Parse a blm file and return an Animation object. """ |
||||
|
||||
f = open(filename, 'r') |
||||
|
||||
w = h = 0 |
||||
tags = {} |
||||
|
||||
header = True |
||||
framedelay = 100 |
||||
frame = [] |
||||
animframes = [] |
||||
for line in f: |
||||
if header: |
||||
m = re.match('^\s*#\s+BlinkenLights Movie (\d+)x(\d+)', line) |
||||
if m: |
||||
w = int(m.group(1)) |
||||
h = int(m.group(2)) |
||||
continue |
||||
|
||||
m = re.match('^\s*#\s+(.+)\s+=\s+(.+)\s+$', line) |
||||
if m: |
||||
key = m.group(1) |
||||
val = m.group(2) |
||||
tags[key] = val |
||||
continue |
||||
|
||||
m = re.match('^@(\d+)\s+$', line) |
||||
if m: |
||||
if header: |
||||
header = False |
||||
framedelay = int(m.group(1)) |
||||
if len(frame) > 0: |
||||
if h == 0: |
||||
h = len(frame) |
||||
else: |
||||
if len(frame) != h: |
||||
raise AnimationFileError( |
||||
'frame height does not match specified or ' + |
||||
'previous height') |
||||
|
||||
frm = AnimationFrame(FrameDimension(w, h, 2, 1), |
||||
delay=framedelay) |
||||
frm.pixels = frame |
||||
print frame.pixels |
||||
animframes.append(frm) |
||||
frame = [] |
||||
continue |
||||
|
||||
if not header: |
||||
if line.strip() == '' or line.strip().startswith('#'): |
||||
continue |
||||
|
||||
if re.match('^(0|1)+$', line): |
||||
line = line.strip() |
||||
if w == 0: |
||||
w = len(line) |
||||
else: |
||||
if len(line) != w: |
||||
raise AnimationFileError( |
||||
'frame width does not match specified or ' + |
||||
'previous width') |
||||
row = map(lambda c: int(c), list(line)) |
||||
frame.append(row) |
||||
|
||||
if h != 0 and len(frame) == h: |
||||
frm = AnimationFrame(FrameDimension(w, h, 2, 1), |
||||
delay=framedelay) |
||||
frm.pixels = frame |
||||
animframes.append(frm) |
||||
frame = [] |
||||
|
||||
continue |
||||
|
||||
f.close() |
||||
|
||||
if len(frame) > 0: |
||||
frm = AnimationFrame(FrameDimension(w, h, 2, 1), delay=framedelay) |
||||
frm.pixels = frame |
||||
animframes.append(frm) |
||||
|
||||
if len(animframes) == 0: |
||||
raise AnimationFileError('no frames found in this file') |
||||
|
||||
|
||||
a = Animation(FrameDimension(w, h, 2, 1)) |
||||
a.tags = tags |
||||
for f in animframes: |
||||
a.addFrame(f) |
||||
|
||||
return a |
||||
|
||||
|
||||
def loadBml(filename): |
||||
""" Parse a bml file and return an Animation object. """ |
||||
|
||||
try: |
||||
tree = ET.parse(filename) |
||||
except ET.ParseError: |
||||
raise AnimationFileError('invalid xml') |
||||
root = tree.getroot() |
||||
|
||||
if root.tag != 'blm': |
||||
raise AnimationFileError('the root tag needs to be a \'blm\' tag') |
||||
|
||||
try: |
||||
width = int(root.attrib['width']) |
||||
height = int(root.attrib['height']) |
||||
bits = int(root.attrib['bits']) |
||||
channels = int(root.attrib['channels']) |
||||
|
||||
dim = FrameDimension(width, height, 2**bits, channels) |
||||
except KeyError as e: |
||||
raise AnimationFileError('the root tag does not contain a ' + |
||||
'\'%s\' attribute.' % (e[0])) |
||||
|
||||
if channels not in [1, 3]: |
||||
raise AnimationFileError('unsupported channel count') |
||||
|
||||
tags = {} |
||||
frames = [] |
||||
|
||||
for child in root: |
||||
if child.tag == 'header': |
||||
for headerchild in child: |
||||
tags[headerchild.tag] = headerchild.text |
||||
# TODO (maybe) raise an exception in case no 'title' is given |
||||
|
||||
elif child.tag == 'frame': |
||||
delay = int(child.attrib['duration']) |
||||
currentframe = AnimationFrame(dim, delay) |
||||
rows = [] |
||||
for row in child: |
||||
charsPerPixel = channels |
||||
if bits > 4: |
||||
charsPerPixel *= 2 |
||||
|
||||
rowPixels = [] |
||||
for i in xrange(0, width * charsPerPixel, charsPerPixel): |
||||
if channels == 1: |
||||
pixel = row.text[i:(i + charsPerPixel)] |
||||
rowPixels.append(int(pixel, 16)) |
||||
elif channels == 3: |
||||
charsPerColor = charsPerPixel / 3 |
||||
r = row.text[i:(i + charsPerColor)] |
||||
g = row.text[(i + charsPerColor):(i + 2*charsPerColor)] |
||||
b = row.text[(i + 2*charsPerColor):(i + charsPerPixel)] |
||||
rowPixels.append((int(r, 16), int(g, 16), int(b, 16))) |
||||
rows.append(rowPixels) |
||||
currentframe.pixels = rows |
||||
frames.append(currentframe) |
||||
|
||||
a = Animation(dim) |
||||
a.tags = tags |
||||
for f in frames: |
||||
a.addFrame(f) |
||||
|
||||
return a |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def load(filename): |
||||
""" |
||||
(Try to) parse the given file using all available parser functions and |
||||
return the Animation afterwards. |
||||
|
||||
""" |
||||
|
||||
loaders = [ loadBlm, loadBml ] |
||||
|
||||
anim = None |
||||
for loader in loaders: |
||||
try: |
||||
anim = loader(filename) |
||||
except AnimationFileError as e: |
||||
pass |
||||
if anim: |
||||
break |
||||
|
||||
if not anim: |
||||
raise AnimationFileError('file could not be read') |
||||
|
||||
return anim |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,97 @@
@@ -0,0 +1,97 @@
|
||||
""" |
||||
This module is part of the 'blup' package and defines a font class as well as |
||||
some methods to load fonts from files and draw text on frames. |
||||
|
||||
""" |
||||
|
||||
import re |
||||
|
||||
class FontFileError(Exception): |
||||
def __init__(self, value): |
||||
self.__value = value |
||||
def __str__(self): |
||||
return repr(self.__value) |
||||
|
||||
class Font(object): |
||||
|
||||
def __init__(self): |
||||
self.__chars = {} |
||||
self.__spacing = 1 |
||||
self.__height = -1 |
||||
|
||||
@property |
||||
def spacing(self): |
||||
return self.__spacing |
||||
|
||||
@spacing.setter |
||||
def spacing(self, value): |
||||
if type(self.value) == int and self.value > 0: |
||||
self.__spacing = value |
||||
else: |
||||
raise ValueError('illegal spacing given') |
||||
|
||||
@property |
||||
def height(self): |
||||
if self.__height == -1: |
||||
raise ValueError('this font does not contain any characters') |
||||
else: |
||||
return self.__height |
||||
|
||||
def __setitem__(self, item, value): |
||||
height = len(value) |
||||
if self.__height == -1: |
||||
self.__height = height |
||||
elif height != self.__height: |
||||
raise ValueError('character height does not match font height') |
||||
|
||||
if len(set(map(len, value))) != 1: |
||||
raise ValueError('character lines differ in width') |
||||
|
||||
allowedValues = set([0,1]) |
||||
for line in value: |
||||
if set(line) != allowedValues: |
||||
raise ValueError('character contains invalid pixel value') |
||||
|
||||
self.__chars[item] = value |
||||
|
||||
def __getitem__(self, item): |
||||
return self.__chars[item] |
||||
|
||||
|
||||
charPixels = None |
||||
def load(filename): |
||||
f = open(filename, 'r') |
||||
data = f.read() |
||||
|
||||
f = Font() |
||||
for line in f: |
||||
m = re.match('^\s*§(\d+)\s*$', line) |
||||
if m: |
||||
f.spacing = int(m.group(1)) |
||||
continue |
||||
|
||||
m = re.match('^\s*@(\d+)\|(\d+)\s*$', line): |
||||
if m: |
||||
charCode = int(m.group(1)) |
||||
charWidth = int(m.group(2)) |
||||
charPixels = [] |
||||
continue |
||||
|
||||
m = re.match('^\s*([01]+)\s*$', line): |
||||
if m and charPixels != None: |
||||
charRow = m.group(1) |
||||
if len(charRow) < charWidth: |
||||
raise FontFileError('char row does not contain enough pixels') |
||||
row = [] |
||||
for i in xrange(charWidth): |
||||
row.append(int(charRow[i])) |
||||
charPixels.append(row) |
||||
|
||||
continue |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,152 @@
@@ -0,0 +1,152 @@
|
||||
""" |
||||
This module is part of the 'blup' package and contains the definition of a |
||||
basic Frame class. |
||||
|
||||
""" |
||||
|
||||
class FrameDimension(object): |
||||
""" This class serves as container for the dimensions of a frame. """ |
||||
|
||||
def __init__(self, width=18, height=8, depth=2, channels=1): |
||||
self.width = width |
||||
self.height = height |
||||
self.depth = depth |
||||
self.channels = channels |
||||
|
||||
def size(self): |
||||
return (self.width, self.height) |
||||
|
||||
def depth(self): |
||||
return self.depth |
||||
|
||||
def channels(self): |
||||
return self.channels |
||||
|
||||
def __eq__(self, other): |
||||
if ( self.width == other.width and self.height == other.height and |
||||
self.depth == other.depth and self.channels == other.channels ): |
||||
return True |
||||
else: |
||||
return False |
||||
|
||||
def __ne__(self, other): |
||||
return not self.__eq__(other) |
||||
|
||||
def __str__(self): |
||||
return "(%d, %d, %d, %d)" % (self.width, self.height, self.depth, |
||||
self.channels) |
||||
|
||||
class Frame(object): |
||||
""" This class describes a single frame. """ |
||||
|
||||
def __init__(self, dimension): |
||||
""" |
||||
Initialize the frame with the given dimensions. The 'depth' parameter |
||||
describes, how many different values a single pixel can have. |
||||
|
||||
""" |
||||
self.__dimension = dimension |
||||
width = dimension.width |
||||
height = dimension.height |
||||
if dimension.channels == 1: |
||||
self.__pixels = [ [ 0 ] * width for i in range(height) ] |
||||
else: |
||||
self.__pixels = [ [ tuple([0]*dimension.channels) ] * width for i in range(height) ] |
||||
|
||||
@property |
||||
def dimension(self): |
||||
return self.__dimension |
||||
|
||||
@property |
||||
def size(self): |
||||
return (self.__dimension.width, self.__dimension.height) |
||||
@property |
||||
def depth(self): |
||||
return self.__dimension.depth |
||||
@property |
||||
def channels(self): |
||||
return self.__dimension.channels |
||||
|
||||
@property |
||||
def pixels(self): |
||||
return self.__pixels |
||||
|
||||
@pixels.setter |
||||
def pixels(self, pixels): |
||||
if len(pixels) != self.__dimension.height: |
||||
raise ValueError('not enough rows given') |
||||
widths = set(map(lambda r: len(r), pixels)) |
||||
if len(widths) != 1: |
||||
raise ValueError('rows are not of the same length') |
||||
if widths.pop() != self.__dimension.width: |
||||
raise ValueError('rows are too short') |
||||
|
||||
# TODO check depth |
||||
# TODO check number of channels |
||||
|
||||
self.__pixels = pixels |
||||
|
||||
def transform(self, depth): |
||||
""" Transform the frame to a frame with a different depth. """ |
||||
oldmaxval = self.__dimension.depth - 1 |
||||
newmaxval = depth - 1 |
||||
factor = (newmaxval*1.0) / (oldmaxval*1.0) |
||||
newdim = FrameDimension(self.__dimension.width, |
||||
self.__dimension.height, |
||||
depth, |
||||
self.__dimension.channels) |
||||
newframe = Frame(newdim) |
||||
|
||||
transformfkt = lambda p: int(round(p * factor)) |
||||
for y in range(self.__dimension.height): |
||||
if self.__dimension.channels == 1: |
||||
newframe.__pixels[y] = map(transformfkt, self.__pixels[y]) |
||||
else: |
||||
for x in range(self.__dimension.width): |
||||
newframe.__pixels[y][x] = tuple(map(transformfkt, p)) |
||||
|
||||
return newframe |
||||
|
||||
def getPixel(self, x, y): |
||||
""" Return the pixel value at (x, y). """ |
||||
return self.__pixels[y][x] |
||||
|
||||
def setPixel(self, x, y, value): |
||||
""" Set the pixel value at (x, y). """ |
||||
if self.__dimension.channels == 1: |
||||
if value < 0 or value >= self.__dimension.depth: |
||||
raise ValueError('pixel value not in depth range') |
||||
else: |
||||
if len(value) != self.__dimension.channels: |
||||
raise ValueError('channel count does not match') |
||||
for v in value: |
||||
if v < 0 or v >= self.__dimension.depth: |
||||
raise ValueError('pixel value not in depth range') |
||||
self.__pixels[y][x] = value |
||||
|
||||
|
||||
def getPixelsAsBytes(self): |
||||
""" Return the frame as a byte string. """ |
||||
if self.__dimension.channels != 1: |
||||
raise Exception('cannot convert a multi-channel frame') |
||||
|
||||
bytes = '' |
||||
for y in range(self.__dimension.width): |
||||
for x in range(self.__dimension.height): |
||||
bytes += chr(self.__pixels[x][y]) |
||||
return bytes |
||||
|
||||
def invert(self): |
||||
""" Ivert the frame. """ |
||||
for y in range(self.__dimension.height): |
||||
for x in range(self.__dimension.width): |
||||
if self.__dimension.channels == 1: |
||||
self.__pixels[x][y] = ((self.__dimension.depth - 1) - |
||||
self.__pixels[x][y]) |
||||
else: |
||||
self.__pixels[y][x] = tuple(map( |
||||
lambda x: ((self.depth - 1) - x), |
||||
self.__pixels[y][x])) |
||||
|
||||
|
||||
|
@ -0,0 +1,444 @@
@@ -0,0 +1,444 @@
|
||||
""" |
||||
This module is part of the 'blup' package and provides several output modules |
||||
to send frames as well as a mechanism to create them from a string |
||||
specification. |
||||
|
||||
""" |
||||
|
||||
import re |
||||
import serial |
||||
import socket |
||||
|
||||
from blup import BLP |
||||
|
||||
|
||||
# |
||||
# ---- exceptions ---- |
||||
# |
||||
|
||||
class IllegalOutputSpecificationError(Exception): |
||||
""" |
||||
Will be thrown when a string is given to getOutput (see below) that is not |
||||
matched by any output modules regex. |
||||
|
||||
""" |
||||
def __str__(self): |
||||
return __class__.__name__ |
||||
|
||||
|
||||
# |
||||
# ---- generic output class to be subclassed ---- |
||||
# |
||||
|
||||
class Output(object): |
||||
def sendFrame(self, frame): |
||||
raise Exception('not implemented - just a dummy class') |
||||
|
||||
|
||||
# |
||||
# ---- MCUFOutput (MicroController Unit Frame) ---- |
||||
# |
||||
|
||||
class MCUFOutput(Output): |
||||
""" |
||||
This output module allows to send frames to blinkendevices over a serial |
||||
line using the MCUF protocol. |
||||
|
||||
""" |
||||
|
||||
moduleRegexDesc = 'mcuf:DEVICE:BAUDRATE[:DEPTH]' |
||||
moduleRegex = '^mcuf:([^:]+):(\d+)(:(\d+))?$' |
||||
|
||||
def __init__(self, port, baudrate, depth=0): |
||||
""" |
||||
Initialize the module with given port and baudrate. If depth is |
||||
specified, every frame will be transformed, if it not already has the |
||||
correct depth. |
||||
|
||||
""" |
||||
|
||||
self.__port = port |
||||
self.__baudrate = baudrate |
||||
self.__ser = serial.Serial(port=port, baudrate=baudrate) |
||||
self.__depth = depth |
||||
|
||||
@classmethod |
||||
def fromRegexMatch(cls, regexMatch): |
||||
""" Create an instance from a RegexMatch (matched with moduleRegex) """ |
||||
port = regexMatch.group(1) |
||||
baudrate = int(regexMatch.group(2)) |
||||
if regexMatch.group(4): |
||||
depth = int(regexMatch.group(4)) |
||||
else: |
||||
depth = 0 |
||||
return cls(port, baudrate, depth) |
||||
|
||||
def sendFrame(self, frame): |
||||
""" Pass a frame to the module. """ |
||||
(w, h) = frame.size |
||||
|
||||
if self.__depth != 0: |
||||
if frame.depth != self.__depth: |
||||
frame = frame.transform(self.__depth) |
||||
maxval = frame.depth - 1 |
||||
|
||||
framedata = '' |
||||
for y in range(h): |
||||
for x in range(w): |
||||
framedata += chr(frame.getPixel(x, y)) |
||||
|
||||
packet = '\x23\x54\x26\x66' |
||||
packet += '%s%s%s%s' % (chr(h>>8), chr(h & 0xff), |
||||
chr(w>>8), chr(w & 0xff)) |
||||
packet += '\x00\x01\x00%s' % (chr(maxval)) |
||||
packet += framedata |
||||
|
||||
self.__ser.write(packet) |
||||
|
||||
return True |
||||
|
||||
|
||||
# |
||||
# ---- SerialBlupOutput ---- |
||||
# |
||||
|
||||
class SerialBlupOutput(Output): |
||||
""" |
||||
This output module allows sending frames to 18x8 blinkendevices with |
||||
depth 2 over a serial line, by sending every column as a byte. |
||||
|
||||
""" |
||||
|
||||
moduleRegexDesc = 'serialblup:DEVICE:BAUDRATE' |
||||
moduleRegex = '^serialblup:([^:]+):(\d+)$' |
||||
|
||||
def __init__(self, port, baudrate): |
||||
""" Initialize the output module with the given port. """ |
||||
self.__port = port |
||||
self.__baudrate = baudrate |
||||
self.__ser = serial.Serial(port=port, baudrate=baudrate) |
||||
self.__stx2 = 'a' |
||||
self.__etx2 = 'b' |
||||
self.__stx8 = 'q' |
||||
self.__etx8 = 'b' |
||||
self.__stxRGB = chr(0xca) |
||||
self.__etxRGB = chr(0xfe) |
||||
|
||||
@classmethod |
||||
def fromRegexMatch(cls, regexMatch): |
||||
""" Create an instance from a RegexMatch (matched with moduleRegex) """ |
||||
port = regexMatch.group(1) |
||||
baudrate = int(regexMatch.group(2)) |
||||
return cls(port, baudrate) |
||||
|
||||
def sendFrame(self, frame): |
||||
""" Pass a frame to the module. """ |
||||
if frame.depth not in [2, 16] and frame.channels == 1: |
||||
raise ValueError('%s only supports frames of depth 2 or 16' % |
||||
(self.__class__.__name__)) |
||||
|
||||
if frame.channels not in [1, 3]: |
||||
raise ValueError('%s only supports frames with 1 or 3 channels' % |
||||
(self.__class__.__name__)) |
||||
|
||||
if frame.size != (18, 8): |
||||
raise ValueError('%s currently only supports 18x8 frames' % |
||||
(self.__class__.__name__)) |
||||
|
||||
if frame.channels == 1 and frame.depth == 2: |
||||
bytes = [ 0 ] * 18 |
||||
for x in range(18): |
||||
for y in range(8): |
||||
if frame.getPixel(x, y) == 1: |
||||
bytes[x] += 2**(7 - y) |
||||
|
||||
packet = self.__stx2 + ''.join(map(lambda b: chr(b), bytes)) + self.__etx2 |
||||
elif frame.channels == 1 and frame.depth == 16: |
||||
bytes = [ 0 ] * (18*4) |
||||
for x in range(18): |
||||
for y in range(8): |
||||
pixelval = frame.getPixel(x, y) |
||||
pixelnum = y*18 + x |
||||
bytes[pixelnum/2] += (pixelval << ((pixelnum % 2) * 4)) |
||||
|
||||
packet = self.__stx8 + ''.join(map(lambda b: chr(b), bytes)) + self.__etx8 |
||||
elif frame.channels == 3: |
||||
bytes = [ 0 ] * (18*8*3) |
||||
for y in range(8): |
||||
for x in range(18): |
||||
(r,g,b) = frame.getPixel(x, y) |
||||
pixelnum = y*18 + x |
||||
bytes[3*pixelnum] = r |
||||
bytes[3*pixelnum + 1] = g |
||||
bytes[3*pixelnum + 2] = b |
||||
packet = self.__stxRGB + ''.join(map(lambda b: chr(b), bytes)) + self.__etxRGB |
||||
|
||||
self.__ser.write(packet) |
||||
|
||||
return True |
||||
|
||||
# |
||||
# ---- ShellOuput ---- |
||||
# |
||||
|
||||
class ShellOutput(Output): |
||||
""" This output module prints frames with depth 2 on the terminal. """ |
||||
|
||||
moduleRegexDesc = 'shell[:ON_CHAR:OFF_CHAR]' |
||||
moduleRegex = '^shell(:(.):(.))?$' |
||||
|
||||
def __init__(self, onChar='#', offChar=' '): |
||||
""" Initialize the output. """ |
||||
self.__onChar = onChar |
||||
self.__offChar = offChar |
||||
self.__initialized = False |
||||
|
||||
@classmethod |
||||
def fromRegexMatch(cls, regexMatch): |
||||
""" Create an instance from a RegexMatch (matched with moduleRegex) """ |
||||
if regexMatch.group(2) is not None: |
||||
return cls(regexMatch.group(2), regexMatch.group(3)) |
||||
else: |
||||
return cls() |
||||
|
||||
def sendFrame(self, frame): |
||||
""" Pass a frame to the module. """ |
||||
if frame.depth != 2: |
||||
raise ValueError('%s only supports frames of depth 2' % |
||||
(self.__class__.__name__)) |
||||
|
||||
(width, height) = frame.size |
||||
|
||||
if not self.__initialized: |
||||
self.__initialized = True |
||||
print('\n' * height) |
||||
|
||||
print('\x1b[%dA' % (height + 2)) |
||||
for y in range(height): |
||||
line = '' |
||||
for x in range(width): |
||||
if frame.getPixel(x, y) == 1: |
||||
line += self.__onChar |
||||
else: |
||||
line += self.__offChar |
||||
print(line) |
||||
print('') |
||||
|
||||
return True |
||||
|
||||
# |
||||
# ---- ColorfulShellOuput ---- |
||||
# |
||||
|
||||
class ColorfulShellOutput(Output): |
||||
""" This output module prints frames on the terminal, using colors. """ |
||||
|
||||
moduleRegexDesc = 'colorfulshell' |
||||
moduleRegex = '^colorfulshell?$' |
||||
|
||||
def __init__(self): |
||||
""" Initialize the output. """ |
||||
self.__initialized = False |
||||
|
||||
@classmethod |
||||
def fromRegexMatch(cls, regexMatch): |
||||
""" Create an instance from a RegexMatch (matched with moduleRegex) """ |
||||
return cls() |
||||
|
||||
def sendFrame(self, frame): |
||||
""" Pass a frame to the module. """ |
||||
if frame.channels not in [1, 3]: |
||||
raise ValueError('%s only supports frames with 1 or 3 channels' % |
||||
(self.__class__.__name__)) |
||||
|
||||
(width, height) = frame.size |
||||
|
||||
if not self.__initialized: |
||||
self.__initialized = True |
||||
print('\n' * height) |
||||
|
||||
print('\x1b[%dA' % (height + 2)) |
||||
for y in range(height): |
||||
line = '' |
||||
for x in range(width): |
||||
if frame.channels == 3: |
||||
(r,g,b) = frame.getPixel(x, y) |
||||
if frame.depth != 5: |
||||
factor = 5 / ((frame.depth - 1) * 1.0) |
||||
r = round(r*factor) |
||||
g = round(g*factor) |
||||
b = round(b*factor) |
||||
colorval = 36*r + 6*g + b + 16 |
||||
|
||||
line += '\033[48;05;%dm ' % (colorval) |
||||
elif frame.channels == 1: |
||||
colorval = frame.getPixel(x, y) |
||||
if frame.depth != 24: |
||||
factor = (23 / ((frame.depth - 1) * 1.0)) |
||||
colorval = 232 + colorval * factor |
||||
|
||||
line += '\033[48;05;%dm ' % (colorval) |
||||
print(line) |
||||
print('') |
||||
|
||||
return True |
||||
# |
||||
# ---- BLPOutput ---- |
||||
# |
||||
|
||||
class BLPOutput(Output): |
||||
""" |
||||
This output module sends frames over the network using the UDP-based BLP |
||||
protocol. |
||||
|
||||
""" |
||||
|
||||
moduleRegexDesc = 'blp[:HOST:PORT]' |
||||
moduleRegex = '^(blp|eblp|e3blp)(:(.+):(\d+))?$' |
||||
|
||||
def __init__(self, host='127.0.0.1', port=4242, protocol=BLP.PROTO_BLP): |
||||
""" Initialize the output. Frames are sent to host:port. """ |
||||
self.__host = host |
||||
self.__port = port |
||||
self.__sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
||||
self.__protocol = protocol |
||||
|
||||
@classmethod |
||||
def fromRegexMatch(cls, regexMatch): |
||||
""" Create an instance from a RegexMatch (matched with moduleRegex) """ |
||||
if regexMatch.group(1) == 'blp': |
||||
protocol = BLP.PROTO_BLP |
||||
elif regexMatch.group(1) == 'eblp': |
||||
protocol = BLP.PROTO_EBLP |
||||
elif regexMatch.group(1) == 'e3blp': |
||||
protocol = BLP.PROTO_E3BLP |
||||
|
||||
if regexMatch.group(3) and regexMatch.group(4): |
||||
host = regexMatch.group(3) |
||||
port = int(regexMatch.group(4)) |
||||
return cls(host, port, protocol) |
||||
else: |
||||
return cls(protocol=protocol) |
||||
|
||||
def sendFrame(self, frame): |
||||
""" Pass a frame to the module. """ |
||||
packet = BLP.frame2blp(frame, protocol=self.__protocol) |
||||
self.__sock.sendto(packet, (self.__host, self.__port)) |
||||
return True |
||||
|
||||
# |
||||
# ---- BlinkenbuntHDOutput ---- |
||||
# |
||||
|
||||
class BlinkenbuntHDOutput(Output): |
||||
moduleRegexDesc = 'bbunthd' |
||||
moduleRegex = '^bbunthd$' |
||||
|
||||
def __init__(self): |
||||
import neopixel |
||||
self.w = 22 |
||||
self.h = 16 |
||||
LED_COUNT = self.w * self.h |
||||
LED_PIN = 13 |
||||
LED_FREQ_HZ = 800000 |
||||
LED_DMA = 5 |
||||
LED_BRIGHTNESS = 50 |
||||
LED_INVERT = False |
||||
LED_PWM = 1 |
||||
|
||||
self.strip = neopixel.Adafruit_NeoPixel( |
||||
LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, |
||||
LED_BRIGHTNESS, LED_PWM |
||||
) |
||||
self.strip.begin() |
||||
self._color_cls = neopixel.Color |
||||
|
||||
@classmethod |
||||
def fromRegexMatch(cls, regexMatch): |
||||
return cls() |
||||
|
||||
def sendFrame(self, frame): |
||||
for y in range(self.h): |
||||
for x in range(self.w): |
||||
if x%2 == 0: |
||||
lednum = x*self.h + y |
||||
else: |
||||
lednum = x*self.h - y + 15 |
||||
pix = frame.getPixel(x, y) |
||||
print(pix) |
||||
self.strip.setPixelColor(lednum, self._color_cls(*pix)) |
||||
self.strip.show() |
||||
|
||||
# |
||||
# ---- PygameOutput ---- |
||||
# |
||||
|
||||
class PygameOutput(Output): |
||||
moduleRegexDesc = 'pygame' |
||||
moduleRegex = '^pygame$' |
||||
|
||||
def __init__(self, width=22, height=16, scalex=8, scaley=8): |
||||
self.width = width |
||||
self.height = height |
||||
self.scalex = scalex |
||||
self.scaley = scaley |
||||
import pygame |
||||
self.__pygame = pygame |
||||
self.screen = pygame.display.set_mode((width*scalex, height*scaley)) |
||||
pygame.display.update() |
||||
|
||||
@classmethod |
||||
def fromRegexMatch(cls, regexMatch): |
||||
return cls() |
||||
|
||||
def sendFrame(self, frame): |
||||
pygame = self.__pygame |
||||
for y in range(self.height): |
||||
for x in range(self.width): |
||||
rect = (x*self.scalex, y*self.scaley, self.scalex, self.scaley) |
||||
pygame.draw.rect(self.screen, frame.getPixel(x, y), rect, 0) |
||||
pygame.display.update() |
||||
#import time |
||||
#time.sleep(0.1) |
||||
|
||||
|
||||
|
||||
# |
||||
# ---- output creator ;) ---- |
||||
# |
||||
|
||||
__outputModules = { |
||||
SerialBlupOutput.moduleRegex: SerialBlupOutput, |
||||
MCUFOutput.moduleRegex: MCUFOutput, |
||||
ShellOutput.moduleRegex: ShellOutput, |
||||
ColorfulShellOutput.moduleRegex: ColorfulShellOutput, |
||||
BLPOutput.moduleRegex: BLPOutput, |
||||
BlinkenbuntHDOutput.moduleRegex: BlinkenbuntHDOutput, |
||||
PygameOutput.moduleRegex: PygameOutput, |
||||
} |
||||
|
||||
def getOutput(outputSpec): |
||||
""" |
||||
Return an output module instance described by the given specification. |
||||
|
||||
""" |
||||
|
||||
for regex in __outputModules.keys(): |
||||
m = re.match(regex, outputSpec) |
||||
if m: |
||||
return __outputModules[regex].fromRegexMatch(regexMatch=m) |
||||
|
||||
raise IllegalOutputSpecificationError() |
||||
|
||||
def getOutputDescriptions(): |
||||
""" |
||||
Return a string containing the moduleRegexDesc of every available output |
||||
module. |
||||
|
||||
""" |
||||
|
||||
s = "" |
||||
for outputModule in __outputModules.values(): |
||||
s += " %s\n" % (outputModule.moduleRegexDesc) |
||||
return s |
||||
|
@ -0,0 +1,24 @@
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/python |
||||
|
||||
import sys |
||||
|
||||
import blup.animation |
||||
import writebml |
||||
|
||||
anim = blup.animation.load(sys.argv[1]) |
||||
plasmaanim = blup.animation.load('/tmp/plasma.bml') |
||||
newanim = blup.animation.Animation(anim.dimension) |
||||
|
||||
plasmaFrame = 0 |
||||
for f in anim: |
||||
newframe = blup.animation.AnimationFrame(f.dimension, f.delay) |
||||
for x in range(anim.dimension.width): |
||||
for y in range(anim.dimension.height): |
||||
p = f.getPixel(x,y) |
||||
if max(p) > 14: |
||||
newframe.setPixel(x,y, plasmaanim[plasmaFrame].getPixel(x,y)) |
||||
newanim.addFrame(newframe) |
||||
plasmaFrame += 1 |
||||
|
||||
writebml.writeBml(newanim, '/tmp/colormarq.bml') |
||||
|
@ -0,0 +1,44 @@
@@ -0,0 +1,44 @@
|
||||
#!/usr/bin/python |
||||
|
||||
import colorsys |
||||
import time |
||||
|
||||
import blup.output |
||||
import blup.frame |
||||
|
||||
dim = blup.frame.FrameDimension(18,8,16,3) |
||||
frame = blup.frame.Frame(dim) |
||||
|
||||
HEIGHT = 8 |
||||
WIDTH = 18 |
||||
|
||||
#h = 0.0 |
||||
#for y in range(HEIGHT): |
||||
# h += 1.0 / (HEIGHT * 1.0) |
||||
# v = 0 |
||||
# s = 0.7 |
||||
# for x in range(WIDTH): |
||||
# v += 1.0 / (WIDTH * 1.0) |
||||
# print s |
||||
# color = colorsys.hsv_to_rgb(h, s, v) |
||||
# color = tuple(map(lambda c: int(round(c * 15)), color)) |
||||
# print color |
||||
# frame.setPixel(x, y, color) |
||||
|
||||
for y in range(HEIGHT): |
||||
for x in range(WIDTH): |
||||
h = (x * 1.0) / (WIDTH - 1.0) |
||||
s = 1.0 - (y * 1.0) / (HEIGHT - 0.0) |
||||
v = 1.0 - (y * 1.0) / (HEIGHT - 0.0) |
||||
color = colorsys.hsv_to_rgb(h, s, v) |
||||
color = tuple(map(lambda c: int(round(c * 15)), color)) |
||||
frame.setPixel(x, y, color) |
||||
|
||||
out = blup.output.getOutput('e3blp') |
||||
while True: |
||||
out.sendFrame(frame) |
||||
time.sleep(0.4) |
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,34 @@
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/python |
||||
|
||||
import sys |
||||
|
||||
import blup.animation |
||||
import blup.frame |
||||
import writebml |
||||
import random |
||||
|
||||
dim = blup.frame.FrameDimension(18, 8, 16, 3) |
||||
newanim = blup.animation.Animation(dim) |
||||
|
||||
|
||||
num = 10 |
||||
horiz = True |
||||
delay = 100 |
||||
for i in range(num): |
||||
newcolor = (random.randint(0, 15), random.randint(0, 15), random.randint(0, 15)) |
||||
horiz = not horiz |
||||
newframe = blup.animation.AnimationFrame(dim, delay) |
||||
if horiz: |
||||
for x in range(dim.size()[0]): |
||||
for y in range(dim.size()[1]): |
||||
newframe.setPixel(x, y, newcolor) |
||||
newanim.addFrame(newframe) |
||||
else: |
||||
for y in range(dim.size()[1]): |
||||
for x in range(dim.size()[0]): |
||||
newframe.setPixel(x, y, newcolor) |
||||
newanim.addFrame(newframe) |
||||
|
||||
|
||||
|
||||
writebml.writeBml(newanim, '/tmp/colorwischer.bml') |
@ -0,0 +1,104 @@
@@ -0,0 +1,104 @@
|
||||
# Major library imports |
||||
import atexit |
||||
import pyaudio |
||||
from numpy import zeros, short, fromstring, array |
||||
from numpy.fft import fft |
||||
|
||||
import colorsys |
||||
import time |
||||
import blup.frame |
||||
import blup.output |
||||
|
||||
#NUM_SAMPLES = 512 |
||||
NUM_SAMPLES = 36 |
||||
#NUM_SAMPLES = 18 |
||||
#NUM_SAMPLES = 16 |
||||
#SAMPLING_RATE = 11025 |
||||
#SAMPLING_RATE = 11024 / 2 |
||||
SAMPLING_RATE = 1100 |
||||
|
||||
_stream = None |
||||
|
||||
def read_fft(): |
||||
global _stream |
||||
pa = None |
||||
|
||||
def cleanup_audio(): |
||||
if _stream: |
||||
_stream.stop_stream() |
||||
_stream.close() |
||||
pa.terminate() |
||||
|
||||
if _stream is None: |
||||
pa = pyaudio.PyAudio() |
||||
_stream = pa.open(format=pyaudio.paInt16, channels=1, |
||||
rate=SAMPLING_RATE, |
||||
input=True, frames_per_buffer=NUM_SAMPLES) |
||||
atexit.register(cleanup_audio) |
||||
|
||||
audio_data = fromstring(_stream.read(NUM_SAMPLES), dtype=short) |
||||
normalized_data = audio_data / 32768.0 |
||||
|
||||
return fft(normalized_data)[1:1+NUM_SAMPLES/2] |
||||
|
||||
def flatten_fft(scale = 1.0): |
||||
""" |
||||
Produces a nicer graph, I'm not sure if this is correct |
||||
""" |
||||
for i, v in enumerate(read_fft()): |
||||
yield scale * (i * v) / NUM_SAMPLES |
||||
|
||||
|
||||
def makeColor(x): |
||||
hue = (7-x) / 7.0 / 3.0 -0.1 |
||||
if hue < 0: |
||||
hue += 1 |
||||
rgb = map(lambda x: int(round(x*7)), colorsys.hsv_to_rgb(hue, 1, 1)) |
||||
return rgb |
||||
def makeSpectrumFrame(spectrum): |
||||
f = blup.frame.Frame(blup.frame.FrameDimension(18,8,8,3)) |
||||
for x, y in enumerate(spectrum): |
||||
if y > 7: |
||||
y = 7 |
||||
y = int(y) |
||||
for i in range(y): |
||||
#val = (7,7,7) |
||||
val = makeColor(i) |
||||
f.setPixel(x, 7-i, val) |
||||
return f |
||||
|
||||
def draw(): |
||||
'''Draw 3 different colour graphs''' |
||||
global NUM_SAMPLES |
||||
audio = array(list(flatten_fft(scale = 80))) |
||||
freqs = len(audio) |
||||
bass, mid, treble = triple(audio) |
||||
|
||||
colours = (0.5, 1.0, 0.5), (1, 1, 0), (1, 0.2, 0.5) |
||||
|
||||
fill(0, 0, 1) |
||||
rect(0, 0, WIDTH, 400) |
||||
translate(50, 200) |
||||
|
||||
for spectrum, col in zip((bass, mid, treble), colours): |
||||
fill(col) |
||||
for i, s in enumerate(spectrum): |
||||
rect(i, 0, 1, -abs(s)) |
||||
else: |
||||
translate(i, 0) |
||||
|
||||
audio = array(list(flatten_fft(scale = 80))) |
||||
|
||||
|
||||
out = blup.output.getOutput('colorfulshell') |
||||
while True: |
||||
audio = array(list(flatten_fft(scale=80))) |
||||
#audio = map(abs, audio) |
||||
#print len(audio) |
||||
frame = makeSpectrumFrame([ int((x/18.0)*8) for x in range(18)]) |
||||
frame = makeSpectrumFrame(audio) |
||||
#print map(int,audio) |
||||
out.sendFrame(frame) |
||||
#time.sleep(0.1) |
||||
|
||||
|
@ -0,0 +1,63 @@
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/python |
||||
|
||||
import colorsys |
||||
import random |
||||
|
||||
import blup.frame |
||||
import blup.output |
||||
import blup.animation |
||||
|
||||
# thanks to |
||||
# http://quiteuseful.co.uk/post/96424751/beautiful-algorithms-1-fire-part-2 |
||||
|
||||
|
||||
dim = blup.frame.FrameDimension(18,8,16,3) |
||||
anim = blup.animation.Animation(dim) |
||||
|
||||
def getRandomColor(maxval): |
||||
return mmeap(lambda x: int(round(x*maxval)), colorsys.hsv_to_rgb(random.random() % 0.16666666, 1, 1)) |
||||
|
||||
def colorAvg(colors): |
||||
r = 0 |
||||
g = 0 |
||||
b = 0 |
||||
for c in colors: |
||||
r += c[0] |
||||
g += c[1] |
||||
b += c[2] |
||||
avg = (r,g,b) |
||||
avg = map(lambda x: int(round(x / (len(colors) * 1.0))), avg) |
||||
return avg |
||||
|
||||
|
||||
for i in range(100): |
||||
randRow = [ getRandomColor(dim.depth - 1) for i in range(dim.width) ] |
||||
print randRow |
||||
|
||||
f = blup.animation.AnimationFrame(dim, 60) |
||||
|
||||
for y in range(dim.height - 1, -1, -1): |
||||
for x in range(dim.width): |
||||
colors = [] |
||||
if x >=1 : |
||||
colors.append(f.getPixel(x-1,y)) |
||||
if x < dim.width - 1: |
||||
pass |
||||
colors.append(f.getPixel(x+1,y)) |
||||
if y < dim.height - 1: |
||||
colors.append(f.getPixel(x, y+1)) |
||||
else: |
||||
#colors.append(randRow[y]) |
||||
colors.append(getRandomColor(dim.depth - 1)) |
||||
#colors = [randRow[x]] |
||||
c = colorAvg(colors) |
||||
f.setPixel(x, y, c) |
||||
anim.addFrame(f) |
||||
|
||||
#out = blup.output.getOutput('e3blp:bbunt:4242') |
||||
out = blup.output.getOutput('colorfulshell') |
||||
player = blup.animation.AnimationPlayer() |
||||
player.play(anim, out) |
||||
|
||||
|
||||
|
@ -0,0 +1,84 @@
@@ -0,0 +1,84 @@
|
||||
|
||||
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler |
||||
import socket |
||||
import cgi |
||||
import json |
||||
import time |
||||
import os.path |
||||
import threading |
||||
|
||||
TELNET_HOST = 'localhost' |
||||
TELNET_PORT = 4243 |
||||
|
||||
DOCROOT = '/var/tmp/blup2/blayer-webif' |
||||
UPLOAD_DIR = '/var/tmp/blup2/blm/incoming' |
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
||||
sock.connect((TELNET_HOST, TELNET_PORT)) |
||||
socklock = threading.Lock() |
||||
|
||||
class HTTPRequestHandler(BaseHTTPRequestHandler): |
||||
def do_GET(self): |
||||
self.send_response(200) |
||||
self.send_header('Content-type', 'text/html') |
||||
self.end_headers() |
||||
print self.path |
||||
#self.wfile.write('<form action="bla" method="post"><input type="text" name="text" /><input type="submit" value="ok" /></form>') |
||||
|
||||
p = os.path.join(DOCROOT, self.path[1:]) |
||||
print p |
||||
if os.path.isfile(p): |
||||
f = open(p, 'r') |
||||
data = f.read() |
||||
f.close() |
||||
self.wfile.write(data) |
||||
|
||||
|
||||
|
||||
def do_POST(self): |
||||
|
||||
if self.path == '/do_upload': |
||||
formdata = cgi.FieldStorage( |
||||
fp=self.rfile, headers=self.headers, |
||||
environ={'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': self.headers['Content-Type']}) |
||||
|
||||
fileField = formdata['file'] |
||||
|
||||
f = open(os.path.join(UPLOAD_DIR, fileField.filename), 'w') |
||||
f.write(fileField.file.read()) |
||||
f.close() |
||||
|
||||
sock.send(json.dumps({"cmd": "refresh"}) + '\n') |
||||
print 'server answer to refresh cmd: ', sock.recv(1024) |
||||
|
||||
self.send_response(200); |
||||
self.end_headers(); |
||||
|
||||
return |
||||
|
||||
postdata = self.rfile.read(int(self.headers.getheader('content-length'))) |
||||
#self.wfile.write('test') |
||||
|
||||
if postdata[-1:] != '\n': |
||||
postdata += '\n' |
||||
print 'postdata:', postdata |
||||
|
||||
with socklock: |
||||
sock.send(postdata) |
||||
time.sleep(0.1) |
||||
resp = sock.recv(1024 * 10) |
||||
#sock.shutdown(socket.SHUT_RDWR) |
||||
#sock.close() |
||||
|
||||
self.send_response(200) |
||||
self.send_header('Content-type', 'text/html') |
||||
self.send_header('Content-length', len(resp)) |
||||
self.end_headers() |
||||
print 'resp:', resp |
||||
self.wfile.write(resp) |
||||
|
||||
|
||||
|
||||
server = HTTPServer(('0.0.0.0',8080), HTTPRequestHandler) |
||||
server.serve_forever() |
||||
|
@ -0,0 +1,43 @@
@@ -0,0 +1,43 @@
|
||||
|
||||
def loadBbm(filename): |
||||
""" Parse a bbm file and return an Animation object. """ |
||||
|
||||
def getint16(vals): |
||||
return (cals[2]>>8) + vals[3] |
||||
def getint32(vals): |
||||
return (vals[0]>>24) + (vals[1]>>16) + (cals[2]>>8) + vals[3] |
||||
|
||||
f = open(filename, 'r') |
||||
rawdata = f.read() |
||||
f.close() |
||||
|
||||
data = [ ord(x) for x in rawdata ] |
||||
|
||||
if getint32(data[0:4]) != 0x23542666: |
||||
raise ValueError('magic does not match') |
||||
|
||||
height = getint16(data[4:6]) |
||||
width = getint16(data[6:8]) |
||||
channels = getint16(data[8:10]) |
||||
maxval = getint16(data[10:12]) |
||||
|
||||
framecnt = getint32(data[10:14]) |
||||
duration = getint32(data[14:18]) |
||||
frameptr = getint32(data[18:22]) |
||||
|
||||
# TODO: parse additional headers |
||||
|
||||
if rawdata[frameptr:(frameptr+4)] != 'frms': |
||||
raise ValueError('frame pointer does not point to frame start marker') |
||||
|
||||
framedata = data[frameptr + 4:] |
||||
framesize = width*height*channels |
||||
|
||||
frames = [] |
||||
while True: |
||||
pass |
||||
|
||||
raise Exception('uarrgh!!') |
||||
|
||||
|
||||
|
@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
|
||||
# BlinkenLights Movie 18x8 |
||||
# loop = yes |
||||
|
||||
@200 |
||||
000000000000000000 |
||||
000011100011100000 |
||||
000111110111110000 |
||||
000111111111110000 |
||||
000011111111100000 |
||||
000000111110000000 |
||||
000000001000000000 |
||||
000000000000000000 |
||||
|
||||
@800 |
||||
000011100011100000 |
||||
000111110111110000 |
||||
001111111111111000 |
||||
001111111111111000 |
||||
000111111111110000 |
||||
000011111111100000 |
||||
000000111110000000 |
||||
000000001000000000 |
||||
|
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/pyton |
||||
|
||||
import blup.frame |
||||
import blup.output |
||||
import blup.animation |
||||
|
||||
out = blup.output.getOutput('e3blp') |
||||
|
||||
points = [] |
||||
|
||||
|
||||
for i in |
||||
|
@ -0,0 +1,183 @@
@@ -0,0 +1,183 @@
|
||||
#!/usr/bin/python |
||||
|
||||
import threading |
||||
import os |
||||
import os.path |
||||
import random |
||||
import time |
||||
import sys |
||||
import getopt |
||||
|
||||
import blup.output |
||||
import blup.animation |
||||
import blup.frame |
||||
|
||||
|
||||
class AnimationLoaderThread(threading.Thread): |
||||
""" |
||||
This class is used by the player to pre-load an animation while another one |
||||
is being played. |
||||
|
||||
""" |
||||
|
||||
def __init__(self, filename): |
||||
threading.Thread.__init__(self) |
||||
self.__filename = filename |
||||
self.__anim = None |
||||
self.__error = False |
||||
|
||||
def run(self): |
||||
try: |
||||
self.__anim = blup.animation.load(self.__filename) |
||||
except Exception as e: |
||||
print '%s: %s while loading %s' % (self.__class__.__name__, |
||||
repr(e), |
||||
self.__filename) |
||||
self.__error = True |
||||
self.__anim = None |
||||
|
||||
def getAnim(self): |
||||
self.join() |
||||
return self.__anim |
||||
|
||||
|
||||
class MiniPlayer(object): |
||||
""" |
||||
Minimal animation player that does nothing but randomly selecting |
||||
animations out of a directory and playing them. |
||||
|
||||
""" |
||||
|
||||
def __init__(self, animDir, output): |
||||
self.__animDir = animDir |
||||
self.__output = output |
||||
self.loopTime = 10000 |
||||
self.gap = 800 |
||||
self.maxPlayNext = 3 |
||||
self.__playNext = [] |
||||
self.__running = False |
||||
|
||||
def terminate(self): |
||||
self.__running = False |
||||
|
||||
def playNext(self, filename): |
||||
""" Add an animation to the queue. """ |
||||
|
||||
if filename.find('/') >= 0: |
||||
raise ValueError('filename must not contain slashes') |
||||
|
||||
if ( os.path.isfile(os.path.join(self.__animDir, filename)) and |
||||
len(self.__playNext) < self.maxPlayNext ): |
||||
self.__playNext.append(filename) |
||||
else: |
||||
return False |
||||
|
||||
def getNextFilename(self): |
||||
""" |
||||
Return the next animation filename to be played, either from the queue, |
||||
or randomly chosen. |
||||
|
||||
""" |
||||
|
||||
if len(self.__playNext) > 0 and os.path.isfile(self.__playNext[0]): |
||||
return self.__playNext.pop(0) |
||||
else: |
||||
files = os.listdir(self.__animDir) |
||||
return random.choice(files) |
||||
|
||||
def run(self): |
||||
""" |
||||
Runs the player until terminate() gets called (e.g. by another thread). |
||||
|
||||
""" |
||||
|
||||
self.__running = True |
||||
currentAnim = None |
||||
player = blup.animation.AnimationPlayer() |
||||
|
||||
while self.__running: |
||||
|
||||
# begin to load the next animation |
||||
filename = os.path.join(self.__animDir, self.getNextFilename()) |
||||
loader = AnimationLoaderThread(filename) |
||||
loader.start() |
||||
|
||||
# play the animation in case it had been successfully loaded before |
||||
if currentAnim is not None: |
||||
if currentAnim.duration < self.loopTime: |
||||
count = self.loopTime / currentAnim.duration |
||||
else: |
||||
count = 1 |
||||
player.play(currentAnim, self.__output, count=count) |
||||
|
||||
# show a blank frame for some thime |
||||
if self.gap > 0 and self.__running: |
||||
# TODO: use correct frame size |
||||
dim = blup.frame.FrameDimension(18,8,8,3) |
||||
self.__output.sendFrame(blup.frame.Frame(dim)) |
||||
time.sleep(self.gap / 1000.0) |
||||
|
||||
# get the next animation from the loader |
||||
currentAnim = loader.getAnim() |
||||
|
||||
|
||||
def printUsage(errMsg=None): |
||||
if errMsg is not None: |
||||
print 'error: %s\n' % (errMsg) |
||||
print 'usage: %s [OPTIONS] PATH' % (sys.argv[0]) |
||||
print 'where PATH is the directory containing the animations to play' |
||||
print 'supported options:' |
||||
print ' -o OUTPUT where to output the frames (default: shell)' |
||||
print ' --output OUTPUT\n' |
||||
print ' -h print this text' |
||||
print ' --help' |
||||
|
||||
def main(): |
||||
try: |
||||
(opts, args) = getopt.gnu_getopt(sys.argv, 'ho:', ['help', 'output=']) |
||||
opts = dict(opts) |
||||
except getopt.GetoptError as e: |
||||
printUsage(e.msg) |
||||
sys.exit(1) |
||||
|
||||
if opts.has_key('--help'): |
||||
printUsage() |
||||
sys.exit(0) |
||||
|
||||
if opts.has_key('-o'): |
||||
output = opts['-o'] |
||||
elif opts.has_key('--output'): |
||||
output = opts['--output'] |
||||
else: |
||||
output = 'shell' |
||||
|
||||
try: |
||||
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: |
||||
printUsage() |
||||
sys.exit(1) |
||||
else: |
||||
animDir = args[1] |
||||
|
||||
if not os.path.isdir(animDir): |
||||
print '%s is not a directory' % (animDir) |
||||
sys.exit(1) |
||||
|
||||
p = MiniPlayer(animDir, out) |
||||
try: |
||||
p.run() |
||||
except KeyboardInterrupt: |
||||
sys.exit(0) |
||||
|
||||
if __name__ == '__main__': |
||||
main() |
||||
|
@ -0,0 +1,873 @@
@@ -0,0 +1,873 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<blm width="18" height="8" bits="4" channels="3"> |
||||
<frame duration="100"> |
||||
<row>000000000000000000000000000000000000000000000000f00000</row> |
||||
<row>000000000000000000000000000000000000000000000000f00f00</row> |
||||
<row>000000000000000000000000000000000000000000000000f00000</row> |
||||
<row>000000000000000000000000000000000000000000000000f00000</row> |
||||
<row>000000000000000000000000000000000000000000000000f00000</row> |
||||
<row>000000000000000000000000000000000000000000000000f00000</row> |
||||
<row>000000000000000000000000000000000000000000000000f00000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000000000000000000000000000000000000000f00000000</row> |
||||
<row>000000000000000000000000000000000000000000000f00f00000</row> |
||||
<row>000000000000000000000000000000000000000000000f00000f00</row> |
||||
<row>000000000000000000000000000000000000000000000f00000000</row> |
||||
<row>000000000000000000000000000000000000000000000f00000000</row> |
||||
<row>000000000000000000000000000000000000000000000f00000000</row> |
||||
<row>000000000000000000000000000000000000000000000f00000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000000000000000000000000000000000000f00000000000</row> |
||||
<row>000000000000000000000000000000000000000000f00f00000000</row> |
||||
<row>000000000000000000000000000000000000000000f00000f00000</row> |
||||
<row>000000000000000000000000000000000000000000f00000000f00</row> |
||||
<row>000000000000000000000000000000000000000000f00000000000</row> |
||||
<row>000000000000000000000000000000000000000000f00000000000</row> |
||||
<row>000000000000000000000000000000000000000000f00000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000000000000000000000000000000000f00000000000000</row> |
||||
<row>000000000000000000000000000000000000000f00f00000000000</row> |
||||
<row>000000000000000000000000000000000000000f00000f00000f00</row> |
||||
<row>000000000000000000000000000000000000000f00000000f00000</row> |
||||
<row>000000000000000000000000000000000000000f00000000000000</row> |
||||
<row>000000000000000000000000000000000000000f00000000000000</row> |
||||
<row>000000000000000000000000000000000000000f00000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000000000000000000000000000000f00000000000000000</row> |
||||
<row>000000000000000000000000000000000000f00f00000000000f00</row> |
||||
<row>000000000000000000000000000000000000f00000f00000f00000</row> |
||||
<row>000000000000000000000000000000000000f00000000f00000000</row> |
||||
<row>000000000000000000000000000000000000f00000000000000000</row> |
||||
<row>000000000000000000000000000000000000f00000000000000000</row> |
||||
<row>000000000000000000000000000000000000f00000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000000000000000000000000000f00000000000000000f00</row> |
||||
<row>000000000000000000000000000000000f00f00000000000f00f00</row> |
||||
<row>000000000000000000000000000000000f00000f00000f00000f00</row> |
||||
<row>000000000000000000000000000000000f00000000f00000000f00</row> |
||||
<row>000000000000000000000000000000000f00000000000000000f00</row> |
||||
<row>000000000000000000000000000000000f00000000000000000f00</row> |
||||
<row>000000000000000000000000000000000f00000000000000000f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000000000000000000000000f00000000000000000f00000</row> |
||||
<row>000000000000000000000000000000f00f00000000000f00f00000</row> |
||||
<row>000000000000000000000000000000f00000f00000f00000f00000</row> |
||||
<row>000000000000000000000000000000f00000000f00000000f00000</row> |
||||
<row>000000000000000000000000000000f00000000000000000f00000</row> |
||||
<row>000000000000000000000000000000f00000000000000000f00000</row> |
||||
<row>000000000000000000000000000000f00000000000000000f00000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000000000000000000000f00000000000000000f00000f00</row> |
||||
<row>000000000000000000000000000f00f00000000000f00f00000f00</row> |
||||
<row>000000000000000000000000000f00000f00000f00000f00000f00</row> |
||||
<row>000000000000000000000000000f00000000f00000000f00000f00</row> |
||||
<row>000000000000000000000000000f00000000000000000f00000f00</row> |
||||
<row>000000000000000000000000000f00000000000000000f00000f00</row> |
||||
<row>000000000000000000000000000f00000000000000000f00000f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000000000000000000f00000000000000000f00000f00f00</row> |
||||
<row>000000000000000000000000f00f00000000000f00f00000f00000</row> |
||||
<row>000000000000000000000000f00000f00000f00000f00000f00000</row> |
||||
<row>000000000000000000000000f00000000f00000000f00000f00f00</row> |
||||
<row>000000000000000000000000f00000000000000000f00000f00000</row> |
||||
<row>000000000000000000000000f00000000000000000f00000f00000</row> |
||||
<row>000000000000000000000000f00000000000000000f00000f00000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000000000000000f00000000000000000f00000f00f00f00</row> |
||||
<row>000000000000000000000f00f00000000000f00f00000f00000000</row> |
||||
<row>000000000000000000000f00000f00000f00000f00000f00000000</row> |
||||
<row>000000000000000000000f00000000f00000000f00000f00f00f00</row> |
||||
<row>000000000000000000000f00000000000000000f00000f00000000</row> |
||||
<row>000000000000000000000f00000000000000000f00000f00000000</row> |
||||
<row>000000000000000000000f00000000000000000f00000f00000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000000000000f00000000000000000f00000f00f00f00f00</row> |
||||
<row>000000000000000000f00f00000000000f00f00000f00000000000</row> |
||||
<row>000000000000000000f00000f00000f00000f00000f00000000000</row> |
||||
<row>000000000000000000f00000000f00000000f00000f00f00f00f00</row> |
||||
<row>000000000000000000f00000000000000000f00000f00000000f00</row> |
||||
<row>000000000000000000f00000000000000000f00000f00000000000</row> |
||||
<row>000000000000000000f00000000000000000f00000f00000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000000000f00000000000000000f00000f00f00f00f00f00</row> |
||||
<row>000000000000000f00f00000000000f00f00000f00000000000000</row> |
||||
<row>000000000000000f00000f00000f00000f00000f00000000000000</row> |
||||
<row>000000000000000f00000000f00000000f00000f00f00f00f00f00</row> |
||||
<row>000000000000000f00000000000000000f00000f00000000f00000</row> |
||||
<row>000000000000000f00000000000000000f00000f00000000000f00</row> |
||||
<row>000000000000000f00000000000000000f00000f00000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000000f00000000000000000f00000f00f00f00f00f00f00</row> |
||||
<row>000000000000f00f00000000000f00f00000f00000000000000000</row> |
||||
<row>000000000000f00000f00000f00000f00000f00000000000000000</row> |
||||
<row>000000000000f00000000f00000000f00000f00f00f00f00f00f00</row> |
||||
<row>000000000000f00000000000000000f00000f00000000f00000000</row> |
||||
<row>000000000000f00000000000000000f00000f00000000000f00000</row> |
||||
<row>000000000000f00000000000000000f00000f00000000000000f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000f00000000000000000f00000f00f00f00f00f00f00000</row> |
||||
<row>000000000f00f00000000000f00f00000f00000000000000000f00</row> |
||||
<row>000000000f00000f00000f00000f00000f00000000000000000f00</row> |
||||
<row>000000000f00000000f00000000f00000f00f00f00f00f00f00000</row> |
||||
<row>000000000f00000000000000000f00000f00000000f00000000000</row> |
||||
<row>000000000f00000000000000000f00000f00000000000f00000000</row> |
||||
<row>000000000f00000000000000000f00000f00000000000000f00000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000f00000000000000000f00000f00f00f00f00f00f00000000</row> |
||||
<row>000000f00f00000000000f00f00000f00000000000000000f00000</row> |
||||
<row>000000f00000f00000f00000f00000f00000000000000000f00000</row> |
||||
<row>000000f00000000f00000000f00000f00f00f00f00f00f00000000</row> |
||||
<row>000000f00000000000000000f00000f00000000f00000000000000</row> |
||||
<row>000000f00000000000000000f00000f00000000000f00000000000</row> |
||||
<row>000000f00000000000000000f00000f00000000000000f00000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000f00000000000000000f00000f00f00f00f00f00f00000000f00</row> |
||||
<row>000f00f00000000000f00f00000f00000000000000000f00000f00</row> |
||||
<row>000f00000f00000f00000f00000f00000000000000000f00000f00</row> |
||||
<row>000f00000000f00000000f00000f00f00f00f00f00f00000000f00</row> |
||||
<row>000f00000000000000000f00000f00000000f00000000000000f00</row> |
||||
<row>000f00000000000000000f00000f00000000000f00000000000f00</row> |
||||
<row>000f00000000000000000f00000f00000000000000f00000000f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00000000000000000f00000f00f00f00f00f00f00000000f00000</row> |
||||
<row>f00f00000000000f00f00000f00000000000000000f00000f00f00</row> |
||||
<row>f00000f00000f00000f00000f00000000000000000f00000f00000</row> |
||||
<row>f00000000f00000000f00000f00f00f00f00f00f00000000f00000</row> |
||||
<row>f00000000000000000f00000f00000000f00000000000000f00000</row> |
||||
<row>f00000000000000000f00000f00000000000f00000000000f00000</row> |
||||
<row>f00000000000000000f00000f00000000000000f00000000f00000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000000000f00000f00f00f00f00f00f00000000f00000000</row> |
||||
<row>f00000000000f00f00000f00000000000000000f00000f00f00000</row> |
||||
<row>000f00000f00000f00000f00000000000000000f00000f00000f00</row> |
||||
<row>000000f00000000f00000f00f00f00f00f00f00000000f00000000</row> |
||||
<row>000000000000000f00000f00000000f00000000000000f00000000</row> |
||||
<row>000000000000000f00000f00000000000f00000000000f00000000</row> |
||||
<row>000000000000000f00000f00000000000000f00000000f00000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000000f00000f00f00f00f00f00f00000000f00000000000</row> |
||||
<row>000000000f00f00000f00000000000000000f00000f00f00000000</row> |
||||
<row>f00000f00000f00000f00000000000000000f00000f00000f00000</row> |
||||
<row>000f00000000f00000f00f00f00f00f00f00000000f00000000f00</row> |
||||
<row>000000000000f00000f00000000f00000000000000f00000000000</row> |
||||
<row>000000000000f00000f00000000000f00000000000f00000000000</row> |
||||
<row>000000000000f00000f00000000000000f00000000f00000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000f00000f00f00f00f00f00f00000000f00000000000000</row> |
||||
<row>000000f00f00000f00000000000000000f00000f00f00000000000</row> |
||||
<row>000f00000f00000f00000000000000000f00000f00000f00000f00</row> |
||||
<row>f00000000f00000f00f00f00f00f00f00000000f00000000f00000</row> |
||||
<row>000000000f00000f00000000f00000000000000f00000000000000</row> |
||||
<row>000000000f00000f00000000000f00000000000f00000000000000</row> |
||||
<row>000000000f00000f00000000000000f00000000f00000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000f00000f00f00f00f00f00f00000000f00000000000000000</row> |
||||
<row>000f00f00000f00000000000000000f00000f00f00000000000f00</row> |
||||
<row>f00000f00000f00000000000000000f00000f00000f00000f00000</row> |
||||
<row>000000f00000f00f00f00f00f00f00000000f00000000f00000000</row> |
||||
<row>000000f00000f00000000f00000000000000f00000000000000000</row> |
||||
<row>000000f00000f00000000000f00000000000f00000000000000000</row> |
||||
<row>000000f00000f00000000000000f00000000f00000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000f00000f00f00f00f00f00f00000000f00000000000000000f00</row> |
||||
<row>f00f00000f00000000000000000f00000f00f00000000000f00f00</row> |
||||
<row>000f00000f00000000000000000f00000f00000f00000f00000f00</row> |
||||
<row>000f00000f00f00f00f00f00f00000000f00000000f00000000f00</row> |
||||
<row>000f00000f00000000f00000000000000f00000000000000000f00</row> |
||||
<row>000f00000f00000000000f00000000000f00000000000000000f00</row> |
||||
<row>000f00000f00000000000000f00000000f00000000000000000f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00000f00f00f00f00f00f00000000f00000000000000000f00000</row> |
||||
<row>f00000f00000000000000000f00000f00f00000000000f00f00000</row> |
||||
<row>f00000f00000000000000000f00000f00000f00000f00000f00000</row> |
||||
<row>f00000f00f00f00f00f00f00000000f00000000f00000000f00000</row> |
||||
<row>f00000f00000000f00000000000000f00000000000000000f00000</row> |
||||
<row>f00000f00000000000f00000000000f00000000000000000f00000</row> |
||||
<row>f00000f00000000000000f00000000f00000000000000000f00000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000f00f00f00f00f00f00000000f00000000000000000f00000000</row> |
||||
<row>000f00000000000000000f00000f00f00000000000f00f00000f00</row> |
||||
<row>000f00000000000000000f00000f00000f00000f00000f00000f00</row> |
||||
<row>000f00f00f00f00f00f00000000f00000000f00000000f00000f00</row> |
||||
<row>000f00000000f00000000000000f00000000000000000f00000f00</row> |
||||
<row>000f00000000000f00000000000f00000000000000000f00000f00</row> |
||||
<row>000f00000000000000f00000000f00000000000000000f00000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00f00f00f00f00000000f00000000000000000f00000000f00</row> |
||||
<row>f00000000000000000f00000f00f00000000000f00f00000f00000</row> |
||||
<row>f00000000000000000f00000f00000f00000f00000f00000f00000</row> |
||||
<row>f00f00f00f00f00f00000000f00000000f00000000f00000f00000</row> |
||||
<row>f00000000f00000000000000f00000000000000000f00000f00000</row> |
||||
<row>f00000000000f00000000000f00000000000000000f00000f00000</row> |
||||
<row>f00000000000000f00000000f00000000000000000f00000000f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00f00f00f00000000f00000000000000000f00000000f00f00</row> |
||||
<row>000000000000000f00000f00f00000000000f00f00000f00000000</row> |
||||
<row>000000000000000f00000f00000f00000f00000f00000f00000000</row> |
||||
<row>f00f00f00f00f00000000f00000000f00000000f00000f00000000</row> |
||||
<row>000000f00000000000000f00000000000000000f00000f00000000</row> |
||||
<row>000000000f00000000000f00000000000000000f00000f00000000</row> |
||||
<row>000000000000f00000000f00000000000000000f00000000f00f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00f00f00000000f00000000000000000f00000000f00f00f00</row> |
||||
<row>000000000000f00000f00f00000000000f00f00000f00000000000</row> |
||||
<row>000000000000f00000f00000f00000f00000f00000f00000000000</row> |
||||
<row>f00f00f00f00000000f00000000f00000000f00000f00000000000</row> |
||||
<row>000f00000000000000f00000000000000000f00000f00000000000</row> |
||||
<row>000000f00000000000f00000000000000000f00000f00000000000</row> |
||||
<row>000000000f00000000f00000000000000000f00000000f00f00f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00f00000000f00000000000000000f00000000f00f00f00f00</row> |
||||
<row>000000000f00000f00f00000000000f00f00000f00000000000000</row> |
||||
<row>000000000f00000f00000f00000f00000f00000f00000000000000</row> |
||||
<row>f00f00f00000000f00000000f00000000f00000f00000000000000</row> |
||||
<row>f00000000000000f00000000000000000f00000f00000000000000</row> |
||||
<row>000f00000000000f00000000000000000f00000f00000000000000</row> |
||||
<row>000000f00000000f00000000000000000f00000000f00f00f00f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00000000f00000000000000000f00000000f00f00f00f00f00</row> |
||||
<row>000000f00000f00f00000000000f00f00000f00000000000000000</row> |
||||
<row>000000f00000f00000f00000f00000f00000f00000000000000000</row> |
||||
<row>f00f00000000f00000000f00000000f00000f00000000000000000</row> |
||||
<row>000000000000f00000000000000000f00000f00000000000000000</row> |
||||
<row>f00000000000f00000000000000000f00000f00000000000000000</row> |
||||
<row>000f00000000f00000000000000000f00000000f00f00f00f00f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00000000f00000000000000000f00000000f00f00f00f00f00f00</row> |
||||
<row>000f00000f00f00000000000f00f00000f00000000000000000000</row> |
||||
<row>000f00000f00000f00000f00000f00000f00000000000000000000</row> |
||||
<row>f00000000f00000000f00000000f00000f00000000000000000000</row> |
||||
<row>000000000f00000000000000000f00000f00000000000000000000</row> |
||||
<row>000000000f00000000000000000f00000f00000000000000000000</row> |
||||
<row>f00000000f00000000000000000f00000000f00f00f00f00f00f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000f00000000000000000f00000000f00f00f00f00f00f00000</row> |
||||
<row>f00000f00f00000000000f00f00000f00000000000000000000000</row> |
||||
<row>f00000f00000f00000f00000f00000f00000000000000000000000</row> |
||||
<row>000000f00000000f00000000f00000f00000000000000000000000</row> |
||||
<row>000000f00000000000000000f00000f00000000000000000000000</row> |
||||
<row>000000f00000000000000000f00000f00000000000000000000000</row> |
||||
<row>000000f00000000000000000f00000000f00f00f00f00f00f00000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000f00000000000000000f00000000f00f00f00f00f00f00000f00</row> |
||||
<row>000f00f00000000000f00f00000f00000000000000000000000f00</row> |
||||
<row>000f00000f00000f00000f00000f00000000000000000000000f00</row> |
||||
<row>000f00000000f00000000f00000f00000000000000000000000f00</row> |
||||
<row>000f00000000000000000f00000f00000000000000000000000f00</row> |
||||
<row>000f00000000000000000f00000f00000000000000000000000f00</row> |
||||
<row>000f00000000000000000f00000000f00f00f00f00f00f00000f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00000000000000000f00000000f00f00f00f00f00f00000f00f00</row> |
||||
<row>f00f00000000000f00f00000f00000000000000000000000f00000</row> |
||||
<row>f00000f00000f00000f00000f00000000000000000000000f00000</row> |
||||
<row>f00000000f00000000f00000f00000000000000000000000f00000</row> |
||||
<row>f00000000000000000f00000f00000000000000000000000f00000</row> |
||||
<row>f00000000000000000f00000f00000000000000000000000f00000</row> |
||||
<row>f00000000000000000f00000000f00f00f00f00f00f00000f00f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000000000f00000000f00f00f00f00f00f00000f00f00f00</row> |
||||
<row>f00000000000f00f00000f00000000000000000000000f00000000</row> |
||||
<row>000f00000f00000f00000f00000000000000000000000f00000000</row> |
||||
<row>000000f00000000f00000f00000000000000000000000f00000000</row> |
||||
<row>000000000000000f00000f00000000000000000000000f00000000</row> |
||||
<row>000000000000000f00000f00000000000000000000000f00000000</row> |
||||
<row>000000000000000f00000000f00f00f00f00f00f00000f00f00f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000000f00000000f00f00f00f00f00f00000f00f00f00f00</row> |
||||
<row>000000000f00f00000f00000000000000000000000f00000000000</row> |
||||
<row>f00000f00000f00000f00000000000000000000000f00000000000</row> |
||||
<row>000f00000000f00000f00000000000000000000000f00000000000</row> |
||||
<row>000000000000f00000f00000000000000000000000f00000000000</row> |
||||
<row>000000000000f00000f00000000000000000000000f00000000000</row> |
||||
<row>000000000000f00000000f00f00f00f00f00f00000f00f00f00f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000f00000000f00f00f00f00f00f00000f00f00f00f00f00</row> |
||||
<row>000000f00f00000f00000000000000000000000f00000000000000</row> |
||||
<row>000f00000f00000f00000000000000000000000f00000000000000</row> |
||||
<row>f00000000f00000f00000000000000000000000f00000000000000</row> |
||||
<row>000000000f00000f00000000000000000000000f00000000000000</row> |
||||
<row>000000000f00000f00000000000000000000000f00000000000000</row> |
||||
<row>000000000f00000000f00f00f00f00f00f00000f00f00f00f00f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000f00000000f00f00f00f00f00f00000f00f00f00f00f00000</row> |
||||
<row>000f00f00000f00000000000000000000000f00000000000000f00</row> |
||||
<row>f00000f00000f00000000000000000000000f00000000000000000</row> |
||||
<row>000000f00000f00000000000000000000000f00000000000000000</row> |
||||
<row>000000f00000f00000000000000000000000f00000000000000000</row> |
||||
<row>000000f00000f00000000000000000000000f00000000000000f00</row> |
||||
<row>000000f00000000f00f00f00f00f00f00000f00f00f00f00f00000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000f00000000f00f00f00f00f00f00000f00f00f00f00f00000000</row> |
||||
<row>f00f00000f00000000000000000000000f00000000000000f00000</row> |
||||
<row>000f00000f00000000000000000000000f00000000000000000f00</row> |
||||
<row>000f00000f00000000000000000000000f00000000000000000f00</row> |
||||
<row>000f00000f00000000000000000000000f00000000000000000f00</row> |
||||
<row>000f00000f00000000000000000000000f00000000000000f00000</row> |
||||
<row>000f00000000f00f00f00f00f00f00000f00f00f00f00f00000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00000000f00f00f00f00f00f00000f00f00f00f00f00000000000</row> |
||||
<row>f00000f00000000000000000000000f00000000000000f00000000</row> |
||||
<row>f00000f00000000000000000000000f00000000000000000f00000</row> |
||||
<row>f00000f00000000000000000000000f00000000000000000f00000</row> |
||||
<row>f00000f00000000000000000000000f00000000000000000f00000</row> |
||||
<row>f00000f00000000000000000000000f00000000000000f00000000</row> |
||||
<row>f00000000f00f00f00f00f00f00000f00f00f00f00f00000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000f00f00f00f00f00f00000f00f00f00f00f00000000000000</row> |
||||
<row>000f00000000000000000000000f00000000000000f00000000f00</row> |
||||
<row>000f00000000000000000000000f00000000000000000f00000000</row> |
||||
<row>000f00000000000000000000000f00000000000000000f00000000</row> |
||||
<row>000f00000000000000000000000f00000000000000000f00000000</row> |
||||
<row>000f00000000000000000000000f00000000000000f00000000f00</row> |
||||
<row>000000f00f00f00f00f00f00000f00f00f00f00f00000000000f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000f00f00f00f00f00f00000f00f00f00f00f00000000000000f00</row> |
||||
<row>f00000000000000000000000f00000000000000f00000000f00000</row> |
||||
<row>f00000000000000000000000f00000000000000000f00000000000</row> |
||||
<row>f00000000000000000000000f00000000000000000f00000000000</row> |
||||
<row>f00000000000000000000000f00000000000000000f00000000f00</row> |
||||
<row>f00000000000000000000000f00000000000000f00000000f00000</row> |
||||
<row>000f00f00f00f00f00f00000f00f00f00f00f00000000000f00f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00f00f00f00f00000f00f00f00f00f00000000000000f00f00</row> |
||||
<row>000000000000000000000f00000000000000f00000000f00000000</row> |
||||
<row>000000000000000000000f00000000000000000f00000000000000</row> |
||||
<row>000000000000000000000f00000000000000000f00000000000f00</row> |
||||
<row>000000000000000000000f00000000000000000f00000000f00000</row> |
||||
<row>000000000000000000000f00000000000000f00000000f00000000</row> |
||||
<row>f00f00f00f00f00f00000f00f00f00f00f00000000000f00f00f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00f00f00f00000f00f00f00f00f00000000000000f00f00f00</row> |
||||
<row>000000000000000000f00000000000000f00000000f00000000000</row> |
||||
<row>000000000000000000f00000000000000000f00000000000000000</row> |
||||
<row>000000000000000000f00000000000000000f00000000000f00f00</row> |
||||
<row>000000000000000000f00000000000000000f00000000f00000000</row> |
||||
<row>000000000000000000f00000000000000f00000000f00000000000</row> |
||||
<row>f00f00f00f00f00000f00f00f00f00f00000000000f00f00f00f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00f00f00000f00f00f00f00f00000000000000f00f00f00f00</row> |
||||
<row>000000000000000f00000000000000f00000000f00000000000000</row> |
||||
<row>000000000000000f00000000000000000f00000000000000000000</row> |
||||
<row>000000000000000f00000000000000000f00000000000f00f00f00</row> |
||||
<row>000000000000000f00000000000000000f00000000f00000000000</row> |
||||
<row>000000000000000f00000000000000f00000000f00000000000000</row> |
||||
<row>f00f00f00f00000f00f00f00f00f00000000000f00f00f00f00f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00f00000f00f00f00f00f00000000000000f00f00f00f00f00</row> |
||||
<row>000000000000f00000000000000f00000000f00000000000000000</row> |
||||
<row>000000000000f00000000000000000f00000000000000000000000</row> |
||||
<row>000000000000f00000000000000000f00000000000f00f00f00f00</row> |
||||
<row>000000000000f00000000000000000f00000000f00000000000000</row> |
||||
<row>000000000000f00000000000000f00000000f00000000000000000</row> |
||||
<row>f00f00f00000f00f00f00f00f00000000000f00f00f00f00f00f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00000f00f00f00f00f00000000000000f00f00f00f00f00000</row> |
||||
<row>000000000f00000000000000f00000000f00000000000000000f00</row> |
||||
<row>000000000f00000000000000000f00000000000000000000000f00</row> |
||||
<row>000000000f00000000000000000f00000000000f00f00f00f00000</row> |
||||
<row>000000000f00000000000000000f00000000f00000000000000000</row> |
||||
<row>000000000f00000000000000f00000000f00000000000000000000</row> |
||||
<row>f00f00000f00f00f00f00f00000000000f00f00f00f00f00f00f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00000f00f00f00f00f00000000000000f00f00f00f00f00000000</row> |
||||
<row>000000f00000000000000f00000000f00000000000000000f00000</row> |
||||
<row>000000f00000000000000000f00000000000000000000000f00000</row> |
||||
<row>000000f00000000000000000f00000000000f00f00f00f00000000</row> |
||||
<row>000000f00000000000000000f00000000f00000000000000000000</row> |
||||
<row>000000f00000000000000f00000000f00000000000000000000000</row> |
||||
<row>f00000f00f00f00f00f00000000000f00f00f00f00f00f00f00000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000f00f00f00f00f00000000000000f00f00f00f00f00000000000</row> |
||||
<row>000f00000000000000f00000000f00000000000000000f00000f00</row> |
||||
<row>000f00000000000000000f00000000000000000000000f00000f00</row> |
||||
<row>000f00000000000000000f00000000000f00f00f00f00000000f00</row> |
||||
<row>000f00000000000000000f00000000f00000000000000000000f00</row> |
||||
<row>000f00000000000000f00000000f00000000000000000000000f00</row> |
||||
<row>000f00f00f00f00f00000000000f00f00f00f00f00f00f00000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00f00f00f00000000000000f00f00f00f00f00000000000f00</row> |
||||
<row>f00000000000000f00000000f00000000000000000f00000f00000</row> |
||||
<row>f00000000000000000f00000000000000000000000f00000f00000</row> |
||||
<row>f00000000000000000f00000000000f00f00f00f00000000f00000</row> |
||||
<row>f00000000000000000f00000000f00000000000000000000f00000</row> |
||||
<row>f00000000000000f00000000f00000000000000000000000f00000</row> |
||||
<row>f00f00f00f00f00000000000f00f00f00f00f00f00f00000000f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00f00f00000000000000f00f00f00f00f00000000000f00f00</row> |
||||
<row>000000000000f00000000f00000000000000000f00000f00000000</row> |
||||
<row>000000000000000f00000000000000000000000f00000f00000000</row> |
||||
<row>000000000000000f00000000000f00f00f00f00000000f00000000</row> |
||||
<row>000000000000000f00000000f00000000000000000000f00000000</row> |
||||
<row>000000000000f00000000f00000000000000000000000f00000000</row> |
||||
<row>f00f00f00f00000000000f00f00f00f00f00f00f00000000f00f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00f00000000000000f00f00f00f00f00000000000f00f00f00</row> |
||||
<row>000000000f00000000f00000000000000000f00000f00000000000</row> |
||||
<row>000000000000f00000000000000000000000f00000f00000000000</row> |
||||
<row>000000000000f00000000000f00f00f00f00000000f00000000000</row> |
||||
<row>000000000000f00000000f00000000000000000000f00000000000</row> |
||||
<row>000000000f00000000f00000000000000000000000f00000000000</row> |
||||
<row>f00f00f00000000000f00f00f00f00f00f00f00000000f00f00f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00000000000000f00f00f00f00f00000000000f00f00f00f00</row> |
||||
<row>000000f00000000f00000000000000000f00000f00000000000000</row> |
||||
<row>000000000f00000000000000000000000f00000f00000000000000</row> |
||||
<row>000000000f00000000000f00f00f00f00000000f00000000000000</row> |
||||
<row>000000000f00000000f00000000000000000000f00000000000000</row> |
||||
<row>000000f00000000f00000000000000000000000f00000000000000</row> |
||||
<row>f00f00000000000f00f00f00f00f00f00f00000000f00f00f00f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00000000000000f00f00f00f00f00000000000f00f00f00f00f00</row> |
||||
<row>000f00000000f00000000000000000f00000f00000000000000000</row> |
||||
<row>000000f00000000000000000000000f00000f00000000000000000</row> |
||||
<row>000000f00000000000f00f00f00f00000000f00000000000000000</row> |
||||
<row>000000f00000000f00000000000000000000f00000000000000000</row> |
||||
<row>000f00000000f00000000000000000000000f00000000000000000</row> |
||||
<row>f00000000000f00f00f00f00f00f00f00000000f00f00f00f00f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000000f00f00f00f00f00000000000f00f00f00f00f00000</row> |
||||
<row>f00000000f00000000000000000f00000f00000000000000000f00</row> |
||||
<row>000f00000000000000000000000f00000f00000000000000000f00</row> |
||||
<row>000f00000000000f00f00f00f00000000f00000000000000000f00</row> |
||||
<row>000f00000000f00000000000000000000f00000000000000000f00</row> |
||||
<row>f00000000f00000000000000000000000f00000000000000000f00</row> |
||||
<row>000000000f00f00f00f00f00f00f00000000f00f00f00f00f00000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000f00f00f00f00f00000000000f00f00f00f00f00000000</row> |
||||
<row>000000f00000000000000000f00000f00000000000000000f00000</row> |
||||
<row>f00000000000000000000000f00000f00000000000000000f00000</row> |
||||
<row>f00000000000f00f00f00f00000000f00000000000000000f00000</row> |
||||
<row>f00000000f00000000000000000000f00000000000000000f00000</row> |
||||
<row>000000f00000000000000000000000f00000000000000000f00000</row> |
||||
<row>000000f00f00f00f00f00f00f00000000f00f00f00f00f00000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000f00f00f00f00f00000000000f00f00f00f00f00000000000</row> |
||||
<row>000f00000000000000000f00000f00000000000000000f00000000</row> |
||||
<row>000000000000000000000f00000f00000000000000000f00000f00</row> |
||||
<row>000000000f00f00f00f00000000f00000000000000000f00000000</row> |
||||
<row>000000f00000000000000000000f00000000000000000f00000000</row> |
||||
<row>000f00000000000000000000000f00000000000000000f00000000</row> |
||||
<row>000f00f00f00f00f00f00f00000000f00f00f00f00f00000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000f00f00f00f00f00000000000f00f00f00f00f00000000000000</row> |
||||
<row>f00000000000000000f00000f00000000000000000f00000000f00</row> |
||||
<row>000000000000000000f00000f00000000000000000f00000f00000</row> |
||||
<row>000000f00f00f00f00000000f00000000000000000f00000000000</row> |
||||
<row>000f00000000000000000000f00000000000000000f00000000000</row> |
||||
<row>f00000000000000000000000f00000000000000000f00000000000</row> |
||||
<row>f00f00f00f00f00f00f00000000f00f00f00f00f00000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00f00f00f00000000000f00f00f00f00f00000000000000f00</row> |
||||
<row>000000000000000f00000f00000000000000000f00000000f00000</row> |
||||
<row>000000000000000f00000f00000000000000000f00000f00000000</row> |
||||
<row>000f00f00f00f00000000f00000000000000000f00000000000000</row> |
||||
<row>f00000000000000000000f00000000000000000f00000000000000</row> |
||||
<row>000000000000000000000f00000000000000000f00000000000000</row> |
||||
<row>f00f00f00f00f00f00000000f00f00f00f00f00000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00f00f00000000000f00f00f00f00f00000000000000f00f00</row> |
||||
<row>000000000000f00000f00000000000000000f00000000f00000f00</row> |
||||
<row>000000000000f00000f00000000000000000f00000f00000000f00</row> |
||||
<row>f00f00f00f00000000f00000000000000000f00000000000000f00</row> |
||||
<row>000000000000000000f00000000000000000f00000000000000f00</row> |
||||
<row>000000000000000000f00000000000000000f00000000000000f00</row> |
||||
<row>f00f00f00f00f00000000f00f00f00f00f00000000000000000f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00f00000000000f00f00f00f00f00000000000000f00f00000</row> |
||||
<row>000000000f00000f00000000000000000f00000000f00000f00000</row> |
||||
<row>000000000f00000f00000000000000000f00000f00000000f00000</row> |
||||
<row>f00f00f00000000f00000000000000000f00000000000000f00000</row> |
||||
<row>000000000000000f00000000000000000f00000000000000f00000</row> |
||||
<row>000000000000000f00000000000000000f00000000000000f00000</row> |
||||
<row>f00f00f00f00000000f00f00f00f00f00000000000000000f00000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00000000000f00f00f00f00f00000000000000f00f00000000</row> |
||||
<row>000000f00000f00000000000000000f00000000f00000f00000f00</row> |
||||
<row>000000f00000f00000000000000000f00000f00000000f00000000</row> |
||||
<row>f00f00000000f00000000000000000f00000000000000f00000000</row> |
||||
<row>000000000000f00000000000000000f00000000000000f00000000</row> |
||||
<row>000000000000f00000000000000000f00000000000000f00000f00</row> |
||||
<row>f00f00f00000000f00f00f00f00f00000000000000000f00000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00000000000f00f00f00f00f00000000000000f00f00000000f00</row> |
||||
<row>000f00000f00000000000000000f00000000f00000f00000f00000</row> |
||||
<row>000f00000f00000000000000000f00000f00000000f00000000000</row> |
||||
<row>f00000000f00000000000000000f00000000000000f00000000000</row> |
||||
<row>000000000f00000000000000000f00000000000000f00000000000</row> |
||||
<row>000000000f00000000000000000f00000000000000f00000f00000</row> |
||||
<row>f00f00000000f00f00f00f00f00000000000000000f00000000f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000f00f00f00f00f00000000000000f00f00000000f00f00</row> |
||||
<row>f00000f00000000000000000f00000000f00000f00000f00000000</row> |
||||
<row>f00000f00000000000000000f00000f00000000f00000000000000</row> |
||||
<row>000000f00000000000000000f00000000000000f00000000000f00</row> |
||||
<row>000000f00000000000000000f00000000000000f00000000000000</row> |
||||
<row>000000f00000000000000000f00000000000000f00000f00000000</row> |
||||
<row>f00000000f00f00f00f00f00000000000000000f00000000f00f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000f00f00f00f00f00000000000000f00f00000000f00f00f00</row> |
||||
<row>000f00000000000000000f00000000f00000f00000f00000000000</row> |
||||
<row>000f00000000000000000f00000f00000000f00000000000000000</row> |
||||
<row>000f00000000000000000f00000000000000f00000000000f00f00</row> |
||||
<row>000f00000000000000000f00000000000000f00000000000000000</row> |
||||
<row>000f00000000000000000f00000000000000f00000f00000000000</row> |
||||
<row>000000f00f00f00f00f00000000000000000f00000000f00f00f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000f00f00f00f00f00000000000000f00f00000000f00f00f00f00</row> |
||||
<row>f00000000000000000f00000000f00000f00000f00000000000000</row> |
||||
<row>f00000000000000000f00000f00000000f00000000000000000000</row> |
||||
<row>f00000000000000000f00000000000000f00000000000f00f00f00</row> |
||||
<row>f00000000000000000f00000000000000f00000000000000000000</row> |
||||
<row>f00000000000000000f00000000000000f00000f00000000000000</row> |
||||
<row>000f00f00f00f00f00000000000000000f00000000f00f00f00f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00f00f00f00000000000000f00f00000000f00f00f00f00f00</row> |
||||
<row>000000000000000f00000000f00000f00000f00000000000000000</row> |
||||
<row>000000000000000f00000f00000000f00000000000000000000000</row> |
||||
<row>000000000000000f00000000000000f00000000000f00f00f00f00</row> |
||||
<row>000000000000000f00000000000000f00000000000000000000000</row> |
||||
<row>000000000000000f00000000000000f00000f00000000000000000</row> |
||||
<row>f00f00f00f00f00000000000000000f00000000f00f00f00f00f00</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00f00f00000000000000f00f00000000f00f00f00f00f00000</row> |
||||
<row>000000000000f00000000f00000f00000f00000000000000000f00</row> |
||||
<row>000000000000f00000f00000000f00000000000000000000000f00</row> |
||||
<row>000000000000f00000000000000f00000000000f00f00f00f00000</row> |
||||
<row>000000000000f00000000000000f00000000000000000000000f00</row> |
||||
<row>000000000000f00000000000000f00000f00000000000000000f00</row> |
||||
<row>f00f00f00f00000000000000000f00000000f00f00f00f00f00000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00f00000000000000f00f00000000f00f00f00f00f00000000</row> |
||||
<row>000000000f00000000f00000f00000f00000000000000000f00000</row> |
||||
<row>000000000f00000f00000000f00000000000000000000000f00000</row> |
||||
<row>000000000f00000000000000f00000000000f00f00f00f00000000</row> |
||||
<row>000000000f00000000000000f00000000000000000000000f00000</row> |
||||
<row>000000000f00000000000000f00000f00000000000000000f00000</row> |
||||
<row>f00f00f00000000000000000f00000000f00f00f00f00f00000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00000000000000f00f00000000f00f00f00f00f00000000000</row> |
||||
<row>000000f00000000f00000f00000f00000000000000000f00000000</row> |
||||
<row>000000f00000f00000000f00000000000000000000000f00000000</row> |
||||
<row>000000f00000000000000f00000000000f00f00f00f00000000000</row> |
||||
<row>000000f00000000000000f00000000000000000000000f00000000</row> |
||||
<row>000000f00000000000000f00000f00000000000000000f00000000</row> |
||||
<row>f00f00000000000000000f00000000f00f00f00f00f00000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00000000000000f00f00000000f00f00f00f00f00000000000000</row> |
||||
<row>000f00000000f00000f00000f00000000000000000f00000000000</row> |
||||
<row>000f00000f00000000f00000000000000000000000f00000000000</row> |
||||
<row>000f00000000000000f00000000000f00f00f00f00000000000000</row> |
||||
<row>000f00000000000000f00000000000000000000000f00000000000</row> |
||||
<row>000f00000000000000f00000f00000000000000000f00000000000</row> |
||||
<row>f00000000000000000f00000000f00f00f00f00f00000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000000f00f00000000f00f00f00f00f00000000000000000</row> |
||||
<row>f00000000f00000f00000f00000000000000000f00000000000000</row> |
||||
<row>f00000f00000000f00000000000000000000000f00000000000000</row> |
||||
<row>f00000000000000f00000000000f00f00f00f00000000000000000</row> |
||||
<row>f00000000000000f00000000000000000000000f00000000000000</row> |
||||
<row>f00000000000000f00000f00000000000000000f00000000000000</row> |
||||
<row>000000000000000f00000000f00f00f00f00f00000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000f00f00000000f00f00f00f00f00000000000000000000</row> |
||||
<row>000000f00000f00000f00000000000000000f00000000000000000</row> |
||||
<row>000f00000000f00000000000000000000000f00000000000000000</row> |
||||
<row>000000000000f00000000000f00f00f00f00000000000000000000</row> |
||||
<row>000000000000f00000000000000000000000f00000000000000000</row> |
||||
<row>000000000000f00000f00000000000000000f00000000000000000</row> |
||||
<row>000000000000f00000000f00f00f00f00f00000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000f00f00000000f00f00f00f00f00000000000000000000000</row> |
||||
<row>000f00000f00000f00000000000000000f00000000000000000000</row> |
||||
<row>f00000000f00000000000000000000000f00000000000000000000</row> |
||||
<row>000000000f00000000000f00f00f00f00000000000000000000000</row> |
||||
<row>000000000f00000000000000000000000f00000000000000000000</row> |
||||
<row>000000000f00000f00000000000000000f00000000000000000000</row> |
||||
<row>000000000f00000000f00f00f00f00f00000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000f00f00000000f00f00f00f00f00000000000000000000000000</row> |
||||
<row>f00000f00000f00000000000000000f00000000000000000000000</row> |
||||
<row>000000f00000000000000000000000f00000000000000000000000</row> |
||||
<row>000000f00000000000f00f00f00f00000000000000000000000000</row> |
||||
<row>000000f00000000000000000000000f00000000000000000000000</row> |
||||
<row>000000f00000f00000000000000000f00000000000000000000000</row> |
||||
<row>000000f00000000f00f00f00f00f00000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00000000f00f00f00f00f00000000000000000000000000000</row> |
||||
<row>000f00000f00000000000000000f00000000000000000000000000</row> |
||||
<row>000f00000000000000000000000f00000000000000000000000000</row> |
||||
<row>000f00000000000f00f00f00f00000000000000000000000000000</row> |
||||
<row>000f00000000000000000000000f00000000000000000000000000</row> |
||||
<row>000f00000f00000000000000000f00000000000000000000000000</row> |
||||
<row>000f00000000f00f00f00f00f00000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00000000f00f00f00f00f00000000000000000000000000000000</row> |
||||
<row>f00000f00000000000000000f00000000000000000000000000000</row> |
||||
<row>f00000000000000000000000f00000000000000000000000000000</row> |
||||
<row>f00000000000f00f00f00f00000000000000000000000000000000</row> |
||||
<row>f00000000000000000000000f00000000000000000000000000000</row> |
||||
<row>f00000f00000000000000000f00000000000000000000000000000</row> |
||||
<row>f00000000f00f00f00f00f00000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000f00f00f00f00f00000000000000000000000000000000000</row> |
||||
<row>000f00000000000000000f00000000000000000000000000000000</row> |
||||
<row>000000000000000000000f00000000000000000000000000000000</row> |
||||
<row>000000000f00f00f00f00000000000000000000000000000000000</row> |
||||
<row>000000000000000000000f00000000000000000000000000000000</row> |
||||
<row>000f00000000000000000f00000000000000000000000000000000</row> |
||||
<row>000000f00f00f00f00f00000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000f00f00f00f00f00000000000000000000000000000000000000</row> |
||||
<row>f00000000000000000f00000000000000000000000000000000000</row> |
||||
<row>000000000000000000f00000000000000000000000000000000000</row> |
||||
<row>000000f00f00f00f00000000000000000000000000000000000000</row> |
||||
<row>000000000000000000f00000000000000000000000000000000000</row> |
||||
<row>f00000000000000000f00000000000000000000000000000000000</row> |
||||
<row>000f00f00f00f00f00000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00f00f00f00000000000000000000000000000000000000000</row> |
||||
<row>000000000000000f00000000000000000000000000000000000000</row> |
||||
<row>000000000000000f00000000000000000000000000000000000000</row> |
||||
<row>000f00f00f00f00000000000000000000000000000000000000000</row> |
||||
<row>000000000000000f00000000000000000000000000000000000000</row> |
||||
<row>000000000000000f00000000000000000000000000000000000000</row> |
||||
<row>f00f00f00f00f00000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00f00f00000000000000000000000000000000000000000000</row> |
||||
<row>000000000000f00000000000000000000000000000000000000000</row> |
||||
<row>000000000000f00000000000000000000000000000000000000000</row> |
||||
<row>f00f00f00f00000000000000000000000000000000000000000000</row> |
||||
<row>000000000000f00000000000000000000000000000000000000000</row> |
||||
<row>000000000000f00000000000000000000000000000000000000000</row> |
||||
<row>f00f00f00f00000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00f00000000000000000000000000000000000000000000000</row> |
||||
<row>000000000f00000000000000000000000000000000000000000000</row> |
||||
<row>000000000f00000000000000000000000000000000000000000000</row> |
||||
<row>f00f00f00000000000000000000000000000000000000000000000</row> |
||||
<row>000000000f00000000000000000000000000000000000000000000</row> |
||||
<row>000000000f00000000000000000000000000000000000000000000</row> |
||||
<row>f00f00f00000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00f00000000000000000000000000000000000000000000000000</row> |
||||
<row>000000f00000000000000000000000000000000000000000000000</row> |
||||
<row>000000f00000000000000000000000000000000000000000000000</row> |
||||
<row>f00f00000000000000000000000000000000000000000000000000</row> |
||||
<row>000000f00000000000000000000000000000000000000000000000</row> |
||||
<row>000000f00000000000000000000000000000000000000000000000</row> |
||||
<row>f00f00000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>f00000000000000000000000000000000000000000000000000000</row> |
||||
<row>000f00000000000000000000000000000000000000000000000000</row> |
||||
<row>000f00000000000000000000000000000000000000000000000000</row> |
||||
<row>f00000000000000000000000000000000000000000000000000000</row> |
||||
<row>000f00000000000000000000000000000000000000000000000000</row> |
||||
<row>000f00000000000000000000000000000000000000000000000000</row> |
||||
<row>f00000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>f00000000000000000000000000000000000000000000000000000</row> |
||||
<row>f00000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>f00000000000000000000000000000000000000000000000000000</row> |
||||
<row>f00000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
<frame duration="100"> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
</blm> |
@ -0,0 +1,147 @@
@@ -0,0 +1,147 @@
|
||||
#heart.blm |
||||
#countdown.blm |
||||
test.blm |
||||
klonfish-dancing.blm |
||||
blm/invasion.blm |
||||
blm/torus.blm |
||||
blm/antiwar.blm |
||||
blm/billard.blm |
||||
blm/x-ball.blm |
||||
blm/game.blm |
||||
blm/tropfen.blm |
||||
blm/moewe_frontal.blm |
||||
blm/fireworks_2.blm |
||||
blm/thunderstorm.blm |
||||
blm/biker.blm |
||||
blm/fantasticspace.blm |
||||
blm/allyourbase.blm |
||||
blm/21st_century_man.blm |
||||
blm/dont_try_at_home.blm |
||||
blm/spin.blm |
||||
blm/heart.blm |
||||
blm/encyclops.blm |
||||
blm/fant.blm |
||||
blm/schnecke.blm |
||||
blm/peao.blm |
||||
blm/falling_pix.blm |
||||
blm/tetris.blm |
||||
blm/text.blm |
||||
blm/landing_zone.blm |
||||
blm/dance_micky_chickee.blm |
||||
blm/supermaennchen.blm |
||||
blm/allon.blm |
||||
blm/kame.blm |
||||
blm/kiss.blm |
||||
blm/fallingrows.blm |
||||
blm/jesus.blm |
||||
blm/vlhurg.blm |
||||
blm/wasserhahn.blm |
||||
blm/gewaber.blm |
||||
blm/tla_them.blm |
||||
blm/quix_glitter.blm |
||||
blm/herz.blm |
||||
blm/sanduhr.blm |
||||
blm/defect_car.blm |
||||
blm/peepshow.blm |
||||
blm/countdown.blm |
||||
blm/luftballoons.blm |
||||
blm/impaexpa.blm |
||||
blm/wein_trinken.blm |
||||
blm/coffee.blm |
||||
blm/camel.blm |
||||
blm/matrix.blm |
||||
blm/life.blm |
||||
blm/snake.blm |
||||
blm/windshield.blm |
||||
blm/raumschiff_enterprise.blm |
||||
blm/pumpkin.blm |
||||
blm/yeastman.blm |
||||
blm/genomix.blm |
||||
blm/bad_luck.blm |
||||
blm/om_sweet_om.blm |
||||
blm/invaders_andreas.blm |
||||
blm/manifesto5.blm |
||||
blm/chat_noir.blm |
||||
blm/rakete.blm |
||||
blm/manifesto3.blm |
||||
blm/relativity.blm |
||||
blm/fussballer.blm |
||||
blm/sinus.blm |
||||
blm/love_triangles.blm |
||||
blm/ascii_people.blm |
||||
blm/hanoi.blm |
||||
blm/monster.blm |
||||
blm/come_together.blm |
||||
blm/matrix_maennchen.blm |
||||
blm/silent_night.blm |
||||
blm/halt.blm |
||||
blm/das_leben_ist_schoen.blm |
||||
blm/moving_car.blm |
||||
blm/spirals.blm |
||||
blm/mandel.blm |
||||
blm/alloff.blm |
||||
blm/manifesto1.blm |
||||
blm/quantorenlyrik.blm |
||||
blm/manifesto4.blm |
||||
blm/warterad.blm |
||||
blm/surprise.blm |
||||
blm/oneon.blm |
||||
blm/manifesto7.blm |
||||
blm/fireworks.blm |
||||
blm/labyrinth.blm |
||||
blm/brainfuck.blm |
||||
blm/die_erde.blm |
||||
blm/klo.blm |
||||
blm/psychowarrior.blm |
||||
blm/timeless.blm |
||||
blm/auge.blm |
||||
blm/worm.blm |
||||
blm/colagoboom.blm |
||||
blm/musterbeispiel.blm |
||||
blm/fallingpixels.blm |
||||
blm/franknstoned.blm |
||||
blm/rollo_grow.blm |
||||
blm/illuminati.blm |
||||
blm/3rd_advent.blm |
||||
blm/chaosknoten.blm |
||||
blm/be_happy.blm |
||||
blm/binary_god.blm |
||||
blm/kingzilla.blm |
||||
blm/paradox.blm |
||||
blm/dj.blm |
||||
blm/hut.blm |
||||
blm/sortofselftest.blm |
||||
blm/blubb.blm |
||||
blm/bleeeh.blm |
||||
blm/rain.blm |
||||
blm/flash.blm |
||||
blm/femina_light.blm |
||||
blm/gator_rogat.blm |
||||
blm/speedit.blm |
||||
blm/scooter.blm |
||||
blm/pixie_in_the_box.blm |
||||
blm/zeichen.blm |
||||
blm/humanistic.blm |
||||
blm/pacman_saga.blm |
||||
blm/die_autobahn.blm |
||||
blm/mytest3.blm |
||||
blm/yin_yang.blm |
||||
blm/klettermensch.blm |
||||
blm/baustein.blm |
||||
blm/pwm.blm |
||||
blm/babelfish.blm |
||||
blm/mytest2.blm |
||||
blm/winslows.blm |
||||
blm/dance.blm |
||||
blm/martelo.blm |
||||
blm/raindrops.blm |
||||
blm/g.blm |
||||
blm/clear.blm |
||||
blm/sport_haelt_fit.blm |
||||
blm/kreise.blm |
||||
blm/railroad.blm |
||||
blm/3D_cube.blm |
||||
blm/balloon.blm |
||||
blm/flipp.blm |
||||
blm/mytest.blm |
||||
blm/tux.blm |
@ -0,0 +1,143 @@
@@ -0,0 +1,143 @@
|
||||
blm/invasion.blm |
||||
blm/torus.blm |
||||
blm/antiwar.blm |
||||
blm/billard.blm |
||||
blm/x-ball.blm |
||||
blm/game.blm |
||||
blm/tropfen.blm |
||||
blm/moewe_frontal.blm |
||||
blm/fireworks_2.blm |
||||
blm/thunderstorm.blm |
||||
blm/biker.blm |
||||
blm/fantasticspace.blm |
||||
blm/allyourbase.blm |
||||
blm/21st_century_man.blm |
||||
blm/dont_try_at_home.blm |
||||
blm/spin.blm |
||||
blm/heart.blm |
||||
blm/encyclops.blm |
||||
blm/fant.blm |
||||
blm/schnecke.blm |
||||
blm/peao.blm |
||||
blm/falling_pix.blm |
||||
blm/tetris.blm |
||||
blm/text.blm |
||||
blm/landing_zone.blm |
||||
blm/dance_micky_chickee.blm |
||||
blm/supermaennchen.blm |
||||
blm/allon.blm |
||||
blm/kame.blm |
||||
blm/kiss.blm |
||||
blm/fallingrows.blm |
||||
blm/jesus.blm |
||||
blm/vlhurg.blm |
||||
blm/wasserhahn.blm |
||||
blm/gewaber.blm |
||||
blm/tla_them.blm |
||||
blm/quix_glitter.blm |
||||
blm/herz.blm |
||||
blm/sanduhr.blm |
||||
blm/defect_car.blm |
||||
blm/peepshow.blm |
||||
blm/countdown.blm |
||||
blm/luftballoons.blm |
||||
blm/impaexpa.blm |
||||
blm/wein_trinken.blm |
||||
blm/coffee.blm |
||||
blm/camel.blm |
||||
blm/matrix.blm |
||||
blm/life.blm |
||||
blm/snake.blm |
||||
blm/windshield.blm |
||||
blm/raumschiff_enterprise.blm |
||||
blm/pumpkin.blm |
||||
blm/yeastman.blm |
||||
blm/genomix.blm |
||||
blm/bad_luck.blm |
||||
blm/om_sweet_om.blm |
||||
blm/invaders_andreas.blm |
||||
blm/manifesto5.blm |
||||
blm/chat_noir.blm |
||||
blm/rakete.blm |
||||
blm/manifesto3.blm |
||||
blm/relativity.blm |
||||
blm/fussballer.blm |
||||
blm/sinus.blm |
||||
blm/love_triangles.blm |
||||
blm/ascii_people.blm |
||||
blm/hanoi.blm |
||||
blm/monster.blm |
||||
blm/come_together.blm |
||||
blm/matrix_maennchen.blm |
||||
blm/silent_night.blm |
||||
blm/halt.blm |
||||
blm/das_leben_ist_schoen.blm |
||||
blm/moving_car.blm |
||||
blm/spirals.blm |
||||
blm/mandel.blm |
||||
blm/alloff.blm |
||||
blm/manifesto1.blm |
||||
blm/quantorenlyrik.blm |
||||
blm/manifesto4.blm |
||||
blm/warterad.blm |
||||
blm/surprise.blm |
||||
blm/oneon.blm |
||||
blm/manifesto7.blm |
||||
blm/fireworks.blm |
||||
blm/labyrinth.blm |
||||
blm/brainfuck.blm |
||||
blm/die_erde.blm |
||||
blm/klo.blm |
||||
blm/psychowarrior.blm |
||||
blm/timeless.blm |
||||
blm/auge.blm |
||||
blm/worm.blm |
||||
blm/colagoboom.blm |
||||
blm/musterbeispiel.blm |
||||
blm/fallingpixels.blm |
||||
blm/franknstoned.blm |
||||
blm/rollo_grow.blm |
||||
blm/illuminati.blm |
||||
blm/3rd_advent.blm |
||||
blm/chaosknoten.blm |
||||
blm/be_happy.blm |
||||
blm/binary_god.blm |
||||
blm/kingzilla.blm |
||||
blm/paradox.blm |
||||
blm/dj.blm |
||||
blm/hut.blm |
||||
blm/sortofselftest.blm |
||||
blm/blubb.blm |
||||
blm/bleeeh.blm |
||||
blm/rain.blm |
||||
blm/flash.blm |
||||
blm/femina_light.blm |
||||
blm/gator_rogat.blm |
||||
blm/speedit.blm |
||||
blm/scooter.blm |
||||
blm/pixie_in_the_box.blm |
||||
blm/zeichen.blm |
||||
blm/humanistic.blm |
||||
blm/pacman_saga.blm |
||||
blm/die_autobahn.blm |
||||
blm/mytest3.blm |
||||
blm/yin_yang.blm |
||||
blm/klettermensch.blm |
||||
blm/baustein.blm |
||||
blm/pwm.blm |
||||
blm/babelfish.blm |
||||
blm/mytest2.blm |
||||
blm/winslows.blm |
||||
blm/dance.blm |
||||
blm/martelo.blm |
||||
blm/raindrops.blm |
||||
blm/g.blm |
||||
blm/clear.blm |
||||
blm/sport_haelt_fit.blm |
||||
blm/kreise.blm |
||||
blm/railroad.blm |
||||
blm/3D_cube.blm |
||||
blm/balloon.blm |
||||
blm/flipp.blm |
||||
blm/mytest.blm |
||||
blm/tux.blm |
@ -0,0 +1,456 @@
@@ -0,0 +1,456 @@
|
||||
#!/usr/bin/python |
||||
|
||||
import time |
||||
import random |
||||
import sys |
||||
import colorsys |
||||
|
||||
import blup.frame |
||||
import blup.output |
||||
|
||||
|
||||
__numbers = { |
||||
1: [[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0],[0,0,1,0,0,]], |
||||
2: [[1,1,1,1,1],[0,0,0,0,1],[1,1,1,1,1],[1,0,0,0,0],[1,1,1,1,1,]], |
||||
3: [[1,1,1,1,1],[0,0,0,0,1],[0,0,1,1,1],[0,0,0,0,1],[1,1,1,1,1,]], |
||||
4: [[1,0,0,0,1],[1,0,0,0,1],[1,1,1,1,1],[0,0,0,0,1],[0,0,0,0,1,]], |
||||
5: [[1,1,1,1,1],[1,0,0,0,0],[1,1,1,1,1],[0,0,0,0,1],[1,1,1,1,1,]], |
||||
6: [[1,1,1,1,1],[1,0,0,0,0],[1,1,1,1,1],[1,0,0,0,1],[1,1,1,1,1,]], |
||||
7: [[1,1,1,1,1],[0,0,0,0,1],[0,0,0,0,1],[0,0,0,0,1],[0,0,0,0,1,]], |
||||
8: [[1,1,1,1,1],[1,0,0,0,1],[1,1,1,1,1],[1,0,0,0,1],[1,1,1,1,1,]], |
||||
9: [[1,1,1,1,1],[1,0,0,0,1],[1,1,1,1,1],[0,0,0,0,1],[1,1,1,1,1,]], |
||||
0: [[1,1,1,1,1],[1,0,0,0,1],[1,0,0,0,1],[1,0,0,0,1],[1,1,1,1,1,]], |
||||
} |
||||
|
||||
class Paddle(object): |
||||
def __init__(self, playground, xpos, ypos, size): |
||||
self.__playground = playground |
||||
self.__xpos = xpos |
||||
self.__size = size |
||||
self.__ypos = ypos |
||||
self.__nextMove = 0 |
||||
|
||||
@property |
||||
def nextMove(self): |
||||
return self.__nextMove |
||||
@nextMove.setter |
||||
def nextMove(self, value): |
||||
if value in [-1, 0, 1]: |
||||
self.__nextMove = value |
||||
else: |
||||
raise ValueError('invalid move') |
||||
|
||||
@property |
||||
def xpos(self): |
||||
return self.__xpos |
||||
@property |
||||
def ypos(self): |
||||
return self.__ypos |
||||
@property |
||||
def size(self): |
||||
return self.__size |
||||
@property |
||||
def nextMove(self): |
||||
return self.__nextMove |
||||
|
||||
def containsPoint(self, x, y): |
||||
if x == self.__xpos and y in range(self.__ypos, self.__ypos + self.__size): |
||||
return True |
||||
else: |
||||
return False |
||||
|
||||
def nextMoveUp(self): |
||||
self.__nextMove = -1 |
||||
|
||||
def nextMoveDown(self): |
||||
self.__nextMove = 1 |
||||
|
||||
def doNextMove(self): |
||||
if self.__nextMove is not 0: |
||||
if self.__nextMove == -1 and self.__ypos > 0: |
||||
self.__ypos -= 1 |
||||
elif self.__nextMove == 1 and self.__ypos + self.__size < self.__playground.height: |
||||
self.__ypos += 1 |
||||
self.__nextMove = 0 |
||||
|
||||
class Wall(object): |
||||
HORIZONTAL = 1 |
||||
VERTICAL = 2 |
||||
def __init__(self, orientation): |
||||
self.orientation = orientation |
||||
|
||||
class Ball(object): |
||||
def __init__(self, playground, xpos, ypos, xspeed, yspeed): |
||||
self.__playground = playground |
||||
self.__xpos = xpos |
||||
self.__ypos = ypos |
||||
self.__xspeed = xspeed |
||||
self.__yspeed = yspeed |
||||
self.__hitCallbacks = [] |
||||
|
||||
@property |
||||
def xpos(self): |
||||
return self.__xpos |
||||
@property |
||||
def ypos(self): |
||||
return self.__ypos |
||||
@property |
||||
def xspeed(self): |
||||
return self.__xspeed |
||||
@property |
||||
def yspeed(self): |
||||
return self.__yspeed |
||||
@xpos.setter |
||||
def xpos(self, value): |
||||
self.__xpos = value |
||||
@ypos.setter |
||||
def ypos(self, value): |
||||
self.__ypos = value |
||||
@xspeed.setter |
||||
def xspeed(self, value): |
||||
self.__xspeed = value |
||||
@yspeed.setter |
||||
def yspeed(self, value): |
||||
self.__yspeed = value |
||||
|
||||
def addHitCallback(self, callback): |
||||
self.__hitCallbacks.append(callback) |
||||
|
||||
def doHitCallbacks(self, obj): |
||||
for callback in self.__hitCallbacks: |
||||
callback(obj) |
||||
|
||||
def move(self, ignorePaddles=False): |
||||
if self.__xspeed == 0 and self.__yspeed == 0: |
||||
return |
||||
foundpos = False |
||||
while not foundpos: |
||||
if self.__xspeed == 0 and self.__yspeed == 0: |
||||
break |
||||
newx = self.__xpos + self.__xspeed |
||||
newy = self.__ypos + self.__yspeed |
||||
|
||||
newobj = self.__playground.getObjectAtPosition(newx, newy) |
||||
if isinstance(newobj, Wall): |
||||
self.doHitCallbacks(newobj) |
||||
|
||||
# bounce off at horizontal wall |
||||
if newobj.orientation == Wall.HORIZONTAL: |
||||
self.__yspeed *= -1 |
||||
else: |
||||
#self.__xspeed *= -1 |
||||
foundpos = True |
||||
elif isinstance(newobj, Paddle) and not ignorePaddles: |
||||
self.doHitCallbacks(newobj) |
||||
self.__xspeed *= -1 |
||||
|
||||
# bounce off at the paddle |
||||
if self.__yspeed == 0: |
||||
if self.__playground.getObjectAtPosition(newobj.xpos, newobj.ypos - 1) is None: |
||||
self.__yspeed = -1 |
||||
elif self.__plauground.getObjectAtPosition(newobj.xpos, newobj.ypos + 1) is None: |
||||
self.__yspeed = 1 |
||||
else: |
||||
if newobj.xpos < self.__xpos: |
||||
if self.__playground.getObjectAtPosition(self.__xpos - 1, self.__ypos) is None: |
||||
self.__yspeed *= -1 |
||||
if newobj.xpos > self.__xpos: |
||||
if self.__playground.getObjectAtPosition(self.__xpos + 1, self.__ypos) is None: |
||||
self.__yspeed *= -1 |
||||
|
||||
if newobj.nextMove != 0 and random.randint(0, 2) == 0: |
||||
self.__yspeed += newobj.nextMove |
||||
elif abs(self.__yspeed) != 1: |
||||
self.__yspeed = 1 if self.__yspeed > 0 else -1 |
||||
else: |
||||
adjobj = self.__playground.getObjectAtPosition(newx, self.__ypos) |
||||
if not ignorePaddles and isinstance(adjobj, Paddle): |
||||
self.doHitCallbacks(adjobj) |
||||
self.__xspeed *= -1 |
||||
else: |
||||
foundpos = True |
||||
|
||||
|
||||
self.__xpos = newx |
||||
self.__ypos = newy |
||||
|
||||
class Playground(object): |
||||
def __init__(self, width, height, paddlesize=3): |
||||
self.__width = width |
||||
self.__height = height |
||||
|
||||
paddleLeft = Paddle(self, 0, (height - paddlesize)/2, paddlesize) |
||||
paddleRight = Paddle(self, width - 1, (height - paddlesize)/2, paddlesize) |
||||
self.__paddles = [paddleLeft, paddleRight] |
||||
|
||||
self.__ball = Ball(self, 0, 0, 1, 1) |
||||
self.__gameTickCallbacks = [] |
||||
self.__newRoundCallbacks = [] |
||||
|
||||
@property |
||||
def width(self): |
||||
return self.__width |
||||
@property |
||||
def height(self): |
||||
return self.__height |
||||
@property |
||||
def leftPaddle(self): |
||||
return self.__paddles[0] |
||||
@property |
||||
def rightPaddle(self): |
||||
return self.__paddles[1] |
||||
@property |
||||
def ball(self): |
||||
return self.__ball |
||||
|
||||
def containsPoint(self, x, y): |
||||
if x >= 0 and x < self.width and y >= 0 and y < self.height: |
||||
return True |
||||
else: |
||||
return False |
||||
|
||||
def addGameTickCallback(self, callback): |
||||
self.__gameTickCallbacks.append(callback) |
||||
print 'registered callback',callback |
||||
def addNewRoundCallback(self, callback): |
||||
self.__newRoundCallbacks.append(callback) |
||||
|
||||
def getObjectAtPosition(self, x, y): |
||||
if x >= self.__width or x < 0: |
||||
return Wall(Wall.VERTICAL) |
||||
elif y >= self.__height or y < 0: |
||||
return Wall(Wall.HORIZONTAL) |
||||
elif y == self.__ball.ypos and x == self.__ball.xpos: |
||||
return self.__ball |
||||
else: |
||||
for paddle in self.__paddles: |
||||
if paddle.containsPoint(x, y): |
||||
return paddle |
||||
|
||||
def play(self, serve=None): |
||||
leftPaddle = self.__paddles[0] |
||||
rightPaddle = self.__paddles[1] |
||||
ball = self.__ball |
||||
|
||||
if serve not in self.__paddles: |
||||
serve = self.__paddles[random.randint(0, 1)] |
||||
|
||||
ball.ypos = self.__height / 2 |
||||
ball.xpos = self.__width / 2 |
||||
if serve == rightPaddle and self.__width % 2 == 1: |
||||
ball.xpos += 1 |
||||
|
||||
ball.yspeed = 0 |
||||
if serve == rightPaddle: |
||||
ball.xspeed = 1 |
||||
else: |
||||
ball.yspeed = -1 |
||||
|
||||
for callback in self.__newRoundCallbacks: |
||||
callback() |
||||
|
||||
while True: |
||||
time.sleep(0.1) |
||||
ball.move() |
||||
leftPaddle.doNextMove() |
||||
rightPaddle.doNextMove() |
||||
|
||||
if not self.containsPoint(ball.xpos, ball.ypos): |
||||
break |
||||
|
||||
for callback in self.__gameTickCallbacks: |
||||
print callback |
||||
callback() |
||||
|
||||
if ball.xpos >= self.width: |
||||
return leftPaddle |
||||
elif ball.xpos <= 0: |
||||
return rightPaddle |
||||
|
||||
|
||||
def getRandomColor(maxval): |
||||
return map(lambda x: int(round(x*maxval)), colorsys.hsv_to_rgb(random.random(), 1, 1)) |
||||
#return map(lambda x: int(round(x*maxval)), colorsys.hsv_to_rgb(random.random(), random.random(), random.random())) |
||||
|
||||
|
||||
class PlaygroundPainter(object): |
||||
def __init__(self, out, dimension, playground): |
||||
self.__out = out |
||||
self.__playground = playground |
||||
self.__dimension = dimension |
||||
self.__playground.addGameTickCallback(self.update) |
||||
self.__playground.ball.addHitCallback(self.ballhit) |
||||
|
||||
if dimension.channels == 1: |
||||
self.__ballColor = 1 |
||||
self.__paddleColor = 1 |
||||
else: |
||||
self.__ballColor = getRandomColor(dimension.depth - 1) |
||||
self.__leftPaddleColor = getRandomColor(dimension.depth - 1) |
||||
self.__rightPaddleColor = getRandomColor(dimension.depth - 1) |
||||
|
||||
def update(self): |
||||
frame = blup.frame.Frame(self.__dimension) |
||||
#self.__paddleColor = getRandomColor(self.__dimension.depth - 1) |
||||
|
||||
if self.__dimension.channels == 3: |
||||
for x in range(self.__dimension.width): |
||||
for y in range(self.__dimension.height): |
||||
frame.setPixel(x, y, (0,0,0)) |
||||
|
||||
frame.setPixel(self.__playground.ball.xpos, self.__playground.ball.ypos, self.__ballColor) |
||||
|
||||
for i in range(self.__playground.leftPaddle.size): |
||||
frame.setPixel(self.__playground.leftPaddle.xpos, self.__playground.leftPaddle.ypos + i, self.__leftPaddleColor) |
||||
for i in range(self.__playground.rightPaddle.size): |
||||
frame.setPixel(self.__playground.rightPaddle.xpos, self.__playground.rightPaddle.ypos + i, self.__rightPaddleColor) |
||||
|
||||
self.__out.sendFrame(frame) |
||||
|
||||
def ballhit(self, obj): |
||||
if self.__dimension.channels == 3: |
||||
if isinstance(obj, Paddle): |
||||
self.__ballColor = getRandomColor(self.__dimension.depth - 1) |
||||
if obj.xpos == 0: |
||||
self.__leftPaddleColor = self.__ballColor |
||||
else: |
||||
self.__rightPaddleColor = self.__ballColor |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class PongBot(object): |
||||
def __init__(self, playground, ownPaddle, dullness=0): |
||||
self.__playground = playground |
||||
self.__ownPaddle = ownPaddle |
||||
self.__dullness = dullness |
||||
self.__playground.addGameTickCallback(self.onGameTick) |
||||
self.__playground.addNewRoundCallback(self.onNewRound) |
||||
self.__playground.ball.addHitCallback(self.onHit) |
||||
self.__hitpoint = None |
||||
self.__oldHitpoint = None |
||||
self.__dull = False |
||||
|
||||
def updateHitpoint(self): |
||||
origball = self.__playground.ball |
||||
ball = Ball(self.__playground, origball.xpos, origball.ypos, origball.xspeed, origball.yspeed) |
||||
ball.move() |
||||
hitpoint = None |
||||
while hitpoint is None: |
||||
if ball.xpos >= self.__playground.width - 1 or ball.xpos <= 0: |
||||
hitpoint = (ball.xpos, ball.ypos) |
||||
break |
||||
|
||||
ball.move(ignorePaddles=True) |
||||
self.__oldHitpoint = self.__hitpoint |
||||
self.__hitpoint = hitpoint |
||||
|
||||
def onHit(self, obj): |
||||
print 'hit!', obj |
||||
if isinstance(obj, Paddle) and obj is not self.__ownPaddle: |
||||
self.updateHitpoint() |
||||
|
||||
if self.__dullness > random.randint(0, 4): |
||||
self.__dull = True |
||||
else: |
||||
self.__dull = False |
||||
|
||||
def onNewRound(self): |
||||
self.updateHitpoint() |
||||
self.__oldHitpoint = None |
||||
self.__dull = False |
||||
|
||||
def onGameTick(self): |
||||
#self.updateHitpoint() |
||||
print 'hitpoint', self.__hitpoint |
||||
(hitx, hity) = self.__hitpoint |
||||
|
||||
|
||||
|
||||
if hitx == self.__ownPaddle.xpos: |
||||
if abs(self.__playground.ball.xpos - self.__ownPaddle.xpos) < 15: |
||||
if not self.__dull: |
||||
if not self.__ownPaddle.containsPoint(hitx, hity): |
||||
print 'moving!!' |
||||
if self.__ownPaddle.ypos < hity: |
||||
self.__ownPaddle.nextMoveDown() |
||||
else: |
||||
self.__ownPaddle.nextMoveUp() |
||||
else: |
||||
if self.__ownPaddle.containsPoint(hitx, hity): |
||||
print 'moving!!' |
||||
if hity < self.__ownPaddle.size: |
||||
self.__ownPaddle.nextMoveDown() |
||||
else: |
||||
self.__ownPaddle.nextMoveUp() |
||||
|
||||
elif self.__dull: |
||||
r = random.randint(-1, 1) |
||||
if r == -1: |
||||
self.__ownPaddle.nextMoveUp() |
||||
elif r == 1: |
||||
self.__ownPaddle.nextMoveDown() |
||||
|
||||
|
||||
def displayScore(out, dimension, leftScore, rightScore, delay=0): |
||||
leftNumber = __numbers[leftScore] |
||||
rightNumber = __numbers[rightScore] |
||||
|
||||
frame = blup.frame.Frame(dimension) |
||||
|
||||
if dimension.channels == 1: |
||||
dotcolor = 1 |
||||
numcolor = 1 |
||||
else: |
||||
dotcolor = getRandomColor(dimension.depth - 1) |
||||
numcolor = getRandomColor(dimension.depth - 1) |
||||
|
||||
frame.setPixel(8, 1, dotcolor) |
||||
frame.setPixel(9, 1, dotcolor) |
||||
frame.setPixel(8, 2, dotcolor) |
||||
frame.setPixel(9, 2, dotcolor) |
||||
frame.setPixel(8, 4, dotcolor) |
||||
frame.setPixel(9, 4, dotcolor) |
||||
frame.setPixel(8, 5, dotcolor) |
||||
frame.setPixel(9, 5, dotcolor) |
||||
|
||||
for x in range(5): |
||||
for y in range(5): |
||||
if leftNumber[y][x] == 1: |
||||
frame.setPixel(x+1, y+1, numcolor) |
||||
if rightNumber[y][x] == 1: |
||||
frame.setPixel(x+12, y+1, numcolor) |
||||
|
||||
out.sendFrame(frame) |
||||
if delay > 0: |
||||
time.sleep(delay / 1000.0) |
||||
|
||||
|
||||
|
||||
def main(): |
||||
print 'running a \'Bot vs. Bot\' round...' |
||||
#out = blup.output.getOutput('blp:localhost:42421') |
||||
#out = blup.output.getOutput('e3blp:bastel0:4242') |
||||
out = blup.output.getOutput('e3blp:localhost:4242') |
||||
#out = blup.output.getOutput('e3blp:bbunt:42429') |
||||
#dim = blup.frame.FrameDimension(18, 8, 8, 3) |
||||
dim = blup.frame.FrameDimension(22, 16, 255, 3) |
||||
|
||||
playground = Playground(22, 16, paddlesize=4) |
||||
pp = PlaygroundPainter(out, dim, playground) |
||||
|
||||
leftPaddle = playground.leftPaddle |
||||
rightPaddle = playground.rightPaddle |
||||
ball = playground.ball |
||||
|
||||
leftBot = PongBot(playground, leftPaddle, 1) |
||||
rightBot = PongBot(playground, rightPaddle, 1) |
||||
|
||||
print playground.play() |
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
main() |
||||
|
||||
|
@ -0,0 +1,178 @@
@@ -0,0 +1,178 @@
|
||||
#!/usr/bin/python |
||||
|
||||
import time |
||||
import threading |
||||
import sys |
||||
|
||||
import blup.frame |
||||
import blup.output |
||||
sys.path.append('/var/tmp/r0ket/tools/game') |
||||
#sys.path.append('/var/tmp/r0ket_misc/game_noprint') |
||||
import r0ketrem0te.game |
||||
import pong |
||||
|
||||
logo = [ |
||||
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], |
||||
[1,1,1,0,1,1,1,1,0,1,0,0,1,0,0,1,1,0], |
||||
[1,0,1,0,1,0,0,1,0,1,1,0,1,0,1,0,0,1], |
||||
[1,0,1,0,1,0,0,1,0,1,0,1,1,0,1,0,0,0], |
||||
[1,1,1,0,1,0,0,1,0,1,0,0,1,0,1,0,1,1], |
||||
[1,0,0,0,1,0,0,1,0,1,0,0,1,0,1,0,0,1], |
||||
[1,0,0,0,1,1,1,1,0,1,0,0,1,0,0,1,1,0], |
||||
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] |
||||
] |
||||
onePlayer = [ |
||||
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], |
||||
[0,0,1,1,0,0,1,1,1,1,0,0,0,1,1,1,0,0], |
||||
[0,1,0,1,0,0,1,0,0,1,0,0,1,0,0,0,1,0], |
||||
[0,0,0,1,0,0,1,0,0,1,0,0,0,0,0,1,0,0], |
||||
[0,0,0,1,0,0,1,1,1,1,0,0,0,0,1,0,0,0], |
||||
[0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0], |
||||
[0,0,1,1,1,0,1,0,0,0,0,0,0,0,1,0,0,0], |
||||
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] |
||||
] |
||||
|
||||
def convertPixels(dimension, pixels): |
||||
p = [] |
||||
maxval = dimension.depth - 1 |
||||
for i in range(len(pixels)): |
||||
row = [] |
||||
for j in range(len(pixels[i])): |
||||
if pixels[i][j] == 1: |
||||
row.append((maxval, maxval, maxval)) |
||||
else: |
||||
row.append((0,0,0)) |
||||
p.append(row) |
||||
return p |
||||
|
||||
class R0ketPongPlayer(object): |
||||
def __init__(self, playground, ownPaddle, r0ketPlayer): |
||||
self.__playground = playground |
||||
self.__ownPaddle = ownPaddle |
||||
self.__r0ketPlayer = r0ketPlayer |
||||
self.__ready = False |
||||
|
||||
@property |
||||
def ownPaddle(self): |
||||
return self.__ownPaddle |
||||
|
||||
@property |
||||
def ready(self): |
||||
return self.__ready |
||||
|
||||
def receivedPacket(self, packet): |
||||
if self.__r0ketPlayer is not None: |
||||
if packet.id == self.__r0ketPlayer.id: |
||||
if isinstance(packet, r0ketrem0te.packets.Button): |
||||
print 'button=', packet.button |
||||
if packet.button == 1: |
||||
self.__ownPaddle.nextMoveUp() |
||||
print 'up' |
||||
if packet.button == 2: |
||||
self.__ownPaddle.nextMoveDown() |
||||
print 'down' |
||||
if packet.button == 16: |
||||
self.__ready = True |
||||
|
||||
class R0ketPong(object): |
||||
def __init__(self, out, color=False, port='/dev/ttyACM0', gameName='pong'): |
||||
self.__playground = None |
||||
self.__out = out |
||||
self.__rem0te = r0ketrem0te.game.Game(port, gameName, 83, 87, [ord(x) for x in 'REM0T'], 2, False) |
||||
self.__rem0te.registerPlayerCallback(self.playerCallback) |
||||
self.__color = color |
||||
|
||||
self.__players = [] |
||||
self.__playersChangedCondition = threading.Condition() |
||||
|
||||
if color: |
||||
self.__dimension = blup.frame.FrameDimension(18, 8, 8, 3) |
||||
else: |
||||
self.__dimension = blup.frame.FrameDimension(18, 8, 2, 1) |
||||
|
||||
def playerCallback(self, action, player): |
||||
if self.__playground is None: |
||||
return |
||||
print '\n\n\n\nplayer added',player,'\n\n\n\n' |
||||
if action == 'added' and len(self.__players) < 2: |
||||
if len(self.__players) == 0: |
||||
playerPaddle = self.__playground.leftPaddle |
||||
else: |
||||
playerPaddle = self.__playground.rightPaddle |
||||
player = R0ketPongPlayer(self.__playground, playerPaddle, player) |
||||
self.__rem0te.bridge.registerCallback(player.receivedPacket) |
||||
self.__players.append(player) |
||||
with self.__playersChangedCondition: |
||||
self.__playersChangedCondition.notifyAll() |
||||
|
||||
def runGame(self): |
||||
print 'starting a game...' |
||||
scoreLeft = 0 |
||||
scoreRight = 0 |
||||
|
||||
self.__playground = pong.Playground(18, 8) |
||||
pp = pong.PlaygroundPainter(out, self.__dimension, self.__playground) |
||||
|
||||
while len(self.__players) == 0: |
||||
print 'waiting for player...' |
||||
with self.__playersChangedCondition: |
||||
self.__playersChangedCondition.wait() |
||||
|
||||
frame = blup.frame.Frame(self.__dimension) |
||||
global onePlayer |
||||
if self.__color: |
||||
frame.pixels = convertPixels(self.__dimension, onePlayer) |
||||
else: |
||||
frame.pixels = onePlayer |
||||
self.__out.sendFrame(frame) |
||||
|
||||
while len(self.__players) == 1 and not self.__players[0].ready: |
||||
print 'waiting for another player...' |
||||
time.sleep(1) |
||||
self.__out.sendFrame(frame) |
||||
|
||||
frame = blup.frame.Frame(self.__dimension) |
||||
global logo |
||||
if self.__color: |
||||
frame.pixels = convertPixels(self.__dimension, logo) |
||||
else: |
||||
frame.pixels = logo |
||||
self.__out.sendFrame(frame) |
||||
time.sleep(1) |
||||
|
||||
if len(self.__players) == 1: |
||||
bot = pong.PongBot(self.__playground, self.__playground.rightPaddle, 2) |
||||
self.__players.append(bot) |
||||
|
||||
while max(scoreLeft, scoreRight) < 9: |
||||
winner = self.__playground.play() |
||||
if winner is self.__players[0].ownPaddle: |
||||
scoreLeft += 1 |
||||
else: |
||||
scoreRight += 1 |
||||
pong.displayScore(self.__out, self.__dimension, scoreLeft, scoreRight, 3000) |
||||
|
||||
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) |
||||
self.__out.sendFrame(frame) |
||||
|
||||
self.__playground = None |
||||
self.__players = [] |
||||
|
||||
|
||||
|
||||
out = blup.output.getOutput('e3blp:localhost:4242') |
||||
|
||||
|
||||
p0ng = R0ketPong(out, color=True) |
||||
while True: |
||||
p0ng.runGame() |
||||
|
||||
sys.exit(0) |
||||
|
||||
|
||||
|
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<blm width="18" height="8" bits="4" channels="3"> |
||||
<header> |
||||
<creator>Blimp (version 0.1.5)</creator> |
||||
</header> |
||||
|
||||
<frame duration="500"> |
||||
<row>000000000FF00F0FF0000000000000000000FF00F0FF0000000000</row> |
||||
<row>0000000000F00F00F00000000000000000000F00F00F0000000000</row> |
||||
<row>000000000000090000000000000000000000000090000000000000</row> |
||||
<row>0000000000F00F00F00000900000000000000F00F00F0000090000</row> |
||||
<row>0000900900F0FF00F00900900000000900900F0FF00F0090090000</row> |
||||
<row>0000900000F00F00F00000000000000900000F00F00F0000000000</row> |
||||
<row>0000000000F00F00F00000000000000000000F00F00F0000000000</row> |
||||
<row>000000000090000090000000000000000000090000090000000000</row> |
||||
</frame> |
||||
|
||||
<frame duration="500"> |
||||
<row>000000000FF00F0FF0000000000000000000FF00F0FF0000000000</row> |
||||
<row>0000000000F00F00F00000000000000000000F00F00F0000000000</row> |
||||
<row>000000000000090000000000000000000000000090000000000000</row> |
||||
<row>0000900000F00F00F00000000000000900000F00F00F0000000000</row> |
||||
<row>0000900900F0FF00F00900900000000900900F0FF00F0090090000</row> |
||||
<row>0000000000F00F00F00000900000000000000F00F00F0000090000</row> |
||||
<row>0000000000F00F00F00000000000000000000F00F00F0000000000</row> |
||||
<row>000000000090000090000000000000000000090000090000000000</row> |
||||
</frame> |
||||
</blm> |
@ -0,0 +1,39 @@
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<blm width="18" height="8" bits="4" channels="3"> |
||||
<header> |
||||
<creator>Blimp (version 0.1.5)</creator> |
||||
</header> |
||||
|
||||
<frame duration="500"> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>0000FF0000000FF000000FF0000000FF0000000F0F000000F0F000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>0000FF0000000FF000000FF0000000FF0000000F0F000000F0F000</row> |
||||
<row>0000000FF0FF000000000000FF0FF0000000000000F0FF0F000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
|
||||
<frame duration="500"> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>0000F00000000F0000000F00000000F0000000000F00000000F000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>0000F00000000F0000000F00000000F0000000000F00000000F000</row> |
||||
<row>0000000F00F0000000000000F00F0000000000000000F00F000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
|
||||
<frame duration="500"> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000FFF000000FFF000000FFF000000FFF000000FFF000000FFF000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000FFF000000FFF000000FFF000000FFF000000FFF000000FFF000</row> |
||||
<row>000000FFFFFF000000000000FFFFFF000000000000FFFFFF000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
</blm> |
@ -0,0 +1,50 @@
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<blm width="18" height="8" bits="4" channels="3"> |
||||
<header> |
||||
<creator>Blimp (version 0.1.5)</creator> |
||||
</header> |
||||
|
||||
<frame duration="500"> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000FF0000000FF0000000F0F000000F0F000</row> |
||||
<row>0000FF0000000FF000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000FF0000000FF0000000F0F000000F0F000</row> |
||||
<row>0000FF0000000FF000000000FF0FF0000000000000F0FF0F000000</row> |
||||
<row>0000000FF0FF000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
|
||||
<frame duration="500"> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>0000F00000000F000000000000000000000000000F00000000F000</row> |
||||
<row>000000000000000000000F00000000F00000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>0000F00000000F000000000000000000000000000F00000000F000</row> |
||||
<row>0000000F00F0000000000F00000000F0000000000000F00F000000</row> |
||||
<row>000000000000000000000000F00F00000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
|
||||
<frame duration="500"> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000FFF000000FFF000000FFF000000FFF000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000FFF000000FFF000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000FFF000000FFF000000FFF000000FFF000000000000000000000</row> |
||||
<row>000000FFFFFF000000000000FFFFFF000000000FFF000000FFF000</row> |
||||
<row>000000000000000000000000000000000000000000FFFFFF000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
|
||||
<frame duration="500"> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000FF0000000FF0000FF0000000FF0000FF0000000FF0000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000FF0000000FF0000FF0000000FF0000FF0000000FF0000000</row> |
||||
<row>000000000FF0FF0000000000FF0FF0000000000FF0FF0000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
<row>000000000000000000000000000000000000000000000000000000</row> |
||||
</frame> |
||||
</blm> |
@ -0,0 +1,135 @@
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/python |
||||
|
||||
# inspired by: |
||||
# http://lodev.org/cgtutor/plasma.html |
||||
|
||||
import sys |
||||
import time |
||||
import math |
||||
#import pygame |
||||
import colorsys |
||||
import blup.frame |
||||
import blup.output |
||||
|
||||
|
||||
class Plasma(object): |
||||
def __init__(self, width, height): |
||||
self.width = width |
||||
self.height = height |
||||
|
||||
self.pixelvalues = [[0] * height for i in xrange(width)] |
||||
self.pixelsReady = False |
||||
self.offset = 0 |
||||
self.update() |
||||
|
||||
@property |
||||
def minValue(self): |
||||
return min(map(min, self.pixelvalues)) |
||||
|
||||
@property |
||||
def maxValue(self): |
||||
return max(map(max, self.pixelvalues)) |
||||
|
||||
def applyPalette(self, palette): |
||||
min = self.minValue |
||||
max = self.maxValue |
||||
norm = lambda x: ((x - min) / max) |
||||
|
||||
pixeldata = [ [None] * self.height for i in xrange(self.width) ] |
||||
|
||||
for x in xrange(self.width): |
||||
rowvalues = self.pixelvalues[x] |
||||
for y in xrange(self.height): |
||||
pixeldata[x][y] = palette.getColorValue(norm(rowvalues[y])) |
||||
|
||||
return pixeldata |
||||
|
||||
def update(self): |
||||
#if self.pixelsReady: |
||||
# return |
||||
self.offset += 1 |
||||
|
||||
for x in xrange(self.width): |
||||
for y in xrange(self.height): |
||||
xx = x+self.offset |
||||
yy = y+self.offset |
||||
p = 128 + 128 * math.sin(xx / 9.0) |
||||
p += 128 + 128 * math.sin(yy / 16.0) |
||||
p += 128 + 128 * math.sin((yy + xx) / 16.0) |
||||
p += 128 + 128 * math.sin(math.sqrt(yy*yy + xx*xx) / 16.0) |
||||
p += 128 + 8 * math.sin(math.sqrt(yy* xx) / 16.0) |
||||
self.pixelvalues[x][y] = p |
||||
self.pixelsReady = True |
||||
|
||||
class Palette(object): |
||||
def __init__(self): |
||||
self.offset = 0 |
||||
self.cache = {} |
||||
|
||||
def getColorValue(self, x): |
||||
x = round(x, 3) |
||||
if self.cache.has_key(x): |
||||
return self.cache[x] |
||||
c = colorsys.hsv_to_rgb((x + self.offset) % 1, 1, 1) |
||||
c = map(lambda x: int(x * 255), c) |
||||
self.cache[x] = c |
||||
return c |
||||
|
||||
def update(self): |
||||
self.cache = {} |
||||
self.offset += 0.01 |
||||
if self.offset > 1: |
||||
self.offset -= 1 |
||||
|
||||
############################################## |
||||
|
||||
w = 22 |
||||
h = 16 |
||||
|
||||
#w = 80 |
||||
#h = 18 |
||||
|
||||
scalex = 30 |
||||
scaley = 60 |
||||
|
||||
plasma = Plasma(w,h) |
||||
palette= Palette() |
||||
|
||||
#screen = pygame.display.set_mode((w*scalex, h*scaley)) |
||||
#pygame.display.update() |
||||
|
||||
#dim = blup.frame.FrameDimension(w, h, 8, 3) |
||||
dim = blup.frame.FrameDimension(w, h, 256, 3) |
||||
#out = blup.output.getOutput('serialblup:/dev/ttyUSB0:115200') |
||||
#out = blup.output.getOutput('colorfulshell') |
||||
#out = blup.output.getOutput('e3blp:bastel0:4242') |
||||
out = blup.output.getOutput('e3blp') |
||||
|
||||
while True: |
||||
#screen.fill((0,0,0)) |
||||
|
||||
#print 'updating palette...' |
||||
palette.update() |
||||
#print 'updating plasma...' |
||||
plasma.update() |
||||
#print 'applying palette...' |
||||
pixeldata = plasma.applyPalette(palette) |
||||
|
||||
#print 'drawing...' |
||||
f = blup.frame.Frame(dim) |
||||
for x in xrange(w): |
||||
#rowdata = pixeldata[x] |
||||
for y in xrange(h): |
||||
#pygame.draw.rect(screen, pixeldata[x][y], (x*scalex, y*scaley, scalex, scaley), 0) |
||||
#pygame.draw.circle(screen, rowdata[y], (x, y), 1, 1) |
||||
i= f.setPixel(x, y, pixeldata[x][y]) |
||||
#print frame.channels, frame.depth |
||||
#print 'done drawing' |
||||
|
||||
out.sendFrame(f) |
||||
|
||||
#pygame.display.update() |
||||
#print 'done updating' |
||||
#time.sleep(0.05) |
||||
|
||||
|
@ -0,0 +1,152 @@
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/python |
||||
|
||||
# inspired by: |
||||
# http://lodev.org/cgtutor/plasma.html |
||||
|
||||
import sys |
||||
import time |
||||
import math |
||||
#import pygame |
||||
import colorsys |
||||
from neopixel import * |
||||
|
||||
|
||||
class Plasma(object): |
||||
def __init__(self, width, height): |
||||
self.width = width |
||||
self.height = height |
||||
|
||||
self.pixelvalues = [[0] * height for i in xrange(width)] |
||||
self.pixelsReady = False |
||||
self.offset = 0 |
||||
self.update() |
||||
|
||||
@property |
||||
def minValue(self): |
||||
return min(map(min, self.pixelvalues)) |
||||
|
||||
@property |
||||
def maxValue(self): |
||||
return max(map(max, self.pixelvalues)) |
||||
|
||||
def applyPalette(self, palette): |
||||
min = self.minValue |
||||
max = self.maxValue |
||||
norm = lambda x: ((x - min) / max) |
||||
|
||||
pixeldata = [ [None] * self.height for i in xrange(self.width) ] |
||||
|
||||
for x in xrange(self.width): |
||||
rowvalues = self.pixelvalues[x] |
||||
for y in xrange(self.height): |
||||
pixeldata[x][y] = palette.getColorValue(norm(rowvalues[y])) |
||||
|
||||
return pixeldata |
||||
|
||||
def update(self): |
||||
#if self.pixelsReady: |
||||
# return |
||||
self.offset += 1 |
||||
|
||||
for x in xrange(self.width): |
||||
for y in xrange(self.height): |
||||
xx = x+self.offset |
||||
yy = y+self.offset |
||||
#xx = 10*x+self.offset |
||||
#yy = 10*y+self.offset |
||||
p = 128 + 128 * math.sin(xx / 9.0) |
||||
p += 128 + 128 * math.sin(yy / 16.0) |
||||
p += 128 + 128 * math.sin((yy + xx) / 16.0) |
||||
p += 128 + 128 * math.sin(math.sqrt(yy*yy + xx*xx) / 16.0) |
||||
p += 128 + 8 * math.sin(math.sqrt(yy* xx) / 16.0) |
||||
self.pixelvalues[x][y] = p |
||||
self.pixelsReady = True |
||||
|
||||
class Palette(object): |
||||
def __init__(self): |
||||
self.offset = 0 |
||||
self.cache = {} |
||||
|
||||
def getColorValue(self, x): |
||||
x = round(x, 3) |
||||
if self.cache.has_key(x): |
||||
return self.cache[x] |
||||
c = colorsys.hsv_to_rgb((x + self.offset) % 1, 1, 1) |
||||
#c = map(lambda x: int(x * 7), c) |
||||
depth = 255 |
||||
c = map(lambda x: int(x * depth), c) |
||||
self.cache[x] = c |
||||
return c |
||||
|
||||
def update(self): |
||||
self.cache = {} |
||||
self.offset += 0.01 |
||||
if self.offset > 1: |
||||
self.offset -= 1 |
||||
|
||||
############################################## |
||||
|
||||
w = 22 |
||||
h = 16 |
||||
|
||||
#w = 80 |
||||
#h = 18 |
||||
|
||||
scalex = 30 |
||||
scaley = 60 |
||||
|
||||
plasma = Plasma(w,h) |
||||
palette= Palette() |
||||
|
||||
#screen = pygame.display.set_mode((w*scalex, h*scaley)) |
||||
#pygame.display.update() |
||||
|
||||
##dim = blup.frame.FrameDimension(w, h, 8, 3) |
||||
#dim = blup.frame.FrameDimension(w, h, 8, 3) |
||||
##out = blup.output.getOutput('serialblup:/dev/ttyUSB0:115200') |
||||
##out = blup.output.getOutput('colorfulshell') |
||||
##out = blup.output.getOutput('e3blp:bastel0:4242') |
||||
#out = blup.output.getOutput('e3blp') |
||||
|
||||
|
||||
# LED strip configuration: |
||||
LED_COUNT = 352 # Number of LED pixels. |
||||
LED_PIN = 13 # GPIO pin connected to the pixels (must support PWM!). |
||||
LED_FREQ_HZ = 800000 # LED signal frequency in hertz (usually 800khz) |
||||
LED_DMA = 5 # DMA channel to use for generating signal (try 5) |
||||
LED_BRIGHTNESS = 50 # Set to 0 for darkest and 255 for brightest |
||||
LED_INVERT = False # True to invert the signal (when using NPN transistor level shift) |
||||
LED_PWM = 1 |
||||
|
||||
strip = Adafruit_NeoPixel(LED_COUNT, LED_PIN, LED_FREQ_HZ, LED_DMA, LED_INVERT, LED_BRIGHTNESS, LED_PWM) |
||||
strip.begin() |
||||
|
||||
while True: |
||||
#screen.fill((0,0,0)) |
||||
|
||||
#print 'updating palette...' |
||||
palette.update() |
||||
#print 'updating plasma...' |
||||
plasma.update() |
||||
#print 'applying palette...' |
||||
pixeldata = plasma.applyPalette(palette) |
||||
|
||||
#print 'drawing...' |
||||
#f = blup.frame.Frame(dim) |
||||
for y in xrange(h): |
||||
for x in xrange(w): |
||||
if x%2 == 0: |
||||
lednum = x*h + y |
||||
else: |
||||
lednum = x*h - y + 15 |
||||
strip.setPixelColor(lednum, Color(*pixeldata[x][y])) |
||||
strip.show() |
||||
#print 'done drawing' |
||||
|
||||
#out.sendFrame(f) |
||||
|
||||
#pygame.display.update() |
||||
#print 'done updating' |
||||
#time.sleep(0.01) |
||||
|
||||
|
@ -0,0 +1,311 @@
@@ -0,0 +1,311 @@
|
||||
#!/usr/bin/env python3 |
||||
|
||||
import time |
||||
import enum |
||||
import math |
||||
import collections |
||||
import blup.frame |
||||
import blup.output |
||||
import random |
||||
|
||||
|
||||
class InvalidMoveError(Exception): |
||||
pass |
||||
|
||||
class Point(collections.namedtuple('Point', ['x', 'y'])): |
||||
__slots__ = () |
||||
|
||||
def __add__(self, other): |
||||
return Point(self.x + other.x, self.y + other.y) |
||||
|
||||
def floored(self): |
||||
return Point(math.floor(self.x), math.floor(self.y)) |
||||
|
||||
Block = collections.namedtuple('Block', ['pos', 'color']) |
||||
|
||||
|
||||
class Tetrimino(): |
||||
def __init__(self, shape, playground, pos): |
||||
self.shape = shape |
||||
self.playground = playground |
||||
self.pos = pos |
||||
|
||||
@property |
||||
def width(self): |
||||
x = list(map(lambda s: s.pos.x, self.shape)) |
||||
return max(x) - min(x) |
||||
|
||||
@property |
||||
def height(self): |
||||
y = list(map(lambda s: s.pos.y, self.shape)) |
||||
return max(y) - min(y) |
||||
|
||||
@property |
||||
def blocks(self): |
||||
ret = { Block((self.pos + b.pos).floored(), b.color) for b in |
||||
self.shape } |
||||
return ret |
||||
|
||||
def __calc_points(self, pos=None, shape=None): |
||||
if pos is None: |
||||
pos = self.pos |
||||
if shape is None: |
||||
shape = self.shape |
||||
return { (pos + b.pos).floored() for b in shape } |
||||
|
||||
@property |
||||
def points(self): |
||||
return self.__calc_points() |
||||
|
||||
def __check_collision(self, newpoints): |
||||
if not self.playground.contains_points(newpoints): |
||||
raise InvalidMoveError('out of playground bounds') |
||||
print(self.playground.block_points) |
||||
print(self.playground.blocks) |
||||
print('new', newpoints) |
||||
if not self.playground.block_points.isdisjoint(newpoints): |
||||
raise InvalidMoveError('new position already occupied') |
||||
other_mino_points = self.playground.mino_points - self.points |
||||
if not other_mino_points.isdisjoint(newpoints): |
||||
raise InvalidMoveError('other Tetrimino at new position') |
||||
|
||||
def rotate(self, ccw=False): |
||||
if ccw: |
||||
transform = lambda s: Block(Point(s.pos.y, -s.pos.x), s.color) |
||||
else: |
||||
transform = lambda s: Block(Point(-s.pos.y, s.pos.x), s.color) |
||||
newshape = set(map(transform, self.shape)) |
||||
newpoints = self.__calc_points(shape=newshape) |
||||
self.__check_collision(newpoints) |
||||
self.shape = newshape |
||||
|
||||
def move(self, m): |
||||
newpos = self.pos + m |
||||
newpoints = self.__calc_points(pos=newpos) |
||||
self.__check_collision(newpoints) |
||||
self.pos = newpos |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class TetriL(Tetrimino): |
||||
def __init__(self, playground, pos): |
||||
color = (255,165,0) |
||||
points = [(-1, 1), (-1, 0), (0, 0), (1, 0)] |
||||
shape = { Block(Point(x, y), color) for (x, y) in points } |
||||
Tetrimino.__init__(self, shape, playground, pos) |
||||
|
||||
|
||||
class TetriJ(Tetrimino): |
||||
def __init__(self, playground, pos): |
||||
color = (0,0,255) |
||||
points = [(-1, 0), (0, 0), (1, 0), (1, 1)] |
||||
shape = { Block(Point(x, y), color) for (x, y) in points } |
||||
Tetrimino.__init__(self, shape, playground, pos) |
||||
|
||||
|
||||
class TetriI(Tetrimino): |
||||
def __init__(self, playground, pos): |
||||
color = (0,255,255) |
||||
points = {(1.5, -0.5), (0.5, -0.5), (-0.5, -0.5), (-1.5, -0.5)} |
||||
shape = { Block(Point(x, y), color) for (x, y) in points } |
||||
Tetrimino.__init__(self, shape, playground, pos) |
||||
|
||||
|
||||
class TetriO(Tetrimino): |
||||
def __init__(self, playground, pos): |
||||
color = (255,255,0) |
||||
points = {(-0.5, -0.5), (-0.5, 0.5), (0.5, -0.5), (0.5, 0.5)} |
||||
shape = { Block(Point(x, y), color) for (x, y) in points } |
||||
Tetrimino.__init__(self, shape, playground, pos) |
||||
|
||||
|
||||
class TetriS(Tetrimino): |
||||
def __init__(self, playground, pos): |
||||
color = (128,255,0) |
||||
points = {(-1, 0), (0, 0), (0, -1), (1, -1)} |
||||
shape = { Block(Point(x, y), color) for (x, y) in points } |
||||
Tetrimino.__init__(self, shape, playground, pos) |
||||
|
||||
|
||||
class TetriZ(Tetrimino): |
||||
def __init__(self, playground, pos): |
||||
color = (255,0,0) |
||||
points = {(-1, 0), (0, 0), (0, 1), (1, 1)} |
||||
shape = { Block(Point(x, y), color) for (x, y) in points } |
||||
Tetrimino.__init__(self, shape, playground, pos) |
||||
|
||||
|
||||
class TetriT(Tetrimino): |
||||
def __init__(self, playground, pos): |
||||
color = (128,0,128) |
||||
points = {(-1, 0), (0, 0), (1, 0), (0, 1)} |
||||
shape = { Block(Point(x, y), color) for (x, y) in points } |
||||
Tetrimino.__init__(self, shape, playground, pos) |
||||
|
||||
|
||||
class Playground(): |
||||
def __init__(self, width=10, height=22): |
||||
self.width = width |
||||
self.height = height |
||||
self.blocks = set() |
||||
self.minos = set() |
||||
|
||||
@property |
||||
def block_points(self): |
||||
return set([ b.pos for b in self.blocks ]) |
||||
|
||||
@property |
||||
def mino_points(self): |
||||
return set.union(set(), *[ m.points for m in self.minos ]) |
||||
|
||||
def contains_points(self, points): |
||||
for p in points: |
||||
if p.x >= self.width or p.y >= self.height or p.x < 0 or p.y < 0: |
||||
return False |
||||
return True |
||||
|
||||
def paint(self, frame, xpos, ypos): |
||||
for x in range(frame.dimension.width): |
||||
for y in range(frame.dimension.height): |
||||
frame.setPixel(x, y, (0, 0, 0)) |
||||
for b in set.union(self.blocks, *[ m.blocks for m in self.minos ]): |
||||
frame.setPixel(xpos + int(b.pos.x), ypos + int(b.pos.y), b.color) |
||||
|
||||
|
||||
class TtrsGame(): |
||||
def __init__(self, playground): |
||||
self.playground = playground |
||||
self.running = False |
||||
self.next_move = 0 |
||||
self.rotate = False |
||||
self.drop = False |
||||
self.tick_callbacks = [] |
||||
|
||||
def add_tick_callback(self, cb): |
||||
self.tick_callbacks.append(cb) |
||||
|
||||
def run(self): |
||||
self.running = True |
||||
spawnpos = Point(self.playground.width // 2, 0) |
||||
mino = None |
||||
TICK_TIME = 0.1 |
||||
FALL_INTERVAL = 5 |
||||
MOVE_INTERVAL = 1 |
||||
ticks = 0 |
||||
lastfall = 0 |
||||
lastmove = 0 |
||||
while self.running: |
||||
for cb in self.tick_callbacks: |
||||
cb() |
||||
#time.sleep(TICK_TIME) |
||||
ticks += 1 |
||||
|
||||
x = input() |
||||
if x == 'a': |
||||
self.next_move = -1 |
||||
elif x == 'd': |
||||
self.next_move = 1 |
||||
elif x == 'w': |
||||
self.rotate = True |
||||
elif x == 's': |
||||
self.drop = True |
||||
else: |
||||
self.drop = False |
||||
self.rotate = False |
||||
self.next_move = 0 |
||||
|
||||
|
||||
if mino is None: |
||||
newminocls = random.choice(Tetrimino.__subclasses__()) |
||||
mino = newminocls(self.playground, spawnpos) |
||||
self.playground.minos = {mino} |
||||
|
||||
for y in range(self.playground.height): |
||||
row = { Point(x, y) for x in range(self.playground.width) } |
||||
if row.issubset(self.playground.block_points): |
||||
for b in list(self.playground.blocks): |
||||
if b.pos.y == y: |
||||
self.playground.blocks.remove(b) |
||||
elif b.pos.y < y: |
||||
self.playground.blocks.remove(b) |
||||
newb = Block(b.pos + Point(0, 1), b.color) |
||||
self.playground.blocks.add(newb) |
||||
|
||||
|
||||
|
||||
if ticks - lastfall >= FALL_INTERVAL or self.drop: |
||||
lastfall = ticks |
||||
try: |
||||
mino.move(Point(0, 1)) |
||||
except InvalidMoveError: |
||||
self.playground.blocks.update(mino.blocks) |
||||
mino = None |
||||
self.playground.minos = set() |
||||
continue |
||||
|
||||
if ticks - lastmove >= MOVE_INTERVAL and self.next_move != 0: |
||||
lastmove = ticks |
||||
try: |
||||
mino.move(Point(self.next_move, 0)) |
||||
except InvalidMoveError: |
||||
pass |
||||
self.next_move = 0 |
||||
|
||||
if self.rotate: |
||||
self.rotate = False |
||||
try: |
||||
mino.rotate() |
||||
except InvalidMoveError: |
||||
pass |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__': |
||||
w = 22 |
||||
h = 16 |
||||
|
||||
dim = blup.frame.FrameDimension(w, h, 256, 3) |
||||
frame = blup.frame.Frame(dim) |
||||
out = blup.output.getOutput('e3blp') |
||||
|
||||
pg = Playground(10, 16) |
||||
pg.paint(frame, 0, 0) |
||||
out.sendFrame(frame) |
||||
time.sleep(0.5) |
||||
|
||||
def repaint(): |
||||
pg.paint(frame, 0, 0) |
||||
out.sendFrame(frame) |
||||
|
||||
game = TtrsGame(pg) |
||||
game.add_tick_callback(repaint) |
||||
game.run() |
||||
|
||||
#t = TetriL(pg, Point(2, 2)) |
||||
#pg.minos.add(t) |
||||
#pg.paint(frame, 0, 0) |
||||
#out.sendFrame(frame) |
||||
#time.sleep(0.5) |
||||
|
||||
#for i in range(10): |
||||
# t.rotate(ccw=(i>=5)) |
||||
# pg.paint(frame, 0, 0) |
||||
# out.sendFrame(frame) |
||||
# time.sleep(0.1) |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,49 @@
@@ -0,0 +1,49 @@
|
||||
|
||||
|
||||
import math |
||||
import xml.etree.ElementTree as ET |
||||
|
||||
def writeBml(anim, filename): |
||||
root = ET.Element('blm') |
||||
root.attrib['width'] = str(anim.dimension.width) |
||||
root.attrib['height'] = str(anim.dimension.height) |
||||
bits = int(math.ceil(math.log(anim.dimension.depth, 2))) |
||||
root.attrib['bits'] = str(bits) |
||||
root.attrib['channels'] = str(anim.dimension.channels) |
||||
|
||||
header = ET.Element('header') |
||||
for (name, val) in anim.tags: |
||||
elem = ET.Element(name) |
||||
elem.text = str(val) |
||||
header.append(elem) |
||||
root.append(header) |
||||
|
||||
for animFrame in anim: |
||||
frame = ET.Element('frame') |
||||
frame.attrib['duration'] = str(animFrame.delay) |
||||
for y in range(anim.dimension.height): |
||||
rowData = '' |
||||
for x in range(anim.dimension.width): |
||||
p = animFrame.getPixel(x,y) |
||||
if type(p) == tuple or type(p) == list: |
||||
for v in p: |
||||
if bits >= 5: |
||||
rowData += '%02x' % (v) |
||||
else: |
||||
rowData += '%x' % (v) |
||||
else: |
||||
if bits >= 5: |
||||
rowData += '%02x' % (p) |
||||
else: |
||||
rowData += '%x' % (p) |
||||
|
||||
row = ET.Element('row') |
||||
row.text = rowData |
||||
frame.append(row) |
||||
root.append(frame) |
||||
|
||||
f = open(filename, 'w') |
||||
ET.ElementTree(root).write(f) |
||||
f.close() |
||||
|
||||
|
Loading…
Reference in new issue