2015-06-10 22:00:35 -07:00
|
|
|
import numpy as np
|
|
|
|
import itertools as it
|
|
|
|
import inspect
|
|
|
|
import copy
|
|
|
|
import warnings
|
|
|
|
|
|
|
|
from helpers import *
|
|
|
|
|
2015-10-27 21:00:50 -07:00
|
|
|
from animation import Animation
|
2015-12-13 15:41:45 -08:00
|
|
|
from mobject import Mobject, Point
|
2015-10-12 19:39:46 -07:00
|
|
|
|
2015-06-10 22:00:35 -07:00
|
|
|
class Transform(Animation):
|
2015-09-28 16:25:18 -07:00
|
|
|
DEFAULT_CONFIG = {
|
2016-01-01 14:51:16 -08:00
|
|
|
"path_func" : straight_path,
|
2015-09-28 16:25:18 -07:00
|
|
|
"should_black_out_extra_points" : False
|
|
|
|
}
|
|
|
|
def __init__(self, mobject, ending_mobject, **kwargs):
|
2015-10-20 21:55:46 -07:00
|
|
|
mobject, ending_mobject = map(instantiate, [mobject, ending_mobject])
|
2015-10-28 16:03:33 -07:00
|
|
|
digest_config(self, kwargs, locals())
|
2015-09-28 16:25:18 -07:00
|
|
|
count1, count2 = mobject.get_num_points(), ending_mobject.get_num_points()
|
2015-06-10 22:00:35 -07:00
|
|
|
if count2 == 0:
|
2015-12-23 11:31:11 -08:00
|
|
|
ending_mobject.add_points(
|
|
|
|
[mobject.get_center()],
|
|
|
|
color = BLACK
|
|
|
|
)
|
2015-09-28 16:25:18 -07:00
|
|
|
count2 = ending_mobject.get_num_points()
|
|
|
|
Mobject.align_data(mobject, ending_mobject)
|
|
|
|
if self.should_black_out_extra_points and count2 < count1:
|
2015-09-25 19:43:53 -07:00
|
|
|
self.black_out_extra_points(count1, count2)
|
|
|
|
|
2015-09-28 16:25:18 -07:00
|
|
|
Animation.__init__(self, mobject, **kwargs)
|
|
|
|
self.name += "To" + str(ending_mobject)
|
2015-10-09 19:53:38 -07:00
|
|
|
self.mobject.point_thickness = ending_mobject.point_thickness
|
|
|
|
|
2015-06-10 22:00:35 -07:00
|
|
|
|
2015-09-25 19:43:53 -07:00
|
|
|
def black_out_extra_points(self, count1, count2):
|
|
|
|
#Ensure redundant pixels fade to black
|
|
|
|
indices = np.arange(
|
|
|
|
0, count1-1, float(count1) / count2
|
|
|
|
).astype('int')
|
|
|
|
temp = np.zeros(self.ending_mobject.points.shape)
|
|
|
|
temp[indices] = self.ending_mobject.rgbs[indices]
|
|
|
|
self.ending_mobject.rgbs = temp
|
|
|
|
self.non_redundant_m2_indices = indices
|
2015-06-10 22:00:35 -07:00
|
|
|
|
|
|
|
def update_mobject(self, alpha):
|
2015-11-02 14:09:49 -08:00
|
|
|
families = map(
|
2015-12-22 11:02:58 -08:00
|
|
|
Mobject.submobject_family,
|
2015-11-02 14:09:49 -08:00
|
|
|
[self.mobject, self.starting_mobject, self.ending_mobject]
|
2015-06-10 22:00:35 -07:00
|
|
|
)
|
2015-11-02 14:09:49 -08:00
|
|
|
for m, start, end in zip(*families):
|
2015-11-02 19:09:55 -08:00
|
|
|
# print m, start, end
|
2016-01-01 14:51:16 -08:00
|
|
|
m.points = self.path_func(
|
2015-11-02 14:09:49 -08:00
|
|
|
start.points, end.points, alpha
|
|
|
|
)
|
|
|
|
m.rgbs = straight_path(start.rgbs, end.rgbs, alpha)
|
|
|
|
|
2015-06-10 22:00:35 -07:00
|
|
|
|
|
|
|
def clean_up(self):
|
2015-06-19 08:31:02 -07:00
|
|
|
Animation.clean_up(self)
|
2015-06-10 22:00:35 -07:00
|
|
|
if hasattr(self, "non_redundant_m2_indices"):
|
|
|
|
#Reduce mobject (which has become identical to mobject2), as
|
|
|
|
#well as mobject2 itself
|
|
|
|
for mobject in [self.mobject, self.ending_mobject]:
|
|
|
|
for attr in ['points', 'rgbs']:
|
|
|
|
setattr(
|
|
|
|
mobject, attr,
|
|
|
|
getattr(
|
|
|
|
self.ending_mobject,
|
|
|
|
attr
|
|
|
|
)[self.non_redundant_m2_indices]
|
|
|
|
)
|
|
|
|
|
2015-08-07 18:10:00 -07:00
|
|
|
class ClockwiseTransform(Transform):
|
2015-09-28 16:25:18 -07:00
|
|
|
DEFAULT_CONFIG = {
|
2016-01-01 14:51:16 -08:00
|
|
|
"path_func" : clockwise_path()
|
2015-09-28 16:25:18 -07:00
|
|
|
}
|
2015-06-13 19:00:23 -07:00
|
|
|
|
2015-08-07 18:10:00 -07:00
|
|
|
class CounterclockwiseTransform(Transform):
|
2015-09-28 16:25:18 -07:00
|
|
|
DEFAULT_CONFIG = {
|
2016-01-01 14:51:16 -08:00
|
|
|
"path_func" : counterclockwise_path()
|
2015-09-28 16:25:18 -07:00
|
|
|
}
|
2015-06-10 22:00:35 -07:00
|
|
|
|
2015-12-19 13:06:09 -08:00
|
|
|
class GrowFromCenter(Transform):
|
|
|
|
def __init__(self, mobject, **kwargs):
|
|
|
|
Transform.__init__(
|
|
|
|
self,
|
|
|
|
Point(mobject.get_center()),
|
|
|
|
mobject,
|
|
|
|
**kwargs
|
|
|
|
)
|
|
|
|
|
|
|
|
class SpinInFromNothing(GrowFromCenter):
|
2015-09-28 16:25:18 -07:00
|
|
|
DEFAULT_CONFIG = {
|
2016-01-01 14:51:16 -08:00
|
|
|
"path_func" : counterclockwise_path()
|
2015-09-28 16:25:18 -07:00
|
|
|
}
|
2015-06-10 22:00:35 -07:00
|
|
|
|
2015-12-25 08:26:51 -08:00
|
|
|
class ShrinkToCenter(Transform):
|
|
|
|
def __init__(self, mobject, **kwargs):
|
|
|
|
Transform.__init__(
|
|
|
|
self, mobject,
|
|
|
|
Point(mobject.get_center()),
|
|
|
|
**kwargs
|
|
|
|
)
|
|
|
|
|
2015-06-10 22:00:35 -07:00
|
|
|
class ApplyMethod(Transform):
|
|
|
|
def __init__(self, method, *args, **kwargs):
|
|
|
|
"""
|
|
|
|
Method is a method of Mobject. *args is for the method,
|
|
|
|
**kwargs is for the transform itself.
|
|
|
|
|
|
|
|
Relies on the fact that mobject methods return the mobject
|
|
|
|
"""
|
2015-10-20 21:55:46 -07:00
|
|
|
assert(inspect.ismethod(method))
|
|
|
|
assert(isinstance(method.im_self, Mobject))
|
2015-06-10 22:00:35 -07:00
|
|
|
Transform.__init__(
|
|
|
|
self,
|
|
|
|
method.im_self,
|
|
|
|
copy.deepcopy(method)(*args),
|
|
|
|
**kwargs
|
|
|
|
)
|
|
|
|
|
2015-10-27 21:00:50 -07:00
|
|
|
class Rotate(ApplyMethod):
|
2015-11-09 10:34:00 -08:00
|
|
|
DEFAULT_CONFIG = {
|
|
|
|
"in_place" : False,
|
|
|
|
}
|
|
|
|
def __init__(self, mobject, angle = np.pi, axis = OUT, **kwargs):
|
2016-01-01 14:51:16 -08:00
|
|
|
kwargs["path_func"] = path_along_arc(angle)
|
2015-11-09 10:34:00 -08:00
|
|
|
digest_config(self, kwargs, locals())
|
|
|
|
if self.in_place:
|
|
|
|
method = mobject.rotate_in_place
|
|
|
|
else:
|
|
|
|
method = mobject.rotate
|
|
|
|
ApplyMethod.__init__(self, method, angle, axis, **kwargs)
|
2015-10-27 21:00:50 -07:00
|
|
|
|
|
|
|
|
2015-10-20 21:55:46 -07:00
|
|
|
class ApplyPointwiseFunction(ApplyMethod):
|
|
|
|
DEFAULT_CONFIG = {
|
|
|
|
"run_time" : DEFAULT_POINTWISE_FUNCTION_RUN_TIME
|
|
|
|
}
|
|
|
|
def __init__(self, function, mobject, **kwargs):
|
|
|
|
ApplyMethod.__init__(
|
|
|
|
self, mobject.apply_function, function, **kwargs
|
|
|
|
)
|
|
|
|
|
2015-09-28 16:25:18 -07:00
|
|
|
class FadeToColor(ApplyMethod):
|
|
|
|
def __init__(self, mobject, color, **kwargs):
|
|
|
|
ApplyMethod.__init__(self, mobject.highlight, color, **kwargs)
|
|
|
|
|
|
|
|
class ScaleInPlace(ApplyMethod):
|
|
|
|
def __init__(self, mobject, scale_factor, **kwargs):
|
|
|
|
ApplyMethod.__init__(self, mobject.scale_in_place, scale_factor, **kwargs)
|
|
|
|
|
2015-06-10 22:00:35 -07:00
|
|
|
class ApplyFunction(Transform):
|
2015-10-06 15:35:34 -07:00
|
|
|
def __init__(self, function, mobject, **kwargs):
|
2015-08-07 18:10:00 -07:00
|
|
|
Transform.__init__(
|
|
|
|
self,
|
|
|
|
mobject,
|
2015-11-02 19:09:55 -08:00
|
|
|
function(mobject.copy()),
|
2015-08-07 18:10:00 -07:00
|
|
|
**kwargs
|
|
|
|
)
|
|
|
|
self.name = "ApplyFunctionTo"+str(mobject)
|
|
|
|
|
2015-10-06 18:40:18 -07:00
|
|
|
class ApplyMatrix(Animation):
|
|
|
|
#Truth be told, I'm not sure if this is useful.
|
|
|
|
def __init__(self, matrix, mobject, **kwargs):
|
|
|
|
matrix = np.array(matrix)
|
|
|
|
if matrix.shape == (2, 2):
|
|
|
|
self.matrix = np.identity(3)
|
|
|
|
self.matrix[:2, :2] = matrix
|
|
|
|
elif matrix.shape == (3, 3):
|
|
|
|
self.matrix = matrix
|
|
|
|
else:
|
|
|
|
raise "Matrix has bad dimensions"
|
|
|
|
Animation.__init__(self, mobject, **kwargs)
|
|
|
|
|
|
|
|
def update_mobject(self, alpha):
|
|
|
|
matrix = interpolate(np.identity(3), self.matrix, alpha)
|
|
|
|
self.mobject.points = np.dot(
|
|
|
|
self.starting_mobject.points,
|
|
|
|
np.transpose(matrix)
|
|
|
|
)
|
|
|
|
|
2015-06-10 22:00:35 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|