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