-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsat.py
More file actions
137 lines (105 loc) · 5.59 KB
/
sat.py
File metadata and controls
137 lines (105 loc) · 5.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
from utils.utilbase import *
class SeparatingAxisTest:
def __init__(self, polyA, polyB):
self.polygonA = polyA
self.polygonB = polyB
self.polyAPoints = polyA.getTransformedPoints()[0]
self.polyBPoints = polyB.getTransformedPoints()[0]
#Generate a big list made up of all the potential separating axes of both polygons
self.axes = [edge.getNormal(self.polyAPoints, True) for edge in polyA.getEdgeList().edges] + [edge.getNormal(self.polyBPoints, True) for edge in polyB.getEdgeList().edges]
def calculate(self):
leastOverlap = float('inf')
leastOverlapAxis = self.axes[0]
#We want our collision normals to point FROM B to A
#In other words, vector BA, which is generated by the origin of point A - origin of point B
#We will use this in a bit
relativeVector = self.polygonA.origin - self.polygonB.origin
for axis in self.axes:
a_min, b_min = float('inf'), float('inf')
a_max, b_max = float('-inf'), float('-inf')
#Iterate through each axis, finding the Amin, Amax, Bmin and Bmax for each polygon on that axis
for point in self.polyAPoints:
projection = point.dot(axis)
#Note that this is an if statement and not an elif
#This caused a very subtle bug where my infinities were not being replaced
#It is entirely possible that in an early iteration through the points, the minimum and maximum points
#may be the exact same value - this corrects itself in subsequent iterations
if projection < a_min:
a_min = projection
if projection > a_max:
a_max = projection
for point in self.polyBPoints:
projection = point.dot(axis)
if projection < b_min:
b_min = projection
if projection > b_max:
b_max = projection
#Check for overlap - we create an interval. If the interval is positive, we save it and its associated axis as our
# newest least overlap values If there is no overlap, we can exit early, since we have found a separating axis, so there
# can't be any collision
intervalMax = min(a_max, b_max)
intervalMin = max(a_min, b_min)
dist = intervalMax - intervalMin
if dist < 0:
#There is no overlap, so we have found a separating axis and can terminate early
return (False, 0, None)
if dist <= leastOverlap:
#We found a new overlap interval less than or equal to our current one
#We want to ensure that our direction vector points from B to A
#By using the dot product, we compare the new and old overlap vector to our desired target direction BA
#If the new vector is more in the direction of BA than the old one, then we replace the old vector
#This is to keep our normals consistent
newDot = relativeVector.dot(axis)
oldDot = relativeVector.dot(leastOverlapAxis)
if newDot > oldDot:
leastOverlap = dist
leastOverlapAxis = axis
return (True, leastOverlap, leastOverlapAxis)
if __name__ == '__main__':
polyA = Polygon(Vector(165.0, 175.0))
polyA.addPoint(Vector(0.0, -60.0))
polyA.addPoint(Vector(-60.0, 60.0))
polyA.addPoint(Vector(60.0, 60.0))
polyB = Polygon(Vector(320, 225))
angle = 2.0 * math.pi / 9.0
for i in range(0, -9, -1):
newPt = Vector(55 * math.cos(angle * i), 55 * math.sin(angle * i))
polyB.addPoint(newPt)
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Separating Axis Test Demo")
running = True
clock = pygame.time.Clock()
currentControl = 1
speed = 6.0
rot_speed = 0.15
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_1:
currentControl = 1
elif event.key == pygame.K_2:
currentControl = 2
keys = pygame.key.get_pressed()
if currentControl == 1:
polyA.origin += Vector(keys[pygame.K_RIGHT] - keys[pygame.K_LEFT], keys[pygame.K_DOWN] - keys[pygame.K_UP]) * speed * 0.1666
polyA.rotation += keys[pygame.K_r] * rot_speed * 0.1666
else:
polyB.origin += Vector(keys[pygame.K_RIGHT] - keys[pygame.K_LEFT], keys[pygame.K_DOWN] - keys[pygame.K_UP]) * speed * 0.1666
polyB.rotation += keys[pygame.K_r] * rot_speed * 0.1666
sat = SeparatingAxisTest(polyA, polyB)
(isColliding, penetrationDepth, normal_vector) = sat.calculate()
screen.fill((0, 0, 0))
drawPolygon(screen, polyA, color = (255, 255, 255) if not isColliding else (255, 0, 0))
drawPolygon(screen, polyB, color = (255, 255, 255) if not isColliding else (255, 0, 0))
if isColliding:
font = pygame.font.SysFont(None, 24)
penDepthText = font.render("Penetration Depth " + str(round(penetrationDepth, 2)), True, (255, 255, 255))
normDirText = font.render("Normal " + str(normal_vector), True, (255, 255, 255))
screen.blit(penDepthText, (500, 500))
screen.blit(normDirText, (500, 550))
pygame.display.flip()
clock.tick(60)
pygame.quit()