Python : collision entre des polygones

Pour l'instant seul les collisions entre polygones convexes sont traitées. Pour les polygones concaves, on peut les découper en plusieurs polygones convexes.

class HalfPlane

Cette classe définie un demi plan d'équation side*(b + a*x - y) > 0 de bordure d'équation a*x + b = y.

class HalfPlane:
	""" Cette classe définie un demi plan d'équation side*(b + a*x - y) > 0
	de bordure d'équation a*x + b = y """
	def __init__(self, x1, y1, x2, y2, x3, y3):
		"p1 et p2 doivent appertir à la bordure, p3 doit appartenir au demi plan"
		if x1 != x2:
			self.vertical = False
			self.a = (y1-y2)/(x1-x2)
			self.b = y1 - self.a * x1
			self.side = self.b + self.a*x3 - y3
		else: # bordure verticale
			self.vertical = True
			self.b = x1
			self.side = x3 - x1
 
	def contains(self, x, y):
		if self.vertical:
			return self.side*(x - self.b) > 0
		else:
			return self.side*(self.b + self.a*x - y) > 0
 
	def square_distance(self, x, y):
		if self.vertical:
			return (self.b - x)**2
		else:
			return (self.a*x-y+self.b)**2/(self.a**2+1)
 
	def intersects_circle(self, x, y, r):
		return self.contains(x, y) or self.square_distance(x, y) < r**2

class ConvexShape

Construit une forme à partir d'une liste de points. Pour tester les collisions, on voit cette forme comme intersection de demi plans. Cette classe définit des fonctions de test de collision utiles dont notamment intersects_shape, et check_convex qui vérifie que la forme est effectivement convexe (sans quoi les tests de collision sont faux). Vous pouvez faire ma_fenetre.Draw(ma_forme_convexe) mais comme vous le voyez dans le code, les appels aux fonctions telles que sf.Shape.SetPosition, etc n'affecteront pas les calculs de collision.

from PySFML import sf
 
class ConvexShape(sf.Shape):
	"forme définie par une liste de points"
	def __init__(self, points):
		sf.Shape.__init__(self)
		for point in points:
			self.AddPoint(*point)
		self.points = points
		self.half_planes = [HalfPlane(*(self.get_point(i)+self.get_point(i+1)+self.get_point(i+2))) for i in range(len(self.points))]
 
	def get_point(self, i):
		return self.points[i%len(self.points)]
 
	def check_convex(self):
		for i in range(len(self.points)):
			if not self.half_planes[i].contains(*self.get_point(i-1)):
				return False
		return True
 
	def intersects_shape(self, shape):
		for p in shape.points:
			if self.contains(*p):
				return True
		for p in self.points:
			if shape.contains(*p):
				return True
		return False
 
	def contains(self, x, y):
		for l in self.half_planes:
			if not l.contains(x, y):
				return False
		return True
 
	def intersects_half_plane(self, plane):
		for p in self.points:
			if plane.contains(*p):
				return True
		return False

Utiliser un path depuis un svg

Aller, un petit bonus pour la fin. Créez une forme fermée constituée uniquement de lignes droites sous inkscape. Sélectionnez la, puis faites Shift+Ctrl+X (ou Edit > XML Editor). Passez la valeur de l'attribut d à la fonction ci-dessous, et elle renvoit une liste de points utilisable par ConvexShape, en une ligne :)

def points_list_from_svg_path(path):
	return [map(float, i.split(',')) for i in path[2:-2].split(" L ")[:-1]]

ou en python3 :

def points_list_from_svg_path(path):
	return [list(map(float, i.split(','))) for i in path[2:-2].split(" L ")[:-1]]
 
fr/sources/py_shape_collision.txt · Last modified: 2009/03/07 21:14 by remi.k2620
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki