#!/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 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) def __mul__(self, other): if isinstance(other, Point): raise Exception('not implemented') else: return Point(self.x * other, self.y * other) def __truediv__(self, other): if isinstance(other, Point): raise ValueError() else: return self * (1/other) @property def len(self): return math.sqrt(self.x**2 + self.y**2) @property def n(self): n = Point(self.y, -self.x) return n / n.len @property def intp(self): return Point(int(round(self.x)), int(round(self.y))) 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 BlinkingCheckerboard: def __init__(self, size, fieldsize, h, num_fields, rounds): self.size, self.fieldsize, self.h, self.num_fields, self.rounds = \ size, fieldsize, h, num_fields, rounds self.roundtime = 0.5 self.fieldpoints = [ Point(random.randint(0, WIDTH-1), random.randint(0, HEIGHT-1)) ] xm = self.fieldpoints[-1].x % (fieldsize * 2) ym = self.fieldpoints[-1].y % (fieldsize * 2) while len(self.fieldpoints) < self.num_fields: p = random.choice(self.fieldpoints) o = Point(random.randint(-2, 2), random.randint(-2, 2)) if abs(o.x) + abs(o.y) != 2: continue p += o * fieldsize if p.x < 0 or p.y < 0 or p.x >= size.x or p.y > size.y: continue if p not in self.fieldpoints: self.fieldpoints.append(p) self.done = False def update(self, t, dt): r = t / self.roundtime if r >= self.rounds + 1: self.done = True self.fieldvals = [0] * self.num_fields else: self.fieldvals = [] for i in range(self.num_fields): offs = i / (self.num_fields - 1) * 2 * math.pi pos = 2 * math.pi * r self.fieldvals.append(max(math.sin(offs + pos), 0)) if r > self.rounds and r < self.rounds + 1: if self.fieldvals[-1] < 0.01: self.fieldvals[-1] = 0 def draw(self, frame): for i, f in enumerate(self.fieldpoints): color = colorsys.hsv_to_rgb(self.h, 1, self.fieldvals[i]) for x in range(self.fieldsize): for y in range(self.fieldsize): try: p = f + Point(x, y) frame.setPixel(p.x, p.y, convert_color(color)) except ValueError: pass 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) t = 0 dt = args.delay / 1000 tt = 0 bc = None while t < args.time or not bc.done: t += dt tt += dt frame = blup.animation.AnimationFrame(dim, args.delay) if (bc is None or bc.done) and t < args.time: tt = 0 bc = BlinkingCheckerboard( Point(WIDTH, HEIGHT), 2, random.random(), random.randint(13, 23), random.randint(3, 5) ) bc.update(tt, dt) bc.draw(frame) anim.addFrame(frame) blup.writebml.writeBml(anim, args.output_file)