#include "Colors.hpp" #include #include Color elevationColor(double e, double seaLevel) { if (e < seaLevel) { // Water: deep -> shallow blue. double t = std::clamp((e + 11000.0) / (seaLevel + 11000.0), 0.0, 1.0); return Color{ (unsigned char)(10 + 20 * t), (unsigned char)(30 + 90 * t), (unsigned char)(80 + 120 * t), 255 }; } // Land: green -> brown -> white by height. double t = std::clamp(e / 9000.0, 0.0, 1.0); if (t < 0.4) { double u = t / 0.4; return Color{ (unsigned char)(60 + 80 * u), (unsigned char)(140 - 30 * u), (unsigned char)(50), 255 }; } if (t < 0.75) { double u = (t - 0.4) / 0.35; return Color{ (unsigned char)(140 - 30 * u), (unsigned char)(110 - 40 * u), (unsigned char)(50 + 10 * u), 255 }; } double u = (t - 0.75) / 0.25; return Color{ (unsigned char)(110 + 145 * u), (unsigned char)(70 + 185 * u), (unsigned char)(60 + 195 * u), 255 }; } Color plateColor(int id) { float h = std::fmod(id * 0.61803398875f, 1.0f) * 360.0f; return ColorFromHSV(h, 0.65f, 0.85f); } Color ageColor(double age, double maxAge) { double t = std::clamp(age / std::max(1.0, maxAge), 0.0, 1.0); return Color{ (unsigned char)(40 + 200 * t), (unsigned char)(40), (unsigned char)(200 - 160 * t), 255 }; } Color crustColor(bool oceanic) { return oceanic ? Color{ 30, 60, 160, 255 } // oceanic: deep blue : Color{ 160, 130, 70, 255 }; // continental: warm brown } Color lakeColor() { return Color{ 40, 200, 210, 255 }; } // bright turquoise (vs ocean blue) Color biomeColor(Biome b) { switch (b) { case Biome::Ocean: return Color{ 20, 60, 120, 255 }; // deep blue case Biome::Ice: return Color{ 235, 240, 250, 255 }; // white (polar caps / snow) case Biome::Lake: return Color{ 40, 200, 210, 255 }; // bright turquoise case Biome::Beach: return Color{ 222, 210, 150, 255 }; // pale sand case Biome::Wetland: return Color{ 70, 115, 95, 255 }; // dark teal-green (swamp/bayou) case Biome::Grassland: return Color{ 130, 185, 80, 255 }; // light green case Biome::Savanna: return Color{ 185, 180, 85, 255 }; // yellow-green case Biome::Desert: return Color{ 214, 184, 120, 255 }; // tan case Biome::Forest: return Color{ 40, 110, 50, 255 }; // dark green case Biome::Taiga: return Color{ 55, 105, 85, 255 }; // blue-green (boreal) case Biome::Tundra: return Color{ 155, 165, 150, 255 }; // pale grey-green case Biome::Hills: return Color{ 120, 135, 70, 255 }; // olive case Biome::Mountains: return Color{ 135, 125, 115, 255 }; // grey-brown } return Color{ 255, 0, 255, 255 }; // unreachable; flags an unmapped biome } const char* biomeName(Biome b) { switch (b) { case Biome::Ocean: return "Ocean"; case Biome::Ice: return "Ice cap"; case Biome::Lake: return "Lake"; case Biome::Beach: return "Beach"; case Biome::Wetland: return "Wetland"; case Biome::Grassland: return "Grassland"; case Biome::Savanna: return "Savanna"; case Biome::Desert: return "Desert"; case Biome::Forest: return "Forest"; case Biome::Taiga: return "Taiga"; case Biome::Tundra: return "Tundra"; case Biome::Hills: return "Hills"; case Biome::Mountains: return "Mountains"; } return "?"; } const char* colorModeName(ColorMode m) { switch (m) { case ColorMode::Elevation: return "Elevation"; case ColorMode::Plate: return "Plates"; case ColorMode::Age: return "Crust age"; case ColorMode::Crust: return "Crust type"; case ColorMode::Biome: return "Biome"; case ColorMode::Temperature: return "Temperature"; case ColorMode::Precip: return "Precipitation"; case ColorMode::FloraDensity: return "Flora density"; case ColorMode::FaunaDensity: return "Fauna density"; case ColorMode::FungaDensity: return "Funga density"; case ColorMode::Ecoregion: return "Ecoregions"; case ColorMode::Habitability: return "Habitability"; case ColorMode::Territory: return "Territory / realms"; case ColorMode::Culture: return "Culture / faiths"; case ColorMode::TempSummer: return "Temperature (summer)"; case ColorMode::TempWinter: return "Temperature (winter)"; case ColorMode::Seasonality: return "Seasonality (summer-winter)"; } return "?"; } // Two-colour density ramp helper: barren -> rich. static Color ramp2(double d01, const unsigned char lo[3], const unsigned char hi[3]) { double t = std::clamp(d01, 0.0, 1.0); auto L = [&](int c) { return (unsigned char)(lo[c] + (hi[c] - lo[c]) * t); }; return Color{ L(0), L(1), L(2), 255 }; } Color floraColor(double d01) { // barren tan -> lush green static const unsigned char lo[3] = { 200, 190, 150 }, hi[3] = { 25, 120, 35 }; return ramp2(d01, lo, hi); } Color faunaColor(double d01) { // pale -> amber -> red double t = std::clamp(d01, 0.0, 1.0); static const unsigned char key[3][3] = { { 225, 220, 195 }, // 0.0 pale { 220, 160, 60 }, // 0.5 amber { 180, 55, 40 }, // 1.0 red }; double s = t * 2.0; int k = std::min(1, (int)s); double f = s - k; auto L = [&](int c) { return (unsigned char)(key[k][c] + (key[k + 1][c] - key[k][c]) * f); }; return Color{ L(0), L(1), L(2), 255 }; } Color fungaColor(double d01) { // pale -> violet/brown static const unsigned char lo[3] = { 215, 205, 210 }, hi[3] = { 110, 55, 120 }; return ramp2(d01, lo, hi); } Color marineFloraColor(double d01) { // deep ocean blue -> bright teal/green bloom static const unsigned char lo[3] = { 18, 45, 80 }, hi[3] = { 60, 215, 160 }; return ramp2(d01, lo, hi); } Color marineFaunaColor(double d01) { // deep blue -> cyan -> warm (rich shelves) double t = std::clamp(d01, 0.0, 1.0); static const unsigned char key[3][3] = { { 18, 45, 80 }, // 0.0 deep blue { 50, 175, 200 }, // 0.5 cyan { 235, 195, 90 }, // 1.0 warm/gold }; double s = t * 2.0; int k = std::min(1, (int)s); double f = s - k; auto L = [&](int c) { return (unsigned char)(key[k][c] + (key[k + 1][c] - key[k][c]) * f); }; return Color{ L(0), L(1), L(2), 255 }; } Color nationColor(int id) { // distinct per-realm tint (offset hue/sat vs plateColor) if (id < 0) return Color{ 40, 44, 50, 255 }; // wilderness: dim grey float h = std::fmod((id + 4) * 0.61803398875f + 0.13f, 1.0f) * 360.0f; return ColorFromHSV(h, 0.58f, 0.92f); } Color cultureColor(int id) { // per-culture tint (different phase so blocs read vs realms) if (id < 0) return Color{ 40, 44, 50, 255 }; // no culture: dim grey float h = std::fmod((id + 2) * 0.61803398875f + 0.47f, 1.0f) * 360.0f; return ColorFromHSV(h, 0.50f, 0.86f); // slightly softer/paler than realm tints } Color habitabilityColor(double h01) { // barren grey -> green -> fertile gold double t = std::clamp(h01, 0.0, 1.0); static const unsigned char key[3][3] = { { 70, 74, 82 }, // 0.0 barren grey { 70, 150, 80 }, // 0.5 green { 230, 205, 90 }, // 1.0 fertile gold }; double s = t * 2.0; int k = std::min(1, (int)s); double f = s - k; auto L = [&](int c) { return (unsigned char)(key[k][c] + (key[k + 1][c] - key[k][c]) * f); }; return Color{ L(0), L(1), L(2), 255 }; } Color ecoregionColor(int id, Biome b, double productivity) { if (id < 0) return Color{ 48, 52, 58, 255 }; Color base = ColorFromHSV(std::fmod((id + 11) * 0.61803398875f, 1.0f) * 360.0f, 0.55f, 0.82f); Color bio = biomeColor(b); double p = std::clamp(productivity, 0.0, 1.0); auto mix = [&](unsigned char a, unsigned char c, double t) { return (unsigned char)(a * (1.0 - t) + c * t); }; double biomeWeight = 0.35; Color out{ mix(base.r, bio.r, biomeWeight), mix(base.g, bio.g, biomeWeight), mix(base.b, bio.b, biomeWeight), 255 }; double brighten = 0.72 + 0.28 * p; out.r = (unsigned char)std::clamp(out.r * brighten, 0.0, 255.0); out.g = (unsigned char)std::clamp(out.g * brighten, 0.0, 255.0); out.b = (unsigned char)std::clamp(out.b * brighten, 0.0, 255.0); return out; } // Temperature ramp over ~[-40, 40] C: deep blue -> cyan -> green -> yellow -> red. Color tempColor(double celsius) { double t = std::clamp((celsius + 40.0) / 80.0, 0.0, 1.0); // 0 cold .. 1 hot // 4 segments between 5 control colors. static const unsigned char key[5][3] = { { 30, 40, 130 }, // -40 C deep blue { 60, 160, 210 }, // -20 C cyan { 90, 190, 90 }, // 0 C green { 225, 200, 70 }, // +20 C yellow { 210, 70, 50 }, // +40 C red }; double s = t * 4.0; int k = std::min(3, (int)s); double f = s - k; auto L = [&](int c){ return (unsigned char)(key[k][c] + (key[k + 1][c] - key[k][c]) * f); }; return Color{ L(0), L(1), L(2), 255 }; } // Seasonality ramp: summer-winter range in deg C, ~[0, 45]: calm grey -> warm orange. Color seasonColor(double rangeC) { static const unsigned char lo[3] = { 95, 100, 110 }, hi[3] = { 235, 130, 40 }; double t = std::clamp(rangeC / 45.0, 0.0, 1.0); auto L = [&](int c){ return (unsigned char)(lo[c] + (hi[c] - lo[c]) * t); }; return Color{ L(0), L(1), L(2), 255 }; } // Tide: diverging around mid-tide. Low (negative) -> amber, high (positive) -> cyan. Color tideColor(double level, double range) { static const unsigned char loC[3] = { 235, 175, 80 }; // low tide (amber) static const unsigned char midC[3] = { 150, 175, 185 }; // mid tide (pale) static const unsigned char hiC[3] = { 55, 165, 235 }; // high tide (cyan) double t = std::clamp(level / std::max(1e-6, range), -1.0, 1.0); const unsigned char* a = t < 0.0 ? loC : midC; const unsigned char* b = t < 0.0 ? midC : hiC; double f = std::fabs(t); auto L = [&](int c){ return (unsigned char)(a[c] + (b[c] - a[c]) * f); }; return Color{ L(0), L(1), L(2), 255 }; } // Precipitation ramp over normalized [0,1]: tan (dry) -> green -> teal/blue (wet). Color precipColor(double moist01) { double t = std::clamp(moist01, 0.0, 1.0); static const unsigned char key[4][3] = { { 205, 180, 120 }, // 0.00 dry tan { 170, 185, 90 }, // 0.33 scrub { 70, 160, 90 }, // 0.66 green { 40, 120, 190 }, // 1.00 wet blue }; double s = t * 3.0; int k = std::min(2, (int)s); double f = s - k; auto L = [&](int c){ return (unsigned char)(key[k][c] + (key[k + 1][c] - key[k][c]) * f); }; return Color{ L(0), L(1), L(2), 255 }; }