From 3d8e318aac6deb541e3f4fb445ff4f1b19a4bb0d Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 17 Feb 2023 15:27:46 -0500 Subject: [PATCH 1/4] add env var toggle to disable shapely; benchmark with and without shapely --- arcade/geometry.py | 6 +++++- benchmarks/bench-shapely-helper.sh | 7 +++++++ benchmarks/bench-shapely.sh | 18 ++++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 benchmarks/bench-shapely-helper.sh create mode 100644 benchmarks/bench-shapely.sh diff --git a/arcade/geometry.py b/arcade/geometry.py index e997ca4e9..4dc4866b1 100644 --- a/arcade/geometry.py +++ b/arcade/geometry.py @@ -1,3 +1,5 @@ +import os + shapely_exists = False try: @@ -6,8 +8,10 @@ except ImportError: pass +use_shapely = shapely_exists and not os.environ.get("DISABLE_SHAPELY") +print("Use shapely: " + str(use_shapely)) -if shapely_exists: +if use_shapely: from .geometry_shapely import are_polygons_intersecting # noqa: F401 from .geometry_shapely import is_point_in_polygon # noqa: F401 else: diff --git a/benchmarks/bench-shapely-helper.sh b/benchmarks/bench-shapely-helper.sh new file mode 100644 index 000000000..65d5b4474 --- /dev/null +++ b/benchmarks/bench-shapely-helper.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +if [ "$1" == "disabled" ] +then + export DISABLE_SHAPELY=true +fi +python -m benchmarks.collisions.bench \ No newline at end of file diff --git a/benchmarks/bench-shapely.sh b/benchmarks/bench-shapely.sh new file mode 100644 index 000000000..6c1a9e50e --- /dev/null +++ b/benchmarks/bench-shapely.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +# On windows, can run via the bash you get with git: +# C:\Program Files\Git\bin\bash.exe + +__dirname="$(CDPATH= cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$__dirname" +cd .. + +bench_name="$1" + +python.exe --version + +hyperfine \ + --show-output \ + --export-markdown benchmarks/results.md \ + --warmup 1 --runs 2 \ + --parameter-list shapely 'enabled,disabled' \ + 'bash ./benchmarks/bench-shapely-helper.sh {shapely}' From cab93b0c5ca339849dfabf9e276a778495830402 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 17 Feb 2023 15:28:36 -0500 Subject: [PATCH 2/4] fix benchmark --- benchmarks/collisions/bench.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmarks/collisions/bench.py b/benchmarks/collisions/bench.py index cfd5a6d4e..a3a5062dd 100644 --- a/benchmarks/collisions/bench.py +++ b/benchmarks/collisions/bench.py @@ -30,7 +30,7 @@ # like something I might create in a game. rng.seed(2) for i in range(0, WALLS_COUNT): - wall = arcade.SpriteSolidColor(rng.randint(WALL_DIM_MIN, WALL_DIM_MAX), rng.randint(WALL_DIM_MIN, WALL_DIM_MAX), arcade.color.BLACK) + wall = arcade.SpriteSolidColor(rng.randint(WALL_DIM_MIN, WALL_DIM_MAX), rng.randint(WALL_DIM_MIN, WALL_DIM_MAX), color=arcade.color.BLACK) wall.position = rng.randint(0, SCREEN_WIDTH), rng.randint(0, SCREEN_HEIGHT) walls.append(wall) From ebd06eb329986d83717714795aab9d353e8404ac Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 17 Feb 2023 16:20:41 -0500 Subject: [PATCH 3/4] add line-of-sight benchmark; tweak bench-shapely to run it --- benchmarks/bench-shapely-helper.sh | 4 +-- benchmarks/bench-shapely.sh | 2 +- benchmarks/line-of-sight/bench.py | 55 ++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 benchmarks/line-of-sight/bench.py diff --git a/benchmarks/bench-shapely-helper.sh b/benchmarks/bench-shapely-helper.sh index 65d5b4474..3ae995d67 100644 --- a/benchmarks/bench-shapely-helper.sh +++ b/benchmarks/bench-shapely-helper.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash -if [ "$1" == "disabled" ] +if [ "$2" == "disabled" ] then export DISABLE_SHAPELY=true fi -python -m benchmarks.collisions.bench \ No newline at end of file +python -m benchmarks.$1.bench \ No newline at end of file diff --git a/benchmarks/bench-shapely.sh b/benchmarks/bench-shapely.sh index 6c1a9e50e..baeef08ee 100644 --- a/benchmarks/bench-shapely.sh +++ b/benchmarks/bench-shapely.sh @@ -15,4 +15,4 @@ hyperfine \ --export-markdown benchmarks/results.md \ --warmup 1 --runs 2 \ --parameter-list shapely 'enabled,disabled' \ - 'bash ./benchmarks/bench-shapely-helper.sh {shapely}' + 'bash ./benchmarks/bench-shapely-helper.sh '"$bench_name"' {shapely}' diff --git a/benchmarks/line-of-sight/bench.py b/benchmarks/line-of-sight/bench.py new file mode 100644 index 000000000..93708b92d --- /dev/null +++ b/benchmarks/line-of-sight/bench.py @@ -0,0 +1,55 @@ +import math +import arcade +import pyglet +import random +import time + +SCREEN_WIDTH = 800 +SCREEN_HEIGHT = 600 + +WALL_DIM_MIN = 10 +WALL_DIM_MAX = 200 +WALLS_COUNT = 10 + +SIMULATE_MINUTES = 1 +SIMULATE_FPS = 60 + +# Predictable randomization so that each benchmark is identical +rng = random.Random(0) + +walls = arcade.SpriteList(use_spatial_hash=True) + +window = arcade.Window() + +# Seed chosen manually to create a wall distribution that looked good enough, +# like something I might create in a game. +rng.seed(2) +for i in range(0, WALLS_COUNT): + wall = arcade.SpriteSolidColor(rng.randint(WALL_DIM_MIN, WALL_DIM_MAX), rng.randint(WALL_DIM_MIN, WALL_DIM_MAX), color=arcade.color.BLACK) + wall.position = rng.randint(0, SCREEN_WIDTH), rng.randint(0, SCREEN_HEIGHT) + walls.append(wall) + +# Check for line-of-sight +def check_ray(): + x = rng.randint(0, SCREEN_WIDTH) + y = rng.randint(0, SCREEN_HEIGHT) + hit = arcade.has_line_of_sight(origin, (x, y), walls) + return x, y, hit + +origin = (SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2) +for i in range(0, int(SIMULATE_MINUTES * 60 * SIMULATE_FPS)): + pyglet.clock.tick() + + window.switch_to() + window.dispatch_events() + + rays = [check_ray() for x in range(1000)] + + window.dispatch_event('on_draw') + + window.clear(color=arcade.color.WHITE) + walls.draw() + + for ray in rays: + arcade.draw_line(origin[0], origin[1], ray[0], ray[1], arcade.color.BLUE if ray[2] else arcade.color.RED, 2) + window.flip() From f8ce8f2dac2f1a05ebe40a7a6274777d86654088 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 17 Feb 2023 17:11:43 -0500 Subject: [PATCH 4/4] tweak fix etc --- arcade/paths.py | 11 ++++++++++- arcade/paths_python.py | 3 +++ benchmarks/line-of-sight/bench.py | 11 ++++++++--- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/arcade/paths.py b/arcade/paths.py index 2d7505b81..511d03af6 100644 --- a/arcade/paths.py +++ b/arcade/paths.py @@ -2,11 +2,20 @@ Classic A-star algorithm for path finding. """ import sys +import os from arcade.types import Point from arcade import check_for_collision_with_list, SpriteList, Sprite from typing import Union, List, Tuple, Set, Optional -if 'shapely' in sys.modules: +shapely_exists = False +try: + import shapely # noqa: F401 + shapely_exists = True +except ImportError: + pass +use_shapely = shapely_exists and not os.environ.get("DISABLE_SHAPELY") +print("Use shapely for has_line_of_sight: " + str(use_shapely)) +if use_shapely: from .paths_shapely import has_line_of_sight # noqa: F401 else: from .paths_python import has_line_of_sight # noqa: F401 diff --git a/arcade/paths_python.py b/arcade/paths_python.py index 7e28bd8a0..bc82bfc90 100644 --- a/arcade/paths_python.py +++ b/arcade/paths_python.py @@ -27,6 +27,9 @@ def has_line_of_sight(point_1: Point, distance = get_distance(point_1[0], point_1[1], point_2[0], point_2[1]) steps = int(distance // check_resolution) + if distance == 0: + sprite_list = get_sprites_at_point(point_1, walls) + return not (len(sprite_list) > 0) for step in range(steps + 1): step_distance = step * check_resolution u = step_distance / distance diff --git a/benchmarks/line-of-sight/bench.py b/benchmarks/line-of-sight/bench.py index 93708b92d..5ca977335 100644 --- a/benchmarks/line-of-sight/bench.py +++ b/benchmarks/line-of-sight/bench.py @@ -11,7 +11,7 @@ WALL_DIM_MAX = 200 WALLS_COUNT = 10 -SIMULATE_MINUTES = 1 +SIMULATE_MINUTES = 0.1 SIMULATE_FPS = 60 # Predictable randomization so that each benchmark is identical @@ -37,19 +37,24 @@ def check_ray(): return x, y, hit origin = (SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2) +rays = [] for i in range(0, int(SIMULATE_MINUTES * 60 * SIMULATE_FPS)): pyglet.clock.tick() window.switch_to() window.dispatch_events() - rays = [check_ray() for x in range(1000)] + rays[:] = [check_ray() for x in range(1000)] window.dispatch_event('on_draw') window.clear(color=arcade.color.WHITE) walls.draw() - for ray in rays: + # only draw a few: + # A) so the benchmark isn't dominated by slow draw_line calls + # B) to validate that the line-of-sight checks are accurate + # C) so you get a visual indicator of framerate + for ray in rays[:10]: arcade.draw_line(origin[0], origin[1], ray[0], ray[1], arcade.color.BLUE if ray[2] else arcade.color.RED, 2) window.flip()