Replace tile area in WaterRegion with x, y

Use non-virtual tile iterator
This commit is contained in:
Jonathan G Rennison
2024-01-09 22:48:04 +00:00
parent c2e1dfcfc1
commit 5dd67651a3
2 changed files with 55 additions and 21 deletions

View File

@@ -39,6 +39,32 @@ static inline uint32_t GetWaterRegionMapSizeY() { return MapSizeY() / WATER_REGI
static inline TWaterRegionIndex GetWaterRegionIndex(uint32_t region_x, uint32_t region_y) { return GetWaterRegionMapSizeX() * region_y + region_x; } static inline TWaterRegionIndex GetWaterRegionIndex(uint32_t region_x, uint32_t region_y) { return GetWaterRegionMapSizeX() * region_y + region_x; }
static inline TWaterRegionIndex GetWaterRegionIndex(TileIndex tile) { return GetWaterRegionIndex(GetWaterRegionX(tile), GetWaterRegionY(tile)); } static inline TWaterRegionIndex GetWaterRegionIndex(TileIndex tile) { return GetWaterRegionIndex(GetWaterRegionX(tile), GetWaterRegionY(tile)); }
struct WaterRegionTileIterator {
uint32_t x;
uint32_t y;
inline operator TileIndex () const
{
return TileXY(this->x, this->y);
}
inline TileIndex operator *() const
{
return TileXY(this->x, this->y);
}
WaterRegionTileIterator& operator ++()
{
this->x++;
if ((this->x & WATER_REGION_EDGE_MASK) == 0) {
/* reached end of row */
this->x -= WATER_REGION_EDGE_LENGTH;
this->y++;
}
return *this;
}
};
/** /**
* Represents a square section of the map of a fixed size. Within this square individual unconnected patches of water are * Represents a square section of the map of a fixed size. Within this square individual unconnected patches of water are
* identified using a Connected Component Labeling (CCL) algorithm. Note that all information stored in this class applies * identified using a Connected Component Labeling (CCL) algorithm. Note that all information stored in this class applies
@@ -49,12 +75,21 @@ class WaterRegion
{ {
private: private:
std::array<TWaterRegionTraversabilityBits, DIAGDIR_END> edge_traversability_bits{}; std::array<TWaterRegionTraversabilityBits, DIAGDIR_END> edge_traversability_bits{};
const uint32_t tile_x;
const uint32_t tile_y;
bool has_cross_region_aqueducts = false; bool has_cross_region_aqueducts = false;
TWaterRegionPatchLabel number_of_patches = 0; // 0 = no water, 1 = one single patch of water, etc... TWaterRegionPatchLabel number_of_patches = 0; // 0 = no water, 1 = one single patch of water, etc...
const OrthogonalTileArea tile_area;
std::array<TWaterRegionPatchLabel, WATER_REGION_NUMBER_OF_TILES> tile_patch_labels{}; std::array<TWaterRegionPatchLabel, WATER_REGION_NUMBER_OF_TILES> tile_patch_labels{};
bool initialized = false; bool initialized = false;
inline bool ContainsTile(TileIndex tile) const
{
const uint32_t x = TileX(tile);
const uint32_t y = TileY(tile);
return x >= this->tile_x && x < this->tile_x + WATER_REGION_EDGE_LENGTH
&& y >= this->tile_y && y < this->tile_y + WATER_REGION_EDGE_LENGTH;
}
/** /**
* Returns the local index of the tile within the region. The N corner represents 0, * Returns the local index of the tile within the region. The N corner represents 0,
* the x direction is positive in the SW direction, and Y is positive in the SE direction. * the x direction is positive in the SW direction, and Y is positive in the SE direction.
@@ -63,17 +98,17 @@ private:
*/ */
inline int GetLocalIndex(TileIndex tile) const inline int GetLocalIndex(TileIndex tile) const
{ {
assert(this->tile_area.Contains(tile)); assert(this->ContainsTile(tile));
return (TileX(tile) - TileX(this->tile_area.tile)) + WATER_REGION_EDGE_LENGTH * (TileY(tile) - TileY(this->tile_area.tile)); return (TileX(tile) - this->tile_x) + WATER_REGION_EDGE_LENGTH * (TileY(tile) - this->tile_y);
} }
public: public:
WaterRegion(uint32_t region_x, uint32_t region_y) WaterRegion(uint32_t region_x, uint32_t region_y)
: tile_area(TileXY(region_x * WATER_REGION_EDGE_LENGTH, region_y * WATER_REGION_EDGE_LENGTH), WATER_REGION_EDGE_LENGTH, WATER_REGION_EDGE_LENGTH) : tile_x(region_x * WATER_REGION_EDGE_LENGTH), tile_y(region_y * WATER_REGION_EDGE_LENGTH)
{} {}
OrthogonalTileIterator begin() const { return this->tile_area.begin(); } WaterRegionTileIterator begin() const { return { this->tile_x, this->tile_y }; }
OrthogonalTileIterator end() const { return this->tile_area.end(); } WaterRegionTileIterator end() const { return { this->tile_x, this->tile_y + WATER_REGION_EDGE_LENGTH }; }
bool IsInitialized() const { return this->initialized; } bool IsInitialized() const { return this->initialized; }
@@ -106,7 +141,7 @@ public:
*/ */
TWaterRegionPatchLabel GetLabel(TileIndex tile) const TWaterRegionPatchLabel GetLabel(TileIndex tile) const
{ {
assert(this->tile_area.Contains(tile)); assert(this->ContainsTile(tile));
return this->tile_patch_labels[GetLocalIndex(tile)]; return this->tile_patch_labels[GetLocalIndex(tile)];
} }
@@ -120,26 +155,23 @@ public:
this->tile_patch_labels.fill(INVALID_WATER_REGION_PATCH); this->tile_patch_labels.fill(INVALID_WATER_REGION_PATCH);
for (const TileIndex tile : this->tile_area) {
if (IsAqueductTile(tile)) {
const TileIndex other_aqueduct_end = GetOtherBridgeEnd(tile);
if (!tile_area.Contains(other_aqueduct_end)) {
this->has_cross_region_aqueducts = true;
break;
}
}
}
TWaterRegionPatchLabel current_label = 1; TWaterRegionPatchLabel current_label = 1;
TWaterRegionPatchLabel highest_assigned_label = 0; TWaterRegionPatchLabel highest_assigned_label = 0;
/* Perform connected component labeling. This uses a flooding algorithm that expands until no /* Perform connected component labeling. This uses a flooding algorithm that expands until no
* additional tiles can be added. Only tiles inside the water region are considered. */ * additional tiles can be added. Only tiles inside the water region are considered. */
for (const TileIndex start_tile : tile_area) { for (const TileIndex start_tile : *this) {
static std::vector<TileIndex> tiles_to_check; static std::vector<TileIndex> tiles_to_check;
tiles_to_check.clear(); tiles_to_check.clear();
tiles_to_check.push_back(start_tile); tiles_to_check.push_back(start_tile);
if (!this->has_cross_region_aqueducts && IsAqueductTile(start_tile)) {
const TileIndex other_aqueduct_end = GetOtherBridgeEnd(start_tile);
if (!this->ContainsTile(other_aqueduct_end)) {
this->has_cross_region_aqueducts = true;
}
}
bool increase_label = false; bool increase_label = false;
while (!tiles_to_check.empty()) { while (!tiles_to_check.empty()) {
const TileIndex tile = tiles_to_check.back(); const TileIndex tile = tiles_to_check.back();
@@ -157,7 +189,7 @@ public:
for (const Trackdir dir : SetTrackdirBitIterator(valid_dirs)) { for (const Trackdir dir : SetTrackdirBitIterator(valid_dirs)) {
/* By using a TrackFollower we "play by the same rules" as the actual ship pathfinder */ /* By using a TrackFollower we "play by the same rules" as the actual ship pathfinder */
CFollowTrackWater ft; CFollowTrackWater ft;
if (ft.Follow(tile, dir) && this->tile_area.Contains(ft.m_new_tile)) tiles_to_check.push_back(ft.m_new_tile); if (ft.Follow(tile, dir) && this->ContainsTile(ft.m_new_tile)) tiles_to_check.push_back(ft.m_new_tile);
} }
} }
@@ -170,8 +202,8 @@ public:
/* Calculate the traversability (whether the tile can be entered / exited) for all edges. Note that /* Calculate the traversability (whether the tile can be entered / exited) for all edges. Note that
* we always follow the same X and Y scanning direction, this is important for comparisons later on! */ * we always follow the same X and Y scanning direction, this is important for comparisons later on! */
this->edge_traversability_bits.fill(0); this->edge_traversability_bits.fill(0);
const uint32_t top_x = TileX(tile_area.tile); const uint32_t top_x = this->tile_x;
const uint32_t top_y = TileY(tile_area.tile); const uint32_t top_y = this->tile_y;
for (uint32_t i = 0; i < WATER_REGION_EDGE_LENGTH; ++i) { for (uint32_t i = 0; i < WATER_REGION_EDGE_LENGTH; ++i) {
if (GetWaterTracks(TileXY(top_x + i, top_y)) & TRACK_BIT_3WAY_NW) SetBit(this->edge_traversability_bits[DIAGDIR_NW], i); // NW edge if (GetWaterTracks(TileXY(top_x + i, top_y)) & TRACK_BIT_3WAY_NW) SetBit(this->edge_traversability_bits[DIAGDIR_NW], i); // NW edge
if (GetWaterTracks(TileXY(top_x + i, top_y + WATER_REGION_EDGE_LENGTH - 1)) & TRACK_BIT_3WAY_SE) SetBit(this->edge_traversability_bits[DIAGDIR_SE], i); // SE edge if (GetWaterTracks(TileXY(top_x + i, top_y + WATER_REGION_EDGE_LENGTH - 1)) & TRACK_BIT_3WAY_SE) SetBit(this->edge_traversability_bits[DIAGDIR_SE], i); // SE edge

View File

@@ -21,6 +21,8 @@ using TWaterRegionIndex = uint32_t;
constexpr uint32_t WATER_REGION_EDGE_LENGTH = 16; constexpr uint32_t WATER_REGION_EDGE_LENGTH = 16;
constexpr uint32_t WATER_REGION_NUMBER_OF_TILES = WATER_REGION_EDGE_LENGTH * WATER_REGION_EDGE_LENGTH; constexpr uint32_t WATER_REGION_NUMBER_OF_TILES = WATER_REGION_EDGE_LENGTH * WATER_REGION_EDGE_LENGTH;
constexpr uint32_t WATER_REGION_EDGE_MASK = WATER_REGION_EDGE_LENGTH - 1;
static_assert((WATER_REGION_EDGE_LENGTH & WATER_REGION_EDGE_MASK) == 0);
/** /**
* Describes a single interconnected patch of water within a particular water region. * Describes a single interconnected patch of water within a particular water region.