mirror of
https://github.com/3b1b/manim.git
synced 2025-07-30 05:24:22 +08:00
Refactoring how Camera frame height and width are handled
This commit is contained in:
@ -30,9 +30,10 @@ class Camera(object):
|
|||||||
CONFIG = {
|
CONFIG = {
|
||||||
"background_image": None,
|
"background_image": None,
|
||||||
"pixel_shape": (DEFAULT_PIXEL_HEIGHT, DEFAULT_PIXEL_WIDTH),
|
"pixel_shape": (DEFAULT_PIXEL_HEIGHT, DEFAULT_PIXEL_WIDTH),
|
||||||
# Note: frame_shape will be resized to match pixel_shape
|
# Note: frame height and width will be resized to match pixel_shape
|
||||||
"frame_shape": (FRAME_HEIGHT, FRAME_WIDTH),
|
"frame_height": FRAME_HEIGHT,
|
||||||
"space_center": ORIGIN,
|
"frame_width": FRAME_WIDTH,
|
||||||
|
"frame_center": ORIGIN,
|
||||||
"background_color": BLACK,
|
"background_color": BLACK,
|
||||||
"background_opacity": 0,
|
"background_opacity": 0,
|
||||||
# Points in vectorized mobjects with norm greater
|
# Points in vectorized mobjects with norm greater
|
||||||
@ -67,21 +68,41 @@ class Camera(object):
|
|||||||
self.resize_frame_shape()
|
self.resize_frame_shape()
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
|
def get_frame_height(self):
|
||||||
|
return self.frame_height
|
||||||
|
|
||||||
|
def get_frame_width(self):
|
||||||
|
return self.frame_width
|
||||||
|
|
||||||
|
def get_frame_center(self):
|
||||||
|
return self.frame_center
|
||||||
|
|
||||||
|
def set_frame_height(self, frame_height):
|
||||||
|
self.frame_height = frame_height
|
||||||
|
|
||||||
|
def set_frame_width(self, frame_width):
|
||||||
|
self.frame_width = frame_width
|
||||||
|
|
||||||
|
def set_frame_center(self, frame_center):
|
||||||
|
self.frame_center = frame_center
|
||||||
|
|
||||||
def resize_frame_shape(self, fixed_dimension=0):
|
def resize_frame_shape(self, fixed_dimension=0):
|
||||||
"""
|
"""
|
||||||
Changes frame_shape to match the aspect ratio
|
Changes frame_shape to match the aspect ratio
|
||||||
of pixel_shape, where fixed_dimension determines
|
of pixel_shape, where fixed_dimension determines
|
||||||
whether frame_shape[0] (height) or frame_shape[1] (width)
|
whether frame_height or frame_width
|
||||||
remains fixed while the other changes accordingly.
|
remains fixed while the other changes accordingly.
|
||||||
"""
|
"""
|
||||||
frame_height, frame_width = self.frame_shape
|
|
||||||
pixel_height, pixel_width = self.pixel_shape
|
pixel_height, pixel_width = self.pixel_shape
|
||||||
|
frame_height = self.get_frame_height()
|
||||||
|
frame_width = self.get_frame_width()
|
||||||
aspect_ratio = fdiv(pixel_width, pixel_height)
|
aspect_ratio = fdiv(pixel_width, pixel_height)
|
||||||
if fixed_dimension == 0:
|
if fixed_dimension == 0:
|
||||||
frame_height = frame_width / aspect_ratio
|
frame_height = frame_width / aspect_ratio
|
||||||
else:
|
else:
|
||||||
frame_width = aspect_ratio * frame_height
|
frame_width = aspect_ratio * frame_height
|
||||||
self.frame_shape = (frame_height, frame_width)
|
self.set_frame_height(frame_height)
|
||||||
|
self.set_frame_width(frame_width)
|
||||||
|
|
||||||
def init_background(self):
|
def init_background(self):
|
||||||
if self.background_image is not None:
|
if self.background_image is not None:
|
||||||
@ -455,15 +476,16 @@ class Camera(object):
|
|||||||
return points
|
return points
|
||||||
|
|
||||||
def points_to_pixel_coords(self, points):
|
def points_to_pixel_coords(self, points):
|
||||||
shifted_points = points - self.space_center
|
shifted_points = points - self.get_frame_center()
|
||||||
|
|
||||||
result = np.zeros((len(points), 2))
|
result = np.zeros((len(points), 2))
|
||||||
ph, pw = self.pixel_shape
|
pixel_height, pixel_width = self.pixel_shape
|
||||||
sh, sw = self.frame_shape
|
frame_height = self.get_frame_height()
|
||||||
width_mult = pw / sw
|
frame_width = self.get_frame_width()
|
||||||
width_add = pw / 2
|
width_mult = pixel_width / frame_width
|
||||||
height_mult = ph / sh
|
width_add = pixel_width / 2
|
||||||
height_add = ph / 2
|
height_mult = pixel_height / frame_height
|
||||||
|
height_add = pixel_height / 2
|
||||||
# Flip on y-axis as you go
|
# Flip on y-axis as you go
|
||||||
height_mult *= -1
|
height_mult *= -1
|
||||||
|
|
||||||
@ -499,7 +521,10 @@ class Camera(object):
|
|||||||
|
|
||||||
def get_coords_of_all_pixels(self):
|
def get_coords_of_all_pixels(self):
|
||||||
# These are in x, y order, to help me keep things straight
|
# These are in x, y order, to help me keep things straight
|
||||||
full_space_dims = np.array(self.frame_shape)[::-1]
|
full_space_dims = np.array([
|
||||||
|
self.get_frame_width(),
|
||||||
|
self.get_frame_height()
|
||||||
|
])
|
||||||
full_pixel_dims = np.array(self.pixel_shape)[::-1]
|
full_pixel_dims = np.array(self.pixel_shape)[::-1]
|
||||||
|
|
||||||
# These are addressed in the same y, x order as in pixel_array, but the values in them
|
# These are addressed in the same y, x order as in pixel_array, but the values in them
|
||||||
|
@ -43,6 +43,7 @@ class MappingCamera(Camera):
|
|||||||
# TODO: Add optional separator borders between cameras (or perhaps peel this off into a
|
# TODO: Add optional separator borders between cameras (or perhaps peel this off into a
|
||||||
# CameraPlusOverlay class)
|
# CameraPlusOverlay class)
|
||||||
|
|
||||||
|
# TODO, the classes below should likely be deleted
|
||||||
|
|
||||||
class OldMultiCamera(Camera):
|
class OldMultiCamera(Camera):
|
||||||
def __init__(self, *cameras_with_start_positions, **kwargs):
|
def __init__(self, *cameras_with_start_positions, **kwargs):
|
||||||
|
@ -34,21 +34,40 @@ class MovingCamera(Camera):
|
|||||||
self.frame = frame
|
self.frame = frame
|
||||||
Camera.__init__(self, **kwargs)
|
Camera.__init__(self, **kwargs)
|
||||||
|
|
||||||
|
# TODO, make these work for a rotated frame
|
||||||
|
def get_frame_height(self):
|
||||||
|
return self.frame.get_height()
|
||||||
|
|
||||||
|
def get_frame_width(self):
|
||||||
|
return self.frame.get_width()
|
||||||
|
|
||||||
|
def get_frame_center(self):
|
||||||
|
return self.frame.get_center()
|
||||||
|
|
||||||
|
def set_frame_height(self, frame_height):
|
||||||
|
self.frame.stretch_to_fit_height(frame_height)
|
||||||
|
|
||||||
|
def set_frame_width(self, frame_width):
|
||||||
|
self.frame.stretch_to_fit_width(frame_width)
|
||||||
|
|
||||||
|
def set_frame_center(self, frame_center):
|
||||||
|
self.frame.move_to(frame_center)
|
||||||
|
|
||||||
def capture_mobjects(self, mobjects, **kwargs):
|
def capture_mobjects(self, mobjects, **kwargs):
|
||||||
self.reset_space_center()
|
# self.reset_frame_center()
|
||||||
self.realign_frame_shape()
|
# self.realign_frame_shape()
|
||||||
Camera.capture_mobjects(self, mobjects, **kwargs)
|
Camera.capture_mobjects(self, mobjects, **kwargs)
|
||||||
|
|
||||||
def reset_space_center(self):
|
# def reset_frame_center(self):
|
||||||
self.space_center = self.frame.get_center()
|
# self.frame_center = self.frame.get_center()
|
||||||
|
|
||||||
def realign_frame_shape(self):
|
# def realign_frame_shape(self):
|
||||||
height, width = self.frame_shape
|
# height, width = self.frame_shape
|
||||||
if self.fixed_dimension == 0:
|
# if self.fixed_dimension == 0:
|
||||||
self.frame_shape = (height, self.frame.get_width())
|
# self.frame_shape = (height, self.frame.get_width())
|
||||||
else:
|
# else:
|
||||||
self.frame_shape = (self.frame.get_height(), width)
|
# self.frame_shape = (self.frame.get_height(), width)
|
||||||
self.resize_frame_shape(fixed_dimension=self.fixed_dimension)
|
# self.resize_frame_shape(fixed_dimension=self.fixed_dimension)
|
||||||
|
|
||||||
def get_mobjects_indicating_movement(self):
|
def get_mobjects_indicating_movement(self):
|
||||||
"""
|
"""
|
||||||
|
@ -25,15 +25,14 @@ class MultiCamera(MovingCamera):
|
|||||||
def update_sub_cameras(self):
|
def update_sub_cameras(self):
|
||||||
""" Reshape sub_camera pixel_arrays """
|
""" Reshape sub_camera pixel_arrays """
|
||||||
for imfc in self.image_mobjects_from_cameras:
|
for imfc in self.image_mobjects_from_cameras:
|
||||||
frame_height, frame_width = self.frame_shape
|
|
||||||
pixel_height, pixel_width = self.get_pixel_array().shape[:2]
|
pixel_height, pixel_width = self.get_pixel_array().shape[:2]
|
||||||
imfc.camera.frame_shape = (
|
imfc.camera.frame_shape = (
|
||||||
imfc.camera.frame.get_height(),
|
imfc.camera.frame.get_height(),
|
||||||
imfc.camera.frame.get_width(),
|
imfc.camera.frame.get_width(),
|
||||||
)
|
)
|
||||||
imfc.camera.reset_pixel_shape((
|
imfc.camera.reset_pixel_shape((
|
||||||
int(pixel_height * imfc.get_height() / frame_height),
|
int(pixel_height * imfc.get_height() / self.get_frame_height()),
|
||||||
int(pixel_width * imfc.get_width() / frame_width),
|
int(pixel_width * imfc.get_width() / self.get_frame_width()),
|
||||||
))
|
))
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
|
@ -49,7 +49,7 @@ class ThreeDCamera(CameraWithPerspective):
|
|||||||
self.rotation_mobject = VectorizedPoint()
|
self.rotation_mobject = VectorizedPoint()
|
||||||
# moving_center lives in the x-y-z space
|
# moving_center lives in the x-y-z space
|
||||||
# It representes the center of rotation
|
# It representes the center of rotation
|
||||||
self.moving_center = VectorizedPoint(self.space_center)
|
self.moving_center = VectorizedPoint(self.frame_center)
|
||||||
self.set_position(self.phi, self.theta, self.distance)
|
self.set_position(self.phi, self.theta, self.distance)
|
||||||
|
|
||||||
def modified_rgb(self, vmobject, rgb):
|
def modified_rgb(self, vmobject, rgb):
|
||||||
@ -163,7 +163,7 @@ class ThreeDCamera(CameraWithPerspective):
|
|||||||
center_of_rotation = self.get_center_of_rotation(
|
center_of_rotation = self.get_center_of_rotation(
|
||||||
center_x, center_y, center_z)
|
center_x, center_y, center_z)
|
||||||
self.moving_center.move_to(center_of_rotation)
|
self.moving_center.move_to(center_of_rotation)
|
||||||
self.space_center = self.moving_center.points[0]
|
self.frame_center = self.moving_center.points[0]
|
||||||
|
|
||||||
def get_view_transformation_matrix(self):
|
def get_view_transformation_matrix(self):
|
||||||
return (self.default_distance / self.get_distance()) * np.dot(
|
return (self.default_distance / self.get_distance()) * np.dot(
|
||||||
@ -174,6 +174,6 @@ class ThreeDCamera(CameraWithPerspective):
|
|||||||
def points_to_pixel_coords(self, points):
|
def points_to_pixel_coords(self, points):
|
||||||
matrix = self.get_view_transformation_matrix()
|
matrix = self.get_view_transformation_matrix()
|
||||||
new_points = np.dot(points, matrix.T)
|
new_points = np.dot(points, matrix.T)
|
||||||
self.space_center = self.moving_center.points[0]
|
self.frame_center = self.moving_center.points[0]
|
||||||
|
|
||||||
return Camera.points_to_pixel_coords(self, new_points)
|
return Camera.points_to_pixel_coords(self, new_points)
|
||||||
|
@ -199,6 +199,15 @@ class Mobject(Container):
|
|||||||
)
|
)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def apply_function_to_position(self, function):
|
||||||
|
self.move_to(function(self.get_center()))
|
||||||
|
return self
|
||||||
|
|
||||||
|
def apply_function_to_submobject_positions(self, function):
|
||||||
|
for submob in self.submobjects:
|
||||||
|
submob.apply_function_to_position(function)
|
||||||
|
return self
|
||||||
|
|
||||||
def apply_matrix(self, matrix, **kwargs):
|
def apply_matrix(self, matrix, **kwargs):
|
||||||
# Default to applying matrix about the origin, not mobjects center
|
# Default to applying matrix about the origin, not mobjects center
|
||||||
if len(kwargs) == 0:
|
if len(kwargs) == 0:
|
||||||
|
@ -2056,7 +2056,7 @@ class IPTScene1(PiCreatureScene):
|
|||||||
# use the following for the zoomed inset
|
# use the following for the zoomed inset
|
||||||
if show_detail:
|
if show_detail:
|
||||||
self.camera.frame_shape = (0.02 * FRAME_HEIGHT, 0.02 * FRAME_WIDTH)
|
self.camera.frame_shape = (0.02 * FRAME_HEIGHT, 0.02 * FRAME_WIDTH)
|
||||||
self.camera.space_center = C
|
self.camera.frame_center = C
|
||||||
SCREEN_SCALE = 0.01
|
SCREEN_SCALE = 0.01
|
||||||
SCREEN_THICKNESS = 0.02
|
SCREEN_THICKNESS = 0.02
|
||||||
|
|
||||||
|
@ -7,11 +7,12 @@ from mobject.types.image_mobject import ImageMobjectFromCamera
|
|||||||
from utils.simple_functions import fdiv
|
from utils.simple_functions import fdiv
|
||||||
|
|
||||||
from constants import *
|
from constants import *
|
||||||
|
from animation.transform import ApplyMethod
|
||||||
|
|
||||||
# Note, any scenes from old videos using ZoomedScene will almost certainly
|
# Note, any scenes from old videos using ZoomedScene will almost certainly
|
||||||
# break, as it was restructured.
|
# break, as it was restructured.
|
||||||
|
|
||||||
|
|
||||||
class ZoomedScene(MovingCameraScene):
|
class ZoomedScene(MovingCameraScene):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"camera_class": MultiCamera,
|
"camera_class": MultiCamera,
|
||||||
@ -56,33 +57,36 @@ class ZoomedScene(MovingCameraScene):
|
|||||||
self.zoomed_camera = zoomed_camera
|
self.zoomed_camera = zoomed_camera
|
||||||
self.zoomed_display = zoomed_display
|
self.zoomed_display = zoomed_display
|
||||||
|
|
||||||
def activate_zooming(self, animate=False, run_times=[2, 1]):
|
def activate_zooming(self, animate=False):
|
||||||
zoomed_camera = self.zoomed_camera
|
|
||||||
zoomed_display = self.zoomed_display
|
|
||||||
|
|
||||||
self.zoom_activated = True
|
self.zoom_activated = True
|
||||||
self.camera.add_image_mobject_from_camera(zoomed_display)
|
self.camera.add_image_mobject_from_camera(self.zoomed_display)
|
||||||
|
|
||||||
to_add = [zoomed_camera.frame, zoomed_display]
|
|
||||||
if animate:
|
if animate:
|
||||||
zoomed_display.save_state(use_deepcopy=True)
|
self.play(self.get_zoom_in_animation())
|
||||||
zoomed_display.replace(zoomed_camera.frame)
|
self.play(self.get_zoomed_display_pop_out_animation())
|
||||||
|
self.add_foreground_mobjects(
|
||||||
|
self.zoomed_camera.frame,
|
||||||
|
self.zoomed_display,
|
||||||
|
)
|
||||||
|
|
||||||
full_frame_height, full_frame_width = self.camera.frame_shape
|
def get_zoom_in_animation(self, run_time=2, **kwargs):
|
||||||
zoomed_camera.frame.save_state()
|
frame = self.zoomed_camera.frame
|
||||||
zoomed_camera.frame.stretch_to_fit_width(full_frame_width)
|
full_frame_height = self.camera.get_frame_height()
|
||||||
zoomed_camera.frame.stretch_to_fit_height(full_frame_height)
|
full_frame_width = self.camera.get_frame_width()
|
||||||
zoomed_camera.frame.center()
|
frame.save_state()
|
||||||
zoomed_camera.frame.set_stroke(width=0)
|
frame.stretch_to_fit_width(full_frame_width)
|
||||||
|
frame.stretch_to_fit_height(full_frame_height)
|
||||||
|
frame.center()
|
||||||
|
frame.set_stroke(width=0)
|
||||||
|
return ApplyMethod(frame.restore, run_time=run_time, **kwargs)
|
||||||
|
|
||||||
for mover, run_time in zip(to_add, run_times):
|
def get_zoomed_display_pop_out_animation(self, **kwargs):
|
||||||
self.add_foreground_mobject(mover)
|
display = self.zoomed_display
|
||||||
self.play(mover.restore, run_time=run_time)
|
display.save_state(use_deepcopy=True)
|
||||||
else:
|
display.replace(self.zoomed_camera.frame, stretch=True)
|
||||||
self.add_foreground_mobjects(*to_add)
|
return ApplyMethod(display.restore)
|
||||||
|
|
||||||
def get_zoom_factor(self):
|
def get_zoom_factor(self):
|
||||||
return fdiv(
|
return fdiv(
|
||||||
self.zoomed_camera.frame.get_width(),
|
self.zoomed_camera.frame.get_height(),
|
||||||
self.zoomed_display.get_width()
|
self.zoomed_display.get_height()
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user