From 30d0ada9167eb801f4851c474db518f45f2ded63 Mon Sep 17 00:00:00 2001 From: CLIDragon <84266961+CLIDragon@users.noreply.github.com> Date: Mon, 26 Aug 2024 13:35:58 +1000 Subject: [PATCH] Add a variant of closest_points_first that takes a predicate Part of #75945. The variant will be used in pathfinding to find the nearest ramp or stairs without having to enumerate all points. --- src/point.cpp | 24 ++++-------------------- src/point.h | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 20 deletions(-) diff --git a/src/point.cpp b/src/point.cpp index d2ceed95f6758..8008f7385db0f 100644 --- a/src/point.cpp +++ b/src/point.cpp @@ -159,26 +159,10 @@ std::vector closest_points_first( const point ¢er, int min_dist, int std::vector result; result.reserve( n + ( is_center_included ? 1 : 0 ) ); - if( is_center_included ) { - result.push_back( center ); - } - - int x_init = std::max( min_dist, 1 ); - point p( x_init, 1 - x_init ); - - point d( point_east ); - - for( int i = 0; i < n; i++ ) { - result.push_back( center + p ); - - if( p.x == p.y || ( p.x < 0 && p.x == -p.y ) || ( p.x > 0 && p.x == 1 - p.y ) ) { - std::swap( d.x, d.y ); - d.x = -d.x; - } - - p.x += d.x; - p.y += d.y; - } + find_point_closest_first( center, min_dist, max_dist, [&result]( const point & p ) { + result.push_back( p ); + return false; + } ); return result; } diff --git a/src/point.h b/src/point.h index 3047978be41d7..d1a2c6930f6fd 100644 --- a/src/point.h +++ b/src/point.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -290,6 +291,11 @@ std::vector closest_points_first( const tripoint ¢er, int min_dist std::vector closest_points_first( const point ¢er, int max_dist ); std::vector closest_points_first( const point ¢er, int min_dist, int max_dist ); +// TODO: Make this into an Input Iterator. +template +std::optional find_point_closest_first( const point ¢er, int min_dist, int max_dist, + PredicateFn &&fn ); + inline constexpr tripoint tripoint_min { INT_MIN, INT_MIN, INT_MIN }; inline constexpr tripoint tripoint_max{ INT_MAX, INT_MAX, INT_MAX }; @@ -363,4 +369,50 @@ inline const std::array eight_horizontal_neighbors = { { } }; +template +std::optional find_point_closest_first( const point ¢er, int min_dist, int max_dist, + PredicateFn &&predicate_fn ) +{ + min_dist = std::max( min_dist, 0 ); + max_dist = std::max( max_dist, 0 ); + + if( min_dist > max_dist ) { + return std::nullopt; + } + + const int min_edge = min_dist * 2 + 1; + const int max_edge = max_dist * 2 + 1; + + const int n = max_edge * max_edge - ( min_edge - 2 ) * ( min_edge - 2 ); + const bool is_center_included = min_dist == 0; + + if( is_center_included ) { + if( predicate_fn( center ) ) { + return center; + } + } + + int x_init = std::max( min_dist, 1 ); + point p( x_init, 1 - x_init ); + + point d( point_east ); + + for( int i = 0; i < n; i++ ) { + const point next = center + p; + if( predicate_fn( next ) ) { + return next; + } + + if( p.x == p.y || ( p.x < 0 && p.x == -p.y ) || ( p.x > 0 && p.x == 1 - p.y ) ) { + std::swap( d.x, d.y ); + d.x = -d.x; + } + + p.x += d.x; + p.y += d.y; + } + + return std::nullopt; +} + #endif // CATA_SRC_POINT_H