#include "map.h" #include #include #include #include #include #include #include #include using namespace std::chrono; #include #define KBLIB_DEF_MACROS 1 #include "kblib/convert.h" #include "kblib/stats.h" using std::cerr; using std::cin; using std::clog; using std::cout; using std::endl; using std::string; using std::vector; extern char** environ; static constexpr inline float plateau(const float r) { return r * r / (r * r + 1); } template static constexpr inline T square(const T x) { return x * x; } template static constexpr inline T polar_r(const T x, const T y) { return std::sqrt(x * x * .1f + y * y * .1f); } template static constexpr inline T polar_t(const T x, const T y) { return std::atan2(x, y); } vector>& genSpiral(spiral mode, vector>& buf, const std::valarray& x_vals, const std::valarray& y_vals); vector>& genWeather(vector>& buf, const std::valarray& x_vals, const std::valarray& y_vals); template class pixel_traits { public: static P to_pix(size_t i, const png::palette& cmap [[maybe_unused]]) { return static_cast

(i); } }; template <> class pixel_traits { public: static png::rgb_pixel to_pix(size_t i, const png::palette& cmap) { auto c = cmap[i]; return png::rgb_pixel(c.red, c.green, c.blue); } }; // filename left lower width pixels template void genMap(vector inList) { float seed = 5867; std::deque> ts; ts.push_back({steady_clock::now(), "Begin"}); // generate a square image tile // const static unsigned short tileSize = 256; unsigned short tileSize = pFromStr(unsigned short, inList[5]); png::image image(tileSize, tileSize); png::palette cmap; auto a = string::npos; unsigned cmapID; if ((a = inList[0].find_first_of("0123456789ABCDEFabcdef")) != string::npos) { cmapID = stoi(inList[0].substr(a, 1), nullptr, 16); std::string name; std::tie(name, cmap) = current_cmap.at(cmapID); #ifdef _DEBUG // clog<<"Using colormap "< x_vals(tileSize), y_vals(tileSize); float xdiff = (xmax - xmin) / tileSize, ydiff = (ymax - ymin) / tileSize; for (int i = 0; i < tileSize; ++i) { x_vals[i] = xmin + xdiff * i; y_vals[i] = ymin + ydiff * i; } // ts.push_back({steady_clock::now(),"Setup noise"}); SimplexNoise noise(10, 0.65, 2.06, 0.0009f, 65); // vector> imgBuf(tileSize, // std::valarray(tileSize)); // noise.makeSomeNoise(imgBuf, seed); auto imgBuf = noise.genNoise(x_vals, y_vals, seed); ts.push_back({steady_clock::now(), "Simplex"}); spiral mode = parse_spiral(inList[0][1]); if (mode != spiral::none) { genSpiral(mode, imgBuf, x_vals, y_vals); } // genWeather(imgBuf, x_vals, y_vals); ts.push_back({steady_clock::now(), "Apply spiral"}); size_t V = 0, clipmin = 0, clipmax = cmap.size() - 1; float limmin = 0 + .5f, limmax = static_cast(cmap.size()) - 0.5f; for (size_t x = 0; x < tileSize; ++x) { for (size_t y = 0; y < tileSize; ++y) { // double temp = imgBuf[x][y] + 255 - fabs(imgBuf[x][y]-255); // V = (temp + fabs(temp)) * 0.25; auto h = (imgBuf[x][y] / 256) * cmap.size(); V = (h < limmin) ? clipmin : ((h > limmax) ? clipmax : static_cast(h)); image[x][y] = pixel_traits::to_pix(V, cmap); } } ts.push_back({steady_clock::now(), "Fill image"}); if (inList[1] != "-") image.write(inList[1]); else image.template write_stream(cout); ts.push_back({steady_clock::now(), "Write Image"}); if (verbose()) { auto prev = ts[0]; ts.pop_front(); for (auto i : ts) { clog << duration_cast(i.first - prev.first).count() << "ms\t" << i.second << std::endl; prev = i; } } } template void genMap(std::vector); template void genMap(std::vector); template png::image genMap_impl(unsigned cmapID, spiral mode, float xmin, float ymin, float width, int tileSize) { float seed = 5867; std::deque> ts; ts.push_back({steady_clock::now(), "Begin"}); // generate a square image tile png::image image(tileSize, tileSize); png::palette cmap = current_cmap[cmapID].second; image.set_palette(cmap); // ts.push_back({steady_clock::now(),"Setup image"}); float xmax = xmin + width, ymax = ymin + width; std::valarray x_vals(tileSize), y_vals(tileSize); float xdiff = (xmax - xmin) / tileSize, ydiff = (ymax - ymin) / tileSize; for (int i = 0; i < tileSize; ++i) { x_vals[i] = xmin + xdiff * i; y_vals[i] = ymin + ydiff * i; } ts.push_back({steady_clock::now(), "Setup noise"}); SimplexNoise noise(7, 0.65, 2.06, 0.0009f, 65); // vector> imgBuf(tileSize, // std::valarray(tileSize)); // noise.makeSomeNoise(imgBuf, seed); auto imgBuf = noise.genNoise(x_vals, y_vals, seed); ts.push_back({steady_clock::now(), "Simplex"}); if (mode != spiral::none) { genSpiral(mode, imgBuf, x_vals, y_vals); } // genWeather(imgBuf, x_vals, y_vals); ts.push_back({steady_clock::now(), "Apply spiral"}); size_t V = 0, clipmin = 0, clipmax = cmap.size() - 1; float limmin = 0 + .5f, limmax = static_cast(cmap.size()) - 0.5f; for (int x = 0; x < tileSize; ++x) { for (int y = 0; y < tileSize; ++y) { // double temp = imgBuf[x][y] + 255 - fabs(imgBuf[x][y]-255); // V = (temp + fabs(temp)) * 0.25; auto h = (imgBuf[x][y] / 256) * cmap.size(); V = (h < limmin) ? clipmin : ((h > limmax) ? clipmax : static_cast(h)); image[x][y] = pixel_traits::to_pix(V, cmap); } } ts.push_back({steady_clock::now(), "Fill image"}); if (verbose()) { auto prev = ts[0]; ts.pop_front(); for (auto i : ts) { clog << duration_cast(i.first - prev.first).count() << "ms\t" << i.second << std::endl; prev = i; } } return image; } template png::image genMap_impl(unsigned cmapID, spiral mode, float xmin, float ymin, float width, int tileSize); template png::image genMap_impl(unsigned cmapID, spiral mode, float xmin, float ymin, float width, int tileSize); vector>& genSpiral(const spiral mode, vector>& buf, const std::valarray& x_vals, const std::valarray& y_vals) { std::clog << "spiral mode: " << kblib::etoi(mode) << '\n'; // Some constants constexpr float pi_up = kblib::pi(), // Pi, rounded slightly up // pi_down = 3.141592, //and down // Spiral term (shared) spiral_strength = 48, // How strong the spiral motif is // Spiral term (archimedian) spiral_tightness = 0.05f, // turning pitch of spiral spiral_tightness_s = 0.85f, spiral_sharpness = 1.40f, // how much to exaggerate peaks by (exponential) spiral_negative_bias = 0, // how much to subtract from spiral term // Plateau term plt_radius = 150, // Radius of the central plateau in ADU // plt_height = 24, //How high the central plateau is (logarithmic) plt_height_a = 48, // How high the central plateau is (archimedian) plt_cliffiness = 20, // How smooth the slope to the central plateau is plt_cliffiness_x = 1 / plt_cliffiness, // plt_prescale = 1.15, //Scales the plateau term before normalization plt_flatness = 1.3f, // How much to flatten the central plateau by // plt_l10n_adj = plt_height_a/35-pi_down, //Reduces the large-scale // effect of the plateau term plateau_hinge = 160, // plt_l10n_adj2 = plt_l10n_adj/24, // Noise term noise_hinge = 155, // Hinge which noise will be scaled away from noise_scale = 1.20f, // How much to scale noise by // Adjustment // sea_level = 135, sea_adjustment = noise_hinge - 8; // Overall factor to adjust the map height by const float // Spiral term (log) spiral_curviness = 1.12f, // Base of the logarithm used for the spiral spiral_curviness_x = 1 / logf(spiral_curviness); // vector>> pc(x_vals.size(), // vector>(y_vals.size())); // ts.push_back({steady_clock::now(),"Process setup"}); for (size_t x = 0; x < x_vals.size(); ++x) { for (size_t y = 0; y < y_vals.size(); ++y) { auto r = polar_r(x_vals[x], y_vals[y]), t = polar_t(x_vals[x], y_vals[y]); // float r = sqrt(x_vals[x]*x_vals[x]*.1f+y_vals[y]*y_vals[y]*.1f); // float t = atan2(x_vals[x],y_vals[y]); buf[x][y] = (buf[x][y] - noise_hinge) * noise_scale + sea_adjustment; if (mode == spiral::archimedian) { buf[x][y] += static_cast( spiral_strength * std::pow((std::sin(t + spiral_tightness * r) + 1.f), spiral_sharpness) - spiral_negative_bias); } else if (mode == spiral::logarithmic) { buf[x][y] += static_cast( spiral_strength * std::pow( (std::sin(t + spiral_curviness_x * std::log(r)) + 1.f), spiral_sharpness) - spiral_negative_bias); } else if (mode == spiral::polynomial) { auto f = [](float x) { return std::sqrt(x); }; buf[x][y] += spiral_strength * (std::pow((std::sin(t + spiral_tightness_s * f(r)) + 1.f), spiral_sharpness) - spiral_negative_bias); } auto p = atan(plt_cliffiness_x * (r - plt_radius)) / (pi_up) + 0.5; buf[x][y] = static_cast( (buf[x][y] - plateau_hinge) / ((1 + square(plt_flatness)) - square(plt_flatness * p)) + plt_height_a * (1 - p) + plateau_hinge); } } return buf; } static inline bool absLess(float a, float b) { return std::abs(a) < std::abs(b); } template auto deref(std::pair&& p) { return std::tie(*p.first, *p.second); } vector>& genWeather(vector>& buf, const std::valarray& x_vals, const std::valarray& y_vals) { assert(x_vals.size() == y_vals.size() && x_vals.size() > 0); auto [minx, maxx] = deref(std::minmax_element(begin(x_vals), end(x_vals), absLess)); auto [miny, maxy] = deref(std::minmax_element(begin(x_vals), end(x_vals), absLess)); double minr{std::sqrt(square(minx) + square(miny))}, maxr{std::sqrt(square(maxx) + square(maxy)) + 1}; clog << minr << ", " << maxr << endl; auto tempBuf = OlsenNoise::genNoise(0, std::floor(minr), 1, std::ceil(maxr - minr))[0]; auto rainBuf = OlsenNoise::genNoise(1024, std::floor(minr), 1, std::ceil(maxr - minr))[0]; for (size_t x = 0; x < x_vals.size(); ++x) { for (size_t y = 0; y < y_vals.size(); ++y) { double r = std::sqrt(x_vals[x] * x_vals[x] + y_vals[y] * y_vals[y]); buf[x][y] = rainBuf.at(std::llround(r - minr)) / 1.2f - 60 * std::atan(r / 1000 - .2) + 40; } } return buf; } [[deprecated]] png::palette genColorMap(int type) { return current_cmap[type].second; png::palette ret(256); if (type >= numMaps) return ret; switch (type) { case 0: // Greyscale for (short x = 0; x < 256; ++x) { ret[x] = png::color(x, x, x); } break; case 1: // Natural for (short x = 0; x < 256; ++x) { if (x < 132) { // ocean ret[x] = png::color(0, x / 3 + 4 * static_cast( static_cast(x) * x / 768.), x + 70); } else if (x < 136) { // beach ret[x] = png::color(240, 240, 64); } else if (x < 200) { // grass, forests ret[x] = png::color(48 - (x - 168), 180 - (x - 168), (x - 100) / 2); } else if (x < 248) { // Prevent underflow of red ret[x] = png::color(0, 180 - (x - 168), (x - 160)); } else if (x < 252) { // snowy forest ret[x] = png::color(x - 64, 248, x - 24); } else { // snow ret[x] = png::color(x - 24, x - 12, x - 8); // Make snow bluish } } break; case 2: // Arctic // Broken for (short x = 0; x < 256; ++x) { if (x < 149) { ret[x] = png::color(0, 0, x + 77); } else if (x == 149) { ret[x] = png::color(0, 128, 255); } else if (x < 155) { ret[x] = png::color(240 - 3 * (x - 155), 240, 64 + 4 * (x - 155)); } else if (x < 162) { ret[x] = png::color(32 - (x - 162), 180 - (x - 162), 0 + (x - 162)); } else if (x < 210) { ret[x] = png::color(0, 128, 32); } else { ret[x] = png::color(x - 4, x - 2, x); // Make snow bluish } } break; case 3: // Alien // Broken for (short x = 0; x < 256; ++x) { if (x < 149) { // 0+ ret[x] = png::color(0, 0, x + 77); } else if (x == 149) { // 149+ ret[x] = png::color(0, 128, 255); } else if (x < 162) { // 150+ ret[x] = png::color(240 - 3 * (x - 155), 240, 64 + 4 * (x - 155)); } else if (x < 182) { // 162+ ret[x] = png::color(32 - (x - 162), 180 - (x - 162), (x - 162)); } else if (x < 210) { ret[x] = png::color(0, 128, 32); } else { // 182+ ret[x] = png::color(x - 4, x - 2, x); // Make snow bluish } } break; case 4: // Purple, red (Natural * RGB=>GRB) for (short x = 0; x < 256; ++x) { ret[x] = png::color(cmap[1][x][1], cmap[1][x][0], cmap[1][x][2]); } break; case 5: // Water ratio map for (short x = 0; x < 256; ++x) { if (x < 132) { // ocean ret[x] = png::color(0, 0, 100); } else { // land ret[x] = png::color(48, 180, 48); } } break; case 6: // Elevation for (short x = 0; x < 256; ++x) { if (x >= 132 && x <= 135) { // shore ret[x] = png::color(64, 64, 64); } else if (x < 132 && ((x - 132) % 10 == 0)) { // ocean depth marks ret[x] = png::color(128, 128, 128); } else if (x > 135 && ((x - 135) % 10 == 0)) { // land height marks ret[x] = png::color(192, 192, 192); } else { ret[x] = png::color(255, 255, 255); } } break; case 7: // Elevation-color for (short x = 0; x < 256; ++x) { if (x >= 132 && x <= 135) { // shore ret[x] = png::color(64, 64, 64); } else if (x < 132 && ((x - 132) % 10 == 0)) { // ocean depth marks ret[x] = png::color(128, 128, 128); } else if (x > 135 && ((x - 135) % 10 == 0)) { // land height marks ret[x] = png::color(192, 192, 192); } else { ret[x] = png::color(255, 255, 255); } } break; case 15: // Hash { int h = -1; for (size_t x = 0; x < 256; ++x) { h = FNV32a(&h, sizeof(h)); ret[x] = png::color(h & 255, (h >> 8) & 255, (h >> 16) & 255); } } break; default: // Fill cmap.h yourself and rebuild to get new maps return current_cmap[type].second; // for (short x = 0; x < 256; ++x) { // ret[x] = // png::color(cmap[type][x][0],cmap[type][x][1],cmap[type][x][2]); // } break; } return ret; }