diff --git a/src/point.cpp b/src/point.cpp index d2ceed95f6758..bb5b47facc40c 100644 --- a/src/point.cpp +++ b/src/point.cpp @@ -124,14 +124,19 @@ std::vector closest_points_first( const tripoint ¢er, int max_dist std::vector closest_points_first( const tripoint ¢er, int min_dist, int max_dist ) { - const std::vector points = closest_points_first( center.xy(), min_dist, max_dist ); + std::optional n = rectangle_size( min_dist, max_dist ); + + if( n == std::nullopt ) { + return {}; + } std::vector result; - result.reserve( points.size() ); + result.reserve( *n ); - for( const point &p : points ) { - result.emplace_back( p, center.z ); - } + find_point_closest_first( center, min_dist, max_dist, [&result]( const tripoint & p ) { + result.push_back( p ); + return false; + } ); return result; } @@ -143,42 +148,26 @@ 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 ) { - min_dist = std::max( min_dist, 0 ); - max_dist = std::max( max_dist, 0 ); + std::optional n = rectangle_size( min_dist, max_dist ); - if( min_dist > max_dist ) { + if( n == std::nullopt ) { return {}; } - 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; - std::vector result; - result.reserve( n + ( is_center_included ? 1 : 0 ) ); - - if( is_center_included ) { - result.push_back( center ); - } + result.reserve( *n ); - 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; } + +template +std::optional find_point_closest_first( const Point ¢er, int max_dist, + PredicateFn &&fn ) +{ + return find_point_closest_first( center, 0, max_dist, fn ); +}; diff --git a/src/point.h b/src/point.h index 3047978be41d7..ad0fbd916c45e 100644 --- a/src/point.h +++ b/src/point.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -290,6 +291,32 @@ 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 ); +template +std::optional find_point_closest_first( const Point ¢er, int min_dist, int max_dist, + PredicateFn &&fn ); + +template +std::optional find_point_closest_first( const Point ¢er, int max_dist, + PredicateFn &&fn ); + + +// Calculate the number of tiles in a square from min_dist to max_dist about an arbitrary centre. +inline std::optional rectangle_size( int min_dist, int max_dist ) +{ + 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 ) + ( min_dist == 0 ? 1 : 0 ); + return n; +}; + inline constexpr tripoint tripoint_min { INT_MIN, INT_MIN, INT_MIN }; inline constexpr tripoint tripoint_max{ INT_MAX, INT_MAX, INT_MAX }; @@ -363,4 +390,48 @@ 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 ) +{ + std::optional n = rectangle_size( min_dist, max_dist ); + + if( n == std::nullopt ) { + return {}; + } + + 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; + p.x = x_init; + p.y = 1 - x_init; + + Point d; + d.x += 1; + + 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