Go back to scene recomputing mobject families on each frame, and fix a bug with skipping

This commit is contained in:
Grant Sanderson 2020-02-14 11:55:07 -08:00
parent 49d84fccde
commit b73ebb9cf5

View file

@ -5,6 +5,7 @@ import platform
from tqdm import tqdm as ProgressDisplay from tqdm import tqdm as ProgressDisplay
import numpy as np import numpy as np
import itertools as it
import time import time
from manimlib.animation.animation import Animation from manimlib.animation.animation import Animation
@ -16,7 +17,6 @@ from manimlib.mobject.mobject import Mobject
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
from manimlib.utils.iterables import list_difference_update
from manimlib.window import Window from manimlib.window import Window
@ -46,9 +46,9 @@ class Scene(Container):
self.camera = self.camera_class(**self.camera_config) self.camera = self.camera_class(**self.camera_config)
self.file_writer = SceneFileWriter(self, **self.file_writer_config) self.file_writer = SceneFileWriter(self, **self.file_writer_config)
self.mobjects = [] self.mobjects = []
self.displayed_mobjects = []
self.num_plays = 0 self.num_plays = 0
self.time = 0 self.time = 0
self.skip_time = 0
self.original_skipping_status = self.skip_animations self.original_skipping_status = self.skip_animations
self.time_of_last_frame = time.time() self.time_of_last_frame = time.time()
if self.random_seed is not None: if self.random_seed is not None:
@ -91,6 +91,7 @@ class Scene(Container):
# which updates the frame while under # which updates the frame while under
# the hood calling the pyglet event loop # the hood calling the pyglet event loop
while not self.window.is_closing: while not self.window.is_closing:
# t, dt = self.window.timer.next_frame()
self.update_frame() self.update_frame()
self.window.destroy() self.window.destroy()
@ -100,6 +101,7 @@ class Scene(Container):
def print_end_message(self): def print_end_message(self):
print(f"Played {self.num_plays} animations") print(f"Played {self.num_plays} animations")
# TODO, remove this
def set_variables_as_attrs(self, *objects, **newly_named_objects): def set_variables_as_attrs(self, *objects, **newly_named_objects):
""" """
This method is slightly hacky, making it a little easier This method is slightly hacky, making it a little easier
@ -122,27 +124,29 @@ class Scene(Container):
def get_image(self): def get_image(self):
return self.camera.get_image() return self.camera.get_image()
def update_frame(self, ignore_skipping=False): def update_frame(self, dt=0, ignore_skipping=False):
self.increment_time(dt)
if self.skip_animations and not ignore_skipping: if self.skip_animations and not ignore_skipping:
return return
if self.window: if self.window:
self.window.clear() self.window.clear()
self.camera.clear() self.camera.clear()
self.camera.capture(*self.displayed_mobjects) self.camera.capture(*self.get_displayed_mobjects())
if self.window: if self.window:
self.window.swap_buffers() self.window.swap_buffers()
win_time, win_dt = self.window.timer.next_frame()
while (self.time - self.skip_time - win_time) > 0:
self.window.clear()
self.camera.capture(*self.get_displayed_mobjects())
self.window.swap_buffers()
win_time, win_dt = self.window.timer.next_frame()
def emit_frame(self, dt): def emit_frame(self):
self.increment_time(dt)
if not self.skip_animations: if not self.skip_animations:
self.file_writer.write_frame(self.camera) self.file_writer.write_frame(self.camera)
if self.window:
frame_duration = 1 / self.camera.frame_rate
t, dt = self.window.timer.next_frame()
time.sleep(np.clip(0, frame_duration - dt, frame_duration))
### ###
def update_mobjects(self, dt): def update_mobjects(self, dt):
@ -160,15 +164,15 @@ class Scene(Container):
def get_time(self): def get_time(self):
return self.time return self.time
def increment_time(self, d_time): def increment_time(self, dt):
self.time += d_time self.time += dt
### ###
def recompute_displayed_mobjects(self): def get_displayed_mobjects(self):
self.displayed_mobjects = extract_mobject_family_members( return it.chain(*[
self.mobjects, mob.family_members_with_points()
only_those_with_points=True, for mob in self.mobjects
) ])
def get_top_level_mobjects(self): def get_top_level_mobjects(self):
# Return only those which are not in the family # Return only those which are not in the family
@ -194,7 +198,6 @@ class Scene(Container):
""" """
self.remove(*new_mobjects) self.remove(*new_mobjects)
self.mobjects += new_mobjects self.mobjects += new_mobjects
self.recompute_displayed_mobjects()
return self return self
def add_mobjects_among(self, values): def add_mobjects_among(self, values):
@ -213,7 +216,6 @@ class Scene(Container):
self.mobjects = restructure_list_to_exclude_certain_family_members( self.mobjects = restructure_list_to_exclude_certain_family_members(
self.mobjects, mobjects_to_remove self.mobjects, mobjects_to_remove
) )
self.recompute_displayed_mobjects()
return self return self
def bring_to_front(self, *mobjects): def bring_to_front(self, *mobjects):
@ -223,12 +225,10 @@ class Scene(Container):
def bring_to_back(self, *mobjects): def bring_to_back(self, *mobjects):
self.remove(*mobjects) self.remove(*mobjects)
self.mobjects = list(mobjects) + self.mobjects self.mobjects = list(mobjects) + self.mobjects
self.recompute_displayed_mobjects()
return self return self
def clear(self): def clear(self):
self.mobjects = [] self.mobjects = []
self.recompute_displayed_mobjects()
return self return self
def get_mobjects(self): def get_mobjects(self):
@ -345,10 +345,11 @@ class Scene(Container):
return animations return animations
def update_skipping_status(self): def update_skipping_status(self):
if self.start_at_animation_number: if self.start_at_animation_number is not None:
if self.num_plays == self.start_at_animation_number: if self.num_plays == self.start_at_animation_number:
self.skip_animations = False self.skip_animations = False
if self.end_at_animation_number: self.skip_time += self.time
if self.end_at_animation_number is not None:
if self.num_plays >= self.end_at_animation_number: if self.num_plays >= self.end_at_animation_number:
self.skip_animations = True self.skip_animations = True
raise EndSceneEarlyException() raise EndSceneEarlyException()
@ -391,8 +392,8 @@ 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() self.update_frame(dt)
self.emit_frame(dt) self.emit_frame()
def finish_animations(self, animations): def finish_animations(self, animations):
for animation in animations: for animation in animations:
@ -454,8 +455,8 @@ class Scene(Container):
dt = t - last_t dt = t - last_t
last_t = t last_t = t
self.update_mobjects(dt) self.update_mobjects(dt)
self.update_frame() self.update_frame(dt)
self.emit_frame(dt) self.emit_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
@ -463,11 +464,10 @@ class Scene(Container):
# Do nothing # Do nothing
return self return self
else: else:
self.update_frame() self.update_frame(duration)
dt = 1 / self.camera.frame_rate n_frames = int(duration * self.camera.frame_rate)
n_frames = int(duration / dt)
for n in range(n_frames): for n in range(n_frames):
self.emit_frame(dt) self.emit_frame()
return self return self
def wait_until(self, stop_condition, max_time=60): def wait_until(self, stop_condition, max_time=60):
@ -508,10 +508,9 @@ class Scene(Container):
pass pass
def on_mouse_scroll(self, point, offset): def on_mouse_scroll(self, point, offset):
self.camera.frame.scale( frame = self.camera.frame
1 + np.arctan(3 * offset[1]), factor = 1 + np.arctan(10 * offset[1])
about_point=point, frame.scale(factor, about_point=point)
)
self.camera.refresh_shader_uniforms() self.camera.refresh_shader_uniforms()
def on_key_release(self, symbol, modifiers): def on_key_release(self, symbol, modifiers):