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.
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
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
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]]