Action: Don't need the owner in the constructor. Factory function to create a child.
[python-bulletml.git] / bulletml / collision.py
1 """Simple collision check.
2
3 This module provides simple collision checking appropriate for
4 shmups. It provides a routine to check whether two moving circles
5 collided during the past frame.
6
7 If Pyrex was available when installing, this will used optimized
8 versions of the functions.
9
10 Basic Usage:
11
12 from bulletml.collision import collides
13
14 for bullet in bullets:
15 if collides(player, bullet): ... # Kill the player.
16 """
17
18 from __future__ import division
19
20 def overlaps(a, b):
21 """Return true if two circles are overlapping.
22
23 Usually, you'll want to use the 'collides' method instead, but
24 this one can be useful for just checking to see if the player has
25 entered an area or hit a stationary oject.
26
27 (This function is unoptimized.)
28 """
29
30 dx = a.x - b.x
31 dy = a.y - b.y
32 radius = getattr(a, 'radius', 0.5) + getattr(b, 'radius', 0.5)
33
34 return dx * dx + dy * dy <= radius * radius
35
36 def collides(a, b):
37 """Return true if the two moving circles collide.
38
39 a and b should have the following attributes:
40
41 x, y - required, current position
42 px, py - not required, defaults to x, y, previous frame position
43 radius - not required, defaults to 0.5
44
45 (This function is unoptimized.)
46
47 """
48 # Current locations.
49 xa = a.x
50 xb = b.x
51 ya = a.y
52 yb = b.y
53
54 # Treat b as a point, we only need one radius.
55 radius = getattr(a, 'radius', 0.5) + getattr(b, 'radius', 0.5)
56
57 # Previous frame locations.
58 pxa = getattr(a, 'px', xa)
59 pya = getattr(a, 'py', ya)
60 pxb = getattr(b, 'px', xb)
61 pyb = getattr(b, 'py', yb)
62
63 # Translate b's final position to be relative to a's start.
64 # And now, circle/line collision.
65 dir_x = pxa + (xb - xa) - pxb
66 dir_y = pya + (yb - ya) - pyb
67
68 if abs(dir_x) < 0.0001 and abs(dir_y) < 0.0001:
69 # b did not move relative to a, so do point/circle.
70 dx = pxb - pxa
71 dy = pyb - pya
72 return dx * dx + dy * dy < radius * radius
73
74 diff_x = pxa - pxb
75 diff_y = pya - pyb
76
77 # dot(diff, dir) / dot(dir, dir)
78 t = (diff_x * dir_x + diff_y * dir_y) / (dir_x * dir_x + dir_y * dir_y)
79 if t < 0:
80 t = 0
81 elif t > 1:
82 t = 1
83
84 dist_x = pxa - (pxb + dir_x * t)
85 dist_y = pya - (pyb + dir_y * t)
86
87 # dist_sq < radius_sq
88 return dist_x * dist_x + dist_y * dist_y <= radius * radius
89
90 def collides_all(a, others):
91 """Filter the second argument to those that collide with the first.
92
93 This is equivalent to filter(lambda o: collides(a, o), others),
94 but is much faster when the compiled extension is available (which
95 it is not currently).
96
97 """
98 return filter(lambda o: collides(a, o), others)
99
100 try:
101 from bulletml._collision import collides, overlaps, collides_all
102 except ImportError:
103 pass