-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Optimized various point-related :class:.VMobject
methods
#3292
base: main
Are you sure you want to change the base?
Optimized various point-related :class:.VMobject
methods
#3292
Conversation
… length memos and binary search
…d similar methods
…hen subpath has a single curve
…hen subpath has a single curve
…050/manim into optimized_vmobject_points
…050/manim into optimized_vmobject_points
…hange_anchor_mode
…dices_from_points
…stead of copies, and other mistakes
…in VMobject._update_curve_memory
Done! I addressed most comments. def insert_n_curves_to_point_list(self, n: int, points: np.ndarray) -> np.ndarray:
# ... other stuff
for quad, sf in zip(bezier_quads, split_factors):
if sf == 1:
new_points[start_i : start_i + nppcc] = quad
start_i += nppcc
else:
for i in range(sf):
new_points[start_i : start_i + nppcc] = partial_bezier_points(
quad, i / sf, (i + 1) / sf
)
start_i += nppcc
return new_points In #3281 I added a new function in def insert_n_curves_to_point_list(self, n: int, points: np.ndarray) -> np.ndarray:
# ... other stuff
for quad, sf in zip(bezier_quads, split_factors):
new_points[start_i : start_i + sf * nppcc] = subdivide_bezier(quad, sf)
start_i += sf * nppcc
return new_points This would look much cleaner and is also more efficient, since in So, if #3281 gets merged first, I'd wanna add this change to this PR before merging this one. |
…ptimized_vmobject_points
1c5072a
to
d6e3aaf
Compare
Overview: What does this pull request change?
Main changes:
np.append
inVMobject
's methods, and reduced the use ofVMobject.append_points
as much as possible, because their extensive use is expensive. When possible, they're replaced with preallocations of empty ndarrays whose lengths can be precalculated.VMobject.append_points
to usenp.empty
instead ofnp.append
, because it is slightly faster this way. Useful because this method can still get called a lot.np.linspace
fromVMobject
's methods, because repeated call to this method for small arrays is also too expensive.VMobject.bezier_alphas
related to thenppcc
. This attribute is a precalculation of the alphas needed to interpolate pairs of anchors to obtain the required handles for the new straight segments that shall be represented by Bézier curves. This replaces those special cases wherenp.linspace
was originally used for interpolation.VMobject.consider_points_equals
by replacing thenp.allclose
method, which is also really expensive when used repeatedly to compare only two 3D points.VMobject.consider_points_equals_2d
, where a comparison coord-by-coord is made to determine if the points are close or not.np.allclose
approach.Added two new methods,EDIT: Nevermind, I removed those methods to avoid code duplication.VMobject.consider_points_different
andVMobject.consider_points_different_2d
, because more often than not we actually want to know if two points are distant enough from each other, rather than if they're close to each other.VMobject.get_subpath_split_indices
andVMobject.get_subpath_split_indices_from_points
, which instead of explicitly obtaining a Python list of subpaths, it obtains an (n_subpaths, 2) ndarray of indices indicating where every subpath starts and ends.n_dims
(which allows us to choose between 2D or 3D point comparison) andstrip_null_end_curves
(necessary forVMobject.align_points
where the null end curves at every subpath must be removed as a fix to certain bug).VMobject.get_subpaths_from_points
andVMobject.gen_subpaths_from_points_2d
to use this new methodVMobject.get_subpath_split_indices_from_points
.VMobject._gen_subpaths_from_points
is no longer used and can probably be deprecated if this PR is accepted.VMobject.add_points_as_corners
: it originally used a for loop to add every point one by one throughVMobject.add_line_to
. This was extremely expensive (especially with the previous use ofnp.linspace
) and was completely changed to just add all the points at once, calculating the necessary handles via interpolations.VMobject.change_anchor_mode
: if using "jagged" mode, it is not necessary at all to separate the points into subpaths (a simple interpolation is sufficient). Only get the subpaths in "smooth" mode. In this case, the new implementation now uses this new methodVMobject.get_subpath_split_indices
instead of explicitly precalculating all the subpaths previously and storing them all in a Python list. As the number of points remains pretty much the same and only the handles change, there's no need to clear the points or create another ndarray of points: we can just directly overwrite the handles.VMobject.align_points
: this method was completely rewritten so that it now usesVMobject.get_subpath_indices
too. In this way, one can also easily calculate useful information like how long is every subpath (just subtract the pairs of indices!) and, thus, which subpath is longer when comparing pairs of subpaths between the twoVMobject
s (vianp.maximum
). Thenp.append
s were obliterated. As mentioned earlier, this new implementation usesstrip_null_end_curves=True
to remove the excess null curves at the end of the subpaths, if any.Mobject.memory
, which might be useful for memoizing certain desirable properties.VMobject.point_from_proportion
.VMobject._init_curve_memory
andVMobject._update_curve_memory
, which precalculate the lengths and accumulated lengths of the Bézier curves making up theVMobject
and update it only if the actual points or the number of sample points per curve have changed.VMobject.point_from_proportion
's time!VMobject.get_area_vector
, where this could be used as well.Motivation and Explanation: Why and how do your changes improve the library?
My original intention was to optimize my test scene where I have 110 ParametricFunctions being updated in a span of 5 seconds, which is really expensive!
Almost half of the time of the scene is spent on
ParametricFunction.generate_points
, and there are 3 major culprits:VMobject.make_smooth
,VMobject.add_points_as_corners
, andAxes.coords_to_point
.In #3281 I optimized
get_smooth_handle_points
(and other Bézier-related functions), which was an important cause ofVMobject.make_smooth
's excessive time, and in #3285 and #3286 I addressed the issue withAxes.coords_to_point
and, more specifically,NumberLine.number_to_point
.Regarding the
VMobject.add_points_as_corners
and theVMobject.get_subpaths
, which are the other major issues, I initially rewroteParametricFunction
to circumvent those issues. After all, I don't thinkParametricFunction.generate_points
should be relying a lot on these functions to, well, generate its points, when they can be generated in other efficient ways. I have some ideas about rewritingParametricFunction
, which I wanna discuss in a future PR.However, there are still many other
VMobject
subclasses which depend on those currently inefficient methods too. As I noticed this was a general issue for manyVMobject
s, I decided to address the root issues and directly modify and optimize most ofVMobject
's point-related methods.EDIT: here's a new screenshot of how
ParametricFunction.generate_points
looks after these changes.VMobject.get_subpaths
(replaced byVMobject.get_subpath_split_indices
) andVMobject.add_points_as_corners
are no longer a problem!Links to added or changed documentation pages
Further Information and Comments
Reviewer Checklist