3b1b-manim/animation/transform.py

219 lines
7.5 KiB
Python
Raw Normal View History

2015-06-10 22:00:35 -07:00
import numpy as np
import itertools as it
import inspect
import copy
import warnings
from animation import Animation
2015-08-03 22:23:00 -07:00
from mobject import Mobject, Point
2015-06-10 22:00:35 -07:00
from constants import *
from helpers import *
def straight_path(start_points, end_points, alpha):
return (1-alpha)*start_points + alpha*end_points
def semi_circular_path(start_points, end_points, alpha, axis):
midpoints = (start_points + end_points) / 2
angle = alpha * np.pi
rot_matrix = rotation_matrix(angle, axis)[:2, :2]
result = np.zeros(start_points.shape)
result[:,:2] = np.dot(
(start_points - midpoints)[:,:2],
np.transpose(rot_matrix)
) + midpoints[:,:2]
result[:,2] = (1-alpha)*start_points[:,2] + alpha*end_points[:,2]
return result
def clockwise_path(start_points, end_points, alpha):
return semi_circular_path(start_points, end_points, alpha, IN)
def counterclockwise_path(start_points, end_points, alpha):
return semi_circular_path(start_points, end_points, alpha, OUT)
2015-06-10 22:00:35 -07:00
class Transform(Animation):
def __init__(self, mobject1, mobject2,
run_time = DEFAULT_TRANSFORM_RUN_TIME,
interpolation_function = straight_path,
2015-06-19 08:31:02 -07:00
black_out_extra_points = False,
2015-06-10 22:00:35 -07:00
*args, **kwargs):
self.interpolation_function = interpolation_function
2015-06-10 22:00:35 -07:00
count1, count2 = mobject1.get_num_points(), mobject2.get_num_points()
if count2 == 0:
mobject2 = Point((SPACE_WIDTH, SPACE_HEIGHT, 0))
count2 = mobject2.get_num_points()
Mobject.align_data(mobject1, mobject2)
Animation.__init__(self, mobject1, run_time = run_time, *args, **kwargs)
self.ending_mobject = mobject2
self.mobject.SHOULD_BUFF_POINTS = \
mobject1.SHOULD_BUFF_POINTS and mobject2.SHOULD_BUFF_POINTS
self.reference_mobjects.append(mobject2)
self.name += "To" + str(mobject2)
if black_out_extra_points and count2 < count1:
#Ensure redundant pixels fade to black
indices = np.arange(
0, count1-1, float(count1) / count2
).astype('int')
temp = np.zeros(mobject2.points.shape)
temp[indices] = mobject2.rgbs[indices]
mobject2.rgbs = temp
self.non_redundant_m2_indices = indices
def update_mobject(self, alpha):
self.mobject.points = self.interpolation_function(
self.starting_mobject.points,
self.ending_mobject.points,
alpha
)
self.mobject.rgbs = straight_path(
self.starting_mobject.rgbs,
self.ending_mobject.rgbs,
2015-06-10 22:00:35 -07:00
alpha
)
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]
)
class ClockwiseTransform(Transform):
def __init__(self, mobject1, mobject2, **kwargs):
Transform.__init__(
self, mobject1, mobject2,
interpolation_function = clockwise_path, **kwargs
)
2015-06-13 19:00:23 -07:00
class CounterclockwiseTransform(Transform):
def __init__(self, mobject1, mobject2, **kwargs):
Transform.__init__(
self, mobject1, mobject2,
interpolation_function = counterclockwise_path, **kwargs
)
2015-06-10 22:00:35 -07:00
class FadeToColor(Transform):
def __init__(self, mobject, color, *args, **kwargs):
target = copy.deepcopy(mobject).highlight(color)
Transform.__init__(self, mobject, target, *args, **kwargs)
class Highlight(FadeToColor):
def __init__(self, mobject, color = "red",
run_time = DEFAULT_ANIMATION_RUN_TIME,
alpha_func = there_and_back, *args, **kwargs):
FadeToColor.__init__(
self, mobject, color,
run_time = run_time,
alpha_func = alpha_func,
*args, **kwargs
)
class ScaleInPlace(Transform):
def __init__(self, mobject, scale_factor, *args, **kwargs):
target = copy.deepcopy(mobject)
center = mobject.get_center()
target.shift(-center).scale(scale_factor).shift(center)
Transform.__init__(self, mobject, target, *args, **kwargs)
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
"""
if not inspect.ismethod(method) or \
not isinstance(method.im_self, Mobject):
raise "Not a valid Mobject method"
Transform.__init__(
self,
method.im_self,
copy.deepcopy(method)(*args),
**kwargs
)
class ApplyFunction(Transform):
def __init__(self, function, mobject, **kwargs):
Transform.__init__(
self,
mobject,
function(copy.deepcopy(mobject)),
**kwargs
)
self.name = "ApplyFunctionTo"+str(mobject)
class ApplyPointwiseFunction(Transform):
def __init__(self, function, mobject,
run_time = DEFAULT_ANIMATION_RUN_TIME, **kwargs):
2015-06-10 22:00:35 -07:00
map_image = copy.deepcopy(mobject)
map_image.points = np.array(map(function, map_image.points))
Transform.__init__(
self, mobject, map_image,
run_time = run_time, **kwargs
)
2015-06-10 22:00:35 -07:00
self.name = "".join([
"Apply",
"".join([s.capitalize() for s in function.__name__.split("_")]),
"To" + str(mobject)
])
class ComplexFunction(ApplyPointwiseFunction):
2015-06-10 22:00:35 -07:00
def __init__(self, function, *args, **kwargs):
def point_map(point):
x, y, z = point
c = np.complex(x, y)
c = function(c)
return c.real, c.imag, z
if len(args) > 0:
args = list(args)
mobject = args.pop(0)
elif "mobject" in kwargs:
mobject = kwargs.pop("mobject")
else:
mobject = Grid()
ApplyPointwiseFunction.__init__(self, point_map, mobject, *args, **kwargs)
2015-06-10 22:00:35 -07:00
self.name = "ComplexFunction" + to_cammel_case(function.__name__)
#Todo, abstract away function naming'
2015-06-19 08:31:02 -07:00
class TransformAnimations(Transform):
def __init__(self, start_anim, end_anim,
alpha_func = squish_alpha_func(smooth),
2015-06-19 08:31:02 -07:00
**kwargs):
2015-06-27 04:49:10 -07:00
if "run_time" in kwargs:
run_time = kwargs.pop("run_time")
for anim in start_anim, end_anim:
anim.set_run_time(run_time)
2015-06-19 08:31:02 -07:00
self.start_anim, self.end_anim = start_anim, end_anim
Transform.__init__(
self,
start_anim.mobject,
end_anim.mobject,
run_time = max(start_anim.run_time, end_anim.run_time),
alpha_func = alpha_func,
**kwargs
)
#Rewire starting and ending mobjects
start_anim.mobject = self.starting_mobject
end_anim.mobject = self.ending_mobject
def update(self, alpha):
self.start_anim.update(alpha)
self.end_anim.update(alpha)
Transform.update(self, alpha)
2015-06-10 22:00:35 -07:00