/Richter/Surface.py
import pygame
from OpenGL.GL import *
from util import QuadTreeR
import math

class Surface:
	def __init__(self, p1, p2):
		self.p1 = p1
		self.p2 = p2
		self.dx = p2[0] - p1[0]
		self.dy = p2[1] - p1[1]
		try:
			self.m_x = float(self.dy) / float(self.dx)
		except ZeroDivisionError:
			self.m_x = 1e999
		self.b_x = p1[1] - self.m_x * p1[0]
		try:
			self.m_y = float(self.dx) / float(self.dy)
		except ZeroDivisionError:
			self.m_y = 1e999
		self.b_y = p1[0] - self.m_y * p1[1]

		# A collision rect for fast collision culling. Only surfaces
		# whose initial rect collision check passes will run the
		# detailed check
		self.bbox = pygame.Rect(p1,(self.dx,self.dy))
		self.bbox.normalize()


	def lineSideTest(self, point):
		if self.m_x > -1.0 and self.m_x < 1.0:
			if self.dx > 0:
				if point[1] < self.m_x * point[0] + self.b_x:
					return True
			else:
				if point[1] > self.m_x * point[0] + self.b_x:
					return True
		else:
			if self.dy > 0:
				if point[0] > self.m_y * point[1] + self.b_y:
					return True
			else:
				if point[0] < self.m_y * point[1] + self.b_y:
					return True
		return False


	def collidePoint(self, point):
		if self.bbox.collidepoint(point[0],point[1]):
			return self.lineSideTest(point)


	def collideRect(self, 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 \
		       self.lineSideTest(rect.bottomright) or \
		       self.lineSideTest(rect.topright) or \
		       self.lineSideTest(rect.topright)


	def draw(self):
		angle = math.atan(self.m_x)
		if self.dx < 0 or self.dy < 0:
			arrow = (angle+3*math.pi/2) % (2*math.pi)
		else:
			arrow = (angle+math.pi/2) % (2*math.pi)
		anglepoint = (math.cos(arrow) * 10, math.sin(arrow) * 10)

		glPushMatrix()
		glDisable(GL_TEXTURE_2D)

		if self.dx > 0:
			py = self.bbox.top	# rects are upside-down in our space
		else:
			py = self.bbox.bottom - 1
		if self.dy > 0:
			px = self.bbox.right - 1
		else:
			px = self.bbox.left
		glColor4f(1.0,0.0,0.0,0.25)
		glBegin(GL_TRIANGLES)
		glVertex3f(self.p2[0], self.p2[1], 0)
		glVertex3f(self.p1[0], self.p1[1], 0)
		glVertex3f(px, py, 0)
		glEnd()

		glColor3f(1.0,1.0,1.0)
		glBegin(GL_LINES)
		glVertex3f(self.p1[0],self.p1[1],0)
		glVertex3f(self.p2[0],self.p2[1],0)
		glVertex3f(self.bbox.centerx, self.bbox.centery, 0)
		glVertex3f(self.bbox.centerx + anglepoint[0], self.bbox.centery + anglepoint[1], 0)
		glEnd()

		glPopMatrix()


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):
		return self.rect.collidepoint(point[0],point[1])


	def collideRect(self, rect):
		return bool(self.rect.colliderect(rect))


	def draw(self):
		glPushMatrix()
		glDisable(GL_TEXTURE_2D)
		glColor4f(1.0,0.0,0.0,0.25)
		glBegin(GL_QUADS)
		glVertex(self.rect.left, self.rect.bottom, 0)
		glVertex(self.rect.left, self.rect.top, 0)
		glVertex(self.rect.right, self.rect.top, 0)
		glVertex(self.rect.right, self.rect.bottom, 0)
		glEnd()
		glColor3f(1.0,1.0,1.0)
		glBegin(GL_LINE_LOOP)
		glVertex(self.rect.left, self.rect.bottom, 0)
		glVertex(self.rect.left, self.rect.top, 0)
		glVertex(self.rect.right, self.rect.top, 0)
		glVertex(self.rect.right, self.rect.bottom, 0)
		glEnd()
		glPopMatrix()


class SurfaceSet:
	surfaces = None

	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, f):
		surfaces = []

		mf = (1,1)
		for line in f:
			args = line.split()
			if not args:
				continue
			cmd = args[0]
			args = args[1:]
			if cmd == 'tilesize':
				mf = (int(args[0]),int(args[1]))
			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])
				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])
				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 = 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):
		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 add(self, s):
		return self.surfaces.add(s.bbox, s)


	def remove(self, s):
		return self.surfaces.remove(s)