MovingCameraScene & ZoomedCameraScene#

[1]:
from manim import *
config.media_embed = True; config.media_width = "100%"
_RV = "-v WARNING -qm --progress_bar None --disable_caching Example"
_RI = "-v WARNING -s --progress_bar None --disable_caching Example"
Manim Community v0.15.1

MovingCameraScene#

If we need to move the camera there is a subclass of Scene that helps us do that.

To manipulate the camera we use Scene.camera.frame, and we can manipulate it like almost any other Mobject, except we can’t change its color or rotate it, but we can move it, resize it, use it in animations, and so on. even add updaters.

[3]:
class Example(MovingCameraScene):
    def setup(self):
        NP = NumberPlane(axis_config={"include_numbers":True})
        rec = Rectangle(height=2.5, width=2.5 * 16/9)
        rec.move_to(NP.c2p(-3,2))
        dot = Dot(NP.c2p(4,1.5))
        path = Line(LEFT*3,RIGHT*3)
        path.points[1] += UP*5
        path.points[-2] += DOWN*5
        path.to_edge(DOWN,buff=2)
        self.add(NP,rec,dot,path)

    def construct(self):
        _,rec,dot,path = self.mobjects
        frame = self.camera.frame
        frame.save_state()

        self.wait()
        self.play(
            frame.animate
                .match_height(rec)
                .move_to(rec.get_center()),
            run_time=3
        )
        self.wait()
        self.play(
            frame.animate
                .move_to(dot.get_center()),
            run_time=3
        )
        self.wait()
        self.play(
            Succession(
                frame.animate(run_time=2).move_to(path.get_start()),
                MoveAlongPath(frame,path,run_time=3)
            ),
        )
        self.wait()
        self.play(frame.animate.move_to(dot))

        frame.add_updater(lambda mob: mob.move_to(dot))
        self.play(dot.animate.shift(LEFT*2))
        self.play(dot.animate.shift(DOWN*4))
        self.wait()
        frame.clear_updaters()
        self.play(Restore(frame,run_time=3))
        self.wait()

%manim $_RV

There is not much more to add about MovingCameraScene.

ZoomedScene#

In case we want to zoom the screen we can use ZoomedScene.

Definitions#

ZoomedScene is made up of 3 main parts:

  • frame: The rectangle that marks the area of ​​the screen in which the zoom is performed.

  • zoomed_display: The rectangle that shows zoomed objects.

  • zoomed_display_border: It is the border of the zoomed_display

The zoomed_display_border is included in the zoomed_display, so if you move the zoomed_display you will also move the zoomed_display_border. We can change this property of the zoomed_display in ZoomedScene.zoomed_camera.cairo_line_width_multiple.

However, we can’t animate the change of a non-Mobject property, but we can use updaters to do this, this is quite a useful technique when you need to animate the change of a non-Mobject property.

The methods to highlight are the following:

  • ZoomedScene.activate_zooming): Move frame and zoomed_display to the top of the layers.

  • ZoomedScene.get_zoomed_display_pop_out_animation(): Returns the bring zoomed_display from frame animation

PRO_TIP#

Something important to note is that the thickness of the lines increases in the zoomed_display.

The camera (in all Scene classes) is defined by the Camera.cairo_line_width_multiple property, defaults to 0.01.

When we define mob.set_stroke(width=3), that 3 means 3 * 0.01 screen units, if we increase Camera.cairo_line_width_multiple then the lines will look thicker.

[4]:
class Example(ZoomedScene):
    def __init__(self, **kwargs):
        # Frame == Purple
        # zoomed_display == RED
        ZoomedScene.__init__(
            self,
            zoom_factor=0.3,
            zoomed_display_height=1,
            zoomed_display_width=6,
            zoomed_camera_config={
                "default_frame_stroke_width": 3, # frame border
            },
            **kwargs
        )

    def construct(self):
        # Define zoom mobs
        zoomed_display = self.zoomed_display
        zoomed_display_border = zoomed_display.display_frame
        frame = self.zoomed_camera.frame
        # set zoom mobs props
        dot = Dot([4,1.5,0])
        frame.move_to(dot)
        frame.set_color(PURPLE) # border
        zoomed_display_border.set_color(RED)
        zoomed_display.move_to(ORIGIN)
        # set mobs
        frame_text = Text("frame", color=PURPLE, font_size=67)\
            .add_background_rectangle()
        zoomed_camera_text = Text("zoomed_display", color=RED, font_size=67)\
            .add_background_rectangle()
        frame_text.next_to(frame, DOWN)
        zoomed_camera_text.next_to(zoomed_display, DOWN)
        NP = NumberPlane(axis_config={"include_numbers":True})
        self.add(NP, dot)
        # Create frame
        self.play(Create(frame), FadeIn(frame_text, shift=UP))
        self.activate_zooming()
        # bring zoomed_display from frame
        self.play(self.get_zoomed_display_pop_out_animation())
        self.play(FadeIn(zoomed_camera_text, shift=UP))
        self.wait()
        # Scale in        x   y   z
        scale_factor = [0.5, 1.5, 0]
        self.play(
            frame.animate.scale(scale_factor),
            zoomed_display.animate.scale(scale_factor).shift(DOWN),
            FadeOut(zoomed_camera_text),
            FadeOut(frame_text)
        )
        self.wait()
        self.play(ScaleInPlace(zoomed_display, 2))
        self.wait()
        self.play(frame.animate.shift(1.5 * DOWN).scale(1.5))
        self.wait()
        self.play(self.change_stroke_width_zoomed_display(0.03,run_time=2))
        self.wait()
        self.play(self.change_stroke_width_zoomed_display(0.01,run_time=2))
        self.wait()
        # restore zoomed_display to frame
        self.play(self.get_zoomed_display_pop_out_animation(), rate_func=lambda t: smooth(1 - t))
        self.play(Uncreate(zoomed_display_border), FadeOut(frame))
        self.wait()

    def change_stroke_width_zoomed_display(self, target_stroke_width, **kwargs):
        init_stroke = self.zoomed_camera.cairo_line_width_multiple
        def update(mob, alpha):
            stroke = interpolate(
                init_stroke, target_stroke_width, alpha
            )
            self.zoomed_camera.cairo_line_width_multiple = stroke
        return UpdateFromAlphaFunc(Mobject(), update, **kwargs)

%manim $_RV