#!/usr/bin/env python3 import sys import random import math import argparse import collections import blup.frame import blup.output import blup.animation import blup.writebml import colorsys import functools WIDTH = 22 HEIGHT = 16 DEPTH = 256 class Point(collections.namedtuple('Point', ['x', 'y'])): def __add__(self, other): return Point(self.x + other.x, self.y + other.y) def __sub__(self, other): return Point(self.x - other.x, self.y - other.y) @property def len(self): return math.sqrt(self.x**2 + self.y**2) class PointLenOrder: def __init__(self, value): self.value = value def __eq__(self, other): return other.value.len == self.value.len def __lt__(self, other): return other.value.len < self.value.len DIRECTIONS = [ Point(x, y) for x in [-1, 1] for y in [-1, 1] ] def convert_color(c): return list(map(lambda x: int(round(x*(DEPTH-1))), c)) def get_random_color(): return colorsys.hsv_to_rgb(random.random(), 0.5 + random.random() / 2, 1) class Line: def __init__(self, worldsize): self.worldsize = worldsize self.line = [Point(random.randint(0, worldsize.x), random.randint(0, worldsize.y))] self.slen = 1 self.dir = random.choice(DIRECTIONS) self.color = get_random_color() diaglen = int(math.sqrt(worldsize.x**2 + worldsize.y**2)) self.maxlen = random.randint(diaglen, diaglen * 3) self.fadeout_time = 2 + random.random() * 2 self.fadeout_remaining = None self.finished = False def check_bounds(self, p): return p.x < self.worldsize.x and p.x >= 0 \ and p.y < self.worldsize.y and p.y >= 0 def update(self, t, dt): if len(self.line) < self.maxlen: if self.slen >= 3 and random.random() < 0.4: dirs = list(DIRECTIONS) dirs.remove(self.line[-1] - self.line[-2]) if self.check_bounds(self.line[-1]): newp = None while newp is None or newp == self.line[-2]: self.dir = random.choice(dirs) newp = self.line[-1] + self.dir else: dirs.sort(key=lambda d: ((self.line[-1]+d) - Point(0, 0)).len) self.dir = dirs[0] self.slen = 1 else: self.slen += 1 self.line.append(self.line[-1] + self.dir) else: if self.fadeout_remaining is None: self.fadeout_remaining = self.fadeout_time elif self.fadeout_remaining <= 0: self.finished = True else: self.fadeout_remaining -= dt def draw(self, frame): if self.fadeout_remaining is not None: if self.fadeout_remaining > 0: h, s, v = colorsys.rgb_to_hsv(*self.color) v = v * (self.fadeout_remaining / self.fadeout_time) color = colorsys.hsv_to_rgb(h, s, v) else: color = (0, 0, 0) else: color = self.color for p in self.line: if self.check_bounds(p): frame.setPixel(p.x, p.y, convert_color(color)) if __name__ == '__main__': parser = argparse.ArgumentParser(description='Generate 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) lines = [Line(Point(WIDTH, HEIGHT))] t = 0 dt = args.delay / 1000 while t < args.time: t += dt frame = blup.animation.AnimationFrame(dim, args.delay) for l in lines: l.update(t, dt) l.draw(frame) if lines[-1].fadeout_remaining is not None: lines.append(Line(Point(WIDTH, HEIGHT))) for l in lines: if l.finished: lines.remove(l) anim.addFrame(frame) blup.writebml.writeBml(anim, args.output_file)