mirror of
https://github.com/3b1b/manim.git
synced 2025-07-30 13:34:19 +08:00
record shape in place of height and width separately
This commit is contained in:
82
camera.py
82
camera.py
@ -11,39 +11,45 @@ from helpers import *
|
|||||||
|
|
||||||
class Camera(object):
|
class Camera(object):
|
||||||
DEFAULT_CONFIG = {
|
DEFAULT_CONFIG = {
|
||||||
#background of a different shape will overwrite these
|
#background of a different shape will overwrite this
|
||||||
"pixel_width" : DEFAULT_WIDTH,
|
"pixel_shape" : (DEFAULT_HEIGHT, DEFAULT_WIDTH),
|
||||||
"pixel_height" : DEFAULT_HEIGHT,
|
#this will be resized to match pixel_shape
|
||||||
|
"space_shape" : (SPACE_HEIGHT, SPACE_WIDTH),
|
||||||
|
"space_center" : ORIGIN,
|
||||||
"background_color" : BLACK,
|
"background_color" : BLACK,
|
||||||
#
|
|
||||||
"space_height" : SPACE_HEIGHT,
|
|
||||||
"space_center" : ORIGIN,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, background = None, **kwargs):
|
def __init__(self, background = None, **kwargs):
|
||||||
digest_config(self, kwargs, locals())
|
digest_config(self, kwargs, locals())
|
||||||
self.init_background()
|
self.init_background()
|
||||||
|
self.resize_space_shape()
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
width_to_height = float(self.pixel_width) / self.pixel_height
|
def resize_space_shape(self, fixed_dimension = 0):
|
||||||
self.space_width = self.space_height * width_to_height
|
"""
|
||||||
|
Changes space_shape to match the aspect ratio
|
||||||
|
of pixel_shape, where fixed_dimension determines
|
||||||
|
whether space_shape[0] (height) or space_shape[1] (width)
|
||||||
|
remains fixed while the other changes accordingly.
|
||||||
|
"""
|
||||||
|
aspect_ratio = float(self.pixel_shape[1])/self.pixel_shape[0]
|
||||||
|
space_height, space_width = self.space_shape
|
||||||
|
if fixed_dimension == 0:
|
||||||
|
space_width = aspect_ratio*space_height
|
||||||
|
else:
|
||||||
|
space_height = space_width/aspect_ratio
|
||||||
|
self.space_shape = (space_height, space_width)
|
||||||
|
|
||||||
def init_background(self):
|
def init_background(self):
|
||||||
if self.background:
|
if self.background:
|
||||||
shape = self.background.shape[:2]
|
self.pixel_shape = self.background.shape[:2]
|
||||||
self.pixel_height, self.pixel_width = shape
|
|
||||||
else:
|
else:
|
||||||
background_color = Color(self.background_color)
|
background_rgb = color_to_int_rgb(self.background_color)
|
||||||
background_rgb = (255*np.array(
|
self.background = np.zeros(
|
||||||
background_color.get_rgb()
|
list(self.pixel_shape)+[3],
|
||||||
)).astype('uint8')
|
|
||||||
ones = np.ones(
|
|
||||||
(self.pixel_height, self.pixel_width, 1),
|
|
||||||
dtype = 'uint8'
|
dtype = 'uint8'
|
||||||
)
|
)
|
||||||
self.background = np.dot(
|
self.background[:,:] = background_rgb
|
||||||
ones, background_rgb.reshape((1, 3))
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_image(self):
|
def get_image(self):
|
||||||
return np.array(self.pixel_array)
|
return np.array(self.pixel_array)
|
||||||
@ -77,8 +83,8 @@ class Camera(object):
|
|||||||
raise Exception("Invalid display mode")
|
raise Exception("Invalid display mode")
|
||||||
|
|
||||||
def display_region(self, region):
|
def display_region(self, region):
|
||||||
(h, w) = self.pixel_height, self.pixel_width
|
(h, w) = self.pixel_shape
|
||||||
scalar = 2*self.space_height / h
|
scalar = 2*self.space_shape[0] / h
|
||||||
xs = scalar*np.arange(-w/2, w/2)
|
xs = scalar*np.arange(-w/2, w/2)
|
||||||
ys = -scalar*np.arange(-h/2, h/2)
|
ys = -scalar*np.arange(-h/2, h/2)
|
||||||
x_array = np.dot(np.ones((h, 1)), xs.reshape((1, w)))
|
x_array = np.dot(np.ones((h, 1)), xs.reshape((1, w)))
|
||||||
@ -106,29 +112,31 @@ class Camera(object):
|
|||||||
pixel_coords = pixel_coords[on_screen_indices]
|
pixel_coords = pixel_coords[on_screen_indices]
|
||||||
rgbs = rgbs[on_screen_indices]
|
rgbs = rgbs[on_screen_indices]
|
||||||
|
|
||||||
flattener = np.array([1, self.pixel_width], dtype = 'int')
|
ph, pw = self.pixel_shape
|
||||||
|
|
||||||
|
flattener = np.array([1, pw], dtype = 'int')
|
||||||
flattener = flattener.reshape((2, 1))
|
flattener = flattener.reshape((2, 1))
|
||||||
indices = np.dot(pixel_coords, flattener)[:,0]
|
indices = np.dot(pixel_coords, flattener)[:,0]
|
||||||
indices = indices.astype('int')
|
indices = indices.astype('int')
|
||||||
|
|
||||||
pw, ph = self.pixel_width, self.pixel_height
|
|
||||||
# new_array = np.zeros((pw*ph, 3), dtype = 'uint8')
|
# new_array = np.zeros((pw*ph, 3), dtype = 'uint8')
|
||||||
# new_array[indices, :] = rgbs
|
# new_array[indices, :] = rgbs
|
||||||
new_pa = self.pixel_array.reshape((ph*pw, 3))
|
new_pa = self.pixel_array.reshape((ph*pw, 3))
|
||||||
new_pa[indices] = rgbs
|
new_pa[indices] = rgbs
|
||||||
self.pixel_array = new_pa.reshape((ph, pw, 3))
|
self.pixel_array = new_pa.reshape((ph, pw, 3))
|
||||||
|
|
||||||
|
|
||||||
def align_points_to_camera(self, points):
|
def align_points_to_camera(self, points):
|
||||||
## This is where projection should live
|
## This is where projection should live
|
||||||
return points - self.space_center
|
return points - self.space_center
|
||||||
|
|
||||||
def points_to_pixel_coords(self, points):
|
def points_to_pixel_coords(self, points):
|
||||||
result = np.zeros((len(points), 2))
|
result = np.zeros((len(points), 2))
|
||||||
width_mult = self.pixel_width/self.space_width/2
|
ph, pw = self.pixel_shape
|
||||||
width_add = self.pixel_width/2
|
sh, sw = self.space_shape
|
||||||
height_mult = self.pixel_height/self.space_height/2
|
width_mult = pw/sw/2
|
||||||
height_add = self.pixel_height/2
|
width_add = pw/2
|
||||||
|
height_mult = ph/sh/2
|
||||||
|
height_add = ph/2
|
||||||
#Flip on y-axis as you go
|
#Flip on y-axis as you go
|
||||||
height_mult *= -1
|
height_mult *= -1
|
||||||
|
|
||||||
@ -139,16 +147,14 @@ class Camera(object):
|
|||||||
def on_screen_pixels(self, pixel_coords):
|
def on_screen_pixels(self, pixel_coords):
|
||||||
return reduce(op.and_, [
|
return reduce(op.and_, [
|
||||||
pixel_coords[:,0] >= 0,
|
pixel_coords[:,0] >= 0,
|
||||||
pixel_coords[:,0] < self.pixel_width,
|
pixel_coords[:,0] < self.pixel_shape[1],
|
||||||
pixel_coords[:,1] >= 0,
|
pixel_coords[:,1] >= 0,
|
||||||
pixel_coords[:,1] < self.pixel_height,
|
pixel_coords[:,1] < self.pixel_shape[0],
|
||||||
])
|
])
|
||||||
|
|
||||||
def adjusted_thickness(self, thickness):
|
def adjusted_thickness(self, thickness):
|
||||||
big_width = PRODUCTION_QUALITY_DISPLAY_CONFIG["width"]
|
big_shape = PRODUCTION_QUALITY_DISPLAY_CONFIG["shape"]
|
||||||
big_height = PRODUCTION_QUALITY_DISPLAY_CONFIG["height"]
|
factor = sum(big_shape)/sum(self.pixel_shape)
|
||||||
factor = (big_width + big_height) / \
|
|
||||||
(self.pixel_width + self.pixel_height)
|
|
||||||
return 1 + (thickness-1)/factor
|
return 1 + (thickness-1)/factor
|
||||||
|
|
||||||
def get_thickening_nudges(self, thickness):
|
def get_thickening_nudges(self, thickness):
|
||||||
@ -171,13 +177,17 @@ class MovingCamera(Camera):
|
|||||||
Stays in line with the height, width and position
|
Stays in line with the height, width and position
|
||||||
of a given mobject
|
of a given mobject
|
||||||
"""
|
"""
|
||||||
|
DEFAULT_CONFIG = {
|
||||||
|
"aligned_dimension" : "height" #or "width"
|
||||||
|
}
|
||||||
def __init__(self, mobject, **kwargs):
|
def __init__(self, mobject, **kwargs):
|
||||||
digest_locals(self)
|
digest_locals(self)
|
||||||
Camera.__init__(self, **kwargs)
|
Camera.__init__(self, **kwargs)
|
||||||
|
|
||||||
def capture_mobjects(self, *args, **kwargs):
|
def capture_mobjects(self, *args, **kwargs):
|
||||||
self.space_height = self.mobject.get_height()
|
|
||||||
self.space_center = self.mobject.get_center()
|
self.space_center = self.mobject.get_center()
|
||||||
|
#TODO
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,20 +7,17 @@ DEFAULT_WIDTH = 2560
|
|||||||
DEFAULT_FRAME_DURATION = 0.04
|
DEFAULT_FRAME_DURATION = 0.04
|
||||||
|
|
||||||
PRODUCTION_QUALITY_DISPLAY_CONFIG = {
|
PRODUCTION_QUALITY_DISPLAY_CONFIG = {
|
||||||
"height" : DEFAULT_HEIGHT,
|
"shape" : (DEFAULT_HEIGHT, DEFAULT_WIDTH),
|
||||||
"width" : DEFAULT_WIDTH ,
|
|
||||||
"frame_duration" : DEFAULT_FRAME_DURATION,
|
"frame_duration" : DEFAULT_FRAME_DURATION,
|
||||||
}
|
}
|
||||||
|
|
||||||
MEDIUM_QUALITY_DISPLAY_CONFIG = {
|
MEDIUM_QUALITY_DISPLAY_CONFIG = {
|
||||||
"height" : 720,
|
"shape" : (720, 1280),
|
||||||
"width" : 1280,
|
|
||||||
"frame_duration" : 0.04,
|
"frame_duration" : 0.04,
|
||||||
}
|
}
|
||||||
|
|
||||||
LOW_QUALITY_DISPLAY_CONFIG = {
|
LOW_QUALITY_DISPLAY_CONFIG = {
|
||||||
"height" : 600,#480,
|
"shape" : (480, 640),
|
||||||
"width" : 1000,#840,
|
|
||||||
"frame_duration" : 0.04,
|
"frame_duration" : 0.04,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,9 @@ import re
|
|||||||
from constants import *
|
from constants import *
|
||||||
|
|
||||||
|
|
||||||
|
def color_to_int_rgb(color):
|
||||||
|
return (255*np.array(Color(color).get_rgb())).astype('uint8')
|
||||||
|
|
||||||
def compass_directions(n = 4, start_vect = UP):
|
def compass_directions(n = 4, start_vect = UP):
|
||||||
angle = 2*np.pi/n
|
angle = 2*np.pi/n
|
||||||
return [
|
return [
|
||||||
|
@ -18,18 +18,15 @@ from mobject import Mobject
|
|||||||
|
|
||||||
class Scene(object):
|
class Scene(object):
|
||||||
DEFAULT_CONFIG = {
|
DEFAULT_CONFIG = {
|
||||||
"height" : DEFAULT_HEIGHT,
|
"shape" : (DEFAULT_HEIGHT, DEFAULT_WIDTH),
|
||||||
"width" : DEFAULT_WIDTH ,
|
"frame_duration" : DEFAULT_FRAME_DURATION,
|
||||||
"frame_duration" : DEFAULT_FRAME_DURATION,
|
"construct_args" : [],
|
||||||
"construct_args" : [],
|
"background" : None,
|
||||||
"background" : None,
|
|
||||||
"start_dither_time" : DEFAULT_DITHER_TIME,
|
|
||||||
}
|
}
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
digest_config(self, kwargs)
|
digest_config(self, kwargs)
|
||||||
self.camera = Camera(
|
self.camera = Camera(
|
||||||
pixel_width = self.width,
|
pixel_shape = self.shape,
|
||||||
pixel_height = self.height,
|
|
||||||
background = self.background
|
background = self.background
|
||||||
)
|
)
|
||||||
self.frames = []
|
self.frames = []
|
||||||
@ -154,11 +151,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([
|
self.camera.capture_mobjects(moving_mobjects)
|
||||||
anim.mobject
|
frame = self.camera.get_image()
|
||||||
for anim in animations
|
|
||||||
])
|
self.frames.append(frame)
|
||||||
self.frames.append(self.camera.get_image())
|
|
||||||
self.camera.set_image(static_image)
|
self.camera.set_image(static_image)
|
||||||
for animation in animations:
|
for animation in animations:
|
||||||
animation.clean_up()
|
animation.clean_up()
|
||||||
@ -247,7 +243,7 @@ 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 = (self.width, self.height)
|
dim = tuple(reversed(self.shape))
|
||||||
|
|
||||||
command = [
|
command = [
|
||||||
FFMPEG_BIN,
|
FFMPEG_BIN,
|
||||||
|
@ -10,7 +10,10 @@ class TkSceneRoot(Tkinter.Tk):
|
|||||||
raise Exception(str(scene) + " has no frames!")
|
raise Exception(str(scene) + " has no frames!")
|
||||||
Tkinter.Tk.__init__(self)
|
Tkinter.Tk.__init__(self)
|
||||||
|
|
||||||
kwargs = {"height" : scene.height, "width" : scene.width}
|
kwargs = {
|
||||||
|
"height" : scene.shape[0],
|
||||||
|
"width" : scene.shape[1],
|
||||||
|
}
|
||||||
self.frame = Tkinter.Frame(self, **kwargs)
|
self.frame = Tkinter.Frame(self, **kwargs)
|
||||||
self.frame.pack()
|
self.frame.pack()
|
||||||
self.canvas = Tkinter.Canvas(self.frame, **kwargs)
|
self.canvas = Tkinter.Canvas(self.frame, **kwargs)
|
||||||
|
Reference in New Issue
Block a user