Starting to chase scene to account for a shader-based camera

This commit is contained in:
Grant Sanderson 2020-02-04 15:28:50 -08:00
parent 1549998a19
commit 0fe05ce07d

View file

@ -7,13 +7,11 @@ from tqdm import tqdm as ProgressDisplay
import numpy as np import numpy as np
from manimlib.animation.animation import Animation from manimlib.animation.animation import Animation
from manimlib.animation.creation import Write from manimlib.animation.transform import MoveToTarget
from manimlib.animation.transform import MoveToTarget, ApplyMethod
from manimlib.camera.camera import Camera from manimlib.camera.camera import Camera
from manimlib.constants import * from manimlib.constants import *
from manimlib.container.container import Container from manimlib.container.container import Container
from manimlib.mobject.mobject import Mobject from manimlib.mobject.mobject import Mobject
from manimlib.mobject.svg.tex_mobject import TextMobject
from manimlib.scene.scene_file_writer import SceneFileWriter from manimlib.scene.scene_file_writer import SceneFileWriter
from manimlib.utils.family_ops import extract_mobject_family_members from manimlib.utils.family_ops import extract_mobject_family_members
from manimlib.utils.family_ops import restructure_list_to_exclude_certain_family_members from manimlib.utils.family_ops import restructure_list_to_exclude_certain_family_members
@ -21,7 +19,7 @@ from manimlib.utils.family_ops import restructure_list_to_exclude_certain_family
class Scene(Container): class Scene(Container):
CONFIG = { CONFIG = {
"camera_class": Camera, "camera_class": Camera, # TODO, there should be only one camera
"camera_config": {}, "camera_config": {},
"file_writer_config": {}, "file_writer_config": {},
"skip_animations": False, "skip_animations": False,
@ -35,9 +33,7 @@ class Scene(Container):
def __init__(self, **kwargs): def __init__(self, **kwargs):
Container.__init__(self, **kwargs) Container.__init__(self, **kwargs)
self.camera = self.camera_class(**self.camera_config) self.camera = self.camera_class(**self.camera_config)
self.file_writer = SceneFileWriter( self.file_writer = SceneFileWriter(self, **self.file_writer_config)
self, **self.file_writer_config,
)
self.mobjects = [] self.mobjects = []
self.num_plays = 0 self.num_plays = 0
@ -64,12 +60,12 @@ class Scene(Container):
""" """
pass pass
def tear_down(self):
pass
def construct(self): def construct(self):
pass # To be implemented in subclasses pass # To be implemented in subclasses
def tear_down(self):
pass
def __str__(self): def __str__(self):
return self.__class__.__name__ return self.__class__.__name__
@ -98,49 +94,25 @@ class Scene(Container):
def set_camera(self, camera): def set_camera(self, camera):
self.camera = camera self.camera = camera
def get_frame(self):
return np.array(self.camera.get_pixel_array())
def get_image(self): def get_image(self):
return self.camera.get_image() return self.camera.get_image()
def set_camera_pixel_array(self, pixel_array): def update_frame(self, mobjects=None, ignore_skipping=False, excluded_mobjects=None):
self.camera.set_pixel_array(pixel_array)
def set_camera_background(self, background):
self.camera.set_background(background)
def reset_camera(self):
self.camera.reset()
def capture_mobjects_in_camera(self, mobjects, excluded_mobjects=None):
self.camera.capture_mobjects(mobjects, excluded_mobjects=excluded_mobjects)
def update_frame(
self,
mobjects=None,
background=None,
ignore_skipping=False,
excluded_mobjects=None):
if self.skip_animations and not ignore_skipping: if self.skip_animations and not ignore_skipping:
return return
# Default to displaying everything in self.mobjects
if mobjects is None: if mobjects is None:
mobjects = self.mobjects mobjects = self.mobjects
if background is not None: self.camera.clear()
self.set_camera_pixel_array(background) self.camera.capture_mobjects(mobjects, excluded_mobjects=excluded_mobjects)
else:
self.reset_camera()
self.capture_mobjects_in_camera( def write_frame(self):
mobjects, excluded_mobjects=excluded_mobjects, self.increment_time(1.0 / self.camera.frame_rate)
) if self.skip_animations:
return
data = self.camera.get_raw_fbo_data()
self.file_writer.write_frame(data)
def freeze_background(self):
self.update_frame()
self.set_camera(Camera(self.get_frame()))
self.clear()
### ###
def update_mobjects(self, dt): def update_mobjects(self, dt):
@ -234,9 +206,9 @@ class Scene(Container):
animation_mobjects = [anim.mobject for anim in animations] animation_mobjects = [anim.mobject for anim in animations]
mobjects = self.get_mobject_family_members() mobjects = self.get_mobject_family_members()
for i, mob in enumerate(mobjects): for i, mob in enumerate(mobjects):
animated = (mob in animation_mobjects) is_animated = (mob in animation_mobjects)
updated = (len(mob.get_family_updaters()) > 0) is_updated = (len(mob.get_family_updaters()) > 0)
if animated or updated: if is_animated or is_updated:
return mobjects[i:] return mobjects[i:]
return [] return []
@ -369,7 +341,7 @@ class Scene(Container):
# have to be rendered every frame # have to be rendered every frame
moving_mobjects = self.get_moving_mobjects(*animations) moving_mobjects = self.get_moving_mobjects(*animations)
self.update_frame(excluded_mobjects=moving_mobjects) self.update_frame(excluded_mobjects=moving_mobjects)
static_image = self.get_frame() self.camera.lock_state_as_background()
last_t = 0 last_t = 0
for t in self.get_animation_time_progression(animations): for t in self.get_animation_time_progression(animations):
dt = t - last_t dt = t - last_t
@ -379,8 +351,9 @@ class Scene(Container):
alpha = t / animation.run_time alpha = t / animation.run_time
animation.interpolate(alpha) animation.interpolate(alpha)
self.update_mobjects(dt) self.update_mobjects(dt)
self.update_frame(moving_mobjects, static_image) self.update_frame(moving_mobjects)
self.add_frames(self.get_frame()) self.write_frame()
self.camera.unlock_background()
def finish_animations(self, animations): def finish_animations(self, animations):
for animation in animations: for animation in animations:
@ -442,7 +415,7 @@ class Scene(Container):
self.update_mobjects(dt=0) # Any problems with this? self.update_mobjects(dt=0) # Any problems with this?
if self.should_update_mobjects(): if self.should_update_mobjects():
time_progression = self.get_wait_time_progression(duration, stop_condition) time_progression = self.get_wait_time_progression(duration, stop_condition)
# TODO, be smart about setting a static image # TODO, be smart about locking the camera background
# the same way Scene.play does # the same way Scene.play does
last_t = 0 last_t = 0
for t in time_progression: for t in time_progression:
@ -450,7 +423,7 @@ class Scene(Container):
last_t = t last_t = t
self.update_mobjects(dt) self.update_mobjects(dt)
self.update_frame() self.update_frame()
self.add_frames(self.get_frame()) self.write_frame()
if stop_condition is not None and stop_condition(): if stop_condition is not None and stop_condition():
time_progression.close() time_progression.close()
break break
@ -461,8 +434,8 @@ class Scene(Container):
self.update_frame() self.update_frame()
dt = 1 / self.camera.frame_rate dt = 1 / self.camera.frame_rate
n_frames = int(duration / dt) n_frames = int(duration / dt)
frame = self.get_frame() for n in range(n_frames):
self.add_frames(*[frame] * n_frames) self.write_frame()
return self return self
def wait_until(self, stop_condition, max_time=60): def wait_until(self, stop_condition, max_time=60):
@ -478,35 +451,16 @@ class Scene(Container):
self.skip_animations = self.original_skipping_status self.skip_animations = self.original_skipping_status
return self return self
def add_frames(self, *frames):
dt = 1 / self.camera.frame_rate
self.increment_time(len(frames) * dt)
if self.skip_animations:
return
for frame in frames:
self.file_writer.write_frame(frame)
def add_sound(self, sound_file, time_offset=0, gain=None, **kwargs): def add_sound(self, sound_file, time_offset=0, gain=None, **kwargs):
if self.skip_animations: if self.skip_animations:
return return
time = self.get_time() + time_offset time = self.get_time() + time_offset
self.file_writer.add_sound(sound_file, time, gain, **kwargs) self.file_writer.add_sound(sound_file, time, gain, **kwargs)
def show_frame(self): def show(self):
self.update_frame(ignore_skipping=True) self.update_frame(ignore_skipping=True)
self.get_image().show() self.get_image().show()
# TODO, this doesn't belong in Scene, but should be
# part of some more specialized subclass optimized
# for livestreaming
def tex(self, latex):
eq = TextMobject(latex)
anims = []
anims.append(Write(eq))
for mobject in self.mobjects:
anims.append(ApplyMethod(mobject.shift, 2 * UP))
self.play(*anims)
class EndSceneEarlyException(Exception): class EndSceneEarlyException(Exception):
pass pass