I'm trying to make a curved shape that passes through a set of points and forms a closed loop. I was able to create a VMobject with the points and use .make_smooth()
to form the curve, but the first point has a sharp angle in it. Is there a way to make the entire curve smooth?
Here's the script:
from manim import *
class Help(Scene):
def construct(self):
points = ((-3, 2, 0), (-1, 0, 0), (-4, -2, 0), (-5, 0, 0))
redCurve = VMobject(color=RED)
redCurve.set_points_as_corners(points)
redCurve.close_path()
redCurve.make_smooth()
self.play(Create(redCurve))
self.wait(3)
Thanks!
The reason the Bezier curve has a sharp angle in it is because the make_smooth() function doesn't consider whether the anchors (points) form a closed loop or not. As a result, it has one less segment in it than it should. The solution is to calculate the handles for the loop using one of the utils Manim provides (the incredibly verbose get_smooth_closed_cubic_bezier_handle_points()
) and set them on the VMobject directly:
class Help(Scene):
def construct(self):
# The starting point is also added to the end here to form a loop
points = np.array([(-3, 2, 0), (-1, 0, 0), (-4, -2, 0), (-5, 0, 0), (-3, 2, 0)])
(handles1, handles2) = get_smooth_closed_cubic_bezier_handle_points(points)
redCurve = VMobject(color=RED)
# Arguments are in the form of startPoint[], firstHandle[], secondHandle[], endPoint[]
redCurve.set_anchors_and_handles(points[:-1], handles1, handles2, points[1:])
self.play(Create(redCurve))
self.wait(3)
You can also visualize what the get_smooth_closed...()
function is doing to make the shape smooth:
class Help(Scene):
def construct(self):
points = np.array([(-3, 2, 0), (-1, 0, 0), (-4, -2, 0), (-5, 0, 0), (-3, 2, 0)])
(handles1, handles2) = get_smooth_closed_cubic_bezier_handle_points(points)
redCurve = VMobject(color=RED)
redCurve.set_anchors_and_handles(points[:-1], handles1, handles2, points[1:])
# Gets the VMobject's parameters in the form of startPoint[], firstHandle[], secondHandle[], endPoint[]
for anchors in zip(*redCurve.get_anchors_and_handles()):
print("control points of this bezier segment: ", *anchors)
for point in anchors:
self.add(Dot(point, radius=0.05))
self.play(Create(redCurve))
self.wait(3)
control points of this bezier segment: [-3. 2. 0.] [-2. 2. 0.] [-0.75 1. 0. ] [-1. 0. 0.]
control points of this bezier segment: [-1. 0. 0.] [-1.25 -1. 0. ] [-3. -2. 0.] [-4. -2. 0.]
control points of this bezier segment: [-4. -2. 0.] [-5. -2. 0.] [-5.25 -1. 0. ] [-5. 0. 0.]
control points of this bezier segment: [-5. 0. 0.] [-4.75 1. 0. ] [-4. 2. 0.] [-3. 2. 0.]