From 850b6f9e888e3799a64307f6276495130600bf6d Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 20 Jan 2018 10:56:52 -0800 Subject: [PATCH 1/4] Initial edits to make main movement methods take in about_point and about_edge kwargs --- mobject/mobject.py | 79 ++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/mobject/mobject.py b/mobject/mobject.py index 8f7e0ae1..5ad8afcf 100644 --- a/mobject/mobject.py +++ b/mobject/mobject.py @@ -138,7 +138,7 @@ class Mobject(object): mob.points += total_vector return self - def scale(self, scale_factor, about_point = None, about_edge = ORIGIN): + def scale(self, scale_factor, **kwargs): """ Default behavior is to scale about the center of the mobject. The argument about_edge can be a vector, indicating which side of @@ -148,46 +148,41 @@ class Mobject(object): Otherwise, if about_point is given a value, scaling is done with respect to that point. """ - if about_point is None: - about_point = self.get_critical_point(about_edge) - self.shift(-about_point) - for mob in self.family_members_with_points(): - mob.points *= scale_factor - self.shift(about_point) + self.apply_points_function_about_point( + lambda points : scale_factor*points, **kwargs + ) return self def rotate_about_origin(self, angle, axis = OUT, axes = []): - if len(axes) == 0: - axes = [axis] - rot_matrix = np.identity(self.dim) - for axis in axes: - rot_matrix = np.dot(rot_matrix, rotation_matrix(angle, axis)) - t_rot_matrix = np.transpose(rot_matrix) - for mob in self.family_members_with_points(): - mob.points = np.dot(mob.points, t_rot_matrix) + return self.rotate(angle, axis, about_point = ORIGIN) + + def rotate(self, angle, axis = OUT, **kwargs): + rot_matrix = rotation_matrix(angle, axis) + self.apply_points_function_about_point( + lambda points : np.dot(mob.points, rot_matrix.T), + **kwargs + ) return self - def rotate(self, angle, axis = OUT, axes = [], about_point = None): - if about_point is None: - self.rotate_about_origin(angle, axis, axes) - else: - self.do_about_point(about_point, self.rotate, angle, axis, axes) + def stretch(self, factor, dim, **kwargs): + def func(points): + points[:,dim] *= factor + return points + self.apply_points_function_about_point(func, about_point) return self - def stretch(self, factor, dim): - for mob in self.family_members_with_points(): - mob.points[:,dim] *= factor + def apply_function(self, function, about_point = ORIGIN, **kwargs): + self.apply_points_function_about_point( + lambda points : np.apply_along_axis(function, 1, points), + about_point = about_point, **kwargs + ) return self - def apply_function(self, function): - for mob in self.family_members_with_points(): - mob.points = np.apply_along_axis(function, 1, mob.points) - return self - - def apply_matrix(self, matrix): - matrix = np.array(matrix) - for mob in self.family_members_with_points(): - mob.points = np.dot(mob.points, matrix.T) + def apply_matrix(self, matrix, about_point = ORIGIN, **kwargs): + self.apply_points_function_about_point( + lambda points : np.dot(points, matrix.T), + about_point = about_point, **kwargs + ) return self def wag(self, direction = RIGHT, axis = DOWN, wag_factor = 1.0): @@ -224,6 +219,15 @@ class Mobject(object): #### In place operations ###### + def apply_points_function_about_point(self, func, about_point = None, about_edge = ORIGIN): + if about_point is None: + about_point = self.get_critical_point(about_edge) + for mob in self.family_members_with_points(): + mob.points -= about_point + mob.points = func(mob.points) + mob.points += about_point + return self + def do_about_point(self, point, method, *args, **kwargs): self.shift(-point) method(*args, **kwargs) @@ -235,8 +239,8 @@ class Mobject(object): return self def rotate_in_place(self, angle, axis = OUT, axes = []): - self.do_in_place(self.rotate, angle, axis, axes) - return self + # redundant with default behavior of rotate now. + return self.rotate(angle, axis = axis, axes = axes) def flip(self, axis = UP): self.rotate_in_place(np.pi, axis) @@ -244,12 +248,11 @@ class Mobject(object): def scale_in_place(self, scale_factor): #Redundant with default behavior of scale now. - self.do_in_place(self.scale, scale_factor) - return self + return self.scale(scale_factor) def scale_about_point(self, scale_factor, point): - self.do_about_point(point, self.scale, scale_factor) - return self + #Redundant with default behavior of scale now. + return self.scale(scale_factor, about_point = point) def pose_at_angle(self): self.rotate_in_place(np.pi / 7, RIGHT+UP) From 48dad34f957abce4cefec06052fefe5325e39ac2 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 20 Jan 2018 11:00:23 -0800 Subject: [PATCH 2/4] Editing ApplyMethod so that if the last arg is a dict, it's treated as kwargs for the method --- animation/transform.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/animation/transform.py b/animation/transform.py index e63b8586..e47e6c3c 100644 --- a/animation/transform.py +++ b/animation/transform.py @@ -143,7 +143,13 @@ class ApplyMethod(Transform): "the method you want to animate" ) assert(isinstance(method.im_self, Mobject)) - method_kwargs = kwargs.get("method_kwargs", {}) + args = list(args) #So that args.pop() works + if "method_kwargs" in kwargs: + method_kwargs = kwargs["method_kwargs"] + elif isinstance(args[-1], dict): + method_kwargs = args.pop() + else: + method_kwargs = {} target = method.im_self.copy() method.im_func(target, *args, **method_kwargs) Transform.__init__(self, method.im_self, target, **kwargs) From fcd1e7d6a53d2bae495cfe1eaa11a2c099ed53a5 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 20 Jan 2018 11:18:43 -0800 Subject: [PATCH 3/4] A few small fixes to new about_point behavior --- mobject/mobject.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/mobject/mobject.py b/mobject/mobject.py index 5ad8afcf..a16c6993 100644 --- a/mobject/mobject.py +++ b/mobject/mobject.py @@ -159,7 +159,7 @@ class Mobject(object): def rotate(self, angle, axis = OUT, **kwargs): rot_matrix = rotation_matrix(angle, axis) self.apply_points_function_about_point( - lambda points : np.dot(mob.points, rot_matrix.T), + lambda points : np.dot(points, rot_matrix.T), **kwargs ) return self @@ -168,20 +168,29 @@ class Mobject(object): def func(points): points[:,dim] *= factor return points - self.apply_points_function_about_point(func, about_point) + self.apply_points_function_about_point(func, **kwargs) return self - def apply_function(self, function, about_point = ORIGIN, **kwargs): + def apply_function(self, function, **kwargs): + #Default to applying matrix about the origin, not mobjects center + if len(kwargs) == 0: + kwargs["about_point"] = ORIGIN self.apply_points_function_about_point( lambda points : np.apply_along_axis(function, 1, points), about_point = about_point, **kwargs ) return self - def apply_matrix(self, matrix, about_point = ORIGIN, **kwargs): + def apply_matrix(self, matrix, **kwargs): + #Default to applying matrix about the origin, not mobjects center + if len(kwargs) == 0: + kwargs["about_point"] = ORIGIN + full_matrix = np.identity(self.dim) + matrix = np.array(matrix) + full_matrix[:matrix.shape[0],:matrix.shape[1]] = matrix self.apply_points_function_about_point( - lambda points : np.dot(points, matrix.T), - about_point = about_point, **kwargs + lambda points : np.dot(points, full_matrix.T), + **kwargs ) return self From d07efc6cb5ad2b970ac90ae4bb82c30a0bd113a7 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 20 Jan 2018 11:19:12 -0800 Subject: [PATCH 4/4] When calling Scene.play on a method followed by its args, if you end the list with a dict, it will interpret it as the kwargs of the method. --- scene/scene.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/scene/scene.py b/scene/scene.py index cd4892eb..15bd48bc 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -340,8 +340,9 @@ class Scene(object): def compile_play_args_to_animation_list(self, *args): """ - Eacn arg can either be an animation, or a mobject method - followed by that methods arguments. + Each arg can either be an animation, or a mobject method + followed by that methods arguments (and potentially follow + by a dict of kwargs for that method). This animation list is built by going through the args list, and each animation is simply added, but when a mobject method @@ -364,8 +365,15 @@ class Scene(object): #method should already have target then. else: mobject.target = mobject.copy() + # + if isinstance(state["method_args"][-1], dict): + method_kwargs = state["method_args"].pop() + else: + method_kwargs = {} state["curr_method"].im_func( - mobject.target, *state["method_args"] + mobject.target, + *state["method_args"], + **method_kwargs ) animations.append(MoveToTarget(mobject)) state["last_method"] = state["curr_method"] @@ -482,7 +490,7 @@ class Scene(object): path = os.path.join(self.output_directory, folder) file_name = (name or str(self)) + ".png" return os.path.join(path, file_name) - + def save_image(self, name = None, mode = "RGB", dont_update = False): path = self.get_image_file_path(name, dont_update) directory_path = os.path.dirname(path)