Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Efficient Vector / Point class in Python

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

like image 771
Joschua Avatar asked Oct 18 '13 20:10

Joschua


People also ask

Does Python have a vector class?

Python NumPy module is used to create a vector. We use numpy. array() method to create a one-dimensional array i.e. a vector.

How do you make a vector from 1 to 100 in Python?

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.

How do I normalize a vector in NumPy?

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.


1 Answers

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.

like image 76
askewchan Avatar answered Sep 22 '22 07:09

askewchan