Skip to content

Commit

Permalink
Add a variant of closest_points_first that takes a predicate
Browse files Browse the repository at this point in the history
Part of CleverRaven#75945. The variant will be used in pathfinding to find the nearest
ramp or stairs without having to enumerate all points.

* Make find_point_closest_first generic over point and tripoint.
* Extract common code between closest_points_first variants
  • Loading branch information
CLIDragon committed Nov 7, 2024
1 parent 60cf27e commit 7c8f21b
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 35 deletions.
59 changes: 24 additions & 35 deletions src/point.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,14 +124,19 @@ std::vector<tripoint> closest_points_first( const tripoint &center, int max_dist

std::vector<tripoint> closest_points_first( const tripoint &center, int min_dist, int max_dist )
{
const std::vector<point> points = closest_points_first( center.xy(), min_dist, max_dist );
std::optional<int> n = rectangle_size( min_dist, max_dist );

if( n == std::nullopt ) {
return {};
}

std::vector<tripoint> 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;
}
Expand All @@ -143,42 +148,26 @@ std::vector<point> closest_points_first( const point &center, int max_dist )

std::vector<point> closest_points_first( const point &center, int min_dist, int max_dist )
{
min_dist = std::max( min_dist, 0 );
max_dist = std::max( max_dist, 0 );
std::optional<int> 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<point> 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 <typename PredicateFn, typename Point>
std::optional<Point> find_point_closest_first( const Point &center, int max_dist,
PredicateFn &&fn )
{
return find_point_closest_first( center, 0, max_dist, fn );
};
71 changes: 71 additions & 0 deletions src/point.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <cstdint>
#include <cstdlib>
#include <functional>
#include <optional>
#include <ostream>
#include <vector>

Expand Down Expand Up @@ -290,6 +291,32 @@ std::vector<tripoint> closest_points_first( const tripoint &center, int min_dist
std::vector<point> closest_points_first( const point &center, int max_dist );
std::vector<point> closest_points_first( const point &center, int min_dist, int max_dist );

template <typename PredicateFn, typename Point>
std::optional<Point> find_point_closest_first( const Point &center, int min_dist, int max_dist,
PredicateFn &&fn );

template <typename PredicateFn, typename Point>
std::optional<Point> find_point_closest_first( const Point &center, 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<int> 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 };

Expand Down Expand Up @@ -380,4 +407,48 @@ inline const std::array<tripoint, 8> eight_horizontal_neighbors = { {
}
};

template <typename PredicateFn, typename Point>
std::optional<Point> find_point_closest_first( const Point &center, int min_dist, int max_dist,
PredicateFn &&predicate_fn )
{
std::optional<int> 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

0 comments on commit 7c8f21b

Please sign in to comment.