Skip to content

Commit

Permalink
Work on Issue pythonarcade#89 and Issue pythonarcade#110. Improve col…
Browse files Browse the repository at this point in the history
…lision detection. Improve the speed, and remove issues around floating point errors.
  • Loading branch information
Paul Vincent Craven committed Mar 13, 2017
1 parent 68707c0 commit 007d383
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 10 deletions.
32 changes: 25 additions & 7 deletions arcade/geometry.py
Expand Up @@ -7,6 +7,8 @@
from typing import List
from arcade.arcade_types import PointList

import math

PRECISION = 2

def are_polygons_intersecting(poly_a: PointList,
Expand Down Expand Up @@ -35,6 +37,7 @@ def are_polygons_intersecting(poly_a: PointList,
True False False
"""

for polygon in (poly_a, poly_b):

for i1 in range(len(polygon)):
Expand All @@ -48,22 +51,22 @@ def are_polygons_intersecting(poly_a: PointList,
min_a, max_a, min_b, max_b = (None,) * 4

for poly in poly_a:
projected = normal[0] * poly[0] + normal[1] * poly[1]
projected = round(normal[0] * poly[0] + normal[1] * poly[1], 2)

if not min_a or round(projected, PRECISION) < round(min_a, PRECISION):
if not min_a or projected < min_a:
min_a = projected
if not max_a or round(projected, PRECISION) > round(max_a, PRECISION):
if not max_a or projected > max_a:
max_a = projected

for poly in poly_b:
projected = normal[0] * poly[0] + normal[1] * poly[1]
projected = round(normal[0] * poly[0] + normal[1] * poly[1], 2)

if not min_b or round(projected, PRECISION) < round(min_b, PRECISION):
if not min_b or projected < min_b:
min_b = projected
if not max_b or round(projected, PRECISION) > round(max_b, PRECISION):
if not max_b or projected > max_b:
max_b = projected

if round(max_a, PRECISION) <= round(min_b, PRECISION) or round(max_b, PRECISION) <= round(min_a, PRECISION):
if max_a <= min_b or max_b <= min_a:
return False

return True
Expand Down Expand Up @@ -96,6 +99,21 @@ def check_for_collision(sprite1: Sprite, sprite2: Sprite) -> bool:
raise TypeError("Parameter 1 is not an instance of the Sprite class.")
if not isinstance(sprite2, Sprite):
raise TypeError("Parameter 2 is not an instance of the Sprite class.")

collision_radius_sum = sprite1.collision_radius + sprite2.collision_radius

diff_x = abs(sprite1.center_x - sprite2.center_x)

if diff_x > collision_radius_sum:
return False
diff_y = abs(sprite2.center_y - sprite2.center_y)
if diff_y > collision_radius_sum:
return False

distance = math.sqrt(diff_x * diff_x + diff_y + diff_y)
if distance > collision_radius_sum:
return False

return are_polygons_intersecting(sprite1.points, sprite2.points)


Expand Down
14 changes: 11 additions & 3 deletions arcade/sprite.py
Expand Up @@ -131,6 +131,7 @@ def __init__(self,
self.alpha = 1.0
self.sprite_lists = []
self.transparent = True
self._collision_radius = None

self.can_cache = True
self._points = None
Expand Down Expand Up @@ -215,6 +216,16 @@ def get_points(self) -> List[List[float]]:

points = property(get_points, set_points)

def _set_collision_radius(self, collision_radius):
self._collision_radius = collision_radius

def _get_collision_radius(self):
if not self._collision_radius:
self._collision_radius = max(self.width, self.height)
return self._collision_radius

collision_radius = property(_get_collision_radius, _set_collision_radius)

def _get_bottom(self) -> float:
"""
Return the y coordinate of the bottom of the sprite.
Expand Down Expand Up @@ -303,7 +314,6 @@ def _set_center_y(self, new_value: float):

center_y = property(_get_center_y, _set_center_y)


def _get_change_x(self) -> float:
""" Get the velocity in the x plane of the sprite. """
return self.velocity[0]
Expand All @@ -324,8 +334,6 @@ def _set_change_y(self, new_value: float):

change_y = property(_get_change_y, _set_change_y)



def _get_angle(self) -> float:
""" Get the angle of the sprite's rotation. """
return self._angle
Expand Down

0 comments on commit 007d383

Please sign in to comment.