What is the best way of implementing an efficient Vector / Point class (or even better: is there one already), that can be used both in Python 2.7+ and 3.x?
I've found the blender-mathutils, but they seem to only support Python 3.x. Then there's this Vector class, that uses numpy, but it's only a 3D vector. Using a list for a Vector like kivy's vector class (sourcecode) that has static attributes (x and y) seems weird too. (There are all these list-methods.)
At the moment I'm using a class that extends namedtuple (as you can see below), but this has the disadvantage of not being able to change the coordinates. I think this can become a performance problem, when thousands of objects are moving and a new (vector) tuple is created everytime. (right?)
class Vector2D(namedtuple('Vector2D', ('x', 'y'))):
__slots__ = ()
def __abs__(self):
return type(self)(abs(self.x), abs(self.y))
def __int__(self):
return type(self)(int(self.x), int(self.y))
def __add__(self, other):
return type(self)(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return type(self)(self.x - other.x, self.y - other.y)
def __mul__(self, other):
return type(self)(self.x * other, self.y * other)
def __div__(self, other):
return type(self)(self.x / other, self.y / other)
def dot_product(self, other):
return self.x * other.x + self.y * other.y
def distance_to(self, other):
""" uses the Euclidean norm to calculate the distance """
return hypot((self.x - other.x), (self.y - other.y))
Edit: I did some testing and it seems that using numpy.array
or numpy.ndarray
as a vector is too slow. (For example getting an item takes almost twice as long, not to mention creating an array. I think it's more optimized for doing calculations on a large number of items.)
So, I'm looking more for a lightweight vector class with a fixed number of fields (in my case just x
and y
) that can be used for games. (I don't want to re-invent the wheel if there's already a well-tested one.)
Python NumPy module is used to create a vector. We use numpy. array() method to create a one-dimensional array i.e. a vector.
import numpy as np a = np. zeros(0) for i in range(1,100): a = np. r_[a,np. repeat(i, 100)] print(a) [ 1.
In order to normalize a vector in NumPy, we can use the np. linalg. norm() function, which returns the vector's norm value. We can then use the norm value to divide each value in the array to get the normalized array.
The vector class in numpy
in terms of linear algebra would probably be the numpy.matrix
which is a subclass of numpy.ndarray
. It's not cleaner per se but it makes your code cleaner because algebraic operations are assumed instead of elementwise.
In [77]: a = np.array([1,2])
In [78]: b = np.array([3,3])
In [79]: a*b
Out[79]: array([3, 6])
In [80]: np.dot(a,b)
Out[80]: 9
In [81]: np.outer(a,b)
Out[81]:
array([[3, 3],
[6, 6]])
In [82]: a = np.matrix(a).T
In [83]: b = np.matrix(b)
In [84]: b*a
Out[84]: matrix([[9]])
In [85]: a*b
Out[85]:
matrix([[3, 3],
[6, 6]])
If you want to create your own, base it on one of these, for example:
class v2d(np.ndarray):
def __abs__(self):
return np.linalg.norm(self)
def dist(self,other):
return np.linalg.norm(self-other)
def dot(self, other):
return np.dot(self, other)
# and so on
Which in the simplest case you can just make by viewing an ndarray
as your new class:
In [63]: a = np.array([1,2]).view(v2d)
In [64]: b = np.array([3,3]).view(v2d)
In [65]: a
Out[65]: v2d([1, 2])
In [66]: abs(b)
Out[66]: 4.2426406871192848
In [67]: a - b
Out[67]: v2d([-2, -1])
In [68]: a*b
Out[68]: v2d([3, 6])
In [69]: a*3
Out[69]: v2d([3, 6])
In [70]: a.dist(b)
Out[70]: 2.2360679774997898
In [71]: b.dist(a)
Out[71]: 2.2360679774997898
In [72]: a.dot(b)
Out[72]: 9
Here is more information on subclassing the ndarray
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With