Motivation and Explanation: Why and how do your changes improve the library?
The current implementation splits the cubic Bézier at its inflection point (if it exists, otherwise simply t = 0.5). This sounds quite right: the curvature changes at the inflection point, something which can't be captured by a single quadratic Bézier, so we split the curve into two at that point in order to approximate each curvature with different curves.
As you can see, the speed of the point (1st derivative of the Béziers) is discontinuous when transitioning from one quadratic to the other.
This is problematic for 2 reasons:
An animation like Create, Write or DrawBorderThenFill won't draw the resultant VMobject as smoothly as it could (the drawing speed might suddenly change)... even if it the original cubic spline was actually smooth.
OpenGL uses quadratic Béziers whereas Cairo uses cubic Béziers. When OpenGLVMobject.make_smooth() is called, it interpolates the curve anchors with a smooth cubic spline, and then proceeds to approximate it with quadratic Béziers by calling get_quadratic...(). So, OpenGLVMobject.make_smooth() promises to transform the Mobject into a smooth curve, but get_quadratic...() breaks that promise.
Therefore I propose an alternate implementation: instead of forcing a split at a specific point on the curve, I explicitly require that the curves and their 1st derivatives are continuous. (See the docstring of get_quadratic...() for more details on the mathematical process)
With this implementation, the result is as follows:
Notice that the resultant curve might be slightly more off than the original approximation. The original seems more proper for static images where it's enough that the tangents are continuous (the speed directions are the same, rather than the speeds themselves). However, Manim is mainly an animation library, so it's necessary that the speeds are also continuous in this case.
Overview: What does this pull request change?
Motivation and Explanation: Why and how do your changes improve the library?
The current implementation splits the cubic Bézier at its inflection point (if it exists, otherwise simply
t = 0.5
). This sounds quite right: the curvature changes at the inflection point, something which can't be captured by a single quadratic Bézier, so we split the curve into two at that point in order to approximate each curvature with different curves.However:
https://github.com/ManimCommunity/manim/assets/49853152/6fc5b352-92a7-4787-9bf6-57f002547114
As you can see, the speed of the point (1st derivative of the Béziers) is discontinuous when transitioning from one quadratic to the other.
This is problematic for 2 reasons:
Create
,Write
orDrawBorderThenFill
won't draw the resultantVMobject
as smoothly as it could (the drawing speed might suddenly change)... even if it the original cubic spline was actually smooth.OpenGLVMobject.make_smooth()
is called, it interpolates the curve anchors with a smooth cubic spline, and then proceeds to approximate it with quadratic Béziers by callingget_quadratic...()
. So,OpenGLVMobject.make_smooth()
promises to transform the Mobject into a smooth curve, butget_quadratic...()
breaks that promise.Therefore I propose an alternate implementation: instead of forcing a split at a specific point on the curve, I explicitly require that the curves and their 1st derivatives are continuous. (See the docstring of
get_quadratic...()
for more details on the mathematical process)With this implementation, the result is as follows:
https://github.com/ManimCommunity/manim/assets/49853152/074a82b5-a20b-4440-a32a-0434a58a01b6
Another example with more exotic curves (the spline is, however, not smooth, so the speeds at the green points aren't continuous:
https://github.com/ManimCommunity/manim/assets/49853152/2ed4f09a-6731-402d-b1f3-08d35ffca6e6
https://github.com/ManimCommunity/manim/assets/49853152/d2b18322-f2d4-4118-b0c8-ccabab852f6e
Notice that the resultant curve might be slightly more off than the original approximation. The original seems more proper for static images where it's enough that the tangents are continuous (the speed directions are the same, rather than the speeds themselves). However, Manim is mainly an animation library, so it's necessary that the speeds are also continuous in this case.
Links to added or changed documentation pages
https://manimce--3829.org.readthedocs.build/en/3829/reference/manim.utils.bezier.html#manim.utils.bezier.get_quadratic_approximation_of_cubic
Further Information and Comments
The code I used:
Reviewer Checklist