A vector is an object that has both a magnitude or size and a direction. “Geometrically, we can picture a vector as a directed line segment, whose length is the magnitude of the vector and with an arrow indicating the direction,” An introduction to vectors, Math Insight.
user@pc:~$ python # Let's work with vectors in Python
Python 3.8.10 (default, Jun 2 2021, 10:49:15)
>>> import numpy as np # NumPy is a library that supports vectors and matrices in an optimized way.
>>> v = np.array([3, 4, 5]) # Data manipulation in Python is almost synonymous with NumPy array manipulation.
>>> w = np.array([2, 7, 1])
>>> np.add(v, w) # numpy.add adds arguments element-wise: v+w.
array([ 5, 11, 6])
>>> np.subtract(v, w) # numpy.subtract subtracts arguments element-wise: v-w.
array([ 1, -3, 4])
>>> v*3
array([ 9, 12, 15])
>>> np.dot(v, w) # It calculates the dot product or scalar product of v and w.
39
>>> np.linalg.norm(v) # It computes the magnitude or Euclidean norm of v.
7.0710678118654755 # = sqrt(50)
>>> np.cross(v, w) # numpy.cross returns the cross product of two vectors.
array([-31, 7, 13])
>>> x = np.array([4, 2, 4])
>>> x/np.linalg.norm(x) # It normalizes the vector.
array([0.66666667, 0.33333333, 0.66666667]) # {2/3,1/3,2/3}
import math
from settings import * # It defines the constant THRESHOLD
import copy
class myVector(object):
""" It defines a two-dimensional vector with Cartesian coordinates."""
def __init__(self, x=0, y=0):
self.x = x
self.y = y
## It implements the addition of two vectors.
# @params other is the vector from which self is to be added.
# @return the new vector object that is the result of self + other.
def __add__(self, other):
if isinstance(other, int):
other = myVector(other)
return myVector(self.x + other.x, self.y + other.y)
__radd__ = __add__
radd is called if the left object does not have an add method or that method does not know how to add the two objects, e.g., v1 = myVector(2, 3), print(2 + v1) prints 4i + 3j.
## It implements the subtraction of two vectors.
# @params other is the vector from which self is to be subtracted.
# @return the new vector object that is the result of self - other.
def __sub__(self, other):
if isinstance(other, int):
other = myVector(other)
return myVector(self.x - other.x, self.y - other.y)
def __rsub__(self, other):
return myVector(other) - self
def __neg__(self):
return myVector(-self.x, -self.y)
## It implements the multiplication of a scalar by a vector.
def __mul__(self, scalar):
if isinstance(scalar, int) or isinstance(scalar, float):
return myVector(self.x * scalar, self.y * scalar)
raise NotImplementedError('You can only multiply a vector by a scalar')
def __rmul__(self, scalar):
"""__rmul__ needs to be implemented so scalar * vector also works."""
return self._mul__(scalar)
## It implements the division of the vector by a scalar.
def __div__(self, scalar):
if scalar != 0:
return myVector(self.x / float(scalar), self.y / float(scalar))
return None
def __truediv__(self, scalar):
return self._div__(scalar)
__eq__ implements the comparison of two vectors: self and other. Typically, it would be something like return self.x == other.x and self.y == other.y.
However, rounding errors when using computers are to be expected and we want to be able to deal with them in a consistent manner. This will be useful when we code our Pacman clone.
def __eq__(self, other):
return (self.x - other.x).magnitude() < THRESHOLD and (self.y - other.y).magnitude < THRESHOLD
def __ne__(self, other):
return not self._eq__(other)
def magnitude(self):
""" It returns the absolute value or magnitude of the vector."""
return math.sqrt(self.x**2 + self.y**2)
def distanceTo(self, other):
""" It calculates the distance between two vectors or points: self and other."""
return (self-other).magnitude()
def to_polar(self):
"""It calculates the vector in polar coordinates."""
return self.magnitude(), math.atan2(self.y, self.x)
def __repr__(self):
"""It returns a string representation of the vector."""
return repr((self.x, self.y))
def __str__(self):
"""It returns a string representation of the vector."""
return f"{self.x:.2f}i + {self.y:.2f}j"
@staticmethod
def distance(v1, v2):
""" It calculates the distance between two vectors."""
return (v1-v2).magnitude()
def __copy__(self):
""" It creates a new instance or clone of the object without touching the object itself."""
return myVector(self.x, self.y)
def getVector(self):
return self.x, self.y
def getVectorAsInt(self):
return int(self.x), int(self.y)
def dot(self, other):
"""It implements the scalar product of vectors. It returns the scalar or dot product of self and other. Both self and other must be vectors."""
if not isinstance(other, myVector):
raise TypeError('Both arguments need to be vectors.')
return self.x * other.x + self.y * other.y
# We want to alias the __matmul__ method to dot so we can use a @ b as well as a.dot(b).
__matmul__ = dot
def normalize(self):
""" It returns the normalized vector."""
return self.__class__(self.x/self.magnitude(), self.y/self.magnitude())
if __name__ == '__main__':
v1 = myVector(2, 3)
v2 = copy.copy(v1)
v2 = 2 + v1
print(v1) # 2.00i + 3.00j. We modify the copy but the original object remains unchanged.
print(v2) # 4.00i + 3.00j
v2 = myVector(3, -1.5)
print('repr(v2) = ', repr(v2)) # repr(v2) = (3, -1.5)
print('v1 + v2 = ', v1 + v2) # v1 + v2 = 5.00i + 1.50j
print('v1 - v2 = ', v1 - v2) # v1 - v2 = -1.00i + 4.50j
pprint('abs(v1) = ', myVector.magnitude(v1)) # abs(v1) = 3.605551275463989
print('-v2 = ', -v2) # -v2 = -3.00i + 1.50j
print('v1 * 3 = ', v1 * 3) # v1 * 3 = 6.00i + 9.00j
print(v1.normalize()) # 0.55i + 0.83j
print('7 * v1 = ', 7 * v1) # 7 * v1 = 14.00i + 21.00j
print('v2 / 2.5 = ', v2 / 2.5) # v2 / 2.5 = 1.20i + -0.60j
print('v1.dot(v2) = v1 @ v2 = ', v1 @ v2) # v1.dot(v2) = v1 @ v2 = 1.5
print("v1.distance_to(v2) = " + str(v1.distanceTo(v2))) # v1.distance_to(v2) = 4.6097722286464435
print('v1 as a polar vector, (r, theta) =', v1.to_polar()) # v1 as a polar vector, (r, theta) = (3.605551275463989, 0.982793723247329)