This notebook shows how to rotate 2 dimensional vectors using a basic rotation matrix. Matplotlib is used to visualise the result. A property of rotation is that the magnitude of the vector is maintained. In 2 dimensions, vectors are rotated counterclockwise about the origin in the xy plane, with the x-axis horizontal and the y-axis vertical.
from math import sqrt, pi, cos, sin
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator
from numpy import allclose
def rotate_2D(xy_coords, angle, units="deg"):
"""
The rotation matrix for 2D about angle # is:
R = | cos# -sin# |
| sin# cos# |
xy_coords for n items should be formatted
as a list of lists, like follows:
[
[x_0, y_0],
[x_1, y_1],
...,
[x_n-1, y_n-1]
]
"""
assert units in ("deg", "rad")
# convert to radians
if units == "deg":
# 2pi rad = 360 deg
# ? rad / 2pi rad = angle / 360 deg
# ? rad = 2pi rad * angle / 360 deg
angle = 2 * pi * angle / 360
for i, (x, y) in enumerate(xy_coords):
"""
Rv = | cos# -sin# | * | v_x | = | v_x * cos# - v_y * sin# |
| sin# cos# | | v_y | | v_x * sin# + v_y * cos# |
"""
xy_coords[i] = [
x * cos(angle) - y * sin(angle),
x * sin(angle) + y * cos(angle)
]
return xy_coords
def vector_magnitude(vector):
"""
The magnitude of a vector (or length) is denoted:
||v|| = sqrt (x_0 ^ 2 + x_1 ^ 2 + ... + x_n-1 ^ 2)
"""
result = 0
for element in vector:
result += element ** 2
return sqrt(result)
def test_rotate_2D():
coords = [[1,3], [3,4], [3,5], [2,3], [4,6], [2,9], [3,8]]
colours = ["green", "red", "purple", "orange", "pink", "blue", "yellow"]
x_orig = [coord[0] for coord in coords]
y_orig = [coord[1] for coord in coords]
magnitudes_orig = [vector_magnitude(coord) for coord in coords]
# scatter plot is needed so we can use list of colours
plt.scatter(x_orig, y_orig, color=colours)
angles = range(30, 360, 10) # deg
# angles = [1/2 * pi, pi, 3/2 * pi] # rad
for angle in angles:
rotate_2D(coords, angle)
# rotate_2D(coords, angle, units="rad")
x_new = [coord[0] for coord in coords]
y_new = [coord[1] for coord in coords]
plt.scatter(x_new, y_new, color=colours)
# add gridlines and black lines for axes
plt.grid()
ax = plt.gca()
ax.axhline(color="black", linewidth=1)
ax.axvline(color="black", linewidth=1)
# major increments are 1 for both axes
ax.set_xlim(-10, 10)
ax.set_ylim(-10, 10)
ax.xaxis.set_major_locator(MultipleLocator(1))
ax.yaxis.set_major_locator(MultipleLocator(1))
# make the figure square
fig = plt.gcf()
fig.set_size_inches(10, 10)
# display the plot
plt.show()
magnitudes_new = [vector_magnitude(coord) for coord in coords]
# see if magnitude changed as a result of rotation
for mag_orig, mag_new in zip(magnitudes_orig, magnitudes_new):
assert allclose(mag_orig, mag_new)
test_rotate_2D()