How to FadeOut the axes in GraphScene? - manim

In GraphScene, using setup_axes to setup the axes, How to FadeOut the axes to prepare the room for other animation?
After setup_axes, I try to change the graph_origin to move the axes, also failed.

Technically you can do it using the self.axes object:
class Plot(GraphScene):
CONFIG = {
"y_max" : 50,
"y_min" : 0,
"x_max" : 7,
"x_min" : 0,
"y_tick_frequency" : 5,
"x_tick_frequency" : 0.5,
}
def construct(self):
self.setup_axes()
graph = self.get_graph(lambda x : x**2,
color = GREEN,
x_min = 0,
x_max = 7
)
self.play(
ShowCreation(graph),
run_time = 2
)
self.wait()
self.play(FadeOut(self.axes))
self.wait()
But, GraphScene was intended to be used once for each axes (you can create multiple graphs on the axes, but not change the axes), if you are going to be changing them then use Scene, here is an example:
class Plot2(Scene):
def construct(self):
c1 = FunctionGraph(lambda x: 2*np.exp(-2*(x-1)**2))
c2 = FunctionGraph(lambda x: x**2)
axes1=Axes(y_min=-3,y_max=3)
axes2=Axes(y_min=0,y_max=10)
self.play(ShowCreation(axes1),ShowCreation(c1))
self.wait()
self.play(
ReplacementTransform(axes1,axes2),
ReplacementTransform(c1,c2)
)
self.wait()
However, in case you want to make a very personalized graph, you will have to add more options to the axes, and the axes are created using NumberLine. This is not so easy to do but you can use the manimlib/scene/graph_scene.py example to guide you, the Axes code is in manimlib/mobject/coordinate_systems.py, the NumberLine code is in manimlib/mobject/number_line.py and the FunctionGraph code is in manimlib/mobject/functions.py to see more options.

Related

NumberPlane grid not showing ManimGL Scene

I recently started using ManimGL version 1.6.1
When I ran the "OpeningManimExample" scene from the example_scenes.py,
there should be a grid in the scene. But it shows only the labels, there are no grid lines.
class OpeningManimExample(Scene):
def construct(self):
intro_words = Text("""
The original motivation for manim was to
better illustrate mathematical functions
as transformations.
""")
intro_words.to_edge(UP)
self.play(Write(intro_words))
self.wait(2)
# Linear transform
grid = NumberPlane((-10, 10), (-5, 5))
matrix = [[1, 1], [0, 1]]
linear_transform_words = VGroup(
Text("This is what the matrix"),
IntegerMatrix(matrix, include_background_rectangle=True),
Text("looks like")
)
linear_transform_words.arrange(RIGHT)
linear_transform_words.to_edge(UP)
linear_transform_words.set_stroke(BLACK, 10, background=True)
self.play(
ShowCreation(grid),
FadeTransform(intro_words, linear_transform_words)
)
self.wait()
self.play(grid.animate.apply_matrix(matrix), run_time=3)
self.wait()
# Complex map
c_grid = ComplexPlane()
moving_c_grid = c_grid.copy()
moving_c_grid.prepare_for_nonlinear_transform()
c_grid.set_stroke(BLUE_E, 1)
c_grid.add_coordinate_labels(font_size=24)
complex_map_words = TexText("""
Or thinking of the plane as $\\mathds{C}$,\\\\
this is the map $z \\rightarrow z^2$
""")
complex_map_words.to_corner(UR)
complex_map_words.set_stroke(BLACK, 5, background=True)
self.play(
FadeOut(grid),
Write(c_grid, run_time=3),
FadeIn(moving_c_grid),
FadeTransform(linear_transform_words, complex_map_words),
)
self.wait()
self.play(
moving_c_grid.animate.apply_complex_function(lambda z: z**2),
run_time=6,
)
self.wait(2)
Output is like this:
OpeningManimExample
I also found similar problem in Flash, FlashAround animations.

Draw a line chart based on user input

Please help me to draw a simple line chart in python turtle module based on user input. I am able to draw the x and y axis. The x-axis tick is equal to the number of data points. For example, if the user input is 5 then it will be 5 ticks labelled 1 to 5 and so on. However, the tick for y-axis has 6 ticks(fixed) labelled from 0 to 20, increment in 4(0,4,8..20).
I can't figure out the logic for plotting y values based on user input. For example, if the user types 15, it needs to go to the corresponding data point in y axis and put a dot in there. Similarly, if the user types 5 it needs to go the corresponding value in y axis as well plus connecting to the preceding data point(in this case connecting 15 from previous point to 5 in the current point) and so on based on user input. Also, my x and y labeling could be done it in a better way. Here is my code. Any help is much appreciated. Please look at the image description, I am aiming for similar results.
import turtle as t
import time
wn = t.Screen() # create a window for the turtle to draw on
wn.title("Turtle Demo") # the title to show at the top of the window
WINDOW_WIDTH = 500 # size constants for easy changing
WINDOW_HEIGHT = 500
wn.setup(WINDOW_WIDTH, WINDOW_HEIGHT, 200, 10) # specify window size (width, height)
user_input = t.numinput(title='Line graph', prompt='How many data points:')
x_max = user_input
# print(userInput)
# graph window
x_min = 0
y_min = 0
y_max = 5
# tick info
t_l = 0.1
x_t_space = 1
y_t_space = 1
wn.setworldcoordinates(x_min, y_min, x_max, y_max)
# Draw x-axis
t.tracer(10)
t.hideturtle()
t.up()
t.goto(x_min, 0)
t.down()
t.goto(user_input, 0.0)
t.up()
# Draw the y-axis
t.goto(0.0, y_min)
t.down()
t.goto(0.0, y_max)
t.up()
# Draw the x tick marks
n_x_ticks = int((x_max - x_min) / x_t_space) + 1
for tick in range(n_x_ticks):
loc = x_min + tick * x_t_space
t.up()
t.goto(loc, -t_l * 0.4)
t.down()
t.goto(loc, t_l * 0.4)
t.up()
t.write(tick)
# Draw the y tick marks
y_label = range(0, 21, 4)
n_y_ticks = int((y_max - y_min) / y_t_space) + 1
for tick in range(n_y_ticks):
loc = y_min + tick * y_t_space
t.up()
t.goto(-t_l * 0.4, loc)
t.down()
t.goto(t_l * 0.4, loc)
for i in y_label:
tick = i
print(tick)
t.write(tick, align="left", font=("Arial", 8, "normal"))
# get user input and plot the y value as long as user needed.
# the below code marks the value in the x-axis itself, that is not what I want.
#I want the value to get plotted outside the axis as in the normal line chart
t.backward
t.goto(0, 0)
t.tiltangle(45)
user_value = t.numinput(title='Line graph', prompt='Enter the first value:')
t.shape("square")
t.stamp()
t.forward(user_value)
user_value = t.numinput(title='Line graph', prompt='Enter the next value:')
t.shape("square")
t.stamp()
t.forward(user_value)
user_value = t.numinput(title='Line graph', prompt='Enter the next value:')
t.shape("square")
t.stamp()
t.forward(user_value)
Line chart
The input size is defined by the user and you want the same size of y-values.
Do the following:
for i in range (0, int(user_input)):
user_value = t.numinput(title='Line graph', prompt='Enter the value:')
t.shape("square")
t.stamp()
t.forward(user_value)
EDIT:
Just use matplotlib, it is very simple
import numpy as np
import matplotlib.pyplot as plt
user = int(input("Enter number of data: "))
x = np.arange(user)
y = []
for i in range (0, user):
value = int(input("Enter value: "))
y.append(value)
y = np.array(y)
plt.scatter(x, y)
plt.plot(x, y)
plt.show()
Input:
Enter number of data: 9
Enter value: 10
Enter value: 30
Enter value: 20
Enter value: 50
Enter value: 60
Enter value: 35
Enter value: 38
Enter value: 12
Enter value: 31
Output:
which is the same as the chart you provided.

Manim Zoom Not Preserving Line Thickness?

Here is a modified version of some example code, which is from the bottom of this page. The only change is that I've added a line:
from manimlib.imports import *
class ZoomedSceneExample(ZoomedScene):
CONFIG = {
"zoom_factor": 0.3,
"zoomed_display_height": 1,
"zoomed_display_width": 6,
"image_frame_stroke_width": 20,
"zoomed_camera_config": {
"default_frame_stroke_width": 3,
},
}
def construct(self):
# Set objects
dot = Dot().shift(UL*2)
a_line = Line((0,0,0),(1,1,0)).shift(UL*2) ## THIS IS THE ONLY CHANGE
self.add(a_line) ## TO THE EXAMPLE CODE
image=ImageMobject(np.uint8([[ 0, 100,30 , 200],
[255,0,5 , 33]]))
image.set_height(7)
frame_text=TextMobject("Frame",color=PURPLE).scale(1.4)
zoomed_camera_text=TextMobject("Zommed camera",color=RED).scale(1.4)
self.add(image,dot)
# Set camera
zoomed_camera = self.zoomed_camera
zoomed_display = self.zoomed_display
frame = zoomed_camera.frame
zoomed_display_frame = zoomed_display.display_frame
frame.move_to(dot)
frame.set_color(PURPLE)
zoomed_display_frame.set_color(RED)
zoomed_display.shift(DOWN)
# brackground zoomed_display
zd_rect = BackgroundRectangle(
zoomed_display,
fill_opacity=0,
buff=MED_SMALL_BUFF,
)
self.add_foreground_mobject(zd_rect)
# animation of unfold camera
unfold_camera = UpdateFromFunc(
zd_rect,
lambda rect: rect.replace(zoomed_display)
)
frame_text.next_to(frame,DOWN)
self.play(
ShowCreation(frame),
FadeInFromDown(frame_text)
)
# Activate zooming
self.activate_zooming()
self.play(
# You have to add this line
self.get_zoomed_display_pop_out_animation(),
unfold_camera
)
zoomed_camera_text.next_to(zoomed_display_frame,DOWN)
self.play(FadeInFromDown(zoomed_camera_text))
# Scale in x y z
scale_factor=[0.5,1.5,0]
# Resize the frame and zoomed camera
self.play(
frame.scale, scale_factor,
zoomed_display.scale, scale_factor,
FadeOut(zoomed_camera_text),
FadeOut(frame_text)
)
# Resize the frame
self.play(
frame.scale,3,
frame.shift,2.5*DOWN
)
# Resize zoomed camera
self.play(
ScaleInPlace(zoomed_display,2)
)
self.wait()
self.play(
self.get_zoomed_display_pop_out_animation(),
unfold_camera,
# -------> Inverse
rate_func=lambda t: smooth(1-t),
)
self.play(
Uncreate(zoomed_display_frame),
FadeOut(frame),
)
self.wait()
When I render this with Manim, I find that the zooming does not work as I expected. Here is a screenshot from the video:
When not zoomed in, the line is quite thick. But in the zoomed in display, the line appears significantly thinner. Why is this? Can I fix this effect, so that line thickness scales properly with zoom?
Try this:
# Set camera
zoomed_camera = self.zoomed_camera
zoomed_camera.cairo_line_width_multiple = 0.1 # <-
The thickness is calculated in this way.

How to apply multiple animations for one geometry at the same time

There is a square location in the ORIGIN, I want to move it to UP*3 and scale to 0.5 at the same with below code snippet:
sq = Square()
self.add(sq)
self.play(ApplyMethod(sq.scale, 0.5), ApplyMethod(sq.move_to, UP*3), run_time=5)
However, the first one is skipped, only the last one works.
I know creating another small square and using transform can do, but that will bring more code, is there simple solution for this? thanks!
There are 3 ways:
class MultipleMethods1(Scene):
def construct(self):
sq = Square()
self.add(sq)
self.play(
sq.scale, 0.5,
sq.move_to, UP*3,
run_time=5
)
self.wait()
class MultipleMethods2(Scene):
def construct(self):
sq = Square()
cr = Circle()
VGroup(sq,cr).arrange(RIGHT)
self.add(sq)
def apply_function(mob):
mob.scale(0.5)
mob.shift(UP*3)
return mob
self.play(
ApplyFunction(apply_function,sq),
ApplyFunction(apply_function,cr),
run_time=5
)
self.wait()
class MultipleMethods3(Scene):
def construct(self):
sq = Square()
self.add(sq)
sq.generate_target()
sq.target.scale(0.5)
sq.target.move_to(UP*3)
self.play(
MoveToTarget(sq),
run_time=5
)
self.wait()

The line rotate with unexpected scaling

The scene is simple, one Line and rotate PI/2 with below code:
ln = Line(ORIGIN, RIGHT*2)
self.add(ln)
self.wait()
self.play(ApplyMethod(ln.rotate, PI/2, OUT))
However, during the rotating, seemingly scaling at the same time, I check the axis is [0 0 1] that is z axis, I suppose the length of the line should be kept unchanged.
How to prevent the line from scaling? Thanks!
Use Rotate or Rotating, see this.
class RotateVector(Scene):
def construct(self):
coord_start=[1,1,0]
coord_end=[2,3,0]
dot_start=Dot().move_to(coord_start)
dot_end=Dot().move_to(coord_end)
vector=Arrow(coord_start,coord_end,buff=0)
vector.set_color(RED)
self.add(dot_start,dot_end)
self.play(GrowArrow(vector))
self.play(
Rotating(
vector,
radians=PI*2,
about_point=coord_start,
rate_func=smooth,
run_time=1
)
)
self.wait()
self.play(
Rotating(
vector,
radians=PI*2,
about_point=coord_end,
rate_func=linear,
run_time=1
)
)
self.wait()
Edits
You can create a custom animation:
class RotatingAndMove(Animation):
CONFIG = {
"axis": OUT,
"radians": TAU,
"run_time": 5,
"rate_func": linear,
"about_point": None,
"about_edge": None,
}
def __init__(self, mobject, direction,**kwargs):
assert(isinstance(mobject, Mobject))
digest_config(self, kwargs)
self.mobject = mobject
self.direction = direction
def interpolate_mobject(self, alpha):
self.mobject.become(self.starting_mobject)
self.mobject.rotate(
alpha * self.radians,
axis=self.axis,
about_point=self.about_point,
about_edge=self.about_edge,
)
self.mobject.shift(alpha*self.direction)
class NewSceneRotate(Scene):
def construct(self):
arrow=Vector(UP)
arrow.to_corner(UL)
self.play(GrowArrow(arrow))
self.play(
RotatingAndMove(arrow,RIGHT*12+DOWN*4)
)
self.wait()
Or you can use UpdateFromAlphaFunc:
class NewSceneRotateUpdate(Scene):
def construct(self):
arrow=Vector(UP)
arrow.to_corner(UL)
direction=RIGHT*12+DOWN*4
radians=TAU
arrow.starting_mobject=arrow.copy()
def update_arrow(mob,alpha):
mob.become(mob.starting_mobject)
mob.rotate(alpha*radians)
mob.shift(alpha*direction)
self.play(GrowArrow(arrow))
self.play(
UpdateFromAlphaFunc(arrow,update_arrow,rate_func=linear,run_time=5)
)
self.wait()
Something that should be very clear is that when you define an update function, it is not the same to use dt as alpha. That is, it is not the same to define
def update_function(mob,dt)
as
def update_function(mob,alpha)
dt varies with the fps of the video, and is calculated as follows:
dt = 1/self.camera.frame_rate
# You don't have to calculate it, manim already does it by default,
# I just write it so you know where it comes from.
Where self refers to the Scene class.
And alpha varies from 0 to 1, in fact, you can write the previus scene with this update method and get the same result:
def update_arrow(mob,dt):
alpha=interpolate(0,1,dt)
mob.become(mob.starting_mobject)
mob.rotate(alpha*radians)
mob.shift(alpha*direction)
This can be useful if you want alpha to vary in different intervals, use
alpha=interpolate(alpha_start,alpha_end,dt)

Resources