Preliminaries of scene-oriented model implemented

This commit is contained in:
Grant Sanderson 2015-03-22 16:15:29 -06:00
parent 948e1e3038
commit 5750a40bc6
4 changed files with 135 additions and 171 deletions

View file

@ -17,10 +17,8 @@ import displayer as disp
class Animation(object):
def __init__(self,
mobject,
run_time = DEFAULT_ANIMATION_RUN_TIME,
alpha_func = high_inflection_0_to_1,
run_time = DEFAULT_ANIMATION_RUN_TIME,
pause_time = DEFAULT_ANIMATION_PAUSE_TIME,
dither_time = DEFAULT_DITHER_TIME,
name = None):
if isinstance(mobject, type) and issubclass(mobject, Mobject):
self.mobject = mobject()
@ -34,24 +32,14 @@ class Animation(object):
self.reference_mobjects = [self.starting_mobject]
self.alpha_func = alpha_func or (lambda x : x)
self.run_time = run_time
self.pause_time = pause_time
self.dither_time = dither_time
self.nframes, self.ndither_frames = self.get_frame_count()
self.nframes_past = 0
self.frames = []
self.concurrent_animations = []
self.following_animations = []
self.reference_animations = []
self.background_mobjects = []
self.filter_functions = []
self.restricted_height = SPACE_HEIGHT
self.restricted_width = SPACE_WIDTH
self.spacial_center = np.zeros(3)
self.name = self.__class__.__name__ + str(self.mobject)
self.inputted_name = name
self.name = name or self.__class__.__name__ + str(self.mobject)
def __str__(self):
return self.inputted_name or self.name
return self.name
def get_points_and_rgbs(self):
"""
@ -59,24 +47,10 @@ class Animation(object):
the space. Returns np array of points and corresponding np array
of rgbs
"""
points = np.zeros(0)
rgbs = np.zeros(0)
for mobject in self.background_mobjects + [self.mobject]:
points = np.append(points, mobject.points)
rgbs = np.append(rgbs, mobject.rgbs)
#Kind of hacky
if mobject.SHOULD_BUFF_POINTS: #TODO, think about this.
up_nudge = np.array(
(2.0 * SPACE_HEIGHT / HEIGHT, 0, 0)
)
side_nudge = np.array(
(0, 2.0 * SPACE_WIDTH / WIDTH, 0)
)
for nudge in up_nudge, side_nudge, up_nudge + side_nudge:
points = np.append(points, mobject.points + nudge)
rgbs = np.append(rgbs, mobject.rgbs)
points = points.reshape((points.size/3, 3))
rgbs = rgbs.reshape((rgbs.size/3, 3))
#TODO, I don't think this should be necessary. This should happen
#under the individual mobjects.
points = self.mobject.points
rgbs = self.mobject.rgbs
#Filters out what is out of bounds.
admissibles = (abs(points[:,0]) < self.restricted_width) * \
(abs(points[:,1]) < self.restricted_height)
@ -93,91 +67,27 @@ class Animation(object):
admissibles = admissibles[:rgbs.shape[0]]
return points[admissibles, :], rgbs[admissibles, :]
def update(self):
if self.nframes_past > self.nframes:
return False
self.nframes_past += 1
for anim in self.concurrent_animations + self.reference_animations:
anim.update()
self.update_mobject(self.alpha_func(self.get_fraction_complete()))
return True
def update(self, alpha):
if alpha < 0:
alpha = 0
if alpha > 1:
alpha = 1
self.update_mobject(self.alpha_func(alpha))
def while_also(self, action, display = True, *args, **kwargs):
if isinstance(action, type) and issubclass(action, Animation):
self.reference_animations += [
action(mobject, *args, **kwargs)
for mobject in self.reference_mobjects + [self.mobject]
]
self.name += action.__name__
return self
if action.mobject == self.mobject:
#This is just for a weird edge case
action.mobject = self.starting_mobject
new_home = self.concurrent_animations if display else \
self.reference_animations
new_home.append(action)
self.name += str(action)
return self
# def generate_frames(self):
# print "Generating " + str(self) + "..."
# progress_bar = progressbar.ProgressBar(maxval=self.nframes)
# progress_bar.start()
def with_background(self, *mobjects):
for anim in [self] + self.following_animations:
anim.background_mobjects.append(CompoundMobject(*mobjects))
return self
def then(self, action, carry_over_background = False, *args, **kwargs):
if isinstance(action, type) and issubclass(action, Animation):
action = action(mobject = self.mobject, *args, **kwargs)
if carry_over_background:
action.background_mobjects += self.background_mobjects
self.following_animations.append(action)
if self.frames:
self.frames += action.get_frames()
self.name += "Then" + str(action)
return self
def get_image(self):
all_points, all_rgbs = self.get_points_and_rgbs()
for anim in self.concurrent_animations:
new_points, new_rgbs = anim.get_points_and_rgbs()
all_points = np.append(all_points, new_points)
all_rgbs = np.append(all_rgbs, new_rgbs)
all_points = all_points.reshape((all_points.size/3, 3))
all_rgbs = all_rgbs.reshape((all_rgbs.size/3, 3))
return disp.get_image(all_points, all_rgbs)
def generate_frames(self):
print "Generating " + str(self) + "..."
progress_bar = progressbar.ProgressBar(maxval=self.nframes)
progress_bar.start()
self.frames = []
while self.update():
self.frames.append(self.get_image())
progress_bar.update(self.nframes_past - 1)
self.clean_up()
for anim in self.following_animations:
self.frames += anim.get_frames()
progress_bar.finish()
return self
def get_fraction_complete(self):
result = float(self.nframes_past - self.ndither_frames) / (
self.nframes - 2 * self.ndither_frames)
if result <= 0:
return 0
elif result >= 1:
return 1
return result
def get_frames(self):
if not self.frames:
self.generate_frames()
return self.frames
def get_frame_count(self):
nframes = int((self.run_time + 2*self.dither_time)/ self.pause_time)
ndither_frames = int(self.dither_time / self.pause_time)
return nframes, ndither_frames
# self.frames = []
# while self.update():
# self.frames.append(self.get_image())
# progress_bar.update(self.nframes_past - 1)
# self.clean_up()
# for anim in self.following_animations:
# self.frames += anim.get_frames()
# progress_bar.finish()
# return self
def filter_out(self, *filter_functions):
self.filter_functions += filter_functions
@ -193,22 +103,10 @@ class Animation(object):
def shift(self, vector):
self.spacial_center += vector
for anim in self.following_animations:
anim.shift(vector)
return self
def set_dither(self, time, apply_to_concurrent = False):
self.dither_time = time
if apply_to_concurrent:
for anim in self.concurrent_animations + self.reference_animations:
anim.set_dither(time)
return self.reload()
def set_run_time(self, time, apply_to_concurrent = False):
def set_run_time(self, time):
self.run_time = time
if apply_to_concurrent:
for anim in self.concurrent_animations + self.reference_animations:
anim.set_run_time(time)
return self.reload()
def set_alpha_func(self, alpha_func):
@ -218,30 +116,17 @@ class Animation(object):
return self
def set_name(self, name):
self.inputted_name = name
self.name = name
return self
def reload(self):
self.nframes, self.ndither_frames = self.get_frame_count()
if self.frames:
self.nframes_past = 0
self.generate_frames()
return self
# def drag_pixels(self):
# self.frames = drag_pixels(self.get_frames())
# return self
def drag_pixels(self):
self.frames = drag_pixels(self.get_frames())
return self
def reverse(self):
self.get_frames().reverse()
self.name = 'Reversed' + str(self)
return self
def write_to_gif(self, name = None):
disp.write_to_gif(self, name or str(self))
def write_to_movie(self, name = None):
disp.write_to_movie(self, name or str(self))
# def reverse(self):
# self.get_frames().reverse()
# self.name = 'Reversed' + str(self)
# return self
def update_mobject(self, alpha):
#Typically ipmlemented by subclass
@ -249,9 +134,6 @@ class Animation(object):
def clean_up(self):
pass
def dither(self):
pass
###### Concrete Animations ########
@ -263,12 +145,11 @@ class Rotating(Animation):
axes = [[0, 0, 1], [0, 1, 0]],
radians = 2 * np.pi,
run_time = 20.0,
dither_time = 0.0,
alpha_func = None,
*args, **kwargs):
Animation.__init__(
self, mobject,
run_time = run_time, dither_time = dither_time,
run_time = run_time,
alpha_func = alpha_func,
*args, **kwargs
)
@ -282,19 +163,20 @@ class Rotating(Animation):
self.radians * alpha,
axis
)
class RotationAsTransform(Rotating):
def __init__(self, mobject, radians,
def __init__(self, mobject, radians, axis = (0, 0, 1), axes = None,
run_time = DEFAULT_ANIMATION_RUN_TIME,
dither_time = DEFAULT_DITHER_TIME,
alpha_func = high_inflection_0_to_1,
*args, **kwargs):
Rotating.__init__(
self,
mobject,
axis = (0, 0, 1),
axis = axis,
axes = axes,
run_time = run_time,
dither_time = dither_time,
radians = radians,
alpha_func = high_inflection_0_to_1,
alpha_func = alpha_func,
)
class FadeOut(Animation):
@ -309,7 +191,8 @@ class Reveal(Animation):
#TODO, Why do you need to do this? Shouldn't points always align?
class Transform(Animation):
def __init__(self, mobject1, mobject2, run_time = DEFAULT_TRANSFORM_RUN_TIME,
def __init__(self, mobject1, mobject2,
run_time = DEFAULT_TRANSFORM_RUN_TIME,
*args, **kwargs):
count1, count2 = mobject1.get_num_points(), mobject2.get_num_points()
Mobject.align_data(mobject1, mobject2)
@ -353,7 +236,7 @@ class Transform(Animation):
class ApplyMethod(Transform):
def __init__(self, method, mobject, *args, **kwargs):
"""
method is a method of Mobject
Method is a method of Mobject
"""
method_args = ()
if isinstance(method, tuple):
@ -434,6 +317,7 @@ class ComplexHomotopy(Homotopy):
class ShowCreation(Animation):
def update_mobject(self, alpha):
#TODO, shoudl I make this more efficient?
new_num_points = int(alpha * self.starting_mobject.points.shape[0])
for attr in ["points", "rgbs"]:
setattr(
@ -444,10 +328,9 @@ class ShowCreation(Animation):
class Flash(Animation):
def __init__(self, mobject, color = "white", slow_factor = 0.01,
run_time = 0.1, dither_time = 0, alpha_func = None,
run_time = 0.1, alpha_func = None,
*args, **kwargs):
Animation.__init__(self, mobject, run_time = run_time,
dither_time = dither_time,
alpha_func = alpha_func,
*args, **kwargs)
self.intermediate = Mobject(color = color)

View file

@ -8,7 +8,7 @@ DEFAULT_POINT_DENSITY_1D = 200 if PRODUCTION_QUALITY else 50
HEIGHT = 1024#1440 if PRODUCTION_QUALITY else 480
WIDTH = 1024#2560 if PRODUCTION_QUALITY else 640
#All in seconds
DEFAULT_ANIMATION_PAUSE_TIME = 0.04 if PRODUCTION_QUALITY else 0.1
DEFAULT_FRAME_DURATION = 0.04 if PRODUCTION_QUALITY else 0.1
DEFAULT_ANIMATION_RUN_TIME = 3.0
DEFAULT_TRANSFORM_RUN_TIME = 1.0
DEFAULT_DITHER_TIME = 1.0

View file

@ -36,20 +36,21 @@ def get_pixels(points, rgbs):
pixels[indices] = rgbs
return pixels.reshape((HEIGHT, WIDTH, 3)).astype('uint8')
def write_to_gif(animation, name):
def write_to_gif(scene, name):
#TODO, find better means of compression
if not name.endswith(".gif"):
name += ".gif"
filepath = os.path.join(GIF_DIR, name)
temppath = os.path.join(GIF_DIR, "Temp.gif")
print "Writing " + name + "..."
writeGif(temppath, animation.get_frames(), animation.pause_time)
writeGif(temppath, scene.frames, scene.frame_duration)
print "Compressing..."
os.system("gifsicle -O " + temppath + " > " + filepath)
os.system("rm " + temppath)
def write_to_movie(animation, name):
frames = animation.get_frames()
def write_to_movie(scene, name):
#TODO, incorporate pause time
frames = scene.frames
progress_bar = progressbar.ProgressBar(maxval=len(frames))
progress_bar.start()
print "writing " + name + "..."
@ -75,7 +76,7 @@ def write_to_movie(animation, name):
"-c:v",
"libx264",
"-vf",
"fps=%d,format=yuv420p"%int(1/animation.pause_time),
"fps=%d,format=yuv420p"%int(1/scene.frame_duration),
filepath
]
os.system(" ".join(commands))
@ -87,7 +88,7 @@ def write_to_movie(animation, name):
# filepath = os.path.join(MOVIE_DIR, name + ".mov")
# fourcc = cv2.cv.FOURCC(*"8bps")
# out = cv2.VideoWriter(
# filepath, fourcc, 1.0/animation.pause_time, (WIDTH, HEIGHT), True
# filepath, fourcc, 1.0/animation.frame_duration, (WIDTH, HEIGHT), True
# )
# progress = 0
# for frame in frames:

80
scene.py Normal file
View file

@ -0,0 +1,80 @@
from PIL import Image
from colour import Color
import numpy as np
import warnings
import time
import os
import copy
import progressbar
import inspect
from helpers import *
from mobject import *
from animate import *
import displayer as disp
class Scene(object):
def __init__(self,
frame_duration = DEFAULT_FRAME_DURATION,
name = None):
self.frame_duration = frame_duration
self.frames = []
self.mobjects = set([])
self.name = name
def __str__(self):
return self.name or "Babadinook" #TODO
def add(self, *mobjects):
#TODO, perhaps mobjects should be ordered, for foreground/background
self.mobjects.update(mobjects)
def remove(self, *mobjects):
self.mobjects.difference_update(mobjects)
def animate(self, animations,
dither_time = DEFAULT_DITHER_TIME):
if isinstance(animations, Animation):
animations = [animations]
self.pause(dither_time)
run_time = max([anim.run_time for anim in animations])
print "Generating animations..."
progress_bar = progressbar.ProgressBar(maxval=run_time)
progress_bar.start()
for t in np.arange(0, run_time, self.frame_duration):
progress_bar.update(t)
for anim in animations:
anim.update(t)
self.frames.append(self.get_frame(*animations))
for anim in animations:
anim.clean_up()
progress_bar.finish()
def pause(self, duration):
self.frames += [self.get_frame()]*int(duration / self.frame_duration)
def get_frame(self, *animations):
#Include animations so as to display mobjects not in the list
#TODO, This is temporary
mob = list(self.mobjects)[0]
return disp.get_image(mob.points, mob.rgbs)
def write_to_gif(self, name = None, end_dither_time = DEFAULT_DITHER_TIME):
self.pause(end_dither_time)
disp.write_to_gif(self, name or str(self))
def write_to_movie(self, name = None, end_dither_time = DEFAULT_DITHER_TIME):
self.pause(end_dither_time)
disp.write_to_movie(self, name or str(self))