commit:e132441e48b77f0edbf8dd20513a93b82669457c
author:Chip Black
committer:Chip Black
date:Wed Jul 16 00:35:25 2008 -0500
parents:e60d7fb7e84042d48029f9723a263b607f103e8d
Changed SurfaceSet to use QuadTreeR instead of a list
diff --git a/Richter/Surface.py b/Richter/Surface.py
line changes: +44/-40
index c15f365..4117307
--- a/Richter/Surface.py
+++ b/Richter/Surface.py
@@ -1,9 +1,9 @@
 import pygame
 from OpenGL.GL import *
+from util import QuadTreeR
 import math
 
 class Surface:
-	fuzz = 20
 	def __init__(self, p1, p2):
 		self.p1 = p1
 		self.p2 = p2
@@ -23,10 +23,8 @@ class Surface:
 		# A collision rect for fast collision culling. Only surfaces
 		# whose initial rect collision check passes will run the
 		# detailed check
-		self.rect = pygame.Rect(p1,(self.dx,self.dy))
-		self.rect.normalize()
-		#self.rect.width += 1
-		#self.rect.height += 1
+		self.bbox = pygame.Rect(p1,(self.dx,self.dy))
+		self.bbox.normalize()
 
 
 	def lineSideTest(self, point):
@@ -48,12 +46,12 @@ class Surface:
 
 
 	def collidePoint(self, point):
-		if self.rect.collidepoint(point[0],point[1]):
+		if self.bbox.collidepoint(point[0],point[1]):
 			return self.lineSideTest(point)
 
 
 	def collideRect(self, rect):
-		if not self.rect.colliderect(rect):
+		if not self.bbox.colliderect(rect):
 			return False
 		#print self.collidePoint(rect.bottomleft), self.collidePoint(rect.bottomright), self.collidePoint(rect.topright), self.collidePoint(rect.topright)
 		return self.lineSideTest(rect.bottomleft) or \
@@ -74,13 +72,13 @@ class Surface:
 		glDisable(GL_TEXTURE_2D)
 
 		if self.dx > 0:
-			py = self.rect.top	# rects are upside-down in our space
+			py = self.bbox.top	# rects are upside-down in our space
 		else:
-			py = self.rect.bottom - 1
+			py = self.bbox.bottom - 1
 		if self.dy > 0:
-			px = self.rect.right - 1
+			px = self.bbox.right - 1
 		else:
-			px = self.rect.left
+			px = self.bbox.left
 		glColor4f(1.0,0.0,0.0,0.25)
 		glBegin(GL_TRIANGLES)
 		glVertex3f(self.p2[0], self.p2[1], 0)
@@ -92,8 +90,8 @@ class Surface:
 		glBegin(GL_LINES)
 		glVertex3f(self.p1[0],self.p1[1],0)
 		glVertex3f(self.p2[0],self.p2[1],0)
-		glVertex3f(self.rect.centerx, self.rect.centery, 0)
-		glVertex3f(self.rect.centerx + anglepoint[0], self.rect.centery + anglepoint[1], 0)
+		glVertex3f(self.bbox.centerx, self.bbox.centery, 0)
+		glVertex3f(self.bbox.centerx + anglepoint[0], self.bbox.centery + anglepoint[1], 0)
 		glEnd()
 
 		glPopMatrix()
@@ -103,6 +101,7 @@ class Solid:
 	def __init__(self, p1, p2):
 		self.rect = pygame.Rect(p1,(p2[0]-p1[0],p2[1]-p1[1]))
 		self.rect.normalize()
+		self.bbox = self.rect
 	
 
 	def collidePoint(self, point):
@@ -136,17 +135,19 @@ class Solid:
 class SurfaceSet:
 	surfaces = None
 
-	def __init__(self, file = None):
-		self.surfaces = []
-		if file is not None:
-			self.parse(file)
+	def __init__(self, f):
+		if isinstance(f, str) or isinstance(f, unicode):
+			fo = open(f, 'r')
+			self.parse(fo)
+			fo.close()
+		else:
+			self.parse(f)
 	
 
-	def parse(self, file):
-		self.surfaces = []
+	def parse(self, f):
+		surfaces = []
 
 		mf = (1,1)
-		f = open(file,'r')
 		for line in f:
 			args = line.split()
 			if not args:
@@ -158,42 +159,45 @@ class SurfaceSet:
 			elif cmd == 'surface':
 				p1 = (int(args[0])*mf[0],int(args[1])*mf[1])
 				p2 = (int(args[2])*mf[0],int(args[3])*mf[1])
-				self.surfaces.append(Surface(p1,p2))
+				surfaces.append(Surface(p1,p2))
 			elif cmd == 'solid':
 				p1 = (int(args[0])*mf[0],int(args[1])*mf[1])
 				p2 = (int(args[2])*mf[0],int(args[3])*mf[1])
-				self.surfaces.append(Solid(p1,p2))
+				surfaces.append(Solid(p1,p2))
 		f.close()
-	
+
+		# calculate bounding box for all surfaces
+		bbox = surfaces[0].bbox.unionall([s.bbox for s in surfaces[1:]])
+		self.surfaces = QuadTreeR(bbox)
+		for s in surfaces:
+			self.surfaces.add(s.bbox, s)
+
 
 	def collidePoint(self,point):
-		colliders = filter(lambda x: x.rect.collidepoint(point), self.surfaces)
-		if not colliders:
-			return False
-		for c in colliders:
-			if not c.collidePoint(point):
-				return False
-		return True
+		colliders = self.surfaces.collidePointWalk(point)
+		#if not colliders:
+		#	return False
+		for s in colliders:
+			if s.collidePoint(point):
+				return True
+		return False
 
 
 	def collideRect(self, rect):
-		colliders = filter(lambda x: x.rect.colliderect(rect), self.surfaces)
-		if not colliders:
-			return False
-		#print [(c,c.rect,c.collideRect(rect)) for c in colliders]
-		for c in colliders:
-			if not c.collideRect(rect):
-				return False
-		return True
+		for s in self.surfaces.collideRectWalk(rect):
+			if s.collideRect(rect):
+				return True
+		return False
 
 
 	def draw(self):
 		for s in self.surfaces:
 			s.draw()
+		self.surfaces.draw()
 	
 
-	def append(self, s):
-		return self.surfaces.append(s)
+	def add(self, s):
+		return self.surfaces.add(s.bbox, s)
 
 
 	def remove(self, s):

diff --git a/Richter/util.py b/Richter/util.py
line changes: +38/-5
index 47bcfbc..58c6047
--- a/Richter/util.py
+++ b/Richter/util.py
@@ -52,6 +52,25 @@ class QuadTreeR:
 				self.rects.append((pygame.Rect(rect), obj))
 	
 
+	def remove(self, obj):
+		for (r, o) in self.rects:
+			if o == obj:
+				self.rects.remove((r, o))
+				return
+		if self.subregions:
+			for s in self.subregions:
+				s.remove(obj)
+
+
+	def __iter__(self):
+		for (r, o) in self.rects:
+			yield o
+		if self.subregions:
+			for s in self.subregions:
+				for o in s:
+					yield o
+
+
 	def collideRectWalk(self, rect):
 		"""generator that walks through the QuadTree, returning rects
 		that collide with the given rect"""
@@ -60,16 +79,30 @@ class QuadTreeR:
 				yield o
 		if self.subregions:
 			for s in self.subregions:
-				if s.bbox.contains(rect):
+				if s.bbox.colliderect(rect):
 					for o in s.collideRectWalk(rect):
 						yield o
 	
 
 	def collideRect(self, rect):
-		for o in self.collisionWalk(rect):
-			return o
-		else:
-			return None
+		return [o for o in self.collideRectWalk(rect)]
+
+
+	def collidePointWalk(self, point):
+		"""generator that walks through the QuadTree, returning rects
+		that collide with the given point"""
+		for (r, o) in self.rects:
+			if r.collidepoint(point):
+				yield o
+		if self.subregions:
+			for s in self.subregions:
+				if s.bbox.collidepoint(point):
+					for o in s.collidePointWalk(point):
+						yield o
+
+
+	def collidePoint(self, point):
+		return [o for o in self.collidePointWalk(point)]
 
 
 	def draw(self, rect = None):

diff --git a/quadtreetest.py b/quadtreetest.py
line changes: +1/-1
index fdc742c..67e21dc
--- a/quadtreetest.py
+++ b/quadtreetest.py
@@ -14,7 +14,7 @@ for i in xrange(0, 20):
 	h = randint(0, 480 - y)
 	r.add((x,y,w,h), None)
 
-mouse = pygame.Rect(0, 0, 10, 10)
+mouse = pygame.Rect(0, 0, 100, 100)
 
 Engine.init((640, 480))
 

diff --git a/surfacetest.py b/surfacetest.py
line changes: +22/-16
index 9c5c4c2..04d5bf5
--- a/surfacetest.py
+++ b/surfacetest.py
@@ -4,6 +4,7 @@ import pygame
 from Richter.Sprite import *
 from Richter.Surface import *
 import Richter.Engine as Engine
+from StringIO import StringIO
 
 #resolution = (1280,1024)
 resolution = (640,480)
@@ -13,24 +14,29 @@ Engine.init(resolution)
 
 pygame.display.set_caption("surfacetest")
 
-surfaces = SurfaceSet('data/example1')
-# Octagon
-#surfaces.append(Surface( (520,240),(461,98) ))
-#surfaces.append(Surface( (461,98),(319,40) ))
-#surfaces.append(Surface( (319,40),(178,98) ))
-#surfaces.append(Surface( (178,98),(120,240) ))
-#surfaces.append(Surface( (120,240),(178,381) ))
-#surfaces.append(Surface( (178,381),(320,440) ))
-#surfaces.append(Surface( (320,440),(461,381) ))
-#surfaces.append(Surface( (461,381),(520,240) ))
+#surfaces = SurfaceSet('data/example1')
 
 # Concave
-#surfaces.append(Surface( (200,200), (200,400) ))
-#surfaces.append(Surface( (200,400), (300,300) ))
-#surfaces.append(Surface( (300,300), (400,300) ))
-#surfaces.append(Surface( (400,300), (500,400) ))
-#surfaces.append(Surface( (500,400), (500,200) ))
-#surfaces.append(Surface( (500,200), (200,200) ))
+surfaces = SurfaceSet(StringIO("""
+tilesize 100 100
+surface 2 4 3 2
+surface 3 4 2 3
+solid 2 2 5 3
+surface 4 3 5 4
+"""))
+
+# Octagon
+#surfaces = SurfaceSet(StringIO("""
+#surface 520 240 461 98
+#surface 461 98 319 40
+#surface 319 40 178 98
+#surface 178 98 120 240
+#surface 120 240 178 381
+#surface 178 381 320 440
+#surface 320 440 461 381
+#surface 461 381 520 240
+#"""))
+
 
 player = Sprite('img/star.png')
 player.setGravity(CENTER, CENTER)