mirror of
https://github.com/3b1b/manim.git
synced 2025-08-01 08:54:38 +08:00
Initial ZoomedScene
This commit is contained in:
@ -162,7 +162,7 @@ class DelayByOrder(Animation):
|
|||||||
self.num_mobject_points = animation.mobject.get_num_points()
|
self.num_mobject_points = animation.mobject.get_num_points()
|
||||||
kwargs.update(dict([
|
kwargs.update(dict([
|
||||||
(attr, getattr(animation, attr))
|
(attr, getattr(animation, attr))
|
||||||
for attr in Animation.DEFAULT_CONFIG
|
for attr in Animation.CONFIG
|
||||||
]))
|
]))
|
||||||
Animation.__init__(self, animation.mobject, **kwargs)
|
Animation.__init__(self, animation.mobject, **kwargs)
|
||||||
self.name = str(self) + str(self.animation)
|
self.name = str(self) + str(self.animation)
|
||||||
|
13
camera.py
13
camera.py
@ -65,12 +65,10 @@ class Camera(object):
|
|||||||
|
|
||||||
def capture_mobjects(self, mobjects, include_sub_mobjects = True):
|
def capture_mobjects(self, mobjects, include_sub_mobjects = True):
|
||||||
if include_sub_mobjects:
|
if include_sub_mobjects:
|
||||||
all_families = [
|
mobjects = it.chain(*[
|
||||||
mob.submobject_family()
|
mob.submobject_family()
|
||||||
for mob in mobjects
|
for mob in mobjects
|
||||||
]
|
])
|
||||||
mobjects = reduce(op.add, all_families, [])
|
|
||||||
|
|
||||||
for mobject in mobjects:
|
for mobject in mobjects:
|
||||||
if mobject.display_mode == "region":
|
if mobject.display_mode == "region":
|
||||||
self.display_region(mobject)
|
self.display_region(mobject)
|
||||||
@ -153,9 +151,10 @@ class Camera(object):
|
|||||||
])
|
])
|
||||||
|
|
||||||
def adjusted_thickness(self, thickness):
|
def adjusted_thickness(self, thickness):
|
||||||
big_shape = PRODUCTION_QUALITY_DISPLAY_CONFIG["pixel_shape"]
|
# big_shape = PRODUCTION_QUALITY_DISPLAY_CONFIG["pixel_shape"]
|
||||||
factor = sum(big_shape)/sum(self.pixel_shape)
|
# factor = sum(big_shape)/sum(self.pixel_shape)
|
||||||
return 1 + (thickness-1)/factor
|
# return 1 + (thickness-1)/factor
|
||||||
|
return thickness
|
||||||
|
|
||||||
def get_thickening_nudges(self, thickness):
|
def get_thickening_nudges(self, thickness):
|
||||||
_range = range(-thickness/2+1, thickness/2+1)
|
_range = range(-thickness/2+1, thickness/2+1)
|
||||||
|
@ -15,14 +15,14 @@ MEDIUM_QUALITY_DISPLAY_CONFIG = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LOW_QUALITY_DISPLAY_CONFIG = {
|
LOW_QUALITY_DISPLAY_CONFIG = {
|
||||||
"pixel_shape" : (480, 640),
|
"pixel_shape" : (576, 1024),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_POINT_DENSITY_2D = 25
|
DEFAULT_POINT_DENSITY_2D = 25
|
||||||
DEFAULT_POINT_DENSITY_1D = 200
|
DEFAULT_POINT_DENSITY_1D = 200
|
||||||
|
|
||||||
DEFAULT_POINT_THICKNESS = 6
|
DEFAULT_POINT_THICKNESS = 3
|
||||||
|
|
||||||
#TODO, Make sure these are not needed
|
#TODO, Make sure these are not needed
|
||||||
SPACE_HEIGHT = 4.0
|
SPACE_HEIGHT = 4.0
|
||||||
|
185
displayer.py
185
displayer.py
@ -1,185 +0,0 @@
|
|||||||
import numpy as np
|
|
||||||
import itertools as it
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
from PIL import Image
|
|
||||||
import cv2
|
|
||||||
from colour import Color
|
|
||||||
import progressbar
|
|
||||||
|
|
||||||
from helpers import *
|
|
||||||
|
|
||||||
class Camera(object):
|
|
||||||
CONFIG = {
|
|
||||||
"pixel_width" : DEFAULT_WIDTH,
|
|
||||||
"pixel_height" : DEFAULT_HEIGHT,
|
|
||||||
"space_height" : SPACE_HEIGHT,
|
|
||||||
"space_center" : ORIGIN,
|
|
||||||
"background_color" : BLACK,
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, background = None, **kwargs):
|
|
||||||
digest_config(self, kwargs, locals())
|
|
||||||
self.init_background()
|
|
||||||
self.reset()
|
|
||||||
|
|
||||||
width_to_height = float(self.pixel_width) / self.pixel_height
|
|
||||||
self.space_width = self.space_height * width_to_height
|
|
||||||
|
|
||||||
def init_background(self):
|
|
||||||
if self.background:
|
|
||||||
shape = self.background.shape[:2]
|
|
||||||
self.pixel_height, self.pixel_width = shape
|
|
||||||
else:
|
|
||||||
background_color = Color(self.background_color)
|
|
||||||
background_rgb = np.array(background_color.get_rgb())
|
|
||||||
ones = np.ones(
|
|
||||||
(self.pixel_height, self.pixel_width, 1),
|
|
||||||
dtype = 'uint8'
|
|
||||||
)
|
|
||||||
self.background = np.dot(
|
|
||||||
ones, background_rgb.reshape((1, 3))
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_image(self):
|
|
||||||
return self.pixel_array
|
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
self.pixel_array = np.array(self.background)
|
|
||||||
|
|
||||||
# def get_pixels(image_array): #TODO, FIX WIDTH/HEIGHT PROBLEM HERE
|
|
||||||
# if image_array is None:
|
|
||||||
# return np.zeros(
|
|
||||||
# (DEFAULT_HEIGHT, DEFAULT_WIDTH, 3),
|
|
||||||
# dtype = 'uint8'
|
|
||||||
# )
|
|
||||||
# else:
|
|
||||||
# pixels = np.array(image_array).astype('uint8')
|
|
||||||
# assert len(pixels.shape) == 3 and pixels.shape[2] == 3
|
|
||||||
# return pixels
|
|
||||||
|
|
||||||
# def paint_region(region, image_array = None, color = None):
|
|
||||||
# pixels = get_pixels(image_array)
|
|
||||||
# assert region.shape == pixels.shape[:2]
|
|
||||||
# if color is None:
|
|
||||||
# #Random dark color
|
|
||||||
# rgb = 0.5 * np.random.random(3)
|
|
||||||
# else:
|
|
||||||
# rgb = np.array(Color(color).get_rgb())
|
|
||||||
# pixels[region.bool_grid] = (255*rgb).astype('uint8')
|
|
||||||
# return pixels
|
|
||||||
|
|
||||||
def capture_mobject(self, mobject):
|
|
||||||
return self.capture_mobjects([mobject])
|
|
||||||
|
|
||||||
def capture_mobjects(self, mobjects, include_sub_mobjects = True):
|
|
||||||
# pixels = get_pixels(image_array)
|
|
||||||
# height = pixels.shape[0]
|
|
||||||
# width = pixels.shape[1]
|
|
||||||
# space_height = SPACE_HEIGHT
|
|
||||||
# space_width = SPACE_HEIGHT * width / height
|
|
||||||
# pixels = pixels.reshape((pixels.size/3, 3)).astype('uint8')
|
|
||||||
|
|
||||||
if include_sub_mobjects:
|
|
||||||
all_families = [
|
|
||||||
mob.submobject_family()
|
|
||||||
for mob in mobjects
|
|
||||||
]
|
|
||||||
mobjects = reduce(op.add, all_families, [])
|
|
||||||
|
|
||||||
for mobject in mobjects:
|
|
||||||
self.display_points(
|
|
||||||
mobject.points, mobject.rgbs,
|
|
||||||
mobject.point_thickness
|
|
||||||
)
|
|
||||||
|
|
||||||
def display_points(self, points, rgbs, thickness):
|
|
||||||
points = self.project_onto_screen(points)
|
|
||||||
pixel_coordinates = self.pixel_coordinates_of_points(points)
|
|
||||||
rgbs = (255*rgbs).astype('uint8')
|
|
||||||
on_screen_indices = self.on_screen_pixels(pixel_coordinates)
|
|
||||||
pixel_coordinates = pixel_coordinates[on_screen_indices]
|
|
||||||
rgbs = rgbs[on_screen_indices]
|
|
||||||
|
|
||||||
flattener = np.array([1, self.width], dtype = 'int')
|
|
||||||
flattener = flattener.reshape((2, 1))
|
|
||||||
indices = np.dot(pixel_coordinates, flattener)[:,0]
|
|
||||||
|
|
||||||
pw, ph = self.pixel_width, self.pixel_height
|
|
||||||
self.pixel_array.reshape((pw*ph, 3))
|
|
||||||
self.pixel_array[indices] = rgbs.astype('uint8')
|
|
||||||
self.pixel_array.reshape((ph, pw, 3))
|
|
||||||
|
|
||||||
def project_onto_screen(points):
|
|
||||||
## TODO
|
|
||||||
points[:,2] = 0
|
|
||||||
|
|
||||||
def pixel_coordinates_of_points(self, points):
|
|
||||||
result = np.zeros((len(points), 2))
|
|
||||||
width_mult = self.pixel_width/self.space_width/2
|
|
||||||
width_add = self.pixel_width/2
|
|
||||||
height_mult = self.pixel_height/self.space_height/2
|
|
||||||
height_add = self.pixel_height/2
|
|
||||||
#Flip on y-axis as you go
|
|
||||||
height_mult *= -1
|
|
||||||
|
|
||||||
result[:,0] = points[:,0]*width_mult + width_add
|
|
||||||
result[:,1] = points[:,1]*height_mult + height_add
|
|
||||||
return result
|
|
||||||
|
|
||||||
def on_screen_pixels(self, pixel_coordinates):
|
|
||||||
return (pixel_coordinates[:,0] < 0) | \
|
|
||||||
(pixel_coordinates[:,0] >= width) | \
|
|
||||||
(pixel_coordinates[:,1] < 0) | \
|
|
||||||
(pixel_coordinates[:,1] >= height)
|
|
||||||
|
|
||||||
|
|
||||||
# def add_thickness(pixel_indices_and_rgbs, thickness, width, height):
|
|
||||||
# """
|
|
||||||
# Imagine dragging each pixel around like a paintbrush in
|
|
||||||
# a plus-sign-shaped pixel arrangement surrounding it.
|
|
||||||
|
|
||||||
# Pass rgb = None to do nothing to them
|
|
||||||
# """
|
|
||||||
# thickness = adjusted_thickness(thickness, width, height)
|
|
||||||
# original = np.array(pixel_indices_and_rgbs)
|
|
||||||
# n_extra_columns = pixel_indices_and_rgbs.shape[1] - 2
|
|
||||||
# for nudge in range(-thickness/2+1, thickness/2+1):
|
|
||||||
# if nudge == 0:
|
|
||||||
# continue
|
|
||||||
# for x, y in [[nudge, 0], [0, nudge]]:
|
|
||||||
# pixel_indices_and_rgbs = np.append(
|
|
||||||
# pixel_indices_and_rgbs,
|
|
||||||
# original+([x, y] + [0]*n_extra_columns),
|
|
||||||
# axis = 0
|
|
||||||
# )
|
|
||||||
# admissibles = (pixel_indices_and_rgbs[:,0] >= 0) & \
|
|
||||||
# (pixel_indices_and_rgbs[:,0] < width) & \
|
|
||||||
# (pixel_indices_and_rgbs[:,1] >= 0) & \
|
|
||||||
# (pixel_indices_and_rgbs[:,1] < height)
|
|
||||||
# return pixel_indices_and_rgbs[admissibles]
|
|
||||||
|
|
||||||
def adjusted_thickness(thickness, width, height):
|
|
||||||
big_width = PRODUCTION_QUALITY_DISPLAY_CONFIG["width"]
|
|
||||||
big_height = PRODUCTION_QUALITY_DISPLAY_CONFIG["height"]
|
|
||||||
factor = (big_width + big_height) / (width + height)
|
|
||||||
return 1 + (thickness-1)/facto
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -10,6 +10,7 @@ import imp
|
|||||||
|
|
||||||
from helpers import *
|
from helpers import *
|
||||||
from scene import Scene
|
from scene import Scene
|
||||||
|
from camera import Camera
|
||||||
|
|
||||||
HELP_MESSAGE = """
|
HELP_MESSAGE = """
|
||||||
Usage:
|
Usage:
|
||||||
@ -194,7 +195,9 @@ def main():
|
|||||||
inspect.getmembers(module, is_scene)
|
inspect.getmembers(module, is_scene)
|
||||||
)
|
)
|
||||||
config["movie_prefix"] = config["file"].replace(".py", "")
|
config["movie_prefix"] = config["file"].replace(".py", "")
|
||||||
scene_kwargs = config["display_config"]
|
scene_kwargs = {
|
||||||
|
"camera" : Camera(**config["display_config"])
|
||||||
|
}
|
||||||
for SceneClass in get_scene_classes(scene_names_to_classes, config):
|
for SceneClass in get_scene_classes(scene_names_to_classes, config):
|
||||||
for args in get_scene_args(SceneClass, config):
|
for args in get_scene_args(SceneClass, config):
|
||||||
scene_kwargs["construct_args"] = tuplify(args)
|
scene_kwargs["construct_args"] = tuplify(args)
|
||||||
|
@ -28,7 +28,7 @@ class LogoGeneration(Scene):
|
|||||||
def construct(self):
|
def construct(self):
|
||||||
digest_config(self, {})
|
digest_config(self, {})
|
||||||
## Usually shouldn't need this...
|
## Usually shouldn't need this...
|
||||||
self.frame_duration = self.DEFAULT_CONFIG["frame_duration"]
|
self.frame_duration = self.CONFIG["frame_duration"]
|
||||||
##
|
##
|
||||||
digest_config(self, {})
|
digest_config(self, {})
|
||||||
circle = Circle(
|
circle = Circle(
|
||||||
|
17
helpers.py
17
helpers.py
@ -86,20 +86,25 @@ def filtered_locals(local_args):
|
|||||||
|
|
||||||
def digest_config(obj, kwargs, local_args = {}):
|
def digest_config(obj, kwargs, local_args = {}):
|
||||||
"""
|
"""
|
||||||
Sets init args and DEFAULT_CONFIG values as local variables
|
Sets init args and CONFIG values as local variables
|
||||||
|
|
||||||
|
The purpose of this function is to ensure that all
|
||||||
|
configuration of any object is inheritable, able to
|
||||||
|
be easily passed into instantiation, and is attached
|
||||||
|
as an attribute of the object.
|
||||||
"""
|
"""
|
||||||
### Assemble list of DEFAULT_CONFIGs from all super classes
|
### Assemble list of CONFIGs from all super classes
|
||||||
classes_in_heirarchy = [obj.__class__]
|
classes_in_heirarchy = [obj.__class__]
|
||||||
default_configs = []
|
configs = []
|
||||||
while len(classes_in_heirarchy) > 0:
|
while len(classes_in_heirarchy) > 0:
|
||||||
Class = classes_in_heirarchy.pop()
|
Class = classes_in_heirarchy.pop()
|
||||||
classes_in_heirarchy += Class.__bases__
|
classes_in_heirarchy += Class.__bases__
|
||||||
if hasattr(Class, "DEFAULT_CONFIG"):
|
if hasattr(Class, "CONFIG"):
|
||||||
default_configs.append(Class.DEFAULT_CONFIG)
|
configs.append(Class.CONFIG)
|
||||||
|
|
||||||
#Order matters a lot here, first dicts have higher priority
|
#Order matters a lot here, first dicts have higher priority
|
||||||
all_dicts = [kwargs, filtered_locals(local_args), obj.__dict__]
|
all_dicts = [kwargs, filtered_locals(local_args), obj.__dict__]
|
||||||
all_dicts += default_configs
|
all_dicts += configs
|
||||||
item_lists = reversed([d.items() for d in all_dicts])
|
item_lists = reversed([d.items() for d in all_dicts])
|
||||||
obj.__dict__ = dict(reduce(op.add, item_lists))
|
obj.__dict__ = dict(reduce(op.add, item_lists))
|
||||||
|
|
||||||
|
@ -109,12 +109,11 @@ class Scene(object):
|
|||||||
animation.set_run_time(run_time)
|
animation.set_run_time(run_time)
|
||||||
return animations
|
return animations
|
||||||
|
|
||||||
def separate_static_and_moving_mobjects(self, *animations):
|
def separate_moving_and_static_mobjects(self, *animations):
|
||||||
moving_mobjects = [
|
moving_mobjects = list(it.chain(*[
|
||||||
mobject
|
anim.mobject.submobject_family()
|
||||||
for anim in animations
|
for anim in animations
|
||||||
for mobject in anim.mobject.submobject_family()
|
]))
|
||||||
]
|
|
||||||
bundle = Mobject(*self.mobjects)
|
bundle = Mobject(*self.mobjects)
|
||||||
static_mobjects = filter(
|
static_mobjects = filter(
|
||||||
lambda m : m not in moving_mobjects,
|
lambda m : m not in moving_mobjects,
|
||||||
@ -126,21 +125,30 @@ class Scene(object):
|
|||||||
run_time = animations[0].run_time
|
run_time = animations[0].run_time
|
||||||
times = np.arange(0, run_time, self.frame_duration)
|
times = np.arange(0, run_time, self.frame_duration)
|
||||||
time_progression = ProgressDisplay(times)
|
time_progression = ProgressDisplay(times)
|
||||||
time_progression.set_description(
|
time_progression.set_description("".join([
|
||||||
"Animation %d: "%self.num_animations + \
|
"Animation %d: "%self.num_animations,
|
||||||
str(animations[0]) + \
|
str(animations[0]),
|
||||||
(", etc." if len(animations) > 1 else "")
|
(", etc." if len(animations) > 1 else ""),
|
||||||
)
|
]))
|
||||||
return time_progression
|
return time_progression
|
||||||
|
|
||||||
|
def update_frame(self, moving_mobjects, static_image = None):
|
||||||
|
if static_image is not None:
|
||||||
|
self.camera.set_image(static_image)
|
||||||
|
else:
|
||||||
|
self.camera.reset()
|
||||||
|
self.camera.capture_mobjects(moving_mobjects)
|
||||||
|
|
||||||
|
|
||||||
def play(self, *animations, **kwargs):
|
def play(self, *animations, **kwargs):
|
||||||
if len(animations) == 0:
|
if len(animations) == 0:
|
||||||
raise Warning("Called Scene.play with no animations")
|
raise Warning("Called Scene.play with no animations")
|
||||||
return
|
return
|
||||||
self.num_animations += 1
|
self.num_animations += 1
|
||||||
|
self.add(*[anim.mobject for anim in animations])
|
||||||
animations = self.align_run_times(*animations, **kwargs)
|
animations = self.align_run_times(*animations, **kwargs)
|
||||||
moving_mobjects, static_mobjects = \
|
moving_mobjects, static_mobjects = \
|
||||||
self.separate_static_and_moving_mobjects(*animations)
|
self.separate_moving_and_static_mobjects(*animations)
|
||||||
self.camera.reset()
|
self.camera.reset()
|
||||||
self.camera.capture_mobjects(
|
self.camera.capture_mobjects(
|
||||||
static_mobjects,
|
static_mobjects,
|
||||||
@ -151,14 +159,10 @@ class Scene(object):
|
|||||||
for t in self.get_time_progression(animations):
|
for t in self.get_time_progression(animations):
|
||||||
for animation in animations:
|
for animation in animations:
|
||||||
animation.update(t / animation.run_time)
|
animation.update(t / animation.run_time)
|
||||||
self.camera.capture_mobjects(moving_mobjects)
|
self.update_frame(moving_mobjects, static_image)
|
||||||
frame = self.camera.get_image()
|
self.frames.append(self.get_frame())
|
||||||
|
|
||||||
self.frames.append(frame)
|
|
||||||
self.camera.set_image(static_image)
|
|
||||||
for animation in animations:
|
for animation in animations:
|
||||||
animation.clean_up()
|
animation.clean_up()
|
||||||
self.add(*[anim.mobject for anim in animations])
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def play_over_time_range(self, t0, t1, *animations):
|
def play_over_time_range(self, t0, t1, *animations):
|
||||||
@ -243,14 +247,14 @@ class Scene(object):
|
|||||||
print "Writing to %s"%file_path
|
print "Writing to %s"%file_path
|
||||||
|
|
||||||
fps = int(1/self.frame_duration)
|
fps = int(1/self.frame_duration)
|
||||||
dim = tuple(reversed(self.shape))
|
height, width = self.camera.pixel_shape
|
||||||
|
|
||||||
command = [
|
command = [
|
||||||
FFMPEG_BIN,
|
FFMPEG_BIN,
|
||||||
'-y', # overwrite output file if it exists
|
'-y', # overwrite output file if it exists
|
||||||
'-f', 'rawvideo',
|
'-f', 'rawvideo',
|
||||||
'-vcodec','rawvideo',
|
'-vcodec','rawvideo',
|
||||||
'-s', '%dx%d'%dim, # size of one frame
|
'-s', '%dx%d'%(width, height), # size of one frame
|
||||||
'-pix_fmt', 'rgb24',
|
'-pix_fmt', 'rgb24',
|
||||||
'-r', str(fps), # frames per second
|
'-r', str(fps), # frames per second
|
||||||
'-i', '-', # The imput comes from a pipe
|
'-i', '-', # The imput comes from a pipe
|
||||||
|
61
scene/zoomed_scene.py
Normal file
61
scene/zoomed_scene.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from scene import Scene
|
||||||
|
from mobject import Mobject
|
||||||
|
from camera import MovingCamera, Camera
|
||||||
|
|
||||||
|
from helpers import *
|
||||||
|
|
||||||
|
class ZoomedScene(Scene):
|
||||||
|
CONFIG = {
|
||||||
|
"zoomed_canvas_space_shape" : (3, 3),
|
||||||
|
"zoomed_canvas_center" : None,
|
||||||
|
"zoomed_canvas_corner" : UP+RIGHT,
|
||||||
|
"zoomed_camera_background" : None
|
||||||
|
}
|
||||||
|
def init_zooming(self, moving_camera_mobject):
|
||||||
|
self.setup_zoomed_canvas()
|
||||||
|
self.zoomed_camera = MovingCamera(
|
||||||
|
moving_camera_mobject,
|
||||||
|
pixel_shape = self.zoomed_canvas_pixel_shape,
|
||||||
|
background = self.zoomed_camera_background
|
||||||
|
)
|
||||||
|
self.add(moving_camera_mobject)
|
||||||
|
|
||||||
|
def setup_zoomed_canvas(self):
|
||||||
|
height, width = self.zoomed_canvas_space_shape
|
||||||
|
canvas_corners = Mobject().add_points([
|
||||||
|
ORIGIN, DOWN+RIGHT
|
||||||
|
])
|
||||||
|
canvas_corners.stretch_to_fit_height(height)
|
||||||
|
canvas_corners.stretch_to_fit_width(width)
|
||||||
|
canvas_corners.center()
|
||||||
|
if self.zoomed_canvas_center is not None:
|
||||||
|
canvas_corners.shift(self.zoomed_canvas_center)
|
||||||
|
elif self.zoomed_canvas_corner is not None:
|
||||||
|
canvas_corners.to_corner(self.zoomed_canvas_corner)
|
||||||
|
|
||||||
|
pixel_coords = self.camera.points_to_pixel_coords(canvas_corners.points)
|
||||||
|
upper_left, lower_right = pixel_coords
|
||||||
|
self.zoomed_canvas_pixel_indices = pixel_coords
|
||||||
|
self.zoomed_canvas_pixel_shape = (
|
||||||
|
lower_right[0] - upper_left[0],
|
||||||
|
lower_right[1] - upper_left[1]
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_frame(self):
|
||||||
|
frame = Scene.get_frame(self)
|
||||||
|
(up, left), (down, right) = self.zoomed_canvas_pixel_indices
|
||||||
|
frame[left:right, up:down, :] = self.zoomed_camera.get_image()
|
||||||
|
return frame
|
||||||
|
|
||||||
|
def update_frame(self, *args, **kwargs):
|
||||||
|
Scene.update_frame(self, *args, **kwargs)
|
||||||
|
self.zoomed_camera.reset()
|
||||||
|
self.zoomed_camera.capture_mobjects(self.mobjects)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Reference in New Issue
Block a user