mirror of https://github.com/OpenTTD/OpenTTD
Codechange: Reduced size of water region by not storing tile area
parent
8c7cf3bc75
commit
a0845bba2c
|
@ -40,6 +40,18 @@ static inline TWaterRegionIndex GetWaterRegionIndex(TileIndex tile) { return Get
|
||||||
|
|
||||||
using TWaterRegionPatchLabelArray = std::array<TWaterRegionPatchLabel, WATER_REGION_NUMBER_OF_TILES>;
|
using TWaterRegionPatchLabelArray = std::array<TWaterRegionPatchLabel, WATER_REGION_NUMBER_OF_TILES>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The data stored for each water region.
|
||||||
|
*/
|
||||||
|
class WaterRegionData {
|
||||||
|
friend class WaterRegion;
|
||||||
|
|
||||||
|
std::array<TWaterRegionTraversabilityBits, DIAGDIR_END> edge_traversability_bits{};
|
||||||
|
std::unique_ptr<TWaterRegionPatchLabelArray> tile_patch_labels; // Tile patch labels, this may be nullptr in the following trivial cases: region is invalid, region is only land (0 patches), region is only water (1 patch)
|
||||||
|
bool has_cross_region_aqueducts = false;
|
||||||
|
TWaterRegionPatchLabel number_of_patches = 0; // 0 = no water, 1 = one single patch of water, etc...
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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,11 +61,8 @@ using TWaterRegionPatchLabelArray = std::array<TWaterRegionPatchLabel, WATER_REG
|
||||||
class WaterRegion
|
class WaterRegion
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
std::array<TWaterRegionTraversabilityBits, DIAGDIR_END> edge_traversability_bits{};
|
WaterRegionData &data;
|
||||||
bool has_cross_region_aqueducts = false;
|
|
||||||
TWaterRegionPatchLabel number_of_patches = 0; // 0 = no water, 1 = one single patch of water, etc...
|
|
||||||
const OrthogonalTileArea tile_area;
|
const OrthogonalTileArea tile_area;
|
||||||
std::unique_ptr<TWaterRegionPatchLabelArray> tile_patch_labels; ///< Tile patch labels, this may be nullptr in the following trivial cases: region is invalid, region is only land (0 patches), region is only water (1 patch)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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,
|
||||||
|
@ -68,8 +77,9 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WaterRegion(int region_x, int region_y)
|
WaterRegion(int region_x, int region_y, WaterRegionData &water_region_data)
|
||||||
: tile_area(TileXY(region_x * WATER_REGION_EDGE_LENGTH, region_y * WATER_REGION_EDGE_LENGTH), WATER_REGION_EDGE_LENGTH, WATER_REGION_EDGE_LENGTH)
|
: data(water_region_data)
|
||||||
|
, tile_area(TileXY(region_x * WATER_REGION_EDGE_LENGTH, region_y * WATER_REGION_EDGE_LENGTH), WATER_REGION_EDGE_LENGTH, WATER_REGION_EDGE_LENGTH)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
OrthogonalTileIterator begin() const { return this->tile_area.begin(); }
|
OrthogonalTileIterator begin() const { return this->tile_area.begin(); }
|
||||||
|
@ -82,18 +92,18 @@ public:
|
||||||
* @param side Which side of the region we want to know the edge traversability of.
|
* @param side Which side of the region we want to know the edge traversability of.
|
||||||
* @returns A value holding the edge traversability bits.
|
* @returns A value holding the edge traversability bits.
|
||||||
*/
|
*/
|
||||||
TWaterRegionTraversabilityBits GetEdgeTraversabilityBits(DiagDirection side) const { return edge_traversability_bits[side]; }
|
TWaterRegionTraversabilityBits GetEdgeTraversabilityBits(DiagDirection side) const { return this->data.edge_traversability_bits[side]; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns The amount of individual water patches present within the water region. A value of
|
* @returns The amount of individual water patches present within the water region. A value of
|
||||||
* 0 means there is no water present in the water region at all.
|
* 0 means there is no water present in the water region at all.
|
||||||
*/
|
*/
|
||||||
int NumberOfPatches() const { return this->number_of_patches; }
|
int NumberOfPatches() const { return this->data.number_of_patches; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns Whether the water region contains aqueducts that cross the region boundaries.
|
* @returns Whether the water region contains aqueducts that cross the region boundaries.
|
||||||
*/
|
*/
|
||||||
bool HasCrossRegionAqueducts() const { return this->has_cross_region_aqueducts; }
|
bool HasCrossRegionAqueducts() const { return this->data.has_cross_region_aqueducts; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the patch label that was assigned to the tile.
|
* Returns the patch label that was assigned to the tile.
|
||||||
|
@ -103,10 +113,10 @@ public:
|
||||||
TWaterRegionPatchLabel GetLabel(TileIndex tile) const
|
TWaterRegionPatchLabel GetLabel(TileIndex tile) const
|
||||||
{
|
{
|
||||||
assert(this->tile_area.Contains(tile));
|
assert(this->tile_area.Contains(tile));
|
||||||
if (this->tile_patch_labels == nullptr) {
|
if (this->data.tile_patch_labels == nullptr) {
|
||||||
return this->NumberOfPatches() == 0 ? INVALID_WATER_REGION_PATCH : 1;
|
return this->NumberOfPatches() == 0 ? INVALID_WATER_REGION_PATCH : 1;
|
||||||
}
|
}
|
||||||
return (*this->tile_patch_labels)[GetLocalIndex(tile)];
|
return (*this->data.tile_patch_labels)[GetLocalIndex(tile)];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -116,15 +126,15 @@ public:
|
||||||
void ForceUpdate()
|
void ForceUpdate()
|
||||||
{
|
{
|
||||||
Debug(map, 3, "Updating water region ({},{})", GetWaterRegionX(this->tile_area.tile), GetWaterRegionY(this->tile_area.tile));
|
Debug(map, 3, "Updating water region ({},{})", GetWaterRegionX(this->tile_area.tile), GetWaterRegionY(this->tile_area.tile));
|
||||||
this->has_cross_region_aqueducts = false;
|
this->data.has_cross_region_aqueducts = false;
|
||||||
|
|
||||||
/* Acquire a tile patch label array if this region does not already have one */
|
/* Acquire a tile patch label array if this region does not already have one */
|
||||||
if (this->tile_patch_labels == nullptr) {
|
if (this->data.tile_patch_labels == nullptr) {
|
||||||
this->tile_patch_labels = std::make_unique<TWaterRegionPatchLabelArray>();
|
this->data.tile_patch_labels = std::make_unique<TWaterRegionPatchLabelArray>();
|
||||||
}
|
}
|
||||||
|
|
||||||
this->tile_patch_labels->fill(INVALID_WATER_REGION_PATCH);
|
this->data.tile_patch_labels->fill(INVALID_WATER_REGION_PATCH);
|
||||||
this->edge_traversability_bits.fill(0);
|
this->data.edge_traversability_bits.fill(0);
|
||||||
|
|
||||||
TWaterRegionPatchLabel current_label = 1;
|
TWaterRegionPatchLabel current_label = 1;
|
||||||
TWaterRegionPatchLabel highest_assigned_label = 0;
|
TWaterRegionPatchLabel highest_assigned_label = 0;
|
||||||
|
@ -144,7 +154,7 @@ public:
|
||||||
const TrackdirBits valid_dirs = TrackBitsToTrackdirBits(GetWaterTracks(tile));
|
const TrackdirBits valid_dirs = TrackBitsToTrackdirBits(GetWaterTracks(tile));
|
||||||
if (valid_dirs == TRACKDIR_BIT_NONE) continue;
|
if (valid_dirs == TRACKDIR_BIT_NONE) continue;
|
||||||
|
|
||||||
TWaterRegionPatchLabel &tile_patch = (*this->tile_patch_labels)[GetLocalIndex(tile)];
|
TWaterRegionPatchLabel &tile_patch = (*this->data.tile_patch_labels)[GetLocalIndex(tile)];
|
||||||
if (tile_patch != INVALID_WATER_REGION_PATCH) continue;
|
if (tile_patch != INVALID_WATER_REGION_PATCH) continue;
|
||||||
|
|
||||||
tile_patch = current_label;
|
tile_patch = current_label;
|
||||||
|
@ -161,9 +171,9 @@ public:
|
||||||
assert(DistanceManhattan(ft.m_new_tile, tile) == 1);
|
assert(DistanceManhattan(ft.m_new_tile, tile) == 1);
|
||||||
const auto side = DiagdirBetweenTiles(tile, ft.m_new_tile);
|
const auto side = DiagdirBetweenTiles(tile, ft.m_new_tile);
|
||||||
const int local_x_or_y = DiagDirToAxis(side) == AXIS_X ? TileY(tile) - TileY(this->tile_area.tile) : TileX(tile) - TileX(this->tile_area.tile);
|
const int local_x_or_y = DiagDirToAxis(side) == AXIS_X ? TileY(tile) - TileY(this->tile_area.tile) : TileX(tile) - TileX(this->tile_area.tile);
|
||||||
SetBit(this->edge_traversability_bits[side], local_x_or_y);
|
SetBit(this->data.edge_traversability_bits[side], local_x_or_y);
|
||||||
} else {
|
} else {
|
||||||
this->has_cross_region_aqueducts = true;
|
this->data.has_cross_region_aqueducts = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -172,12 +182,12 @@ public:
|
||||||
if (increase_label) current_label++;
|
if (increase_label) current_label++;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->number_of_patches = highest_assigned_label;
|
this->data.number_of_patches = highest_assigned_label;
|
||||||
|
|
||||||
if (this->number_of_patches == 0 || (this->number_of_patches == 1 &&
|
if (this->data.number_of_patches == 0 || (this->data.number_of_patches == 1 &&
|
||||||
std::all_of(this->tile_patch_labels->begin(), this->tile_patch_labels->end(), [](TWaterRegionPatchLabel label) { return label == 1; }))) {
|
std::all_of(this->data.tile_patch_labels->begin(), this->data.tile_patch_labels->end(), [](TWaterRegionPatchLabel label) { return label == 1; }))) {
|
||||||
/* No need for patch storage: trivial cases */
|
/* No need for patch storage: trivial cases */
|
||||||
this->tile_patch_labels.reset();
|
this->data.tile_patch_labels.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,10 +195,10 @@ public:
|
||||||
{
|
{
|
||||||
Debug(map, 9, "Water region {},{} labels and edge traversability = ...", GetWaterRegionX(tile_area.tile), GetWaterRegionY(tile_area.tile));
|
Debug(map, 9, "Water region {},{} labels and edge traversability = ...", GetWaterRegionX(tile_area.tile), GetWaterRegionY(tile_area.tile));
|
||||||
|
|
||||||
const size_t max_element_width = std::to_string(this->number_of_patches).size();
|
const size_t max_element_width = std::to_string(this->data.number_of_patches).size();
|
||||||
|
|
||||||
std::array<int, 16> traversability_NW{0};
|
std::array<int, 16> traversability_NW{0};
|
||||||
for (auto bitIndex : SetBitIterator(edge_traversability_bits[DIAGDIR_NW])) *(traversability_NW.rbegin() + bitIndex) = 1;
|
for (auto bitIndex : SetBitIterator(this->data.edge_traversability_bits[DIAGDIR_NW])) *(traversability_NW.rbegin() + bitIndex) = 1;
|
||||||
Debug(map, 9, " {:{}}", fmt::join(traversability_NW, " "), max_element_width);
|
Debug(map, 9, " {:{}}", fmt::join(traversability_NW, " "), max_element_width);
|
||||||
Debug(map, 9, " +{:->{}}+", "", WATER_REGION_EDGE_LENGTH * (max_element_width + 1) + 1);
|
Debug(map, 9, " +{:->{}}+", "", WATER_REGION_EDGE_LENGTH * (max_element_width + 1) + 1);
|
||||||
|
|
||||||
|
@ -199,17 +209,17 @@ public:
|
||||||
const std::string label_str = label == INVALID_WATER_REGION_PATCH ? "." : std::to_string(label);
|
const std::string label_str = label == INVALID_WATER_REGION_PATCH ? "." : std::to_string(label);
|
||||||
line = fmt::format("{:{}}", label_str, max_element_width) + " " + line;
|
line = fmt::format("{:{}}", label_str, max_element_width) + " " + line;
|
||||||
}
|
}
|
||||||
Debug(map, 9, "{} | {}| {}", GB(this->edge_traversability_bits[DIAGDIR_SW], y, 1), line, GB(this->edge_traversability_bits[DIAGDIR_NE], y, 1));
|
Debug(map, 9, "{} | {}| {}", GB(this->data.edge_traversability_bits[DIAGDIR_SW], y, 1), line, GB(this->data.edge_traversability_bits[DIAGDIR_NE], y, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug(map, 9, " +{:->{}}+", "", WATER_REGION_EDGE_LENGTH * (max_element_width + 1) + 1);
|
Debug(map, 9, " +{:->{}}+", "", WATER_REGION_EDGE_LENGTH * (max_element_width + 1) + 1);
|
||||||
std::array<int, 16> traversability_SE{0};
|
std::array<int, 16> traversability_SE{0};
|
||||||
for (auto bitIndex : SetBitIterator(edge_traversability_bits[DIAGDIR_SE])) *(traversability_SE.rbegin() + bitIndex) = 1;
|
for (auto bitIndex : SetBitIterator(this->data.edge_traversability_bits[DIAGDIR_SE])) *(traversability_SE.rbegin() + bitIndex) = 1;
|
||||||
Debug(map, 9, " {:{}}", fmt::join(traversability_SE, " "), max_element_width);
|
Debug(map, 9, " {:{}}", fmt::join(traversability_SE, " "), max_element_width);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<WaterRegion> _water_regions;
|
std::vector<WaterRegionData> _water_region_data;
|
||||||
std::vector<bool> _is_water_region_valid;
|
std::vector<bool> _is_water_region_valid;
|
||||||
|
|
||||||
TileIndex GetTileIndexFromLocalCoordinate(int region_x, int region_y, int local_x, int local_y)
|
TileIndex GetTileIndexFromLocalCoordinate(int region_x, int region_y, int local_x, int local_y)
|
||||||
|
@ -231,10 +241,10 @@ TileIndex GetEdgeTileCoordinate(int region_x, int region_y, DiagDirection side,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WaterRegion &GetUpdatedWaterRegion(uint16_t region_x, uint16_t region_y)
|
WaterRegion GetUpdatedWaterRegion(uint16_t region_x, uint16_t region_y)
|
||||||
{
|
{
|
||||||
const int index = GetWaterRegionIndex(region_x, region_y);
|
const int index = GetWaterRegionIndex(region_x, region_y);
|
||||||
auto &water_region = _water_regions[index];
|
WaterRegion water_region(region_x, region_y, _water_region_data[index]);
|
||||||
if (!_is_water_region_valid[index]) {
|
if (!_is_water_region_valid[index]) {
|
||||||
water_region.ForceUpdate();
|
water_region.ForceUpdate();
|
||||||
_is_water_region_valid[index] = true;
|
_is_water_region_valid[index] = true;
|
||||||
|
@ -242,7 +252,7 @@ WaterRegion &GetUpdatedWaterRegion(uint16_t region_x, uint16_t region_y)
|
||||||
return water_region;
|
return water_region;
|
||||||
}
|
}
|
||||||
|
|
||||||
WaterRegion &GetUpdatedWaterRegion(TileIndex tile)
|
WaterRegion GetUpdatedWaterRegion(TileIndex tile)
|
||||||
{
|
{
|
||||||
return GetUpdatedWaterRegion(GetWaterRegionX(tile), GetWaterRegionY(tile));
|
return GetUpdatedWaterRegion(GetWaterRegionX(tile), GetWaterRegionY(tile));
|
||||||
}
|
}
|
||||||
|
@ -290,7 +300,7 @@ WaterRegionDesc GetWaterRegionInfo(TileIndex tile)
|
||||||
*/
|
*/
|
||||||
WaterRegionPatchDesc GetWaterRegionPatchInfo(TileIndex tile)
|
WaterRegionPatchDesc GetWaterRegionPatchInfo(TileIndex tile)
|
||||||
{
|
{
|
||||||
WaterRegion ®ion = GetUpdatedWaterRegion(tile);
|
const WaterRegion region = GetUpdatedWaterRegion(tile);
|
||||||
return WaterRegionPatchDesc{ GetWaterRegionX(tile), GetWaterRegionY(tile), region.GetLabel(tile)};
|
return WaterRegionPatchDesc{ GetWaterRegionX(tile), GetWaterRegionY(tile), region.GetLabel(tile)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,7 +340,7 @@ static inline void VisitAdjacentWaterRegionPatchNeighbors(const WaterRegionPatch
|
||||||
{
|
{
|
||||||
if (water_region_patch.label == INVALID_WATER_REGION_PATCH) return;
|
if (water_region_patch.label == INVALID_WATER_REGION_PATCH) return;
|
||||||
|
|
||||||
const WaterRegion ¤t_region = GetUpdatedWaterRegion(water_region_patch.x, water_region_patch.y);
|
const WaterRegion current_region = GetUpdatedWaterRegion(water_region_patch.x, water_region_patch.y);
|
||||||
|
|
||||||
const TileIndexDiffC offset = TileIndexDiffCByDiagDir(side);
|
const TileIndexDiffC offset = TileIndexDiffCByDiagDir(side);
|
||||||
const int nx = water_region_patch.x + offset.x;
|
const int nx = water_region_patch.x + offset.x;
|
||||||
|
@ -338,7 +348,7 @@ static inline void VisitAdjacentWaterRegionPatchNeighbors(const WaterRegionPatch
|
||||||
|
|
||||||
if (nx < 0 || ny < 0 || nx >= GetWaterRegionMapSizeX() || ny >= GetWaterRegionMapSizeY()) return;
|
if (nx < 0 || ny < 0 || nx >= GetWaterRegionMapSizeX() || ny >= GetWaterRegionMapSizeY()) return;
|
||||||
|
|
||||||
const WaterRegion &neighboring_region = GetUpdatedWaterRegion(nx, ny);
|
const WaterRegion neighboring_region = GetUpdatedWaterRegion(nx, ny);
|
||||||
const DiagDirection opposite_side = ReverseDiagDir(side);
|
const DiagDirection opposite_side = ReverseDiagDir(side);
|
||||||
|
|
||||||
/* Indicates via which local x or y coordinates (depends on the "side" parameter) we can cross over into the adjacent region. */
|
/* Indicates via which local x or y coordinates (depends on the "side" parameter) we can cross over into the adjacent region. */
|
||||||
|
@ -379,7 +389,7 @@ void VisitWaterRegionPatchNeighbors(const WaterRegionPatchDesc &water_region_pat
|
||||||
{
|
{
|
||||||
if (water_region_patch.label == INVALID_WATER_REGION_PATCH) return;
|
if (water_region_patch.label == INVALID_WATER_REGION_PATCH) return;
|
||||||
|
|
||||||
const WaterRegion ¤t_region = GetUpdatedWaterRegion(water_region_patch.x, water_region_patch.y);
|
const WaterRegion current_region = GetUpdatedWaterRegion(water_region_patch.x, water_region_patch.y);
|
||||||
|
|
||||||
/* Visit adjacent water region patches in each cardinal direction */
|
/* Visit adjacent water region patches in each cardinal direction */
|
||||||
for (DiagDirection side = DIAGDIR_BEGIN; side < DIAGDIR_END; side++) VisitAdjacentWaterRegionPatchNeighbors(water_region_patch, side, callback);
|
for (DiagDirection side = DIAGDIR_BEGIN; side < DIAGDIR_END; side++) VisitAdjacentWaterRegionPatchNeighbors(water_region_patch, side, callback);
|
||||||
|
@ -402,21 +412,14 @@ void AllocateWaterRegions()
|
||||||
{
|
{
|
||||||
const int number_of_regions = GetWaterRegionMapSizeX() * GetWaterRegionMapSizeY();
|
const int number_of_regions = GetWaterRegionMapSizeX() * GetWaterRegionMapSizeY();
|
||||||
|
|
||||||
_water_regions.clear();
|
_water_region_data.clear();
|
||||||
_water_regions.reserve(number_of_regions);
|
_water_region_data.resize(number_of_regions);
|
||||||
|
|
||||||
_is_water_region_valid.clear();
|
_is_water_region_valid.clear();
|
||||||
_is_water_region_valid.resize(number_of_regions, false);
|
_is_water_region_valid.resize(number_of_regions, false);
|
||||||
|
|
||||||
Debug(map, 2, "Allocating {} x {} water regions", GetWaterRegionMapSizeX(), GetWaterRegionMapSizeY());
|
Debug(map, 2, "Allocating {} x {} water regions", GetWaterRegionMapSizeX(), GetWaterRegionMapSizeY());
|
||||||
|
assert(_is_water_region_valid.size() == _water_region_data.size());
|
||||||
for (int region_y = 0; region_y < GetWaterRegionMapSizeY(); region_y++) {
|
|
||||||
for (int region_x = 0; region_x < GetWaterRegionMapSizeX(); region_x++) {
|
|
||||||
_water_regions.emplace_back(region_x, region_y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(_is_water_region_valid.size() == _water_regions.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrintWaterRegionDebugInfo(TileIndex tile)
|
void PrintWaterRegionDebugInfo(TileIndex tile)
|
||||||
|
|
Loading…
Reference in New Issue