Short introduction to Manim

From wikiluntti
Revision as of 14:56, 13 July 2026 by Mol (talk | contribs) (→‎Introduction)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Introduction

Manim or manimgl.

Virtual engine

python -m venv .
source bin/activate

For faster rendering use smaller resolution resolution=(15,15) and smaller fps rate

manim -pql --fps 10 ma_animatedplanewave.py PlaneWave3D

Some short manim scrips to generate easy visualizations.

manim shorts.py MyScene -r 1080,1920
manim -pql bez2.py
manim -pqh bez2.py BezierSplineExample

   --format=png
   f: open the directory/ folder
   i: generate gif file
   p: play
  qm, qh, qk: quality (low, high, 4k)
  qh: high quality
   s: save last frame
   t: transparent background
config.background_color = WHITE

Add a background image

bg_image = ImageMobject("bg_P5.png")
self.add( bg_image )

Add the background plane

plane = NumberPlane( background_line_style={
                "stroke_color": BLACK,
                "stroke_width": 4,
                "stroke_opacity": 1
            },
            x_range=[-10, 10, 1],
            y_range=[-10, 10, 1],
                            )
self.add( plane )

Install Manim on Arch Linux

Pip is not working on a virtual environment. However use uv to install it. That works (on Sep 2024):

uv pip install manim

It is now installed on virtual environment, but the global system is not affected (not working outside virtual environment).

BG plane

       number_plane = NumberPlane(
            background_line_style={
                "stroke_color": TEAL,
                "stroke_width": 1,
                "stroke_opacity": 0.2
            }
        )
        self.add(number_plane)

Bezier curve

Animated bezier curve with two points:

https://github.com/markkuleino/manim/blob/main/FLL_CC0_bezier.py

See tutorial on Bezier Curves https://pomax.github.io/bezierinfo/ and the interactive tutorial https://www.jasondavies.com/animated-bezier/

Heat engine


Paths

https://github.com/Elteoremadebeethoven/AnimationsWithManim/blob/master/English/extra/faqs/paths.md

  • Show Points
  • Path as corners
  • Path smoothly
  • Bezier points of a path
  • Change path style


Lines

https://github.com/markkuleino/manim/blob/main/FLL_CC2_m13b.py

Camera

Animate camera movement while animating sine wave (plane wave).

surface = always_redraw(
     lambda: Surface(
         lambda u, v: np.array([ u, v, np.sin(u - phase.get_value()) ]),
         u_range=[-50, 50],
         v_range=[-5, 5],
         resolution=(15, 15),
         fill_opacity=0.8,
         #checkerboard_colors=[ BLUE_D, BLUE_E, ],
     ).set_fill_by_value(
         axes=axes,
         colors=[ (BLUE_E, -1), (BLUE, 0), (YELLOW, 1), ]
    )
)

phi, theta, focal_distance, gamma, zoom = self.camera.get_value_trackers()

#1. Zoom away
self.play(
   zoom.animate.set_value(0.5),
   phase.animate.set_value(8 * np.pi),
   run_time=10,
   rate_func=linear,
)

The camera parameters

  • phi Vertical angle (elevation)
  • theta Horizontal angle (azimuth/orbit angle)
  • focal_distance Perspective strength
  • gamma Roll (rotation around viewing axis)
  • zoom Zoom factor

Plane wave

from manim import *
from manim import smooth
import numpy as np

class PlaneWave3D(ThreeDScene):
    def construct(self):
        axes = ThreeDAxes( x_range=[-5, 5, 1], y_range=[-5, 5, 1], z_range=[-2, 2, 1],)

        self.set_camera_orientation(
            phi=65 * DEGREES,
            theta=-45 * DEGREES,
            zoom=2,
        )
        phase = ValueTracker(0)
        x_pos = ValueTracker(20)  # start at x=10 (right)

        surface = always_redraw(
            lambda: Surface(
                lambda u, v: np.array([ u, v, 1*np.sin(u - phase.get_value()) ]),
                u_range=[-50, 50],
                v_range=[-5, 15],
                resolution=(50, 50),
                fill_opacity=1.0,
                #checkerboard_colors=[ BLUE_D, BLUE_E, ],
            ).set_fill_by_value(
                axes=axes,
                colors=[ (BLUE_E, -1), (BLUE, 0), (YELLOW, 1), ]
            )
        )

        gap = 0.02
        antenna = always_redraw(
            lambda: VGroup(
                Line(
                    start=[x_pos.get_value(), 0, -1],  # bottom of screen
                    end=[x_pos.get_value(), 0, -gap],     # top of screen
                    color=YELLOW,
                    stroke_width=8
                ),
                Line(
                    start=[x_pos.get_value(), 0, gap],  # bottom of screen
                    end=[x_pos.get_value(), 0, 1],     # top of screen
                    color=YELLOW,
                    stroke_width=8
                ),

        ))

        amplitude = 0.5
        antenna_sine = always_redraw(
            lambda: VGroup(
                VMobject().set_points_smoothly([
                    np.array([
                        x_pos.get_value() + amplitude*np.sin( np.pi*
                                                           (z-gap)/(2-gap)*np.cos(phase.get_value())),
                        0,                   # fixed y
                        z
                    ])
                    for z in np.linspace(gap, 1, 50)
                ]),
                VMobject().set_points_smoothly([
                    np.array([
                        x_pos.get_value() - amplitude*np.sin( np.pi + np.pi*
                                                           (z-gap)/(2-gap)*np.cos(phase.get_value())),
                        0,                   # fixed y
                        z
                    ])
                    for z in np.linspace(-gap, -1, 50)
                ]),

        ))
        #self.play(Create(axes))
        self.play(Create(surface))

        #self.begin_ambient_camera_rotation( rate=0.15, about='theta')
        #self.begin_ambient_camera_rotation( rate=0.15, about='phi')
        #self.begin_ambient_camera_rotation( rate=0.15, about='gamma')
        #self.play(
        #    phase.animate.set_value(8 * np.pi), run_time=10, rate_func=linear,
        #)
        #self.stop_ambient_camera_rotation( about='theta')
        #self.stop_ambient_camera_rotation( about='pi')
        #self.stop_ambient_camera_rotation( about='gamma')
        #self.wait()


        phi, theta, focal_distance, gamma, zoom = self.camera.get_value_trackers()

        #2. Slow down
        current = phase.get_value()
        self.play(
            zoom.animate.set_value(0.5),
            theta.animate.set_value(-10*DEGREES),
            phase.animate.set_value(current + 20),
            run_time=4,
            #rate_func=lambda t: 1 - (1-t)**2,
            rate_func = linear
        )
        #2. Ramp the
        cruise_speed = 2.0  # phase units per second
        phase.add_updater(
            lambda m, dt: m.increment_value(cruise_speed * dt)
        )
        self.wait(10)
        phase.clear_updaters()

        #3 Add the antenna
        #Add the sinewave
        self.add(antenna)
        current = phase.get_value()
        self.play(
            phase.animate.set_value(current+20),
            theta.animate.set_value(-90*DEGREES),
            phi.animate.set_value(90*DEGREES),
            x_pos.animate.set_value(0),  # move to x=-10 (left)
            run_time=10,
            rate_func=linear,
        )


        self.add(antenna_sine)
        self.play(
            zoom.animate.set_value(3),
            phase.animate.set_value(current+40),
            run_time=10,
            rate_func=linear,
        )