Skip to content

Commit

Permalink
Merge pull request CleverRaven#57187 from anothersimulacrum/pointifex
Browse files Browse the repository at this point in the history
Allow applying arbitrary filters to tripoint_range, add points_in_radius_circ
  • Loading branch information
ZhilkinSerg authored Apr 27, 2022
2 parents 733ee33 + 3d98a92 commit d6087a2
Show file tree
Hide file tree
Showing 2 changed files with 245 additions and 25 deletions.
88 changes: 73 additions & 15 deletions src/map_iterator.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,24 +33,31 @@ class tripoint_range

point_generator( const Tripoint &_p, const tripoint_range &_range )
: p( _p ), range( _range ) {
// Make sure we start on a valid point
if( range.predicate && !( *range.predicate )( p ) && p != range.endp ) {
operator++();
}
}

// Increment x, then if it goes outside range, "wrap around" and increment y
// Same for y and z
inline point_generator &operator++() {
traits::x( p )++;
if( traits::x( p ) <= traits::x( range.maxp ) ) {
return *this;
}

traits::y( p )++;
traits::x( p ) = traits::x( range.minp );
if( traits::y( p ) <= traits::y( range.maxp ) ) {
return *this;
}
do {
traits::x( p )++;
if( traits::x( p ) <= traits::x( range.maxp ) ) {
continue;
}

traits::y( p )++;
traits::x( p ) = traits::x( range.minp );
if( traits::y( p ) <= traits::y( range.maxp ) ) {
continue;
}

traits::z( p )++;
traits::y( p ) = traits::y( range.minp );
} while( range.predicate && !( *range.predicate )( p ) && p != range.endp );

traits::z( p )++;
traits::y( p ) = traits::y( range.minp );
return *this;
}

Expand All @@ -74,15 +81,26 @@ class tripoint_range

Tripoint minp;
Tripoint maxp;

Tripoint endp;

cata::optional<std::function<bool( const Tripoint & )>> predicate;
public:
using value_type = typename point_generator::value_type;
using difference_type = typename point_generator::difference_type;
using pointer = typename point_generator::pointer;
using reference = typename point_generator::reference;
using iterator_category = typename point_generator::iterator_category;

tripoint_range( const Tripoint &_minp, const Tripoint &_maxp,
const std::function<bool( const Tripoint & )> &pred ) :
minp( _minp ), maxp( _maxp ), predicate( pred ) {
endp = Tripoint( minp.xy(), traits::z( maxp ) + 1 );
}

tripoint_range( const Tripoint &_minp, const Tripoint &_maxp ) :
minp( _minp ), maxp( _maxp ) {
endp = Tripoint( minp.xy(), traits::z( maxp ) + 1 );
}

point_generator begin() const {
Expand All @@ -92,12 +110,21 @@ class tripoint_range
point_generator end() const {
// Return the point AFTER the last one
// That is, point under (in z-levels) the first one, but one z-level below the last one
return point_generator( Tripoint( minp.xy(), traits::z( maxp ) + 1 ), *this );
return point_generator( endp, *this );
}

size_t size() const {
Tripoint range( maxp - minp );
return std::max( ++traits::x( range ) * ++traits::y( range ) * ++traits::z( range ), 0 );
if( !predicate ) {
Tripoint range( traits::x( maxp ) - traits::x( minp ), traits::y( maxp ) - traits::y( minp ),
traits::z( maxp ) - traits::z( minp ) );
return std::max( ++traits::x( range ) * ++traits::y( range ) * ++traits::z( range ), 0 );
} else {
size_t count = 0;
for( point_generator it = begin(); it != end(); ++it ) {
++count;
}
return count;
}
}

bool empty() const {
Expand Down Expand Up @@ -130,4 +157,35 @@ inline tripoint_range<Tripoint> points_in_radius( const Tripoint &center, const
return tripoint_range<Tripoint>( center - offset, center + offset );
}

template<typename Tripoint>
inline tripoint_range<Tripoint> points_in_radius_circ( const Tripoint &center, const int radius,
const int radiusz = 0 )
{
static_assert( Tripoint::dimension == 3, "Requires tripoint type" );
const tripoint offset( radius, radius, radiusz );
return tripoint_range<Tripoint>( center - offset,
center + offset, [center, radius]( const Tripoint & pt ) {
return trig_dist( center, pt ) < radius + 0.5f;
} );
}

/* Template vodoo to allow passing lambdas to the below function without a compiler complaint
* Courtesy of
* https://stackoverflow.com/questions/13358672/how-to-convert-a-lambda-to-an-stdfunction-using-templates#13359347
*/
template <typename T>
struct tripoint_predicate_fun {
using type = T;
};

template<typename Tripoint>
inline tripoint_range<Tripoint> points_in_radius_where( const Tripoint &center, const int radius,
const typename tripoint_predicate_fun<std::function<bool( const Tripoint &pt )>>::type &func,
const int radiusz = 0 )
{
static_assert( Tripoint::dimension == 3, "Requires tripoint type" );
const tripoint offset( radius, radius, radiusz );
return tripoint_range<Tripoint>( center - offset, center + offset, func );
}

#endif // CATA_SRC_MAP_ITERATOR_H
182 changes: 172 additions & 10 deletions tests/map_iterator_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ static std::array<tripoint, 9> range_1_2d_centered = {
}
};

TEST_CASE( "Radius one 2D square centered at origin." )
TEST_CASE( "Radius one 2D square centered at origin.", "[tripoint_range]" )
{
for( const tripoint &candidate :
tripoint_range<tripoint>( tripoint_north_west, tripoint_south_east ) ) {
tripoint_range<tripoint> tested( tripoint_north_west, tripoint_south_east );
REQUIRE( tested.size() == range_1_2d_centered.size() );
for( const tripoint &candidate : tested ) {
REQUIRE( std::find( range_1_2d_centered.begin(), range_1_2d_centered.end(), candidate ) !=
range_1_2d_centered.end() );
}
Expand All @@ -30,18 +31,21 @@ static std::array<tripoint, 9> range_1_2d_offset = {
}
};

TEST_CASE( "Radius one 2D square centered at -4/-4/0." )
TEST_CASE( "Radius one 2D square centered at -4/-4/0.", "[tripoint_range]" )
{
for( const tripoint &candidate : tripoint_range<tripoint>( {-5, -5, 0}, {-3, -3, 0} ) ) {
tripoint_range<tripoint> tested( {-5, -5, 0}, {-3, -3, 0} );
REQUIRE( tested.size() == range_1_2d_offset.size() );
for( const tripoint &candidate : tested ) {
REQUIRE( std::find( range_1_2d_offset.begin(), range_1_2d_offset.end(), candidate ) !=
range_1_2d_offset.end() );
}
}

TEST_CASE( "Radius one 2D square centered at -4/-4/0 in abs_omt coords." )
TEST_CASE( "Radius one 2D square centered at -4/-4/0 in abs_omt coords.", "[tripoint_range]" )
{
for( const tripoint_abs_omt &candidate :
tripoint_range<tripoint_abs_omt>( {-5, -5, 0}, {-3, -3, 0} ) ) {
tripoint_range<tripoint_abs_omt> tested( {-5, -5, 0}, {-3, -3, 0} );
REQUIRE( tested.size() == range_1_2d_offset.size() );
for( const tripoint_abs_omt &candidate : tested ) {
REQUIRE( std::find( range_1_2d_offset.begin(), range_1_2d_offset.end(), candidate.raw() ) !=
range_1_2d_offset.end() );
}
Expand Down Expand Up @@ -106,10 +110,168 @@ static std::array<tripoint, 343> range_3_3d_offset = {
}
};

TEST_CASE( "Radius three 3D square centered at 8/8/1." )
TEST_CASE( "Radius three 3D square centered at 8/8/1.", "[tripoint_range]" )
{
for( const tripoint &candidate : tripoint_range<tripoint>( {5, 5, -2}, {11, 11, 4} ) ) {
tripoint_range<tripoint> tested( {5, 5, -2}, {11, 11, 4} );
REQUIRE( tested.size() == range_3_3d_offset.size() );
for( const tripoint &candidate : tested ) {
REQUIRE( std::find( range_3_3d_offset.begin(), range_3_3d_offset.end(), candidate ) !=
range_3_3d_offset.end() );
}
}

TEST_CASE( "tripoint_range_iteration_order", "[tripoint_range]" )
{
tripoint_range<tripoint> tested( tripoint( 4, 4, 0 ), tripoint( 6, 6, 0 ) );
std::vector<tripoint> expected = {
{ 4, 4, 0 }, { 5, 4, 0 }, { 6, 4, 0 },
{ 4, 5, 0 }, { 5, 5, 0 }, { 6, 5, 0 },
{ 4, 6, 0 }, { 5, 6, 0 }, { 6, 6, 0 }
};
REQUIRE( tested.size() == expected.size() );
int i = 0;
for( const tripoint &pt : tested ) {
CHECK( pt == expected[i] );
++i;
}
}

TEST_CASE( "tripoint_range_handle_bad_predicates", "[tripoint_range]" )
{
tripoint_range<tripoint> tested( tripoint( 4, 4, 0 ), tripoint( 6, 6, 0 ), []( const tripoint & ) {
return false;
} );
REQUIRE( tested.empty() );
int visited = 0;
for( const tripoint &pt : tested ) {
INFO( pt );
REQUIRE( false );
++visited;
}
CHECK( visited == 0 );
}

TEST_CASE( "tripoint_range_circle_order", "[tripoint_range]" )
{
const tripoint center( 6, 6, 0 );
tripoint_range<tripoint> range_test( tripoint( 4, 4, 0 ), tripoint( 8, 8,
0 ), [center]( const tripoint & pt ) {
return trig_dist( center, pt ) < 2.5;
} );
tripoint_range<tripoint> radius_test = points_in_radius_circ( center, 2 );
std::vector<tripoint> expected = {
{ 5, 4, 0 }, { 6, 4, 0 }, { 7, 4, 0 },
{ 4, 5, 0 }, { 5, 5, 0 }, { 6, 5, 0 }, { 7, 5, 0 }, { 8, 5, 0 },
{ 4, 6, 0 }, { 5, 6, 0 }, center, { 7, 6, 0 }, { 8, 6, 0 },
{ 4, 7, 0 }, { 5, 7, 0 }, { 6, 7, 0 }, { 7, 7, 0 }, { 8, 7, 0 },
{ 5, 8, 0 }, { 6, 8, 0 }, { 7, 8, 0 },
};
REQUIRE( range_test.size() == expected.size() );
REQUIRE( radius_test.size() == expected.size() );
size_t range = 0;
size_t radius = 0;
for( const tripoint &pt : range_test ) {
CHECK( pt == expected[range] );
++range;
}
for( const tripoint &pt : radius_test ) {
CHECK( pt == expected[radius] );
++radius;
}
CHECK( range == expected.size() );
CHECK( radius == expected.size() );
}

TEST_CASE( "tripoint_range_circle_sizes_correct", "[tripoint_range]" )
{
/* 0:
* ...
* .x.
* ...
*/
CHECK( points_in_radius_circ( tripoint_zero, 0 ).size() == 1 );
/* 1:
* xxx
* xxx
* xxx
*/
CHECK( points_in_radius_circ( tripoint_zero, 1 ).size() == 9 );
/* 2:
* .xxx.
* xxxxx
* xxxxx
* xxxxx
* .xxx.
*/
CHECK( points_in_radius_circ( tripoint_zero, 2 ).size() == 21 );
/* 3:
* ..xxx..
* .xxxxx.
* xxxxxxx
* xxxxxxx
* xxxxxxx
* .xxxxx.
* ..xxx..
*/
CHECK( points_in_radius_circ( tripoint_zero, 3 ).size() == 37 );
/* 4:
* ..xxxxx..
* .xxxxxxx.
* xxxxxxxxx
* xxxxxxxxx
* xxxxxxxxx
* xxxxxxxxx
* xxxxxxxxx
* .xxxxxxx.
* ..xxxxx..
*/
CHECK( points_in_radius_circ( tripoint_zero, 4 ).size() == 69 );
}

TEST_CASE( "tripoint_range_predicates_radius", "[tripoint_range]" )
{
tripoint_range<tripoint> tested = points_in_radius_where( tripoint_zero,
2, []( const tripoint & pt ) {
return pt.z < 0;
}, 2 );
// NOLINTBEGIN(cata-use-named-point-constants)
std::vector<tripoint> expected = {
{ -2, -2, -2 }, { -1, -2, -2 }, { 0, -2, -2 }, { 1, -2, -2 }, { 2, -2, -2 },
{ -2, -1, -2 }, { -1, -1, -2 }, { 0, -1, -2 }, { 1, -1, -2 }, { 2, -1, -2 },
{ -2, 0, -2 }, { -1, 0, -2 }, { 0, 0, -2 }, { 1, 0, -2 }, { 2, 0, -2 },
{ -2, 1, -2 }, { -1, 1, -2 }, { 0, 1, -2 }, { 1, 1, -2 }, { 2, 1, -2 },
{ -2, 2, -2 }, { -1, 2, -2 }, { 0, 2, -2 }, { 1, 2, -2 }, { 2, 2, -2 },

{ -2, -2, -1 }, { -1, -2, -1 }, { 0, -2, -1 }, { 1, -2, -1 }, { 2, -2, -1 },
{ -2, -1, -1 }, { -1, -1, -1 }, { 0, -1, -1 }, { 1, -1, -1 }, { 2, -1, -1 },
{ -2, 0, -1 }, { -1, 0, -1 }, { 0, 0, -1 }, { 1, 0, -1 }, { 2, 0, -1 },
{ -2, 1, -1 }, { -1, 1, -1 }, { 0, 1, -1 }, { 1, 1, -1 }, { 2, 1, -1 },
{ -2, 2, -1 }, { -1, 2, -1 }, { 0, 2, -1 }, { 1, 2, -1 }, { 2, 2, -1 },
};
// NOLINTEND(cata-use-named-point-constants)
REQUIRE( tested.size() == expected.size() );
size_t i = 0;
for( const tripoint &pt : tested ) {
CHECK( pt == expected[i] );
++i;
}
CHECK( i == tested.size() );
}

TEST_CASE( "tripoint_range_predicates", "[tripoint_range]" )
{
tripoint_range<tripoint> tested( tripoint_north_west,
tripoint_south_east, []( const tripoint & pt ) {
return pt.x == 0;
} );
std::vector<tripoint> expected = {
tripoint_north, tripoint_zero, tripoint_south
};
REQUIRE( tested.size() == expected.size() );
size_t i = 0;
for( const tripoint &pt : tested ) {
CHECK( pt == expected[i] );
++i;
}
CHECK( i == tested.size() );
}

0 comments on commit d6087a2

Please sign in to comment.