HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

2D Vector class

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
classvectorstackoverflow

Problem

As part of a game engine I am creating, I am in the process of writing a bunch of math classes (vector2/3/4, matrix2/3/4, and quaternions to start with).

Vec2 class:

```
import math
import random

class Vec2(object):
def __init__(self, x, y):
self._x = float(x)
self._y = float(y)

@property
def x(self):
return self._x

@x.setter
def x(self, new_x):
self._x = float(new_x)

@property
def y(self):
return self._y

@y.setter
def y(self, new_y):
self._y = float(new_y)

def __add__(self, other):
types = (int, float)
if isinstance(self, types):
return Vec2(self + other.x, self + other.y)
elif isinstance(other, types):
return Vec2(self.x + other, self.y + other)
else:
return Vec2(self.x + other.x, self.y + other.y)

def __div__(self, other):
types = (int, float)
if isinstance(self, types):
self = Vec2(self, self)
elif isinstance(other, types):
other = Vec2(other, other)
x = self.x / other.x
y = self.y / other.y
return Vec2(x, y)

def __mul__(self, other):
types = (int, float)
if isinstance(self, types):
return Vec2(self other.x, self other.y)
elif isinstance(other, types):
return Vec2(self.x other, self.y other)
else:
return Vec2(self.x other.x, self.y other.y)

def __neg__(self):
return Vec2(-self.x, -self.y)

def __radd__(self, other):
return Vec2(self.x + other, self.y + other)

def __rdiv__(self, other):
return Vec2(other/self.x, other/self.y)

def __rmul__(self, other):
return Vec2(other self.x, other self.y)

def __rsub__(self, other):
return Vec2(other - self.x, other - self.y)

def __repr__(self):
return self.__str__()

def __str__(self):
return "Vec2: ({0}, {1})".fo

Solution

In your first version, I don't understand your various

if isinstance(self, types):


checks. Since you are building the Vec2 class, you know that self is of type Vec2 or one of its subclasses, if any. So these checks will always evaluate to False: you’d better remove them.

But as you figured out with the second version, you can remove a lot of repetitions with a base class or some kind of factory.

However, what seems a big mistake in the second version is the fact that you are downcasting any VecX to a Vec after the first arithmetic operation. Meaning that

v1 = Vec2(4, 8)
v2 = Vec2(1, 1)
v3 = v1 + v2
print(v3.x, v3.y)


Will raise AttributeError for the v3.x and v3.y accesses. Because v3 is a Vec and not a Vec2.

To avoid that, I don't have any better idea than to hack the __init__ method a bit:

def Vector(n):
    class Vec(object):
        def __init__(self, *args):
            try:
                data, = args
            except ValueError:
                data = np.array(args, dtype=np.float32)
            assert len(data) == n
            self.data = data
        # other common methods
    return Vec

class Vec2(Vector(2)):
    # special methods for Vec2


This allows you to call Vec2(data) where data is a numpy array as well as Vec2(2, 0.8).

This means that, in your various operators, you will need to return the right type, something along the lines of return self.__class__(self.data + other.data) should do, for the addition.

The last thing that I wanted to mention is your usage of the properties. Why manage a separate variable when you have direct access to the underlying data structure? I would simplify Vec2 to:

class Vec2(Vector(2)):
    @property
    def x(self):
        return self.data[0]

    @x.setter
    def x(self, value):
        self.data[0] = value  # No need to manually convert to float, numpy will do it for us

    @property
    def y(self):
        return self.data[1]

    @y.setter
    def y(self, value):
        self.data[1] = value

Code Snippets

if isinstance(self, types):
v1 = Vec2(4, 8)
v2 = Vec2(1, 1)
v3 = v1 + v2
print(v3.x, v3.y)
def Vector(n):
    class Vec(object):
        def __init__(self, *args):
            try:
                data, = args
            except ValueError:
                data = np.array(args, dtype=np.float32)
            assert len(data) == n
            self.data = data
        # other common methods
    return Vec

class Vec2(Vector(2)):
    # special methods for Vec2
class Vec2(Vector(2)):
    @property
    def x(self):
        return self.data[0]

    @x.setter
    def x(self, value):
        self.data[0] = value  # No need to manually convert to float, numpy will do it for us

    @property
    def y(self):
        return self.data[1]

    @y.setter
    def y(self, value):
        self.data[1] = value

Context

StackExchange Code Review Q#151205, answer score: 3

Revisions (0)

No revisions yet.