[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: [pygame] 2D vector class



Brian Fisher wrote:
> so I wrote test functions to take a 2d position, subtract another
> position from it then multiply the result by a scalar (basically pan
> and zoom a position). One version uses the 2d vector with operator
> overloading, so it will allocate 2 vec2d's, each with a list member.
> Another version does explicit math operations on variables, so no
> allocations happen. I also tried converting the vec2d class to be
> inherited from tuple, thinking that maybe you could save the list
> member allocations by being a tuple to start with...

When I said "explicitly storing two variables", I meant doing more
like the following:

class Vector2D(object):
    def __init__(self, dx, dy):
        object.__init__(self)
        self.dx = dx
        self.dy = dy

    def __sub__(self, v2):
        return Vector(self.dx - v2.dx, self.dy - v2.dy)
    def __add__(self, v2):
        return Vector(self.dx + v2.dx, self.dy + v2.dy)

    def __iadd__(self, v2):
        self.dx = self.dx + v2.dx
        self.dy = self.dy + v2.dy
        return self

    def __isub__(self, v2):
        self.dx = self.dx - v2.dx
        self.dy = self.dy - v2.dy
        return self

This is the Vector class I use. I did tests with this approach and, as
you might expect, it's also much slower than just using two variables
outside of a class. I'm attaching the modified benchmarking script.

> like a 48:1 performance diff. Also the tuple inherited version was
> slower.

This is good information but really astonishing. The next step would
probably be benchmarking just to confirm that the large number of
calls to __init__ and __new__ are what are causing the slowdown. It
could also be that method calls are much slower than no function
calls. It certainly isn't clear how best to take advantage of this
speedup without going crazy from pos_x and pos_y variables all over
the place. Maybe unpacking the vectors in certain parts of the code
would be sufficient to speed things up, or maybe converting certain
operations to in-place vector modifications would be good enough.

Ethan
"""
2D Vector Math Class (with operator overload goodness)
(C) Copyright 2005 James Paige and Hamster Republic Productions
"""

########################################################################
import operator
import math

########################################################################
class vec2d(object):

    def __init__(self, x_or_pair, y = None):
        if y == None:
            try:
                self.vec = [x_or_pair[0],x_or_pair[1]]
            except TypeError:
                raise TypeError("vec2d constructor requires a tuple or two arguments")
        else:
            self.vec = [x_or_pair,y]

    def get_x(self):
        return self.vec[0]
    def set_x(self, value):
        self.vec[0] = value
    x = property(get_x, set_x)
    
    def get_y(self):
        return self.vec[1]
    def set_y(self, value):
        self.vec[1] = value
    y = property(get_y, set_y)
    
    def set(self, x, y):
        self.vec[0] = x
        self.vec[1] = y
        
    # String representaion (for debugging)
    def __repr__(self):
        return 'vec2d(%s, %s)' % (self.x, self.y)
    
    # Array-style access
    def __len__(self): return 2

    def __getitem__(self, key):
        return self.vec[key]

    def __setitem__(self, key, value):
        self.vec[key] = value

    # Comparison
    def __eq__(self, other):
        return self.vec[0] == other[0] and self.vec[1] == other[1]
    
    def __ne__(self, other):
        return self.vec[0] != other[0] or self.vec[1] != other[1]

    def __nonzero__(self):
        return self.vec[0] or self.vec[1]

    # Generic operator handlers
    def _o2(self, other, f):
        "Any two-operator operation where the left operand is a vec2d"
        try:
            return vec2d(f(self.vec[0], other[0]),
                         f(self.vec[1], other[1]))
        except TypeError:
            return vec2d(f(self.vec[0], other),
                         f(self.vec[1], other))

    def _r_o2(self, other, f):
        "Any two-operator operation where the right operand is a vec2d"
        try:
            return vec2d(f(other[0], self.vec[0]),
                         f(other[1], self.vec[1]))
        except TypeError:
            return vec2d(f(other, self.vec[0]),
                         f(other, self.vec[1]))

    def _o1(self, f):
        "Any unary operation on a vec2d"
        return vec2d(f(self.vec[0]), f(self.vec[1]))

    # Addition
    def __add__(self, other):
        return self._o2(other, operator.add)
    __radd__ = __add__

    # Subtraction
    def __sub__(self, other):
        return self._o2(other, operator.sub)
    def __rsub__(self, other):
        return self._r_o2(other, operator.sub)

    # Multiplication
    def __mul__(self, other):
        return self._o2(other, operator.mul)
    __rmul__ = __mul__

    # Division
    def __div__(self, other):
        return self._o2(other, operator.div)
    def __rdiv__(self, other):
        return self._r_o2(other, operator.div)

    def __floordiv__(self, other):
        return self._o2(other, operator.floordiv)
    def __rfloordiv__(self, other):
        return self._r_o2(other, operator.floordiv)

    def __truediv__(self, other):
        return self._o2(other, operator.truediv)
    def __rtruediv__(self, other):
        return self._r_o2(other, operator.truediv)

    # Modulo
    def __mod__(self, other):
        return self._o2(other, operator.mod)
    def __rmod__(self, other):
        return self._r_o2(other, operator.mod)

    def __divmod__(self, other):
        return self._o2(other, operator.divmod)
    def __rdivmod__(self, other):
        return self._r_o2(other, operator.divmod)

    # Exponentation
    def __pow__(self, other):
        return self._o2(other, operator.pow)
    def __rpow__(self, other):
        return self._r_o2(other, operator.pow)

    # Bitwise operators
    def __lshift__(self, other):
        return self._o2(other, operator.lshift)
    def __rlshift__(self, other):
        return self._r_o2(other, operator.lshift)

    def __rshift__(self, other):
        return self._o2(other, operator.rshift)
    def __rrshift__(self, other):
        return self._r_o2(other, operator.rshift)

    def __and__(self, other):
        return self._o2(other, operator.and_)
    __rand__ = __and__

    def __or__(self, other):
        return self._o2(other, operator.or_)
    __ror__ = __or__

    def __xor__(self, other):
        return self._o2(other, operator.xor)
    __rxor__ = __xor__

    # Unary operations
    def __neg__(self):
        return self._o1(operator.neg)

    def __pos__(self):
        return self._o1(operator.pos)

    def __abs__(self):
        return self._o1(operator.abs)

    def __invert__(self):
        return self._o1(operator.invert)

    # vectory functions
    def get_length_sqrd(self): 
        return self.vec[0]**2 + self.vec[1]**2

    def get_length(self):
        return math.sqrt(self.vec[0]**2 + self.vec[1]**2)    
    def __setlength(self, value):
        self.normalize_return_length()
        self.vec[0] *= value
        self.vec[1] *= value
    length = property(get_length, __setlength, None, "gets or sets the magnitude of the vector")
       
    def rotate(self, angle_degrees):
        radians = math.radians(angle_degrees)
        cos = math.cos(radians)
        sin = math.sin(radians)
        x = self.vec[0]*cos - self.vec[1]*sin
        y = self.vec[0]*sin + self.vec[1]*cos
        self.vec[0] = x
        self.vec[1] = y
    
    def get_angle(self):
        if (self.get_length_sqrd() == 0):
            return 0
        return math.degrees(math.atan2(self.vec[1], self.vec[0]))

    def get_angle_between(self, other):
        cross = self.vec[0]*other[1] - self.vec[1]*other[0]
        dot = self.vec[0]*other[0] + self.vec[1]*other[1]
        return math.degrees(math.atan2(cross, dot))
    
    def __setangle(self, angle_degrees):
        self.vec[0] = self.length
        self.vec[1] = 0
        self.rotate(angle_degrees)
    angle = property(get_angle, __setangle, None, "gets or sets the angle of a vector")
        
    def normalized(self):
        length = self.length
        if length != 0:
            return self/length
        return vec2d(self)

    def perpendicular(self):
        return vec2d(-self.vec[1], self.vec[0])
    
    def perpendicular_normal(self):
        length = self.length
        if length != 0:
            return vec2d(-self.vec[1]/length, self.vec[0]/length)
        return vec2d(self)
        
    def normalize_return_length(self):
        length = self.length
        if length != 0:
            self.vec[0] /= length
            self.vec[1] /= length
        return length

    def dot(self, other):
        return self.vec[0]*other[0] + self.vec[1]*other[1]
        
    def get_distance(self, other):
        return math.sqrt((self.vec[0] - other[0])**2 + (self.vec[1] - other[1])**2)
        
    def projection(self, other):
        normal = other.normalized()
        projected_length = self.dot(normal)
        return normal*projected_length
    
    def cross(self, other):
        return self.vec[0]*other[1] - self.vec[1]*other[0]
    
    def interpolate_to(self, other, range):
        return vec2d(self.vec[0] + (other.vec[0] - self.vec[0])*range, self.vec[1] + (other.vec[1] - self.vec[1])*range)
    
    def convert_to_basis(self, x_vector, y_vector):
        return vec2d(self.dot(x_vector)/x_vector.get_length_sqrd(), self.dot(y_vector)/y_vector.get_length_sqrd())
        

########################################################################
class vec2dTuple(tuple):

    def __new__(cls, *args):
        if len(args) == 1:
            return tuple.__new__(cls, args[0])
        else:
            return tuple.__new__(cls, args)

    def get_x(self):
        return self[0]
    x = property(get_x)
    
    def get_y(self):
        return self[1]
    y = property(get_y)
    
       
    # String representaion (for debugging)
    def __repr__(self):
        return 'vec2dTuple(%s, %s)' % (self.x, self.y)
    
    # Comparison
    def __eq__(self, other):
        return self[0] == other[0] and self[1] == other[1]
    
    def __ne__(self, other):
        return self[0] != other[0] or self[1] != other[1]

    def __nonzero__(self):
        return self[0] or self[1]

    # Generic operator handlers
    def _o2(self, other, f):
        "Any two-operator operation where the left operand is a vec2dTuple"
        try:
            return vec2dTuple(f(self[0], other[0]),
                         f(self[1], other[1]))
        except TypeError:
            return vec2dTuple(f(self[0], other),
                         f(self[1], other))

    def _r_o2(self, other, f):
        "Any two-operator operation where the right operand is a vec2dTuple"
        try:
            return vec2dTuple(f(other[0], self[0]),
                         f(other[1], self[1]))
        except TypeError:
            return vec2dTuple(f(other, self[0]),
                         f(other, self[1]))

    def _o1(self, f):
        "Any unary operation on a vec2dTuple"
        return vec2dTuple(f(self[0]), f(self[1]))

    # Addition
    def __add__(self, other):
        return self._o2(other, operator.add)
    __radd__ = __add__

    # Subtraction
    def __sub__(self, other):
        return self._o2(other, operator.sub)
    def __rsub__(self, other):
        return self._r_o2(other, operator.sub)

    # Multiplication
    def __mul__(self, other):
        return self._o2(other, operator.mul)
    __rmul__ = __mul__

    # Division
    def __div__(self, other):
        return self._o2(other, operator.div)
    def __rdiv__(self, other):
        return self._r_o2(other, operator.div)

    def __floordiv__(self, other):
        return self._o2(other, operator.floordiv)
    def __rfloordiv__(self, other):
        return self._r_o2(other, operator.floordiv)

    def __truediv__(self, other):
        return self._o2(other, operator.truediv)
    def __rtruediv__(self, other):
        return self._r_o2(other, operator.truediv)

    # Modulo
    def __mod__(self, other):
        return self._o2(other, operator.mod)
    def __rmod__(self, other):
        return self._r_o2(other, operator.mod)

    def __divmod__(self, other):
        return self._o2(other, operator.divmod)
    def __rdivmod__(self, other):
        return self._r_o2(other, operator.divmod)

    # Exponentation
    def __pow__(self, other):
        return self._o2(other, operator.pow)
    def __rpow__(self, other):
        return self._r_o2(other, operator.pow)

    # Bitwise operators
    def __lshift__(self, other):
        return self._o2(other, operator.lshift)
    def __rlshift__(self, other):
        return self._r_o2(other, operator.lshift)

    def __rshift__(self, other):
        return self._o2(other, operator.rshift)
    def __rrshift__(self, other):
        return self._r_o2(other, operator.rshift)

    def __and__(self, other):
        return self._o2(other, operator.and_)
    __rand__ = __and__

    def __or__(self, other):
        return self._o2(other, operator.or_)
    __ror__ = __or__

    def __xor__(self, other):
        return self._o2(other, operator.xor)
    __rxor__ = __xor__

    # Unary operations
    def __neg__(self):
        return self._o1(operator.neg)

    def __pos__(self):
        return self._o1(operator.pos)

    def __abs__(self):
        return self._o1(operator.abs)

    def __invert__(self):
        return self._o1(operator.invert)

    # vectory functions
    def get_length_sqrd(self): 
        return self[0]**2 + self[1]**2

    def get_length(self):
        return math.sqrt(self[0]**2 + self[1]**2)    
    length = property(get_length, None, None, "gets or sets the magnitude of the vector")
       
    def get_angle(self):
        if (self.get_length_sqrd() == 0):
            return 0
        return math.degrees(math.atan2(self[1], self[0]))

    def get_angle_between(self, other):
        cross = self[0]*other[1] - self[1]*other[0]
        dot = self[0]*other[0] + self[1]*other[1]
        return math.degrees(math.atan2(cross, dot))
    
    angle = property(get_angle, None, None, "gets or sets the angle of a vector")
        
    def normalized(self):
        length = self.length
        if length != 0:
            return self/length
        return vec2dTuple(self)

    def perpendicular(self):
        return vec2dTuple(-self[1], self[0])
    
    def perpendicular_normal(self):
        length = self.length
        if length != 0:
            return vec2dTuple(-self[1]/length, self[0]/length)
        return vec2dTuple(self)
        
    def normalize_return_length(self):
        length = self.length
        if length != 0:
            self[0] /= length
            self[1] /= length
        return length

    def dot(self, other):
        return self[0]*other[0] + self[1]*other[1]
        
    def get_distance(self, other):
        return math.sqrt((self[0] - other[0])**2 + (self[1] - other[1])**2)
        
    def projection(self, other):
        normal = other.normalized()
        projected_length = self.dot(normal)
        return normal*projected_length
    
    def cross(self, other):
        return self[0]*other[1] - self[1]*other[0]
    
    def interpolate_to(self, other, range):
        return vec2dTuple(self[0] + (other[0] - self[0])*range, self[1] + (other[1] - self[1])*range)
    
    def convert_to_basis(self, x_vector, y_vector):
        return vec2dTuple(self.dot(x_vector)/x_vector.get_length_sqrd(), self.dot(y_vector)/y_vector.get_length_sqrd())

####
class vec2dExp(object):
    def __init__(self, dx, dy):
        self.dx = dx
        self.dy = dy

    def get_x(self):
        return self.dx
    x = property(get_x)
    
    def get_y(self):
        return self.dy
    y = property(get_y)
    
       
    # String representaion (for debugging)
    def __repr__(self):
        return 'vec2dExp(%s, %s)' % (self.x, self.y)
    
    # Comparison
    def __eq__(self, other):
        return self.dx == other.dx and self.dy == other.dy
    
    def __ne__(self, other):
        return self.dx != other.dx or self.dy != other.dy

    def __nonzero__(self):
        return self.dx or self.dy

    # Generic operator handlers
    def _o2(self, other, f):
        "Any two-operator operation where the left operand is a vec2dExp"
        try:
            return vec2dExp(f(self.dx, other.dx),
                         f(self.dy, other.dy))
        except AttributeError:
            return vec2dTuple(f(self.dx, other),
                         f(self.dy, other))

    def _r_o2(self, other, f):
        "Any two-operator operation where the right operand is a vec2dExp"
        try:
            return vec2dTuple(f(other.dx, self.dx),
                         f(other.dy, self.dy))
        except AttributeError:
            return vec2dTuple(f(other, self.dx),
                         f(other, self.dy))

    def _o1(self, f):
        "Any unary operation on a vec2dTuple"
        return vec2dTuple(f(self.dx), f(self.dy))

    # Addition
    def __add__(self, other):
        return self._o2(other, operator.add)
    __radd__ = __add__

    # Subtraction
    def __sub__(self, other):
        return self._o2(other, operator.sub)
    def __rsub__(self, other):
        return self._r_o2(other, operator.sub)

    # Multiplication
    def __mul__(self, other):
        return self._o2(other, operator.mul)
    __rmul__ = __mul__

    # Division
    def __div__(self, other):
        return self._o2(other, operator.div)
    def __rdiv__(self, other):
        return self._r_o2(other, operator.div)

    def __floordiv__(self, other):
        return self._o2(other, operator.floordiv)
    def __rfloordiv__(self, other):
        return self._r_o2(other, operator.floordiv)

    def __truediv__(self, other):
        return self._o2(other, operator.truediv)
    def __rtruediv__(self, other):
        return self._r_o2(other, operator.truediv)

    # Modulo
    def __mod__(self, other):
        return self._o2(other, operator.mod)
    def __rmod__(self, other):
        return self._r_o2(other, operator.mod)

    def __divmod__(self, other):
        return self._o2(other, operator.divmod)
    def __rdivmod__(self, other):
        return self._r_o2(other, operator.divmod)

    # Exponentation
    def __pow__(self, other):
        return self._o2(other, operator.pow)
    def __rpow__(self, other):
        return self._r_o2(other, operator.pow)

    # Bitwise operators
    def __lshift__(self, other):
        return self._o2(other, operator.lshift)
    def __rlshift__(self, other):
        return self._r_o2(other, operator.lshift)

    def __rshift__(self, other):
        return self._o2(other, operator.rshift)
    def __rrshift__(self, other):
        return self._r_o2(other, operator.rshift)

    def __and__(self, other):
        return self._o2(other, operator.and_)
    __rand__ = __and__

    def __or__(self, other):
        return self._o2(other, operator.or_)
    __ror__ = __or__

    def __xor__(self, other):
        return self._o2(other, operator.xor)
    __rxor__ = __xor__

    # Unary operations
    def __neg__(self):
        return self._o1(operator.neg)

    def __pos__(self):
        return self._o1(operator.pos)

    def __abs__(self):
        return self._o1(operator.abs)

    def __invert__(self):
        return self._o1(operator.invert)

    # vectory functions
    def get_length_sqrd(self): 
        return self.dx**2 + self.dy**2

    def get_length(self):
        return math.sqrt(self.dx**2 + self.dy**2)    
    length = property(get_length, None, None, "gets or sets the magnitude of the vector")
       
    def get_angle(self):
        if (self.get_length_sqrd() == 0):
            return 0
        return math.degrees(math.atan2(self.dx, self.dy))

    def get_angle_between(self, other):
        cross = self.dx*other.dy - self.dy*other.dx
        dot = self.dx*other.dx + self.dy*other.dy
        return math.degrees(math.atan2(cross, dot))
    
    angle = property(get_angle, None, None, "gets or sets the angle of a vector")
        
    def normalized(self):
        length = self.length
        if length != 0:
            return self/length
        return vec2dTuple(self)

    def perpendicular(self):
        return vec2dTuple(-self.dy, self.dx)
    
    def perpendicular_normal(self):
        length = self.length
        if length != 0:
            return vec2dTuple(-self.dy/length, self.dx/length)
        return vec2dTuple(self)
        
    def normalize_return_length(self):
        length = self.length
        if length != 0:
            self[0] /= length
            self[1] /= length
        return length

    def dot(self, other):
        return self.dx*other.dx + self.dy*other.dy
        
    def get_distance(self, other):
        return math.sqrt((self.dx - other.dx)**2 + (self.dy - other.dy)**2)
        
    def projection(self, other):
        normal = other.normalized()
        projected_length = self.dot(normal)
        return normal*projected_length
    
    def cross(self, other):
        return self.dx*other.dy - self.dy*other.dx
    
    def interpolate_to(self, other, range):
        return vec2dTuple(self.dx + (other.dx - self.dx)*range, self.dy + (other.dy - self.dy)*range)
    
    def convert_to_basis(self, x_vector, y_vector):
        return vec2dTuple(self.dot(x_vector)/x_vector.get_length_sqrd(), self.dot(y_vector)/y_vector.get_length_sqrd())


        
########################################################################
## Unit Testing                                                       ##
########################################################################
if __name__ == "__main__":

    import unittest

    ####################################################################
    class UnitTestVec2D(unittest.TestCase):
    
        def setUp(self):
            pass
        
        def testCreationAndAccess(self):
            v = vec2d(111,222)
            self.assert_(v.x == 111 and v.y == 222)
            v.x = 333
            v[1] = 444
            self.assert_(v[0] == 333 and v[1] == 444)

        def testMath(self):
            v = vec2d(111,222)
            self.assert_(v + 1 == vec2d(112,223))
            self.assert_(v - 2 == [109,220])
            self.assert_(v * 3 == (333,666))
            self.assert_(v / 2.0 == vec2d(55.5, 111))
            self.assert_(v / 2 == (55, 111))
            self.assert_(v ** vec2d(2,3) == [12321, 10941048])
            self.assert_(v + [-11, 78] == vec2d(100, 300))
            self.assert_(v / [11,2] == [10,111])

        def testReverseMath(self):
            v = vec2d(111,222)
            self.assert_(1 + v == vec2d(112,223))
            self.assert_(2 - v == [-109,-220])
            self.assert_(3 * v == (333,666))
            self.assert_([222,999] / v == [2,4])
            self.assert_([111,222] ** vec2d(2,3) == [12321, 10941048])
            self.assert_([-11, 78] + v == vec2d(100, 300))

        def testUnary(self):
            v = vec2d(111,222)
            v = -v
            self.assert_(v == [-111,-222])
            v = abs(v)
            self.assert_(v == [111,222])

        def testLength(self):
            v = vec2d(3,4)
            self.assert_(v.length == 5)
            self.assert_(v.get_length_sqrd() == 25)
            self.assert_(v.normalize_return_length() == 5)
            self.assert_(v.length == 1)
            v.length = 5
            self.assert_(v == vec2d(3,4))
            v2 = vec2d(10, -2)
            self.assert_(v.get_distance(v2) == (v - v2).get_length())
            
        def testAngles(self):            
            v = vec2d(0, 3)
            self.assertEquals(v.angle, 90)
            v2 = vec2d(v)
            v.rotate(-90)
            self.assertEqual(v.get_angle_between(v2), 90)
            v2.angle -= 90
            self.assertEqual(v.length, v2.length)
            self.assertEquals(v2.angle, 0)
            self.assertEqual(v2, [3, 0])
            self.assert_((v - v2).length < .00001)
            self.assertEqual(v.length, v2.length)
            v2.rotate(300)
            self.assertAlmostEquals(v.get_angle_between(v2), -60)
            v2.rotate(v2.get_angle_between(v))
            angle = v.get_angle_between(v2)
            self.assertAlmostEquals(v.get_angle_between(v2), 0)  

        def testHighLevel(self):
            basis0 = vec2d(5.0, 0)
            basis1 = vec2d(0, .5)
            v = vec2d(10, 1)
            self.assert_(v.convert_to_basis(basis0, basis1) == [2, 2])
            self.assert_(v.projection(basis0) == (10, 0))
            self.assert_(basis0.dot(basis1) == 0)
            
        def testCross(self):
            lhs = vec2d(1, .5)
            rhs = vec2d(4,6)
            self.assert_(lhs.cross(rhs) == 4)
    
    ####################################################################
    class UnitTestvec2dTuple(unittest.TestCase):
    
        def setUp(self):
            pass
        
        def testCreationAndAccess(self):
            v = vec2dTuple(111,222)
            self.assert_(v.x == 111 and v.y == 222)

        def testMath(self):
            v = vec2dTuple(111,222)
            self.assert_(v + 1 == vec2dTuple(112,223))
            self.assert_(v - 2 == [109,220])
            self.assert_(v * 3 == (333,666))
            self.assert_(v / 2.0 == vec2dTuple(55.5, 111))
            self.assert_(v / 2 == (55, 111))
            self.assert_(v ** vec2dTuple(2,3) == [12321, 10941048])
            self.assert_(v + [-11, 78] == vec2dTuple(100, 300))
            self.assert_(v / [11,2] == [10,111])
            self.assert_(v + (89, -122) == [200,100])

        def testReverseMath(self):
            v = vec2dTuple(111,222)
            self.assert_(1 + v == vec2dTuple(112,223))
            self.assert_(2 - v == [-109,-220])
            self.assert_(3 * v == (333,666))
            self.assert_([222,999] / v == [2,4])
            self.assert_([111,222] ** vec2dTuple(2,3) == [12321, 10941048])
            self.assert_([-11, 78] + v == vec2dTuple(100, 300))
            self.assert_((89,-122) + v == [200,100])

        def testUnary(self):
            v = vec2dTuple(111,222)
            v = -v
            self.assert_(v == [-111,-222])
            v = abs(v)
            self.assert_(v == [111,222])

        def testLength(self):
            v = vec2dTuple(3,4)
            self.assert_(v.length == 5)
            self.assert_(v.get_length_sqrd() == 25)
            v2 = v.normalized()
            self.assert_(v2.length == 1)
            v2 = vec2dTuple(10, -2)
            self.assert_(v.get_distance(v2) == (v - v2).get_length())
            
        def testAngles(self):            
            v = vec2dTuple(0, 3)
            self.assertEquals(v.angle, 90)
            v2 = vec2dTuple(v)
            self.assertEqual(v.length, v2.length)

        def testHighLevel(self):
            basis0 = vec2dTuple(5.0, 0)
            basis1 = vec2dTuple(0, .5)
            v = vec2dTuple(10, 1)
            self.assert_(v.convert_to_basis(basis0, basis1) == [2, 2])
            self.assert_(v.projection(basis0) == (10, 0))
            self.assert_(basis0.dot(basis1) == 0)
            
        def testCross(self):
            lhs = vec2dTuple(1, .5)
            rhs = vec2dTuple(4,6)
            self.assert_(lhs.cross(rhs) == 4)
            
    ########################################################################
    import time
    
    def perfTest(func, num_runs = 10000, fps=60, budget=.05):
        start = time.clock()
        func(num_runs)
        end = time.clock()
        elapsed = end - start
        print "time: ", elapsed
        print "%s runs at %.1f loops/s" % (str(func), num_runs/elapsed)
        print "that's %.2f loops to fill %d fps" % (num_runs/elapsed/fps, fps)
        print "or %.2f at a %.1f%% budget at %d fps" % (budget*num_runs/elapsed/fps, budget*100.0, fps)
        
    class TestData:
        screen_offsetx = 100
        screen_offsety = 120
        screen_offset = vec2d(screen_offsetx, screen_offsety)
        screen_offset_tuple = vec2dTuple(screen_offsetx, screen_offsety)
        screen_offset_exp = vec2dExp(screen_offsetx, screen_offsety)
        screen_scale = .5
        object_positionx = 130.5
        object_positiony = 191.5
        object_position = vec2d(object_positionx, object_positiony)
        object_position_tuple = vec2dTuple(object_positionx, object_positiony)
        object_position_exp = vec2dExp(object_positionx, object_positiony)
        
    def ScreenTranslationTestVec2d(loop_count):
        for i in xrange(loop_count):
            final_pos = (TestData.object_position_tuple - TestData.screen_offset_tuple)*TestData.screen_scale
    
    def ScreenTranslationTestVec2dTuple(loop_count):
        for i in xrange(loop_count):
            final_pos = (TestData.object_position_tuple - TestData.screen_offset_tuple)*TestData.screen_scale
    
    
    def ScreenTranslationTestVec2dExp(loop_count):
        for i in xrange(loop_count):
            final_pos = (TestData.object_position_exp - TestData.screen_offset_exp)*TestData.screen_scale
    

    def ScreenTranslationTestExplicit(loop_count):
        for i in xrange(loop_count):
            final_posx = (TestData.object_positionx - TestData.screen_offsetx)*TestData.screen_scale
            final_posy = (TestData.object_positiony - TestData.screen_offsety)*TestData.screen_scale
    
    ########################################################################
    print "testing vector code..."
    perfTest(ScreenTranslationTestVec2d)
    perfTest(ScreenTranslationTestVec2dTuple)
    perfTest(ScreenTranslationTestExplicit)
    
    print "\nnow psychoing everything..."
    import psyco
    psyco.bind(ScreenTranslationTestVec2d)
    psyco.bind(ScreenTranslationTestVec2dTuple)
    psyco.bind(ScreenTranslationTestVec2dExp)
    psyco.bind(ScreenTranslationTestExplicit)
    perfTest(ScreenTranslationTestVec2d)
    perfTest(ScreenTranslationTestVec2dTuple)
    perfTest(ScreenTranslationTestVec2dExp)
    perfTest(ScreenTranslationTestExplicit)

    #####################################################################   
    unittest.main()

    #####################################################################   

Attachment: signature.asc
Description: OpenPGP digital signature