Epicycloid

In geometry, an epicycloid is a plane curve generated by tracing the path of a point on the circumference of a circle that rolls without slipping around a fixed circle.

About

Setup

Create a new Colab notebook at colab.new and run the cells below.

The session will restart to apply changes.

%pip install mscene
import mscene
%mscene -l manim plugins
from mscene.manim import *
from mscene.plugins import *

Actions

The epitrochoid_scene function animates epicycloid or epitrochoid scenes. An epicycloid is a special case of epitrochoid where the tracing point is on the circumference of the rolling circle.

def epitrochoid_scene(
    scene,
    k,
    angle=-TAU,
    run_time=4,
    markers=[(1, PI / 2, ManimColor("#6019E3"))],
    scale=1,
    title=None,
):
    """Animates epicycloid or epitrochoid scenes. An epicycloid is a special case of epitrochoid where the tracing point is on the circumference of the rolling circle.

    Args:
        scene (Scene): An instance of Manim Scene for animation.
        k (float): The ratio R/r, where R is the radius of the fixed circle and r is the radius of the rolling circle. This affects the shape of the curve.
        angle (float): The angle of rotation around the fixed circle. Defaults to -TAU.
        run_time (float): The duration of rolling animation. Defaults to 4.
        markers (list[tuple[float, float, ManimColor]]): A list of markers for tracing the path, where each marker is a tuple of distance, angle and color. Defaults to [(1, PI / 2, ManimColor("#6019E3"))].
        scale (float): The scale factor to fit the scene within the frame. Defaults to 1.
        title (str | None, optional): The title of the scene. If not provided (None), no title will be shown. Defaults to None.

    Returns:
        None
    """

    h = config.frame_height * scale * 3 / 8
    r = h / (2 + k)

    wheel = Wheel(radius=r, color=ManimColor("#04D9FF"), markers=markers)

    circle = Circle(radius=k * r, color=ManimColor("#6F828A"))

    txt = f"k = {k}" if title is None else f"{title}\nk = {k}"
    text = Text(txt).to_corner(UL, buff=2 / 3)

    wheel.move(circle.get_top(), UP)

    scene.play(FadeIn(text, circle, wheel))
    scene.wait(0.5)

    path = wheel.trace_paths()
    scene.add(path)

    scene.play(wheel.roll(angle, about=circle, run_time=run_time))

    path.clear_updaters()
    scene.wait(2)

    scene.play(FadeOut(path))
    scene.wait()

    scene.play(FadeOut(text, circle, wheel))
    scene.wait(0.5)

Scenes

Epicycloids

Scene One

%%manim -qm EpicycloidOne
class EpicycloidOne(Scene):
    def construct(self):

        epitrochoid_scene(self, k=3, angle=-TAU, title="Epicycloid")

Scene Two

%%manim -qm EpicycloidTwo
class EpicycloidTwo(Scene):
    def construct(self):

        k, angle, run_time = (1.5, -TAU * 2, 8)

        # roll again with these values
        # k, angle, run_time = (2, -TAU, 4)
        # k, angle, run_time = (1, -TAU, 4)
        # k, angle, run_time = (.5, -TAU*2, 8)

        epitrochoid_scene(self, k, angle, run_time, title="Epicycloid")

Epitrochoids

%%manim -qm Epitrochoids
class Epitrochoids(Scene):
    def construct(self):

        k, angle, run_time = (3, -TAU, 5)
        markers = [
            (2, PI / 2, ManimColor("#6019E3")),
            (0.5, -PI / 2, ManimColor("#E31937")),
        ]
        epitrochoid_scene(
            self, k, angle, run_time, markers, scale=0.875, title="Epitrochoids"
        )

Two Rolling Circles

%%manim -qm TwoRollingCircles
class TwoRollingCircles(Scene):
    def construct(self):

        k, angle, run_time = (2.25, -4 * TAU, 16)

        # try these values
        # k, angle, run_time = (2.8, -5*TAU, 18)

        h = config.frame_height * 3 / 8
        r = h / (2 + k)

        color_one = ManimColor("#6019E3")
        color_two = ManimColor("#E31937")

        wheel_one_markers = [(1, PI / 2, color_one)]
        wheel_two_markers = [(1, -PI / 2, color_two)]

        wheel_one = Wheel(
            radius=r, color=ManimColor("#04D9FF"), markers=wheel_one_markers
        )
        wheel_two = Wheel(
            radius=r, color=ManimColor("#04D9FF"), markers=wheel_two_markers
        )
        circle = Circle(radius=k * r, color=ManimColor("#6F828A"))

        wheel_one.move(circle.get_top(), UP)
        wheel_two.move(circle.get_top(), DOWN)

        self.play(FadeIn(circle, wheel_one, wheel_two))
        self.wait(0.5)

        wheel_one_path = wheel_one.trace_paths()
        wheel_two_path = wheel_two.trace_paths()
        self.add(wheel_one_path, wheel_two_path)

        self.play(
            wheel_one.roll(angle, about=circle, run_time=run_time),
            wheel_two.roll(angle, about=circle, run_time=run_time),
        )
        wheel_one_path.clear_updaters()
        wheel_two_path.clear_updaters()
        self.wait(2)

        self.play(FadeOut(wheel_one_path, wheel_two_path))
        self.wait()

        self.play(FadeOut(circle, wheel_one, wheel_two))
        self.wait(0.5)