Short introduction to Manim: Difference between revisions

From wikiluntti
 
(31 intermediate revisions by the same user not shown)
Line 1: Line 1:
== Introduction ==
== Introduction ==
Manim or manimgl.
Virtual engine
<syntaxhighlight lang="bash">
python -m venv .
source bin/activate
</syntaxhighlight>
For faster rendering use smaller resolution <code>resolution=(15,15)</code> and smaller fps rate
<syntaxhighlight lang="bash">
manim -pql --fps 10 ma_animatedplanewave.py PlaneWave3D
</syntaxhighlight >


Some short manim scrips to generate easy visualizations.
Some short manim scrips to generate easy visualizations.
<syntaxhighlight lang="bash">
manim shorts.py MyScene -r 1080,1920
</syntaxhighlight >


<pre>
<pre>
Line 7: Line 23:
manim -pqh bez2.py BezierSplineExample
manim -pqh bez2.py BezierSplineExample


  --format=png
   f: open the directory/ folder
   f: open the directory/ folder
   i: generate gif file
   i: generate gif file
Line 13: Line 30:
   qh: high quality
   qh: high quality
   s: save last frame
   s: save last frame
  t: transparent background
</pre>
</pre>
<pre>
config.background_color = WHITE
</pre>
Add a background image
<pre>
bg_image = ImageMobject("bg_P5.png")
self.add( bg_image )
</pre>
Add the background plane
<pre>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 )
</pre>
=== Install Manim on Arch Linux ===
Pip is not working on a virtual environment. However use <code>uv</code> to install it. That works (on Sep 2024):
<code>
uv pip install manim
</code>
It is now installed on virtual environment, but the global system is not affected (not working outside virtual environment).
== BG plane ==
<syntaxhighlight lang="python">
      number_plane = NumberPlane(
            background_line_style={
                "stroke_color": TEAL,
                "stroke_width": 1,
                "stroke_opacity": 0.2
            }
        )
        self.add(number_plane)
</syntaxhighlight >


== Bezier curve ==
== Bezier curve ==


Bezier curve with two points:  
Animated bezier curve with two points:  


[[File:Bez2.svg|thumb]]
[[File:Bez2.svg|thumb]]
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 ==
<gallery>
CarnotCycle.gif|Carnot cycle. Remove bg.
Ottocycle.gif|Otto cycle. Remove bg.
DieselCycle.gif|Diesel cycle. Remove bg.
</gallery>
*[[File:Carnot.zip]]
*[[File:otto.zip]]
*[[File:diesel.zip]]
== 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).
<syntaxhighlight lang="python">
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,
)
</syntaxhighlight >
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 ==
<syntaxhighlight lang="python">
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,
        )
</syntaxhighlight>

Latest revision as of 14:56, 13 July 2026

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,
        )