I'm trying to find a sensible way to move an Mobject
along a path defined by the n-length vectors ts,xs,ys,(zs)
.
The solution I have now is by using ParametricFunction
and MoveAlongPath
. I can then define a rate function to make sure the timing adds up. This is extremely backwards and not quite reliable in my experience.
I feel like I'm probably missing some builtin function but I can't find it.
# This function takes a path defined by arrays and returns a function
# ts is assumed to be strictly increasing
def manim_curve(ts,xs,ys):
ts,xs,ys = map(np.array,(ts,xs,ys))
# Calculate the total distance traveled over the curve
dist = np.cumsum(np.abs(np.diff(xs+1j*ys,prepend=0)))
# Normalize to a time range of [0,1]
nts = ts / ts[-1]
ndist = dist / dist[-1]
# Create a function that can be passed `ParametricFunction`
def f(t):
n = np.abs(nts-t).argmin() # Find index from t
return (xs[n],ys[n],0)
# Create a rate function for `MoveAlongPath`
def rate(t):
n = np.abs(nts-t).argmin() # Find index from t
return ndist[n]
# Create manim curve
curve = ParametricFunction(function=f)
return curve,rate
# Animation class to move along a discretely defined path
class MoveAlongMeasuredPath(MoveAlongPath):
def __init__(self,object,ts,xs,ys,**kwargs):
ts,xs,ys = map(np.array,(ts,xs,ys))
curve,rate = manim_curve(ts,xs,ys)
super().__init__(object,curve,
run_time = ts[-1],
rate_func = rate,
**kwargs)
I dug a little deeper in the code and realized there is a simple solution. The class below is only a slight alteration of the MoveAlongPath
class source code:
class MoveAlongTXYZPath(Animation):
def __init__(
self,
mobject: Mobject,
ts:NDArray,
points:NDArray,
is_sorted:bool=False,
suspend_mobject_updating: bool = False,
**kwargs,
) -> None:
assert np.all(ts>=0), "no negative t_values allowed"
assert len(ts)==len(points), "vectors have to be the same length"
# Sort if unsorted in t
if not is_sorted:
ts,points = map(np.array,zip(*sorted([*zip(ts,points)])))
self.points = points
run_time = np.max(ts)
self.alphas = ts/run_time
super().__init__( mobject,
suspend_mobject_updating=suspend_mobject_updating,
run_time=run_time,
rate_func=linear,
**kwargs)
def interpolate_mobject(self, alpha: float) -> None:
index = np.searchsorted(self.alphas,alpha)
point = self.points[index]
self.mobject.move_to(point)