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