Refactoring how Camera frame height and width are handled

This commit is contained in:
Grant Sanderson
2018-05-11 17:41:37 -07:00
parent f0b8ae7647
commit f04b2e270c
8 changed files with 112 additions and 55 deletions

View File

@ -30,9 +30,10 @@ class Camera(object):
CONFIG = {
"background_image": None,
"pixel_shape": (DEFAULT_PIXEL_HEIGHT, DEFAULT_PIXEL_WIDTH),
# Note: frame_shape will be resized to match pixel_shape
"frame_shape": (FRAME_HEIGHT, FRAME_WIDTH),
"space_center": ORIGIN,
# Note: frame height and width will be resized to match pixel_shape
"frame_height": FRAME_HEIGHT,
"frame_width": FRAME_WIDTH,
"frame_center": ORIGIN,
"background_color": BLACK,
"background_opacity": 0,
# Points in vectorized mobjects with norm greater
@ -67,21 +68,41 @@ class Camera(object):
self.resize_frame_shape()
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):
"""
Changes frame_shape to match the aspect ratio
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.
"""
frame_height, frame_width = self.frame_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)
if fixed_dimension == 0:
frame_height = frame_width / aspect_ratio
else:
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):
if self.background_image is not None:
@ -455,15 +476,16 @@ class Camera(object):
return 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))
ph, pw = self.pixel_shape
sh, sw = self.frame_shape
width_mult = pw / sw
width_add = pw / 2
height_mult = ph / sh
height_add = ph / 2
pixel_height, pixel_width = self.pixel_shape
frame_height = self.get_frame_height()
frame_width = self.get_frame_width()
width_mult = pixel_width / frame_width
width_add = pixel_width / 2
height_mult = pixel_height / frame_height
height_add = pixel_height / 2
# Flip on y-axis as you go
height_mult *= -1
@ -499,7 +521,10 @@ class Camera(object):
def get_coords_of_all_pixels(self):
# 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]
# These are addressed in the same y, x order as in pixel_array, but the values in them

View File

@ -43,6 +43,7 @@ class MappingCamera(Camera):
# TODO: Add optional separator borders between cameras (or perhaps peel this off into a
# CameraPlusOverlay class)
# TODO, the classes below should likely be deleted
class OldMultiCamera(Camera):
def __init__(self, *cameras_with_start_positions, **kwargs):

View File

@ -34,21 +34,40 @@ class MovingCamera(Camera):
self.frame = frame
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):
self.reset_space_center()
self.realign_frame_shape()
# self.reset_frame_center()
# self.realign_frame_shape()
Camera.capture_mobjects(self, mobjects, **kwargs)
def reset_space_center(self):
self.space_center = self.frame.get_center()
# def reset_frame_center(self):
# self.frame_center = self.frame.get_center()
def realign_frame_shape(self):
height, width = self.frame_shape
if self.fixed_dimension == 0:
self.frame_shape = (height, self.frame.get_width())
else:
self.frame_shape = (self.frame.get_height(), width)
self.resize_frame_shape(fixed_dimension=self.fixed_dimension)
# def realign_frame_shape(self):
# height, width = self.frame_shape
# if self.fixed_dimension == 0:
# self.frame_shape = (height, self.frame.get_width())
# else:
# self.frame_shape = (self.frame.get_height(), width)
# self.resize_frame_shape(fixed_dimension=self.fixed_dimension)
def get_mobjects_indicating_movement(self):
"""

View File

@ -25,15 +25,14 @@ class MultiCamera(MovingCamera):
def update_sub_cameras(self):
""" Reshape sub_camera pixel_arrays """
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]
imfc.camera.frame_shape = (
imfc.camera.frame.get_height(),
imfc.camera.frame.get_width(),
)
imfc.camera.reset_pixel_shape((
int(pixel_height * imfc.get_height() / frame_height),
int(pixel_width * imfc.get_width() / frame_width),
int(pixel_height * imfc.get_height() / self.get_frame_height()),
int(pixel_width * imfc.get_width() / self.get_frame_width()),
))
def reset(self):

View File

@ -49,7 +49,7 @@ class ThreeDCamera(CameraWithPerspective):
self.rotation_mobject = VectorizedPoint()
# moving_center lives in the x-y-z space
# 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)
def modified_rgb(self, vmobject, rgb):
@ -163,7 +163,7 @@ class ThreeDCamera(CameraWithPerspective):
center_of_rotation = self.get_center_of_rotation(
center_x, center_y, center_z)
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):
return (self.default_distance / self.get_distance()) * np.dot(
@ -174,6 +174,6 @@ class ThreeDCamera(CameraWithPerspective):
def points_to_pixel_coords(self, points):
matrix = self.get_view_transformation_matrix()
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)

View File

@ -199,6 +199,15 @@ class Mobject(Container):
)
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):
# Default to applying matrix about the origin, not mobjects center
if len(kwargs) == 0:

View File

@ -2056,7 +2056,7 @@ class IPTScene1(PiCreatureScene):
# use the following for the zoomed inset
if show_detail:
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_THICKNESS = 0.02

View File

@ -7,11 +7,12 @@ from mobject.types.image_mobject import ImageMobjectFromCamera
from utils.simple_functions import fdiv
from constants import *
from animation.transform import ApplyMethod
# Note, any scenes from old videos using ZoomedScene will almost certainly
# break, as it was restructured.
class ZoomedScene(MovingCameraScene):
CONFIG = {
"camera_class": MultiCamera,
@ -56,33 +57,36 @@ class ZoomedScene(MovingCameraScene):
self.zoomed_camera = zoomed_camera
self.zoomed_display = zoomed_display
def activate_zooming(self, animate=False, run_times=[2, 1]):
zoomed_camera = self.zoomed_camera
zoomed_display = self.zoomed_display
def activate_zooming(self, animate=False):
self.zoom_activated = True
self.camera.add_image_mobject_from_camera(zoomed_display)
to_add = [zoomed_camera.frame, zoomed_display]
self.camera.add_image_mobject_from_camera(self.zoomed_display)
if animate:
zoomed_display.save_state(use_deepcopy=True)
zoomed_display.replace(zoomed_camera.frame)
self.play(self.get_zoom_in_animation())
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
zoomed_camera.frame.save_state()
zoomed_camera.frame.stretch_to_fit_width(full_frame_width)
zoomed_camera.frame.stretch_to_fit_height(full_frame_height)
zoomed_camera.frame.center()
zoomed_camera.frame.set_stroke(width=0)
def get_zoom_in_animation(self, run_time=2, **kwargs):
frame = self.zoomed_camera.frame
full_frame_height = self.camera.get_frame_height()
full_frame_width = self.camera.get_frame_width()
frame.save_state()
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):
self.add_foreground_mobject(mover)
self.play(mover.restore, run_time=run_time)
else:
self.add_foreground_mobjects(*to_add)
def get_zoomed_display_pop_out_animation(self, **kwargs):
display = self.zoomed_display
display.save_state(use_deepcopy=True)
display.replace(self.zoomed_camera.frame, stretch=True)
return ApplyMethod(display.restore)
def get_zoom_factor(self):
return fdiv(
self.zoomed_camera.frame.get_width(),
self.zoomed_display.get_width()
self.zoomed_camera.frame.get_height(),
self.zoomed_display.get_height()
)