Stricter PEP-8 conformance.
[python-bulletml.git] / bulletml / collision.py
index f185745..2e88b3c 100644 (file)
@@ -1,9 +1,13 @@
 """Simple collision check.
 
 This module provides simple collision checking appropriate for
 """Simple collision check.
 
 This module provides simple collision checking appropriate for
-shmups. It provides a routine to check whether two moving circles
+shmups. It provides routines to check whether two moving circles
 collided during the past frame.
 
 collided during the past frame.
 
+An equivalent C-based version will be used automatically if it was
+compiled and installed with the module. If available, it will be noted
+in the docstrings for the functions.
+
 Basic Usage:
 
     from bulletml.collision import collides
 Basic Usage:
 
     from bulletml.collision import collides
@@ -20,11 +24,16 @@ def overlaps(a, b):
     Usually, you'll want to use the 'collides' method instead, but
     this one can be useful for just checking to see if the player has
     entered an area or hit a stationary oject.
     Usually, you'll want to use the 'collides' method instead, but
     this one can be useful for just checking to see if the player has
     entered an area or hit a stationary oject.
+
+    (This function is unoptimized.)
     """
 
     dx = a.x - b.x
     dy = a.y - b.y
     """
 
     dx = a.x - b.x
     dy = a.y - b.y
-    radius = getattr(a, 'radius', 0.5) + getattr(b, 'radius', 0.5)
+    try:
+        radius = a.radius + b.radius
+    except AttributeError:
+        radius = getattr(a, 'radius', 0.5) + getattr(b, 'radius', 0.5)
 
     return dx * dx + dy * dy <= radius * radius
 
 
     return dx * dx + dy * dy <= radius * radius
 
@@ -37,6 +46,8 @@ def collides(a, b):
     px, py - not required, defaults to x, y, previous frame position
     radius - not required, defaults to 0.5
 
     px, py - not required, defaults to x, y, previous frame position
     radius - not required, defaults to 0.5
 
+    (This function is unoptimized.)
+
     """
     # Current locations.
     xa = a.x
     """
     # Current locations.
     xa = a.x
@@ -45,27 +56,32 @@ def collides(a, b):
     yb = b.y
 
     # Treat b as a point, we only need one radius.
     yb = b.y
 
     # Treat b as a point, we only need one radius.
-    radius = getattr(a, 'radius', 0.5) + getattr(b, 'radius', 0.5)
+    try:
+        radius = a.radius + b.radius
+    except AttributeError:
+        radius = getattr(a, 'radius', 0.5) + getattr(b, 'radius', 0.5)
 
     # Previous frame locations.
 
     # Previous frame locations.
-    pxa = getattr(a, 'px', xa)
-    pya = getattr(a, 'py', ya)
-    pxb = getattr(b, 'px', xb)
-    pyb = getattr(b, 'py', yb)
+    try: pxa = a.px
+    except KeyError: pxa = xa
+    try: pya = a.py
+    except KeyError: pya = ya
+    try: pxb = b.px
+    except KeyError: pxb = xb
+    try: pyb = b.py
+    except KeyError: pyb = yb
 
     # Translate b's final position to be relative to a's start.
     # And now, circle/line collision.
     dir_x = pxa + (xb - xa) - pxb
     dir_y = pya + (yb - ya) - pyb
 
 
     # Translate b's final position to be relative to a's start.
     # And now, circle/line collision.
     dir_x = pxa + (xb - xa) - pxb
     dir_y = pya + (yb - ya) - pyb
 
-    if abs(dir_x) < 0.0001 and abs(dir_y) < 0.0001:
-        # b did not move relative to a, so do point/circle.
-        dx = pxb - pxa
-        dy = pyb - pya
-        return dx * dx + dy * dy < radius * radius
-
     diff_x = pxa - pxb
     diff_y = pya - pyb
     diff_x = pxa - pxb
     diff_y = pya - pyb
+    if (dir_x < 0.0001 and dir_x > -0.0001
+        and dir_y < 0.0001 and dir_y > -0.0001):
+        # b did not move relative to a, so do point/circle.
+        return diff_x * diff_x + diff_y * diff_y < radius * radius
 
     # dot(diff, dir) / dot(dir, dir)
     t = (diff_x * dir_x + diff_y * dir_y) / (dir_x * dir_x + dir_y * dir_y)
 
     # dot(diff, dir) / dot(dir, dir)
     t = (diff_x * dir_x + diff_y * dir_y) / (dir_x * dir_x + dir_y * dir_y)
@@ -79,3 +95,18 @@ def collides(a, b):
 
     # dist_sq < radius_sq
     return dist_x * dist_x + dist_y * dist_y <= radius * radius
 
     # dist_sq < radius_sq
     return dist_x * dist_x + dist_y * dist_y <= radius * radius
+
+def collides_all(a, others):
+    """Filter the second argument to those that collide with the first.
+
+    This is equivalent to filter(lambda o: collides(a, o), others),
+    but is much faster when the compiled extension is available (which
+    it is not currently).
+
+    """
+    return filter(lambda o: collides(a, o), others)
+
+try:
+    from bulletml._collision import collides, overlaps, collides_all
+except ImportError:
+    pass