diff --git a/generators/twister.py b/generators/twister.py new file mode 100755 index 0000000..85fa069 --- /dev/null +++ b/generators/twister.py @@ -0,0 +1,124 @@ +#!/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 Twister: + def __init__(self, worldsize): + self.worldsize = worldsize + self.cycletime = 1.3 + self.sat = 0.5 + random.random() / 2 + self.hues = [random.random()] + self.hues += [ self.hues[0] + (i+1) * 0.25 for i in range(3) ] + random.shuffle(self.hues) + self.width = worldsize.x // 2.9 + + def update(self, t, dt): + # inspired by https://www.lexaloffle.com/bbs/?tid=3050 + + self.rows = [] + a0 = t * math.pi / self.cycletime + while len(self.rows) < self.worldsize.y: + y = len(self.rows) + a = a0 - y * math.pi / self.worldsize.y \ + * (math.sin(t * math.pi / 10) * 0.7) + ot1 = t * math.pi / 5 + ot2 = y * math.pi / (self.worldsize.y * 2) + offs = self.worldsize.x / 2 + math.sin(ot1 + ot2) * self.width / 2 + x1 = int(round(offs + self.width * math.sin(a))) + x2 = int(round(offs + self.width * math.sin(a+0.5*math.pi))) + x3 = int(round(offs + self.width * math.sin(a+math.pi))) + x4 = int(round(offs + self.width * math.sin(a+1.5*math.pi))) + row = [] + for i, (start, end) in enumerate([(x1, x2), (x2, x3), + (x3, x4), (x4, x1)]): + if start < end: + row += [(x, i, (x - start) / (end - start - 1)) + for x in range(start, end - 1)] + self.rows.append(row) + + def draw(self, frame): + for y, line in enumerate(self.rows): + for point in line: + x, i, j = point + val = 1 - math.sin(j * math.pi) / 2.3 + color = colorsys.hsv_to_rgb(self.hues[i], self.sat, val) + try: + frame.setPixel(x, 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) + + twister = Twister(Point(WIDTH, HEIGHT)) + t = 0 + dt = args.delay / 1000 + while t < args.time: + t += dt + frame = blup.animation.AnimationFrame(dim, args.delay) + + twister.update(t, dt) + twister.draw(frame) + + anim.addFrame(frame) + + blup.writebml.writeBml(anim, args.output_file)