mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
Start tracking pixel_height and pixel_width instead of pixel_shape, since all uses of it involved unpacking anyway, and the ordering makes it harder to read and edit.
This commit is contained in:
parent
f04b2e270c
commit
ecd48d885e
8 changed files with 64 additions and 36 deletions
|
@ -29,8 +29,10 @@ from utils.space_ops import angle_of_vector
|
|||
class Camera(object):
|
||||
CONFIG = {
|
||||
"background_image": None,
|
||||
"pixel_shape": (DEFAULT_PIXEL_HEIGHT, DEFAULT_PIXEL_WIDTH),
|
||||
# Note: frame height and width will be resized to match pixel_shape
|
||||
"pixel_height": DEFAULT_PIXEL_HEIGHT,
|
||||
"pixel_width": DEFAULT_PIXEL_WIDTH,
|
||||
# Note: frame height and width will be resized to match
|
||||
# the pixel aspect ratio
|
||||
"frame_height": FRAME_HEIGHT,
|
||||
"frame_width": FRAME_WIDTH,
|
||||
"frame_center": ORIGIN,
|
||||
|
@ -62,12 +64,19 @@ class Camera(object):
|
|||
self.canvas = None
|
||||
return copy.copy(self)
|
||||
|
||||
def reset_pixel_shape(self, new_shape):
|
||||
self.pixel_shape = tuple(new_shape)
|
||||
def reset_pixel_shape(self, new_height, new_width):
|
||||
self.pixel_width = new_width
|
||||
self.pixel_height = new_height
|
||||
self.init_background()
|
||||
self.resize_frame_shape()
|
||||
self.reset()
|
||||
|
||||
def get_pixel_height(self):
|
||||
return self.pixel_height
|
||||
|
||||
def get_pixel_width(self):
|
||||
return self.pixel_width
|
||||
|
||||
def get_frame_height(self):
|
||||
return self.frame_height
|
||||
|
||||
|
@ -89,11 +98,12 @@ class Camera(object):
|
|||
def resize_frame_shape(self, fixed_dimension=0):
|
||||
"""
|
||||
Changes frame_shape to match the aspect ratio
|
||||
of pixel_shape, where fixed_dimension determines
|
||||
of the pixels, where fixed_dimension determines
|
||||
whether frame_height or frame_width
|
||||
remains fixed while the other changes accordingly.
|
||||
"""
|
||||
pixel_height, pixel_width = self.pixel_shape
|
||||
pixel_height = self.get_pixel_height()
|
||||
pixel_width = self.get_pixel_width()
|
||||
frame_height = self.get_frame_height()
|
||||
frame_width = self.get_frame_width()
|
||||
aspect_ratio = fdiv(pixel_width, pixel_height)
|
||||
|
@ -105,10 +115,11 @@ class Camera(object):
|
|||
self.set_frame_width(frame_width)
|
||||
|
||||
def init_background(self):
|
||||
height = self.get_pixel_height()
|
||||
width = self.get_pixel_width()
|
||||
if self.background_image is not None:
|
||||
path = get_full_raster_image_path(self.background_image)
|
||||
image = Image.open(path).convert(self.image_mode)
|
||||
height, width = self.pixel_shape
|
||||
# TODO, how to gracefully handle backgrounds
|
||||
# with different sizes?
|
||||
self.background = np.array(image)[:height, :width]
|
||||
|
@ -118,7 +129,7 @@ class Camera(object):
|
|||
self.background_color, self.background_opacity
|
||||
)
|
||||
self.background = np.zeros(
|
||||
list(self.pixel_shape) + [self.n_rgb_coords],
|
||||
(height, width, self.n_rgb_coords),
|
||||
dtype=self.pixel_array_dtype
|
||||
)
|
||||
self.background[:, :] = background_rgba
|
||||
|
@ -388,7 +399,8 @@ class Camera(object):
|
|||
pixel_coords = pixel_coords[on_screen_indices]
|
||||
rgbas = rgbas[on_screen_indices]
|
||||
|
||||
ph, pw = self.pixel_shape
|
||||
ph = self.get_pixel_height()
|
||||
pw = self.get_pixel_width()
|
||||
|
||||
flattener = np.array([1, pw], dtype='int')
|
||||
flattener = flattener.reshape((2, 1))
|
||||
|
@ -433,9 +445,8 @@ class Camera(object):
|
|||
# TODO, there is no accounting for a shear...
|
||||
|
||||
# Paste into an image as large as the camear's pixel array
|
||||
h, w = self.pixel_shape
|
||||
full_image = Image.fromarray(
|
||||
np.zeros((h, w)),
|
||||
np.zeros((self.get_pixel_height(), self.get_pixel_width())),
|
||||
mode="RGBA"
|
||||
)
|
||||
new_ul_coords = center_coords - np.array(sub_image.size) / 2
|
||||
|
@ -479,7 +490,8 @@ class Camera(object):
|
|||
shifted_points = points - self.get_frame_center()
|
||||
|
||||
result = np.zeros((len(points), 2))
|
||||
pixel_height, pixel_width = self.pixel_shape
|
||||
pixel_height = self.get_pixel_height()
|
||||
pixel_width = self.get_pixel_width()
|
||||
frame_height = self.get_frame_height()
|
||||
frame_width = self.get_frame_width()
|
||||
width_mult = pixel_width / frame_width
|
||||
|
@ -496,14 +508,22 @@ class Camera(object):
|
|||
def on_screen_pixels(self, pixel_coords):
|
||||
return reduce(op.and_, [
|
||||
pixel_coords[:, 0] >= 0,
|
||||
pixel_coords[:, 0] < self.pixel_shape[1],
|
||||
pixel_coords[:, 0] < self.get_pixel_width(),
|
||||
pixel_coords[:, 1] >= 0,
|
||||
pixel_coords[:, 1] < self.pixel_shape[0],
|
||||
pixel_coords[:, 1] < self.get_pixel_height(),
|
||||
])
|
||||
|
||||
def adjusted_thickness(self, thickness):
|
||||
big_shape = PRODUCTION_QUALITY_CAMERA_CONFIG["pixel_shape"]
|
||||
factor = sum(big_shape) / sum(self.pixel_shape)
|
||||
# TODO: This seems...unsystematic
|
||||
big_sum = op.add(
|
||||
PRODUCTION_QUALITY_CAMERA_CONFIG["pixel_height"],
|
||||
PRODUCTION_QUALITY_CAMERA_CONFIG["pixel_width"],
|
||||
)
|
||||
this_sum = op.add(
|
||||
self.get_pixel_height(),
|
||||
self.get_pixel_width(),
|
||||
)
|
||||
factor = fdiv(big_sum, this_sum)
|
||||
return 1 + (thickness - 1) / factor
|
||||
|
||||
def get_thickening_nudges(self, thickness):
|
||||
|
@ -525,12 +545,16 @@ class Camera(object):
|
|||
self.get_frame_width(),
|
||||
self.get_frame_height()
|
||||
])
|
||||
full_pixel_dims = np.array(self.pixel_shape)[::-1]
|
||||
full_pixel_dims = np.array([
|
||||
self.get_pixel_width(),
|
||||
self.get_pixel_height()
|
||||
])
|
||||
|
||||
# These are addressed in the same y, x order as in pixel_array, but the values in them
|
||||
# are listed in x, y order
|
||||
uncentered_pixel_coords = np.indices(self.pixel_shape)[
|
||||
::-1].transpose(1, 2, 0)
|
||||
uncentered_pixel_coords = np.indices(
|
||||
[self.get_pixel_height(), self.get_pixel_width()]
|
||||
)[::-1].transpose(1, 2, 0)
|
||||
uncentered_space_coords = fdiv(
|
||||
uncentered_pixel_coords * full_space_dims,
|
||||
full_pixel_dims)
|
||||
|
@ -540,7 +564,8 @@ class Camera(object):
|
|||
# overflow is unlikely to be a problem)
|
||||
|
||||
centered_space_coords = (
|
||||
uncentered_space_coords - fdiv(full_space_dims, 2))
|
||||
uncentered_space_coords - fdiv(full_space_dims, 2)
|
||||
)
|
||||
|
||||
# Have to also flip the y coordinates to account for pixel array being listed in
|
||||
# top-to-bottom order, opposite of screen coordinate convention
|
||||
|
|
|
@ -53,8 +53,8 @@ class OldMultiCamera(Camera):
|
|||
"camera": camera_with_start_positions[0],
|
||||
"start_x": camera_with_start_positions[1][1],
|
||||
"start_y": camera_with_start_positions[1][0],
|
||||
"end_x": camera_with_start_positions[1][1] + camera_with_start_positions[0].pixel_shape[1],
|
||||
"end_y": camera_with_start_positions[1][0] + camera_with_start_positions[0].pixel_shape[0],
|
||||
"end_x": camera_with_start_positions[1][1] + camera_with_start_positions[0].get_pixel_width(),
|
||||
"end_y": camera_with_start_positions[1][0] + camera_with_start_positions[0].get_pixel_height(),
|
||||
})
|
||||
for camera_with_start_positions in cameras_with_start_positions
|
||||
]
|
||||
|
@ -103,13 +103,10 @@ class SplitScreenCamera(OldMultiCamera):
|
|||
self.left_camera = left_camera
|
||||
self.right_camera = right_camera
|
||||
|
||||
half_width = self.pixel_shape[1] / 2
|
||||
half_width = self.get_pixel_width() / 2
|
||||
for camera in [self.left_camera, self.right_camera]:
|
||||
# TODO: Round up on one if width is odd
|
||||
camera.pixel_shape = (self.pixel_shape[0], half_width)
|
||||
camera.init_background()
|
||||
camera.resize_frame_shape()
|
||||
camera.reset()
|
||||
camera.reset_pixel_shape(camera.get_pixel_height(), half_width)
|
||||
|
||||
OldMultiCamera.__init__(
|
||||
self,
|
||||
|
|
|
@ -30,10 +30,10 @@ class MultiCamera(MovingCamera):
|
|||
imfc.camera.frame.get_height(),
|
||||
imfc.camera.frame.get_width(),
|
||||
)
|
||||
imfc.camera.reset_pixel_shape((
|
||||
imfc.camera.reset_pixel_shape(
|
||||
int(pixel_height * imfc.get_height() / self.get_frame_height()),
|
||||
int(pixel_width * imfc.get_width() / self.get_frame_width()),
|
||||
))
|
||||
)
|
||||
|
||||
def reset(self):
|
||||
for imfc in self.image_mobjects_from_cameras:
|
||||
|
|
|
@ -12,6 +12,8 @@ from utils.bezier import interpolate
|
|||
from utils.space_ops import rotation_about_z
|
||||
from utils.space_ops import rotation_matrix
|
||||
|
||||
# TODO: Make sure this plays well with latest camera updates
|
||||
|
||||
|
||||
class CameraWithPerspective(Camera):
|
||||
CONFIG = {
|
||||
|
|
11
constants.py
11
constants.py
|
@ -17,17 +17,20 @@ LOW_QUALITY_FRAME_DURATION = 1. / 15
|
|||
MEDIUM_QUALITY_FRAME_DURATION = 1. / 30
|
||||
PRODUCTION_QUALITY_FRAME_DURATION = 1. / 60
|
||||
|
||||
# There might be other configuration than pixel_shape later...
|
||||
# There might be other configuration than pixel shape later...
|
||||
PRODUCTION_QUALITY_CAMERA_CONFIG = {
|
||||
"pixel_shape": (DEFAULT_PIXEL_HEIGHT, DEFAULT_PIXEL_WIDTH),
|
||||
"pixel_height": DEFAULT_PIXEL_HEIGHT,
|
||||
"pixel_width": DEFAULT_PIXEL_WIDTH,
|
||||
}
|
||||
|
||||
MEDIUM_QUALITY_CAMERA_CONFIG = {
|
||||
"pixel_shape": (720, 1280),
|
||||
"pixel_height": 720,
|
||||
"pixel_width": 1280,
|
||||
}
|
||||
|
||||
LOW_QUALITY_CAMERA_CONFIG = {
|
||||
"pixel_shape": (480, 854),
|
||||
"pixel_height": 480,
|
||||
"pixel_width": 854,
|
||||
}
|
||||
|
||||
DEFAULT_POINT_DENSITY_2D = 25
|
||||
|
|
|
@ -668,7 +668,7 @@ class ColorMappedByFuncScene(Scene):
|
|||
# We hash just based on output image
|
||||
# Thus, multiple scenes with same output image can re-use it
|
||||
# without recomputation
|
||||
full_hash = hash((func_hash, self.camera.pixel_shape))
|
||||
full_hash = hash((func_hash, self.camera.get_pixel_width()))
|
||||
self.background_image_file = self.short_path_to_long_path(
|
||||
"color_mapped_bg_hash_" + str(full_hash) + ".png"
|
||||
)
|
||||
|
|
|
@ -572,7 +572,8 @@ class Scene(Container):
|
|||
self.args_to_rename_file = (temp_file_path, file_path)
|
||||
|
||||
fps = int(1 / self.frame_duration)
|
||||
height, width = self.camera.pixel_shape
|
||||
height = self.camera.get_pixel_height()
|
||||
width = self.camera.get_pixel_width()
|
||||
|
||||
command = [
|
||||
FFMPEG_BIN,
|
||||
|
|
|
@ -34,7 +34,7 @@ def get_scene_output_directory(scene_class):
|
|||
def get_movie_output_directory(scene_class, camera_config, frame_duration):
|
||||
directory = get_scene_output_directory(scene_class)
|
||||
sub_dir = "%dp%d" % (
|
||||
camera_config["pixel_shape"][0],
|
||||
camera_config["pixel_height"],
|
||||
int(1.0 / frame_duration)
|
||||
)
|
||||
return guarantee_existance(os.path.join(directory, sub_dir))
|
||||
|
|
Loading…
Add table
Reference in a new issue