Fr3deric
7 years ago
1 changed files with 222 additions and 0 deletions
@ -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)*5) * 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) |
Loading…
Reference in new issue