changeset 5:bc88f7d5ca8b

Added base files for grease
author KarstenBock@gmx.net
date Tue, 12 Jul 2011 10:16:48 +0200
parents bf1dd9c24a7e
children ce33d344e202
files grease/__init__.py grease/collision.py grease/color.py grease/component/__init__.py grease/component/base.py grease/component/field.py grease/component/general.py grease/component/schema.py grease/controller/__init__.py grease/controller/integrator.py grease/entity.py grease/geometry.py grease/impl/__init__.py grease/impl/controls.py grease/impl/mode.py grease/impl/world.py grease/mode.py grease/renderer/__init__.py grease/renderer/camera.py grease/renderer/vector.py grease/world.py mode.py world.py
diffstat 23 files changed, 3720 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grease/__init__.py	Tue Jul 12 10:16:48 2011 +0200
@@ -0,0 +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 grease.component
+import grease.geometry
+import grease.collision
+from grease.entity import Entity
+from grease.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.
+		"""
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grease/collision.py	Tue Jul 12 10:16:48 2011 +0200
@@ -0,0 +1,528 @@
+#############################################################################
+#
+# 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.
+#
+#############################################################################
+"""
+**Grease collision detection systems**
+
+Grease uses two-phase broad and narrow collision detection. *Broad-phase*
+collision systems are used to efficiently identify pairs that may be colliding
+without resorting to a brute-force check of all possible pairs. *Narrow-phase*
+collision systems use the pairs generated by the broad-phase and perform more
+precise collision tests to determine if a collision has actually occurred. The
+narrow-phase system also calculates more details about each collision,
+including collision point and normal vector for use in collision response.
+
+A typical collision detection system consists of a narrow-phase system that
+contains a broad-phased system. The narrow-phase system is usually the only
+
+one that the application directly interacts with, though the application is
+free to use the broad-phased system directly if desired. This could be
+useful in cases where speed, rather than precision is paramount.
+
+The narrow-phase system can be assigned handler objects to run after
+collision detection. These can perform tasks like handling collision response
+or dispatching collision events to application handlers.
+
+Note that broad-phase systems can return false positives, though they should
+never return false negatives. Do not assume that all pairs returned by a
+broad-phase system are actually in collision.
+"""
+
+__version__ = '$Id$'
+
+from grease.geometry import Vec2d
+from bisect import bisect_right
+
+
+class Pair(tuple):
+	"""Pair of entities in collision. This is an ordered sequence of two
+	entities, that compares and hashes unordered.
+	
+	Also stores additional collision point and normal vectors
+	for each entity.
+
+	Sets of ``Pair`` objects are exposed in the ``collision_pairs``
+	attribute of collision systems to indicate the entity pairs in
+	collision.
+	"""
+	info = None
+	"""A sequence of (entity, collision point, collision normal)
+	for each entity in the pair
+	"""
+
+	def __new__(cls, entity1, entity2, point=None, normal=None):
+		pair = tuple.__new__(cls, (entity1, entity2))
+		return pair
+	
+	def __hash__(self):
+		return hash(self[0]) ^ hash(self[1])
+	
+	def __eq__(self, other):
+		other = tuple(other)
+		return tuple(self) == other or (self[1], self[0]) == other
+	
+	def __repr__(self):
+		return '%s%r' % (self.__class__.__name__, tuple(self))
+	
+	def set_point_normal(self, point0, normal0, point1, normal1):
+		"""Set the collision point and normal for both entities"""
+		self.info = (
+			(self[0], point0, normal0),
+			(self[1], point1, normal1),
+		)
+
+
+class BroadSweepAndPrune(object):
+	"""2D Broad-phase sweep and prune bounding box collision detector
+
+	This algorithm is efficient for collision detection between many
+	moving bodies. It has linear algorithmic complexity and takes
+	advantage of temporal coherence between frames. It also does
+	not suffer from bad worst-case performance (like RDC can). 
+	Unlike spacial hashing, it does not need to be optimized for 
+	specific space and body sizes.
+
+	Other algorithms may be more efficient for collision detection with
+	stationary bodies, bodies that are always evenly distributed, or ad-hoc
+	queries.
+
+	:param collision_component: Name of the collision component used by this
+		system, defaults to 'collision'. This component supplies each
+		entities' aabb and collision masks.
+	:type collision_component: str
+	"""
+	world = None
+	"""|World| object this system belongs to"""
+
+	collision_component = None
+	"""Name of world's collision component used by this system"""
+
+	LEFT_ATTR = "left"
+	RIGHT_ATTR = "right"
+	TOP_ATTR = "top"
+	BOTTOM_ATTR = "bottom"
+
+	def __init__(self, collision_component='collision'):
+		self.collision_component = collision_component
+		self._by_x = None
+		self._by_y = None
+		self._collision_pairs = None
+	
+	def set_world(self, world):
+		"""Bind the system to a world"""
+		self.world = world
+	
+	def step(self, dt):
+		"""Update the system for this time step, updates and sorts the 
+		axis arrays.
+		"""
+		component = getattr(self.world.components, self.collision_component)
+		LEFT = self.LEFT_ATTR
+		RIGHT = self.RIGHT_ATTR
+		TOP = self.TOP_ATTR
+		BOTTOM = self.BOTTOM_ATTR
+		if self._by_x is None:
+			# Build axis lists from scratch
+			# Note we cache the box positions here
+			# so that we can perform hit tests efficiently
+			# it also isolates us from changes made to the 
+			# box positions after we run
+			by_x = self._by_x = []
+			append_x = by_x.append
+			by_y = self._by_y = []
+			append_y = by_y.append
+			for data in component.itervalues():
+				append_x([data.aabb.left, LEFT, data])
+				append_x([data.aabb.right, RIGHT, data])
+				append_y([data.aabb.bottom, BOTTOM, data])
+				append_y([data.aabb.top, TOP, data])
+		else:
+			by_x = self._by_x
+			by_y = self._by_y
+			removed = []
+			for entry in by_x:
+				entry[0] = getattr(entry[2].aabb, entry[1])
+			for entry in by_y:
+				entry[0] = getattr(entry[2].aabb, entry[1])
+			# Removing entities is inefficient, but expected to be rare
+			if component.deleted_entities:
+				deleted_entities = component.deleted_entities
+				deleted_x = []
+				deleted_y = []
+				for i, (_, _, data) in enumerate(by_x):
+					if data.entity in deleted_entities:
+						deleted_x.append(i)
+				deleted_x.reverse()
+				for i in deleted_x:
+					del by_x[i]
+				for i, (_, _, data) in enumerate(by_y):
+					if data.entity in deleted_entities:
+						deleted_y.append(i)
+				deleted_y.reverse()
+				for i in deleted_y:
+					del by_y[i]
+			# Tack on new entities
+			for entity in component.new_entities:
+				data = component[entity]
+				by_x.append([data.aabb.left, LEFT, data])
+				by_x.append([data.aabb.right, RIGHT, data])
+				by_y.append([data.aabb.bottom, BOTTOM, data])
+				by_y.append([data.aabb.top, TOP, data])
+				
+		# Tim-sort is highly efficient with mostly sorted lists.
+		# Because positions tend to change little each frame
+		# we take advantage of this here. Obviously things are
+		# less efficient with very fast moving, or teleporting entities
+		by_x.sort()
+		by_y.sort()
+		self._collision_pairs = None
+	
+	@property
+	def collision_pairs(self):
+		"""Set of candidate collision pairs for this timestep"""
+		if self._collision_pairs is None:
+			if self._by_x is None:
+				# Axis arrays not ready
+				return set()
+
+			LEFT = self.LEFT_ATTR
+			RIGHT = self.RIGHT_ATTR
+			TOP = self.TOP_ATTR
+			BOTTOM = self.BOTTOM_ATTR
+			# Build candidates overlapping along the x-axis
+			component = getattr(self.world.components, self.collision_component)
+			xoverlaps = set()
+			add_xoverlap = xoverlaps.add
+			discard_xoverlap = xoverlaps.discard
+			open = {}
+			for _, side, data in self._by_x:
+				if side is LEFT:
+					for open_entity, (from_mask, into_mask) in open.iteritems():
+						if data.from_mask & into_mask or from_mask & data.into_mask:
+							add_xoverlap(Pair(data.entity, open_entity))
+					open[data.entity] = (data.from_mask, data.into_mask)
+				elif side is RIGHT:
+					del open[data.entity]
+
+			if len(xoverlaps) <= 10 and len(xoverlaps)*4 < len(self._by_y):
+				# few candidates were found, so just scan the x overlap candidates
+				# along y. This requires an additional sort, but it should
+				# be cheaper than scanning everyone and its simpler
+				# than a separate brute-force check
+				entities = set([entity for entity, _ in xoverlaps] 
+					+ [entity for _, entity in xoverlaps])
+				by_y = []
+				for entity in entities:
+					data = component[entity]
+					# We can use tuples here, which are cheaper to create
+					by_y.append((data.aabb.bottom, BOTTOM, data))
+					by_y.append((data.aabb.top, TOP, data))
+				by_y.sort()
+			else:
+				by_y = self._by_y
+
+			# Now check the candidates along the y-axis
+			open = set()
+			add_open = open.add
+			discard_open = open.discard
+			self._collision_pairs = set()
+			add_pair = self._collision_pairs.add
+			for _, side, data in by_y:
+				if side is BOTTOM:
+					for open_entity in open:
+						pair = Pair(data.entity, open_entity)
+						if pair in xoverlaps:
+							discard_xoverlap(pair)
+							add_pair(pair)
+							if not xoverlaps:
+								# No more candidates, bail
+								return self._collision_pairs
+					add_open(data.entity)
+				elif side is TOP:
+					discard_open(data.entity)
+		return self._collision_pairs
+	
+	def query_point(self, x_or_point, y=None, from_mask=0xffffffff):
+		"""Hit test at the point specified. 
+
+		:param x_or_point: x coordinate (float) or sequence of (x, y) floats.
+
+		:param y: y coordinate (float) if x is not a sequence
+
+		:param from_mask: Bit mask used to filter query results. This value
+			is bit ANDed with candidate entities' ``collision.into_mask``.
+			If the result is non-zero, then it is considered a hit. By
+			default all entities colliding with the input point are
+			returned.
+
+		:return: A set of entities where the point is inside their bounding
+			boxes as of the last time step.
+		"""
+		if self._by_x is None:
+			# Axis arrays not ready
+			return set()
+		if y is None:
+			x, y = x_or_point
+		else:
+			x = x_or_point
+		LEFT = self.LEFT_ATTR
+		RIGHT = self.RIGHT_ATTR
+		TOP = self.TOP_ATTR
+		BOTTOM = self.BOTTOM_ATTR
+		x_index = bisect_right(self._by_x, [x])
+		x_hits = set()
+		add_x_hit = x_hits.add
+		discard_x_hit = x_hits.discard
+		if x_index <= len(self._by_x) // 2:
+			# closer to the left, scan from left to right
+			while (x == self._by_x[x_index][0] 
+				and self._by_x[x_index][1] is LEFT 
+				and x_index < len(self._by_x)):
+				# Ensure we hit on exact left edge matches
+				x_index += 1
+			for _, side, data in self._by_x[:x_index]:
+				if side is LEFT and from_mask & data.into_mask:
+					add_x_hit(data.entity)
+				else:
+					discard_x_hit(data.entity)
+		else:
+			# closer to the right
+			for _, side, data in reversed(self._by_x[x_index:]):
+				if side is RIGHT and from_mask & data.into_mask:
+					add_x_hit(data.entity)
+				else:
+					discard_x_hit(data.entity)
+		if not x_hits:
+			return x_hits
+
+		y_index = bisect_right(self._by_y, [y])
+		y_hits = set()
+		add_y_hit = y_hits.add
+		discard_y_hit = y_hits.discard
+		if y_index <= len(self._by_y) // 2:
+			# closer to the bottom
+			while (y == self._by_y[y_index][0] 
+				and self._by_y[y_index][1] is BOTTOM 
+				and y_index < len(self._by_y)):
+				# Ensure we hit on exact bottom edge matches
+				y_index += 1
+			for _, side, data in self._by_y[:y_index]:
+				if side is BOTTOM:
+					add_y_hit(data.entity)
+				else:
+					discard_y_hit(data.entity)
+		else:
+			# closer to the top
+			for _, side, data in reversed(self._by_y[y_index:]):
+				if side is TOP:
+					add_y_hit(data.entity)
+				else:
+					discard_y_hit(data.entity)
+		if y_hits:
+			return x_hits & y_hits
+		else:
+			return y_hits
+
+
+class Circular(object):
+	"""Basic narrow-phase collision detector which treats all entities as
+	circles with their radius defined in the collision component.
+
+	:param handlers: A sequence of collision handler functions that are invoked
+		after collision detection.
+	:type handlers: sequence of functions
+	
+	:param collision_component: Name of collision component for this system,
+		defaults to 'collision'. This supplies each entity's collision
+		radius and masks.
+	:type collision_component: str
+
+	:param position_component: Name of position component for this system,
+		defaults to 'position'. This supplies each entity's position.
+	:type position_component: str
+
+	:param update_aabbs: If True (the default), then the entities'
+		`collision.aabb` fields will be updated using their position
+		and collision radius before invoking the broad phase system. 
+		Set this False if another system updates the aabbs.
+	:type update_aabbs: bool
+
+	:param broad_phase: A broad-phase collision system to use as a source
+		for collision pairs. If not specified, a :class:`BroadSweepAndPrune`
+		system will be created automatically.
+	"""
+	world = None
+	"""|World| object this system belongs to"""
+
+	position_component = None
+	"""Name of world's position component used by this system"""
+
+	collision_component = None
+	"""Name of world's collision component used by this system"""
+
+	update_aabbs = True
+	"""Flag to indicate whether the system updates the entities' `collision.aabb`
+	field before invoking the broad phase collision system
+	"""
+	
+	handlers = None
+	"""A sequence of collision handler functions invoke after collision
+	detection
+	"""
+
+	broad_phase = None
+	"""Broad phase collision system used as a source for collision pairs"""
+
+	def __init__(self, handlers=(), position_component='position', 
+		collision_component='collision', update_aabbs=True, broad_phase=None):
+		self.handlers = tuple(handlers)
+		if broad_phase is None:
+			broad_phase = BroadSweepAndPrune(collision_component)
+		self.collision_component = collision_component
+		self.position_component = position_component
+		self.update_aabbs = bool(update_aabbs)
+		self.broad_phase = broad_phase
+		self._collision_pairs = None
+	
+	def set_world(self, world):
+		"""Bind the system to a world"""
+		self.world = world
+		self.broad_phase.set_world(world)
+		for handler in self.handlers:
+			if hasattr(handler, 'set_world'):
+				handler.set_world(world)
+	
+	def step(self, dt):
+		"""Update the collision system for this time step and invoke
+		the handlers
+		"""
+		if self.update_aabbs:
+			for position, collision in self.world.components.join(
+				self.position_component, self.collision_component):
+				aabb = collision.aabb
+				x, y = position.position
+				radius = collision.radius
+				aabb.left = x - radius
+				aabb.right = x + radius
+				aabb.bottom = y - radius
+				aabb.top = y + radius
+		self.broad_phase.step(dt)
+		self._collision_pairs = None
+		for handler in self.handlers:
+			handler(self)
+	
+	@property
+	def collision_pairs(self):
+		"""The set of entity pairs in collision in this timestep"""
+		if self._collision_pairs is None:
+			position = getattr(self.world.components, self.position_component)
+			collision = getattr(self.world.components, self.collision_component)
+			pairs = self._collision_pairs = set()
+			for pair in self.broad_phase.collision_pairs:
+				entity1, entity2 = pair
+				position1 = position[entity1].position
+				position2 = position[entity2].position
+				radius1 = collision[entity1].radius
+				radius2 = collision[entity2].radius
+				separation = position2 - position1
+				if separation.get_length_sqrd() <= (radius1 + radius2)**2:
+					normal = separation.normalized()
+					pair.set_point_normal(
+						normal * radius1 + position1, normal,
+						normal * -radius2 + position2, -normal)
+					pairs.add(pair)
+		return self._collision_pairs
+	
+	def query_point(self, x_or_point, y=None, from_mask=0xffffffff):
+		"""Hit test at the point specified. 
+
+		:param x_or_point: x coordinate (float) or sequence of (x, y) floats.
+
+		:param y: y coordinate (float) if x is not a sequence
+
+		:param from_mask: Bit mask used to filter query results. This value
+			is bit ANDed with candidate entities' ``collision.into_mask``.
+			If the result is non-zero, then it is considered a hit. By
+			default all entities colliding with the input point are
+			returned.
+
+		:return: A set of entities where the point is inside their collision
+			radii as of the last time step.
+
+		"""
+		if y is None:
+			point = Vec2d(x_or_point)
+		else:
+			point = Vec2d(x_or_point, y)
+		hits = set()
+		position = getattr(self.world.components, self.position_component)
+		collision = getattr(self.world.components, self.collision_component)
+		for entity in self.broad_phase.query_point(x_or_point, y, from_mask):
+			separation = point - position[entity].position
+			if separation.get_length_sqrd() <= collision[entity].radius**2:
+				hits.add(entity)
+		return hits
+
+
+def dispatch_events(collision_system):
+	"""Collision handler that dispatches `on_collide()` events to entities
+	marked for collision by the specified collision system. The `on_collide()`
+	event handler methods are defined by the application on the desired entity
+	classes. These methods should have the following signature::
+
+		def on_collide(self, other_entity, collision_point, collision_normal):
+			'''Handle A collision between this entity and `other_entity`
+
+			- other_entity (Entity): The other entity in collision with 
+			  `self`
+
+			- collision_point (Vec2d): The point on this entity (`self`)
+			  where the collision occurred. Note this may be `None` for 
+			  some collision systems that do not report it.
+
+			- collision_normal (Vec2d): The normal vector at the point of
+			  collision. As with `collision_point`, this may be None for
+			  some collision systems.
+			'''
+
+	Note the arguments to `on_collide()` are always passed positionally, so you
+	can use different argument names than above if desired.
+
+	If a pair of entities are in collision, then the event will be dispatched
+	to both objects in arbitrary order if all of their collision masks align.
+	"""
+	collision = getattr(collision_system.world.components, 
+		collision_system.collision_component)
+	for pair in collision_system.collision_pairs:
+		entity1, entity2 = pair
+		if pair.info is not None:
+			args1, args2 = pair.info
+		else:
+			args1 = entity1, None, None
+			args2 = entity2, None, None
+		try:
+			on_collide = entity1.on_collide
+			masks_align = collision[entity2].from_mask & collision[entity1].into_mask
+		except (AttributeError, KeyError):
+			pass
+		else:
+			if masks_align:
+				on_collide(*args2)
+		try:
+			on_collide = entity2.on_collide
+			masks_align = collision[entity1].from_mask & collision[entity2].into_mask
+		except (AttributeError, KeyError):
+			pass
+		else:
+			if masks_align:
+				on_collide(*args1)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grease/color.py	Tue Jul 12 10:16:48 2011 +0200
@@ -0,0 +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)
+			
+			
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grease/component/__init__.py	Tue Jul 12 10:16:48 2011 +0200
@@ -0,0 +1,149 @@
+#############################################################################
+#
+# 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.
+#
+#############################################################################
+"""Components store all entity data in a given |World|. You can
+think of components as tables with entities as their primary keys. Like
+database tables, components are defined with a "schema" that specifies
+the data fields. Each field in a component has a name and a type.
+
+Component objects themselves have a dict-like interface with entities
+as keys and data records as values. An application will typically 
+interact with components via entity attributes, entity extents or
+by joining them. For more information see:
+
+- :class:`~grease.entity.Entity` class.
+- :class:`~grease.world.EntityExtent` class.
+- :meth:`~grease.world.ComponentParts.join` method of ComponentParts.
+
+See also :ref:`defining custom components in the tutorial <custom-component-example>`.
+"""
+
+__version__ = '$Id$'
+
+__all__ = ('Component', 'ComponentError', 'Position', 'Transform', 'Movement', 
+	'Shape', 'Renderable', 'Collision')
+
+from grease.component.general import Component
+from grease.geometry import Vec2d, Vec2dArray, Rect
+from grease import color
+
+
+class ComponentError(Exception):
+	"""General component error"""
+
+
+class Position(Component):
+	"""Predefined component that stores position and orientation info for 
+	entities.
+
+	Fields:
+
+	- **position** (Vec2d) -- Position vector
+	- **angle** (float) -- Angle, in degrees
+	"""
+
+	def __init__(self):
+		Component.__init__(self, position=Vec2d, angle=float)
+
+
+class Transform(Component):
+	"""Predefined component that stores offset, shear, 
+	rotation and scale info for entity shapes.
+	
+	Fields:
+
+	- **offset** (Vec2d)
+	- **shear** (Vec2d)
+	- **rotation** (float)
+	- **scale** (float, default 1.0)
+	"""
+
+	def __init__(self):
+		Component.__init__(self, offset=Vec2d, shear=Vec2d, rotation=float, scale=float)
+		self.fields['scale'].default = lambda: 1.0
+
+
+class Movement(Component):
+	"""Predefined component that stores velocity, 
+	acceleration and rotation info for entities.
+	
+	Fields:
+
+	- **velocity** (Vec2d) -- Rate of change of entity position
+	- **accel** (Vec2d) -- Rate of change of entity velocity
+	- **rotation** (Vec2d) -- Rate of change of entity angle, in degrees/time
+	"""
+
+	def __init__(self):
+		Component.__init__(self, velocity=Vec2d, accel=Vec2d, rotation=float)
+
+
+class Shape(Component):
+	"""Predefined component that stores shape vertices for entities
+	
+	- **closed** (bool) -- If the shapes is closed implying an edge between
+	  last and first vertices.
+	- **verts** (Vec2dArray) -- Array of vertex points
+	"""
+
+	def __init__(self):
+		Component.__init__(self, closed=int, verts=Vec2dArray)
+		self.fields['closed'].default = lambda: 1
+
+
+class Renderable(Component):
+	"""Predefined component that identifies entities to be 
+	rendered and provides their depth and color.
+	
+	- **depth** (float) -- Drawing depth, can be used to determine z-order
+		  while rendering.
+	- **color** (color.RGBA) -- Color used for entity. The effect of this
+		  field depends on the renderer.
+	"""
+
+	def __init__(self):
+		Component.__init__(self, depth=float, color=color.RGBA)
+		self.fields['color'].default = lambda: color.RGBA(1,1,1,1)
+
+
+class Collision(Component):
+	"""Predefined component that stores collision masks to determine 
+	which entities can collide.
+
+	Fields:
+
+	- **aabb** (Rect) -- The axis-aligned bounding box for the entity.
+		This is used for broad-phase collision detection.
+
+	- **radius** (float) -- The collision radius of the entity, used for narrow-phase
+		collision detection. The exact meaning of this value depends on the collision
+		system in use.
+	
+	- **from_mask** (int) -- A bitmask that determines what entities this object
+		can collide with.
+
+	- **into_mask** (int) -- A bitmask that determines what entities can collide
+		with this object.
+
+	When considering an entity A for collision with entity B, A's ``from_mask`` is
+	bit ANDed with B's ``into_mask``. If the result is nonzero (meaning 1 or more
+	bits is set the same for each) then the collision test is made. Otherwise,
+	the pair cannot collide. 
+
+	The default value for both of these masks is ``0xffffffff``, which means that
+	all entities will collide with each other by default.
+	"""
+	def __init__(self):
+		Component.__init__(self, aabb=Rect, radius=float, from_mask=int, into_mask=int)
+		self.fields['into_mask'].default = lambda: 0xffffffff
+		self.fields['from_mask'].default = lambda: 0xffffffff
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grease/component/base.py	Tue Jul 12 10:16:48 2011 +0200
@@ -0,0 +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()
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grease/component/field.py	Tue Jul 12 10:16:48 2011 +0200
@@ -0,0 +1,301 @@
+#############################################################################
+#
+# 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$'
+
+import operator
+from grease.geometry import Vec2d, Vec2dArray, Rect
+from grease import color
+
+# Allowed field types -> default values
+types = {int:lambda: 0, 
+         float:lambda: 0.0, 
+		 bool:lambda: False,
+		 str:lambda:"", 
+		 object:lambda:None,
+		 Vec2d:lambda: Vec2d(0,0), 
+		 Vec2dArray:lambda: Vec2dArray(),
+		 color.RGBA: lambda: color.RGBA(0.0, 0.0, 0.0, 0.0),
+		 Rect: lambda: Rect(0.0, 0.0, 0.0, 0.0)}
+
+class Schema(dict):
+	"""Field schema definition for custom components"""
+
+	def __init__(self, **fields):
+		for ftype in fields.values():
+			assert ftype in types, fname + " has an illegal field type"
+		self.update(fields)
+
+
+class FieldAccessor(object):
+	"""Facade for manipulating a field for a set of entities"""
+
+	__field = None
+	__entities = None
+	__attrs = None
+	__getter = None
+	__parent_getters = ()
+
+	def __init__(self, field, entities, attrs=()):
+		self.__field = field
+		self.__entities = entities
+		field_getter = operator.attrgetter(field.name)
+		self.__attrs = attrs
+		if attrs:
+			getters = [field_getter] + [operator.attrgetter(attr) for attr in attrs]
+			def get(entity):
+				value = entity
+				for getter in getters:
+					value = getter(value)
+				return value
+			self.__getter = get
+			self.__parent_getters = getters[:-1]
+		else:
+			self.__getter = field_getter
+	
+	def __getattr__(self, name):
+		"""Return a FieldAccessor for the child attribute"""
+		return self.__class__(self.__field, self.__entities, self.__attrs + (name,))
+	
+	def __setattr__(self, name, value):
+		if value is self:
+			return # returned by mutators
+		if hasattr(self.__class__, name):
+			# Set local attr
+			self.__dict__[name] = value
+		elif not name.startswith('_'):
+			getattr(self, name).__set__(value)
+		else:
+			raise AttributeError("Cannot set field attribute: %s" % name)
+	
+	@property
+	def __setter(self):
+		"""Return the proper setter function for setting the field value"""
+		if not self.__attrs:
+			return setattr
+		else:
+			parent_getters = self.__parent_getters
+			def setter(data, name, value):
+				for getter in parent_getters:
+					data = getter(data)
+				setattr(data, name, value)
+			self.__setter = setter
+			return setter
+	
+	def __set__(self, value):
+		"""Set field values en masse"""
+		# Mass set field attr
+		setter = self.__setter
+		component = self.__field.component
+		if self.__attrs:
+			name = self.__attrs[-1]
+		else:
+			name = self.__field.name
+		if isinstance(value, FieldAccessor):
+			# Join set between two entity sets
+			if not self.__attrs:
+				cast = self.__field.cast
+			else: 
+				cast = lambda x: x
+			for entity in self.__entities:
+				try:
+					setter(component[entity], name, cast(value[entity]))
+				except KeyError:
+					pass
+		else:
+			if not self.__attrs:
+				value = self.__field.cast(value)
+			for entity in self.__entities:
+				try:
+					setter(component[entity], name, value)
+				except KeyError:
+					pass
+	
+	def __getitem__(self, entity):
+		"""Return the field value for a single entity (used for joins)"""
+		if entity in self.__entities:
+			return self.__getter(self.__field.component[entity])
+		raise KeyError(entity)
+	
+	def __contains__(self, entity):
+		return entity in self.__entities
+
+	def __repr__(self):
+		return '<%s %s @ %x>' % (
+			self.__class__.__name__, 
+			'.'.join((self.__field.name,) + self.__attrs), id(self))
+	
+	def __nonzero__(self):
+		return bool(self.__entities)
+	
+	def __iter__(self):
+		"""Return an iterator of all field values in the set"""
+		component = self.__field.component
+		getter = self.__getter
+		for entity in self.__entities:
+			try:
+				data = component[entity]
+			except KeyError:
+				continue
+			yield getter(data)
+	
+	## batch comparison operators ##
+	
+	def __match(self, value, op):
+		component = self.__field.component
+		getter = self.__getter
+		matches = set()
+		add = matches.add
+		if isinstance(value, FieldAccessor):
+			# Join match between entity sets
+			for entity in self.__entities:
+				try:
+					data = component[entity]
+					other = value[entity]
+				except KeyError:
+					continue
+				if op(getter(data), other):
+					add(entity)
+		else:
+			for entity in self.__entities:
+				try:
+					data = component[entity]
+				except KeyError:
+					continue
+				if op(getter(data), value):
+					add(entity)
+		return matches
+	
+	def __eq__(self, value):
+		"""Return an entity set of all entities with a matching field value"""
+		return self.__match(value, operator.eq)
+	
+	def __ne__(self, value):
+		"""Return an entity set of all entities not matching field value"""
+		return self.__match(value, operator.ne)
+	
+	def __gt__(self, value):
+		"""Return an entity set of all entities with a greater field value"""
+		return self.__match(value, operator.gt)
+	
+	def __ge__(self, value):
+		"""Return an entity set of all entities with a greater or equal field value"""
+		return self.__match(value, operator.ge)
+	
+	def __lt__(self, value):
+		"""Return an entity set of all entities with a lesser field value"""
+		return self.__match(value, operator.lt)
+	
+	def __le__(self, value):
+		"""Return an entity set of all entities with a lesser or equal field value"""
+		return self.__match(value, operator.le)
+	
+	def _contains(self, values):
+		"""Return an entity set of all entities with a field value contained in values"""
+		return self.__match(values, operator.contains)
+	
+	## Batch in-place mutator methods
+
+	def __mutate(self, value, op):
+		component = self.__field.component
+		if self.__attrs:
+			name = self.__attrs[-1]
+		else:
+			name = self.__field.name
+		getter = self.__getter
+		setter = self.__setter
+		if isinstance(value, FieldAccessor):
+			# Join between entity sets
+			for entity in self.__entities:
+				try:
+					data = component[entity]
+					other = value[entity]
+				except KeyError:
+					continue
+				setter(data, name, op(getter(data), other))
+		else:
+			for entity in self.__entities:
+				try:
+					data = component[entity]
+				except KeyError:
+					continue
+				setter(data, name, op(getter(data), value))
+		return self
+	
+	def __iadd__(self, value):
+		return self.__mutate(value, operator.iadd)
+	
+	def __isub__(self, value):
+		return self.__mutate(value, operator.isub)
+	
+	def __imul__(self, value):
+		return self.__mutate(value, operator.imul)
+	
+	def __idiv__(self, value):
+		return self.__mutate(value, operator.idiv)
+	
+	def __itruediv__(self, value):
+		return self.__mutate(value, operator.itruediv)
+	
+	def __ifloordiv__(self, value):
+		return self.__mutate(value, operator.ifloordiv)
+
+	def __imod__(self, value):
+		return self.__mutate(value, operator.imod)
+
+	def __ipow__(self, value):
+		return self.__mutate(value, operator.ipow)
+
+	def __ilshift__(self, value):
+		return self.__mutate(value, operator.ilshift)
+
+	def __irshift__(self, value):
+		return self.__mutate(value, operator.irshift)
+
+	def __iand__(self, value):
+		return self.__mutate(value, operator.iand)
+
+	def __ior__(self, value):
+		return self.__mutate(value, operator.ior)
+
+	def __ixor__(self, value):
+		return self.__mutate(value, operator.ixor)
+
+
+class Field(object):
+	"""Component field metadata and accessor interface"""
+
+	def __init__(self, component, name, type, accessor_factory=FieldAccessor):
+		self.component = component
+		self.name = name
+		self.type = type
+		self.default = types.get(type)
+		self.accessor_factory = accessor_factory
+	
+	def cast(self, value):
+		"""Cast value to the appropriate type for thi field"""
+		if self.type is not object:
+			return self.type(value)
+		else:
+			return value
+			
+	def accessor(self, entities=None):
+		"""Return the field accessor for the entities in the component,
+		or all entities in the set specified that are also in the component
+		"""
+		if entities is None or entities is self.component.entities:
+			entities = self.component.entities
+		else:
+			entities = entities & self.component.entities
+		return self.accessor_factory(self, entities)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grease/component/general.py	Tue Jul 12 10:16:48 2011 +0200
@@ -0,0 +1,143 @@
+#############################################################################
+#
+# 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$'
+
+from grease.component import base
+from grease.component import field
+from grease.entity import ComponentEntitySet
+
+
+class Component(dict):
+	"""General component with a configurable schema
+
+	The field schema is defined via keyword args where the 
+	arg name is the field name and the value is the type object.
+
+	The following types are supported for fields:
+
+	- :class:`int`
+	- :class:`float`
+	- :class:`bool`
+	- :class:`str`
+	- :class:`object`
+	- |Vec2d|
+	- |Vec2dArray|
+	- |RGBA|
+	- |Rect|
+	"""
+
+	deleted_entities = ()
+	"""List of entities deleted from the component since the last time step"""
+
+	new_entities = ()
+	"""List of entities added to the component since the last time step"""
+
+	def __init__(self, **fields):
+		self.fields = {}
+		for fname, ftype in fields.items():
+			assert ftype in field.types, fname + " has an illegal field type"
+			self.fields[fname] = field.Field(self, fname, ftype)
+		self.entities = ComponentEntitySet(self)
+		self._added = []
+		self._deleted = []
+	
+	def set_world(self, world):
+		self.world = world
+	
+	def step(self, dt):
+		"""Update the component for the next timestep"""
+		delitem = super(Component, self).__delitem__
+		for entity in self._deleted:
+			delitem(entity)
+		self.new_entities = self._added
+		self.deleted_entities = self._deleted
+		self._added = []
+		self._deleted = []
+	
+	def set(self, entity, data=None, **data_kw):
+		"""Set the component data for an entity, adding it to the
+		component if it is not already a member.
+
+		If data is specified, its data for the new entity's fields are
+		copied from its attributes, making it easy to copy another
+		entity's data. Keyword arguments are also matched to fields.
+		If both a data attribute and keyword argument are supplied for
+		a single field, the keyword arg is used.
+		"""
+		if data is not None:
+			for fname, field in self.fields.items():
+				if fname not in data_kw and hasattr(data, fname):
+					data_kw[fname] = getattr(data, fname)
+		data = self[entity] = Data(self.fields, entity, **data_kw)
+		return data
+	
+	def __setitem__(self, entity, data):
+		assert entity.world is self.world, "Entity not in component's world"
+		if entity not in self.entities:
+			self._added.append(entity)
+			self.entities.add(entity)
+		super(Component, self).__setitem__(entity, data)
+	
+	def remove(self, entity):
+		if entity in self.entities:
+			self._deleted.append(entity)
+			self.entities.remove(entity)
+			return True
+		return False
+	
+	__delitem__ = remove
+
+	def __repr__(self):
+		return '<%s %x of %r>' % (
+			self.__class__.__name__, id(self), getattr(self, 'world', None))
+
+
+class Singleton(Component):
+	"""Component that may contain only a single entity"""
+
+	def add(self, entity_id, data=None, **data_kw):
+		if entity_id not in self._data:
+			self.entity_id_set.clear()
+			self._data.clear()
+		Component.add(self, entity_id, data, **data_kw)
+	
+	@property
+	def entity(self):
+		"""Return the entity in the component, or None if empty"""
+		if self._data:
+			return self.manager[self._data.keys()[0]]
+	
+
+class Data(object):
+
+	def __init__(self, fields, entity, **data):
+		self.__dict__['_Data__fields'] = fields
+		self.__dict__['entity'] = entity
+		for field in fields.values():
+			if field.name in data:
+				setattr(self, field.name, data[field.name])
+			else:
+				setattr(self, field.name, field.default())
+	
+	def __setattr__(self, name, value):
+		if name in self.__fields:
+			self.__dict__[name] = self.__fields[name].cast(value)
+		else:
+			raise AttributeError("Invalid data field: " + name)
+	
+	def __repr__(self):
+		return '<%s(%r)>' % (self.__class__.__name__, self.__dict__)
+
+			
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grease/component/schema.py	Tue Jul 12 10:16:48 2011 +0200
@@ -0,0 +1,1 @@
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grease/controller/__init__.py	Tue Jul 12 10:16:48 2011 +0200
@@ -0,0 +1,4 @@
+
+__all__ = ('EulerMovement',)
+
+from grease.controller.integrator import EulerMovement
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grease/controller/integrator.py	Tue Jul 12 10:16:48 2011 +0200
@@ -0,0 +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
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grease/entity.py	Tue Jul 12 10:16:48 2011 +0200
@@ -0,0 +1,212 @@
+#############################################################################
+#
+# 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.
+#
+#############################################################################
+"""Grease entities are useful as actionable, interactive
+game elements that are often visible to the player.
+
+You might use entities to represent:
+
+- Characters
+- Bullets
+- Particles
+- Pick-ups
+- Space Ships
+- Weapons
+- Trees
+- Planets
+- Explosions
+
+See :ref:`an example entity class in the tutorial <tut-entity-example>`.
+"""
+
+__version__ = '$Id$'
+
+__all__ = ('Entity', 'EntityComponentAccessor', 'ComponentEntitySet')
+
+
+class EntityMeta(type):
+	"""The entity metaclass enforces fixed slots of `entity_id` and `world`
+	for all subclasses. This prevents accidental use of other entity instance 
+	attributes, which may not be saved. 
+	
+	Class attributes are not affected by this restriction, but subclasses
+	should be careful not to cause name collisions with world components,
+	which are exposed as entity attributes. Using a naming convention for
+	class attributes, such as UPPER_CASE_WITH_UNDERSCORES is recommended to
+	avoid name clashes.
+
+	Note as a result of this, entity subclasses are not allowed to define
+	`__slots__`, and doing so will cause a `TypeError` to be raised.
+	"""
+
+	def __new__(cls, name, bases, clsdict):
+		if '__slots__' in clsdict:
+			raise TypeError('__slots__ may not be defined in Entity subclasses')
+		clsdict['__slots__'] = ('world', 'entity_id')
+		return type.__new__(cls, name, bases, clsdict)
+
+
+class Entity(object):
+	"""Base class for grease entities.
+	
+	Entity objects themselves are merely identifiers within a :class:`grease.world.World`.
+	They also provide a facade for convenient entity-wise access of component
+	data. However, they do not contain any data themselves other than an
+	entity id.
+
+	Entities must be instantiated in the context of a world. To instantiate an
+	entity, you must pass the world as the first argument to the constructor.
+	Subclasses that implement the :meth:`__init__()` method, must accept the world
+	as their first argument (after ``self``). Other constructor arguments can be
+	specified arbitarily by the subclass.
+	"""
+	__metaclass__ = EntityMeta
+
+	def __new__(cls, world, *args, **kw):
+		"""Create a new entity and add it to the world"""
+		entity = object.__new__(cls)
+		entity.world = world
+		entity.entity_id = world.new_entity_id()
+		world.entities.add(entity)
+		return entity
+	
+	def __getattr__(self, name):
+		"""Return an :class:`EntityComponentAccessor` for this entity
+		for the component named.
+
+		Example::
+
+			my_entity.movement
+		"""
+		component = getattr(self.world.components, name)
+		return EntityComponentAccessor(component, self)
+	
+	def __setattr__(self, name, value):
+		"""Set the entity data in the named component for this entity.
+		This sets the values of the component fields to the values of
+		the matching attributes of the value provided. This value must
+		have attributes for each of the component fields.
+
+		This allows you to easily copy component data from one entity
+		to another.
+
+		Example::
+
+			my_entity.position = other_entity.position
+		"""
+		if name in self.__class__.__slots__:
+			super(Entity, self).__setattr__(name, value)
+		else:
+			component = getattr(self.world.components, name)
+			component.set(self, value)
+	
+	def __delattr__(self, name):
+		"""Remove this entity and its data from the component.
+		
+		Example::
+		
+			del my_entity.renderable
+		"""
+		component = getattr(self.world.components, name)
+		del component[self]
+	
+	def __hash__(self):
+		return self.entity_id
+	
+	def __eq__(self, other):
+		return self.world is other.world and self.entity_id == other.entity_id
+
+	def __repr__(self):
+		return "<%s id: %s of %s %x>" % (
+			self.__class__.__name__, self.entity_id,
+			self.world.__class__.__name__, id(self.world))
+
+	def delete(self):
+		"""Delete the entity from its world. This removes all of its
+		component data. If then entity has already been deleted, 
+		this call does nothing.
+		"""
+		self.world.entities.discard(self)
+
+	@property
+	def exists(self):
+		"""True if the entity still exists in the world"""
+		return self in self.world.entities
+
+
+class EntityComponentAccessor(object):
+	"""A facade for accessing specific component data for a single entity.
+	The implementation is lazy and does not actually access the component
+	data until needed. If an attribute is set for a component that the 
+	entity is not yet a member of, it is automatically added to the
+	component first.
+
+	:param component: The :class:`grease.Component` being accessed
+	:param entity: The :class:`Entity` being accessed
+	"""
+	
+	# beware, name mangling ahead. We want to avoid clashing with any
+	# user-configured component field names
+	__data = None
+
+	def __init__(self, component, entity):
+		clsname = self.__class__.__name__
+		self.__dict__['_%s__component' % clsname] = component
+		self.__dict__['_%s__entity' % clsname] = entity
+	
+	def __nonzero__(self):
+		"""The accessor is True if the entity is in the component,
+		False if not, for convenient membership tests
+		"""
+		return self.__entity in self.__component
+	
+	def __getattr__(self, name):
+		"""Return the data for the specified field of the entity's component"""
+		if self.__data is None:
+			try:
+				data = self.__component[self.__entity]
+			except KeyError:
+				raise AttributeError(name)
+			clsname = self.__class__.__name__
+			self.__dict__['_%s__data' % clsname] = data
+		return getattr(self.__data, name)
+	
+	def __setattr__(self, name, value):
+		"""Set the data for the specified field of the entity's component"""
+		if self.__data is None:
+			clsname = self.__class__.__name__
+			if self.__entity in self.__component:
+				self.__dict__['_%s__data' % clsname] = self.__component[self.__entity]
+			else:
+				self.__dict__['_%s__data' % clsname] = self.__component.set(self.__entity)
+		setattr(self.__data, name, value)
+
+
+class ComponentEntitySet(set):
+	"""Set of entities in a component, can be queried by component fields"""
+
+	_component = None
+
+	def __init__(self, component, entities=()):
+		self.__dict__['_component'] = component
+		super(ComponentEntitySet, self).__init__(entities)
+	
+	def __getattr__(self, name):
+		if self._component is not None and name in self._component.fields:
+			return self._component.fields[name].accessor(self)
+		raise AttributeError(name)
+	
+	def __setattr__(self, name, value):
+		if self._component is not None and name in self._component.fields:
+			self._component.fields[name].accessor(self).__set__(value)
+		raise AttributeError(name)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grease/geometry.py	Tue Jul 12 10:16:48 2011 +0200
@@ -0,0 +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()
+ 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grease/impl/__init__.py	Tue Jul 12 10:16:48 2011 +0200
@@ -0,0 +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
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grease/impl/controls.py	Tue Jul 12 10:16:48 2011 +0200
@@ -0,0 +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 grease
+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()
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grease/impl/mode.py	Tue Jul 12 10:16:48 2011 +0200
@@ -0,0 +1,203 @@
+#############################################################################
+#
+# 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
+import pyglet
+from grease.mode import *
+
+class PygletManager(BaseManager):
+	"""Mode manager abstract base class using pyglet.
+	
+	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.
+	"""
+
+	event_dispatcher = None
+	""":class:`pyglet.event.EventDispatcher` object that the
+	active mode receive events from.
+	"""
+	
+	def activate_mode(self, mode):
+		"""Perform actions to activate a node
+		
+		:param mode: The :class: 'Mode' object to activate
+		"""
+		BaseManager.activate_mode(self, mode)
+		self.event_dispatcher.push_handlers(mode)
+		
+	def deactivate_mode(self, mode):
+		"""Perform actions to deactivate a node
+		
+		:param mode: The :class: 'Mode' object to deactivate
+		"""
+		BaseManager.deactivate_mode(self, mode)
+		self.event_dispatcher.remove_handlers(mode)
+
+class Manager(PygletManager):
+	"""A basic mode manager that wraps a single
+	:class:`pyglet.event.EventDispatcher` object for use by its modes.
+	"""
+
+	def __init__(self, event_dispatcher):
+		self.modes = []
+		self.event_dispatcher = event_dispatcher
+
+
+class ManagerWindow(PygletManager, pyglet.window.Window):
+	"""An integrated mode manager and pyglet window for convenience.
+	The window is the event dispatcher used by modes pushed to
+	this manager.
+
+	Constructor arguments are identical to :class:`pyglet.window.Window`
+	"""
+	
+	def __init__(self, *args, **kw):
+		super(ManagerWindow, self).__init__(*args, **kw)
+		self.modes = []
+		self.event_dispatcher = self
+
+	def on_key_press(self, symbol, modifiers):
+		"""Default :meth:`on_key_press handler`, pops the current mode on ``ESC``"""
+		if symbol == pyglet.window.key.ESCAPE:
+			self.pop_mode()
+
+	def on_last_mode_pop(self, mode):
+		"""Hook executed when the last mode is popped from the manager.
+		When the last mode is popped from a window, an :meth:`on_close` event
+		is dispatched.
+
+		:param mode: The :class:`Mode` object just popped from the manager
+		"""
+		self.dispatch_event('on_close')
+
+
+class Mode(BaseMode):
+	"""Application mode abstract base class using pyglet
+
+	Subclasses must implement the :meth:`step` method
+	
+	:param step_rate: The rate of :meth:`step()` calls per second. 
+
+	:param master_clock: The :class:`pyglet.clock.Clock` interface used
+		as the master clock that ticks the world's clock. This 
+		defaults to the main pyglet clock.
+	"""
+	clock = None
+	"""The :class:`pyglet.clock.Clock` instance used as this mode's clock.
+	You should use this clock to schedule tasks for this mode, so they
+	properly respect when the mode is active or inactive
+
+	Example::
+
+		my_mode.clock.schedule_once(my_cool_function, 4)
+	"""
+
+	def __init__(self, step_rate=60, master_clock=pyglet.clock, 
+		         clock_factory=pyglet.clock.Clock):
+		BaseMode.__init__(self)
+		self.step_rate = step_rate
+		self.time = 0.0
+		self.master_clock = master_clock
+		self.clock = clock_factory(time_function=lambda: self.time)
+		self.clock.schedule_interval(self.step, 1.0 / step_rate)
+	
+	def on_activate(self):
+		"""Being called when the Mode is activated"""
+		self.master_clock.schedule(self.tick)
+	
+	def on_deactivate(self):
+		"""Being called when the Mode is deactivated"""
+		self.master_clock.unschedule(self.tick)
+		
+	def tick(self, dt):
+		"""Tick the mode's clock.
+
+		:param dt: The time delta since the last tick
+		:type dt: float
+		"""
+		self.time += dt
+		self.clock.tick(poll=False)
+	
+	@abc.abstractmethod
+	def step(self, dt):
+		"""Execute a timestep for this mode. Must be defined by subclasses.
+		
+		:param dt: The time delta since the last time step
+		:type dt: float
+		"""
+
+class Multi(BaseMulti, Mode):
+	"""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.
+	"""
+	
+	def __init__(self, submodes):
+		BaseMulti.__init__(self, submodes)
+		self.time = 0.0
+
+	
+	def _set_active_submode(self, submode):
+		BaseMulti._set_active_submode(self, submode)
+		self.master_clock = submode.master_clock
+		self.clock = submode.clock
+
+	def clear_subnode(self):
+		"""Clear any subnmode data"""
+		BaseMulti.clear_subnode(self)
+		self.master_clock = None
+		self.clock = None
+
+	def tick(self, dt):
+		"""Tick the active submode's clock.
+
+		:param dt: The time delta since the last tick
+		:type dt: float
+		"""
+		self.time += dt
+		if self.active_submode is not None:
+			self.active_submode.clock.tick(poll=False)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grease/impl/world.py	Tue Jul 12 10:16:48 2011 +0200
@@ -0,0 +1,129 @@
+#############################################################################
+#
+# 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.
+#
+#############################################################################
+"""Worlds are environments described by a configuration of components, systems and 
+renderers. These parts describe the data, behavioral and presentation aspects
+of the world respectively.
+
+The world environment is the context within which entities exist. A typical
+application consists of one or more worlds containing entities that evolve
+over time and react to internal and external interaction.
+
+See :ref:`an example of world configuration in the tutorial <tut-world-example>`.
+"""
+
+__version__ = '$Id$'
+
+import itertools
+import pyglet
+from pyglet import gl
+from grease.world import *
+from grease.impl import Mode
+
+class World(Mode, BaseWorld):
+	"""A coordinated collection of components, systems and entities
+	
+	A world is also a mode that may be pushed onto a 
+	:class:`grease.mode.Manager`
+
+	:param step_rate: The rate of :meth:`step()` calls per second. 
+
+	:param master_clock: The :class:`pyglet.clock.Clock` interface used
+		as the master clock that ticks the world's clock. This 
+		defaults to the main pyglet clock.
+	"""
+
+	clock = None
+	""":class:`pyglet.clock` interface for use by constituents
+	of the world for scheduling
+	"""
+
+	time = None
+	"""Current clock time of the world, starts at 0 when the world
+	is instantiated
+	"""
+
+	running = True
+	"""Flag to indicate that the world clock is running, advancing time
+	and stepping the world. Set running to False to pause the world.
+	"""
+
+	def __init__(self, step_rate=60, master_clock=pyglet.clock,
+				 clock_factory=pyglet.clock.Clock):
+		Mode.__init__(self, step_rate, master_clock, clock_factory)
+		BaseWorld.__init__(self)
+	
+	def activate(self, manager):
+		"""Activate the world/mode for the given manager, if the world is already active, 
+		do nothing. This method is typically not used directly, it is called
+		automatically by the mode manager when the world becomes active.
+
+		The systems of the world are pushed onto `manager.event_dispatcher`
+		so they can receive system events.
+
+		:param manager: :class:`mode.BaseManager` instance
+		"""
+		if not self.active:
+			for system in self.systems:
+				manager.event_dispatcher.push_handlers(system)
+		super(World, self).activate(manager)
+	
+	def deactivate(self, manager):
+		"""Deactivate the world/mode, if the world is not active, do nothing.
+		This method is typically not used directly, it is called
+		automatically by the mode manager when the world becomes active.
+
+		Removes the system handlers from the `manager.event_dispatcher`
+
+		:param manager: :class:`mode.BaseManager` instance
+		"""
+		for system in self.systems:
+			manager.event_dispatcher.remove_handlers(system)
+		super(World, self).deactivate(manager)
+
+	def tick(self, dt):
+		"""Tick the mode's clock, but only if the world is currently running
+		
+		:param dt: The time delta since the last tick
+		:type dt: float
+		"""
+		if self.running:
+			super(World, self).tick(dt)
+	
+	def step(self, dt):
+		"""Execute a time step for the world. Updates the world `time`
+		and invokes the world's systems.
+		
+		Note that the specified time delta will be pinned to 10x the
+		configured step rate. For example if the step rate is 60,
+		then dt will be pinned at a maximum of 0.1666. This avoids 
+		pathological behavior when the time between steps goes
+		much longer than expected.
+
+		:param dt: The time delta since the last time step
+		:type dt: float
+		"""
+		dt = min(dt, 10.0 / self.step_rate)
+		for component in self.components:
+			if hasattr(component, "step"):
+				component.step(dt)
+		for system in self.systems:
+			if hasattr(system, "step"):
+				system.step(dt)
+
+	def on_draw(self, gl=pyglet.gl):
+		"""Clear the current OpenGL context, reset the model/view matrix and
+		invoke the `draw()` methods of the renderers in order
+		"""
+		gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
+		gl.glLoadIdentity()
+		BaseWorld.draw_renderers(self)
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grease/mode.py	Tue Jul 12 10:16:48 2011 +0200
@@ -0,0 +1,392 @@
+#############################################################################
+#
+# 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)
+		self.event_dispatcher.remove_handlers(mode)
+		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)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grease/renderer/__init__.py	Tue Jul 12 10:16:48 2011 +0200
@@ -0,0 +1,25 @@
+#############################################################################
+#
+# 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.
+#
+#############################################################################
+"""Renderers define world presentation. This module contains the
+built-in renderer classes.
+
+See also:
+
+- :class:`~grease.Renderer` abstract base class.
+- :ref:`Example renderer class in the tutorial <tut-renderer-example>`
+"""
+
+__all__ = ('Vector', 'Camera')
+
+from grease.renderer.vector import Vector
+from grease.renderer.camera import Camera
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grease/renderer/camera.py	Tue Jul 12 10:16:48 2011 +0200
@@ -0,0 +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)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grease/renderer/vector.py	Tue Jul 12 10:16:48 2011 +0200
@@ -0,0 +1,166 @@
+#############################################################################
+#
+# 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$'
+
+__all__ = ('Vector',)
+
+from grease.geometry import Vec2d
+import ctypes
+from math import sin, cos, radians
+import pyglet
+
+
+class Vector(object):
+	"""Renders shapes in a classic vector graphics style
+	
+	:param scale: Scaling factor applied to shape vertices when rendered.
+	
+	:param line_width: The line width provided to ``glLineWidth`` before rendering.
+		If not specified or None, ``glLineWidth`` is not called, and the line
+		width used is determined by the OpenGL state at the time of rendering.
+
+	:param anti_alias: If ``True``, OpenGL blending and line smoothing is enabled.
+		This allows for fractional line widths as well. If ``False``, the blending
+		and line smoothing modes are unchanged.
+
+	:param corner_fill: If true (the default), the shape corners will be filled
+		with round points when the ``line_width`` exceeds 2.0. This improves
+		the visual quality of the rendering at larger line widths at some
+		cost to performance. Has no effect if ``line_width`` is not specified.
+
+	:param position_component: Name of :class:`grease.component.Position` 
+		component to use. Shapes rendered are offset by the entity positions.
+
+	:param renderable_component: Name of :class:`grease.component.Renderable` 
+		component to use. This component specifies the entities to be 
+		rendered and their base color.
+
+	:param shape_component: Name of :class:`grease.component.Shape` 
+		component to use. Source of the shape vertices for each entity.
+
+	The entities rendered are taken from the intersection of he position,
+	renderable and shape components each time :meth:`draw` is called.
+	"""
+
+	CORNER_FILL_SCALE = 0.6
+	CORNER_FILL_THRESHOLD = 2.0
+
+	def __init__(self, scale=1.0, line_width=None, anti_alias=True, corner_fill=True,
+		position_component='position', 
+		renderable_component='renderable', 
+		shape_component='shape'):
+		self.scale = float(scale)
+		self.corner_fill = corner_fill
+		self.line_width = line_width
+		self.anti_alias = anti_alias
+		self._max_line_width = None
+		self.position_component = position_component
+		self.renderable_component = renderable_component
+		self.shape_component = shape_component
+	
+	def set_world(self, world):
+		self.world = world
+
+	def _generate_verts(self):
+		"""Generate vertex and index arrays for rendering"""
+		vert_count = sum(len(shape.verts) + 1
+			for shape, ignored, ignored in self.world.components.join(
+				self.shape_component, self.position_component, self.renderable_component))
+		v_array = (CVertColor * vert_count)()
+		if vert_count > 65536:
+			i_array = (ctypes.c_uint * 2 * vert_count)()
+			i_size = pyglet.gl.GL_UNSIGNED_INT
+		else:
+			i_array = (ctypes.c_ushort * (2 * vert_count))()
+			i_size = pyglet.gl.GL_UNSIGNED_SHORT
+		v_index = 0
+		i_index = 0
+		scale = self.scale
+		rot_vec = Vec2d(0, 0)
+		for shape, position, renderable in self.world.components.join(
+			self.shape_component, self.position_component, self.renderable_component):
+			shape_start = v_index
+			angle = radians(-position.angle)
+			rot_vec.x = cos(angle)
+			rot_vec.y = sin(angle)
+			r = int(renderable.color.r * 255)
+			g = int(renderable.color.g * 255)
+			b = int(renderable.color.b * 255)
+			a = int(renderable.color.a * 255)
+			for vert in shape.verts:
+				vert = vert.cpvrotate(rot_vec) * scale + position.position
+				v_array[v_index].vert.x = vert.x
+				v_array[v_index].vert.y = vert.y
+				v_array[v_index].color.r = r
+				v_array[v_index].color.g = g
+				v_array[v_index].color.b = b
+				v_array[v_index].color.a = a
+				if v_index > shape_start:
+					i_array[i_index] = v_index - 1
+					i_index += 1
+					i_array[i_index] = v_index
+					i_index += 1
+				v_index += 1
+			if shape.closed and v_index - shape_start > 2:
+				i_array[i_index] = v_index - 1
+				i_index += 1
+				i_array[i_index] = shape_start
+				i_index += 1
+		return v_array, i_size, i_array, i_index
+
+	def draw(self, gl=pyglet.gl):
+		vertices, index_size, indices, index_count = self._generate_verts()
+		if index_count:
+			if self.anti_alias:
+				gl.glEnable(gl.GL_LINE_SMOOTH)
+				gl.glHint(gl.GL_LINE_SMOOTH_HINT, gl.GL_NICEST)
+				gl.glEnable(gl.GL_BLEND)
+				gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA)
+			gl.glPushClientAttrib(gl.GL_CLIENT_VERTEX_ARRAY_BIT)
+			gl.glEnableClientState(gl.GL_VERTEX_ARRAY)
+			gl.glEnableClientState(gl.GL_COLOR_ARRAY)
+			gl.glVertexPointer(
+				2, gl.GL_FLOAT, ctypes.sizeof(CVertColor), ctypes.pointer(vertices))
+			gl.glColorPointer(
+				4, gl.GL_UNSIGNED_BYTE, ctypes.sizeof(CVertColor), 
+				ctypes.pointer(vertices[0].color))
+			if self.line_width is not None:
+				gl.glLineWidth(self.line_width)
+				if self._max_line_width is None:
+					range_out = (ctypes.c_float * 2)()
+					gl.glGetFloatv(gl.GL_ALIASED_LINE_WIDTH_RANGE, range_out)
+					self._max_line_width = float(range_out[1]) * self.CORNER_FILL_SCALE
+				if self.corner_fill and self.line_width > self.CORNER_FILL_THRESHOLD:
+					gl.glEnable(gl.GL_POINT_SMOOTH)
+					gl.glPointSize(
+						min(self.line_width * self.CORNER_FILL_SCALE, self._max_line_width))
+					gl.glDrawArrays(gl.GL_POINTS, 0, index_count)
+			gl.glDrawElements(gl.GL_LINES, index_count, index_size, ctypes.pointer(indices))
+			gl.glPopClientAttrib()
+
+
+class CVert(ctypes.Structure):
+	_fields_ = [("x", ctypes.c_float), ("y", ctypes.c_float)]
+
+class CColor(ctypes.Structure):
+	_fields_ = [
+		("r", ctypes.c_ubyte), 
+		("g", ctypes.c_ubyte), 
+		("b", ctypes.c_ubyte), 
+		("a", ctypes.c_ubyte),
+	]
+
+class CVertColor(ctypes.Structure):
+	_fields_ = [("vert", CVert), ("color", CColor)]
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/grease/world.py	Tue Jul 12 10:16:48 2011 +0200
@@ -0,0 +1,302 @@
+#############################################################################
+#
+# 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.
+#
+#############################################################################
+"""Worlds are environments described by a configuration of components, systems and 
+renderers. These parts describe the data, behavioral and presentation aspects
+of the world respectively.
+
+The world environment is the context within which entities exist. A typical
+application consists of one or more worlds containing entities that evolve
+over time and react to internal and external interaction.
+
+See :ref:`an example of world configuration in the tutorial <tut-world-example>`.
+"""
+
+__version__ = '$Id$'
+
+import itertools
+from grease import mode
+from grease.component import ComponentError
+from grease.entity import Entity, ComponentEntitySet
+
+
+class BaseWorld(object):
+	"""A coordinated collection of components, systems and entities
+	
+	A world is also a mode that may be pushed onto a 
+	:class:`grease.mode.Manager`
+	"""
+
+	components = None
+	""":class:`ComponentParts` object containing all world components.
+	:class:`grease.component.Component` objects define and contain all entity data
+	"""
+
+	systems = None
+	""":class:`Parts` object containing all world systems. 
+	:class:`grease.System` objects define world and entity behavior
+	"""
+
+	renderers = None
+	""":class:`Parts` object containing all world renderers. 
+	:class:`grease.Renderer` objects define world presentation
+	"""
+
+	entities = None
+	"""Set of all entities that exist in the world"""
+
+	def __init__(self):
+		self.components = ComponentParts(self)
+		self.systems = Parts(self)
+		self.renderers = Parts(self)
+		self.new_entity_id = itertools.count().next
+		self.new_entity_id() # skip id 0
+		self.entities = WorldEntitySet(self)
+		self._full_extent = EntityExtent(self, self.entities)
+		self._extents = {}
+		self.configure()
+
+	def configure(self):
+		"""Hook to configure the world after construction. This method
+		is called immediately after the world is initialized. Override
+		in a subclass to configure the world's components, systems,
+		and renderers.
+
+		The default implementation does nothing.
+		"""
+	
+	def __getitem__(self, entity_class):
+		"""Return an :class:`EntityExtent` for the given entity class. This extent
+		can be used to access the set of entities of that class in the world
+		or to query these entities via their components. 
+
+		Examples::
+
+			world[MyEntity]
+			world[...]
+
+		:param entity_class: The entity class for the extent.
+
+			May also be a tuple of entity classes, in which case
+			the extent returned contains union of all entities of the classes
+			in the world.
+
+			May also be the special value ellipsis (``...``), which
+			returns an extent containing all entities in the world.  This allows
+			you to conveniently query all entities using ``world[...]``.
+		"""
+		if isinstance(entity_class, tuple):
+			entities = set()
+			for cls in entity_class:
+				if cls in self._extents:
+					entities |= self._extents[cls].entities
+			return EntityExtent(self, entities)
+		elif entity_class is Ellipsis:
+			return self._full_extent
+		try:
+			return self._extents[entity_class]
+		except KeyError:
+			extent = self._extents[entity_class] = EntityExtent(self, set())
+			return extent
+	
+	def draw_renderers(self):
+		"""Draw all renderers"""
+		for renderer in self.renderers:
+			renderer.draw()
+
+class WorldEntitySet(set):
+	"""Entity set for a :class:`World`"""
+
+	def __init__(self, world):
+		self.world = world
+	
+	def add(self, entity):
+		"""Add the entity to the set and all necessary class sets
+		Return the unique entity id for the entity, creating one
+		as needed.
+		"""
+		super(WorldEntitySet, self).add(entity)
+		for cls in entity.__class__.__mro__:
+			if issubclass(cls, Entity):
+				self.world[cls].entities.add(entity)
+
+	def remove(self, entity):
+		"""Remove the entity from the set and, world components,
+		and all necessary class sets
+		"""
+		super(WorldEntitySet, self).remove(entity)
+		for component in self.world.components:
+			try:
+				del component[entity]
+			except KeyError:
+				pass
+		for cls in entity.__class__.__mro__:
+			if issubclass(cls, Entity):
+				self.world[cls].entities.discard(entity)
+	
+	def discard(self, entity):
+		"""Remove the entity from the set if it exists, if not,
+		do nothing
+		"""
+		try:
+			self.remove(entity)
+		except KeyError:
+			pass
+
+
+class EntityExtent(object):
+	"""Encapsulates a set of entities queriable by component. Extents
+	are accessed by using an entity class as a key on the :class:`World`::
+
+		extent = world[MyEntity]
+	"""
+
+	entities = None
+	"""The full set of entities in the extent""" 
+
+	def __init__(self, world, entities):
+		self.__world = world
+		self.entities = entities
+
+	def __getattr__(self, name):
+		"""Return a queriable :class:`ComponentEntitySet` for the named component 
+
+		Example::
+
+			world[MyEntity].movement.velocity > (0, 0)
+
+		Returns a set of entities where the value of the :attr:`velocity` field
+		of the :attr:`movement` component is greater than ``(0, 0)``.
+		"""
+		component = getattr(self.__world.components, name)
+		return ComponentEntitySet(component, self.entities & component.entities)
+
+
+class Parts(object):
+	"""Maps world parts to attributes. The parts are kept in the
+	order they are set. Parts may also be inserted out of order.
+	
+	Used for:
+	
+	- :attr:`World.systems`
+	- :attr:`World.renderers`
+	"""
+
+	_world = None
+	_parts = None
+	_reserved_names = ('entities', 'entity_id', 'world')
+
+	def __init__(self, world):
+		self._world = world
+		self._parts = []
+	
+	def _validate_name(self, name):
+		if (name in self._reserved_names or name.startswith('_') 
+			or hasattr(self.__class__, name)):
+			raise ComponentError('illegal part name: %s' % name)
+		return name
+
+	def __setattr__(self, name, part):
+		if not hasattr(self.__class__, name):
+			self._validate_name(name)
+			if not hasattr(self, name):
+				self._parts.append(part)
+			else:
+				old_part = getattr(self, name)
+				self._parts[self._parts.index(old_part)] = part
+			super(Parts, self).__setattr__(name, part)
+			if hasattr(part, 'set_world'):
+				part.set_world(self._world)
+		elif name.startswith("_"):
+			super(Parts, self).__setattr__(name, part)
+		else:
+			raise AttributeError("%s attribute is read only" % name)
+	
+	def __delattr__(self, name):
+		self._validate_name(name)
+		part = getattr(self, name)
+		self._parts.remove(part)
+		super(Parts, self).__delattr__(name)
+
+	def insert(self, name, part, before=None, index=None):
+		"""Add a part with a particular name at a particular index.
+		If a part by that name already exists, it is replaced.
+			
+		:arg name: The name of the part.
+		:type name: str
+
+		:arg part: The component, system, or renderer part to insert
+	
+		:arg before: A part object or name. If specified, the part is
+			inserted before the specified part in order.
+
+		:arg index: If specified, the part is inserted in the position
+			specified. You cannot specify both before and index.
+		:type index: int
+		"""
+		assert before is not None or index is not None, (
+			"Must specify a value for 'before' or 'index'")
+		assert before is None or index is None, (
+			"Cannot specify both 'before' and 'index' arguments when inserting")
+		self._validate_name(name)
+		if before is not None:
+			if isinstance(before, str):
+				before = getattr(self, before)
+			index = self._parts.index(before)
+		if hasattr(self, name):
+			old_part = getattr(self, name)
+			self._parts.remove(old_part)
+		self._parts.insert(index, part)
+		super(Parts, self).__setattr__(name, part)
+		if hasattr(part, 'set_world'):
+			part.set_world(self._world)
+
+	def __iter__(self):
+		"""Iterate the parts in order"""
+		return iter(tuple(self._parts))
+	
+	def __len__(self):
+		return len(self._parts)
+
+
+class ComponentParts(Parts):
+	"""Maps world components to attributes. The components are kept in the
+	order they are set. Components may also be inserted out of order.
+
+	Used for: :attr:`World.components`
+	"""
+
+	def join(self, *component_names):
+		"""Join and iterate entity data from multiple components together.
+
+		For each entity in all of the components named, yield a tuple containing
+		the entity data from each component specified.
+
+		This is useful in systems that pull data from multiple components.
+		
+		Typical Usage::
+
+			for position, movement in world.components.join("position", "movement"):
+				# Do something with each entity's position and movement data
+		"""
+		if component_names:
+			components = [getattr(self, self._validate_name(name)) 
+				for name in component_names]
+			if len(components) > 1:
+				entities = components[0].entities & components[1].entities
+				for comp in components[2:]:
+					entities &= comp.entities
+			else:
+				entities = components[0].entities
+			for entity in entities:
+				yield tuple(comp[entity] for comp in components)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mode.py	Tue Jul 12 10:16:48 2011 +0200
@@ -0,0 +1,21 @@
+
+from grease.mode import *
+from fife.extensions.basicapplication import ApplicationBase
+import abc
+
+class FifeManager(BaseManager, ApplicationBase):
+
+	def __init__(self, TDS):
+		ApplicationBase.__init__(self, TDS)
+		self.modes = []
+		self._settings = TDS
+	
+	def _pump(self):
+		if self.current_mode:
+			self.current_mode.pump(self.engine.getTimeManager().getTimeDelta() / 1000)
+	
+class FifeMode(BaseMode):
+	
+	@abc.abstractmethod
+	def pump(self, dt):
+		"""Performs actions every frame"""
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/world.py	Tue Jul 12 10:16:48 2011 +0200
@@ -0,0 +1,12 @@
+from grease.world import *
+from mode import FifeMode
+
+class World(FifeModeMode, BaseWorld):
+    
+    def pump(self, dt):
+	for component in self.components:
+	    if hasattr(component, "step"):
+		component.step(dt)
+	for system in self.systems:
+	    if hasattr(system, "step"):
+		system.step(dt)