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

Re: [pygame] 2D vector class more tests



Hi

I just wondered what the fastest way would be and joined the vector fun. I did some more test with interessting result:

To be fast write your class as explicit (and simple) as possible. As someone stated befor, dont make any if's or try..catch in your method, that will slow it down even more.

I can not confirm that the math operators from the operators module are faster than the built in one (I think because of the added function overhead?).

The overhead of the map() function is too big for 2d vectors. I think only for long or n-dim vectors it will be faster as explicit (well for a generic vector it is not bad perhaps, perhaps a simple for loop is faster, I dont know).

I did not write a full featured classes, only the parts I needed (and only the fastest one I would use to write a full featured class).
But the result speaks for its self.


I dont know exactly why the given classes are so slow, perhaps its because of the "generic operator handlers".

My Vector2dTuple class is the fastest class, but you cant asign a single value directly (because its a tuple). My class using slots is the second one. But still, both are more than 2 time slower than using functions! (even worse using psyco) The question is if it is worse to use operator overloading or not. Perhaps it is easier to use and read but the speed penalty is there.

I modified the script that was posted here some time ago. I only rearranged the output to have a better overview.

Tell me about more optimisations if you can find one ( perhaps in the "constructors" of the classes).
Perhaps I made a mistake somewhere, if you found one, please tell me. Or if you find a faster way, I'm surely interested in. :-)


I will appreciate any suggestions, opinions or discussion.

~DR0ID

I did some back-of-the-envelope benchmarking of various
implementations of Vector operations. Attached is the program that I
used. My findings were:

- Numeric is faster than map is faster than a list comprehension
- explicitly storing two variables is even faster (not shown)
- using functions from the operator module is faster than built-in
math operators
- tuples seem to be a little faster to iterate over than lists

And some vector-unrelated findings:

- function calls are faster than method calls
- accessing a bound method is slower than accessing an instance variable

In benchmarking my game, I found that I was creating a *lot* of new
vector objects, and this does things to your performance. For this
reason my vectors are not immutable, but take advantage of in-place
operations to conserve objects.


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

########################################################################
import operator
import math
import vec
from vec import vsub
from vec import vmul

########################################################################
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()
        res = func(num_runs)
        end = time.clock()
        elapsed = end - start
        print "\n-------------------------------------------------------------"
        print str(func.__name__.ljust( 35) ), "time: ", elapsed, "runs at %.1f loops/s" % (num_runs/elapsed)
        print "\t\tthat's %.2f loops to fill %d fps or %.2f at a %.1f%% budget at %d fps" % (num_runs/elapsed/fps, fps, budget*num_runs/elapsed/fps, budget*100.0, fps)
##        print "\t\tor %.2f at a %.1f%% budget at %d fps, result: %s" % (budget*num_runs/elapsed/fps, budget*100.0, fps, str(res))
        print res
        
    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_offset_list  = [screen_offsetx, screen_offsety]
        
        screen_offset_Vector        = vec.Vector((screen_offsetx, screen_offsety))
##        screen_offset_list3         = [screen_offsetx, screen_offsety]
        screen_offset_aVector       = vec.aVector(screen_offsetx, screen_offsety)
        screen_offset_Vector2dList  = vec.Vector2dList(screen_offsetx, screen_offsety)
        screen_offset_Vector2dTuple = vec.Vector2dTuple(screen_offsetx, screen_offsety)
        screen_offset_Vector2dAttr  = vec.Vector2dAttr(screen_offsetx, screen_offsety)
        screen_offset_Vector2dProp  = vec.Vector2dProp(screen_offsetx, screen_offsety)
        screen_offset_Vector2dSlots = vec.Vector2dSlots(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)
        
        object_position_list  = [object_positionx, object_positiony]
        
        object_position_Vector        = vec.Vector((object_positionx, object_positiony))
##        object_position_list3         = [object_positionx, object_positiony]
        object_position_aVector       = vec.aVector(object_positionx, object_positiony)
        object_position_Vector2dList  = vec.Vector2dList(object_positionx, object_positiony)
        object_position_Vector2dTuple = vec.Vector2dTuple(object_positionx, object_positiony)
        object_position_Vector2dAttr  = vec.Vector2dAttr(object_positionx, object_positiony)
        object_position_Vector2dProp  = vec.Vector2dProp(object_positionx, object_positiony)
        object_position_Vector2dSlots = vec.Vector2dSlots(object_positionx, object_positiony)
        
    def ScreenTranslationTestVec2d(loop_count):
        final_pos = 0
        for i in xrange(loop_count):
            final_pos = (TestData.object_position_tuple - TestData.screen_offset_tuple)*TestData.screen_scale
        return final_pos
    
    def ScreenTranslationTestVec2dTuple(loop_count):
        final_pos = 0
        for i in xrange(loop_count):
            final_pos = (TestData.object_position_tuple - TestData.screen_offset_tuple)*TestData.screen_scale
        return final_pos
    
    
    def ScreenTranslationTestVec2dExp(loop_count):
        final_pos = 0
        for i in xrange(loop_count):
            final_pos = (TestData.object_position_exp - TestData.screen_offset_exp)*TestData.screen_scale
        return final_pos


    def STT_functions_map(loop_count):
        final_pos = 0
        for i in xrange(loop_count):
            final_pos =vec.vmul( vec.vsub(TestData.object_position_list, TestData.screen_offset_list), TestData.screen_scale)
        return final_pos


    def STT_functions2_map(loop_count):
        final_pos = 0
        for i in xrange(loop_count):
            final_pos =vmul( vsub(TestData.object_position_list, TestData.screen_offset_list), TestData.screen_scale)
        return final_pos

    def STT_functions3(loop_count):
        final_pos = 0
        for i in xrange(loop_count):
            final_pos =vec.mul2d( vec.sub2d(TestData.object_position_list, TestData.screen_offset_list), TestData.screen_scale)
        return final_pos

    def STT_functions4op(loop_count):
        final_pos = 0
        for i in xrange(loop_count):
            final_pos =vec.mul2dop( vec.sub2dop(TestData.object_position_list, TestData.screen_offset_list), TestData.screen_scale)
        return final_pos

##Vector
    def STT_Vector(loop_count):
        final_pos = 0
        for i in xrange(loop_count):
            final_pos = (TestData.object_position_Vector - TestData.screen_offset_Vector)*TestData.screen_scale
        return final_pos
    
##aVector
    def STT_aVector(loop_count):
        final_pos = 0
        for i in xrange(loop_count):
            final_pos = (TestData.object_position_aVector - TestData.screen_offset_aVector)*TestData.screen_scale
        return final_pos

##Vector2dList
    def STT_Vector2dList(loop_count):
        final_pos = 0
        for i in xrange(loop_count):
            final_pos = (TestData.object_position_Vector2dList - TestData.screen_offset_Vector2dList)*TestData.screen_scale
        return final_pos

##Vector2dTuple
    def STT_Vector2dTuple(loop_count):
        final_pos = 0
        for i in xrange(loop_count):
            final_pos = (TestData.object_position_Vector2dTuple - TestData.screen_offset_Vector2dTuple)*TestData.screen_scale
        return final_pos

##Vector2dAttr
    def STT_Vector2dAttr(loop_count):
        final_pos = 0
        for i in xrange(loop_count):
            final_pos = (TestData.object_position_Vector2dAttr - TestData.screen_offset_Vector2dAttr)*TestData.screen_scale
        return final_pos

##Vector2dProp
    def STT_Vector2dProp(loop_count):
        final_pos = 0
        for i in xrange(loop_count):
            final_pos = (TestData.object_position_Vector2dProp - TestData.screen_offset_Vector2dProp)*TestData.screen_scale
        return final_pos

##Vector2dSlots
    def STT_Vector2dSlots(loop_count):
        final_pos = 0
        for i in xrange(loop_count):
            final_pos = (TestData.object_position_Vector2dSlots - TestData.screen_offset_Vector2dSlots)*TestData.screen_scale
        return final_pos

     
##STT_Vector
##STT_Vector2dList
##STT_Vector2dTuple
##STT_Vector2dAttr
##STT_Vector2dProp
##STT_Vector2dSlots

    def ScreenTranslationTestExplicit(loop_count):
        final_posx = 0
        final_posy = 0
        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
        return (final_posx, final_posy)
    
    ########################################################################
    print ">>>>>>>>>>>testing vector code<<<<<<<<<<<<<<"
    perfTest(ScreenTranslationTestVec2d)
    perfTest(ScreenTranslationTestVec2dTuple)
    perfTest(ScreenTranslationTestVec2dExp)
    perfTest(ScreenTranslationTestExplicit)
    
    perfTest(STT_functions_map)
    perfTest(STT_functions2_map)
    perfTest(STT_functions3)
    perfTest(STT_functions4op)
    
    perfTest(STT_Vector)
    perfTest(STT_Vector2dList)
    perfTest(STT_Vector2dTuple)
    perfTest(STT_Vector2dAttr)
    perfTest(STT_Vector2dProp)
    perfTest(STT_Vector2dSlots)

    print "\n\n>>>>>>>>>>>>now psychoing everything<<<<<<<<<<<<<<<<<<<<<<"
    import psyco
    psyco.bind(ScreenTranslationTestVec2d)
    psyco.bind(ScreenTranslationTestVec2dTuple)
    psyco.bind(ScreenTranslationTestVec2dExp)
    psyco.bind(ScreenTranslationTestExplicit)
    
    psyco.bind(STT_functions_map)
    psyco.bind(STT_functions2_map)
    psyco.bind(STT_functions3)
    psyco.bind(STT_functions4op)

    psyco.bind(STT_Vector)
    psyco.bind(STT_Vector2dList)
    psyco.bind(STT_Vector2dTuple)
    psyco.bind(STT_Vector2dAttr)
    psyco.bind(STT_Vector2dProp)
    psyco.bind(STT_Vector2dSlots)

    perfTest(ScreenTranslationTestVec2d)
    perfTest(ScreenTranslationTestVec2dTuple)
    perfTest(ScreenTranslationTestVec2dExp)
    perfTest(ScreenTranslationTestExplicit)
    
    perfTest(STT_functions_map)
    perfTest(STT_functions2_map)
    perfTest(STT_functions3)
    perfTest(STT_functions4op)
    
    perfTest(STT_Vector)
    perfTest(STT_Vector2dList)
    perfTest(STT_Vector2dTuple)
    perfTest(STT_Vector2dAttr)
    perfTest(STT_Vector2dProp)
    perfTest(STT_Vector2dSlots)
    
    print "\n\n"
    #####################################################################   
    unittest.main()
##    suite = unittest.makeSuite(UnitTestVec2D)
##    unittest.TextTestRunner(verbosity=2).run(suite)



    #####################################################################   
#Copyright 2006 DR0ID <dr0id@xxxxxxxxxx> http://mypage.bluewin.ch/DR0ID
#
#
#
"""
#TODO: documentation!
"""

__author__   = "$Author: DR0ID $"
__version__  = "$Revision: 27 $"
__date__     = "$Date: 2006-10-05 13:23:21 +0200 (Do, 05 Okt 2006) $"
__license__  = ''
__copyright__= "DR0ID (c) 2006"



import operator

import math

class Vector(list):
    
    def __init__(self, *args):
        list.__init__(self, *args)
        
    def __add__(self, v):
        return Vector(map(operator.add, self, v))
    
    def __mul__(self, s):
        vs = [s]*len(self)
        return Vector(map(operator.mul, self, vs))
    
    def __sub__(self, v):
        return Vector(map(operator.sub, self, v))
    
    def __repr__(self):
        return "Vector<"+str(list(self))+">"


class Vector2dList(list):
    
    def __init__(self, *args):
        list.__init__(self, args)
        
    def __add__(self, v):
        return Vector2dList(self[0]+v[0], self[1]+v[1]) #, self[2]+v[2])
    
    def __mul__(self, s):
        return Vector2dList(self[0]*s, self[0]*s) #, self[0]*s)
    
    def __sub__(self, v):
        return Vector2dList(self[0]-v[0], self[1]-v[1]) #, self[2]-v[2])
    
    def __repr__(self):
        return "Vector2dList<"+str(list(self))+">"

class Vector2dTuple(object):
    
    def __init__(self, *args):
        object.__init__(self)
        self.val = args
        
    def __add__(self, v):
        return Vector2dTuple(self.val[0]+v.val[0], self.val[1]+v.val[1]) #, self.val[2]+v[2])
    
    def __mul__(self, s):
        return Vector2dTuple(self.val[0]*s, self.val[0]*s) #, self.val[0]*s)
    
    def __sub__(self, v):
        return Vector2dTuple(self.val[0]-v.val[0], self.val[1]-v.val[1]) #, self.val[2]-v[2])

    def __repr__(self):
        return "Vector2dTuple<"+str(list(self.val))+">"

class Vector2dAttr(object):
    
    def __init__(self, *args):
        object.__init__(self)
        self.x = args[0]
        self.y = args[1]
        
    def __add__(self, v):
        return Vector2dAttr(self.x+v.x, self.y+v.y) #, self.z+v.z)
    
    def __mul__(self, s):
        return Vector2dAttr(self.x*s, self.y*s) #, self.z*s)
    
    def __sub__(self, v):
        return Vector2dAttr(self.x-v.x, self.y-v.y) #, self.z-v.z)
    
    def __repr__(self):
        return "Vector2dAttr<%f, %f>" % (self.x, self.y) #, self.z)
    
class Vector2dProp(object):
    
    def __init__(self, *args):
        object.__init__(self)
        self._x = args[0]
        self._y = args[1]
    
    def getx(self)   : return self._x
    def setx(self, x): self._x = x
    x = property(getx, setx)
    
    def gety(self)   : return self._y
    def sety(self, y): self._y = y
    y = property(gety, sety)
    
##    def getz(self)   : return self._z
##    def setz(self, z): self._z = z
##    z = property(getz, setz)
        
    def __add__(self, v):
        return Vector2dProp(self.x+v.x, self.y+v.y) #, self.z+v.z)
    
    def __mul__(self, s):
        return Vector2dProp(self.x*s, self.y*s) #, self.z*s)
    
    def __sub__(self, v):
        return Vector2dProp(self.x-v.x, self.y-v.y) #, self.z-v.z)
    
    def __repr__(self):
        return "Vector2dProp<%f, %f>" % (self.x, self.y)#, self.z)


class Vector2dSlots(object):
    
    __slot__ = ['x','y']
    
    def __init__(self, *args):
        object.__init__(self)
        self.x = args[0]
        self.y = args[1]
        
    def __add__(self, v):
        return Vector2dSlots(self.x+v.x, self.y+v.y) #, self.z+v.z)
    
    def __mul__(self, s):
        return Vector2dSlots(self.x*s, self.y*s) #, self.z*s)
    
    def __sub__(self, v):
        return Vector2dSlots(self.x-v.x, self.y-v.y) #, self.z-v.z)

    def __repr__(self):
        return "Vector2dSlots<%f, %f>" % (self.x, self.y) #, self.z)

##    def __repr__(self):
##        return super(type(self), self).__repr__()
    
    
import array

class aVector(array.array):
    
##    def __init__(self, *args):
##        array.array.__init__(self, 'f', *args)
    def __new__(cls, *args):
        return array.array.__new__(cls, 'd', args)
        
    def __sub__(self, v):
        return aVector(vsub(self, v))
    
    def __mul__(self, s):
        return aVector(vmul(self, s))


##__abs__
##__add__
##__and__
##__concat__
##__contains__
##__delitem__
##__delslice__
##__div__
##__doc__
##__eq__
##__floordiv__
##__ge__
##__getitem__
##__getslice__
##__gt__
##__inv__
##__invert__
##__le__
##__lshift__
##__lt__
##__mod__
##__mul__
##__name__
##__ne__
##__neg__
##__not__
##__or__
##__pos__
##__pow__
##__repeat__
##__rshift__
##__setitem__
##__setslice__
##__sub__
##__truediv__
##__xor__

##abs

##add
##from operator import add
##def vadd(v1, v2):
##    return map(operator.add, v1, v2)

##and_
##attrgetter
##concat
##contains
##countOf
##delitem
##delslice
##div
##from operator import div
##def vdiv(v, s):
##    vs = (s)*len(v)
##    return map(operator.div, v, vs)
##    return map(lambda e:operator.div(e,s), v) # slower
##eq
##floordiv
##ge
##getitem
##getslice
##gt
##indexOf
##inv
##invert
##isCallable
##isMappingType
##isNumberType
##isSequenceType
##is_
##is_not
##itemgetter
##le
##lshift
##lt
##mod
##mul
##from operator import mul
def vmul(v, s):
    vs = [s]*len(v)
    return map(operator.mul,v, vs)
##    return map(lambda e: operator.mul(e, s), v)#slower

def mul2d(v, s):
##    return list(v[0]*s, v[1]*s)
    return [v[0]*s, v[1]*s]

def mul2dop(v, s):
##    return list(operator.mul(v[0], s), operator.mul(v[1], s))
    return [operator.mul(v[0], s), operator.mul(v[1], s)]
    
##def dot(v1, v2):
##    return sum(map(operator.mul, v1, v2))
##
##def cross3d(v1, v2):
##    return [ v1[1]*v2[2]-v1[2]*v2[1], v1[2]*v2[0]-v1[2]*v2[0], v1[0]*v2[1]-v1[1]*v2[0] ]
##
##def cross2d(v1, v2):
##    return [ 0, 0, v1[0]*v2[1]-v1[1]*v2[0] ]
##
##from math import sqrt
##def length(v):
##    return sqrt( sum( map(operator.mul, v, v) ) )
##magnitude = length
##
##def length2(v):
##    return sum( map(operator.mul, v, v) )
##
##def vnormalized(v):
##    n = len(v)
##    l = (length(v),)*n
##    if l!=0:
##        return map(operator.div, v, l)
##    else:
##        return v

##ne
##neg
##not_
##or_
##pos
##pow
##repeat
##rshift
##sequenceIncludes
##setitem
##setslice
##sub
##from operator import sub
def vsub(v1, v2):
    return map(operator.sub, v1, v2)

def sub2d(v1, v2):
##    return list(v1[0]-v2[0], v1[1]-v2[1])
    return [v1[0]-v2[0], v1[1]-v2[1]]

def sub2dop(v1, v2):
##    return list(operator.sub(v1[0],v2[0]), operator.sub(v1[1],v2[1]))
    return [operator.sub(v1[0],v2[0]), operator.sub(v1[1],v2[1])]

##truediv
##truth
##xor


########################################################################
## Unit Testing                                                       ##
########################################################################
def run_unittest():
    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)
    

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

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

    


if __name__ == "__main__":
    run_unittest()