Skip to content

Issue #782 Using shapely to find line of sight #783

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 26 additions & 29 deletions arcade/paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,51 +2,48 @@
Path-related functions.

"""
from shapely import speedups
from shapely.geometry import LineString, Polygon

from arcade import Point
from arcade import get_distance
from arcade import lerp_vec
from arcade import get_sprites_at_point
from arcade import check_for_collision_with_list
from arcade import Sprite
from arcade import SpriteList


speedups.enable()


def has_line_of_sight(point_1: Point,
point_2: Point,
walls: SpriteList,
max_distance: int = -1,
check_resolution: int = 2):
max_distance: int = -1):
"""
Determine if we have line of sight between two points. Try to make sure
that spatial hashing is enabled on the wall SpriteList or this will be
very slow.

:param Point point_1: Start position
:param Point point_2: End position position
:param SpriteList walls: List of all blocking sprites
:param int max_distance: Max distance point 1 can see
:param int check_resolution: Check every x pixels for a sprite. Trade-off
between accuracy and speed.
Determine if we have line of sight between two points. Having a line of
sight means, that you can connect both points with straight line without
intersecting any obstacle.
Thanks to the shapely efficiency and speedups boost, this method is very
fast. It can easily test 10 000 lines_of_sight.

:param point_1: tuple -- coordinates of first position (x, y)
:param point_2: tuple -- coordinates of second position (x, y)
:param walls: list -- Obstacle objects to check against
:param max_distance: int --
:return: tuple -- (bool, list)
"""
distance = get_distance(point_1[0], point_1[1],
point_2[0], point_2[1])
steps = int(distance // check_resolution)
for step in range(steps + 1):
step_distance = step * check_resolution
u = step_distance / distance
midpoint = lerp_vec(point_1, point_2, u)
if max_distance != -1 and step_distance > max_distance:
return False
# print(point_1, point_2, step, u, step_distance, midpoint)
sprite_list = get_sprites_at_point(midpoint, walls)
if len(sprite_list) > 0:
return False
return True
if not walls:
return True
line_of_sight = LineString([point_1, point_2])
if 0 < max_distance < line_of_sight.length:
return False
return not any((Polygon(o.get_adjusted_hit_box()).crosses(line_of_sight) for o in walls))


"""
Classic A-star algorithm for path finding.
"""


def _spot_is_blocked(position, moving_sprite, blocking_sprites):
original_pos = moving_sprite.position
moving_sprite.position = position
Expand Down
19 changes: 10 additions & 9 deletions arcade/texture.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,16 @@ def reset(self):
return self

def multiply(self, o: List[float]):
self.v = [self.v[0] * o[0] + self.v[3] * o[1] + self.v[6] * o[2],
self.v[1] * o[0] + self.v[4] * o[1] + self.v[7] * o[2],
self.v[2] * o[0] + self.v[5] * o[1] + self.v[8] * o[2],
self.v[0] * o[3] + self.v[3] * o[4] + self.v[6] * o[5],
self.v[1] * o[3] + self.v[4] * o[4] + self.v[7] * o[5],
self.v[2] * o[3] + self.v[5] * o[4] + self.v[8] * o[5],
self.v[0] * o[6] + self.v[3] * o[7] + self.v[6] * o[8],
self.v[1] * o[6] + self.v[4] * o[7] + self.v[7] * o[8],
self.v[2] * o[6] + self.v[5] * o[7] + self.v[8] * o[8]]
v = self.v
self.v = [v[0] * o[0] + v[3] * o[1] + v[6] * o[2],
v[1] * o[0] + v[4] * o[1] + v[7] * o[2],
v[2] * o[0] + v[5] * o[1] + v[8] * o[2],
v[0] * o[3] + v[3] * o[4] + v[6] * o[5],
v[1] * o[3] + v[4] * o[4] + v[7] * o[5],
v[2] * o[3] + v[5] * o[4] + v[8] * o[5],
v[0] * o[6] + v[3] * o[7] + v[6] * o[8],
v[1] * o[6] + v[4] * o[7] + v[7] * o[8],
v[2] * o[6] + v[5] * o[7] + v[8] * o[8]]
return self

def scale(self, sx: float, sy: float):
Expand Down
1 change: 1 addition & 0 deletions tests/unit2/test_line_of_sight.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import arcade


def test_line_of_sight():
player = arcade.Sprite(":resources:images/animated_characters/female_person/femalePerson_idle.png")
player.center_x = 50
Expand Down