instructed/tilemap/src/pass/mountain_hole_fill.cpp
szdytom 5ea6d46a9f
refactor: re-organize tilemap library structure
Signed-off-by: szdytom <szdytom@qq.com>
2025-08-03 15:22:33 +08:00

119 lines
3.3 KiB
C++

#include "tilemap/pass/mountain_hole_fill.h"
#include "tilemap/generation.h"
#include <algorithm>
#include <queue>
namespace istd {
MountainHoleFillPass::MountainHoleFillPass(const GenerationConfig &config)
: config_(config) {}
void MountainHoleFillPass::operator()(TileMap &tilemap) {
std::uint8_t map_size = tilemap.get_size();
std::uint32_t total_tiles = map_size * Chunk::size;
// Create visited array for the entire map
std::vector<std::vector<bool>> visited(
total_tiles, std::vector<bool>(total_tiles, false)
);
// Process all tiles in the map
for (std::uint8_t chunk_x = 0; chunk_x < map_size; ++chunk_x) {
for (std::uint8_t chunk_y = 0; chunk_y < map_size; ++chunk_y) {
for (std::uint8_t local_x = 0; local_x < Chunk::size; ++local_x) {
for (std::uint8_t local_y = 0; local_y < Chunk::size;
++local_y) {
TilePos pos{chunk_x, chunk_y, local_x, local_y};
auto [global_x, global_y] = pos.to_global();
// Skip if already visited
if (visited[global_x][global_y]) {
continue;
}
const Tile &tile = tilemap.get_tile(pos);
// Only process passable tiles
if (!is_passable(tile.base)) {
visited[global_x][global_y] = true;
continue;
}
// Find connected component
std::vector<TilePos> component_positions;
std::uint32_t component_size = bfs_component_size(
tilemap, pos, visited, component_positions
);
// Check if this component touches the boundary
auto boundary_checker =
[&tilemap](const TilePos &component_pos) {
return tilemap.is_at_boundary(component_pos);
};
bool touches_boundary = std::ranges::any_of(
component_positions, boundary_checker
);
// Fill small holes that don't touch the boundary
if (!touches_boundary
&& component_size <= config_.fill_threshold) {
for (const TilePos &fill_pos : component_positions) {
Tile fill_tile = tilemap.get_tile(fill_pos);
fill_tile.base = BaseTileType::Mountain;
tilemap.set_tile(fill_pos, fill_tile);
}
}
}
}
}
}
}
bool MountainHoleFillPass::is_passable(BaseTileType type) const {
return type != BaseTileType::Mountain;
}
std::uint32_t MountainHoleFillPass::bfs_component_size(
TileMap &tilemap, TilePos start_pos,
std::vector<std::vector<bool>> &visited, std::vector<TilePos> &positions
) {
std::queue<TilePos> queue;
queue.push(start_pos);
auto [start_global_x, start_global_y] = start_pos.to_global();
visited[start_global_x][start_global_y] = true;
std::uint32_t size = 0;
positions.clear();
while (!queue.empty()) {
TilePos current = queue.front();
queue.pop();
positions.push_back(current);
++size;
// Check all neighbors
std::vector<TilePos> neighbors = tilemap.get_neighbors(current);
for (const auto neighbor : neighbors) {
auto [neighbor_global_x, neighbor_global_y] = neighbor.to_global();
if (visited[neighbor_global_x][neighbor_global_y]) {
continue;
}
const Tile &neighbor_tile = tilemap.get_tile(neighbor);
if (is_passable(neighbor_tile.base)) {
visited[neighbor_global_x][neighbor_global_y] = true;
queue.push(neighbor);
}
}
}
return size;
}
void TerrainGenerator::mountain_hole_fill_pass(TileMap &tilemap) {
MountainHoleFillPass pass(config_);
pass(tilemap);
}
} // namespace istd