Merge pull request #80 from 3b1b/changes-implementation-of-basic-mobject-functions

Changes implementation of basic mobject functions
This commit is contained in:
Grant Sanderson 2018-01-20 11:21:20 -08:00 committed by GitHub
commit 999a57a90b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 68 additions and 42 deletions

View file

@ -143,7 +143,13 @@ class ApplyMethod(Transform):
"the method you want to animate" "the method you want to animate"
) )
assert(isinstance(method.im_self, Mobject)) 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() target = method.im_self.copy()
method.im_func(target, *args, **method_kwargs) method.im_func(target, *args, **method_kwargs)
Transform.__init__(self, method.im_self, target, **kwargs) Transform.__init__(self, method.im_self, target, **kwargs)

View file

@ -138,7 +138,7 @@ class Mobject(object):
mob.points += total_vector mob.points += total_vector
return self 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. Default behavior is to scale about the center of the mobject.
The argument about_edge can be a vector, indicating which side of The argument about_edge can be a vector, indicating which side of
@ -148,46 +148,50 @@ class Mobject(object):
Otherwise, if about_point is given a value, scaling is done with Otherwise, if about_point is given a value, scaling is done with
respect to that point. respect to that point.
""" """
if about_point is None: self.apply_points_function_about_point(
about_point = self.get_critical_point(about_edge) lambda points : scale_factor*points, **kwargs
self.shift(-about_point) )
for mob in self.family_members_with_points():
mob.points *= scale_factor
self.shift(about_point)
return self return self
def rotate_about_origin(self, angle, axis = OUT, axes = []): def rotate_about_origin(self, angle, axis = OUT, axes = []):
if len(axes) == 0: return self.rotate(angle, axis, about_point = ORIGIN)
axes = [axis]
rot_matrix = np.identity(self.dim) def rotate(self, angle, axis = OUT, **kwargs):
for axis in axes: rot_matrix = rotation_matrix(angle, axis)
rot_matrix = np.dot(rot_matrix, rotation_matrix(angle, axis)) self.apply_points_function_about_point(
t_rot_matrix = np.transpose(rot_matrix) lambda points : np.dot(points, rot_matrix.T),
for mob in self.family_members_with_points(): **kwargs
mob.points = np.dot(mob.points, t_rot_matrix) )
return self return self
def rotate(self, angle, axis = OUT, axes = [], about_point = None): def stretch(self, factor, dim, **kwargs):
if about_point is None: def func(points):
self.rotate_about_origin(angle, axis, axes) points[:,dim] *= factor
else: return points
self.do_about_point(about_point, self.rotate, angle, axis, axes) self.apply_points_function_about_point(func, **kwargs)
return self return self
def stretch(self, factor, dim): def apply_function(self, function, **kwargs):
for mob in self.family_members_with_points(): #Default to applying matrix about the origin, not mobjects center
mob.points[:,dim] *= factor 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 return self
def apply_function(self, function): def apply_matrix(self, matrix, **kwargs):
for mob in self.family_members_with_points(): #Default to applying matrix about the origin, not mobjects center
mob.points = np.apply_along_axis(function, 1, mob.points) if len(kwargs) == 0:
return self kwargs["about_point"] = ORIGIN
full_matrix = np.identity(self.dim)
def apply_matrix(self, matrix):
matrix = np.array(matrix) matrix = np.array(matrix)
for mob in self.family_members_with_points(): full_matrix[:matrix.shape[0],:matrix.shape[1]] = matrix
mob.points = np.dot(mob.points, matrix.T) self.apply_points_function_about_point(
lambda points : np.dot(points, full_matrix.T),
**kwargs
)
return self return self
def wag(self, direction = RIGHT, axis = DOWN, wag_factor = 1.0): def wag(self, direction = RIGHT, axis = DOWN, wag_factor = 1.0):
@ -224,6 +228,15 @@ class Mobject(object):
#### In place operations ###### #### 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): def do_about_point(self, point, method, *args, **kwargs):
self.shift(-point) self.shift(-point)
method(*args, **kwargs) method(*args, **kwargs)
@ -235,8 +248,8 @@ class Mobject(object):
return self return self
def rotate_in_place(self, angle, axis = OUT, axes = []): def rotate_in_place(self, angle, axis = OUT, axes = []):
self.do_in_place(self.rotate, angle, axis, axes) # redundant with default behavior of rotate now.
return self return self.rotate(angle, axis = axis, axes = axes)
def flip(self, axis = UP): def flip(self, axis = UP):
self.rotate_in_place(np.pi, axis) self.rotate_in_place(np.pi, axis)
@ -244,12 +257,11 @@ class Mobject(object):
def scale_in_place(self, scale_factor): def scale_in_place(self, scale_factor):
#Redundant with default behavior of scale now. #Redundant with default behavior of scale now.
self.do_in_place(self.scale, scale_factor) return self.scale(scale_factor)
return self
def scale_about_point(self, scale_factor, point): def scale_about_point(self, scale_factor, point):
self.do_about_point(point, self.scale, scale_factor) #Redundant with default behavior of scale now.
return self return self.scale(scale_factor, about_point = point)
def pose_at_angle(self): def pose_at_angle(self):
self.rotate_in_place(np.pi / 7, RIGHT+UP) self.rotate_in_place(np.pi / 7, RIGHT+UP)

View file

@ -340,8 +340,9 @@ class Scene(object):
def compile_play_args_to_animation_list(self, *args): def compile_play_args_to_animation_list(self, *args):
""" """
Eacn arg can either be an animation, or a mobject method Each arg can either be an animation, or a mobject method
followed by that methods arguments. 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, This animation list is built by going through the args list,
and each animation is simply added, but when a mobject method and each animation is simply added, but when a mobject method
@ -364,8 +365,15 @@ class Scene(object):
#method should already have target then. #method should already have target then.
else: else:
mobject.target = mobject.copy() 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( state["curr_method"].im_func(
mobject.target, *state["method_args"] mobject.target,
*state["method_args"],
**method_kwargs
) )
animations.append(MoveToTarget(mobject)) animations.append(MoveToTarget(mobject))
state["last_method"] = state["curr_method"] state["last_method"] = state["curr_method"]