From 0667e134278f04c95c0c81f3f675cd18097d4ad7 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 27 Feb 2016 13:33:46 -0800 Subject: [PATCH] record shape in place of height and width separately --- camera.py | 82 ++++++++++++++++++++++++++--------------------- constants.py | 9 ++---- helpers.py | 3 ++ scene/scene.py | 24 ++++++-------- scene/tk_scene.py | 5 ++- 5 files changed, 66 insertions(+), 57 deletions(-) diff --git a/camera.py b/camera.py index e33c7c60..fa9eec15 100644 --- a/camera.py +++ b/camera.py @@ -11,39 +11,45 @@ from helpers import * class Camera(object): DEFAULT_CONFIG = { - #background of a different shape will overwrite these - "pixel_width" : DEFAULT_WIDTH, - "pixel_height" : DEFAULT_HEIGHT, + #background of a different shape will overwrite this + "pixel_shape" : (DEFAULT_HEIGHT, DEFAULT_WIDTH), + #this will be resized to match pixel_shape + "space_shape" : (SPACE_HEIGHT, SPACE_WIDTH), + "space_center" : ORIGIN, "background_color" : BLACK, - # - "space_height" : SPACE_HEIGHT, - "space_center" : ORIGIN, } def __init__(self, background = None, **kwargs): digest_config(self, kwargs, locals()) self.init_background() + self.resize_space_shape() self.reset() - width_to_height = float(self.pixel_width) / self.pixel_height - self.space_width = self.space_height * width_to_height + def resize_space_shape(self, fixed_dimension = 0): + """ + 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): if self.background: - shape = self.background.shape[:2] - self.pixel_height, self.pixel_width = shape + self.pixel_shape = self.background.shape[:2] else: - background_color = Color(self.background_color) - background_rgb = (255*np.array( - background_color.get_rgb() - )).astype('uint8') - ones = np.ones( - (self.pixel_height, self.pixel_width, 1), + background_rgb = color_to_int_rgb(self.background_color) + self.background = np.zeros( + list(self.pixel_shape)+[3], dtype = 'uint8' ) - self.background = np.dot( - ones, background_rgb.reshape((1, 3)) - ) + self.background[:,:] = background_rgb def get_image(self): return np.array(self.pixel_array) @@ -77,8 +83,8 @@ class Camera(object): raise Exception("Invalid display mode") def display_region(self, region): - (h, w) = self.pixel_height, self.pixel_width - scalar = 2*self.space_height / h + (h, w) = self.pixel_shape + scalar = 2*self.space_shape[0] / h xs = scalar*np.arange(-w/2, w/2) ys = -scalar*np.arange(-h/2, h/2) 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] 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)) indices = np.dot(pixel_coords, flattener)[:,0] indices = indices.astype('int') - - pw, ph = self.pixel_width, self.pixel_height + # new_array = np.zeros((pw*ph, 3), dtype = 'uint8') # new_array[indices, :] = rgbs new_pa = self.pixel_array.reshape((ph*pw, 3)) new_pa[indices] = rgbs self.pixel_array = new_pa.reshape((ph, pw, 3)) - def align_points_to_camera(self, points): ## This is where projection should live return points - self.space_center def points_to_pixel_coords(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 + ph, pw = self.pixel_shape + sh, sw = self.space_shape + width_mult = pw/sw/2 + width_add = pw/2 + height_mult = ph/sh/2 + height_add = ph/2 #Flip on y-axis as you go height_mult *= -1 @@ -139,16 +147,14 @@ class Camera(object): def on_screen_pixels(self, pixel_coords): return reduce(op.and_, [ pixel_coords[:,0] >= 0, - pixel_coords[:,0] < self.pixel_width, + pixel_coords[:,0] < self.pixel_shape[1], pixel_coords[:,1] >= 0, - pixel_coords[:,1] < self.pixel_height, + pixel_coords[:,1] < self.pixel_shape[0], ]) def adjusted_thickness(self, thickness): - big_width = PRODUCTION_QUALITY_DISPLAY_CONFIG["width"] - big_height = PRODUCTION_QUALITY_DISPLAY_CONFIG["height"] - factor = (big_width + big_height) / \ - (self.pixel_width + self.pixel_height) + big_shape = PRODUCTION_QUALITY_DISPLAY_CONFIG["shape"] + factor = sum(big_shape)/sum(self.pixel_shape) return 1 + (thickness-1)/factor def get_thickening_nudges(self, thickness): @@ -171,13 +177,17 @@ class MovingCamera(Camera): Stays in line with the height, width and position of a given mobject """ + DEFAULT_CONFIG = { + "aligned_dimension" : "height" #or "width" + } def __init__(self, mobject, **kwargs): digest_locals(self) Camera.__init__(self, **kwargs) def capture_mobjects(self, *args, **kwargs): - self.space_height = self.mobject.get_height() self.space_center = self.mobject.get_center() + #TODO + diff --git a/constants.py b/constants.py index 39a57484..06ade3e5 100644 --- a/constants.py +++ b/constants.py @@ -7,20 +7,17 @@ DEFAULT_WIDTH = 2560 DEFAULT_FRAME_DURATION = 0.04 PRODUCTION_QUALITY_DISPLAY_CONFIG = { - "height" : DEFAULT_HEIGHT, - "width" : DEFAULT_WIDTH , + "shape" : (DEFAULT_HEIGHT, DEFAULT_WIDTH), "frame_duration" : DEFAULT_FRAME_DURATION, } MEDIUM_QUALITY_DISPLAY_CONFIG = { - "height" : 720, - "width" : 1280, + "shape" : (720, 1280), "frame_duration" : 0.04, } LOW_QUALITY_DISPLAY_CONFIG = { - "height" : 600,#480, - "width" : 1000,#840, + "shape" : (480, 640), "frame_duration" : 0.04, } diff --git a/helpers.py b/helpers.py index bad521f4..c66d82ec 100644 --- a/helpers.py +++ b/helpers.py @@ -12,6 +12,9 @@ import re 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): angle = 2*np.pi/n return [ diff --git a/scene/scene.py b/scene/scene.py index ccd74c10..71ecfd3c 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -18,18 +18,15 @@ from mobject import Mobject class Scene(object): DEFAULT_CONFIG = { - "height" : DEFAULT_HEIGHT, - "width" : DEFAULT_WIDTH , - "frame_duration" : DEFAULT_FRAME_DURATION, - "construct_args" : [], - "background" : None, - "start_dither_time" : DEFAULT_DITHER_TIME, + "shape" : (DEFAULT_HEIGHT, DEFAULT_WIDTH), + "frame_duration" : DEFAULT_FRAME_DURATION, + "construct_args" : [], + "background" : None, } def __init__(self, **kwargs): digest_config(self, kwargs) self.camera = Camera( - pixel_width = self.width, - pixel_height = self.height, + pixel_shape = self.shape, background = self.background ) self.frames = [] @@ -154,11 +151,10 @@ class Scene(object): for t in self.get_time_progression(animations): for animation in animations: animation.update(t / animation.run_time) - self.camera.capture_mobjects([ - anim.mobject - for anim in animations - ]) - self.frames.append(self.camera.get_image()) + self.camera.capture_mobjects(moving_mobjects) + frame = self.camera.get_image() + + self.frames.append(frame) self.camera.set_image(static_image) for animation in animations: animation.clean_up() @@ -247,7 +243,7 @@ class Scene(object): print "Writing to %s"%file_path fps = int(1/self.frame_duration) - dim = (self.width, self.height) + dim = tuple(reversed(self.shape)) command = [ FFMPEG_BIN, diff --git a/scene/tk_scene.py b/scene/tk_scene.py index a3eab8f4..2d9c2359 100644 --- a/scene/tk_scene.py +++ b/scene/tk_scene.py @@ -10,7 +10,10 @@ class TkSceneRoot(Tkinter.Tk): raise Exception(str(scene) + " has no frames!") 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.pack() self.canvas = Tkinter.Canvas(self.frame, **kwargs)