Skip to content

Instantly share code, notes, and snippets.

@uwezi
Last active August 10, 2026 17:32
Show Gist options
  • Select an option

  • Save uwezi/e8adc59097c64002dd8a681434907009 to your computer and use it in GitHub Desktop.

Select an option

Save uwezi/e8adc59097c64002dd8a681434907009 to your computer and use it in GitHub Desktop.
[Bezier racing] Two Bezier curves on a path. #manim #animation #bezier #updater
from manim import *
class Bezier(VGroup):
def __init__(self, start, handle1, handle2, end, **kwargs):
super().__init__(**kwargs)
self.curve = VMobject(stroke_color=ORANGE,stroke_width=4)
self.start = Circle(arc_center=start, color=RED, radius=0.08, fill_opacity=0.7)
self.end = Circle(arc_center=end, color=BLUE, radius=0.08, fill_opacity=0.7)
self.handle1 = Circle(arc_center=handle1, color=RED, radius=0.05, fill_opacity=0.0)
self.handle2 = Circle(arc_center=handle2, color=BLUE, radius=0.05, fill_opacity=0.0)
self.hline1 = VMobject()
self.hline2 = VMobject()
self.add(
self.curve,
self.start,
self.handle1,
self.handle2,
self.end,
self.hline1,
self.hline2
)
self.add_updater(self.updater,call_updater=True)
def updater(self, grp):
start = grp.start.get_center()
handle1 = grp.handle1.get_center()
handle2 = grp.handle2.get_center()
end = grp.end.get_center()
grp.curve.set_anchors_and_handles(anchors1=start, handles1=handle1,handles2=handle2, anchors2=end)
grp.hline1.become(always_redraw(lambda: Line(self.start.get_center(),self.handle1.get_center(), stroke_color=RED,stroke_width=2)))
grp.hline2.become(always_redraw(lambda: Line(self.end.get_center(),self.handle2.get_center(), stroke_color=BLUE,stroke_width=2)))
class bezierex2(Scene):
def construct(self):
path = VMobject(stroke_width=0.3).set_points_as_corners(
[
[-6,3,0],[-2,2,0],[2,-3,0],[6,0,0],[3,2,0],[-3,-3,0],[-6,0,0]
]
).make_smooth()
self.add(path)
bz = Bezier(2*LEFT,LEFT,RIGHT,2*RIGHT)
bz.a = ValueTracker(0.21)
def bz_updater(mobj):
mobj.start.move_to(path.point_from_proportion(mobj.a.get_value()))
mobj.end.move_to(path.point_from_proportion(mobj.a.get_value()-0.1))
mobj.handle1.move_to(TangentLine(path, mobj.a.get_value(), length=2, d_alpha=1e-6).get_start())
mobj.handle2.move_to(TangentLine(path, mobj.a.get_value()-0.1, length=2, d_alpha=1e-6).get_end())
mobj.updater(mobj)
bz.add_updater(bz_updater, call_updater=True)
bz2 = bz.copy()
bz2.a.set_value(0.11)
bz2.update()
self.add(bz,bz2)
self.wait()
self.play(
LaggedStart(
bz.a.animate(rate_func=rate_functions.linear).set_value(1-0.05),
bz2.a.animate(rate_func=rate_functions.linear).set_value(0.9-0.05),
lag_ratio=0.3,
),
run_time=5,
)
self.play(
bz.a.animate.set_value(1),
bz2.a.animate.set_value(0.9),
run_time=1,
rate_func=rate_functions.linear
)
self.wait()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment