#pragma once
#include "Isaac.h"
///
/// Global utility classes and functions that don't really fit anywhere else, but are
/// too small to justify being in their own file.
///
namespace EmberNs
{
#ifndef _WIN32
#define THREAD_PRIORITY_LOWEST 1
#define THREAD_PRIORITY_BELOW_NORMAL 25
#define THREAD_PRIORITY_NORMAL 50
#define THREAD_PRIORITY_ABOVE_NORMAL 75
#define THREAD_PRIORITY_HIGHEST 99
#endif
///
/// Enum to encapsulate and add type safety to the thread priority defines.
///
enum eThreadPriority
{
LOWEST = THREAD_PRIORITY_LOWEST,//-2
BELOW_NORMAL = THREAD_PRIORITY_BELOW_NORMAL,//-1
NORMAL = THREAD_PRIORITY_NORMAL,//0
ABOVE_NORMAL = THREAD_PRIORITY_ABOVE_NORMAL,//1
HIGHEST = THREAD_PRIORITY_HIGHEST//2
};
///
/// Thin wrapper around std::find_if() to relieve the caller of having to
/// pass the implicitly obvious .begin() and .end(), and then compare the results to .end().
///
/// The container to call find_if() on
/// The lambda to call on each element
/// True if pred returned true once, else false.
template
static inline bool FindIf(c& container, pr pred)
{
return std::find_if(container.begin(), container.end(), pred) != container.end();
}
///
/// Thin wrapper around std::find_if() determine if a value exists at least once.
///
/// The container to call find_if() on
/// The value to search for
/// True if the value was contained at least once, else false.
template
static inline bool Contains(c& container, const T& val)
{
return std::find_if(container.begin(), container.end(), [&](const T& t) -> bool { return t == val; }) != container.end();
}
///
/// Thin wrapper around computing the total size of a vector.
///
/// The vector to compute the size of
/// The size of one element times the length.
template
static inline size_t SizeOf(vector& vec)
{
return sizeof(vec[0]) * vec.size();
}
///
/// After a run completes, information about what was run can be saved as strings to the comments
/// section of a jpg or png file. This class is just a container for those values.
///
class EMBER_API EmberImageComments
{
public:
///
/// Empty destructor.
/// Needed to eliminate warnings about inlining.
///
~EmberImageComments()
{
}
///
/// Set all values to the empty string.
///
void Clear()
{
m_Genome = "";
m_Badvals = "";
m_NumIters = "";
m_Runtime = "";
}
string m_Genome;
string m_Badvals;
string m_NumIters;
string m_Runtime;
};
///
/// Since running is an incredibly complex process with multiple points of possible failure,
/// it's important that as much information as possible is captured if something goes wrong.
/// Classes wishing to capture this failure information will derive from this class and populate
/// the vector of strings with any useful error information. Note that a small complication can occur
/// when a class derives from this class, yet also has one or more members which do too. In that case, they should
/// override the methods to aggregate the error information from themselves, as well as their members.
///
class EMBER_API EmberReport
{
public:
///
/// Virtual destructor needed for virtual classes.
///
virtual ~EmberReport() { }
///
/// Write the entire error report as a single string to the console.
/// Derived classes with members that also derive from EmberReport should override this to capture
/// their error information as well as that of their members.
///
virtual void DumpErrorReport() { cout << ErrorReportString(); }
///
/// Clear the error report string vector.
/// Derived classes with members that also derive from EmberReport should override this to clear
/// their error information as well as that of their members.
///
virtual void ClearErrorReport() { m_ErrorReport.clear(); }
///
/// Return the entire error report as a single string.
/// Derived classes with members that also derive from EmberReport should override this to capture
/// their error information as well as that of their members.
///
/// The entire error report as a single string. Empty if no errors.
virtual string ErrorReportString() { return StaticErrorReportString(m_ErrorReport); }
///
/// Return the entire error report as a vector of strings.
/// Derived classes with members that also derive from EmberReport should override this to capture
/// their error information as well as that of their members.
///
/// The entire error report as a vector of strings. Empty if no errors.
virtual vector ErrorReport() { return m_ErrorReport; }
///
/// Add string to report.
///
/// The string to add
virtual void AddToReport(const string& s) { if (!Contains(m_ErrorReport, s)) m_ErrorReport.push_back(s); }
///
/// Add a vector of strings to report.
///
/// The vector of strings to add
virtual void AddToReport(const vector& vec) { for (auto& v : vec) AddToReport(v); }
///
/// Static function to dump a vector of strings passed in.
///
/// The vector of strings to dump
static void StaticDumpErrorReport(const vector& errorReport) { cout << StaticErrorReportString(errorReport); }
///
/// Static function to return the entire error report passed in as a single string.
///
/// The vector of strings to concatenate
/// A string containing all strings in the vector passed in separated by newlines
static string StaticErrorReportString(const vector& errorReport)
{
stringstream ss;
for (auto& s : errorReport) ss << s << endl;
return ss.str();
}
private:
vector m_ErrorReport;
};
///
/// Open a file in binary mode and read its entire contents into a vector of bytes. Optionally null terminate.
///
/// The full path to the file to read
/// The vector which will be populated with the file's contents
/// Whether to append a NULL character as the last element of the vector. Needed when reading text files. Default: true.
/// True if successfully read and populated, else false
static bool ReadFile(const char* filename, string& buf, bool nullTerminate = true)
{
bool b = false;
FILE* f = nullptr;
try
{
fopen_s(&f, filename, "rb");//Open in binary mode.
if (f)
{
struct _stat statBuf;
#if defined(_WIN32) || defined(__APPLE__)
int statResult = _fstat(f->_file, &statBuf);//Get data associated with file.
#else
int statResult = _fstat(f->_fileno, &statBuf);//Get data associated with file.
#endif
if (statResult == 0)//Check if statistics are valid.
{
buf.resize(statBuf.st_size + (nullTerminate ? 1 : 0));//Allocate vector to be the size of the entire file, with an optional additional character for nullptr.
if (buf.size() == static_cast(statBuf.st_size + 1))//Ensure allocation succeeded.
{
size_t bytesRead = fread(&buf[0], 1, statBuf.st_size, f);//Read the entire file at once.
if (bytesRead == (static_cast(statBuf.st_size)))//Ensure the number of bytes read matched what was requested.
{
if (nullTerminate)//Optionally nullptr terminate if they want to treat it as a string.
buf[buf.size() - 1] = 0;
b = true;//Success.
}
}
}
fclose(f);
f = nullptr;
}
}
catch (const std::exception& e)
{
cout << "Error: Reading file " << filename << " failed: " << e.what() << endl;
b = false;
}
catch (...)
{
cout << "Error: Reading file " << filename << " failed." << endl;
b = false;
}
if (f)
fclose(f);
return b;
}
///
/// Clear dest and copy all of the elements of vector source with elements of type U to the vector
/// dest with elements of type T.
///
/// The vector of type T to copy to
/// The vector of type U to copy from
template
static void CopyVec(vector& dest, const vector& source)
{
dest.clear();
dest.resize(source.size());
for (size_t i = 0; i < source.size(); i++)
dest[i] = static_cast(source[i]);//Valid assignment operator between T and U types must be defined somewhere.
}
///
/// Clear dest and copy all of the elements of vector source with elements of type U to the vector
/// dest with elements of type T.
/// Call a function on each element after it's been copied.
///
/// The vector of type T to copy to
/// The vector of type U to copy from
/// A function to call on each element after it's copied
template
static void CopyVec(vector& dest, const vector& source, std::function perElementOperation)
{
dest.clear();
dest.resize(source.size());
for (size_t i = 0; i < source.size(); i++)
{
dest[i] = static_cast(source[i]);//Valid assignment operator between T and U types must be defined somewhere.
perElementOperation(dest[i]);
}
}
///
/// Clear a vector of pointers to any type by checking each element for nullptr and calling delete on it, then clearing the entire vector.
/// Optionally call array delete if the elements themselves are pointers to dynamically allocated arrays.
///
/// The vector to be cleared
/// Whether to call delete or delete []. Default: false.
template
static void ClearVec(vector& vec, bool arrayDelete = false)
{
for (size_t i = 0; i < vec.size(); i++)
{
if (vec[i])
{
if (arrayDelete)
delete [] vec[i];
else
delete vec[i];
}
vec[i] = nullptr;
}
vec.clear();
}
///
/// Determine whether all elements in two containers are equal.
///
/// The first collection to compare
/// The second collection to compare
/// True if the sizes and all elements in both collections are equal, else false.
template
static bool Equal(const T& c1, const T& c2)
{
bool equal = c1.size() == c2.size();
if (equal)
{
for (auto it1 = c1.begin(), it2 = c2.begin(); it1 != c1.end(); ++it1, ++it2)
{
if (*it1 != *it2)
{
equal = false;
break;
}
}
}
return equal;
}
///
/// Thin wrapper around passing a vector to memset() to relieve
/// the caller of having to pass the size.
///
/// The vector to memset
/// The value to set each element to, default 0.
template
static inline void Memset(vector& vec, int val = 0)
{
memset(static_cast(vec.data()), val, SizeOf(vec));
}
///
/// System floor() extremely slow because it accounts for various error conditions.
/// This is a much faster version that works on data that is not NaN.
///
/// The value to return the floor of
/// The floored value
template
static inline intmax_t Floor(T val)
{
if (val >= 0)
{
return static_cast(val);
}
else
{
intmax_t i = static_cast(val);//Truncate.
return i - (i > val);//Convert trunc to floor.
}
}
///
/// Clamp and return a value to be greater than or equal to a specified minimum and less than
/// or equal to a specified maximum.
///
/// The value to be clamped
/// A value which the clamped value must be greater than or equal to
/// A value which the clamped value must be less than or equal to
/// The clamped value
template
static inline T Clamp(T val, T min, T max)
{
if (val < min)
return min;
else if (val > max)
return max;
else
return val;
}
///
/// Clamp and return a value to be greater than or equal to a specified minimum and less than
/// or equal to a specified maximum. If lesser, the value is fmod(val - min, max - min). If greater,
/// the value is max - fmod(max - val, max - min).
///
/// The value to be clamped
/// A value which the clamped value must be greater than or equal to
/// A value which the clamped value must be less than or equal to
/// The clamped and modded value
template
static inline T ClampMod(T val, T min, T max)
{
if (val < min)
return min + fmod(val - min, max - min);
else if (val > max)
return max - fmod(max - val, max - min);
else
return val;
}
///
/// Similar to Clamp(), but clamps a reference value in place rather than returning.
///
/// The reference value to be clamped in place
/// A value which the clamped value must be greater than or equal to
/// A value which the clamped value must be less than or equal to
template
static inline void ClampRef(T& val, T min, T max)
{
if (val < min)
val = min;
else if (val > max)
val = max;
}
///
/// Similar to Clamp(), but clamps a reference value in place rather than returning.
///
/// The reference value to be clamped in place
/// A value which the clamped value must be less than or equal to
template
static inline void ClampLteRef(T& val, T lte)
{
if (val > lte)
val = lte;
}
///
/// Clamp and return a value to be greater than or equal to a specified value.
/// Useful for ensuring something is not less than zero.
///
/// The value to be clamped
/// A value which the clamped value must be greater than or equal to
/// The clamped value
template
static inline T ClampGte(T val, T gte)
{
return (val < gte) ? gte : val;
}
///
/// Similar to Clamp(), but clamps a reference value in place rather than returning.
///
/// The reference value to be clamped in place
/// A value which the clamped value must be greater than or equal to
template
static inline void ClampGteRef(T& val, T gte)
{
if (val < gte)
val = gte;
}
///
/// Thin wrapper around a call to ClampGte() with a gte value of zero.
///
/// The value to be clamped
/// The clamped value
template
static inline T ClampGte0(T val)
{
return ClampGte(val, 0);
}
///
/// Thin wrapper around a call to ClampGteRef() with a gte value of zero.
///
/// The reference value to be clamped in place
template
static inline void ClampGte0Ref(T& val)
{
ClampGteRef(val, 0);
}
///
/// Return a value rounded up or down. Works for positive and negative numbers.
///
/// The value to round
/// The rounded value
template
static inline T Round(T r)
{
return (r > 0) ? static_cast(Floor(r + T(0.5))) : ceil(r - T(0.5));
}
///
/// Special rounding for certain variations, gotten from Apophysis.
///
/// The value to round
/// The rounded value
static inline float LRint(float x)
{
int temp = (x >= 0 ? static_cast(x + 0.5f) : static_cast(x - 0.5f));
return static_cast(temp);
}
///
/// Special rounding for certain variations, gotten from Apophysis.
///
/// The value to round
/// The rounded value
static inline double LRint(double x)
{
glm::int64_t temp = (x >= 0 ? static_cast(x + 0.5) : static_cast(x - 0.5));
return static_cast(temp);
}
///
/// Never really understood what this did.
///
/// The value to round
/// The rounded value
template
static inline T Round6(T r)
{
r *= 1e6;
if (r < 0)
r -= 1;
return static_cast(1e-6 * static_cast(r + T(0.5)));
}
///
/// Return -1 if the value is less than 0, 1 if it's greater and
/// 0 if it's equal to 0.
///
/// The value to inspect
/// -1, 0 or 1
template
static inline T Sign(T v)
{
return (v < 0) ? static_cast(-1) : (v > 0) ? static_cast(1) : static_cast(0);
}
///
/// Return -1 if the value is less than 0, 1 if it's greater.
/// This differs from Sign() in that it doesn't return 0.
///
/// The value to inspect
/// -1 or 1
template
static inline T SignNz(T v)
{
return (v < 0) ? static_cast(-1) : static_cast(1);
}
///
/// Return the square of the passed in value.
/// This is useful when the value is a result of a computation
/// rather than a fixed number. Otherwise, use the SQR macro.
///
/// The value to square
/// The squared value
template
static inline T Sqr(T t)
{
return t * t;
}
///
/// Taking the square root of numbers close to zero is dangerous. If x is negative
/// due to floating point errors, it can return NaN results.
///
template
static inline T SafeSqrt(T x)
{
if (x <= 0)
return 0;
return std::sqrt(x);
}
template
static inline T SafeTan(T x)
{
return x;
}
template <>
#ifdef _WIN32
static
#endif
float SafeTan(float x)
{
return std::tan(Clamp(x, FLOAT_MIN_TAN, FLOAT_MAX_TAN));
}
template <>
#ifdef _WIN32
static
#endif
double SafeTan(double x)
{
return std::tan(x);
}
///
/// Return the cube of the passed in value.
/// This is useful when the value is a result of a computation
/// rather than a fixed number. Otherwise, use the CUBE macro.
///
/// The value to cube
/// The cubed value
template
static inline T Cube(T t)
{
return t * t * t;
}
///
/// Return the hypotenuse of the passed in values.
///
/// The x distance
/// The y distance
/// The hypotenuse
template
static inline T Hypot(T x, T y)
{
return std::sqrt(SQR(x) + SQR(y));
}
///
/// Spread the values.
///
/// The x distance
/// The y distance
/// The spread
template
static inline T Spread(T x, T y)
{
return Hypot(x, y) * ((x) > 0 ? 1 : -1);
}
///
/// Unsure.
///
/// The x distance
/// The y distance
/// The powq4
template
static inline T Powq4(T x, T y)
{
return std::pow(std::fabs(x), y) * SignNz(x);
}
///
/// Unsure.
///
/// The x distance
/// The y distance
/// The powq4c
template
static inline T Powq4c(T x, T y)
{
return y == 1 ? x : Powq4(x, y);
}
///
/// Return EPS if the passed in value was zero, else return the value.
///
/// The value
/// The y distance
/// EPS or the value if it was non-zero
template
static inline T Zeps(T x)
{
return x == 0 ? EPS : x;
}
///
/// Interpolate a given percentage between two values.
///
/// The first value to interpolate between.
/// The secod value to interpolate between.
/// The percentage between the two values to calculate.
/// The interpolated value.
template
static inline T Lerp(T a, T b, T p)
{
return a + (b - a) * p;
}
///
/// Thin wrapper around a call to modf that discards the integer portion
/// and returns the signed fractional portion.
///
/// The value to retrieve the signed fractional portion of.
/// The signed fractional portion of v.
template
static inline T Fabsmod(T v)
{
T dummy;
return modf(v, &dummy);
}
///
/// Unsure.
///
/// Unsure.
/// Unsure.
/// Unsure.
/// Unsure.
template
static inline T Fosc(T p, T amp, T ph)
{
return T(0.5) - std::cos(p * amp + ph) * T(0.5);
}
///
/// Unsure.
///
/// Unsure.
/// Unsure.
/// Unsure.
template
static inline T Foscn(T p, T ph)
{
return T(0.5) - std::cos(p + ph) * T(0.5);
}
///
/// Log scale from Apophysis.
///
/// The value to log scale
/// The log scaled value
template
static inline T LogScale(T x)
{
return x == 0 ? 0 : std::log((fabs(x) + 1) * T(M_E)) * SignNz(x) / T(M_E);
}
///
/// Log map from Apophysis.
///
/// The value to log map
/// The log mapped value
template
static inline T LogMap(T x)
{
return x == 0 ? 0 : (T(M_E) + std::log(x * T(M_E))) * T(0.25) * SignNz(x);
}
///
/// Thin wrapper around calling xmlStrcmp() on an Xml tag to tell
/// if its name is a given value.
///
/// The name of the tag of the to inspect
/// The value compare against
/// True if the comparison matched, else false
static inline bool Compare(const xmlChar* name, const char* val)
{
return xmlStrcmp(name, XC(val)) != 0;
}
///
/// Determine whether the specified value is very close to zero.
/// This is useful for determining equality of float/double types.
///
/// The value to compare against
/// The tolerance. Default: 1e-6.
/// True if the value was very close to zero, else false
template
static inline bool IsNearZero(T val, T tolerance = 1e-6)
{
return (val > -tolerance && val < tolerance);
}
///
/// Determine whether a specified value is very close to another value.
/// This is useful for determining equality of float/double types.
///
/// The first value.
/// The second value.
/// The tolerance. Default: 1e-6.
/// True if the values were very close to each other, else false
template
static inline bool IsClose(T val1, T val2, T tolerance = 1e-6)
{
return IsNearZero(val1 - val2, tolerance);
}
///
/// Put an angular measurement in degrees into the range of -180 - 180.
///
/// The angle to normalize
/// The normalized angle in a range of -180 - 180
template
static inline T NormalizeDeg180(T angle)
{
angle = fmod(angle, 360);
if (angle > 180)
{
angle -= 360;
}
else if (angle < -180)
{
angle += 360;
}
return angle;
}
///
/// Put an angular measurement in degrees into the range of 0 - 360.
///
/// The angle to normalize
/// The normalized angle in a range of 0 - 360
template
static inline T NormalizeDeg360(T angle)
{
if (angle > 360 || angle < -360)
angle = fmod(angle, 360);
if (angle < 0)
angle += 360;
return angle;
}
///
/// Return a lower case copy of a string.
///
/// The string to copy and make lower case
/// The lower case string
static string ToLower(const string& str)
{
string lower;
lower.resize(str.size());//Allocate the destination space.
std::transform(str.begin(), str.end(), lower.begin(), ::tolower);//Convert the source string to lower case storing the result in the destination string.
return lower;
}
///
/// Return an upper case copy of a string.
///
/// The string to copy and make upper case
/// The upper case string
static string ToUpper(const string& str)
{
string upper;
upper.resize(str.size());//Allocate the destination space.
std::transform(str.begin(), str.end(), upper.begin(), ::toupper);//Convert the source string to lower case storing the result in the destination string.
return upper;
}
///
/// Return a copy of a string with leading and trailing occurrences of a specified character removed.
/// The default character is a space.
///
/// The string to trim
/// The character to trim. Default: space.
/// The trimmed string
static string Trim(const string& str, char ch = ' ')
{
string ret;
if (str != "")
{
size_t firstChar = str.find_first_not_of(ch);
size_t lastChar = str.find_last_not_of(ch);
if (firstChar == string::npos)
firstChar = 0;
if (lastChar == string::npos)
lastChar = str.size();
ret = str.substr(firstChar, lastChar - firstChar + 1);
}
return ret;
}
///
/// Return a copy of a file path string with the path portion removed.
///
/// The string to retrieve the path from
/// The path portion of the string
static string GetPath(const string& filename)
{
string s;
const size_t lastSlash = filename.find_last_of("\\/");
if (std::string::npos != lastSlash)
s = filename.substr(0, lastSlash + 1);
else
s = "";
return s;
}
///
/// Placeholder for a templated function to query the value of a specified system environment variable
/// of a specific type. This function does nothing as the functions for specific types implement the behavior
/// via template specialization.
///
/// The name of the environment variable to query
/// The default value to return if the environment variable was not present
/// The value of the specified environment variable if found, else default
template
static inline T Arg(char* name, T def)
{
char* ch;
T returnVal;
#ifdef WIN32
size_t len;
errno_t err = _dupenv_s(&ch, &len, name);
#else
int err = 1;
ch = getenv(name);
#endif
if (err || !ch)
returnVal = def;
else
{
T tempVal;
istringstream istr(ch);
istr >> tempVal;
if (!istr.bad() && !istr.fail())
returnVal = tempVal;
else
returnVal = def;
}
#ifdef WIN32
free(ch);
#endif
return returnVal;
}
///
/// Template specialization for Arg<>() with a type of bool.
///
/// The name of the environment variable to query
/// The default value to return if the environment variable was not present
/// The value of the specified environment variable if found, else default
template <>
#ifdef _WIN32
static
#endif
bool Arg(char* name, bool def)
{
return (Arg(name, -999) != -999) ? true : def;
}
///
/// Template specialization for Arg<>() with a type of string.
///
/// The name of the environment variable to query
/// The default value to return if the environment variable was not present
/// The value of the specified environment variable if found, else default
template <>
#ifdef _WIN32
static
#endif
string Arg(char* name, string def)
{
char* ch;
string returnVal;
#ifdef WIN32
size_t len;
errno_t err = _dupenv_s(&ch, &len, name);
#else
int err = 1;
ch = getenv(name);
#endif
if (err || !ch)
{
if (def != "")
returnVal = def;
}
else
returnVal = string(ch);
#ifdef WIN32
free(ch);
#endif
return returnVal;
}
///
/// Replaces all instances of a value within a collection, with the specified value.
/// Taken from a StackOverflow.com post.
/// Modified to account for the scenario where the find and replace strings each start with
/// the same character.
/// Template argument should be any STL container.
///
/// Collection to replace values in
/// The value to replace
/// The value to replace with
/// The number of instances replaced
template
static uint FindAndReplace(T& source, const T& find, const T& replace)
{
uint replaceCount = 0;
typename T::size_type fLen = find.size();
typename T::size_type rLen = replace.size();
for (typename T::size_type pos = 0; (pos = source.find(find, pos)) != T::npos; pos += rLen)
{
typename T::size_type pos2 = source.find(replace, pos);
if (pos != pos2)
{
replaceCount++;
source.replace(pos, fLen, replace);
}
}
return replaceCount;
}
///
/// Split a string into tokens and place them in a vector.
///
/// The string to split
/// The delimiter to split the string on
/// The split strings, each as an element in a vector.
static vector Split(const string& str, char del)
{
string tok;
vector vec;
stringstream ss(str);
while (getline(ss, tok, del))
vec.push_back(tok);
return vec;
}
///
/// Return a character pointer to a version string composed of the EMBER_OS and EMBER_VERSION values.
///
static inline const char* EmberVersion()
{
return EMBER_OS "-" EMBER_VERSION;
}
}