*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 .**

# 9.0 Vector Fields

Before diving into draw a vector field, we should set up a Cartesian axes using `NumberPlane()`

. This gives you two axes and an underlying grid. The `CONFIG{}`

for the `NumberPlane()`

class is

class NumberPlane(VMobject): CONFIG = { "color": BLUE_D, "secondary_color": BLUE_E, "axes_color": WHITE, "secondary_stroke_width": 1, "x_radius": None, "y_radius": None, "x_unit_size": 1, "y_unit_size": 1, "center_point": ORIGIN, "x_line_frequency": 1, "y_line_frequency": 1, "secondary_line_ratio": 1, "written_coordinate_height": 0.2, "propagate_style_to_family": False, "make_smooth_after_applying_functions": True, }

You can change any of these default values by passing a dictionary with new values as keyword arguments. For example, if you want to change the spacing of the grid lines you could change `x_line_frequency`

and `y_line_frequency`

by defining a dictionary with these variables and then passing the dictionary to `NumberPlane()`

. If you want to see the x-axis and y-axis indicated you can use `get_axis_labels()`

to draw an `x`

and a`y`

next to the appropriate axis. See the code below.

class DrawAnAxis(Scene): CONFIG = { "plane_kwargs" : { "x_line_frequency" : 2, "y_line_frequency" :2 } } def construct(self): my_plane = NumberPlane(**self.plane_kwargs) my_plane.add(my_plane.get_axis_labels()) self.add(my_plane) self.wait()

The double asterisk in front of the argument `self.plane_kwargs`

lets the class know that this is a dictionary that needs to be unpacked.

I recommend changing the various properties to see what affect they have on the axes and grid. This is the best way to learn what things do.

## 9.1 A Simple Vector Field

Let’s start with a simple vector field; a constant field. We first need to define a set of vector points for each grid point, define the field at each grid point, then create the `Vector()`

for the field at each point. Finally we combine all the `Vector()`

instances into a `VGroup`

to allow us to draw all vector lines with a single command.

class SimpleField(Scene): CONFIG = { "plane_kwargs" : { "color" : RED }, } def construct(self): plane = NumberPlane(**self.plane_kwargs) #Create axes and grid plane.add(plane.get_axis_labels()) #add x and y label self.add(plane) #Place grid on screen points = [x*RIGHT+y*UP for x in np.arange(-5,5,1) for y in np.arange(-5,5,1) ] #List of vectors pointing to each grid point vec_field = [] #Empty list to use in for loop for point in points: field = 0.5*RIGHT + 0.5*UP #Constant field up and to right result = Vector(field).shift(point) #Create vector and shift it to grid point vec_field.append(result) #Append to list draw_field = VGroup(*vec_field) #Pass list of vectors to create a VGroup self.play(ShowCreation(draw_field)) #Draw VGroup on screen

After creating the `NumberPlane()`

we use a list comprehension to create a list of the location of all grid points. Remember that `RIGHT=np.array(1,0,0)`

and `UP=np.array(0,1,0)`

so this list comprehension covers all points from (5,5,0) down to (-5,-5,0) in unit step sizes. The last number in `arange()`

specifies the step size. Next we create an empty list `vec_field`

to hold all of the vectors we are going to create. The `for`

loop goes through each grid location in `points`

and creates a vector whose length and direction are defined by `field`

. It is inefficient to keep defining `field`

each time through the loop but we are setting things up for later. The `shift(point)`

command moves the vector to the grid location defined by `point`

. These results are then appended to a list. After going through the `for`

loop. all of the vectors are grouped together in a single `VGroup`

called `draw_field`

. The only reason for doing this is that you can then add `draw_field`

using a single `add`

or `play`

command. You could have included `self.add(result)`

inside each iteration of the `for`

loop instead of showing the creation of `draw_field`

, but using the `VGroup`

feels cleaner.

## 9.2 A Variable Vector Field

For a slightly more interesting field we will look at the electric field due to a postive point charge. The electric field is:

where is the charge on the point charge, is the distance vector between the charge and the observation point, and is the magnitude of that vector. The constant out front is essentially a conversion factor. For our purposes we will set all constants equal to zero and just look at

.

class FieldWithAxes(Scene): CONFIG = { "plane_kwargs" : { "color" : RED_B }, "point_charge_loc" : 0.5*RIGHT-1.5*UP, } def construct(self): plane = NumberPlane(**self.plane_kwargs) plane.main_lines.fade(.9) plane.add(plane.get_axis_labels()) self.add(plane) field = VGroup(*[self.calc_field(x*RIGHT+y*UP) for x in np.arange(-9,9,1) for y in np.arange(-5,5,1) ]) self.play(ShowCreation(field)) def calc_field(self,point): x,y = point[:2] Rx,Ry = self.point_charge_loc[:2] r = math.sqrt((x-Rx)**2 + (y-Ry)**2) efield = (point - self.point_charge_loc)/r**3

The location of the point charge is set in `CONFIG{}`

. To create the vector field we’ve condensed the previous code. We use a list comprehension and the function `calc_field()`

as the argument of `VGroup()`

. The `calc_field()`

function defines the field to calculate. To make the formulas a little easier to read we unpack the x- and y-coordinates from the `point`

vector and the `self.point_charge_loc`

vector. The code `x,y=point[:2]`

is equivalent to `x=point[0]`

and `y=point[1]`

.

The `fade(0.9)`

method sets the opacity of the lines to be one minus the fade level (so in this case the opacity is set to 0.1). This was done to make it easier to see the tiny field arrows farther from the charge location.

**Things to try:**

– Change each of the elements in `CONFIG{}`

for `NumberPlane()`

to see what affect they have on the axes and grid lines.

– Calculate different fields

– Try `efield = np.array((-y,x,0))/math.sqrt(x**2+y**2)`

– Try `efield = np.array(( -2*(y%2)+1 , -2*(x%2)+1 , 0 ))/3`

– Come up with your own equation

Note: If you end up creating a video using manim and post it online, please link to it in the comments. I’m sure everyone would love to see what sort of fun things everyone is doing.

Next time we will look at plotting vector fields in three dimensions.

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

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

Pingback: 3D Scenes – manim Series: Part 10 | Talking Physics

Pingback: Fields of a Moving Charge – manim Series: Part 11 | Talking Physics

Pingback: Getting Started Animating with manim and Python 3.7 | Talking Physics