Skip to content

Commit

Permalink
Optimize VMobject.pointwise_become_partial() (ManimCommunity#3760)
Browse files Browse the repository at this point in the history
* Optimize VMobject.pointwise_become_partial()

* selftransformation -> self

* Small factorization of nppc
  • Loading branch information
chopan050 committed Jul 13, 2024
1 parent 3a71411 commit 374eeeb
Showing 1 changed file with 64 additions and 33 deletions.
97 changes: 64 additions & 33 deletions manim/mobject/types/vectorized_mobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -1830,60 +1830,91 @@ def pointwise_become_partial(
a: float,
b: float,
) -> Self:
"""Given two bounds a and b, transforms the points of the self vmobject into the points of the vmobject
passed as parameter with respect to the bounds. Points here stand for control points of the bezier curves (anchors and handles)
"""Given a 2nd :class:`.VMobject` ``vmobject``, a lower bound ``a`` and
an upper bound ``b``, modify this :class:`.VMobject`'s points to
match the portion of the Bézier spline described by ``vmobject.points``
with the parameter ``t`` between ``a`` and ``b``.
Parameters
----------
vmobject
The vmobject that will serve as a model.
The :class:`.VMobject` that will serve as a model.
a
upper-bound.
The lower bound for ``t``.
b
lower-bound
The upper bound for ``t``
Returns
-------
:class:`VMobject`
``self``
:class:`.VMobject`
The :class:`.VMobject` itself, after the transformation.
Raises
------
TypeError
If ``vmobject`` is not an instance of :class:`VMobject`.
"""
assert isinstance(vmobject, VMobject)
if not isinstance(vmobject, VMobject):
raise TypeError(
f"Expected a VMobject, got value {vmobject} of type "
f"{type(vmobject).__name__}."
)
# Partial curve includes three portions:
# - A middle section, which matches the curve exactly
# - A start, which is some ending portion of an inner cubic
# - An end, which is the starting portion of a later inner cubic
# - A middle section, which matches the curve exactly.
# - A start, which is some ending portion of an inner cubic.
# - An end, which is the starting portion of a later inner cubic.
if a <= 0 and b >= 1:
self.set_points(vmobject.points)
return self
bezier_quads = vmobject.get_cubic_bezier_tuples()
num_cubics = len(bezier_quads)
num_curves = vmobject.get_num_curves()
if num_curves == 0:
self.clear_points()
return self

# The following two lines will compute which bezier curves of the given mobject need to be processed.
# The residue basically indicates de proportion of the selected bezier curve that have to be selected.
# Ex : if lower_index is 3, and lower_residue is 0.4, then the algorithm will append to the points 0.4 of the third bezier curve
lower_index, lower_residue = integer_interpolate(0, num_cubics, a)
upper_index, upper_residue = integer_interpolate(0, num_cubics, b)
# The following two lines will compute which Bézier curves of the given Mobject must be processed.
# The residue indicates the proportion of the selected Bézier curve which must be selected.
#
# Example: if num_curves is 10, a is 0.34 and b is 0.78, then:
# - lower_index is 3 and lower_residue is 0.4, which means the algorithm will look at the 3rd Bézier
# and select its part which ranges from t=0.4 to t=1.
# - upper_index is 7 and upper_residue is 0.8, which means the algorithm will look at the 7th Bézier
# and select its part which ranges from t=0 to t=0.8.
lower_index, lower_residue = integer_interpolate(0, num_curves, a)
upper_index, upper_residue = integer_interpolate(0, num_curves, b)

self.clear_points()
if num_cubics == 0:
return self
nppc = self.n_points_per_curve
# If both indices coincide, get a part of a single Bézier curve.
if lower_index == upper_index:
self.append_points(
partial_bezier_points(
bezier_quads[lower_index],
lower_residue,
upper_residue,
),
# Look at the "lower_index"-th Bézier curve and select its part from
# t=lower_residue to t=upper_residue.
self.points = partial_bezier_points(
vmobject.points[nppc * lower_index : nppc * (lower_index + 1)],
lower_residue,
upper_residue,
)
else:
self.append_points(
partial_bezier_points(bezier_quads[lower_index], lower_residue, 1),
# Allocate space for (upper_index-lower_index+1) Bézier curves.
self.points = np.empty((nppc * (upper_index - lower_index + 1), self.dim))
# Look at the "lower_index"-th Bezier curve and select its part from
# t=lower_residue to t=1. This is the first curve in self.points.
self.points[:nppc] = partial_bezier_points(
vmobject.points[nppc * lower_index : nppc * (lower_index + 1)],
lower_residue,
1,
)
for quad in bezier_quads[lower_index + 1 : upper_index]:
self.append_points(quad)
self.append_points(
partial_bezier_points(bezier_quads[upper_index], 0, upper_residue),
# If there are more curves between the "lower_index"-th and the
# "upper_index"-th Béziers, add them all to self.points.
self.points[nppc:-nppc] = vmobject.points[
nppc * (lower_index + 1) : nppc * upper_index
]
# Look at the "upper_index"-th Bézier curve and select its part from
# t=0 to t=upper_residue. This is the last curve in self.points.
self.points[-nppc:] = partial_bezier_points(
vmobject.points[nppc * upper_index : nppc * (upper_index + 1)],
0,
upper_residue,
)

return self

def get_subcurve(self, a: float, b: float) -> Self:
Expand Down

0 comments on commit 374eeeb

Please sign in to comment.