mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-10 17:46:53 -10:00
This is similar to the script I wrote a few years ago but unlike that one, this solution doesn't kill the performance.
206 lines
4.6 KiB
C++
206 lines
4.6 KiB
C++
#ifndef RANDOM_RANGE_H
|
|
#define RANDOM_RANGE_H
|
|
|
|
#include <random>
|
|
#include <type_traits>
|
|
|
|
#include "globalincs/pstypes.h"
|
|
#include "parse/parselo.h"
|
|
|
|
namespace particle {
|
|
namespace util {
|
|
/**
|
|
* @defgroup particleUtils Particle Effect utilities
|
|
*
|
|
* @ingroup particleSystems
|
|
*/
|
|
|
|
namespace {
|
|
|
|
/**
|
|
* @brief Parses a generic list of numbers
|
|
* @param list The array where the numbers should be stored.
|
|
* @return The amount of parsed numbers
|
|
*
|
|
* @ingroup particleUtils
|
|
*/
|
|
template<typename T, size_t N>
|
|
size_t parse_number_list(T (& list)[N]) {
|
|
float helpList[N];
|
|
auto num = stuff_float_list(helpList, N);
|
|
|
|
for (size_t i = 0; i < num; ++i) {
|
|
list[i] = static_cast<T>(helpList[i]);
|
|
}
|
|
|
|
return num;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Generic class for generating numbers in a specific range
|
|
*
|
|
* This allows to use a generic value, distribution and generator type. It's valid to only use one value for the range
|
|
* in which case the returned value is constant.
|
|
*
|
|
* @ingroup particleUtils
|
|
*/
|
|
template<typename Value, typename Distribution, typename Generator>
|
|
class RandomRange {
|
|
public:
|
|
typedef Distribution DistributionType;
|
|
typedef Generator GeneratorType;
|
|
typedef Value ValueType;
|
|
|
|
private:
|
|
GeneratorType m_generator;
|
|
DistributionType m_distribution;
|
|
|
|
bool m_constant;
|
|
ValueType m_constantValue;
|
|
|
|
public:
|
|
template<typename... Ts>
|
|
RandomRange(ValueType param1, ValueType param2, Ts&& ... distributionParameters) :
|
|
m_generator(std::random_device()()),
|
|
m_distribution(param1, param2, distributionParameters...) {
|
|
m_constantValue = static_cast<ValueType>(0.0);
|
|
m_constant = false;
|
|
}
|
|
|
|
explicit RandomRange(const ValueType& val) : RandomRange() {
|
|
m_constantValue = val;
|
|
m_constant = true;
|
|
}
|
|
|
|
RandomRange() :
|
|
m_generator(std::random_device()()),
|
|
m_distribution() {
|
|
m_constantValue = static_cast<ValueType>(0.0);
|
|
m_constant = true;
|
|
}
|
|
|
|
/**
|
|
* @brief Determines the next random number of this range
|
|
* @return The random number
|
|
*/
|
|
ValueType next() {
|
|
if (m_constant) {
|
|
return m_constantValue;
|
|
}
|
|
|
|
return m_distribution(m_generator);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief A random range with a normal distribution
|
|
*
|
|
* The range parameters are passed directly to std::normal_distribution
|
|
*
|
|
* @ingroup particleUtils
|
|
*/
|
|
template<typename Value>
|
|
using NormalRange = RandomRange<Value,
|
|
std::normal_distribution<Value>,
|
|
std::minstd_rand>;
|
|
|
|
/**
|
|
* @brief A normal range which uses floats
|
|
*
|
|
* @ingroup particleUtils
|
|
*/
|
|
typedef NormalRange<float> NormalFloatRange;
|
|
|
|
/**
|
|
* @brief A function for parsing a normal range
|
|
* @return The parsed normal range
|
|
*
|
|
* @ingroup particleUtils
|
|
*/
|
|
template<typename Value>
|
|
NormalRange<Value> parseNormalRange() {
|
|
Value valueList[2];
|
|
auto num = parse_number_list(valueList);
|
|
|
|
if (num == 0) {
|
|
error_display(0, "Need at least one value to form a random range!");
|
|
return NormalRange<Value>();
|
|
}
|
|
else if (num == 1) {
|
|
return NormalRange<Value>(valueList[0]);
|
|
}
|
|
|
|
return NormalRange<Value>(valueList[0], valueList[1]);
|
|
}
|
|
|
|
/**
|
|
* @brief A generic random range which uses a uniform distribution
|
|
*
|
|
* @ingroup particleUtils
|
|
*/
|
|
template<typename Value>
|
|
using UniformRange = RandomRange<Value,
|
|
typename std::conditional<std::is_integral<Value>::value,
|
|
std::uniform_int_distribution<Value>,
|
|
std::uniform_real_distribution<Value>>::type,
|
|
std::minstd_rand>;
|
|
|
|
/**
|
|
* @brief A uniform range which uses floats
|
|
*
|
|
* @ingroup particleUtils
|
|
*/
|
|
typedef UniformRange<float> UniformFloatRange;
|
|
|
|
/**
|
|
* @brief A uniform range which uses ints
|
|
*
|
|
* @ingroup particleUtils
|
|
*/
|
|
typedef UniformRange<int> UniformIntRange;
|
|
|
|
/**
|
|
* @brief A uniform range which uses uints
|
|
*
|
|
* @ingroup particleUtils
|
|
*/
|
|
typedef UniformRange<uint> UniformUIntRange;
|
|
|
|
/**
|
|
* @brief Parses a generic uniform range
|
|
* @param allowNegativ If @c true, negative values will be allowed
|
|
* @return The parsed uniform range
|
|
*
|
|
* @ingroup particleUtils
|
|
*/
|
|
template<typename Value>
|
|
UniformRange<Value> parseUniformRange(bool allowNegativ = false) {
|
|
Value valueList[2];
|
|
auto num = parse_number_list(valueList);
|
|
|
|
if (num == 0) {
|
|
error_display(0, "Need at least one value to form a random range!");
|
|
return UniformRange<Value>();
|
|
}
|
|
else if (num == 1) {
|
|
return UniformRange<Value>(valueList[0]);
|
|
}
|
|
|
|
if (valueList[0] > valueList[1]) {
|
|
error_display(0, "Minimum value %f is more than maximum value %f!", (float) valueList[0], (float) valueList[1]);
|
|
std::swap(valueList[0], valueList[1]);
|
|
}
|
|
|
|
if (!allowNegativ && valueList[0] < 0.0) {
|
|
error_display(0, "Minimum value may not be less than zero, got %f", (float) valueList[0]);
|
|
valueList[0] = static_cast<Value>(0.0);
|
|
}
|
|
|
|
return UniformRange<Value>(valueList[0], valueList[1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // RANDOM_RANGE_H
|