You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
150 lines
4.3 KiB
150 lines
4.3 KiB
#!/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)
|
|
|