23 using TWaterRegionTraversabilityBits = uint16_t;
24 constexpr TWaterRegionPatchLabel FIRST_REGION_LABEL = 1;
26 static_assert(
sizeof(TWaterRegionTraversabilityBits) * 8 == WATER_REGION_EDGE_LENGTH);
27 static_assert(
sizeof(TWaterRegionPatchLabel) ==
sizeof(uint8_t));
32 static inline int GetWaterRegionX(
TileIndex tile) {
return TileX(tile) / WATER_REGION_EDGE_LENGTH; }
33 static inline int GetWaterRegionY(
TileIndex tile) {
return TileY(tile) / WATER_REGION_EDGE_LENGTH; }
35 static inline int GetWaterRegionMapSizeX() {
return Map::SizeX() / WATER_REGION_EDGE_LENGTH; }
36 static inline int GetWaterRegionMapSizeY() {
return Map::SizeY() / WATER_REGION_EDGE_LENGTH; }
38 static inline TWaterRegionIndex
GetWaterRegionIndex(
int region_x,
int region_y) {
return GetWaterRegionMapSizeX() * region_y + region_x; }
41 using TWaterRegionPatchLabelArray = std::array<TWaterRegionPatchLabel, WATER_REGION_NUMBER_OF_TILES>;
49 std::array<TWaterRegionTraversabilityBits, DIAGDIR_END> edge_traversability_bits{};
50 std::unique_ptr<TWaterRegionPatchLabelArray> tile_patch_labels;
51 bool has_cross_region_aqueducts =
false;
52 TWaterRegionPatchLabel number_of_patches = 0;
75 assert(this->tile_area.
Contains(tile));
81 : data(water_region_data)
82 , tile_area(
TileXY(region_x * WATER_REGION_EDGE_LENGTH, region_y * WATER_REGION_EDGE_LENGTH), WATER_REGION_EDGE_LENGTH, WATER_REGION_EDGE_LENGTH)
115 assert(this->tile_area.
Contains(tile));
116 if (this->data.tile_patch_labels ==
nullptr) {
128 Debug(map, 3,
"Updating water region ({},{})", GetWaterRegionX(this->tile_area.
tile), GetWaterRegionY(this->tile_area.
tile));
129 this->data.has_cross_region_aqueducts =
false;
132 if (this->data.tile_patch_labels ==
nullptr) {
133 this->data.tile_patch_labels = std::make_unique<TWaterRegionPatchLabelArray>();
136 this->data.tile_patch_labels->fill(INVALID_WATER_REGION_PATCH);
137 this->data.edge_traversability_bits.fill(0);
139 TWaterRegionPatchLabel current_label = 1;
140 TWaterRegionPatchLabel highest_assigned_label = 0;
144 for (
const TileIndex start_tile : tile_area) {
145 static std::vector<TileIndex> tiles_to_check;
146 tiles_to_check.clear();
147 tiles_to_check.push_back(start_tile);
149 bool increase_label =
false;
150 while (!tiles_to_check.empty()) {
151 const TileIndex tile = tiles_to_check.back();
152 tiles_to_check.pop_back();
157 TWaterRegionPatchLabel &tile_patch = (*this->data.tile_patch_labels)[
GetLocalIndex(tile)];
158 if (tile_patch != INVALID_WATER_REGION_PATCH)
continue;
160 tile_patch = current_label;
161 highest_assigned_label = current_label;
162 increase_label =
true;
167 if (ft.
Follow(tile, dir)) {
168 if (this->tile_area.Contains(ft.
m_new_tile)) {
174 SetBit(this->data.edge_traversability_bits[side], local_x_or_y);
176 this->data.has_cross_region_aqueducts =
true;
182 if (increase_label) current_label++;
185 this->data.number_of_patches = highest_assigned_label;
187 if (this->data.number_of_patches == 0 || (this->data.number_of_patches == 1 &&
188 std::all_of(this->data.tile_patch_labels->begin(), this->data.tile_patch_labels->end(), [](TWaterRegionPatchLabel label) { return label == 1; }))) {
190 this->data.tile_patch_labels.reset();
194 void PrintDebugInfo()
196 Debug(map, 9,
"Water region {},{} labels and edge traversability = ...", GetWaterRegionX(tile_area.
tile), GetWaterRegionY(tile_area.
tile));
198 const size_t max_element_width = std::to_string(this->data.number_of_patches).size();
200 std::array<int, 16> traversability_NW{0};
201 for (
auto bitIndex :
SetBitIterator(this->data.edge_traversability_bits[
DIAGDIR_NW])) *(traversability_NW.rbegin() + bitIndex) = 1;
202 Debug(map, 9,
" {:{}}", fmt::join(traversability_NW,
" "), max_element_width);
203 Debug(map, 9,
" +{:->{}}+",
"", WATER_REGION_EDGE_LENGTH * (max_element_width + 1) + 1);
205 for (
int y = 0; y < WATER_REGION_EDGE_LENGTH; ++y) {
207 for (
int x = 0; x < WATER_REGION_EDGE_LENGTH; ++x) {
209 const std::string label_str = label == INVALID_WATER_REGION_PATCH ?
"." : std::to_string(label);
210 line = fmt::format(
"{:{}}", label_str, max_element_width) +
" " + line;
212 Debug(map, 9,
"{} | {}| {}",
GB(this->data.edge_traversability_bits[
DIAGDIR_SW], y, 1), line,
GB(this->data.edge_traversability_bits[
DIAGDIR_NE], y, 1));
215 Debug(map, 9,
" +{:->{}}+",
"", WATER_REGION_EDGE_LENGTH * (max_element_width + 1) + 1);
216 std::array<int, 16> traversability_SE{0};
217 for (
auto bitIndex :
SetBitIterator(this->data.edge_traversability_bits[
DIAGDIR_SE])) *(traversability_SE.rbegin() + bitIndex) = 1;
218 Debug(map, 9,
" {:{}}", fmt::join(traversability_SE,
" "), max_element_width);
222 std::vector<WaterRegionData> _water_region_data;
223 std::vector<bool> _is_water_region_valid;
225 TileIndex GetTileIndexFromLocalCoordinate(
int region_x,
int region_y,
int local_x,
int local_y)
227 assert(local_x >= 0 && local_x < WATER_REGION_EDGE_LENGTH);
228 assert(local_y >= 0 && local_y < WATER_REGION_EDGE_LENGTH);
229 return TileXY(WATER_REGION_EDGE_LENGTH * region_x + local_x, WATER_REGION_EDGE_LENGTH * region_y + local_y);
234 assert(x_or_y >= 0 && x_or_y < WATER_REGION_EDGE_LENGTH);
236 case DIAGDIR_NE:
return GetTileIndexFromLocalCoordinate(region_x, region_y, 0, x_or_y);
237 case DIAGDIR_SW:
return GetTileIndexFromLocalCoordinate(region_x, region_y, WATER_REGION_EDGE_LENGTH - 1, x_or_y);
238 case DIAGDIR_NW:
return GetTileIndexFromLocalCoordinate(region_x, region_y, x_or_y, 0);
239 case DIAGDIR_SE:
return GetTileIndexFromLocalCoordinate(region_x, region_y, x_or_y, WATER_REGION_EDGE_LENGTH - 1);
240 default: NOT_REACHED();
244 WaterRegion GetUpdatedWaterRegion(uint16_t region_x, uint16_t region_y)
247 WaterRegion water_region(region_x, region_y, _water_region_data[index]);
248 if (!_is_water_region_valid[index]) {
249 water_region.ForceUpdate();
250 _is_water_region_valid[index] =
true;
257 return GetUpdatedWaterRegion(GetWaterRegionX(tile), GetWaterRegionY(tile));
285 return TileXY(water_region.
x * WATER_REGION_EDGE_LENGTH + (WATER_REGION_EDGE_LENGTH / 2), water_region.
y * WATER_REGION_EDGE_LENGTH + (WATER_REGION_EDGE_LENGTH / 2));
294 return WaterRegionDesc{ GetWaterRegionX(tile), GetWaterRegionY(tile) };
303 const WaterRegion region = GetUpdatedWaterRegion(tile);
315 auto invalidate_region = [](
TileIndex tile) {
317 if (!_is_water_region_valid[water_region_index])
Debug(map, 3,
"Invalidated water region ({},{})", GetWaterRegionX(tile), GetWaterRegionY(tile));
318 _is_water_region_valid[water_region_index] =
false;
321 invalidate_region(tile);
341 if (water_region_patch.
label == INVALID_WATER_REGION_PATCH)
return;
343 const WaterRegion current_region = GetUpdatedWaterRegion(water_region_patch.
x, water_region_patch.
y);
346 const int nx = water_region_patch.
x + offset.
x;
347 const int ny = water_region_patch.
y + offset.
y;
349 if (nx < 0 || ny < 0 || nx >= GetWaterRegionMapSizeX() || ny >= GetWaterRegionMapSizeY())
return;
351 const WaterRegion neighboring_region = GetUpdatedWaterRegion(nx, ny);
357 if (traversability_bits == 0)
return;
365 static std::vector<TWaterRegionPatchLabel> unique_labels;
366 unique_labels.clear();
367 for (
int x_or_y = 0; x_or_y < WATER_REGION_EDGE_LENGTH; ++x_or_y) {
368 if (!
HasBit(traversability_bits, x_or_y))
continue;
370 const TileIndex current_edge_tile = GetEdgeTileCoordinate(water_region_patch.
x, water_region_patch.
y, side, x_or_y);
371 const TWaterRegionPatchLabel current_label = current_region.
GetLabel(current_edge_tile);
372 if (current_label != water_region_patch.
label)
continue;
374 const TileIndex neighbor_edge_tile = GetEdgeTileCoordinate(nx, ny, opposite_side, x_or_y);
375 const TWaterRegionPatchLabel neighbor_label = neighboring_region.
GetLabel(neighbor_edge_tile);
376 assert(neighbor_label != INVALID_WATER_REGION_PATCH);
377 if (std::find(unique_labels.begin(), unique_labels.end(), neighbor_label) == unique_labels.end()) unique_labels.push_back(neighbor_label);
379 for (TWaterRegionPatchLabel unique_label : unique_labels) func(
WaterRegionPatchDesc{ nx, ny, unique_label });
390 if (water_region_patch.
label == INVALID_WATER_REGION_PATCH)
return;
392 const WaterRegion current_region = GetUpdatedWaterRegion(water_region_patch.
x, water_region_patch.
y);
399 for (
const TileIndex tile : current_region) {
413 const int number_of_regions = GetWaterRegionMapSizeX() * GetWaterRegionMapSizeY();
415 _water_region_data.clear();
416 _water_region_data.resize(number_of_regions);
418 _is_water_region_valid.clear();
419 _is_water_region_valid.resize(number_of_regions,
false);
421 Debug(map, 2,
"Allocating {} x {} water regions", GetWaterRegionMapSizeX(), GetWaterRegionMapSizeY());
422 assert(_is_water_region_valid.size() == _water_region_data.size());
425 void PrintWaterRegionDebugInfo(
TileIndex tile)
427 GetUpdatedWaterRegion(tile).PrintDebugInfo();