From c0053960400b0e98224db604fca132c353671b69 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sat, 9 May 2026 07:22:06 -0700 Subject: [PATCH] refactor(editor): extract --info-{zone,project}-extents into cli_info_extents.cpp Moves the two spatial-bounds info handlers (--info-zone-extents, --info-project-extents) out of main.cpp into a new cli_info_extents.{hpp,cpp} module. Both compute world-space XYZ bounding boxes from manifest tile coords + per-chunk height samples; the project variant unions every zone's box into one. main.cpp shrinks by 232 lines (7,695 to 7,463). Both --json output modes preserved for camera-framing pipelines. --- CMakeLists.txt | 1 + tools/editor/cli_info_extents.cpp | 277 ++++++++++++++++++++++++++++++ tools/editor/cli_info_extents.hpp | 22 +++ tools/editor/main.cpp | 240 +------------------------- 4 files changed, 304 insertions(+), 236 deletions(-) create mode 100644 tools/editor/cli_info_extents.cpp create mode 100644 tools/editor/cli_info_extents.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ee2ae901..309f82aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1331,6 +1331,7 @@ add_executable(wowee_editor tools/editor/cli_world_io.cpp tools/editor/cli_info_tree.cpp tools/editor/cli_info_bytes.cpp + tools/editor/cli_info_extents.cpp tools/editor/editor_app.cpp tools/editor/editor_camera.cpp tools/editor/editor_viewport.cpp diff --git a/tools/editor/cli_info_extents.cpp b/tools/editor/cli_info_extents.cpp new file mode 100644 index 00000000..b360d2f9 --- /dev/null +++ b/tools/editor/cli_info_extents.cpp @@ -0,0 +1,277 @@ +#include "cli_info_extents.hpp" + +#include "zone_manifest.hpp" +#include "pipeline/wowee_terrain_loader.hpp" +#include "pipeline/adt_loader.hpp" +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace wowee { +namespace editor { +namespace cli { + +namespace { + +int handleInfoZoneExtents(int& i, int argc, char** argv) { + // Compute the zone's spatial bounding box. XY from manifest + // tile coords (each tile is 533.33 yards); Z from height + // range across all loaded chunks. Useful for sizing the + // camera frustum, planning where new tiles can fit + // contiguously, or quick sanity-checks ('this zone is 4km + // across? that seems wrong'). + std::string zoneDir = argv[++i]; + bool jsonOut = (i + 1 < argc && + std::strcmp(argv[i + 1], "--json") == 0); + if (jsonOut) i++; + namespace fs = std::filesystem; + std::string manifestPath = zoneDir + "/zone.json"; + if (!fs::exists(manifestPath)) { + std::fprintf(stderr, + "info-zone-extents: %s has no zone.json\n", zoneDir.c_str()); + return 1; + } + wowee::editor::ZoneManifest zm; + if (!zm.load(manifestPath)) { + std::fprintf(stderr, "info-zone-extents: parse failed\n"); + return 1; + } + // Tile XY range — straightforward integer min/max. + int tileMinX = 64, tileMaxX = -1; + int tileMinY = 64, tileMaxY = -1; + for (const auto& [tx, ty] : zm.tiles) { + tileMinX = std::min(tileMinX, tx); + tileMaxX = std::max(tileMaxX, tx); + tileMinY = std::min(tileMinY, ty); + tileMaxY = std::max(tileMaxY, ty); + } + // Z range from loaded chunks. Walk every WHM tile; this is + // the same scan --info-whm does per-tile but rolled up. + float zMin = 1e30f, zMax = -1e30f; + int loadedTiles = 0, missingTiles = 0; + for (const auto& [tx, ty] : zm.tiles) { + std::string tileBase = zoneDir + "/" + zm.mapName + "_" + + std::to_string(tx) + "_" + std::to_string(ty); + if (!wowee::pipeline::WoweeTerrainLoader::exists(tileBase)) { + missingTiles++; + continue; + } + wowee::pipeline::ADTTerrain terrain; + wowee::pipeline::WoweeTerrainLoader::load(tileBase, terrain); + loadedTiles++; + for (const auto& chunk : terrain.chunks) { + if (!chunk.heightMap.isLoaded()) continue; + float baseZ = chunk.position[2]; + for (float h : chunk.heightMap.heights) { + if (!std::isfinite(h)) continue; + zMin = std::min(zMin, baseZ + h); + zMax = std::max(zMax, baseZ + h); + } + } + } + if (zMin > zMax) { zMin = 0; zMax = 0; } + // Convert tile coords to world-space yards. WoW grid centers + // tile (32, 32) at world origin; +X tile = -X world (north), + // +Y tile = -Y world (west). + constexpr float kTileSize = 533.33333f; + float worldMinX = (32.0f - tileMaxY - 1) * kTileSize; + float worldMaxX = (32.0f - tileMinY) * kTileSize; + float worldMinY = (32.0f - tileMaxX - 1) * kTileSize; + float worldMaxY = (32.0f - tileMinX) * kTileSize; + float widthX = worldMaxX - worldMinX; + float widthY = worldMaxY - worldMinY; + float heightZ = zMax - zMin; + if (jsonOut) { + nlohmann::json j; + j["zone"] = zoneDir; + j["tileCount"] = zm.tiles.size(); + j["loadedTiles"] = loadedTiles; + j["missingTiles"] = missingTiles; + j["tileRange"] = {{"x", {tileMinX, tileMaxX}}, + {"y", {tileMinY, tileMaxY}}}; + j["worldBox"] = {{"min", {worldMinX, worldMinY, zMin}}, + {"max", {worldMaxX, worldMaxY, zMax}}}; + j["sizeYards"] = {widthX, widthY, heightZ}; + std::printf("%s\n", j.dump(2).c_str()); + return 0; + } + std::printf("Zone extents: %s\n", zoneDir.c_str()); + std::printf(" tile count : %zu (%d loaded, %d missing on disk)\n", + zm.tiles.size(), loadedTiles, missingTiles); + if (zm.tiles.empty()) { + std::printf(" *no tiles in manifest*\n"); + return 0; + } + std::printf(" tile range : x=[%d, %d] y=[%d, %d]\n", + tileMinX, tileMaxX, tileMinY, tileMaxY); + std::printf(" world box : (%.1f, %.1f, %.1f) - (%.1f, %.1f, %.1f) yards\n", + worldMinX, worldMinY, zMin, + worldMaxX, worldMaxY, zMax); + std::printf(" size : %.1f x %.1f x %.1f yards (%.0fm x %.0fm x %.1fm)\n", + widthX, widthY, heightZ, + widthX * 0.9144f, widthY * 0.9144f, heightZ * 0.9144f); + return 0; +} + +int handleInfoProjectExtents(int& i, int argc, char** argv) { + // Combined spatial bounding box across every zone in + // . Per-zone XY tile range + Z height range, + // unioned into a project-wide world box. Useful for + // understanding total project area, sizing the world map + // overview, or sanity-checking that zones don't overlap + // (the union should equal the sum of disjoint per-zone + // boxes). + std::string projectDir = argv[++i]; + bool jsonOut = (i + 1 < argc && + std::strcmp(argv[i + 1], "--json") == 0); + if (jsonOut) i++; + namespace fs = std::filesystem; + if (!fs::exists(projectDir) || !fs::is_directory(projectDir)) { + std::fprintf(stderr, + "info-project-extents: %s is not a directory\n", + projectDir.c_str()); + return 1; + } + std::vector zones; + for (const auto& entry : fs::directory_iterator(projectDir)) { + if (!entry.is_directory()) continue; + if (!fs::exists(entry.path() / "zone.json")) continue; + zones.push_back(entry.path().string()); + } + std::sort(zones.begin(), zones.end()); + constexpr float kTileSize = 533.33333f; + struct ZBox { + std::string name; + int tileCount = 0; + float wMinX = 1e30f, wMaxX = -1e30f; + float wMinY = 1e30f, wMaxY = -1e30f; + float zMin = 1e30f, zMax = -1e30f; + }; + std::vector rows; + float gMinX = 1e30f, gMaxX = -1e30f; + float gMinY = 1e30f, gMaxY = -1e30f; + float gZMin = 1e30f, gZMax = -1e30f; + int totalTiles = 0; + for (const auto& zoneDir : zones) { + ZBox b; + b.name = fs::path(zoneDir).filename().string(); + wowee::editor::ZoneManifest zm; + if (!zm.load(zoneDir + "/zone.json")) { + rows.push_back(b); + continue; + } + b.tileCount = static_cast(zm.tiles.size()); + if (zm.tiles.empty()) { + rows.push_back(b); + continue; + } + int tMinX = 64, tMaxX = -1, tMinY = 64, tMaxY = -1; + for (const auto& [tx, ty] : zm.tiles) { + tMinX = std::min(tMinX, tx); + tMaxX = std::max(tMaxX, tx); + tMinY = std::min(tMinY, ty); + tMaxY = std::max(tMaxY, ty); + } + b.wMinX = (32.0f - tMaxY - 1) * kTileSize; + b.wMaxX = (32.0f - tMinY) * kTileSize; + b.wMinY = (32.0f - tMaxX - 1) * kTileSize; + b.wMaxY = (32.0f - tMinX) * kTileSize; + for (const auto& [tx, ty] : zm.tiles) { + std::string tileBase = zoneDir + "/" + zm.mapName + "_" + + std::to_string(tx) + "_" + std::to_string(ty); + if (!wowee::pipeline::WoweeTerrainLoader::exists(tileBase)) continue; + wowee::pipeline::ADTTerrain terrain; + wowee::pipeline::WoweeTerrainLoader::load(tileBase, terrain); + for (const auto& chunk : terrain.chunks) { + if (!chunk.heightMap.isLoaded()) continue; + float baseZ = chunk.position[2]; + for (float h : chunk.heightMap.heights) { + if (!std::isfinite(h)) continue; + b.zMin = std::min(b.zMin, baseZ + h); + b.zMax = std::max(b.zMax, baseZ + h); + } + } + } + if (b.zMin > b.zMax) { b.zMin = 0; b.zMax = 0; } + gMinX = std::min(gMinX, b.wMinX); + gMaxX = std::max(gMaxX, b.wMaxX); + gMinY = std::min(gMinY, b.wMinY); + gMaxY = std::max(gMaxY, b.wMaxY); + gZMin = std::min(gZMin, b.zMin); + gZMax = std::max(gZMax, b.zMax); + totalTiles += b.tileCount; + rows.push_back(b); + } + if (totalTiles == 0) { + gMinX = gMaxX = gMinY = gMaxY = gZMin = gZMax = 0.0f; + } + float gWidthX = gMaxX - gMinX; + float gWidthY = gMaxY - gMinY; + float gHeightZ = gZMax - gZMin; + if (jsonOut) { + nlohmann::json j; + j["project"] = projectDir; + j["zoneCount"] = zones.size(); + j["totalTiles"] = totalTiles; + j["worldBox"] = {{"min", {gMinX, gMinY, gZMin}}, + {"max", {gMaxX, gMaxY, gZMax}}}; + j["sizeYards"] = {gWidthX, gWidthY, gHeightZ}; + nlohmann::json zarr = nlohmann::json::array(); + for (const auto& b : rows) { + zarr.push_back({{"name", b.name}, + {"tileCount", b.tileCount}, + {"worldBox", {{"min", {b.wMinX, b.wMinY, b.zMin}}, + {"max", {b.wMaxX, b.wMaxY, b.zMax}}}}}); + } + j["zones"] = zarr; + std::printf("%s\n", j.dump(2).c_str()); + return 0; + } + std::printf("Project extents: %s\n", projectDir.c_str()); + std::printf(" zones : %zu\n", zones.size()); + std::printf(" total tiles : %d\n", totalTiles); + if (totalTiles == 0) { + std::printf(" *no tiles in any zone manifest*\n"); + return 0; + } + std::printf(" world union : (%.1f, %.1f, %.1f) - (%.1f, %.1f, %.1f) yards\n", + gMinX, gMinY, gZMin, gMaxX, gMaxY, gZMax); + std::printf(" total size : %.1f x %.1f x %.1f yards (%.0fm x %.0fm x %.1fm)\n", + gWidthX, gWidthY, gHeightZ, + gWidthX * 0.9144f, gWidthY * 0.9144f, gHeightZ * 0.9144f); + std::printf("\n zone tiles worldX (min..max) worldY (min..max)\n"); + for (const auto& b : rows) { + if (b.tileCount == 0) { + std::printf(" %-20s %5d (no tiles)\n", + b.name.substr(0, 20).c_str(), b.tileCount); + continue; + } + std::printf(" %-20s %5d %9.1f .. %9.1f %9.1f .. %9.1f\n", + b.name.substr(0, 20).c_str(), b.tileCount, + b.wMinX, b.wMaxX, b.wMinY, b.wMaxY); + } + return 0; +} + +} // namespace + +bool handleInfoExtents(int& i, int argc, char** argv, int& outRc) { + if (std::strcmp(argv[i], "--info-zone-extents") == 0 && i + 1 < argc) { + outRc = handleInfoZoneExtents(i, argc, argv); return true; + } + if (std::strcmp(argv[i], "--info-project-extents") == 0 && i + 1 < argc) { + outRc = handleInfoProjectExtents(i, argc, argv); return true; + } + return false; +} + +} // namespace cli +} // namespace editor +} // namespace wowee diff --git a/tools/editor/cli_info_extents.hpp b/tools/editor/cli_info_extents.hpp new file mode 100644 index 00000000..dae71347 --- /dev/null +++ b/tools/editor/cli_info_extents.hpp @@ -0,0 +1,22 @@ +#pragma once + +namespace wowee { +namespace editor { +namespace cli { + +// Dispatch the spatial-bounds info handlers — compute world-space +// XYZ bounding boxes from manifest tile coords + per-chunk +// height samples. Useful for sizing camera frustums, planning +// new tile placement, and sanity-checking project layouts. +// --info-zone-extents one zone's bounding box +// --info-project-extents union across every zone in a project +// +// Both support an optional trailing `--json` flag for +// machine-readable reports. +// +// Returns true if matched; outRc holds the exit code. +bool handleInfoExtents(int& i, int argc, char** argv, int& outRc); + +} // namespace cli +} // namespace editor +} // namespace wowee diff --git a/tools/editor/main.cpp b/tools/editor/main.cpp index 1a3b4593..bb743763 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -32,6 +32,7 @@ #include "cli_world_io.hpp" #include "cli_info_tree.hpp" #include "cli_info_bytes.hpp" +#include "cli_info_extents.hpp" #include "content_pack.hpp" #include "npc_spawner.hpp" #include "object_placer.hpp" @@ -459,6 +460,9 @@ int main(int argc, char* argv[]) { if (wowee::editor::cli::handleInfoBytes(i, argc, argv, outRc)) { return outRc; } + if (wowee::editor::cli::handleInfoExtents(i, argc, argv, outRc)) { + return outRc; + } } if (std::strcmp(argv[i], "--data") == 0 && i + 1 < argc) { dataPath = argv[++i]; @@ -1300,242 +1304,6 @@ int main(int argc, char* argv[]) { questTotal, chainWarnings); } return v.openFormatScore() == 7 ? 0 : 1; - } else if (std::strcmp(argv[i], "--info-zone-extents") == 0 && i + 1 < argc) { - // Compute the zone's spatial bounding box. XY from manifest - // tile coords (each tile is 533.33 yards); Z from height - // range across all loaded chunks. Useful for sizing the - // camera frustum, planning where new tiles can fit - // contiguously, or quick sanity-checks ('this zone is 4km - // across? that seems wrong'). - std::string zoneDir = argv[++i]; - bool jsonOut = (i + 1 < argc && - std::strcmp(argv[i + 1], "--json") == 0); - if (jsonOut) i++; - namespace fs = std::filesystem; - std::string manifestPath = zoneDir + "/zone.json"; - if (!fs::exists(manifestPath)) { - std::fprintf(stderr, - "info-zone-extents: %s has no zone.json\n", zoneDir.c_str()); - return 1; - } - wowee::editor::ZoneManifest zm; - if (!zm.load(manifestPath)) { - std::fprintf(stderr, "info-zone-extents: parse failed\n"); - return 1; - } - // Tile XY range — straightforward integer min/max. - int tileMinX = 64, tileMaxX = -1; - int tileMinY = 64, tileMaxY = -1; - for (const auto& [tx, ty] : zm.tiles) { - tileMinX = std::min(tileMinX, tx); - tileMaxX = std::max(tileMaxX, tx); - tileMinY = std::min(tileMinY, ty); - tileMaxY = std::max(tileMaxY, ty); - } - // Z range from loaded chunks. Walk every WHM tile; this is - // the same scan --info-whm does per-tile but rolled up. - float zMin = 1e30f, zMax = -1e30f; - int loadedTiles = 0, missingTiles = 0; - for (const auto& [tx, ty] : zm.tiles) { - std::string tileBase = zoneDir + "/" + zm.mapName + "_" + - std::to_string(tx) + "_" + std::to_string(ty); - if (!wowee::pipeline::WoweeTerrainLoader::exists(tileBase)) { - missingTiles++; - continue; - } - wowee::pipeline::ADTTerrain terrain; - wowee::pipeline::WoweeTerrainLoader::load(tileBase, terrain); - loadedTiles++; - for (const auto& chunk : terrain.chunks) { - if (!chunk.heightMap.isLoaded()) continue; - float baseZ = chunk.position[2]; - for (float h : chunk.heightMap.heights) { - if (!std::isfinite(h)) continue; - zMin = std::min(zMin, baseZ + h); - zMax = std::max(zMax, baseZ + h); - } - } - } - if (zMin > zMax) { zMin = 0; zMax = 0; } - // Convert tile coords to world-space yards. WoW grid centers - // tile (32, 32) at world origin; +X tile = -X world (north), - // +Y tile = -Y world (west). - constexpr float kTileSize = 533.33333f; - float worldMinX = (32.0f - tileMaxY - 1) * kTileSize; - float worldMaxX = (32.0f - tileMinY) * kTileSize; - float worldMinY = (32.0f - tileMaxX - 1) * kTileSize; - float worldMaxY = (32.0f - tileMinX) * kTileSize; - float widthX = worldMaxX - worldMinX; - float widthY = worldMaxY - worldMinY; - float heightZ = zMax - zMin; - if (jsonOut) { - nlohmann::json j; - j["zone"] = zoneDir; - j["tileCount"] = zm.tiles.size(); - j["loadedTiles"] = loadedTiles; - j["missingTiles"] = missingTiles; - j["tileRange"] = {{"x", {tileMinX, tileMaxX}}, - {"y", {tileMinY, tileMaxY}}}; - j["worldBox"] = {{"min", {worldMinX, worldMinY, zMin}}, - {"max", {worldMaxX, worldMaxY, zMax}}}; - j["sizeYards"] = {widthX, widthY, heightZ}; - std::printf("%s\n", j.dump(2).c_str()); - return 0; - } - std::printf("Zone extents: %s\n", zoneDir.c_str()); - std::printf(" tile count : %zu (%d loaded, %d missing on disk)\n", - zm.tiles.size(), loadedTiles, missingTiles); - if (zm.tiles.empty()) { - std::printf(" *no tiles in manifest*\n"); - return 0; - } - std::printf(" tile range : x=[%d, %d] y=[%d, %d]\n", - tileMinX, tileMaxX, tileMinY, tileMaxY); - std::printf(" world box : (%.1f, %.1f, %.1f) - (%.1f, %.1f, %.1f) yards\n", - worldMinX, worldMinY, zMin, - worldMaxX, worldMaxY, zMax); - std::printf(" size : %.1f x %.1f x %.1f yards (%.0fm x %.0fm x %.1fm)\n", - widthX, widthY, heightZ, - widthX * 0.9144f, widthY * 0.9144f, heightZ * 0.9144f); - return 0; - } else if (std::strcmp(argv[i], "--info-project-extents") == 0 && i + 1 < argc) { - // Combined spatial bounding box across every zone in - // . Per-zone XY tile range + Z height range, - // unioned into a project-wide world box. Useful for - // understanding total project area, sizing the world map - // overview, or sanity-checking that zones don't overlap - // (the union should equal the sum of disjoint per-zone - // boxes). - std::string projectDir = argv[++i]; - bool jsonOut = (i + 1 < argc && - std::strcmp(argv[i + 1], "--json") == 0); - if (jsonOut) i++; - namespace fs = std::filesystem; - if (!fs::exists(projectDir) || !fs::is_directory(projectDir)) { - std::fprintf(stderr, - "info-project-extents: %s is not a directory\n", - projectDir.c_str()); - return 1; - } - std::vector zones; - for (const auto& entry : fs::directory_iterator(projectDir)) { - if (!entry.is_directory()) continue; - if (!fs::exists(entry.path() / "zone.json")) continue; - zones.push_back(entry.path().string()); - } - std::sort(zones.begin(), zones.end()); - constexpr float kTileSize = 533.33333f; - struct ZBox { - std::string name; - int tileCount = 0; - float wMinX = 1e30f, wMaxX = -1e30f; - float wMinY = 1e30f, wMaxY = -1e30f; - float zMin = 1e30f, zMax = -1e30f; - }; - std::vector rows; - float gMinX = 1e30f, gMaxX = -1e30f; - float gMinY = 1e30f, gMaxY = -1e30f; - float gZMin = 1e30f, gZMax = -1e30f; - int totalTiles = 0; - for (const auto& zoneDir : zones) { - ZBox b; - b.name = fs::path(zoneDir).filename().string(); - wowee::editor::ZoneManifest zm; - if (!zm.load(zoneDir + "/zone.json")) { - rows.push_back(b); - continue; - } - b.tileCount = static_cast(zm.tiles.size()); - if (zm.tiles.empty()) { - rows.push_back(b); - continue; - } - int tMinX = 64, tMaxX = -1, tMinY = 64, tMaxY = -1; - for (const auto& [tx, ty] : zm.tiles) { - tMinX = std::min(tMinX, tx); - tMaxX = std::max(tMaxX, tx); - tMinY = std::min(tMinY, ty); - tMaxY = std::max(tMaxY, ty); - } - b.wMinX = (32.0f - tMaxY - 1) * kTileSize; - b.wMaxX = (32.0f - tMinY) * kTileSize; - b.wMinY = (32.0f - tMaxX - 1) * kTileSize; - b.wMaxY = (32.0f - tMinX) * kTileSize; - for (const auto& [tx, ty] : zm.tiles) { - std::string tileBase = zoneDir + "/" + zm.mapName + "_" + - std::to_string(tx) + "_" + std::to_string(ty); - if (!wowee::pipeline::WoweeTerrainLoader::exists(tileBase)) continue; - wowee::pipeline::ADTTerrain terrain; - wowee::pipeline::WoweeTerrainLoader::load(tileBase, terrain); - for (const auto& chunk : terrain.chunks) { - if (!chunk.heightMap.isLoaded()) continue; - float baseZ = chunk.position[2]; - for (float h : chunk.heightMap.heights) { - if (!std::isfinite(h)) continue; - b.zMin = std::min(b.zMin, baseZ + h); - b.zMax = std::max(b.zMax, baseZ + h); - } - } - } - if (b.zMin > b.zMax) { b.zMin = 0; b.zMax = 0; } - gMinX = std::min(gMinX, b.wMinX); - gMaxX = std::max(gMaxX, b.wMaxX); - gMinY = std::min(gMinY, b.wMinY); - gMaxY = std::max(gMaxY, b.wMaxY); - gZMin = std::min(gZMin, b.zMin); - gZMax = std::max(gZMax, b.zMax); - totalTiles += b.tileCount; - rows.push_back(b); - } - if (totalTiles == 0) { - gMinX = gMaxX = gMinY = gMaxY = gZMin = gZMax = 0.0f; - } - float gWidthX = gMaxX - gMinX; - float gWidthY = gMaxY - gMinY; - float gHeightZ = gZMax - gZMin; - if (jsonOut) { - nlohmann::json j; - j["project"] = projectDir; - j["zoneCount"] = zones.size(); - j["totalTiles"] = totalTiles; - j["worldBox"] = {{"min", {gMinX, gMinY, gZMin}}, - {"max", {gMaxX, gMaxY, gZMax}}}; - j["sizeYards"] = {gWidthX, gWidthY, gHeightZ}; - nlohmann::json zarr = nlohmann::json::array(); - for (const auto& b : rows) { - zarr.push_back({{"name", b.name}, - {"tileCount", b.tileCount}, - {"worldBox", {{"min", {b.wMinX, b.wMinY, b.zMin}}, - {"max", {b.wMaxX, b.wMaxY, b.zMax}}}}}); - } - j["zones"] = zarr; - std::printf("%s\n", j.dump(2).c_str()); - return 0; - } - std::printf("Project extents: %s\n", projectDir.c_str()); - std::printf(" zones : %zu\n", zones.size()); - std::printf(" total tiles : %d\n", totalTiles); - if (totalTiles == 0) { - std::printf(" *no tiles in any zone manifest*\n"); - return 0; - } - std::printf(" world union : (%.1f, %.1f, %.1f) - (%.1f, %.1f, %.1f) yards\n", - gMinX, gMinY, gZMin, gMaxX, gMaxY, gZMax); - std::printf(" total size : %.1f x %.1f x %.1f yards (%.0fm x %.0fm x %.1fm)\n", - gWidthX, gWidthY, gHeightZ, - gWidthX * 0.9144f, gWidthY * 0.9144f, gHeightZ * 0.9144f); - std::printf("\n zone tiles worldX (min..max) worldY (min..max)\n"); - for (const auto& b : rows) { - if (b.tileCount == 0) { - std::printf(" %-20s %5d (no tiles)\n", - b.name.substr(0, 20).c_str(), b.tileCount); - continue; - } - std::printf(" %-20s %5d %9.1f .. %9.1f %9.1f .. %9.1f\n", - b.name.substr(0, 20).c_str(), b.tileCount, - b.wMinX, b.wMaxX, b.wMinY, b.wMaxY); - } - return 0; } else if (std::strcmp(argv[i], "--info-zone-water") == 0 && i + 1 < argc) { // Aggregate water-layer stats across all tiles in a zone. // Useful for confirming a 'lake zone' actually has water,