diff --git a/generators/checkerboard.py b/generators/checkerboard.py new file mode 100755 index 0000000..b1f055c --- /dev/null +++ b/generators/checkerboard.py @@ -0,0 +1,140 @@ +#!/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)