[Rod Stephens Books]
Index Books Python Examples About Rod Contact
[Mastodon] [Bluesky]
[Build Your Own Ray Tracer With Python]

[Beginning Database Design Solutions, Second Edition]

[Beginning Software Engineering, Second Edition]

[Essential Algorithms, Second Edition]

[The Modern C# Challenge]

[WPF 3d, Three-Dimensional Graphics with WPF and C#]

[The C# Helper Top 100]

[Interview Puzzles Dissected]

Title: A brief primer on vector arithmetic in Pygame and Python

[A Python program demonstrating vector arithmetic]

Vectors are pretty useful when you need to manipulate points and line segments. This post gives a brief introduction to vector arithmetic. Fortunately, it's pretty intuitive so you should be able to take advantage of vectors with a little practice.

Vectors and Points

A vector represents a direction and a distance in that direction. Usually a vector is written by giving its coordinate components surrounded by pointy brackets. For example, the vector <100, 50> means "move 100 units to the right and 50 units down." Here I'm using the goofy screen coordinate system where Y coordinates increase downward. You can also use them to represent normal mathematical coordinates, you may just need to adjust the Y values if you want to draw the results.

You can also think of a vector as a bearing and range, as in 45° for a distance of 100 units, but the <100, 50> version is much more common in computer graphics.

It's important to remember that a vector is a direction and distance only and has no real location in space. You can use a vector to find a location, however, by adding it to a point.

If you add a vector to a point, you get a new point as if you had started at the first point and then moved in the direction of the vector through its length. For example, if you add the starting point (75, 25) to the vector <100, 50>, you get a segment that runs from the point (55, 25) to the point (175, 75). Point/vector addition is coordinate-wise; you simply add the point's X coordinate to the vector's X coordinate and similarly for the Y coordinates.

You could add that same vector to a different point to get a different end point. Because the vector represents a direction and distance, the new segment will point in the same direction so the two segments will be parallel. This can be useful if you're trying to build a rectangle, square, trapezoid, or other shape that has parallel sides.

Pygame represents vectors with its Vector2 class in two dimensions and its Vector3 class in three dimensions. Unfortunately, Pygame has no point class. You can use Vector2 and Vector3 objects to represent points, but you need to remember whether a particular object represents a point or a vector. That can be slightly confusing, but I suppose it fits well with Python's "pay attention or you'll get the pain you deserve" philosophy.

Vector Addition

In addition to adding a vector to a point, you can add a vector to another vector. The result is a vector that gives the same result you would get if you start at a point, move along the first vector, and then move along the second vector. For example, consider the picture at the top of this post. In the upper left, you can see vector a plus vector b equals vector c. (Don't worry about how the program draws the vectors for now.) Here's the code that the program used to create those vectors.

a = pygame.Vector2(50, 100) b = pygame.Vector2(100, -30) c = a + b

This code creates Vector2 objects to represent vectors a and b. It then sets vector c equal to a plus b. The Vector2 class knows how to perform this kind of addition so the code is simple and intuitive.

Vector addition is commutative so a + b = b + a.

You can add as many vectors as you like. For example, the bottom left part of this post's picture shows the calculation a + b + d + e = f.

Scalar Multiplication

You can scale a vector by simply multiplying its components by a number. The result is a vector pointing in the same direction as the original and with length equal to the original vector's length multiplied by the number.

For example, the middle top of the picture at the top of this post shows vector a and 2 * a. The vector 2 * a points in the same direction a vector a and is twice as long.

Vector Subtraction

Vector subtraction is slightly more confusing than vector addition but it's easy enough to calculate. To subtract vector a from vector b, first negate vector a and then add it to b. In other words, calculate b + (-a).

To negate a vector, you just multiply it by -1 to get a vector with the same length as the original but pointed in the opposite direction. The vectors in the upper right corner of the picture at the top of this post show vector a and its negation -a.

You can draw a picture showing vector subtraction, but it's not as intuitive as the other pictures so I didn't bother.

Vector2

Pygame's Vector2 class represents vectors and knows how to add, subtract, and scale vectors. For example, the code shown earlier performs the calculation c = a + b.

It's just as easy to scale a Vector2 object. Here are a few examples in Python code.

StatementResult
b = 3 * aVector b points in the same direction as a and is 3 times as long
b = -aVector b points in the direction opposite that of a and is the same length
a *= 10Vector a still points in the same direction as before but is now 10 times as long

In addition to supporting these simple mathematical calculations, the Vector2 class provides a bunch of useful methods. The following table lists the ones I use the most.

MethodResult
lengthReturns the vector’s length
normalizeReturns a new vector with the same direction but length 1
scale_to_lengthScales the vector in place so it has a given length leaving it pointing in its original direction
rotateReturns a copy of the vector that has been rotated by a given angle in degrees
rotate_ipRotates the vector in place by a given angle in degrees
angle_toReturns the angle between this vector and another one
as_polarReturns this vector’s polar coordinate representation
from_polarReturns a vector from its polar coordinate representation
projectProjects a vector onto another vector
copyReturns a copy of the vector

For information about other Vector2 and Vector3 methods, see the Pygame.org page math.html.

Notice that a few of these methods (scale_to_length and rotate_ip) modify the vector itself while the other methods return new vectors. This makes sense because there are other methods for doing the same thing and producing a new vector. For example, to scale a vector not in place, just multiply it by the scale factor. To make a new rotated copy of the vector, use the rotate method instead of rotate_ip.

Example Code

The example program uses the following code to draw the vectors shown in the picture at the top of this post.

def draw(self): '''Draw example vectors.''' # Draw a + b = c. a = pygame.Vector2(50, 100) b = pygame.Vector2(100, -30) c = a + b x = 20 width = 1 arrowhead_size = 5 fill_arrowhead = False origin = pygame.Vector2(x, 20) draw_vector(self.canvas, origin, a, 'a', 'green', width, arrowhead_size, fill_arrowhead) point = origin + a draw_vector(self.canvas, point, b, 'b', 'blue', width, arrowhead_size, fill_arrowhead) draw_vector(self.canvas, origin, c, 'c', 'red', width, arrowhead_size, fill_arrowhead) # Draw a and 2 * a. x = 250 origin = pygame.Vector2(x, 50) draw_vector(self.canvas, origin, a, 'a', 'green', width, arrowhead_size, fill_arrowhead) x += 50 origin = pygame.Vector2(x, 50) draw_vector(self.canvas, origin, 2 * a, '2 * a', 'blue', width, arrowhead_size, fill_arrowhead) # Draw a and -a. x += 200 origin = pygame.Vector2(x, 150) draw_vector(self.canvas, origin, a, 'a', 'green', width, arrowhead_size, fill_arrowhead) draw_vector(self.canvas, origin, -a, '-a', 'blue', width, arrowhead_size, fill_arrowhead) # Draw f = a + b + d + e. x = 20 origin = pygame.Vector2(x, 200) draw_vector(self.canvas, origin, a, 'a', 'green', width, arrowhead_size, fill_arrowhead) point = origin + a draw_vector(self.canvas, point, b, 'b', 'blue', width, arrowhead_size, fill_arrowhead) d = pygame.Vector2(100, 100) point += b draw_vector(self.canvas, point, d, 'd', 'purple', width, arrowhead_size, fill_arrowhead) e = pygame.Vector2(20, -100) point += d draw_vector(self.canvas, point, e, 'e', 'orange', width, arrowhead_size, fill_arrowhead) f = a + b + d + e draw_vector(self.canvas, origin, f, 'f', 'red', width, arrowhead_size, fill_arrowhead)

This code should be fairly easy to figure out except for the calls to draw_vector. That function draws a vector starting at a given point. For example, the last call to draw_vector draws an arrow from the point origin in the direction and distance of vector f.

The following draw_vector function uses vector arithmetic and several vectors methods to draw a vector on the window.

def draw_vector(canvas, start_point, vector, text=None, fill='black'): '''Draw a pygame.Vector2.''' width = 1 arrowhead_size = 5 # Draw the start point. radius = 3 * width canvas.create_oval( start_point.x - radius, start_point.y - radius, start_point.x + radius, start_point.y + radius, fill=fill, outline=fill) # Get arrowhead parameters. tip = start_point + vector dy = vector.normalize() * arrowhead_size dx = dy.rotate(90) # Draw the vector. canvas.create_line(start_point.x, start_point.y, tip.x, tip.y, fill=fill, width=width) # Draw the arrowhead. arrowhead_points = [ tip + dx - 2 * dy, tip, tip - dx - 2 * dy, ] arrowhead_points = vectors_to_coords(arrowhead_points) canvas.create_line(arrowhead_points, fill=fill, width=width) # Label the vector. if text is not None: # Get the text's size. font = tkFont.Font(family='courier', size=14, weight='bold') rx = font.measure(text) / 2 + 3 ry = font.metrics('linespace') / 2 center = (start_point + tip) / 2 canvas.create_rectangle( center.x - rx, center.y - ry, center.x + rx, center.y + ry, outline='', fill='white') canvas.create_text(center.x, center.y, text=text, anchor='c', fill=fill, font=('Courier', 14))

[Defining an arrowhead with Pygame's Vector2 objects] First, the function draws a circle at the start point. Remember that vectors only have direction and length so they don't have a real position unless you start them somewhere.

Next, the code generates points to define an arrowhead as shown in the picture on the right. The code first finds tip (the blue dot in the picture), the spot where the vector ends when added to the start point. The code to do that is super easy using vector arithmetic: tip = start_point + vector.

The function then normalizes the vector to get a new vector pointing in the same direction but with length 1. It multiplies that by arrowhead_size to make the vector a bit bigger and saves the result vector as dy. (See the picture on the right.)

To find a perpendicular vector dx, the code rotates dy by 90°. (See the picture on the right again.)

Now the function draws the vector's body starting at start_point and ending at tip. It then draws the arrowhead.

The arrowhead's first point is given by tip + dx - 2 * dy. The picture shows this calculation by adding two dashed copies of -dy to dx. The arrowhead's second point is tip and the third is given by tip - dx - 2 * dy.


Before it can draw the arrowhead, the program must convert the arrowhead points from Vector2 objects into X/Y coordinate pairs that tkinter can understand. To do that, it calls the following vectors_to_coords helper function.

def vectors_to_coords(vectors): '''Convert a list of pygameVector2 objects into a list of X, Y pairs.''' return [(vector.x, vector.y) for vector in vectors]

This function uses a list comprehension to convert the Vector2 objects. You could just put the comprehension inline in the draw_vector function, but you'll often need to convert back and forth between Vector2 objects and coordinate pairs, so it's useful to have this little helper handy.


After it converts the Vector2 objects into coordinate pairs the function draws the arrowhead.

Next, the code draws the vector's text. It creates a Font object and uses its measure and metrics methods to get the width and height of the text as drawn in tat font. It finds the vector's center point and draws a white rectangle centered at that point to make a clear area on the vector. The code then draws the text on top.

Conclusion

Hopefully this post has given you some insight into how vector arithmetic works and how Pygame's Vector2 class makes working with vectors easy. The class supports simple operations like vector addition and scaling, and its methods provide extra features like normalization and rotation. When you build programs that work with vectors (and I have a couple coming up), Vector2 makes things a lot simpler.

Download the example to experiment with it and to see additional details.

© 2025 Rocky Mountain Computer Consulting, Inc. All rights reserved.