Graphing Functions – manim Series: Part 7

The post is part of a series on learning how to use manim.  You can find the previous tutorial post in this series here and the overview of the entire series here.

Important Note:  These posts are based on an earlier version of manim which uses Python 2.7.  The latest version of manim is using Python 3.  To follow along with these posts, use Python 2.7 and the May 9, 2018 commit of manim .

7.0 Graphing Functions

The easiest way to plot functions is to base your scene class on the GraphScene(). The scene creates a set of axes and has methods for creating graphs. One thing that confused me a little at first is that the axes belong to your scene class so you will need to use self to access the methods related to the axes. This caused me a few issues when I started out.

We will start off by looking at how to create the axes and graphs but we will come back to look at the CONFIG{} dictionary, which is used frequently in manim for initializing many of the class variables.

class PlotFunctions(GraphScene):
CONFIG = {
"x_min" : -10,
"x_max" : 10,
"y_min" : -1.5,
"y_max" : 1.5,
"graph_origin" : ORIGIN ,
"function_color" : RED ,
"axes_color" : GREEN,
"x_labeled_nums" :range(-10,12,2),

}
def construct(self):
self.setup_axes(animate=True)
func_graph=self.get_graph(self.func_to_graph,self.function_color)
func_graph2=self.get_graph(self.func_to_graph2)
vert_line = self.get_vertical_line_to_graph(TAU,func_graph,color=YELLOW)
graph_lab = self.get_graph_label(func_graph, label = "\\cos(x)")
graph_lab2=self.get_graph_label(func_graph2,label = "\\sin(x)", x_val=-10, direction=UP/2)
two_pi = TexMobject("x = 2 \\pi")
label_coord = self.input_to_graph_point(TAU,func_graph)
two_pi.next_to(label_coord,RIGHT+UP)

self.play(ShowCreation(func_graph),ShowCreation(func_graph2))
self.play(ShowCreation(vert_line), ShowCreation(graph_lab), ShowCreation(graph_lab2),ShowCreation(two_pi))

def func_to_graph(self,x):
return np.cos(x)

def func_to_graph2(self,x):
return np.sin(x)

self.setup_axes() will create a set of axes on screen. With the exception of whether the creation is animated or not, all other variables for the axes are set using CONFIG{}, which I’ll explain in a bit. The default values for the GraphScene() are shown below:

CONFIG = {
"x_min": -1,
"x_max": 10,
"x_axis_width": 9,
"x_tick_frequency": 1,
"x_leftmost_tick": None, # Change if different from x_min
"x_labeled_nums": None,
"x_axis_label": "$x$",
"y_min": -1,
"y_max": 10,
"y_axis_height": 6,
"y_tick_frequency": 1,
"y_bottom_tick": None, # Change if different from y_min
"y_labeled_nums": None,
"y_axis_label": "$y$",
"axes_color": GREY,
"graph_origin": 2.5 * DOWN + 4 * LEFT,
"exclude_zero_label": True,
"num_graph_anchor_points": 25,
"default_graph_colors": [BLUE, GREEN, YELLOW],
"default_derivative_color": GREEN,
"default_input_color": YELLOW,
"default_riemann_start_color": BLUE,
"default_riemann_end_color": GREEN,
"area_opacity": 0.8,
"num_rects": 50,
}

With our example we have changed x_min, x_max, y_min, y_max, graph_origin, axes_color, and x_labeled_num. The values assigned in our class take priority over values set by the parent class. Every value that we don’t change is automatically assigned the value defined in the parent class. The x_labeled_num property takes a list of numbers for labels along the x-axis. We’ve used range(-10,12,2) to generate a list of values from -10 to +10 in steps of 2. One issue I’ve noted with the y-axis is that setting the min and max values along either axis to numbers that are not integer multiples of 0.5 results in the tick marks along that axis not being symmetric about zero (e.g. try y_min = -1.2 and y_max = 1.2). I’m not sure what that is about but it isn’t a problem if you stick to integer multiples of 0.5 you don’t have any problems.

Once you have the axes set up you can use self.get_graph() to graph a function. The argument of get_graph() needs to be a pointer to a function, rather than a call to the function itself. In other words, since one of my functions is func_to_graph() I should use self.get_graph(func_to_graph) without any parenthese after func_to_graph.

Rather than defining separate functions for graphing we could use lambda functions. For example, if I define self.func = lambda x: np.cos(x) and then use self.get_graph(self.func) I will get the same result.

With get_graph() you do need to expicitly pass arguments rather than using CONFIG{}. The possible arguments, in addition to the function to graph, are color, x_min, and x_max. If you don’t specify a color GraphScene will cylce through BLUE, GREEN, and YELLOW for successive graphs. Since I didn’t specify a color for my second graph it was automatically assigned the first color, BLUE.

There is a handy method to draw a vertical line from the x-axis to the graph called get_vertical_line_to_graph(). I love that the method naming convention is descriptive enought that you can see what each method does at a glance. Good job, Grant! The arguments for get_vertical_line_to_graph() are the x-value where you want the line and the particular graph you want the line drawn to. Note that get_vertical_line_to_graph() is a method of the GraphScene and not the graph or axes so it is called with self.get_vertical_line_to_graph().

You can label graphs using get_graph_label() to set the text associated with the graph. This is similar to the get_text() method of the Braces() class in that it creates a texmobject at a specific location but does not draw it on the screen; you need to add or play to show the label. The arguments for get_graph_label() are the particular graph you want to add a label to and the text for the label. If you don’t specify an x-value and/or direction the label is placed at the end of the graph. The direction specifies where, relative to the x_value you want the label placed.

There are several other methods associated with the GraphScene() that are worth looking at, but I found the input_to_graph_point() helpful. By specifying an x-value on the graph, this method will return the coordinate on the screen where that graph point lies. This is handy if you want to place some text or other mobject to call out a particular point on a graph.

7.1 The CONFIG{} Dictionary

Whenever a scene or mobject are created a method called digest_config() gets called. This method starts with the class you defined and looks for a dictionary called self.CONFIG and compiles a list of all entries in the dictionary. It then goes to the parent class and looks for self.CONFIG there and adds those entries. If the method comes across keys that have already been found, it ignores the values from the parent class. digest_config() keeps traveling up the hierarchy to the top parent class, with is Container(). Each entry in this dictionary is then assigned a class variable based on the key and value. Thus the dictionary entry "x_min" : -1 becomes self.x_min = -1 and so on. Each dictionary entry becomes a class variable that can be accessed by the methods within the class. Understanding all of the CONFIG{} entries for a class is crucial to getting the most out of manim. For example, GraphScene() has the following CONFIG{} entries:

<br />class GraphScene(Scene):
CONFIG = {
"x_min": -1,
"x_max": 10,
"x_axis_width": 9,
"x_tick_frequency": 1,
"x_leftmost_tick": None, # Change if different from x_min
"x_labeled_nums": None,
"x_axis_label": "$x$",
"y_min": -1,
"y_max": 10,
"y_axis_height": 6,
"y_tick_frequency": 1,
"y_bottom_tick": None, # Change if different from y_min
"y_labeled_nums": None,
"y_axis_label": "$y$",
"axes_color": GREY,
"graph_origin": 2.5 * DOWN + 4 * LEFT,
"exclude_zero_label": True,
"num_graph_anchor_points": 25,
"default_graph_colors": [BLUE, GREEN, YELLOW],
"default_derivative_color": GREEN,
"default_input_color": YELLOW,
"default_riemann_start_color": BLUE,
"default_riemann_end_color": GREEN,
"area_opacity": 0.8,
"num_rects": 50,
}

The parent class for GraphScene() has the following dictionary:

class Scene(Container):
CONFIG = {
"camera_class": Camera,
"camera_config": {},
"frame_duration": LOW_QUALITY_FRAME_DURATION,
"construct_args": [],
"skip_animations": False,
"ignore_waits": False,
"write_to_movie": False,
"save_frames": False,
"save_pngs": False,
"pngs_mode": "RGBA",
"movie_file_extension": ".mp4",
"name": None,
"always_continually_update": False,
"random_seed": 0,
"start_at_animation_number": None,
"end_at_animation_number": None,
}

Container(), the parent to Scene as well as Mobject, has no CONFIG{} entries.

When talking about mobjects, the list of CONFIG{} entries can get a little long. I won’t go into those right now but it is worth you time to take a look at the hierarchy of some of the mobject subclasses to see what all the properties you can control are.

 

Next time we’ll take a look at more graphing.

Advertisements
This entry was posted in Just for Fun, Programming and tagged , , , . Bookmark the permalink.

3 Responses to Graphing Functions – manim Series: Part 7

  1. Pingback: Aligning Text – manim Series: Part 6 | Talking Physics

  2. Pingback: Learning How To Animate Videos Using manim Series – A Journey | Talking Physics

  3. Pingback: More Graphing – manim Series: Part 8 | Talking Physics

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.