2 The polygon module has functions that assist in working with 2D convex polygons.
4 __copyright__ = "Copyright (C) 2013 David Braam - Released under terms of the AGPLv3 License"
10 """ Check if C is left of the infinite line from A to B """
11 return ((b[0] - a[0])*(c[1] - a[1]) - (b[1] - a[1])*(c[0] - a[0])) > 0
14 def _isRightTurn((p, q, r)):
15 sum1 = q[0]*r[1] + p[0]*q[1] + r[0]*p[1]
16 sum2 = q[0]*p[1] + r[0]*q[1] + p[0]*r[1]
24 def convexHull(pointList):
25 """ Create a convex hull from a list of points. """
28 unique[p[0], p[1]] = 1
30 points = unique.keys()
33 return numpy.zeros((0, 2), numpy.float32)
35 return numpy.array(points, numpy.float32)
37 # Build upper half of the hull.
38 upper = [points[0], points[1]]
41 while len(upper) > 2 and not _isRightTurn(upper[-3:]):
44 # Build lower half of the hull.
46 lower = [points[0], points[1]]
49 while len(lower) > 2 and not _isRightTurn(lower[-3:]):
56 return numpy.array(upper + lower, numpy.float32)
59 def minkowskiHull(a, b):
60 """Calculate the minkowski hull of 2 convex polygons"""
61 points = numpy.zeros((len(a) * len(b), 2))
62 for n in xrange(0, len(a)):
63 for m in xrange(0, len(b)):
64 points[n * len(b) + m] = a[n] + b[m]
65 return convexHull(points.copy())
68 def _projectPoly(poly, normal):
70 Project a convex polygon on a given normal.
71 A projection of a convex polygon on a infinite line is a finite line.
72 Give the min and max value on the normal line.
74 pMin = numpy.dot(normal, poly[0])
76 for n in xrange(1 , len(poly)):
77 p = numpy.dot(normal, poly[n])
83 def polygonCollision(polyA, polyB):
84 """ Check if convexy polygon A and B collide, return True if this is the case. """
85 for n in xrange(0, len(polyA)):
88 normal = (p1 - p0)[::-1]
89 normal[1] = -normal[1]
90 normal /= numpy.linalg.norm(normal)
91 aMin, aMax = _projectPoly(polyA, normal)
92 bMin, bMax = _projectPoly(polyB, normal)
97 for n in xrange(0, len(polyB)):
100 normal = (p1 - p0)[::-1]
101 normal[1] = -normal[1]
102 normal /= numpy.linalg.norm(normal)
103 aMin, aMax = _projectPoly(polyA, normal)
104 bMin, bMax = _projectPoly(polyB, normal)
112 def polygonCollisionPushVector(polyA, polyB):
113 """ Check if convex polygon A and B collide, return the vector of penetration if this is the case, else return False. """
116 for n in xrange(0, len(polyA)):
119 normal = (p1 - p0)[::-1]
120 normal[1] = -normal[1]
121 normal /= numpy.linalg.norm(normal)
122 aMin, aMax = _projectPoly(polyA, normal)
123 bMin, bMax = _projectPoly(polyB, normal)
128 size = min(aMax, bMax) - max(aMin, bMin)
130 ret = normal * (size + 0.1)
132 for n in xrange(0, len(polyB)):
135 normal = (p1 - p0)[::-1]
136 normal[1] = -normal[1]
137 normal /= numpy.linalg.norm(normal)
138 aMin, aMax = _projectPoly(polyA, normal)
139 bMin, bMax = _projectPoly(polyB, normal)
144 size = min(aMax, bMax) - max(aMin, bMin)
146 ret = normal * -(size + 0.1)
151 def fullInside(polyA, polyB):
153 Check if convex polygon A is completely inside of convex polygon B.
155 for n in xrange(0, len(polyA)):
158 normal = (p1 - p0)[::-1]
159 normal[1] = -normal[1]
160 normal /= numpy.linalg.norm(normal)
161 aMin, aMax = _projectPoly(polyA, normal)
162 bMin, bMax = _projectPoly(polyB, normal)
167 for n in xrange(0, len(polyB)):
170 normal = (p1 - p0)[::-1]
171 normal[1] = -normal[1]
172 normal /= numpy.linalg.norm(normal)
173 aMin, aMax = _projectPoly(polyA, normal)
174 bMin, bMax = _projectPoly(polyB, normal)
182 def lineLineIntersection(p0, p1, p2, p3):
183 """ Return the intersection of the infinite line trough points p0 and p1 and infinite line trough points p2 and p3. """
186 C1 = A1*p0[0] + B1*p0[1]
190 C2 = A2 * p2[0] + B2 * p2[1]
195 return [(B2*C1 - B1*C2)/det, (A1 * C2 - A2 * C1) / det]
198 def clipConvex(poly0, poly1):
199 """ Cut the convex polygon 0 so that it completely fits in convex polygon 1, any part sticking out of polygon 1 is cut off """
201 for p1idx in xrange(0, len(poly1)):
206 for n in xrange(0, len(src)):
208 if not _isLeft(p0, p1, p):
209 if _isLeft(p0, p1, src[n-1]):
210 res.append(lineLineIntersection(p0, p1, src[n-1], p))
212 elif not _isLeft(p0, p1, src[n-1]):
213 res.append(lineLineIntersection(p0, p1, src[n-1], p))
214 return numpy.array(res, numpy.float32)