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