Compare commits
47 Commits
feature/ba
...
master
29 changed files with 2213 additions and 309 deletions
@ -0,0 +1,130 @@ |
|||||||
|
""" |
||||||
|
this file is part of blup. it provides an easy way |
||||||
|
to determine the direction the player is leaning |
||||||
|
""" |
||||||
|
from enum import Enum |
||||||
|
import struct |
||||||
|
|
||||||
|
Direction = Enum('BalanceDirection', |
||||||
|
['LEFT', 'RIGHT', 'UP', 'DOWN', |
||||||
|
'CENTER', 'QUIT']) |
||||||
|
|
||||||
|
|
||||||
|
def sanitize_threshold(threshold): |
||||||
|
""" |
||||||
|
function to make sure the threshold is positive |
||||||
|
threshold -- the given (untrusted) treshold |
||||||
|
""" |
||||||
|
if threshold < 0: |
||||||
|
threshold = abs(threshold) |
||||||
|
return threshold |
||||||
|
|
||||||
|
|
||||||
|
class BalanceSocket(object): |
||||||
|
def __init__(self, balance_server_socket): |
||||||
|
self.socket = balance_server_socket |
||||||
|
|
||||||
|
def close(self): |
||||||
|
self.socket.close() |
||||||
|
|
||||||
|
def _get_raw_data(self): |
||||||
|
self.socket.send(b'a') |
||||||
|
data = self.socket.recv(4) |
||||||
|
p0x, p0y, p1x, p1y = struct.unpack('bbbb', data) |
||||||
|
return p0x, p0y, p1x, p1y |
||||||
|
|
||||||
|
|
||||||
|
class BalanceUtil(BalanceSocket): |
||||||
|
def __init__(self, balance_server_socket, player_id): |
||||||
|
""" |
||||||
|
BalanceUtil object |
||||||
|
balance_socket -- (ip, port) of the balance server |
||||||
|
player_id -- id of the player |
||||||
|
""" |
||||||
|
super(BalanceUtil, self).__init__(balance_server_socket) |
||||||
|
self.player_id = player_id |
||||||
|
|
||||||
|
def _get_raw_player_data(self): |
||||||
|
self.socket.send(b'a') |
||||||
|
data = self.socket.recv(4) |
||||||
|
p0x, p0y, p1x, p1y = struct.unpack('bbbb', data) |
||||||
|
if self.player_id == 0: |
||||||
|
return p0x, p0y |
||||||
|
else: |
||||||
|
return p1x, p1y |
||||||
|
|
||||||
|
def get_player_ready(self): |
||||||
|
""" |
||||||
|
check if someone is on the board |
||||||
|
:return: true if someone is on the board |
||||||
|
""" |
||||||
|
p_x, p_y = self._get_raw_player_data() |
||||||
|
return p_x > -128 or p_y > -128 |
||||||
|
|
||||||
|
def get_raw_2dir_y(self): |
||||||
|
""" |
||||||
|
Get raw 2-directional data from the y axis of the board. |
||||||
|
e.g. determine how fast the paddle is supposed to move |
||||||
|
in balance pong |
||||||
|
:return: raw y axis balance value |
||||||
|
""" |
||||||
|
x_bal, y_bal = self._get_raw_player_data() |
||||||
|
return y_bal |
||||||
|
|
||||||
|
def get_2dir_y(self, threshold): |
||||||
|
threshold = sanitize_threshold(threshold) |
||||||
|
x_bal, y_bal = self._get_raw_player_data() |
||||||
|
if x_bal == -128 or y_bal == -128: |
||||||
|
return Direction.QUIT |
||||||
|
elif abs(y_bal) < threshold: |
||||||
|
return Direction.CENTER |
||||||
|
elif y_bal > 0: |
||||||
|
return Direction.UP |
||||||
|
else: |
||||||
|
return Direction.DOWN |
||||||
|
|
||||||
|
def get_raw_2dir_x(self): |
||||||
|
x_bal, y_bal = self._get_raw_player_data() |
||||||
|
return x_bal |
||||||
|
|
||||||
|
def get_2dir_x(self, threshold): |
||||||
|
threshold = sanitize_threshold(threshold) |
||||||
|
x_bal, y_bal = self._get_raw_player_data() |
||||||
|
if x_bal == -128 or y_bal == -128: |
||||||
|
return Direction.QUIT |
||||||
|
elif abs(x_bal) < threshold: |
||||||
|
return Direction.CENTER |
||||||
|
elif x_bal > 0: |
||||||
|
return Direction.RIGHT |
||||||
|
else: |
||||||
|
return Direction.LEFT |
||||||
|
|
||||||
|
def get_raw_4dir(self): |
||||||
|
return self._get_raw_player_data() |
||||||
|
|
||||||
|
def get_4dir(self, threshold): |
||||||
|
""" |
||||||
|
Get evaluated 4-directional data of the board |
||||||
|
e.g. determine the direction and rotation or drop move |
||||||
|
in balance tetris |
||||||
|
|
||||||
|
threshold -- deadzone of the board where nothing is |
||||||
|
supposed to happen |
||||||
|
:return: Enum Direction value of direction or event |
||||||
|
""" |
||||||
|
threshold = sanitize_threshold(threshold) |
||||||
|
x_bal, y_bal = self._get_raw_player_data() |
||||||
|
if x_bal == -128 or y_bal == -128: |
||||||
|
return Direction.QUIT |
||||||
|
elif abs(x_bal) < threshold and abs(y_bal) < threshold: |
||||||
|
return Direction.CENTER |
||||||
|
elif abs(x_bal) < abs(y_bal): |
||||||
|
if y_bal > 0: |
||||||
|
return Direction.UP |
||||||
|
else: |
||||||
|
return Direction.DOWN |
||||||
|
else: |
||||||
|
if x_bal > 0: |
||||||
|
return Direction.RIGHT |
||||||
|
else: |
||||||
|
return Direction.LEFT |
@ -0,0 +1,126 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
|
||||||
|
import sys |
||||||
|
import random |
||||||
|
import argparse |
||||||
|
import blup.frame |
||||||
|
import blup.output |
||||||
|
import blup.animation |
||||||
|
import blup.writebml |
||||||
|
import colorsys |
||||||
|
|
||||||
|
|
||||||
|
WIDTH = 22 |
||||||
|
HEIGHT = 16 |
||||||
|
DEPTH = 256 |
||||||
|
|
||||||
|
|
||||||
|
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(), 1, 1) |
||||||
|
|
||||||
|
|
||||||
|
def get_random_point(): |
||||||
|
return (random.randint(0, WIDTH-1), random.randint(0, HEIGHT-1)) |
||||||
|
|
||||||
|
|
||||||
|
def cap(x): |
||||||
|
return min(1, max(0, x)) |
||||||
|
|
||||||
|
|
||||||
|
def cyc(x): |
||||||
|
while x < 0: |
||||||
|
x += 1 |
||||||
|
while x > 1: |
||||||
|
x -= 1 |
||||||
|
return x |
||||||
|
|
||||||
|
|
||||||
|
class Point: |
||||||
|
def __init__(self, pos, color, evolve): |
||||||
|
self.pos = pos |
||||||
|
self.color = color |
||||||
|
self.neighbours = [] |
||||||
|
h, s, v = colorsys.rgb_to_hsv(*color) |
||||||
|
self.evolve = evolve |
||||||
|
if 'h' in evolve: |
||||||
|
h += (random.random()-.5) * 0.1 |
||||||
|
if 's' in evolve: |
||||||
|
s += (random.random()-.5) * 0.5 |
||||||
|
if 'v' in evolve: |
||||||
|
v += (random.random()-.5) * 0.5 |
||||||
|
self.ncolor = colorsys.hsv_to_rgb(cyc(h), cap(s), cap(v)) |
||||||
|
|
||||||
|
def __eq__(self, other): |
||||||
|
return self.pos == other.pos |
||||||
|
|
||||||
|
def __hash__(self): |
||||||
|
return self.pos.__hash__() |
||||||
|
|
||||||
|
def get_neighbours(self): |
||||||
|
for x in [-1, 0, 1]: |
||||||
|
for y in [-1, 0, 1]: |
||||||
|
n = (self.pos[0]+x, self.pos[1]+y) |
||||||
|
if n[0] >= 0 and n[1] >= 0 and n[0] < WIDTH and \ |
||||||
|
n[1] < HEIGHT and abs(x+y) < 2: |
||||||
|
yield Point(n, self.ncolor, self.evolve) |
||||||
|
|
||||||
|
|
||||||
|
class Blob: |
||||||
|
def __init__(self, pos, color): |
||||||
|
e = random.choice('hsv') |
||||||
|
if random.random() > 0.8: |
||||||
|
e += random.choice('hsv') |
||||||
|
p = Point(pos, color, e) |
||||||
|
self.points = set([p]) |
||||||
|
self.candids = set(p.get_neighbours()) |
||||||
|
self.completed = False |
||||||
|
|
||||||
|
def grow(self): |
||||||
|
if len(self.candids) == 0: |
||||||
|
self.completed = True |
||||||
|
return |
||||||
|
newp = random.sample(self.candids, 1)[0] |
||||||
|
self.candids.remove(newp) |
||||||
|
self.points.add(newp) |
||||||
|
self.candids = self.candids.union(set(newp.get_neighbours()) - |
||||||
|
self.points - self.candids) |
||||||
|
|
||||||
|
def draw(self, frame): |
||||||
|
for p in self.points: |
||||||
|
frame.setPixel(p.pos[0], p.pos[1], convert_color(p.color)) |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
parser = argparse.ArgumentParser(description='Generate blob 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 |
||||||
|
blobs = [Blob(get_random_point(), get_random_color())] |
||||||
|
while t < args.time: |
||||||
|
t += args.delay / 1000 |
||||||
|
frame = blup.animation.AnimationFrame(dim, args.delay) |
||||||
|
|
||||||
|
for b in blobs: |
||||||
|
for i in range(4): |
||||||
|
b.grow() |
||||||
|
b.draw(frame) |
||||||
|
|
||||||
|
if len(blobs[-1].points) > WIDTH*HEIGHT*0.6 and len(blobs) < 3: |
||||||
|
blobs.append(Blob(get_random_point(), get_random_color())) |
||||||
|
if len(blobs) >=2 and blobs[0].completed and blobs[1].completed: |
||||||
|
blobs.remove(blobs[0]) |
||||||
|
|
||||||
|
anim.addFrame(frame) |
||||||
|
|
||||||
|
blup.writebml.writeBml(anim, args.output_file) |
@ -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) |
@ -0,0 +1,107 @@ |
|||||||
|
#!/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) |
||||||
|
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 CurveFunction(): |
||||||
|
def __init__(self, p1, p2): |
||||||
|
self.p1, self.p2 = p1, p2 |
||||||
|
d = p2 - p1 |
||||||
|
self.pp = p1 + d/2 + d.n * (d.len * 0.8) |
||||||
|
|
||||||
|
def __call__(self, t): |
||||||
|
ret = (self.p1 - self.pp*2 + self.p2) * t**2 |
||||||
|
ret += (self.p1 * -2 + self.pp * 2) * t |
||||||
|
ret += self.p1 |
||||||
|
return ret |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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 |
||||||
|
while t < args.time: |
||||||
|
t += dt |
||||||
|
|
||||||
|
p1 = p2 = None |
||||||
|
while p1 == p2 or (p1 - p2).len < max(WIDTH, HEIGHT) / 2: |
||||||
|
p1 = Point(random.randint(0, WIDTH-1), random.randint(0, HEIGHT-1)) |
||||||
|
p2 = Point(random.randint(0, WIDTH-1), random.randint(0, HEIGHT-1)) |
||||||
|
cf = CurveFunction(p1, p2) |
||||||
|
color = convert_color(get_random_color()) |
||||||
|
tt = 0 |
||||||
|
points = [] |
||||||
|
while tt <= 1: |
||||||
|
t += dt |
||||||
|
tt += 0.025 |
||||||
|
p = cf(tt).intp |
||||||
|
points.append(p) |
||||||
|
frame = blup.animation.AnimationFrame(dim, args.delay) |
||||||
|
for p in points: |
||||||
|
try: |
||||||
|
frame.setPixel(p.x, p.y, color) |
||||||
|
except ValueError: |
||||||
|
pass |
||||||
|
anim.addFrame(frame) |
||||||
|
|
||||||
|
blup.writebml.writeBml(anim, args.output_file) |
@ -0,0 +1,114 @@ |
|||||||
|
#!/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(), 1, 1) |
||||||
|
|
||||||
|
|
||||||
|
class Disc: |
||||||
|
def __init__(self, worldsize, hue): |
||||||
|
self.worldsize = worldsize |
||||||
|
self.pos = Point(random.randint(0, worldsize.x - 1), |
||||||
|
random.randint(0, worldsize.y - 1)) |
||||||
|
self.hue = hue |
||||||
|
self.r = 1 |
||||||
|
self.speed = min(worldsize.x, worldsize.y) / 2 |
||||||
|
self.done = False |
||||||
|
|
||||||
|
def update(self, t, dt): |
||||||
|
self.r += self.speed * dt |
||||||
|
if self.r > max(self.worldsize.x, self.worldsize.y): |
||||||
|
self.done = True |
||||||
|
|
||||||
|
def draw(self, frame): |
||||||
|
color = colorsys.hsv_to_rgb(self.hue, 1, 1) |
||||||
|
for x in range(self.worldsize.x): |
||||||
|
for y in range(self.worldsize.y): |
||||||
|
p = Point(x, y) |
||||||
|
if (p - self.pos).len < self.r: |
||||||
|
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) |
||||||
|
|
||||||
|
discs = [] |
||||||
|
t = 0 |
||||||
|
dt = args.delay / 1000 |
||||||
|
while t < args.time: |
||||||
|
t += dt |
||||||
|
frame = blup.animation.AnimationFrame(dim, args.delay) |
||||||
|
|
||||||
|
if len(discs) == 0 or discs[-1].r > min(WIDTH, HEIGHT) / 2: |
||||||
|
hue = random.random() |
||||||
|
if len(discs) > 0: |
||||||
|
l = lambda d: hue - d.hue if d.hue < hue else d.hue - hue |
||||||
|
while not 0.2 < sorted(map(l, discs))[0] < 0.8: |
||||||
|
hue = random.random() |
||||||
|
discs.append(Disc(Point(WIDTH, HEIGHT), hue)) |
||||||
|
|
||||||
|
for d in discs: |
||||||
|
d.update(t, dt) |
||||||
|
d.draw(frame) |
||||||
|
|
||||||
|
if len(discs) > 2 and discs[0].done and discs[1].done: |
||||||
|
del discs[0] |
||||||
|
|
||||||
|
anim.addFrame(frame) |
||||||
|
|
||||||
|
blup.writebml.writeBml(anim, args.output_file) |
@ -0,0 +1,141 @@ |
|||||||
|
#!/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) |
@ -0,0 +1,150 @@ |
|||||||
|
#!/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) |
@ -0,0 +1,121 @@ |
|||||||
|
#!/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 Line: |
||||||
|
def __init__(self, size): |
||||||
|
self.size = size |
||||||
|
r = random.choice([0, 1]) |
||||||
|
s = random.choice([-1, 1]) |
||||||
|
if r == 0: |
||||||
|
self.pos = Point(size.x if s == -1 else 0, |
||||||
|
random.randint(0, size.y - 1)) |
||||||
|
else: |
||||||
|
self.pos = Point(random.randint(0, size.x - 1), |
||||||
|
size.y if s == -1 else 0) |
||||||
|
self.dir = Point(s if r == 0 else 0, s if r == 1 else 0) |
||||||
|
m = max(size.x, size.y) |
||||||
|
self.length = random.randint(int(0.4 * m), int(1.1 * m)) |
||||||
|
self.color = get_random_color() |
||||||
|
self.speed = 10 |
||||||
|
self.done = False |
||||||
|
|
||||||
|
def update(self, t, dt): |
||||||
|
self.pos += self.dir * (dt * self.speed) |
||||||
|
end = self.pos - self.dir * self.length |
||||||
|
if self.dir.x > 0 and end.x > self.size.x \ |
||||||
|
or self.dir.y > 0 and end.y > self.size.y \ |
||||||
|
or self.dir.x < 0 and end.x < 0 \ |
||||||
|
or self.dir.y < 0 and end.y < 0: |
||||||
|
self.done = True |
||||||
|
|
||||||
|
def draw(self, frame): |
||||||
|
if self.done: |
||||||
|
return |
||||||
|
for i in range(self.length): |
||||||
|
p = self.pos.intp - self.dir * i |
||||||
|
try: |
||||||
|
frame.setPixel(p.x, p.y, convert_color(self.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) |
||||||
|
|
||||||
|
lines = [] |
||||||
|
t = 0 |
||||||
|
dt = args.delay / 1000 |
||||||
|
while t < args.time or len(lines) > 0: |
||||||
|
t += dt |
||||||
|
frame = blup.animation.AnimationFrame(dim, args.delay) |
||||||
|
|
||||||
|
if len(lines) < 5 and t < args.time: |
||||||
|
lines.append(Line(Point(WIDTH, HEIGHT))) |
||||||
|
|
||||||
|
for l in lines: |
||||||
|
l.update(t, dt) |
||||||
|
l.draw(frame) |
||||||
|
lines = list(filter(lambda l: not l.done, lines)) |
||||||
|
|
||||||
|
anim.addFrame(frame) |
||||||
|
|
||||||
|
blup.writebml.writeBml(anim, args.output_file) |
@ -0,0 +1,222 @@ |
|||||||
|
#!/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 |
||||||
|
import math |
||||||
|
|
||||||
|
|
||||||
|
WIDTH = 22 |
||||||
|
HEIGHT = 16 |
||||||
|
DEPTH = 256 |
||||||
|
|
||||||
|
_ALLSHAPES = [ |
||||||
|
[[0, 1, 0, 1, 0], |
||||||
|
[1, 1, 1, 1, 1], |
||||||
|
[1, 1, 1, 1, 1], |
||||||
|
[0, 1, 1, 1, 0], |
||||||
|
[0, 0, 1, 0, 0]], |
||||||
|
|
||||||
|
[[1, 0, 0, 0, 1], |
||||||
|
[0, 0, 1, 0, 0], |
||||||
|
[0, 0, 1, 0, 0], |
||||||
|
[1, 0, 0, 0, 1], |
||||||
|
[0, 1, 1, 1, 0]], |
||||||
|
|
||||||
|
[[1, 0, 1, 0, 1], |
||||||
|
[0, 1, 1, 1, 0], |
||||||
|
[0, 1, 1, 1, 0], |
||||||
|
[0, 1, 0, 1, 0], |
||||||
|
[1, 1, 0, 1, 1]], |
||||||
|
] |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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) |
||||||
|
|
||||||
|
ALLSHAPES = [] |
||||||
|
for s in _ALLSHAPES: |
||||||
|
ALLSHAPES.append(set()) |
||||||
|
y = 0 |
||||||
|
for l in s: |
||||||
|
x = 0 |
||||||
|
for i in l: |
||||||
|
if i == 1: |
||||||
|
ALLSHAPES[-1].add(Point(x, y)) |
||||||
|
x += 1 |
||||||
|
y += 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(), 1, 1) |
||||||
|
|
||||||
|
|
||||||
|
class Shape: |
||||||
|
def __init__(self, shape, pos, color): |
||||||
|
self.shape, self.pos, self.color = shape, pos, color |
||||||
|
self.period = 2 |
||||||
|
self.fadetime = 0.5 + random.random() / 5 |
||||||
|
self.t = random.random() * self.period |
||||||
|
self.t = 0 |
||||||
|
self.oldv = 1 |
||||||
|
self.v = 1 |
||||||
|
self.newshape = None |
||||||
|
|
||||||
|
def update(self, t, dt): |
||||||
|
self.t += dt |
||||||
|
if self.t > self.period: |
||||||
|
t = self.t - self.period |
||||||
|
if t >= self.fadetime: |
||||||
|
self.v = 1 |
||||||
|
self.t = random.random() * self.period |
||||||
|
else: |
||||||
|
self.v = math.cos(t * (math.pi / self.fadetime)) ** 2 |
||||||
|
if (self.newshape is not None \ |
||||||
|
and (self.oldv < self.v or self.v == 0)): |
||||||
|
self.shape = self.newshape |
||||||
|
self.color = self.newcolor |
||||||
|
self.newshape = None |
||||||
|
self.oldv = self.v |
||||||
|
|
||||||
|
def change_shape_and_color(self, newshape, newcolor): |
||||||
|
self.newshape = newshape |
||||||
|
self.newcolor = newcolor |
||||||
|
self.t = self.period |
||||||
|
|
||||||
|
def draw(self, frame, origin=Point(0, 0)): |
||||||
|
h, s, v = colorsys.rgb_to_hsv(*self.color) |
||||||
|
for p in self.shape: |
||||||
|
try: |
||||||
|
pos = self.pos + p - origin |
||||||
|
c = colorsys.hsv_to_rgb(h, s, v * self.v) |
||||||
|
frame.setPixel(pos.x, pos.y, convert_color(c)) |
||||||
|
except ValueError: |
||||||
|
pass |
||||||
|
|
||||||
|
|
||||||
|
class World: |
||||||
|
def __init__(self, size, allshapes, gap=2): |
||||||
|
self.size, self.allshapes, self.gap = size, allshapes, gap |
||||||
|
self.shapes = {} |
||||||
|
self.pos = Point(0, 0) |
||||||
|
self.cycletime = 60 |
||||||
|
self.curshape = random.choice(allshapes) |
||||||
|
self.curcolor = get_random_color() |
||||||
|
|
||||||
|
self.shapesize = Point(0, 0) |
||||||
|
for s in allshapes: |
||||||
|
assert min(map(lambda p: p.x, s)) >= 0 |
||||||
|
assert min(map(lambda p: p.y, s)) >= 0 |
||||||
|
shapesize = Point( |
||||||
|
max(map(lambda p: p.x, s)) + 1, |
||||||
|
max(map(lambda p: p.y, s)) + 1, |
||||||
|
) |
||||||
|
if shapesize.x > self.shapesize.x: |
||||||
|
self.shapesize = Point(shapesize.x, self.shapesize.y) |
||||||
|
if shapesize.y > self.shapesize.y: |
||||||
|
self.shapesize = Point(self.shapesize.x, shapesize.y) |
||||||
|
print (self.shapesize) |
||||||
|
|
||||||
|
@property |
||||||
|
def shape_space(self): |
||||||
|
ret = set() |
||||||
|
for x in range(self.size.x): |
||||||
|
for y in range(self.size.y): |
||||||
|
p = self.pos + Point(x, y) |
||||||
|
if (p.x % (self.shapesize.x + self.gap) >= self.gap |
||||||
|
and p.y % (self.shapesize.y + self.gap) >= self.gap): |
||||||
|
ret.add(p) |
||||||
|
return ret |
||||||
|
|
||||||
|
def get_shape_coverage(self, shape): |
||||||
|
return { shape.pos + Point(x, y) for x in range(self.shapesize.x) \ |
||||||
|
for y in range(self.shapesize.y) } |
||||||
|
|
||||||
|
def update(self, t, dt): |
||||||
|
self.pos = Point( |
||||||
|
int(math.sin((t * math.pi/self.cycletime)*4) * self.size.x), |
||||||
|
int(math.cos((t * math.pi/self.cycletime)*5) * self.size.y) |
||||||
|
) |
||||||
|
|
||||||
|
space = self.shape_space |
||||||
|
for s in self.shapes.values(): |
||||||
|
space -= self.get_shape_coverage(s) |
||||||
|
while len(space) > 0: # and len(self.shapes) < 1: |
||||||
|
print('...') |
||||||
|
p = space.pop() |
||||||
|
newpos = Point( |
||||||
|
(p.x // (self.shapesize.x + self.gap)) \ |
||||||
|
* (self.shapesize.x + self.gap) + self.gap, |
||||||
|
(p.y // (self.shapesize.y + self.gap)) \ |
||||||
|
* (self.shapesize.y + self.gap) + self.gap |
||||||
|
) |
||||||
|
print(p, newpos) |
||||||
|
# TODO improve shape and color selection |
||||||
|
newshape = Shape(self.curshape, newpos, self.curcolor) |
||||||
|
assert newpos in self.get_shape_coverage(newshape) |
||||||
|
print('covered', self.get_shape_coverage(newshape)) |
||||||
|
print('space before', len(space)) |
||||||
|
space -= self.get_shape_coverage(newshape) |
||||||
|
print('space after ', len(space)) |
||||||
|
assert newpos not in self.shapes |
||||||
|
self.shapes[newpos] = newshape |
||||||
|
print('new', len(self.shapes)) |
||||||
|
#print(space) |
||||||
|
|
||||||
|
newshape = None |
||||||
|
newcolor = None |
||||||
|
if round(t*1000) % 3000 == 0: |
||||||
|
newshape = random.choice(self.allshapes) |
||||||
|
self.curshape = newshape |
||||||
|
newcolor = get_random_color() |
||||||
|
self.curcolor = newcolor |
||||||
|
for s in self.shapes.values(): |
||||||
|
if newshape is not None: |
||||||
|
s.change_shape_and_color(newshape, newcolor) |
||||||
|
s.update(t, dt) |
||||||
|
|
||||||
|
def draw(self, frame): |
||||||
|
for s in self.shapes.values(): |
||||||
|
s.draw(frame, self.pos) |
||||||
|
|
||||||
|
|
||||||
|
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) |
||||||
|
|
||||||
|
w = World(Point(WIDTH, HEIGHT), ALLSHAPES) |
||||||
|
t = 0 |
||||||
|
dt = args.delay / 1000 |
||||||
|
while t < args.time: |
||||||
|
t += dt |
||||||
|
frame = blup.animation.AnimationFrame(dim, args.delay) |
||||||
|
|
||||||
|
w.update(t, dt) |
||||||
|
w.draw(frame) |
||||||
|
|
||||||
|
anim.addFrame(frame) |
||||||
|
|
||||||
|
blup.writebml.writeBml(anim, args.output_file) |
@ -0,0 +1,122 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
|
||||||
|
import sys |
||||||
|
import random |
||||||
|
import numpy as np |
||||||
|
import argparse |
||||||
|
import blup.frame |
||||||
|
import blup.output |
||||||
|
import blup.animation |
||||||
|
import blup.writebml |
||||||
|
import colorsys |
||||||
|
|
||||||
|
|
||||||
|
WIDTH = 22 |
||||||
|
HEIGHT = 16 |
||||||
|
DEPTH = 256 |
||||||
|
|
||||||
|
|
||||||
|
def fill_trace(points): |
||||||
|
for i in range(len(points)): |
||||||
|
yield points[i] |
||||||
|
if i == len(points)-1: |
||||||
|
break |
||||||
|
v = points[i+1] - points[i] |
||||||
|
l = int(np.linalg.norm(v)) |
||||||
|
for j in range(l): |
||||||
|
yield points[i] + v * (j+1)/(l+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(), 1, 1) |
||||||
|
|
||||||
|
|
||||||
|
class Orb: |
||||||
|
def __init__(self, pos, speed, tracelen, color, tracecolor, boxsize): |
||||||
|
self.pos = pos |
||||||
|
self.speed = speed |
||||||
|
self.tracelen = tracelen |
||||||
|
self.color = color |
||||||
|
self.tracecolor = tracecolor |
||||||
|
self.boxsize = boxsize |
||||||
|
self.trace = [] |
||||||
|
|
||||||
|
def update(self, dt): |
||||||
|
newpos = self.pos + self.speed * dt |
||||||
|
#newpos = self.pos |
||||||
|
for dim in [0, 1]: |
||||||
|
if newpos[dim] >= self.boxsize[dim] or newpos[dim] <= 0: |
||||||
|
newpos[dim] = 0 if newpos[dim]<0 else self.boxsize[dim]-1 |
||||||
|
self.speed[dim] *= -1 |
||||||
|
self.speed += np.array([random.randint(-5, 6), random.randint(-5, 6)]) |
||||||
|
self.trace.insert(0, self.pos) |
||||||
|
self.trace = self.trace[:self.tracelen] |
||||||
|
self.pos = newpos |
||||||
|
|
||||||
|
def draw(self, frame): |
||||||
|
for i, p in enumerate(fill_trace([self.pos] + self.trace)): |
||||||
|
f = (self.tracelen - i) / self.tracelen |
||||||
|
try: |
||||||
|
c = colorsys.rgb_to_hsv(*self.tracecolor) |
||||||
|
s = 0.1 if random.random()>0.9 else c[1] |
||||||
|
c = colorsys.hsv_to_rgb(c[0], s, c[2]*f) |
||||||
|
frame.setPixel(int(p[0]), int(p[1]), convert_color(c)) |
||||||
|
except ValueError: |
||||||
|
pass |
||||||
|
|
||||||
|
offs = [ np.array([x,y]) for x in [-1,0,1] for y in [-1,0,1] ] |
||||||
|
points = [] |
||||||
|
for o in offs: |
||||||
|
p = self.pos.astype('int') + o |
||||||
|
if random.random() > 0.2 and not (p == np.array([0,0])).all(): |
||||||
|
points.append(p) |
||||||
|
for p in points: |
||||||
|
try: |
||||||
|
c = colorsys.rgb_to_hsv(*self.color) |
||||||
|
c = colorsys.hsv_to_rgb(c[0], c[1], c[2]*0.2) |
||||||
|
frame.setPixel(p[0], p[1], convert_color(c)) |
||||||
|
except ValueError: |
||||||
|
pass |
||||||
|
|
||||||
|
frame.setPixel(int(self.pos[0]), int(self.pos[1]), convert_color(self.color)) |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
parser = argparse.ArgumentParser(description='Generate plasma animations') |
||||||
|
parser.add_argument('-d', '--delay', type=int, default=50) |
||||||
|
parser.add_argument('-t', '--time', type=int, default=15) |
||||||
|
parser.add_argument('-n', '--num-orbs', type=int, default=2) |
||||||
|
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) |
||||||
|
|
||||||
|
orbs = [] |
||||||
|
for i in range(args.num_orbs): |
||||||
|
orbs.append(Orb( |
||||||
|
np.array([random.randint(0, WIDTH-1),random.randint(0, HEIGHT-1)]), |
||||||
|
np.array([8,10]), |
||||||
|
20, |
||||||
|
get_random_color(), |
||||||
|
get_random_color(), |
||||||
|
np.array([WIDTH, HEIGHT]) |
||||||
|
)) |
||||||
|
|
||||||
|
t = 0 |
||||||
|
while t < args.time: |
||||||
|
t += args.delay / 1000 |
||||||
|
frame = blup.animation.AnimationFrame(dim, args.delay) |
||||||
|
|
||||||
|
for orb in orbs: |
||||||
|
orb.update(args.delay / 1000) |
||||||
|
orb.draw(frame) |
||||||
|
|
||||||
|
anim.addFrame(frame) |
||||||
|
|
||||||
|
blup.writebml.writeBml(anim, args.output_file) |
@ -0,0 +1,232 @@ |
|||||||
|
#!/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) |
||||||
|
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 CurveFunction(): |
||||||
|
def __init__(self, p1, p2): |
||||||
|
self.p1, self.p2 = p1, p2 |
||||||
|
d = p2 - p1 |
||||||
|
self.pp = p1 + d/2 + d.n * (d.len * 0.8) |
||||||
|
|
||||||
|
def __call__(self, t): |
||||||
|
ret = (self.p1 - self.pp*2 + self.p2) * t**2 |
||||||
|
ret += (self.p1 * -2 + self.pp * 2) * t |
||||||
|
ret += self.p1 |
||||||
|
return ret |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Box(): |
||||||
|
def __init__(self, pos, size, hue, swaptime, content=None, |
||||||
|
contentcolor=None): |
||||||
|
self.pos, self.size, self.hue, self.content, self.contentcolor = \ |
||||||
|
pos, size, hue, content, contentcolor |
||||||
|
self.swaptime = swaptime |
||||||
|
self.covered = False |
||||||
|
self.swapstart = None |
||||||
|
self.newpos = None |
||||||
|
self.cf = None |
||||||
|
self.colorcycletime = 1 |
||||||
|
|
||||||
|
def swap(self, other): |
||||||
|
self.newpos = other.pos |
||||||
|
other.newpos = self.pos |
||||||
|
|
||||||
|
def update(self, t, dt): |
||||||
|
self.hue += 0.05 # dt / self.colorcycletime |
||||||
|
if self.hue > 1: |
||||||
|
self.hue -= 1 |
||||||
|
if self.newpos is not None: |
||||||
|
if self.cf is None: |
||||||
|
self.cf = CurveFunction(self.pos, self.newpos) |
||||||
|
self.swapstart = t |
||||||
|
elif t - self.swapstart >= self.swaptime: |
||||||
|
self.pos = self.newpos |
||||||
|
self.newpos = None |
||||||
|
self.cf = None |
||||||
|
self.swapstart = None |
||||||
|
else: |
||||||
|
self.pos = (self.cf((t - self.swapstart) / self.swaptime)).intp |
||||||
|
|
||||||
|
def draw(self, frame): |
||||||
|
if self.covered: |
||||||
|
for x in range(self.size): |
||||||
|
for y in range(self.size): |
||||||
|
if x == 0 or x == self.size - 1 \ |
||||||
|
or y == 0 or y == self.size -1: |
||||||
|
color = (0.6, 0.6, 0.6) |
||||||
|
else: |
||||||
|
color = colorsys.hsv_to_rgb(self.hue, 1, 1) |
||||||
|
try: |
||||||
|
frame.setPixel(self.pos.x + x, self.pos.y + y, |
||||||
|
convert_color(color)) |
||||||
|
except ValueError: |
||||||
|
pass |
||||||
|
else: |
||||||
|
if self.content is not None: |
||||||
|
for p in self.content: |
||||||
|
try: |
||||||
|
frame.setPixel(self.pos.x + p.x + 1, |
||||||
|
self.pos.y + p.y + 1, |
||||||
|
convert_color(self.contentcolor)) |
||||||
|
except ValueError: |
||||||
|
pass |
||||||
|
|
||||||
|
|
||||||
|
class Game(): |
||||||
|
def __init__(self, size, boxsize, gap, content, contentcolor, swaptime): |
||||||
|
self.swaptime = swaptime |
||||||
|
self.boxes = [] |
||||||
|
boxbase = Point((size.x - (2*boxsize + gap)) / 2, |
||||||
|
(size.y - (2*boxsize + gap)) / 2) |
||||||
|
self.n_boxes = 4 |
||||||
|
for i in range(self.n_boxes): |
||||||
|
boxpos = boxbase + Point( |
||||||
|
(i % (self.n_boxes/2)) * (boxsize + gap), |
||||||
|
(i // (self.n_boxes/2)) * (boxsize + gap) |
||||||
|
) |
||||||
|
self.boxes.append(Box( |
||||||
|
boxpos.intp, |
||||||
|
boxsize, |
||||||
|
i * (1/self.n_boxes), |
||||||
|
swaptime |
||||||
|
)) |
||||||
|
winner = random.randint(0, self.n_boxes - 1) |
||||||
|
self.boxes[winner].content = content |
||||||
|
self.boxes[winner].contentcolor = contentcolor |
||||||
|
self.statetimes = [2, 1, 5, 2, 1, 2] |
||||||
|
self.statestarts = [0] |
||||||
|
for t in self.statetimes[:-1]: |
||||||
|
self.statestarts.append(self.statestarts[-1] + t) |
||||||
|
self.done = False |
||||||
|
|
||||||
|
def update(self, t, dt): |
||||||
|
state = 0 |
||||||
|
for i, st in enumerate(self.statestarts): |
||||||
|
if st < t: |
||||||
|
state = i |
||||||
|
else: |
||||||
|
break |
||||||
|
state_t = t - self.statestarts[state] |
||||||
|
|
||||||
|
if state == 1 or state == 4: |
||||||
|
for i, b in enumerate(self.boxes): |
||||||
|
if state_t >= i * (self.statetimes[state] / self.n_boxes): |
||||||
|
b.covered = (state == 1) |
||||||
|
elif state == 2: |
||||||
|
if True not in map(lambda b: b.newpos is not None, self.boxes) \ |
||||||
|
and random.random() > 0.2: |
||||||
|
b1 = random.randint(0, self.n_boxes - 1) |
||||||
|
b2 = random.randint(0, self.n_boxes - 1) |
||||||
|
while b1 == b2: |
||||||
|
b2 = random.randint(0, self.n_boxes - 1) |
||||||
|
self.boxes[b1].swap(self.boxes[b2]) |
||||||
|
|
||||||
|
if state > 0 and state < 5: |
||||||
|
for b in self.boxes: |
||||||
|
b.update(t, dt) |
||||||
|
|
||||||
|
if state == len(self.statetimes) - 1 and state_t > self.statetimes[-1]: |
||||||
|
self.done = True |
||||||
|
|
||||||
|
def draw(self, frame): |
||||||
|
for b in self.boxes: |
||||||
|
if b.newpos is None: |
||||||
|
b.draw(frame) |
||||||
|
for b in self.boxes: |
||||||
|
if b.newpos is not None: |
||||||
|
b.draw(frame) |
||||||
|
|
||||||
|
|
||||||
|
BOXCONTENT = { |
||||||
|
Point(0, 0), |
||||||
|
Point(3, 0), |
||||||
|
Point(0, 2), |
||||||
|
Point(3, 2), |
||||||
|
Point(1, 3), |
||||||
|
Point(2, 3), |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
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 |
||||||
|
gamestart = 0 |
||||||
|
game = None |
||||||
|
while t < args.time or not game.done: |
||||||
|
t += dt |
||||||
|
frame = blup.animation.AnimationFrame(dim, args.delay) |
||||||
|
|
||||||
|
if (game is None or game.done) and t < args.time: |
||||||
|
game = Game(Point(WIDTH, HEIGHT), 6, 2, BOXCONTENT, |
||||||
|
get_random_color(), 0.5) |
||||||
|
gamestart = t |
||||||
|
game.update(t - gamestart, dt) |
||||||
|
game.draw(frame) |
||||||
|
|
||||||
|
anim.addFrame(frame) |
||||||
|
|
||||||
|
|
||||||
|
blup.writebml.writeBml(anim, args.output_file) |
@ -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) |
@ -0,0 +1,133 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
|
||||||
|
import sys |
||||||
|
import random |
||||||
|
import argparse |
||||||
|
import blup.frame |
||||||
|
import blup.output |
||||||
|
import blup.animation |
||||||
|
import blup.writebml |
||||||
|
import colorsys |
||||||
|
|
||||||
|
|
||||||
|
WIDTH = 22 |
||||||
|
HEIGHT = 16 |
||||||
|
DEPTH = 256 |
||||||
|
|
||||||
|
|
||||||
|
def convert_color(c): |
||||||
|
return list(map(lambda x: int(round(x*(DEPTH-1))), c)) |
||||||
|
|
||||||
|
|
||||||
|
def get_random_color(): |
||||||
|
if random.random() < 0.5: |
||||||
|
s = 1 |
||||||
|
v = random.random() |
||||||
|
else: |
||||||
|
s = random.random() |
||||||
|
v = 1 |
||||||
|
return colorsys.hsv_to_rgb(random.random(), s, v) |
||||||
|
|
||||||
|
|
||||||
|
class Unrath: |
||||||
|
def __init__(self, world): |
||||||
|
self.world = world |
||||||
|
self.shape = set([(0, 0)]) |
||||||
|
shapesize = random.randint(2, 5) |
||||||
|
while len(self.shape) < shapesize: |
||||||
|
p = random.sample(self.shape, 1)[0] |
||||||
|
self.shape.add((p[0]+random.randint(-1, 1), |
||||||
|
p[1]+random.randint(-1, 1))) |
||||||
|
minx = min(map(lambda p: p[0], self.shape)) |
||||||
|
miny = min(map(lambda p: p[1], self.shape)) |
||||||
|
self.shape = {(p[0]+abs(minx), p[1]+abs(miny)) for p in self.shape} |
||||||
|
self.shapewidth = max(map(lambda p: p[0], self.shape)) |
||||||
|
shapeheight = max(map(lambda p: p[1], self.shape)) |
||||||
|
|
||||||
|
y = random.randint(0, HEIGHT-shapeheight) |
||||||
|
if random.random() > 0.5: |
||||||
|
d = 1 |
||||||
|
x = -self.shapewidth |
||||||
|
else: |
||||||
|
d = -1 |
||||||
|
x = WIDTH + self.shapewidth |
||||||
|
self.pos = (x, y) |
||||||
|
self.finished = False |
||||||
|
self.speed = d * random.randint(2, WIDTH/2) |
||||||
|
self.color = get_random_color() |
||||||
|
|
||||||
|
def update(self, dt, nocheck=False): |
||||||
|
if (self.pos[0] + self.shapewidth < 0 and self.speed < 0) or \ |
||||||
|
(self.pos[0] > WIDTH and self.speed > 0): |
||||||
|
self.finished = True |
||||||
|
return |
||||||
|
newpos = (self.pos[0] + dt*self.speed, self.pos[1]) |
||||||
|
newpoints = self.get_points(newpos) |
||||||
|
collision = False |
||||||
|
for unrath in self.world.unraethe - {self}: |
||||||
|
if len(unrath.get_points().intersection(newpoints)) > 0: |
||||||
|
collision = True |
||||||
|
if abs(unrath.speed) > abs(self.speed): |
||||||
|
fast = unrath |
||||||
|
slow = self |
||||||
|
else: |
||||||
|
slow = unrath |
||||||
|
fast = self |
||||||
|
slow.speed = fast.speed * (1.1 + random.random()/10) |
||||||
|
slow.update(dt, True) |
||||||
|
if not collision: |
||||||
|
self.pos = newpos |
||||||
|
|
||||||
|
def get_points(self, pos=None): |
||||||
|
if pos is None: |
||||||
|
pos = self.pos |
||||||
|
return {(int(pos[0] + s[0]), int(pos[1] + s[1])) for s in self.shape} |
||||||
|
|
||||||
|
def draw(self, frame): |
||||||
|
for p in self.get_points(): |
||||||
|
try: |
||||||
|
frame.setPixel(p[0], p[1], convert_color(self.color)) |
||||||
|
except ValueError: |
||||||
|
pass |
||||||
|
|
||||||
|
|
||||||
|
class World: |
||||||
|
def __init__(self, num_unraethe): |
||||||
|
self.num_unraethe = num_unraethe |
||||||
|
self.unraethe = {} |
||||||
|
|
||||||
|
def update(self, dt): |
||||||
|
self.unraethe = set(filter(lambda u: not u.finished, self.unraethe)) |
||||||
|
if len(self.unraethe) < self.num_unraethe: |
||||||
|
self.unraethe.add(Unrath(self)) |
||||||
|
|
||||||
|
for u in self.unraethe: |
||||||
|
u.update(dt) |
||||||
|
|
||||||
|
def draw(self, frame): |
||||||
|
for u in self.unraethe: |
||||||
|
u.draw(frame) |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
parser = argparse.ArgumentParser(description='Generate Unrath animations') |
||||||
|
parser.add_argument('-d', '--delay', type=int, default=50) |
||||||
|
parser.add_argument('-t', '--time', type=int, default=15) |
||||||
|
parser.add_argument('-n', '--num-unraethe', type=int, default=5) |
||||||
|
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 |
||||||
|
w = World(args.num_unraethe) |
||||||
|
while t < args.time: |
||||||
|
t += args.delay / 1000 |
||||||
|
frame = blup.animation.AnimationFrame(dim, args.delay) |
||||||
|
w.update(args.delay / 1000) |
||||||
|
w.draw(frame) |
||||||
|
anim.addFrame(frame) |
||||||
|
|
||||||
|
blup.writebml.writeBml(anim, args.output_file) |
Loading…
Reference in new issue