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.
151 lines
4.3 KiB
151 lines
4.3 KiB
7 years ago
|
#!/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)
|