2025-08-03 14:57:36 +08:00

178 lines
4.7 KiB
C++

#ifndef ISTD_TILEMAP_NOISE_H
#define ISTD_TILEMAP_NOISE_H
#include "xoroshiro.h"
#include <array>
#include <cstdint>
#include <vector>
namespace istd {
/**
* @brief Discrete random noise generator for terrain replacement operations
*
* Provides high-quality discrete random values based on Xoroshiro128++ RNG.
* Used for selecting terrain types during mountain smoothing operations.
*/
class DiscreteRandomNoise {
private:
std::uint64_t mask;
std::array<std::uint8_t, 256> permutation_;
std::uint8_t perm(int x) const noexcept;
std::uint32_t rot8(std::uint32_t x) const noexcept;
std::uint32_t map_once(std::uint32_t x) const noexcept;
std::uint32_t map(std::uint32_t x) const noexcept;
public:
/**
* @brief Construct a DiscreteRandomNoise generator with the given seed
* @param rng Random number generator for noise
*/
explicit DiscreteRandomNoise(Xoroshiro128PP rng) noexcept;
/**
* @brief Generate a discrete random value at the given coordinates
* @param x X coordinate
* @param y Y coordinate
* @param z Z coordinate (optional)
* @return Discrete random value between 0 and 2^64-1
*/
std::uint64_t noise(
std::uint32_t x, std::uint32_t y, std::uint32_t z = 0
) const noexcept;
};
class DiscreteRandomNoiseStream {
private:
const DiscreteRandomNoise &noise_;
std::uint32_t x_;
std::uint32_t y_;
std::uint32_t idx_;
public:
DiscreteRandomNoiseStream(
const DiscreteRandomNoise &noise, std::uint32_t x, std::uint32_t y,
std::uint32_t idx = 0
);
std::uint64_t next() noexcept;
// Adaption for STL RandomEngine named requirements
using result_type = std::uint64_t;
static constexpr result_type min() noexcept {
return std::numeric_limits<result_type>::min();
}
static constexpr result_type max() noexcept {
return std::numeric_limits<result_type>::max();
}
// Equivalent to next(), for STL compatibility
result_type operator()() noexcept;
};
class PerlinNoise {
private:
std::vector<int> permutation_;
// Helper functions for Perlin noise calculation
double fade(double t) const;
double lerp(double t, double a, double b) const;
double grad(int hash, double x, double y) const;
public:
/**
* @brief Construct a PerlinNoise generator with the given seed
* @param rng Random number generator for noise
*/
explicit PerlinNoise(Xoroshiro128PP rng);
PerlinNoise() = default;
/**
* @brief Generate 2D Perlin noise value at the given coordinates
* @param x X coordinate
* @param y Y coordinate
* @return Noise value between 0.0 and 1.0
*/
double noise(double x, double y) const;
/**
* @brief Generate octave noise (multiple frequencies combined)
* @param x X coordinate
* @param y Y coordinate
* @param octaves Number of octaves to combine
* @param persistence How much each octave contributes
* @return Noise value between 0.0 and 1.0
*/
double octave_noise(
double x, double y, int octaves, double persistence
) const;
};
/**
* @brief A wrapper that provides uniform distribution mapping for Perlin noise
*
* This class samples the noise distribution and builds a CDF (Cumulative
* Distribution Function) to map the non-uniform Perlin noise values to a
* uniform [0,1] distribution using quantiles.
*/
class UniformPerlinNoise {
private:
PerlinNoise noise_;
Xoroshiro128PP calibrate_rng_;
std::vector<double> cdf_values_; // Sorted noise values for CDF
bool is_calibrated_;
// Parameters used for calibration
double scale_;
int octaves_;
double persistence_;
public:
/**
* @brief Construct a UniformPerlinNoise generator
* @param seed Random seed for noise generation
*/
explicit UniformPerlinNoise(Xoroshiro128PP rng);
UniformPerlinNoise() = default;
/**
* @brief Calibrate the noise distribution by sampling
* @param scale The scale parameter that will be used for generation
* @param octaves Number of octaves for octave noise
* @param persistence Persistence for octave noise
* @param sample_size Number of samples to use for CDF (default: 10000)
*/
void calibrate(
double scale, int octaves, double persistence, int sample_size = 10000
);
/**
* @brief Generate uniform noise value at the given coordinates
* @param x X coordinate
* @param y Y coordinate
* @return Uniformly distributed noise value between 0.0 and 1.0
* @note Must call calibrate() first
*/
double uniform_noise(double x, double y) const;
/**
* @brief Check if the noise generator has been calibrated
*/
bool is_calibrated() const {
return is_calibrated_;
}
private:
/**
* @brief Map a raw noise value to uniform distribution using the CDF
* @param raw_value Raw noise value from Perlin noise
* @return Uniformly distributed value between 0.0 and 1.0
*/
double map_to_uniform(double raw_value) const;
};
} // namespace istd
#endif