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(
byte));
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>;
52 std::array<TWaterRegionTraversabilityBits, DIAGDIR_END> edge_traversability_bits{};
53 bool has_cross_region_aqueducts =
false;
54 bool initialized =
false;
55 TWaterRegionPatchLabel number_of_patches = 0;
67 assert(this->tile_area.
Contains(tile));
73 : tile_area(
TileXY(region_x * WATER_REGION_EDGE_LENGTH, region_y * WATER_REGION_EDGE_LENGTH), WATER_REGION_EDGE_LENGTH, WATER_REGION_EDGE_LENGTH)
79 bool IsInitialized()
const {
return this->initialized; }
83 if (!IsInitialized())
Debug(map, 3,
"Invalidated water region ({},{})", GetWaterRegionX(this->tile_area.
tile), GetWaterRegionY(this->tile_area.
tile));
84 this->initialized =
false;
114 assert(this->tile_area.
Contains(tile));
115 if (this->tile_patch_labels ==
nullptr) {
127 Debug(map, 3,
"Updating water region ({},{})", GetWaterRegionX(this->tile_area.
tile), GetWaterRegionY(this->tile_area.
tile));
128 this->has_cross_region_aqueducts =
false;
131 if (this->tile_patch_labels ==
nullptr) {
132 this->tile_patch_labels = std::make_unique<TWaterRegionPatchLabelArray>();
135 this->tile_patch_labels->fill(INVALID_WATER_REGION_PATCH);
136 this->edge_traversability_bits.fill(0);
138 TWaterRegionPatchLabel current_label = 1;
139 TWaterRegionPatchLabel highest_assigned_label = 0;
143 for (
const TileIndex start_tile : tile_area) {
144 static std::vector<TileIndex> tiles_to_check;
145 tiles_to_check.clear();
146 tiles_to_check.push_back(start_tile);
148 bool increase_label =
false;
149 while (!tiles_to_check.empty()) {
150 const TileIndex tile = tiles_to_check.back();
151 tiles_to_check.pop_back();
157 if (tile_patch != INVALID_WATER_REGION_PATCH)
continue;
159 tile_patch = current_label;
160 highest_assigned_label = current_label;
161 increase_label =
true;
166 if (ft.
Follow(tile, dir)) {
167 if (this->tile_area.Contains(ft.
m_new_tile)) {
173 SetBit(this->edge_traversability_bits[side], local_x_or_y);
175 this->has_cross_region_aqueducts =
true;
181 if (increase_label) current_label++;
184 this->number_of_patches = highest_assigned_label;
185 this->initialized =
true;
187 if (this->number_of_patches == 0 || (this->number_of_patches == 1 &&
188 std::all_of(this->tile_patch_labels->begin(), this->tile_patch_labels->end(), [](TWaterRegionPatchLabel label) { return label == 1; }))) {
190 this->tile_patch_labels.reset();
202 void PrintDebugInfo()
204 Debug(map, 9,
"Water region {},{} labels and edge traversability = ...", GetWaterRegionX(tile_area.
tile), GetWaterRegionY(tile_area.
tile));
206 const size_t max_element_width = std::to_string(this->number_of_patches).size();
208 std::array<int, 16> traversability_NW{0};
209 for (
auto bitIndex :
SetBitIterator(edge_traversability_bits[
DIAGDIR_NW])) *(traversability_NW.rbegin() + bitIndex) = 1;
210 Debug(map, 9,
" {:{}}", fmt::join(traversability_NW,
" "), max_element_width);
211 Debug(map, 9,
" +{:->{}}+",
"", WATER_REGION_EDGE_LENGTH * (max_element_width + 1) + 1);
213 for (
int y = 0; y < WATER_REGION_EDGE_LENGTH; ++y) {
215 for (
int x = 0; x < WATER_REGION_EDGE_LENGTH; ++x) {
217 const std::string label_str = label == INVALID_WATER_REGION_PATCH ?
"." : std::to_string(label);
218 line = fmt::format(
"{:{}}", label_str, max_element_width) +
" " + line;
220 Debug(map, 9,
"{} | {}| {}",
GB(this->edge_traversability_bits[
DIAGDIR_SW], y, 1), line,
GB(this->edge_traversability_bits[
DIAGDIR_NE], y, 1));
223 Debug(map, 9,
" +{:->{}}+",
"", WATER_REGION_EDGE_LENGTH * (max_element_width + 1) + 1);
224 std::array<int, 16> traversability_SE{0};
225 for (
auto bitIndex :
SetBitIterator(edge_traversability_bits[
DIAGDIR_SE])) *(traversability_SE.rbegin() + bitIndex) = 1;
226 Debug(map, 9,
" {:{}}", fmt::join(traversability_SE,
" "), max_element_width);
230 std::vector<WaterRegion> _water_regions;
232 TileIndex GetTileIndexFromLocalCoordinate(
int region_x,
int region_y,
int local_x,
int local_y)
234 assert(local_x >= 0 && local_x < WATER_REGION_EDGE_LENGTH);
235 assert(local_y >= 0 && local_y < WATER_REGION_EDGE_LENGTH);
236 return TileXY(WATER_REGION_EDGE_LENGTH * region_x + local_x, WATER_REGION_EDGE_LENGTH * region_y + local_y);
241 assert(x_or_y >= 0 && x_or_y < WATER_REGION_EDGE_LENGTH);
243 case DIAGDIR_NE:
return GetTileIndexFromLocalCoordinate(region_x, region_y, 0, x_or_y);
244 case DIAGDIR_SW:
return GetTileIndexFromLocalCoordinate(region_x, region_y, WATER_REGION_EDGE_LENGTH - 1, x_or_y);
245 case DIAGDIR_NW:
return GetTileIndexFromLocalCoordinate(region_x, region_y, x_or_y, 0);
246 case DIAGDIR_SE:
return GetTileIndexFromLocalCoordinate(region_x, region_y, x_or_y, WATER_REGION_EDGE_LENGTH - 1);
247 default: NOT_REACHED();
251 WaterRegion &GetUpdatedWaterRegion(uint16_t region_x, uint16_t region_y)
254 result.UpdateIfNotInitialized();
261 result.UpdateIfNotInitialized();
290 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));
299 return WaterRegionDesc{ GetWaterRegionX(tile), GetWaterRegionY(tile) };
320 _water_regions[water_region_index].Invalidate();
327 if (adjacent_region_index != water_region_index) _water_regions[adjacent_region_index].Invalidate();
340 if (water_region_patch.
label == INVALID_WATER_REGION_PATCH)
return;
342 const WaterRegion ¤t_region = GetUpdatedWaterRegion(water_region_patch.
x, water_region_patch.
y);
345 const int nx = water_region_patch.
x + offset.
x;
346 const int ny = water_region_patch.
y + offset.
y;
348 if (nx < 0 || ny < 0 || nx >= GetWaterRegionMapSizeX() || ny >= GetWaterRegionMapSizeY())
return;
350 const WaterRegion &neighboring_region = GetUpdatedWaterRegion(nx, ny);
356 if (traversability_bits == 0)
return;
364 static std::vector<TWaterRegionPatchLabel> unique_labels;
365 unique_labels.clear();
366 for (
int x_or_y = 0; x_or_y < WATER_REGION_EDGE_LENGTH; ++x_or_y) {
367 if (!
HasBit(traversability_bits, x_or_y))
continue;
369 const TileIndex current_edge_tile = GetEdgeTileCoordinate(water_region_patch.
x, water_region_patch.
y, side, x_or_y);
370 const TWaterRegionPatchLabel current_label = current_region.
GetLabel(current_edge_tile);
371 if (current_label != water_region_patch.
label)
continue;
373 const TileIndex neighbor_edge_tile = GetEdgeTileCoordinate(nx, ny, opposite_side, x_or_y);
374 const TWaterRegionPatchLabel neighbor_label = neighboring_region.
GetLabel(neighbor_edge_tile);
375 assert(neighbor_label != INVALID_WATER_REGION_PATCH);
376 if (std::find(unique_labels.begin(), unique_labels.end(), neighbor_label) == unique_labels.end()) unique_labels.push_back(neighbor_label);
378 for (TWaterRegionPatchLabel unique_label : unique_labels) func(
WaterRegionPatchDesc{ nx, ny, unique_label });
389 if (water_region_patch.
label == INVALID_WATER_REGION_PATCH)
return;
391 const WaterRegion ¤t_region = GetUpdatedWaterRegion(water_region_patch.
x, water_region_patch.
y);
398 for (
const TileIndex tile : current_region) {
412 _water_regions.clear();
413 _water_regions.reserve(
static_cast<size_t>(GetWaterRegionMapSizeX()) * GetWaterRegionMapSizeY());
415 Debug(map, 2,
"Allocating {} x {} water regions", GetWaterRegionMapSizeX(), GetWaterRegionMapSizeY());
417 for (
int region_y = 0; region_y < GetWaterRegionMapSizeY(); region_y++) {
418 for (
int region_x = 0; region_x < GetWaterRegionMapSizeX(); region_x++) {
419 _water_regions.emplace_back(region_x, region_y);
424 void PrintWaterRegionDebugInfo(
TileIndex tile)
426 GetUpdatedWaterRegion(tile).PrintDebugInfo();