#ifndef MAPGEN_H_INCLUDED #define MAPGEN_H_INCLUDED #include "image.h" #include #include #include #include #include #include #include #include #include #include template using buffer_t = Eigen::Array; using buffer_type = buffer_t; using point_t = Eigen::Matrix; using float_range = kblib::range_t; using row_array = Eigen::Array; using col_array = Eigen::Array; template struct row_iterator { Array& arr; Eigen::Index i; auto operator*() const -> decltype(auto) { return arr.row(i); } auto operator++() -> row_iterator& { ++i; return *this; } auto operator++(int) -> row_iterator { auto it = *this; ++(*this); return it; } auto operator==(const row_iterator& other) const noexcept { return i == other.i and &arr == &other.arr; } auto operator!=(const row_iterator& other) const noexcept -> bool { return not (*this == other); } }; template row_iterator(Array&, Eigen::Index) -> row_iterator; template auto rows(Array& arr) { return kblib::indirect(row_iterator{arr, 0}, row_iterator{arr, arr.rows()}); } template struct col_iterator { Array& arr; Eigen::Index i; auto operator*() const -> decltype(auto) { return arr.col(i); } auto operator++() -> col_iterator& { ++i; return *this; } auto operator++(int) -> col_iterator { auto it = *this; ++(*this); return it; } auto operator==(const col_iterator& other) const noexcept { return i == other.i and &arr == &other.arr; } auto operator!=(const col_iterator& other) const noexcept -> bool { return not (*this == other); } }; template col_iterator(Array&, Eigen::Index) -> col_iterator; template auto cols(Array& arr) { return kblib::indirect(col_iterator{arr, 0}, col_iterator{arr, arr.cols()}); } template struct index_iterator { Array& arr; Eigen::Index i; auto operator*() const -> decltype(auto) { return arr[i]; } auto operator++() -> index_iterator& { ++i; return *this; } auto operator++(int) -> index_iterator { auto it = *this; ++(*this); return it; } auto operator==(const index_iterator& other) const noexcept { return i == other.i and &arr == &other.arr; } auto operator!=(const index_iterator& other) const noexcept -> bool { return not (*this == other); } }; template index_iterator(Array&, Eigen::Index) -> index_iterator; template auto traverse(Array& arr) { return kblib::indirect(index_iterator{arr, 0}, index_iterator{arr, arr.rows()}); } enum bias_mode { none, archimedian = 1, a = archimedian, logarithmic = 2, l = logarithmic, both = 3, b = both, }; struct params { struct entry { float params::*var; float def; std::string_view desc; }; constexpr static float pi = kblib::pi(); float // Spiral term (shared) spiral_strength = 80, // How strong the spiral motif is // Spiral term (archimedian) spiral_tightness = 0.05f, // turning pitch of spiral spiral_sharpness = 1.40f, // how much to exaggerate peaks by (exponential) spiral_negative_bias = 1.2f, // how much to subtract from spiral term spiral_flatness = 1 / 1.25f, // how much to dampen peaks by (linear) // Plateau term plt_radius = 100, // 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_prescale = 1.15f, // Scales the plateau term before normalization plt_flatness = 2.5f, // How much to flatten the central plateau by plateau_hinge = 120, // Noise term noise_bias = 60, // Amount to adjust noise by (post-scaling) 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, // Spiral term (log) spiral_curviness = 1.12f; // Base of the logarithm used for the spiral #define DER(name, value, desc) \ constexpr float name() const noexcept { return (value); } \ static_assert(true) DER(plt_cliffiness_x, 1 / plt_cliffiness, ""); DER(plt_l10n_adj, plt_height_a / 35 - pi, "Reduces the large-scale effect of the plateau term"); DER(plt_l10n_adj2, plt_l10n_adj() / 24, ""); DER(sea_adjustment, noise_hinge - 8, "Overall factor to adjust the map height by"); DER(spiral_curviness_x, 1 / std::log(spiral_curviness), ""); #undef DER // clang-format off #define VAR(name, ...) { #name, { ¶ms::name, __VA_ARGS__ } } // clang-format on inline const static std::unordered_map entries{ // Spiral term (shared) VAR(spiral_strength, 80, "How strong the spiral motif is"), // Spiral term (archimedian) VAR(spiral_tightness, 0.05f, "turning pitch of spiral"), VAR(spiral_sharpness, 1.40f, "how much to exaggerate peaks by (exponential)"), VAR(spiral_negative_bias, 1.2f, "how much to subtract from spiral term"), VAR(spiral_flatness, 1 / 1.25f, "how much to dampen peaks by (linear)"), // Plateau term VAR(plt_radius, 100, "Radius of the central plateau in ADU"), VAR(plt_height, 24, "How high the central plateau is (logarithmic) [unused]"), VAR(plt_height_a, 48, "How high the central plateau is (archimedian)"), VAR(plt_cliffiness, 20, "How smooth the slope to the central plateau is"), VAR(plt_prescale, 1.15f, "Scales the plateau term before normalization [unused]"), VAR(plt_flatness, 2.5f, "How much to flatten the central plateau by"), VAR(plateau_hinge, 120, ""), // Noise term VAR(noise_bias, 60, "Amount to adjust noise by (post-scaling)"), VAR(noise_hinge, 155, "Hinge which noise will be scaled away from"), VAR(noise_scale, 1.20f, "How much to scale noise by"), // Adjustment VAR(sea_level, 135, "[unused]"), // Spiral term (log) VAR(spiral_curviness, 1.12f, "Base of the logarithm used for the spiral"), }; #undef VAR friend std::istream& operator>>(std::istream& is, params& p); friend std::ostream& operator<<(std::ostream& os, params& p); std::ostream& write_verbose(std::ostream& os); }; using palette = std::vector; using cmap_t = std::vector>; // Parses a file into a set of named palettes cmap_t scan_cmap(std::string_view maps, std::string_view filename); struct sRGB_tuple { double rgb[3]; double& operator[](std::size_t i) noexcept { return rgb[i]; } const double& operator[](std::size_t i) const noexcept { return rgb[i]; } double& red() noexcept { return rgb[0]; } const double& red() const noexcept { return rgb[0]; } double& green() noexcept { return rgb[1]; } const double& green() const noexcept { return rgb[1]; } double& blue() noexcept { return rgb[2]; } const double& blue() const noexcept { return rgb[2]; } }; palette sRGB_to_cmap(gsl::span input) noexcept; // Opens file and calls scan_cmap on it inline cmap_t read_cmap(const char* file) { auto maps = kblib::get_file_contents(file); if (not maps) { return {}; } else { return scan_cmap(*maps, file); } } extern const std::string_view cmaps_default; extern cmap_t current_cmap; // Find map named by query // 1. find exact match // 2. convert query to number unsigned lookupMap(std::string_view query); class malformed_file_error : public std::runtime_error { public: malformed_file_error(const std::string& e) : std::runtime_error(e) {} }; extern bool verbose; buffer_type& genNoise(buffer_type& buf, const row_array& x_vals, const col_array& y_vals); buffer_type genSpiral(bool archimedian, const params& p, const row_array& x_vals, const col_array& y_vals); buffer_type genWeather(const row_array& x_vals, const col_array& y_vals); using noise_buffer = std::unique_ptr>; template pnm::image genMap(unsigned cmapID, bias_mode mode, const params& p, float xmin, float ymin, float width, std::uint16_t tileSize, int seed = 5867) { std::unique_ptr noise_maker{ FastNoiseSIMD::NewFastNoiseSIMD()}; noise_maker->SetNoiseType(FastNoiseSIMD::Simplex); noise_maker->SetSeed(seed); noise_maker->SetFractalOctaves(10); noise_maker->SetFractalGain(0.65f); noise_maker->SetFractalLacunarity(2.06f); noise_maker->SetFrequency(0.0009f); auto buf = noise_buffer(noise_maker->GetSimplexFractalSet( xmin * 10, ymin * 10, 0, tileSize, tileSize, 1, 0.5f)); row_array x_vals = row_array::LinSpaced(tileSize, xmin, xmin + width); col_array y_vals = col_array::LinSpaced(tileSize, ymin, ymin + width); auto abuf = buffer_type::Map(buf.get(), tileSize, tileSize); abuf = (abuf - p.noise_hinge) * p.noise_scale + p.noise_hinge + p.noise_bias; // abuf = (abuf)*480.f + 60; if (mode & logarithmic) { abuf += genSpiral(false, p, x_vals, y_vals); } if (mode & archimedian) { abuf += genSpiral(true, p, x_vals, y_vals); } void(genWeather(x_vals, y_vals)); pnm::image img(tileSize, tileSize); const palette& cpalette = current_cmap[cmapID].second; const buffer_type scaled = abuf / 256 * cpalette.size(); const size_t clipmin = 0, clipmax = cpalette.size() - 1; const float limmin = 0 + .5f, limmax = static_cast(cpalette.size()) - 0.5f; const buffer_t V = scaled.unaryExpr([=](float h) -> std::size_t { return (h < limmin) ? clipmin : ((h > limmax) ? clipmax : static_cast(h)); }); for (const auto y : kblib::range(tileSize)) { for (const auto x : kblib::range(tileSize)) { img[y][x] = cpalette[V(x, y)]; } } return img; } #endif // MAPGEN_H_INCLUDED