Optional Pyrex extension for the collision module. More than doubles its speed.
[python-bulletml.git] / bulletml / _collision.pyx
1 """Optimized collision detection functions."""
2
3 def overlaps(a, b):
4 """Return true if two circles are overlapping.
5
6 Usually, you'll want to use the 'collides' method instead, but
7 this one can be useful for just checking to see if the player has
8 entered an area or hit a stationary oject.
9
10 (This function is optimized.)
11
12 """
13
14 cdef float ax
15 cdef float bx
16 cdef float ay
17 cdef float by
18 cdef float dx
19 cdef float dy
20 cdef float radius_a
21 cdef float radius_b
22 cdef float radius
23
24 ax = a.x
25 bx = b.x
26 ay = a.y
27 by = b.y
28
29 dx = ax - bx
30 dy = ay - by
31 try:
32 radius = a.radius
33 except AttributeError:
34 radius = 0.5
35 try:
36 radius += b.radius
37 except AttributeError:
38 radius += 0.5
39
40 return dx * dx + dy * dy <= radius * radius
41
42
43 cdef int _collides(float xa, float xb, float ya, float yb,
44 float pxa, float pxb, float pya, float pyb,
45 float radius_a, float radius_b):
46
47 """Check collision for two moving circles."""
48
49 cdef float dir_x
50 cdef float dir_y
51
52 cdef float diff_x
53 cdef float diff_y
54 cdef float dist_x
55 cdef float dist_y
56
57 cdef float dx
58 cdef float dy
59 cdef float t
60
61 cdef float radius
62
63 radius = radius_a + radius_b
64
65 # Translate b's final position to be relative to a's start.
66 # And now, circle/line collision.
67 dir_x = pxa + (xb - xa) - pxb
68 dir_y = pya + (yb - ya) - pyb
69
70 if (dir_x < 0.0001 and dir_x > -0.0001
71 and dir_y < 0.0001 and dir_y > -0.0001):
72 # b did not move relative to a, so do point/circle.
73 dx = pxb - pxa
74 dy = pyb - pya
75 return dx * dx + dy * dy < radius * radius
76
77 diff_x = pxa - pxb
78 diff_y = pya - pyb
79
80 # dot(diff, dir) / dot(dir, dir)
81 t = (diff_x * dir_x + diff_y * dir_y) / (dir_x * dir_x + dir_y * dir_y)
82 if t < 0:
83 t = 0
84 elif t > 1:
85 t = 1
86
87 dist_x = pxa - (pxb + dir_x * t)
88 dist_y = pya - (pyb + dir_y * t)
89
90 # dist_sq < radius_sq
91 return dist_x * dist_x + dist_y * dist_y <= radius * radius
92
93 def collides(a, b):
94 """Return true if the two moving circles collide.
95
96 a and b should have the following attributes:
97
98 x, y - required, current position
99 px, py - not required, defaults to x, y, previous frame position
100 radius - not required, defaults to 0.5
101
102 (This function is optimized.)
103
104 """
105 cdef float xa
106 cdef float xb
107 cdef float ya
108 cdef float yb
109
110 cdef float pxa
111 cdef float pya
112 cdef float pxb
113 cdef float pyb
114
115 cdef float radius_a
116 cdef float radius_b
117
118 xa = a.x
119 xb = b.x
120 ya = a.y
121 yb = b.y
122
123 radius_a = getattr3(a, 'radius', 0.5)
124 radius_b = getattr3(b, 'radius', 0.5)
125
126 pxa = getattr3(a, 'px', xa)
127 pya = getattr3(a, 'py', ya)
128 pxb = getattr3(b, 'px', xb)
129 pyb = getattr3(b, 'py', yb)
130
131 return _collides(xa, xb, ya, yb, pxa, pxb, pya, pyb, radius_a, radius_b)
132
133 def collides_all(a, others):
134 """Filter the second argument to those that collide with the first.
135
136 This is equivalent to filter(lambda o: collides(a, o), others),
137 but is much faster when the compiled extension is available (which
138 it is currently).
139
140 """
141 cdef float xa
142 cdef float xb
143 cdef float ya
144 cdef float yb
145
146 cdef float pxa
147 cdef float pya
148 cdef float pxb
149 cdef float pyb
150
151 cdef float radius_a
152 cdef float radius_b
153
154 cdef list bs
155 cdef int length
156
157 cdef list colliding
158
159 cdef int coll
160
161 colliding = []
162
163 xa = a.x
164 ya = a.y
165 radius_a = getattr3(a, 'radius', 0.5)
166 pxa = getattr3(a, 'px', xa)
167 pya = getattr3(a, 'py', ya)
168
169 bs = list(others)
170 length = len(bs)
171
172 for 0 <= i < length:
173 b = others[i]
174 xb = b.x
175 yb = b.y
176 radius_b = getattr3(b, 'radius', 0.5)
177 pxb = getattr3(b, 'px', xb)
178 pyb = getattr3(b, 'py', yb)
179
180 if _collides(xa, xb, ya, yb, pxa, pxb, pya, pyb, radius_a, radius_b):
181 colliding.append(b)
182 return colliding