changeset 166:a6bbb732b27b

Added .hgeol file to automatically convert line endings.
author KarstenBock@gmx.net
date Thu, 12 Jan 2012 18:42:48 +0100
parents 95461b06bac1
children b3b82c2aebee
files .hgeol bGrease/__init__.py bGrease/color.py bGrease/component/base.py bGrease/component/schema.py bGrease/controller/integrator.py bGrease/geometry.py bGrease/impl/__init__.py bGrease/impl/controls.py bGrease/mode.py bGrease/renderer/camera.py
diffstat 11 files changed, 1528 insertions(+), 1524 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgeol	Thu Jan 12 18:42:48 2012 +0100
@@ -0,0 +1,4 @@
+[patterns]
+**.py = native
+**.txt = native
+**.cfg = native
\ No newline at end of file
--- a/bGrease/__init__.py	Thu Dec 15 21:14:13 2011 +0100
+++ b/bGrease/__init__.py	Thu Jan 12 18:42:48 2012 +0100
@@ -1,75 +1,75 @@
-#############################################################################
-#
-# Copyright (c) 2010 by Casey Duncan and contributors
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the MIT License
-# A copy of the license should accompany this distribution.
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-#
-#############################################################################
-
-__versioninfo__ = (0, 3, 0)
-__version__ = '.'.join(str(n) for n in __versioninfo__)
-
-__all__ = ('BaseWorld', 'Entity', 'System', 'Renderer')
-
-import component
-import geometry
-import collision
-from entity import Entity
-from world import BaseWorld
-
-import abc
-
-class System(object):
-	"""Grease system abstract base class. Systems define behaviorial aspects
-	of a |World|. All systems must define a :meth:`step`
-	method that is invoked by the world each timestep.  User-defined systems
-	are not required to subclass this class.
-	
-	See :ref:`an example system from the tutorial <tut-system-example>`.
-	"""
-	__metaclass__ = abc.ABCMeta
-
-	world = None
-	"""The |World| this system belongs to"""
-
-	def set_world(self, world):
-		"""Bind the system to a world"""
-		self.world = world
-	
-	@abc.abstractmethod
-	def step(self, dt):
-		"""Execute a time step for the system. Must be defined
-		by all system classes.
-
-		:param dt: Time since last step invocation
-		:type dt: float
-		"""
-
-class Renderer(object):
-	"""Grease renderer abstract base class. Renderers define the presentation
-	of a |World|. All renderers must define a :meth:`draw`
-	method that is invoked by the world when the display needs to be redrawn.
-	User-defined renderers are not required to subclass this class.
-
-	See :ref:`an example renderer from the tutorial <tut-renderer-example>`.
-	"""
-	__metaclass__ = abc.ABCMeta
-
-	world = None
-	"""The |World| this renderer belongs to"""
-
-	def set_world(self, world):
-		"""Bind the system to a world"""
-		self.world = world
-
-	@abc.abstractmethod
-	def draw(self):
-		"""Issue drawing commands for this renderer. Must be defined
-		for all renderer classes.
-		"""
-
+#############################################################################
+#
+# Copyright (c) 2010 by Casey Duncan and contributors
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the MIT License
+# A copy of the license should accompany this distribution.
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+#
+#############################################################################
+
+__versioninfo__ = (0, 3, 0)
+__version__ = '.'.join(str(n) for n in __versioninfo__)
+
+__all__ = ('BaseWorld', 'Entity', 'System', 'Renderer')
+
+import component
+import geometry
+import collision
+from entity import Entity
+from world import BaseWorld
+
+import abc
+
+class System(object):
+	"""Grease system abstract base class. Systems define behaviorial aspects
+	of a |World|. All systems must define a :meth:`step`
+	method that is invoked by the world each timestep.  User-defined systems
+	are not required to subclass this class.
+	
+	See :ref:`an example system from the tutorial <tut-system-example>`.
+	"""
+	__metaclass__ = abc.ABCMeta
+
+	world = None
+	"""The |World| this system belongs to"""
+
+	def set_world(self, world):
+		"""Bind the system to a world"""
+		self.world = world
+	
+	@abc.abstractmethod
+	def step(self, dt):
+		"""Execute a time step for the system. Must be defined
+		by all system classes.
+
+		:param dt: Time since last step invocation
+		:type dt: float
+		"""
+
+class Renderer(object):
+	"""Grease renderer abstract base class. Renderers define the presentation
+	of a |World|. All renderers must define a :meth:`draw`
+	method that is invoked by the world when the display needs to be redrawn.
+	User-defined renderers are not required to subclass this class.
+
+	See :ref:`an example renderer from the tutorial <tut-renderer-example>`.
+	"""
+	__metaclass__ = abc.ABCMeta
+
+	world = None
+	"""The |World| this renderer belongs to"""
+
+	def set_world(self, world):
+		"""Bind the system to a world"""
+		self.world = world
+
+	@abc.abstractmethod
+	def draw(self):
+		"""Issue drawing commands for this renderer. Must be defined
+		for all renderer classes.
+		"""
+
--- a/bGrease/color.py	Thu Dec 15 21:14:13 2011 +0100
+++ b/bGrease/color.py	Thu Jan 12 18:42:48 2012 +0100
@@ -1,64 +1,64 @@
-
-class RGBA(object):
-	"""Four channel color representation.
-
-	RGBA colors are floating point color representations with color channel
-	values between (0..1). Colors may be initialized from 3 or 4 floating
-	point numbers or a hex string::
-
-		RGBA(1.0, 1.0, 1.0) # Alpha defaults to 1.0
-		RGBA(1.0, 1.0, 0, 0.5)
-		RGBA("#333")
-		RGBA("#7F7F7F")
-	
-	Individual color channels can be accessed by attribute name, or the
-	color object can be treated as a sequence of 4 floats.
-	"""
-
-	def __init__(self, r_or_colorstr, g=None, b=None, a=None):
-		if isinstance(r_or_colorstr, str):
-			assert g is b is a is None, "Ambiguous color arguments" 
-			self.r, self.g, self.b, self.a = self._parse_colorstr(r_or_colorstr)
-		elif g is b is a is None:
-			try:
-				self.r, self.g, self.b, self.a = r_or_colorstr
-			except ValueError:
-				self.r, self.g, self.b = r_or_colorstr
-				self.a = 1.0
-		else:
-			self.r = r_or_colorstr
-			self.g = g
-			self.b = b
-			self.a = a
-		if self.a is None:
-			self.a = 1.0
-	
-	def _parse_colorstr(self, colorstr):
-		length = len(colorstr)
-		if not colorstr.startswith("#") or length not in (4, 5, 7, 9):
-			raise ValueError("Invalid color string: " + colorstr)
-		if length <= 5:
-			parsed = [int(c*2, 16) / 255.0 for c in colorstr[1:]]
-		else:
-			parsed = [int(colorstr[i:i+2], 16) / 255.0 for i in range(1, length, 2)]
-		if len(parsed) == 3:
-			parsed.append(1.0)
-		return parsed
-	
-	def __len__(self):
-		return 4
-	
-	def __getitem__(self, item):
-		return (self.r, self.g, self.b, self.a)[item]
-	
-	def __iter__(self):
-		return iter((self.r, self.g, self.b, self.a))
-	
-	def __eq__(self, other):
-		return tuple(self) == tuple(other)
-	
-	def __repr__(self):
-		return "%s(%.2f, %.2f, %.2f, %.2f)" % (self.__class__.__name__, 
-			self.r, self.g, self.b, self.a)
-			
-			
+
+class RGBA(object):
+	"""Four channel color representation.
+
+	RGBA colors are floating point color representations with color channel
+	values between (0..1). Colors may be initialized from 3 or 4 floating
+	point numbers or a hex string::
+
+		RGBA(1.0, 1.0, 1.0) # Alpha defaults to 1.0
+		RGBA(1.0, 1.0, 0, 0.5)
+		RGBA("#333")
+		RGBA("#7F7F7F")
+	
+	Individual color channels can be accessed by attribute name, or the
+	color object can be treated as a sequence of 4 floats.
+	"""
+
+	def __init__(self, r_or_colorstr, g=None, b=None, a=None):
+		if isinstance(r_or_colorstr, str):
+			assert g is b is a is None, "Ambiguous color arguments" 
+			self.r, self.g, self.b, self.a = self._parse_colorstr(r_or_colorstr)
+		elif g is b is a is None:
+			try:
+				self.r, self.g, self.b, self.a = r_or_colorstr
+			except ValueError:
+				self.r, self.g, self.b = r_or_colorstr
+				self.a = 1.0
+		else:
+			self.r = r_or_colorstr
+			self.g = g
+			self.b = b
+			self.a = a
+		if self.a is None:
+			self.a = 1.0
+	
+	def _parse_colorstr(self, colorstr):
+		length = len(colorstr)
+		if not colorstr.startswith("#") or length not in (4, 5, 7, 9):
+			raise ValueError("Invalid color string: " + colorstr)
+		if length <= 5:
+			parsed = [int(c*2, 16) / 255.0 for c in colorstr[1:]]
+		else:
+			parsed = [int(colorstr[i:i+2], 16) / 255.0 for i in range(1, length, 2)]
+		if len(parsed) == 3:
+			parsed.append(1.0)
+		return parsed
+	
+	def __len__(self):
+		return 4
+	
+	def __getitem__(self, item):
+		return (self.r, self.g, self.b, self.a)[item]
+	
+	def __iter__(self):
+		return iter((self.r, self.g, self.b, self.a))
+	
+	def __eq__(self, other):
+		return tuple(self) == tuple(other)
+	
+	def __repr__(self):
+		return "%s(%.2f, %.2f, %.2f, %.2f)" % (self.__class__.__name__, 
+			self.r, self.g, self.b, self.a)
+			
+			
--- a/bGrease/component/base.py	Thu Dec 15 21:14:13 2011 +0100
+++ b/bGrease/component/base.py	Thu Jan 12 18:42:48 2012 +0100
@@ -1,74 +1,74 @@
-class ComponentBase(object):
-	"""Component abstract base class
-
-	Strictly speaking you do not need to derive from this class to create your
-	own components, but it does serve to document the full interface that a
-	component implements and it provides some basic implementations for
-	certain methods
-	"""
-
-	## Optional attributes and methods ##
-
-	def set_manager(self, manager):
-		"""Set the manager of this component. If this method exists it will be
-		automatically called when the component is added to a manager.
-
-		This method stores the manager and allows the component to be added
-		only once to a single manager.
-		"""
-		assert getattr(self, 'manager', None) is None, 'Component cannot be added to multiple managers'
-		self.manager = manager
-
-	def __del__(self):
-		"""Break circrefs to allow faster collection"""
-		if hasattr(self, 'manager'):
-			del self.manager
-	
-	## Mandatory methods ##
-
-	def add(self, entity_id, data=None, **data_kw):
-		"""Add a data entry in the component for the given entity.  Additional
-		data (if any) for the entry can be provided in the data argument or as
-		keyword arguments. Additional data is optional and if omitted then
-		suitable defaults will be used. Return an entity data object
-		for the new entity entry.
-
-		The semantics of the data arguments is up to the component.
-
-		An entity_id is a unique key, thus multiple separate data entries for
-		a given entity are not allowed.  Components can indivdually decide
-		what to do if an entity_id is added multiple times to the component.
-		Potential options include, raising an exception, replacing the
-		existing data or coalescing it somehow.
-		"""
-	
-	def remove(self, entity_id):
-		"""Remove the entity data entry from the component. If the
-		entity is not in the component, raise KeyError
-		"""
-	
-	def __delitem_(self, entity_id):
-		"""Same as remove()"""
-
-	def __len__(self):
-		"""Return the number of entities in the component"""
-		raise NotImplementedError()
-	
-	def __iter__(self):
-		"""Return an iterator of entity data objects in this component
-
-		No order is defined for these data objects
-		"""
-		raise NotImplementedError()
-	
-	def __contains__(self, entity_id):
-		"""Return True if the entity is contained in the component"""
-		raise NotImplementedError()
-	
-	def __getitem__(self, entity_id):
-		"""Return the entity data object for the given entity. 
-		The entity data object returned may be mutable, immutable or a
-		mutable copy of the data at the discretion of the component
-		"""
-		raise NotImplementedError()
-
+class ComponentBase(object):
+	"""Component abstract base class
+
+	Strictly speaking you do not need to derive from this class to create your
+	own components, but it does serve to document the full interface that a
+	component implements and it provides some basic implementations for
+	certain methods
+	"""
+
+	## Optional attributes and methods ##
+
+	def set_manager(self, manager):
+		"""Set the manager of this component. If this method exists it will be
+		automatically called when the component is added to a manager.
+
+		This method stores the manager and allows the component to be added
+		only once to a single manager.
+		"""
+		assert getattr(self, 'manager', None) is None, 'Component cannot be added to multiple managers'
+		self.manager = manager
+
+	def __del__(self):
+		"""Break circrefs to allow faster collection"""
+		if hasattr(self, 'manager'):
+			del self.manager
+	
+	## Mandatory methods ##
+
+	def add(self, entity_id, data=None, **data_kw):
+		"""Add a data entry in the component for the given entity.  Additional
+		data (if any) for the entry can be provided in the data argument or as
+		keyword arguments. Additional data is optional and if omitted then
+		suitable defaults will be used. Return an entity data object
+		for the new entity entry.
+
+		The semantics of the data arguments is up to the component.
+
+		An entity_id is a unique key, thus multiple separate data entries for
+		a given entity are not allowed.  Components can indivdually decide
+		what to do if an entity_id is added multiple times to the component.
+		Potential options include, raising an exception, replacing the
+		existing data or coalescing it somehow.
+		"""
+	
+	def remove(self, entity_id):
+		"""Remove the entity data entry from the component. If the
+		entity is not in the component, raise KeyError
+		"""
+	
+	def __delitem_(self, entity_id):
+		"""Same as remove()"""
+
+	def __len__(self):
+		"""Return the number of entities in the component"""
+		raise NotImplementedError()
+	
+	def __iter__(self):
+		"""Return an iterator of entity data objects in this component
+
+		No order is defined for these data objects
+		"""
+		raise NotImplementedError()
+	
+	def __contains__(self, entity_id):
+		"""Return True if the entity is contained in the component"""
+		raise NotImplementedError()
+	
+	def __getitem__(self, entity_id):
+		"""Return the entity data object for the given entity. 
+		The entity data object returned may be mutable, immutable or a
+		mutable copy of the data at the discretion of the component
+		"""
+		raise NotImplementedError()
+
--- a/bGrease/component/schema.py	Thu Dec 15 21:14:13 2011 +0100
+++ b/bGrease/component/schema.py	Thu Jan 12 18:42:48 2012 +0100
@@ -1,1 +1,1 @@
-
+
--- a/bGrease/controller/integrator.py	Thu Dec 15 21:14:13 2011 +0100
+++ b/bGrease/controller/integrator.py	Thu Jan 12 18:42:48 2012 +0100
@@ -1,42 +1,42 @@
-#############################################################################
-#
-# Copyright (c) 2010 by Casey Duncan and contributors
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the MIT License
-# A copy of the license should accompany this distribution.
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-#
-#############################################################################
-
-__version__ = '$Id$'
-
-
-class EulerMovement(object):
-	"""System that applies entity movement to position using Euler's method
-
-	:param position_component: Name of :class:`grease.component.Position` 
-		component to update.
-	:param movement_component: Name of :class:`grease.component.Movement` 
-		component used to update position.
-	"""
-
-	def __init__(self, position_component='position', movement_component='movement'):
-		self.position_component = position_component
-		self.movement_component = movement_component
-	
-	def set_world(self, world):
-		"""Bind the system to a world"""
-		self.world = world
-	
-	def step(self, dt):
-		"""Apply movement to position"""
-		assert self.world is not None, "Cannot run with no world set"
-		for position, movement in self.world.components.join(
-			self.position_component, self.movement_component):
-			movement.velocity += movement.accel * dt
-			position.position += movement.velocity * dt
-			position.angle += movement.rotation * dt
-
+#############################################################################
+#
+# Copyright (c) 2010 by Casey Duncan and contributors
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the MIT License
+# A copy of the license should accompany this distribution.
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+#
+#############################################################################
+
+__version__ = '$Id$'
+
+
+class EulerMovement(object):
+	"""System that applies entity movement to position using Euler's method
+
+	:param position_component: Name of :class:`grease.component.Position` 
+		component to update.
+	:param movement_component: Name of :class:`grease.component.Movement` 
+		component used to update position.
+	"""
+
+	def __init__(self, position_component='position', movement_component='movement'):
+		self.position_component = position_component
+		self.movement_component = movement_component
+	
+	def set_world(self, world):
+		"""Bind the system to a world"""
+		self.world = world
+	
+	def step(self, dt):
+		"""Apply movement to position"""
+		assert self.world is not None, "Cannot run with no world set"
+		for position, movement in self.world.components.join(
+			self.position_component, self.movement_component):
+			movement.velocity += movement.accel * dt
+			position.position += movement.velocity * dt
+			position.angle += movement.rotation * dt
+
--- a/bGrease/geometry.py	Thu Dec 15 21:14:13 2011 +0100
+++ b/bGrease/geometry.py	Thu Jan 12 18:42:48 2012 +0100
@@ -1,600 +1,600 @@
-__version__ = "$Id$"
-__docformat__ = "reStructuredText"
-
-import operator
-import math
-import ctypes 
-
-class Vec2d(ctypes.Structure):
-    """2d vector class, supports vector and scalar operators,
-       and also provides a bunch of high level functions
-       """
-    __slots__ = ['x', 'y']
-    
-    @classmethod
-    def from_param(cls, arg):
-        return cls(arg)
-        
-    def __init__(self, x_or_pair, y = None):
-        
-        if y == None:
-            self.x = x_or_pair[0]
-            self.y = x_or_pair[1]
-        else:
-            self.x = x_or_pair
-            self.y = y
- 
-    def __len__(self):
-        return 2
- 
-    def __getitem__(self, key):
-        if key == 0:
-            return self.x
-        elif key == 1:
-            return self.y
-        else:
-            raise IndexError("Invalid subscript "+str(key)+" to Vec2d")
- 
-    def __setitem__(self, key, value):
-        if key == 0:
-            self.x = value
-        elif key == 1:
-            self.y = value
-        else:
-            raise IndexError("Invalid subscript "+str(key)+" to Vec2d")
- 
-    # String representaion (for debugging)
-    def __repr__(self):
-        return 'Vec2d(%s, %s)' % (self.x, self.y)
-    
-    # Comparison
-    def __eq__(self, other):
-        if hasattr(other, "__getitem__") and len(other) == 2:
-            return self.x == other[0] and self.y == other[1]
-        else:
-            return False
-    
-    def __ne__(self, other):
-        if hasattr(other, "__getitem__") and len(other) == 2:
-            return self.x != other[0] or self.y != other[1]
-        else:
-            return True
- 
-    def __nonzero__(self):
-        return self.x or self.y
- 
-    # Generic operator handlers
-    def _o2(self, other, f):
-        "Any two-operator operation where the left operand is a Vec2d"
-        if isinstance(other, Vec2d):
-            return Vec2d(f(self.x, other.x),
-                         f(self.y, other.y))
-        elif (hasattr(other, "__getitem__")):
-            return Vec2d(f(self.x, other[0]),
-                         f(self.y, other[1]))
-        else:
-            return Vec2d(f(self.x, other),
-                         f(self.y, other))
- 
-    def _r_o2(self, other, f):
-        "Any two-operator operation where the right operand is a Vec2d"
-        if (hasattr(other, "__getitem__")):
-            return Vec2d(f(other[0], self.x),
-                         f(other[1], self.y))
-        else:
-            return Vec2d(f(other, self.x),
-                         f(other, self.y))
- 
-    def _io(self, other, f):
-        "inplace operator"
-        if (hasattr(other, "__getitem__")):
-            self.x = f(self.x, other[0])
-            self.y = f(self.y, other[1])
-        else:
-            self.x = f(self.x, other)
-            self.y = f(self.y, other)
-        return self
- 
-    # Addition
-    def __add__(self, other):
-        if isinstance(other, Vec2d):
-            return Vec2d(self.x + other.x, self.y + other.y)
-        elif hasattr(other, "__getitem__"):
-            return Vec2d(self.x + other[0], self.y + other[1])
-        else:
-            return Vec2d(self.x + other, self.y + other)
-    __radd__ = __add__
-    
-    def __iadd__(self, other):
-        if isinstance(other, Vec2d):
-            self.x += other.x
-            self.y += other.y
-        elif hasattr(other, "__getitem__"):
-            self.x += other[0]
-            self.y += other[1]
-        else:
-            self.x += other
-            self.y += other
-        return self
- 
-    # Subtraction
-    def __sub__(self, other):
-        if isinstance(other, Vec2d):
-            return Vec2d(self.x - other.x, self.y - other.y)
-        elif (hasattr(other, "__getitem__")):
-            return Vec2d(self.x - other[0], self.y - other[1])
-        else:
-            return Vec2d(self.x - other, self.y - other)
-    def __rsub__(self, other):
-        if isinstance(other, Vec2d):
-            return Vec2d(other.x - self.x, other.y - self.y)
-        if (hasattr(other, "__getitem__")):
-            return Vec2d(other[0] - self.x, other[1] - self.y)
-        else:
-            return Vec2d(other - self.x, other - self.y)
-    def __isub__(self, other):
-        if isinstance(other, Vec2d):
-            self.x -= other.x
-            self.y -= other.y
-        elif (hasattr(other, "__getitem__")):
-            self.x -= other[0]
-            self.y -= other[1]
-        else:
-            self.x -= other
-            self.y -= other
-        return self
- 
-    # Multiplication
-    def __mul__(self, other):
-        if isinstance(other, Vec2d):
-            return Vec2d(self.x*other.y, self.y*other.y)
-        if (hasattr(other, "__getitem__")):
-            return Vec2d(self.x*other[0], self.y*other[1])
-        else:
-            return Vec2d(self.x*other, self.y*other)
-    __rmul__ = __mul__
-    
-    def __imul__(self, other):
-        if isinstance(other, Vec2d):
-            self.x *= other.x
-            self.y *= other.y
-        elif (hasattr(other, "__getitem__")):
-            self.x *= other[0]
-            self.y *= other[1]
-        else:
-            self.x *= other
-            self.y *= other
-        return self
- 
-    # Division
-    def __div__(self, other):
-        return self._o2(other, operator.div)
-    def __rdiv__(self, other):
-        return self._r_o2(other, operator.div)
-    def __idiv__(self, other):
-        return self._io(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 __ifloordiv__(self, other):
-        return self._io(other, operator.floordiv)
- 
-    def __truediv__(self, other):
-        return self._o2(other, operator.truediv)
-    def __rtruediv__(self, other):
-        return self._r_o2(other, operator.truediv)
-    def __itruediv__(self, other):
-        return self._io(other, operator.floordiv)
- 
-    # 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, divmod)
-    def __rdivmod__(self, other):
-        return self._r_o2(other, 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 Vec2d(operator.neg(self.x), operator.neg(self.y))
- 
-    def __pos__(self):
-        return Vec2d(operator.pos(self.x), operator.pos(self.y))
- 
-    def __abs__(self):
-        return Vec2d(abs(self.x), abs(self.y))
- 
-    def __invert__(self):
-        return Vec2d(-self.x, -self.y)
- 
-    # vectory functions
-    def get_length_sqrd(self): 
-        """Get the squared length of the vector.
-        It is more efficent to use this method instead of first call 
-        get_length() or access .length and then do a sqrt().
-        
-        :return: The squared length
-        """
-        return self.x**2 + self.y**2
- 
-    def get_length(self):
-        """Get the length of the vector.
-        
-        :return: The length
-        """
-        return math.sqrt(self.x**2 + self.y**2)    
-    def __setlength(self, value):
-        length = self.get_length()
-        self.x *= value/length
-        self.y *= value/length
-    length = property(get_length, __setlength, doc = """Gets or sets the magnitude of the vector""")
-       
-    def rotate(self, angle_degrees):
-        """Rotate the vector by angle_degrees degrees clockwise."""
-        radians = -math.radians(angle_degrees)
-        cos = math.cos(radians)
-        sin = math.sin(radians)
-        x = self.x*cos - self.y*sin
-        y = self.x*sin + self.y*cos
-        self.x = x
-        self.y = y
- 
-    def rotated(self, angle_degrees):
-        """Create and return a new vector by rotating this vector by 
-        angle_degrees degrees clockwise.
-        
-        :return: Rotated vector
-        """
-        radians = -math.radians(angle_degrees)
-        cos = math.cos(radians)
-        sin = math.sin(radians)
-        x = self.x*cos - self.y*sin
-        y = self.x*sin + self.y*cos
-        return Vec2d(x, y)
-    
-    def get_angle(self):
-        if (self.get_length_sqrd() == 0):
-            return 0
-        return math.degrees(math.atan2(self.y, self.x))
-    def __setangle(self, angle_degrees):
-        self.x = self.length
-        self.y = 0
-        self.rotate(angle_degrees)
-    angle = property(get_angle, __setangle, doc="""Gets or sets the angle of a vector""")
- 
-    def get_angle_between(self, other):
-        """Get the angle between the vector and the other in degrees
-        
-        :return: The angle
-        """
-        cross = self.x*other[1] - self.y*other[0]
-        dot = self.x*other[0] + self.y*other[1]
-        return math.degrees(math.atan2(cross, dot))
-            
-    def normalized(self):
-        """Get a normalized copy of the vector
-        
-        :return: A normalized vector
-        """
-        length = self.length
-        if length != 0:
-            return self/length
-        return Vec2d(self)
- 
-    def normalize_return_length(self):
-        """Normalize the vector and return its length before the normalization
-        
-        :return: The length before the normalization
-        """
-        length = self.length
-        if length != 0:
-            self.x /= length
-            self.y /= length
-        return length
- 
-    def perpendicular(self):
-        return Vec2d(-self.y, self.x)
-    
-    def perpendicular_normal(self):
-        length = self.length
-        if length != 0:
-            return Vec2d(-self.y/length, self.x/length)
-        return Vec2d(self)
-        
-    def dot(self, other):
-        """The dot product between the vector and other vector
-            v1.dot(v2) -> v1.x*v2.x + v1.y*v2.y
-            
-        :return: The dot product
-        """
-        return float(self.x*other[0] + self.y*other[1])
-        
-    def get_distance(self, other):
-        """The distance between the vector and other vector
-        
-        :return: The distance
-        """
-        return math.sqrt((self.x - other[0])**2 + (self.y - other[1])**2)
-        
-    def get_dist_sqrd(self, other):
-        """The squared distance between the vector and other vector
-        It is more efficent to use this method than to call get_distance()
-        first and then do a sqrt() on the result.
-        
-        :return: The squared distance
-        """
-        return (self.x - other[0])**2 + (self.y - other[1])**2
-        
-    def projection(self, other):
-        other_length_sqrd = other[0]*other[0] + other[1]*other[1]
-        projected_length_times_other_length = self.dot(other)
-        return other*(projected_length_times_other_length/other_length_sqrd)
-    
-    def cross(self, other):
-        """The cross product between the vector and other vector
-            v1.cross(v2) -> v1.x*v2.y - v2.y-v1.x
-        
-        :return: The cross product
-        """
-        return self.x*other[1] - self.y*other[0]
-    
-    def interpolate_to(self, other, range):
-        return Vec2d(self.x + (other[0] - self.x)*range, self.y + (other[1] - self.y)*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())
- 
-    # Extra functions, mainly for chipmunk
-    def cpvrotate(self, other):
-        return Vec2d(self.x*other.x - self.y*other.y, self.x*other.y + self.y*other.x)
-    def cpvunrotate(self, other):
-        return Vec2d(self.x*other.x + self.y*other.y, self.y*other.x - self.x*other.y)
-    
-    # Pickle, does not work atm.
-    def __getstate__(self):
-        return [self.x, self.y]
-        
-    def __setstate__(self, dict):
-        self.x, self.y = dict
-    def __newobj__(cls, *args):
-        return cls.__new__(cls, *args)    
-Vec2d._fields_ = [
-            ('x', ctypes.c_double),
-            ('y', ctypes.c_double),
-        ]
-
-
-class Vec2dArray(list):
-
-	def __init__(self, iterable=()):
-		list.__init__(self, (Vec2d(i) for i in iterable))
-
-	def __setitem__(self, index, value):
-		list.__setitem__(self, index, Vec2d(value))
-	
-	def append(self, value):
-		"""Append a vector to the array"""
-		list.append(self, Vec2d(value))
-	
-	def insert(self, index, value):
-		"""Insert a vector into the array"""
-		list.insert(self, index, Vec2d(value))
-	
-	def transform(self, offset=Vec2d(0,0), angle=0, scale=1.0):
-		"""Return a new transformed Vec2dArray"""
-		offset = Vec2d(offset)
-		angle = math.radians(-angle)
-		rot_vec = Vec2d(math.cos(angle), math.sin(angle))
-		xformed = Vec2dArray()
-		for vec in self:
-			xformed.append(vec.cpvrotate(rot_vec) * scale + offset)
-		return xformed
-	
-	def segments(self, closed=True):
-		"""Generate arrays of line segments connecting adjacent vetices
-		in this array, exploding the shape into it's constituent segments
-		"""
-		if len(self) >= 2:
-			last = self[0]
-			for vert in self[1:]:
-				yield Vec2dArray((last, vert))
-				last = vert
-			if closed:
-				yield Vec2dArray((last, self[0]))
-		elif self and closed:
-			yield Vec2dArray((self[0], self[0]))
-
-
-
-class Rect(ctypes.Structure):
-	"""Simple rectangle. Will gain more functionality as needed"""
-	_fields_ = [
-		('left', ctypes.c_double),
-		('top', ctypes.c_double),
-		('right', ctypes.c_double),
-		('bottom', ctypes.c_double),
-	]
-
-	def __init__(self, rect_or_left, bottom=None, right=None, top=None):
-		if bottom is not None:
-			assert right is not None and top is not None, "No enough arguments to Rect"
-			self.left = rect_or_left
-			self.bottom = bottom
-			self.right = right
-			self.top = top
-		else:
-			self.left = rect_or_left.left
-			self.bottom = rect_or_left.bottom
-			self.right = rect_or_left.right
-			self.top = rect_or_left.top
-
-	@property
-	def width(self):
-		"""Rectangle width"""
-		return self.right - self.left
-	
-	@property
-	def height(self):
-		"""Rectangle height"""
-		return self.top - self.bottom
-
-
-########################################################################
-## Unit Testing                                                       ##
-########################################################################
-if __name__ == "__main__":
- 
-    import unittest
-    import pickle
- 
-    ####################################################################
-    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.assertEqual(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)) # Not supported since this is a c_float structure in the bottom
-            self.assert_(v ** Vec2d(2, 3) == [12321, 10941048])
-            self.assert_(v + [-11, 78] == Vec2d(100, 300))
-            #self.assert_(v / [11,2] == [10,111]) # Not supported since this is a c_float structure in the bottom
- 
-        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]) # Not supported since this is a c_float structure in the bottom
-            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.assertAlmostEquals(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, 5) # Allow a little more error than usual (floats..)
-            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)
-            
-        def testComparison(self):
-            int_vec = Vec2d(3, -2)
-            flt_vec = Vec2d(3.0, -2.0)
-            zero_vec = Vec2d(0, 0)
-            self.assert_(int_vec == flt_vec)
-            self.assert_(int_vec != zero_vec)
-            self.assert_((flt_vec == zero_vec) == False)
-            self.assert_((flt_vec != int_vec) == False)
-            self.assert_(int_vec == (3, -2))
-            self.assert_(int_vec != [0, 0])
-            self.assert_(int_vec != 5)
-            self.assert_(int_vec != [3, -2, -5])
-        
-        def testInplace(self):
-            inplace_vec = Vec2d(5, 13)
-            inplace_ref = inplace_vec
-            inplace_src = Vec2d(inplace_vec)    
-            inplace_vec *= .5
-            inplace_vec += .5
-            inplace_vec /= (3, 6)
-            inplace_vec += Vec2d(-1, -1)
-            alternate = (inplace_src*.5 + .5)/Vec2d(3, 6) + [-1, -1]
-            self.assertEquals(inplace_vec, inplace_ref)
-            self.assertEquals(inplace_vec, alternate)
-        
-        def testPickle(self):
-            return # pickling does not work atm
-            testvec = Vec2d(5, .3)
-            testvec_str = pickle.dumps(testvec)
-            loaded_vec = pickle.loads(testvec_str)
-            self.assertEquals(testvec, loaded_vec)
-    
-    ####################################################################
-    unittest.main()
- 
+__version__ = "$Id$"
+__docformat__ = "reStructuredText"
+
+import operator
+import math
+import ctypes 
+
+class Vec2d(ctypes.Structure):
+    """2d vector class, supports vector and scalar operators,
+       and also provides a bunch of high level functions
+       """
+    __slots__ = ['x', 'y']
+    
+    @classmethod
+    def from_param(cls, arg):
+        return cls(arg)
+        
+    def __init__(self, x_or_pair, y = None):
+        
+        if y == None:
+            self.x = x_or_pair[0]
+            self.y = x_or_pair[1]
+        else:
+            self.x = x_or_pair
+            self.y = y
+ 
+    def __len__(self):
+        return 2
+ 
+    def __getitem__(self, key):
+        if key == 0:
+            return self.x
+        elif key == 1:
+            return self.y
+        else:
+            raise IndexError("Invalid subscript "+str(key)+" to Vec2d")
+ 
+    def __setitem__(self, key, value):
+        if key == 0:
+            self.x = value
+        elif key == 1:
+            self.y = value
+        else:
+            raise IndexError("Invalid subscript "+str(key)+" to Vec2d")
+ 
+    # String representaion (for debugging)
+    def __repr__(self):
+        return 'Vec2d(%s, %s)' % (self.x, self.y)
+    
+    # Comparison
+    def __eq__(self, other):
+        if hasattr(other, "__getitem__") and len(other) == 2:
+            return self.x == other[0] and self.y == other[1]
+        else:
+            return False
+    
+    def __ne__(self, other):
+        if hasattr(other, "__getitem__") and len(other) == 2:
+            return self.x != other[0] or self.y != other[1]
+        else:
+            return True
+ 
+    def __nonzero__(self):
+        return self.x or self.y
+ 
+    # Generic operator handlers
+    def _o2(self, other, f):
+        "Any two-operator operation where the left operand is a Vec2d"
+        if isinstance(other, Vec2d):
+            return Vec2d(f(self.x, other.x),
+                         f(self.y, other.y))
+        elif (hasattr(other, "__getitem__")):
+            return Vec2d(f(self.x, other[0]),
+                         f(self.y, other[1]))
+        else:
+            return Vec2d(f(self.x, other),
+                         f(self.y, other))
+ 
+    def _r_o2(self, other, f):
+        "Any two-operator operation where the right operand is a Vec2d"
+        if (hasattr(other, "__getitem__")):
+            return Vec2d(f(other[0], self.x),
+                         f(other[1], self.y))
+        else:
+            return Vec2d(f(other, self.x),
+                         f(other, self.y))
+ 
+    def _io(self, other, f):
+        "inplace operator"
+        if (hasattr(other, "__getitem__")):
+            self.x = f(self.x, other[0])
+            self.y = f(self.y, other[1])
+        else:
+            self.x = f(self.x, other)
+            self.y = f(self.y, other)
+        return self
+ 
+    # Addition
+    def __add__(self, other):
+        if isinstance(other, Vec2d):
+            return Vec2d(self.x + other.x, self.y + other.y)
+        elif hasattr(other, "__getitem__"):
+            return Vec2d(self.x + other[0], self.y + other[1])
+        else:
+            return Vec2d(self.x + other, self.y + other)
+    __radd__ = __add__
+    
+    def __iadd__(self, other):
+        if isinstance(other, Vec2d):
+            self.x += other.x
+            self.y += other.y
+        elif hasattr(other, "__getitem__"):
+            self.x += other[0]
+            self.y += other[1]
+        else:
+            self.x += other
+            self.y += other
+        return self
+ 
+    # Subtraction
+    def __sub__(self, other):
+        if isinstance(other, Vec2d):
+            return Vec2d(self.x - other.x, self.y - other.y)
+        elif (hasattr(other, "__getitem__")):
+            return Vec2d(self.x - other[0], self.y - other[1])
+        else:
+            return Vec2d(self.x - other, self.y - other)
+    def __rsub__(self, other):
+        if isinstance(other, Vec2d):
+            return Vec2d(other.x - self.x, other.y - self.y)
+        if (hasattr(other, "__getitem__")):
+            return Vec2d(other[0] - self.x, other[1] - self.y)
+        else:
+            return Vec2d(other - self.x, other - self.y)
+    def __isub__(self, other):
+        if isinstance(other, Vec2d):
+            self.x -= other.x
+            self.y -= other.y
+        elif (hasattr(other, "__getitem__")):
+            self.x -= other[0]
+            self.y -= other[1]
+        else:
+            self.x -= other
+            self.y -= other
+        return self
+ 
+    # Multiplication
+    def __mul__(self, other):
+        if isinstance(other, Vec2d):
+            return Vec2d(self.x*other.y, self.y*other.y)
+        if (hasattr(other, "__getitem__")):
+            return Vec2d(self.x*other[0], self.y*other[1])
+        else:
+            return Vec2d(self.x*other, self.y*other)
+    __rmul__ = __mul__
+    
+    def __imul__(self, other):
+        if isinstance(other, Vec2d):
+            self.x *= other.x
+            self.y *= other.y
+        elif (hasattr(other, "__getitem__")):
+            self.x *= other[0]
+            self.y *= other[1]
+        else:
+            self.x *= other
+            self.y *= other
+        return self
+ 
+    # Division
+    def __div__(self, other):
+        return self._o2(other, operator.div)
+    def __rdiv__(self, other):
+        return self._r_o2(other, operator.div)
+    def __idiv__(self, other):
+        return self._io(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 __ifloordiv__(self, other):
+        return self._io(other, operator.floordiv)
+ 
+    def __truediv__(self, other):
+        return self._o2(other, operator.truediv)
+    def __rtruediv__(self, other):
+        return self._r_o2(other, operator.truediv)
+    def __itruediv__(self, other):
+        return self._io(other, operator.floordiv)
+ 
+    # 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, divmod)
+    def __rdivmod__(self, other):
+        return self._r_o2(other, 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 Vec2d(operator.neg(self.x), operator.neg(self.y))
+ 
+    def __pos__(self):
+        return Vec2d(operator.pos(self.x), operator.pos(self.y))
+ 
+    def __abs__(self):
+        return Vec2d(abs(self.x), abs(self.y))
+ 
+    def __invert__(self):
+        return Vec2d(-self.x, -self.y)
+ 
+    # vectory functions
+    def get_length_sqrd(self): 
+        """Get the squared length of the vector.
+        It is more efficent to use this method instead of first call 
+        get_length() or access .length and then do a sqrt().
+        
+        :return: The squared length
+        """
+        return self.x**2 + self.y**2
+ 
+    def get_length(self):
+        """Get the length of the vector.
+        
+        :return: The length
+        """
+        return math.sqrt(self.x**2 + self.y**2)    
+    def __setlength(self, value):
+        length = self.get_length()
+        self.x *= value/length
+        self.y *= value/length
+    length = property(get_length, __setlength, doc = """Gets or sets the magnitude of the vector""")
+       
+    def rotate(self, angle_degrees):
+        """Rotate the vector by angle_degrees degrees clockwise."""
+        radians = -math.radians(angle_degrees)
+        cos = math.cos(radians)
+        sin = math.sin(radians)
+        x = self.x*cos - self.y*sin
+        y = self.x*sin + self.y*cos
+        self.x = x
+        self.y = y
+ 
+    def rotated(self, angle_degrees):
+        """Create and return a new vector by rotating this vector by 
+        angle_degrees degrees clockwise.
+        
+        :return: Rotated vector
+        """
+        radians = -math.radians(angle_degrees)
+        cos = math.cos(radians)
+        sin = math.sin(radians)
+        x = self.x*cos - self.y*sin
+        y = self.x*sin + self.y*cos
+        return Vec2d(x, y)
+    
+    def get_angle(self):
+        if (self.get_length_sqrd() == 0):
+            return 0
+        return math.degrees(math.atan2(self.y, self.x))
+    def __setangle(self, angle_degrees):
+        self.x = self.length
+        self.y = 0
+        self.rotate(angle_degrees)
+    angle = property(get_angle, __setangle, doc="""Gets or sets the angle of a vector""")
+ 
+    def get_angle_between(self, other):
+        """Get the angle between the vector and the other in degrees
+        
+        :return: The angle
+        """
+        cross = self.x*other[1] - self.y*other[0]
+        dot = self.x*other[0] + self.y*other[1]
+        return math.degrees(math.atan2(cross, dot))
+            
+    def normalized(self):
+        """Get a normalized copy of the vector
+        
+        :return: A normalized vector
+        """
+        length = self.length
+        if length != 0:
+            return self/length
+        return Vec2d(self)
+ 
+    def normalize_return_length(self):
+        """Normalize the vector and return its length before the normalization
+        
+        :return: The length before the normalization
+        """
+        length = self.length
+        if length != 0:
+            self.x /= length
+            self.y /= length
+        return length
+ 
+    def perpendicular(self):
+        return Vec2d(-self.y, self.x)
+    
+    def perpendicular_normal(self):
+        length = self.length
+        if length != 0:
+            return Vec2d(-self.y/length, self.x/length)
+        return Vec2d(self)
+        
+    def dot(self, other):
+        """The dot product between the vector and other vector
+            v1.dot(v2) -> v1.x*v2.x + v1.y*v2.y
+            
+        :return: The dot product
+        """
+        return float(self.x*other[0] + self.y*other[1])
+        
+    def get_distance(self, other):
+        """The distance between the vector and other vector
+        
+        :return: The distance
+        """
+        return math.sqrt((self.x - other[0])**2 + (self.y - other[1])**2)
+        
+    def get_dist_sqrd(self, other):
+        """The squared distance between the vector and other vector
+        It is more efficent to use this method than to call get_distance()
+        first and then do a sqrt() on the result.
+        
+        :return: The squared distance
+        """
+        return (self.x - other[0])**2 + (self.y - other[1])**2
+        
+    def projection(self, other):
+        other_length_sqrd = other[0]*other[0] + other[1]*other[1]
+        projected_length_times_other_length = self.dot(other)
+        return other*(projected_length_times_other_length/other_length_sqrd)
+    
+    def cross(self, other):
+        """The cross product between the vector and other vector
+            v1.cross(v2) -> v1.x*v2.y - v2.y-v1.x
+        
+        :return: The cross product
+        """
+        return self.x*other[1] - self.y*other[0]
+    
+    def interpolate_to(self, other, range):
+        return Vec2d(self.x + (other[0] - self.x)*range, self.y + (other[1] - self.y)*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())
+ 
+    # Extra functions, mainly for chipmunk
+    def cpvrotate(self, other):
+        return Vec2d(self.x*other.x - self.y*other.y, self.x*other.y + self.y*other.x)
+    def cpvunrotate(self, other):
+        return Vec2d(self.x*other.x + self.y*other.y, self.y*other.x - self.x*other.y)
+    
+    # Pickle, does not work atm.
+    def __getstate__(self):
+        return [self.x, self.y]
+        
+    def __setstate__(self, dict):
+        self.x, self.y = dict
+    def __newobj__(cls, *args):
+        return cls.__new__(cls, *args)    
+Vec2d._fields_ = [
+            ('x', ctypes.c_double),
+            ('y', ctypes.c_double),
+        ]
+
+
+class Vec2dArray(list):
+
+	def __init__(self, iterable=()):
+		list.__init__(self, (Vec2d(i) for i in iterable))
+
+	def __setitem__(self, index, value):
+		list.__setitem__(self, index, Vec2d(value))
+	
+	def append(self, value):
+		"""Append a vector to the array"""
+		list.append(self, Vec2d(value))
+	
+	def insert(self, index, value):
+		"""Insert a vector into the array"""
+		list.insert(self, index, Vec2d(value))
+	
+	def transform(self, offset=Vec2d(0,0), angle=0, scale=1.0):
+		"""Return a new transformed Vec2dArray"""
+		offset = Vec2d(offset)
+		angle = math.radians(-angle)
+		rot_vec = Vec2d(math.cos(angle), math.sin(angle))
+		xformed = Vec2dArray()
+		for vec in self:
+			xformed.append(vec.cpvrotate(rot_vec) * scale + offset)
+		return xformed
+	
+	def segments(self, closed=True):
+		"""Generate arrays of line segments connecting adjacent vetices
+		in this array, exploding the shape into it's constituent segments
+		"""
+		if len(self) >= 2:
+			last = self[0]
+			for vert in self[1:]:
+				yield Vec2dArray((last, vert))
+				last = vert
+			if closed:
+				yield Vec2dArray((last, self[0]))
+		elif self and closed:
+			yield Vec2dArray((self[0], self[0]))
+
+
+
+class Rect(ctypes.Structure):
+	"""Simple rectangle. Will gain more functionality as needed"""
+	_fields_ = [
+		('left', ctypes.c_double),
+		('top', ctypes.c_double),
+		('right', ctypes.c_double),
+		('bottom', ctypes.c_double),
+	]
+
+	def __init__(self, rect_or_left, bottom=None, right=None, top=None):
+		if bottom is not None:
+			assert right is not None and top is not None, "No enough arguments to Rect"
+			self.left = rect_or_left
+			self.bottom = bottom
+			self.right = right
+			self.top = top
+		else:
+			self.left = rect_or_left.left
+			self.bottom = rect_or_left.bottom
+			self.right = rect_or_left.right
+			self.top = rect_or_left.top
+
+	@property
+	def width(self):
+		"""Rectangle width"""
+		return self.right - self.left
+	
+	@property
+	def height(self):
+		"""Rectangle height"""
+		return self.top - self.bottom
+
+
+########################################################################
+## Unit Testing                                                       ##
+########################################################################
+if __name__ == "__main__":
+ 
+    import unittest
+    import pickle
+ 
+    ####################################################################
+    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.assertEqual(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)) # Not supported since this is a c_float structure in the bottom
+            self.assert_(v ** Vec2d(2, 3) == [12321, 10941048])
+            self.assert_(v + [-11, 78] == Vec2d(100, 300))
+            #self.assert_(v / [11,2] == [10,111]) # Not supported since this is a c_float structure in the bottom
+ 
+        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]) # Not supported since this is a c_float structure in the bottom
+            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.assertAlmostEquals(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, 5) # Allow a little more error than usual (floats..)
+            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)
+            
+        def testComparison(self):
+            int_vec = Vec2d(3, -2)
+            flt_vec = Vec2d(3.0, -2.0)
+            zero_vec = Vec2d(0, 0)
+            self.assert_(int_vec == flt_vec)
+            self.assert_(int_vec != zero_vec)
+            self.assert_((flt_vec == zero_vec) == False)
+            self.assert_((flt_vec != int_vec) == False)
+            self.assert_(int_vec == (3, -2))
+            self.assert_(int_vec != [0, 0])
+            self.assert_(int_vec != 5)
+            self.assert_(int_vec != [3, -2, -5])
+        
+        def testInplace(self):
+            inplace_vec = Vec2d(5, 13)
+            inplace_ref = inplace_vec
+            inplace_src = Vec2d(inplace_vec)    
+            inplace_vec *= .5
+            inplace_vec += .5
+            inplace_vec /= (3, 6)
+            inplace_vec += Vec2d(-1, -1)
+            alternate = (inplace_src*.5 + .5)/Vec2d(3, 6) + [-1, -1]
+            self.assertEquals(inplace_vec, inplace_ref)
+            self.assertEquals(inplace_vec, alternate)
+        
+        def testPickle(self):
+            return # pickling does not work atm
+            testvec = Vec2d(5, .3)
+            testvec_str = pickle.dumps(testvec)
+            loaded_vec = pickle.loads(testvec_str)
+            self.assertEquals(testvec, loaded_vec)
+    
+    ####################################################################
+    unittest.main()
+ 
--- a/bGrease/impl/__init__.py	Thu Dec 15 21:14:13 2011 +0100
+++ b/bGrease/impl/__init__.py	Thu Jan 12 18:42:48 2012 +0100
@@ -1,22 +1,22 @@
-#############################################################################
-#
-# Copyright (c) 2010 by Casey Duncan and contributors
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the MIT License
-# A copy of the license should accompany this distribution.
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-#
-#############################################################################
-
-__versioninfo__ = (0, 3, 0)
-__version__ = '.'.join(str(n) for n in __versioninfo__)
-
-__all__ = ('Mode', 'World')
-
-from mode import Mode
-from world import World
-
-
+#############################################################################
+#
+# Copyright (c) 2010 by Casey Duncan and contributors
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the MIT License
+# A copy of the license should accompany this distribution.
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+#
+#############################################################################
+
+__versioninfo__ = (0, 3, 0)
+__version__ = '.'.join(str(n) for n in __versioninfo__)
+
+__all__ = ('Mode', 'World')
+
+from mode import Mode
+from world import World
+
+
--- a/bGrease/impl/controls.py	Thu Dec 15 21:14:13 2011 +0100
+++ b/bGrease/impl/controls.py	Thu Jan 12 18:42:48 2012 +0100
@@ -1,216 +1,216 @@
-#############################################################################
-#
-# Copyright (c) 2010 by Casey Duncan
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the MIT License
-# A copy of the license should accompany this distribution.
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-#
-#############################################################################
-"""Control systems for binding controls to game logic"""
-
-import bGrease
-from pyglet.window import key
-
-class KeyControls(grease.System):
-	"""System that maps subclass-defined action methods to keys. 
-
-	Keys may be mapped in the subclass definition using decorators
-	defined here as class methods or at runtime using the ``bind_key_*`` 
-	instance methods.
-
-	See :ref:`an example implementation in the tutorial <tut-controls-example>`.
-	"""
-	MODIFIER_MASK = ~(key.MOD_NUMLOCK | key.MOD_SCROLLLOCK | key.MOD_CAPSLOCK)
-	"""The MODIFIER_MASK allows you to filter out modifier keys that should be
-	ignored by the application. By default, capslock, numlock, and scrolllock 
-	are ignored.
-	"""
-
-	world = None
-	""":class:`grease.World` object this system is bound to"""
-
-	def __init__(self):
-		self._key_press_map = {}
-		self._key_release_map = {}
-		self._key_hold_map = {}
-		for name in self.__class__.__dict__:
-			member = getattr(self, name)
-			if hasattr(member, '_grease_hold_key_binding'):
-				for binding in member._grease_hold_key_binding:
-					self.bind_key_hold(member, *binding)
-			if hasattr(member, '_grease_press_key_binding'):
-				for binding in member._grease_press_key_binding:
-					self.bind_key_press(member, *binding)
-			if hasattr(member, '_grease_release_key_binding'):
-				for binding in member._grease_release_key_binding:
-					self.bind_key_release(member, *binding)
-		self.held_keys = set()
-
-	## decorator methods for binding methods to key input events ##
-
-	@classmethod
-	def key_hold(cls, symbol, modifiers=0):
-		"""Decorator to bind a method to be executed where a key is held down"""
-		def bind(f):
-			if not hasattr(f, '_grease_hold_key_binding'):
-				f._grease_hold_key_binding = []
-			f._grease_hold_key_binding.append((symbol, modifiers & cls.MODIFIER_MASK))
-			return f
-		return bind
-
-	@classmethod
-	def key_press(cls, symbol, modifiers=0):
-		"""Decorator to bind a method to be executed where a key is initially depressed"""
-		def bind(f):
-			if not hasattr(f, '_grease_press_key_binding'):
-				f._grease_press_key_binding = []
-			f._grease_press_key_binding.append((symbol, modifiers & cls.MODIFIER_MASK))
-			return f
-		return bind
-
-	@classmethod
-	def key_release(cls, symbol, modifiers=0):
-		"""Decorator to bind a method to be executed where a key is released"""
-		def bind(f):
-			if not hasattr(f, '_grease_release_key_binding'):
-				f._grease_release_key_binding = []
-			f._grease_release_key_binding.append((symbol, modifiers & cls.MODIFIER_MASK))
-			return f
-		return bind
-	
-	## runtime binding methods ##
-	
-	def bind_key_hold(self, method, key, modifiers=0):
-		"""Bind a method to a key at runtime to be invoked when the key is
-		held down, this replaces any existing key hold binding for this key.
-		To unbind the key entirely, pass ``None`` for method.
-		"""
-		if method is not None:
-			self._key_hold_map[key, modifiers & self.MODIFIER_MASK] = method
-		else:
-			try:
-				del self._key_hold_map[key, modifiers & self.MODIFIER_MASK]
-			except KeyError:
-				pass
-
-	def bind_key_press(self, method, key, modifiers=0):
-		"""Bind a method to a key at runtime to be invoked when the key is initially
-		pressed, this replaces any existing key hold binding for this key. To unbind
-		the key entirely, pass ``None`` for method.
-		"""
-		if method is not None:
-			self._key_press_map[key, modifiers & self.MODIFIER_MASK] = method
-		else:
-			try:
-				del self._key_press_map[key, modifiers & self.MODIFIER_MASK]
-			except KeyError:
-				pass
-
-	def bind_key_release(self, method, key, modifiers=0):
-		"""Bind a method to a key at runtime to be invoked when the key is releaseed,
-		this replaces any existing key hold binding for this key. To unbind
-		the key entirely, pass ``None`` for method.
-		"""
-		if method is not None:
-			self._key_release_map[key, modifiers & self.MODIFIER_MASK] = method
-		else:
-			try:
-				del self._key_release_map[key, modifiers & self.MODIFIER_MASK]
-			except KeyError:
-				pass
-
-	def step(self, dt):
-		"""invoke held key functions"""
-		already_run = set()
-		for key in self.held_keys:
-			func = self._key_hold_map.get(key)
-			if func is not None and func not in already_run:
-				already_run.add(func)
-				func(dt)
-
-	def on_key_press(self, key, modifiers):
-		"""Handle pyglet key press. Invoke key press methods and
-		activate key hold functions
-		"""
-		key_mod = (key, modifiers & self.MODIFIER_MASK)
-		if key_mod in self._key_press_map:
-			self._key_press_map[key_mod]()
-		self.held_keys.add(key_mod)
-	
-	def on_key_release(self, key, modifiers):
-		"""Handle pyglet key release. Invoke key release methods and
-		deactivate key hold functions
-		"""
-		key_mod = (key, modifiers & self.MODIFIER_MASK)
-		if key_mod in self._key_release_map:
-			self._key_release_map[key_mod]()
-		self.held_keys.discard(key_mod)
-
-
-if __name__ == '__main__':
-	import pyglet
-
-	class TestKeyControls(KeyControls):
-		
-		MODIFIER_MASK = ~(key.MOD_NUMLOCK | key.MOD_SCROLLLOCK | key.MOD_CTRL)
-
-		remapped = False
-		
-		@KeyControls.key_hold(key.UP)
-		@KeyControls.key_hold(key.W)
-		def up(self, dt):
-			print 'UP!'
-		
-		@KeyControls.key_hold(key.LEFT)
-		@KeyControls.key_hold(key.A)
-		def left(self, dt):
-			print 'LEFT!'
-		
-		@KeyControls.key_hold(key.RIGHT)
-		@KeyControls.key_hold(key.D)
-		def right(self, dt):
-			print 'RIGHT!'
-		
-		@KeyControls.key_hold(key.DOWN)
-		@KeyControls.key_hold(key.S)
-		def down(self, dt):
-			print 'DOWN!'
-
-		@KeyControls.key_press(key.SPACE)
-		def fire(self):
-			print 'FIRE!'
-
-		@KeyControls.key_press(key.R)
-		def remap_keys(self):
-			if not self.remapped:
-				self.bind_key_hold(None, key.W)
-				self.bind_key_hold(None, key.A)
-				self.bind_key_hold(None, key.S)
-				self.bind_key_hold(None, key.D)
-				self.bind_key_hold(self.up, key.I)
-				self.bind_key_hold(self.left, key.J)
-				self.bind_key_hold(self.right, key.L)
-				self.bind_key_hold(self.down, key.K)
-			else:
-				self.bind_key_hold(None, key.I)
-				self.bind_key_hold(None, key.J)
-				self.bind_key_hold(None, key.K)
-				self.bind_key_hold(None, key.L)
-				self.bind_key_hold(self.up, key.W)
-				self.bind_key_hold(self.left, key.A)
-				self.bind_key_hold(self.right, key.D)
-				self.bind_key_hold(self.down, key.S)
-			self.remapped = not self.remapped
-			
-
-	window = pyglet.window.Window()
-	window.clear()
-	controls = TestKeyControls()
-	window.push_handlers(controls)
-	pyglet.clock.schedule_interval(controls.step, 0.5)
-	pyglet.app.run()
-
+#############################################################################
+#
+# Copyright (c) 2010 by Casey Duncan
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the MIT License
+# A copy of the license should accompany this distribution.
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+#
+#############################################################################
+"""Control systems for binding controls to game logic"""
+
+import bGrease
+from pyglet.window import key
+
+class KeyControls(grease.System):
+	"""System that maps subclass-defined action methods to keys. 
+
+	Keys may be mapped in the subclass definition using decorators
+	defined here as class methods or at runtime using the ``bind_key_*`` 
+	instance methods.
+
+	See :ref:`an example implementation in the tutorial <tut-controls-example>`.
+	"""
+	MODIFIER_MASK = ~(key.MOD_NUMLOCK | key.MOD_SCROLLLOCK | key.MOD_CAPSLOCK)
+	"""The MODIFIER_MASK allows you to filter out modifier keys that should be
+	ignored by the application. By default, capslock, numlock, and scrolllock 
+	are ignored.
+	"""
+
+	world = None
+	""":class:`grease.World` object this system is bound to"""
+
+	def __init__(self):
+		self._key_press_map = {}
+		self._key_release_map = {}
+		self._key_hold_map = {}
+		for name in self.__class__.__dict__:
+			member = getattr(self, name)
+			if hasattr(member, '_grease_hold_key_binding'):
+				for binding in member._grease_hold_key_binding:
+					self.bind_key_hold(member, *binding)
+			if hasattr(member, '_grease_press_key_binding'):
+				for binding in member._grease_press_key_binding:
+					self.bind_key_press(member, *binding)
+			if hasattr(member, '_grease_release_key_binding'):
+				for binding in member._grease_release_key_binding:
+					self.bind_key_release(member, *binding)
+		self.held_keys = set()
+
+	## decorator methods for binding methods to key input events ##
+
+	@classmethod
+	def key_hold(cls, symbol, modifiers=0):
+		"""Decorator to bind a method to be executed where a key is held down"""
+		def bind(f):
+			if not hasattr(f, '_grease_hold_key_binding'):
+				f._grease_hold_key_binding = []
+			f._grease_hold_key_binding.append((symbol, modifiers & cls.MODIFIER_MASK))
+			return f
+		return bind
+
+	@classmethod
+	def key_press(cls, symbol, modifiers=0):
+		"""Decorator to bind a method to be executed where a key is initially depressed"""
+		def bind(f):
+			if not hasattr(f, '_grease_press_key_binding'):
+				f._grease_press_key_binding = []
+			f._grease_press_key_binding.append((symbol, modifiers & cls.MODIFIER_MASK))
+			return f
+		return bind
+
+	@classmethod
+	def key_release(cls, symbol, modifiers=0):
+		"""Decorator to bind a method to be executed where a key is released"""
+		def bind(f):
+			if not hasattr(f, '_grease_release_key_binding'):
+				f._grease_release_key_binding = []
+			f._grease_release_key_binding.append((symbol, modifiers & cls.MODIFIER_MASK))
+			return f
+		return bind
+	
+	## runtime binding methods ##
+	
+	def bind_key_hold(self, method, key, modifiers=0):
+		"""Bind a method to a key at runtime to be invoked when the key is
+		held down, this replaces any existing key hold binding for this key.
+		To unbind the key entirely, pass ``None`` for method.
+		"""
+		if method is not None:
+			self._key_hold_map[key, modifiers & self.MODIFIER_MASK] = method
+		else:
+			try:
+				del self._key_hold_map[key, modifiers & self.MODIFIER_MASK]
+			except KeyError:
+				pass
+
+	def bind_key_press(self, method, key, modifiers=0):
+		"""Bind a method to a key at runtime to be invoked when the key is initially
+		pressed, this replaces any existing key hold binding for this key. To unbind
+		the key entirely, pass ``None`` for method.
+		"""
+		if method is not None:
+			self._key_press_map[key, modifiers & self.MODIFIER_MASK] = method
+		else:
+			try:
+				del self._key_press_map[key, modifiers & self.MODIFIER_MASK]
+			except KeyError:
+				pass
+
+	def bind_key_release(self, method, key, modifiers=0):
+		"""Bind a method to a key at runtime to be invoked when the key is releaseed,
+		this replaces any existing key hold binding for this key. To unbind
+		the key entirely, pass ``None`` for method.
+		"""
+		if method is not None:
+			self._key_release_map[key, modifiers & self.MODIFIER_MASK] = method
+		else:
+			try:
+				del self._key_release_map[key, modifiers & self.MODIFIER_MASK]
+			except KeyError:
+				pass
+
+	def step(self, dt):
+		"""invoke held key functions"""
+		already_run = set()
+		for key in self.held_keys:
+			func = self._key_hold_map.get(key)
+			if func is not None and func not in already_run:
+				already_run.add(func)
+				func(dt)
+
+	def on_key_press(self, key, modifiers):
+		"""Handle pyglet key press. Invoke key press methods and
+		activate key hold functions
+		"""
+		key_mod = (key, modifiers & self.MODIFIER_MASK)
+		if key_mod in self._key_press_map:
+			self._key_press_map[key_mod]()
+		self.held_keys.add(key_mod)
+	
+	def on_key_release(self, key, modifiers):
+		"""Handle pyglet key release. Invoke key release methods and
+		deactivate key hold functions
+		"""
+		key_mod = (key, modifiers & self.MODIFIER_MASK)
+		if key_mod in self._key_release_map:
+			self._key_release_map[key_mod]()
+		self.held_keys.discard(key_mod)
+
+
+if __name__ == '__main__':
+	import pyglet
+
+	class TestKeyControls(KeyControls):
+		
+		MODIFIER_MASK = ~(key.MOD_NUMLOCK | key.MOD_SCROLLLOCK | key.MOD_CTRL)
+
+		remapped = False
+		
+		@KeyControls.key_hold(key.UP)
+		@KeyControls.key_hold(key.W)
+		def up(self, dt):
+			print 'UP!'
+		
+		@KeyControls.key_hold(key.LEFT)
+		@KeyControls.key_hold(key.A)
+		def left(self, dt):
+			print 'LEFT!'
+		
+		@KeyControls.key_hold(key.RIGHT)
+		@KeyControls.key_hold(key.D)
+		def right(self, dt):
+			print 'RIGHT!'
+		
+		@KeyControls.key_hold(key.DOWN)
+		@KeyControls.key_hold(key.S)
+		def down(self, dt):
+			print 'DOWN!'
+
+		@KeyControls.key_press(key.SPACE)
+		def fire(self):
+			print 'FIRE!'
+
+		@KeyControls.key_press(key.R)
+		def remap_keys(self):
+			if not self.remapped:
+				self.bind_key_hold(None, key.W)
+				self.bind_key_hold(None, key.A)
+				self.bind_key_hold(None, key.S)
+				self.bind_key_hold(None, key.D)
+				self.bind_key_hold(self.up, key.I)
+				self.bind_key_hold(self.left, key.J)
+				self.bind_key_hold(self.right, key.L)
+				self.bind_key_hold(self.down, key.K)
+			else:
+				self.bind_key_hold(None, key.I)
+				self.bind_key_hold(None, key.J)
+				self.bind_key_hold(None, key.K)
+				self.bind_key_hold(None, key.L)
+				self.bind_key_hold(self.up, key.W)
+				self.bind_key_hold(self.left, key.A)
+				self.bind_key_hold(self.right, key.D)
+				self.bind_key_hold(self.down, key.S)
+			self.remapped = not self.remapped
+			
+
+	window = pyglet.window.Window()
+	window.clear()
+	controls = TestKeyControls()
+	window.push_handlers(controls)
+	pyglet.clock.schedule_interval(controls.step, 0.5)
+	pyglet.app.run()
+
--- a/bGrease/mode.py	Thu Dec 15 21:14:13 2011 +0100
+++ b/bGrease/mode.py	Thu Jan 12 18:42:48 2012 +0100
@@ -1,391 +1,391 @@
-#############################################################################
-#
-# Copyright (c) 2010 by Casey Duncan and contributors
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the MIT License
-# A copy of the license should accompany this distribution.
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-#
-#############################################################################
-"""
-Modes manage the state and transition between different application modes.
-Typically such modes are presented as different screens that the user can
-navigate between, similar to the way a browser navigates web pages. Individual
-modes may be things like:
-
-- Title screen
-- Options dialog
-- About screen
-- In-progress game
-- Inventory interface
-
-The modal framework provides a simple mechanism to ensure that modes are
-activated and deactivated properly. An activated mode is running and receives
-events. A deactivated mode is paused and does not receive events.
-
-Modes may be managed as a *last-in-first-out* stack, or as a list, or ring
-of modes in sequence, or some combination of all.
-
-For example usage see: :ref:`the mode section of the tutorial <tut-mode-section>`.
-"""
-
-__version__ = '$Id$'
-
-import abc
-
-
-class BaseManager(object):
-	"""Mode manager abstract base class.
-	
-	The mode manager keeps a stack of modes where a single mode
-	is active at one time. As modes are pushed on and popped from 
-	the stack, the mode at the top is always active. The current
-	active mode receives events from the manager's event dispatcher.
-	"""
-
-	modes = ()
-	"""The mode stack sequence. The last mode in the stack is
-	the current active mode. Read-only.
-	"""
-
-	@property
-	def current_mode(self):
-		"""The current active mode or ``None``. Read-only"""
-		try:
-			return self.modes[-1]
-		except IndexError:
-			return None
-	
-	def on_last_mode_pop(self, mode):
-		"""Hook executed when the last mode is popped from the manager.
-		Implementing this method is optional for subclasses.
-
-		:param mode: The :class:`Mode` object just popped from the manager
-		"""
-	
-	def activate_mode(self, mode):
-		"""Perform actions to activate a node
-		
-		:param mode: The :class: 'Mode' object to activate
-		"""
-		mode.activate(self)
-
-	def deactivate_mode(self, mode):
-		"""Perform actions to deactivate a node
-		
-		:param mode: The :class: 'Mode' object to deactivate
-		"""
-		mode.deactivate(self)
-
-		
-	def push_mode(self, mode):
-		"""Push a mode to the top of the mode stack and make it active
-		
-		:param mode: The :class:`Mode` object to make active
-		"""
-		current = self.current_mode
-		if current is not None:
-			self.deactivate_mode(current)
-		self.modes.append(mode)
-		self.activate_mode(mode)
-	
-	def pop_mode(self):
-		"""Pop the current mode off the top of the stack and deactivate it.
-		The mode now at the top of the stack, if any is then activated.
-
-		:param mode: The :class:`Mode` object popped from the stack
-		"""
-		mode = self.modes.pop()
-		mode.deactivate(self)
-		current = self.current_mode
-		if current is not None:
-			self.activate_mode(current)
-		else:
-			self.on_last_mode_pop(mode)
-		return mode
-	
-	def swap_modes(self, mode):
-		"""Exchange the specified mode with the mode at the top of the stack.
-		This is similar to popping the current mode and pushing the specified
-		one, but without activating the previous mode on the stack or
-		executing :meth:`on_last_mode_pop()` if there is no previous mode.
-
-		:param mode: The :class:`Mode` object that was deactivated and replaced.
-		"""
-		old_mode = self.modes.pop()
-		self.deactivate_mode(old_mode)
-		self.modes.append(mode)
-		self.activate_mode(mode)
-		return old_mode
-	
-	def remove_mode(self, mode):
-		"""Remove the specified mode. If the mode is at the top of the stack,
-		this is equivilent to :meth:`pop_mode()`. If not, no other modes
-		are affected. If the mode is not in the manager, do nothing.
-
-		:param mode: The :class:`Mode` object to remove from the manager.
-		"""
-		if self.current_mode is mode:
-			self.pop_mode()
-		else:
-			try:
-				self.modes.remove(mode)
-			except ValueError:
-				pass
-
-class BaseMode(object):
-	"""Application mode very abstract base class
-	"""
-	__metaclass__ = abc.ABCMeta
-
-	manager = None
-	"""The :class:`BaseManager` that manages this mode"""
-
-	def __init__(self):
-		self.active = False
-		
-	def on_activate(self):
-		"""Being called when the Mode is activated"""
-		pass
-	
-	def activate(self, mode_manager):
-		"""Activate the mode for the given mode manager, if the mode is already active, 
-		do nothing
-
-		The default implementation schedules time steps at :attr:`step_rate` per
-		second, sets the :attr:`manager` and sets the :attr:`active` flag to True.
-		"""
-		if not self.active:
-			self.on_activate()
-			self.manager = mode_manager
-			self.active = True
-
-	def on_deactivate(self):
-		"""Being called when the Mode is deactivated"""
-		pass
-
-	def deactivate(self, mode_manager):
-		"""Deactivate the mode, if the mode is not active, do nothing
-
-		The default implementation unschedules time steps for the mode and
-		sets the :attr:`active` flag to False.
-		"""
-		self.on_deactivate()
-		self.active = False
-
-
-class BaseMulti(BaseMode):
-	"""A mode with multiple submodes. One submode is active at one time.
-	Submodes can be switched to directly or switched in sequence. If
-	the Multi is active, then one submode is always active.
-
-	Multis are useful when modes can switch in an order other than
-	a LIFO stack, such as in "hotseat" multiplayer games, a
-	"wizard" style ui, or a sequence of slides.
-
-	Note unlike a normal :class:`Mode`, a :class:`Multi` doesn't have it's own
-	:attr:`clock` and :attr:`step_rate`. The active submode's are used
-	instead.
-	"""
-	active_submode = None
-	"""The currently active submode"""
-
-	def __init__(self, *submodes):
-		# We do not invoke the superclass __init__ intentionally
-		self.active = False
-		self.submodes = list(submodes)
-	
-	def add_submode(self, mode, before=None, index=None):
-		"""Add the submode, but do not make it active.
-
-		:param mode: The :class:`Mode` object to add.
-
-		:param before: The existing mode to insert the mode before. 
-			If the mode specified is not a submode, raise
-			ValueError.
-
-		:param index: The place to insert the mode in the mode list.
-			Only one of ``before`` or ``index`` may be specified.
-
-			If neither ``before`` or ``index`` are specified, the
-			mode is appended to the end of the list.
-		"""
-		assert before is None or index is None, (
-			"Cannot specify both 'before' and 'index' arguments")
-		if before is not None:
-			index = self.submodes.index(mode)
-		if index is not None:
-			self.submodes.insert(index, mode)
-		else:
-			self.submodes.append(mode)
-	
-	def remove_submode(self, mode=None):
-		"""Remove the submode.
-
-		:param mode: The submode to remove, if omitted the active submode
-			is removed. If the mode is not present, do nothing.  If the
-			mode is active, it is deactivated, and the next mode, if any
-			is activated. If the last mode is removed, the :class:`Multi`
-			is removed from its manager. 
-		"""
-		# TODO handle multiple instances of the same subnode
-		if mode is None:
-			mode = self.active_submode
-		elif mode not in self.submodes:
-			return
-		next_mode = self.activate_next()
-		self.submodes.remove(mode)
-		if next_mode is mode:
-			if self.manager is not None:
-				self.manager.remove_mode(self)
-			self._deactivate_submode()
-				
-	def activate_subnode(self, mode, before=None, index=None):
-		"""Activate the specified mode, adding it as a subnode
-		if it is not already. If the mode is already the active
-		submode, do nothing.
-
-		:param mode: The mode to activate, and add as necesary.
-
-		:param before: The existing mode to insert the mode before
-			if it is not already a submode.  If the mode specified is not
-			a submode, raise ValueError.
-
-		:param index: The place to insert the mode in the mode list
-			if it is not already a submode.  Only one of ``before`` or
-			``index`` may be specified.
-
-			If the mode is already a submode, the ``before`` and ``index``
-			arguments are ignored.
-		"""
-		if mode not in self.submodes:
-			self.add_submode(mode, before, index)
-		if self.active_submode is not mode:
-			self._activate_submode(mode)
-	
-	def activate_next(self, loop=True):
-		"""Activate the submode after the current submode in order.  If there
-		is no current submode, the first submode is activated.
-
-		Note if there is only one submode, it's active, and `loop` is True
-		(the default), then this method does nothing and the subnode remains
-		active.
-
-		:param loop: When :meth:`activate_next` is called 
-			when the last submode is active, a True value for ``loop`` will
-			cause the first submode to be activated.  Otherwise the
-			:class:`Multi` is removed from its manager.
-		:type loop: bool
-
-		:return:
-			The submode that was activated or None if there is no
-			other submode to activate.
-		"""
-		assert self.submodes, "No submode to activate"
-		next_mode = None
-		if self.active_submode is None:
-			next_mode = self.submodes[0]
-		else:
-			last_mode = self.active_submode
-			index = self.submodes.index(last_mode) + 1
-			if index < len(self.submodes):
-				next_mode = self.submodes[index]
-			elif loop:
-				next_mode = self.submodes[0]
-		self._activate_submode(next_mode)
-		return next_mode
-
-	def activate_previous(self, loop=True):
-		"""Activate the submode before the current submode in order.  If there
-		is no current submode, the last submode is activated.
-
-		Note if there is only one submode, it's active, and `loop` is True
-		(the default), then this method does nothing and the subnode remains
-		active.
-		
-		:param loop: When :meth:`activate_previous` is called 
-			when the first submode is active, a True value for ``loop`` will
-			cause the last submode to be activated.  Otherwise the
-			:class:`Multi` is removed from its manager.
-		:type loop: bool
-
-		:return:
-			The submode that was activated or None if there is no
-			other submode to activate.
-		"""
-		assert self.submodes, "No submode to activate"
-		prev_mode = None
-		if self.active_submode is None:
-			prev_mode = self.submodes[-1]
-		else:
-			last_mode = self.active_submode
-			index = self.submodes.index(last_mode) - 1
-			if loop or index >= 0:
-				prev_mode = self.submodes[index]
-		self._activate_submode(prev_mode)
-		return prev_mode
-	
-	def _set_active_submode(self, submode):
-		self.active_submode = submode
-		self.step_rate = submode.step_rate
-
-	def _activate_submode(self, submode):
-		"""Activate a submode deactivating any current submode. If the Multi
-		itself is active, this happens immediately, otherwise the actual
-		activation is deferred until the Multi is activated. If the submode
-		is None, the Mulitmode is removed from its manager.
-
-		If submode is already the active submode, do nothing.
-		"""
-		if self.active_submode is submode:
-			return
-		assert submode in self.submodes, "Unknown submode"
-		self._deactivate_submode()
-		self._set_active_submode(submode)
-		if submode is not None:
-			if self.active:
-				self.manager.activate_mode(submode)
-		else:
-			if self.manager is not None:
-				self.manager.remove_mode(self)
-	
-	def clear_subnode(self):
-		"""Clear any subnmode data"""
-		self.active_submode = None
-		self.step_rate = None
-				
-	def _deactivate_submode(self, clear_subnode=True):
-		"""Deactivate the current submode, if any. if `clear_subnode` is
-		True, `active_submode` is always None when this method returns
-		"""
-		if self.active_submode is not None:
-			if self.active:
-				self.manager.deactivate_mode(self.active_submode)
-			if clear_subnode:
-				self.clear_subnode()
-	
-	def activate(self, mode_manager):
-		"""Activate the :class:`Multi` for the specified manager. The
-		previously active submode of the :class:`Multi` is activated. If there
-		is no previously active submode, then the first submode is made active. 
-		A :class:`Multi` with no submodes cannot be activated
-		"""
-		assert self.submodes, "No submode to activate"
-		self.manager = mode_manager
-		if self.active_submode is None:
-			self._set_active_submode(self.submodes[0])
-		else:
-			self._set_active_submode(self.active_submode)
-		self.manager.activate_mode(self.active_submode)
-		super(BaseMulti, self).activate(mode_manager)
-	
-	def deactivate(self, mode_manager):
-		"""Deactivate the :class:`Multi` for the specified manager.
-		The `active_submode`, if any, is deactivated.
-		"""
-		self._deactivate_submode(clear_subnode=False)
-		super(BaseMulti, self).deactivate(mode_manager)
-
+#############################################################################
+#
+# Copyright (c) 2010 by Casey Duncan and contributors
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the MIT License
+# A copy of the license should accompany this distribution.
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+#
+#############################################################################
+"""
+Modes manage the state and transition between different application modes.
+Typically such modes are presented as different screens that the user can
+navigate between, similar to the way a browser navigates web pages. Individual
+modes may be things like:
+
+- Title screen
+- Options dialog
+- About screen
+- In-progress game
+- Inventory interface
+
+The modal framework provides a simple mechanism to ensure that modes are
+activated and deactivated properly. An activated mode is running and receives
+events. A deactivated mode is paused and does not receive events.
+
+Modes may be managed as a *last-in-first-out* stack, or as a list, or ring
+of modes in sequence, or some combination of all.
+
+For example usage see: :ref:`the mode section of the tutorial <tut-mode-section>`.
+"""
+
+__version__ = '$Id$'
+
+import abc
+
+
+class BaseManager(object):
+	"""Mode manager abstract base class.
+	
+	The mode manager keeps a stack of modes where a single mode
+	is active at one time. As modes are pushed on and popped from 
+	the stack, the mode at the top is always active. The current
+	active mode receives events from the manager's event dispatcher.
+	"""
+
+	modes = ()
+	"""The mode stack sequence. The last mode in the stack is
+	the current active mode. Read-only.
+	"""
+
+	@property
+	def current_mode(self):
+		"""The current active mode or ``None``. Read-only"""
+		try:
+			return self.modes[-1]
+		except IndexError:
+			return None
+	
+	def on_last_mode_pop(self, mode):
+		"""Hook executed when the last mode is popped from the manager.
+		Implementing this method is optional for subclasses.
+
+		:param mode: The :class:`Mode` object just popped from the manager
+		"""
+	
+	def activate_mode(self, mode):
+		"""Perform actions to activate a node
+		
+		:param mode: The :class: 'Mode' object to activate
+		"""
+		mode.activate(self)
+
+	def deactivate_mode(self, mode):
+		"""Perform actions to deactivate a node
+		
+		:param mode: The :class: 'Mode' object to deactivate
+		"""
+		mode.deactivate(self)
+
+		
+	def push_mode(self, mode):
+		"""Push a mode to the top of the mode stack and make it active
+		
+		:param mode: The :class:`Mode` object to make active
+		"""
+		current = self.current_mode
+		if current is not None:
+			self.deactivate_mode(current)
+		self.modes.append(mode)
+		self.activate_mode(mode)
+	
+	def pop_mode(self):
+		"""Pop the current mode off the top of the stack and deactivate it.
+		The mode now at the top of the stack, if any is then activated.
+
+		:param mode: The :class:`Mode` object popped from the stack
+		"""
+		mode = self.modes.pop()
+		mode.deactivate(self)
+		current = self.current_mode
+		if current is not None:
+			self.activate_mode(current)
+		else:
+			self.on_last_mode_pop(mode)
+		return mode
+	
+	def swap_modes(self, mode):
+		"""Exchange the specified mode with the mode at the top of the stack.
+		This is similar to popping the current mode and pushing the specified
+		one, but without activating the previous mode on the stack or
+		executing :meth:`on_last_mode_pop()` if there is no previous mode.
+
+		:param mode: The :class:`Mode` object that was deactivated and replaced.
+		"""
+		old_mode = self.modes.pop()
+		self.deactivate_mode(old_mode)
+		self.modes.append(mode)
+		self.activate_mode(mode)
+		return old_mode
+	
+	def remove_mode(self, mode):
+		"""Remove the specified mode. If the mode is at the top of the stack,
+		this is equivilent to :meth:`pop_mode()`. If not, no other modes
+		are affected. If the mode is not in the manager, do nothing.
+
+		:param mode: The :class:`Mode` object to remove from the manager.
+		"""
+		if self.current_mode is mode:
+			self.pop_mode()
+		else:
+			try:
+				self.modes.remove(mode)
+			except ValueError:
+				pass
+
+class BaseMode(object):
+	"""Application mode very abstract base class
+	"""
+	__metaclass__ = abc.ABCMeta
+
+	manager = None
+	"""The :class:`BaseManager` that manages this mode"""
+
+	def __init__(self):
+		self.active = False
+		
+	def on_activate(self):
+		"""Being called when the Mode is activated"""
+		pass
+	
+	def activate(self, mode_manager):
+		"""Activate the mode for the given mode manager, if the mode is already active, 
+		do nothing
+
+		The default implementation schedules time steps at :attr:`step_rate` per
+		second, sets the :attr:`manager` and sets the :attr:`active` flag to True.
+		"""
+		if not self.active:
+			self.on_activate()
+			self.manager = mode_manager
+			self.active = True
+
+	def on_deactivate(self):
+		"""Being called when the Mode is deactivated"""
+		pass
+
+	def deactivate(self, mode_manager):
+		"""Deactivate the mode, if the mode is not active, do nothing
+
+		The default implementation unschedules time steps for the mode and
+		sets the :attr:`active` flag to False.
+		"""
+		self.on_deactivate()
+		self.active = False
+
+
+class BaseMulti(BaseMode):
+	"""A mode with multiple submodes. One submode is active at one time.
+	Submodes can be switched to directly or switched in sequence. If
+	the Multi is active, then one submode is always active.
+
+	Multis are useful when modes can switch in an order other than
+	a LIFO stack, such as in "hotseat" multiplayer games, a
+	"wizard" style ui, or a sequence of slides.
+
+	Note unlike a normal :class:`Mode`, a :class:`Multi` doesn't have it's own
+	:attr:`clock` and :attr:`step_rate`. The active submode's are used
+	instead.
+	"""
+	active_submode = None
+	"""The currently active submode"""
+
+	def __init__(self, *submodes):
+		# We do not invoke the superclass __init__ intentionally
+		self.active = False
+		self.submodes = list(submodes)
+	
+	def add_submode(self, mode, before=None, index=None):
+		"""Add the submode, but do not make it active.
+
+		:param mode: The :class:`Mode` object to add.
+
+		:param before: The existing mode to insert the mode before. 
+			If the mode specified is not a submode, raise
+			ValueError.
+
+		:param index: The place to insert the mode in the mode list.
+			Only one of ``before`` or ``index`` may be specified.
+
+			If neither ``before`` or ``index`` are specified, the
+			mode is appended to the end of the list.
+		"""
+		assert before is None or index is None, (
+			"Cannot specify both 'before' and 'index' arguments")
+		if before is not None:
+			index = self.submodes.index(mode)
+		if index is not None:
+			self.submodes.insert(index, mode)
+		else:
+			self.submodes.append(mode)
+	
+	def remove_submode(self, mode=None):
+		"""Remove the submode.
+
+		:param mode: The submode to remove, if omitted the active submode
+			is removed. If the mode is not present, do nothing.  If the
+			mode is active, it is deactivated, and the next mode, if any
+			is activated. If the last mode is removed, the :class:`Multi`
+			is removed from its manager. 
+		"""
+		# TODO handle multiple instances of the same subnode
+		if mode is None:
+			mode = self.active_submode
+		elif mode not in self.submodes:
+			return
+		next_mode = self.activate_next()
+		self.submodes.remove(mode)
+		if next_mode is mode:
+			if self.manager is not None:
+				self.manager.remove_mode(self)
+			self._deactivate_submode()
+				
+	def activate_subnode(self, mode, before=None, index=None):
+		"""Activate the specified mode, adding it as a subnode
+		if it is not already. If the mode is already the active
+		submode, do nothing.
+
+		:param mode: The mode to activate, and add as necesary.
+
+		:param before: The existing mode to insert the mode before
+			if it is not already a submode.  If the mode specified is not
+			a submode, raise ValueError.
+
+		:param index: The place to insert the mode in the mode list
+			if it is not already a submode.  Only one of ``before`` or
+			``index`` may be specified.
+
+			If the mode is already a submode, the ``before`` and ``index``
+			arguments are ignored.
+		"""
+		if mode not in self.submodes:
+			self.add_submode(mode, before, index)
+		if self.active_submode is not mode:
+			self._activate_submode(mode)
+	
+	def activate_next(self, loop=True):
+		"""Activate the submode after the current submode in order.  If there
+		is no current submode, the first submode is activated.
+
+		Note if there is only one submode, it's active, and `loop` is True
+		(the default), then this method does nothing and the subnode remains
+		active.
+
+		:param loop: When :meth:`activate_next` is called 
+			when the last submode is active, a True value for ``loop`` will
+			cause the first submode to be activated.  Otherwise the
+			:class:`Multi` is removed from its manager.
+		:type loop: bool
+
+		:return:
+			The submode that was activated or None if there is no
+			other submode to activate.
+		"""
+		assert self.submodes, "No submode to activate"
+		next_mode = None
+		if self.active_submode is None:
+			next_mode = self.submodes[0]
+		else:
+			last_mode = self.active_submode
+			index = self.submodes.index(last_mode) + 1
+			if index < len(self.submodes):
+				next_mode = self.submodes[index]
+			elif loop:
+				next_mode = self.submodes[0]
+		self._activate_submode(next_mode)
+		return next_mode
+
+	def activate_previous(self, loop=True):
+		"""Activate the submode before the current submode in order.  If there
+		is no current submode, the last submode is activated.
+
+		Note if there is only one submode, it's active, and `loop` is True
+		(the default), then this method does nothing and the subnode remains
+		active.
+		
+		:param loop: When :meth:`activate_previous` is called 
+			when the first submode is active, a True value for ``loop`` will
+			cause the last submode to be activated.  Otherwise the
+			:class:`Multi` is removed from its manager.
+		:type loop: bool
+
+		:return:
+			The submode that was activated or None if there is no
+			other submode to activate.
+		"""
+		assert self.submodes, "No submode to activate"
+		prev_mode = None
+		if self.active_submode is None:
+			prev_mode = self.submodes[-1]
+		else:
+			last_mode = self.active_submode
+			index = self.submodes.index(last_mode) - 1
+			if loop or index >= 0:
+				prev_mode = self.submodes[index]
+		self._activate_submode(prev_mode)
+		return prev_mode
+	
+	def _set_active_submode(self, submode):
+		self.active_submode = submode
+		self.step_rate = submode.step_rate
+
+	def _activate_submode(self, submode):
+		"""Activate a submode deactivating any current submode. If the Multi
+		itself is active, this happens immediately, otherwise the actual
+		activation is deferred until the Multi is activated. If the submode
+		is None, the Mulitmode is removed from its manager.
+
+		If submode is already the active submode, do nothing.
+		"""
+		if self.active_submode is submode:
+			return
+		assert submode in self.submodes, "Unknown submode"
+		self._deactivate_submode()
+		self._set_active_submode(submode)
+		if submode is not None:
+			if self.active:
+				self.manager.activate_mode(submode)
+		else:
+			if self.manager is not None:
+				self.manager.remove_mode(self)
+	
+	def clear_subnode(self):
+		"""Clear any subnmode data"""
+		self.active_submode = None
+		self.step_rate = None
+				
+	def _deactivate_submode(self, clear_subnode=True):
+		"""Deactivate the current submode, if any. if `clear_subnode` is
+		True, `active_submode` is always None when this method returns
+		"""
+		if self.active_submode is not None:
+			if self.active:
+				self.manager.deactivate_mode(self.active_submode)
+			if clear_subnode:
+				self.clear_subnode()
+	
+	def activate(self, mode_manager):
+		"""Activate the :class:`Multi` for the specified manager. The
+		previously active submode of the :class:`Multi` is activated. If there
+		is no previously active submode, then the first submode is made active. 
+		A :class:`Multi` with no submodes cannot be activated
+		"""
+		assert self.submodes, "No submode to activate"
+		self.manager = mode_manager
+		if self.active_submode is None:
+			self._set_active_submode(self.submodes[0])
+		else:
+			self._set_active_submode(self.active_submode)
+		self.manager.activate_mode(self.active_submode)
+		super(BaseMulti, self).activate(mode_manager)
+	
+	def deactivate(self, mode_manager):
+		"""Deactivate the :class:`Multi` for the specified manager.
+		The `active_submode`, if any, is deactivated.
+		"""
+		self._deactivate_submode(clear_subnode=False)
+		super(BaseMulti, self).deactivate(mode_manager)
+
--- a/bGrease/renderer/camera.py	Thu Dec 15 21:14:13 2011 +0100
+++ b/bGrease/renderer/camera.py	Thu Jan 12 18:42:48 2012 +0100
@@ -1,39 +1,39 @@
-import pyglet
-
-class Camera(object):
-	"""Sets the point of view for further renderers by altering the
-	model/view matrix when it is drawn. It does not actually perform
-	any drawing itself.
-
-	:param position: The position vector for the camera. Sets the center of the view.
-	:type position: Vec2d
-	:param angle: Camera rotation in degrees about the z-axis.
-	:type angle: float
-	:param zoom: Scaling vector for the coordinate axis.
-	:type zoom: Vec2d
-	:param relative: Flag to indicate if the camera settings are relative 
-		to the previous view state. If ``False`` the view state is reset before 
-		setting the camera view by loading the identity model/view matrix.
-	
-	At runtime the camera may be manipulated via attributes with the 
-	same names and functions as the parameters above.
-	"""
-
-	def __init__(self, position=None, angle=None, zoom=None, relative=False):
-		self.position = position
-		self.angle = angle
-		self.zoom = zoom
-		self.relative = relative
-	
-	def draw(self, gl=pyglet.gl):
-		if not self.relative:
-			gl.glLoadIdentity()
-		if self.position is not None:
-			px, py = self.position
-			gl.glTranslatef(px, py, 0)
-		if self.angle is not None:
-			gl.glRotatef(self.angle, 0, 0, 1)
-		if self.zoom is not None:
-			sx, sy = self.zoom
-			gl.glScalef(sx, sy ,0)
-
+import pyglet
+
+class Camera(object):
+	"""Sets the point of view for further renderers by altering the
+	model/view matrix when it is drawn. It does not actually perform
+	any drawing itself.
+
+	:param position: The position vector for the camera. Sets the center of the view.
+	:type position: Vec2d
+	:param angle: Camera rotation in degrees about the z-axis.
+	:type angle: float
+	:param zoom: Scaling vector for the coordinate axis.
+	:type zoom: Vec2d
+	:param relative: Flag to indicate if the camera settings are relative 
+		to the previous view state. If ``False`` the view state is reset before 
+		setting the camera view by loading the identity model/view matrix.
+	
+	At runtime the camera may be manipulated via attributes with the 
+	same names and functions as the parameters above.
+	"""
+
+	def __init__(self, position=None, angle=None, zoom=None, relative=False):
+		self.position = position
+		self.angle = angle
+		self.zoom = zoom
+		self.relative = relative
+	
+	def draw(self, gl=pyglet.gl):
+		if not self.relative:
+			gl.glLoadIdentity()
+		if self.position is not None:
+			px, py = self.position
+			gl.glTranslatef(px, py, 0)
+		if self.angle is not None:
+			gl.glRotatef(self.angle, 0, 0, 1)
+		if self.zoom is not None:
+			sx, sy = self.zoom
+			gl.glScalef(sx, sy ,0)
+