Fr3deric
7 years ago
1 changed files with 222 additions and 0 deletions
@ -0,0 +1,222 @@
@@ -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