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