diff --git a/src/render/ViewerRender.cpp b/src/render/ViewerRender.cpp index 69c70eb..8303794 100644 --- a/src/render/ViewerRender.cpp +++ b/src/render/ViewerRender.cpp @@ -619,10 +619,17 @@ void Viewer::exportAtlasImage() { } } // Realm names (kingdoms/empires), regardless of the current colour mode -- an atlas is a - // reference, not "what you're currently looking at". Offset above the capital's own marker/name - // (rather than sharing its exact anchor point) so the two don't fight over the same box in the - // decluttering pass below; city-states are skipped (bare settlement name already covers them). + // reference, not "what you're currently looking at". Stacked *cleanly above* the capital's own + // settlement-name box (drawn separately below, unshifted at this same point) with a guaranteed + // gap -- not just "offset a bit", which previously still let the realm-name/culture block + // overlap the settlement's own label at the same point often enough that the (higher-priority) + // realm name would win the collision check and the capital's own name would silently vanish, so + // a capital never showed its own identity, only "Duchy of X" (built from that same name). + // City-states are skipped (their bare settlement name already covers them -- there's no separate + // realm identity to show). if (!planet.nationList().empty()) { + const float pad = 3.0f; // matches the collision-box padding below + const float settleFontMax = 17.0f * labelScale; // worst case: a City-tier capital's own label for (const Nation& nat : planet.nationList()) { if (nat.tier == NationTier::CityState || nat.name.empty()) continue; if (nat.capital < 0 || nat.capital >= (int)planet.settlements.size()) continue; @@ -632,20 +639,22 @@ void Viewer::exportAtlasImage() { int prio = nat.tier == NationTier::Empire ? 0 : 1; double lon, lat; dirToLonLat(planet.cells[cell].unit, lon, lat); Vector2 lp = projLonLat(lon, lat, mapLon, er); - lp.y -= font * 0.9f; - labels.push_back({ lp, nat.name, font, Color{245, 235, 210, 255}, prio, (float)nat.totalPop, false }); - // The realm's dominant culture, as a smaller "(the Velmar)" label anchored just below the - // realm name (still above the raw capital point) -- a lower-priority candidate in the same - // greedy pass, so on a dense world it simply drops out on its own if it doesn't fit, - // without any special-case logic. - if (nat.cultureId >= 0 && nat.cultureId < (int)planet.cultureList().size()) { + float cultureFont = std::max(8.0f, std::round(font * 0.72f)); + bool hasCulture = nat.cultureId >= 0 && nat.cultureId < (int)planet.cultureList().size() + && !planet.cultureList()[nat.cultureId].name.empty(); + float stackTop = lp.y - settleFontMax * 0.5f - pad; // top edge of the settlement's own box + float cultureCenterY = stackTop - pad - cultureFont * 0.5f; + float realmCenterY = (hasCulture ? cultureCenterY - cultureFont * 0.5f - pad : stackTop - pad) + - pad - font * 0.5f; + labels.push_back({ Vector2{lp.x, realmCenterY}, nat.name, font, Color{245, 235, 210, 255}, + prio, (float)nat.totalPop, false }); + // The realm's dominant culture, as a smaller "(the Velmar)" label right above the realm + // name -- a lower-priority candidate in the same greedy pass, so on a dense world it + // simply drops out on its own if it doesn't fit, without any special-case logic. + if (hasCulture) { const std::string& cname = planet.cultureList()[nat.cultureId].name; - if (!cname.empty()) { - int cfont = std::max(8, (int)std::round(font * 0.72f)); - Vector2 clp = lp; clp.y += font * 0.62f; - labels.push_back({ clp, "(" + cname + ")", cfont, Color{215, 205, 180, 235}, - prio + 1, (float)nat.totalPop, false }); - } + labels.push_back({ Vector2{lp.x, cultureCenterY}, "(" + cname + ")", (int)cultureFont, + Color{215, 205, 180, 235}, prio + 1, (float)nat.totalPop, false }); } } }