2023-04-25 19:59:54 -04:00
# pragma once
# include "Isaac.h"
2023-12-05 08:32:56 -05:00
# ifndef _WIN32
2024-03-04 09:49:22 -05:00
# define _strnicmp strncasecmp
2023-12-05 08:32:56 -05:00
# endif
2023-04-25 19:59:54 -04:00
/// <summary>
/// Global utility classes and functions that don't really fit anywhere else, but are
/// too small to justify being in their own file.
/// </summary>
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
/// <summary>
/// Enum to encapsulate and add type safety to the thread priority defines.
/// </summary>
enum class eThreadPriority : char
{
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
} ;
/// <summary>
/// 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().
/// </summary>
/// <param name="container">The container to call find_if() on</param>
/// <param name="pred">The lambda to call on each element</param>
/// <returns>True if pred returned true once, else false.</returns>
template < class c , class pr >
static inline bool FindIf ( c & container , pr pred )
{
return std : : find_if ( container . begin ( ) , container . end ( ) , pred ) ! = container . end ( ) ;
}
/// <summary>
/// Thin wrapper around std::find_if() determine if a value exists at least once.
/// </summary>
/// <param name="container">The container to call find_if() on</param>
/// <param name="val">The value to search for</param>
/// <returns>True if the value was contained at least once, else false.</returns>
template < class c , class T >
static inline bool Contains ( c & container , const T & val ) noexcept
{
return std : : find_if ( container . begin ( ) , container . end ( ) , [ & ] ( const T & t ) - > bool { return t = = val ; } ) ! = container . end ( ) ;
}
/// <summary>
/// Thin specialization for string because the compiler can't figure out the container template type
/// when passing string to the function above.
/// </summary>
/// <param name="str1">The string to call find() on</param>
/// <param name="str2">The string to search for</param>
/// <returns>True if str2 was present at least once, else false.</returns>
static bool Find ( const string & str1 , const string & str2 ) noexcept
{
return str1 . find ( str2 ) ! = string : : npos ;
}
/// <summary>
/// Thin wrapper around computing the total size of a vector.
/// </summary>
/// <param name="vec">The vector to compute the size of</param>
/// <returns>The size of one element times the length.</returns>
template < typename T >
static inline size_t SizeOf ( const vector < T > & vec ) noexcept
{
return sizeof ( vec [ 0 ] ) * vec . size ( ) ;
}
/// <summary>
/// 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.
/// </summary>
class EMBER_API EmberImageComments
{
public :
/// <summary>
/// Basic defaults.
/// </summary>
EmberImageComments ( ) = default ;
EmberImageComments ( const EmberImageComments & comments ) = default ;
EmberImageComments & operator = ( const EmberImageComments & comments ) = default ;
/// <summary>
/// Needed to eliminate warnings about inlining.
/// </summary>
~ EmberImageComments ( ) = default ;
/// <summary>
/// Set all values to the empty string.
/// </summary>
void Clear ( )
{
m_Genome = " " ;
m_Badvals = " " ;
m_NumIters = " " ;
m_Runtime = " " ;
}
string m_Genome ;
string m_Badvals ;
string m_NumIters ;
string m_Runtime ;
} ;
/// <summary>
/// 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.
/// </summary>
class EMBER_API EmberReport
{
public :
/// <summary>
/// Virtual destructor needed for virtual classes.
/// </summary>
virtual ~ EmberReport ( ) { }
/// <summary>
/// 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.
/// </summary>
virtual void DumpErrorReport ( ) { cout < < ErrorReportString ( ) ; }
/// <summary>
/// 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.
/// </summary>
virtual void ClearErrorReport ( ) noexcept { m_ErrorReport . clear ( ) ; }
/// <summary>
/// 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.
/// </summary>
/// <returns>The entire error report as a single string. Empty if no errors.</returns>
virtual string ErrorReportString ( ) { return StaticErrorReportString ( m_ErrorReport ) ; }
/// <summary>
/// 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.
/// </summary>
/// <returns>The entire error report as a vector of strings. Empty if no errors.</returns>
virtual vector < string > ErrorReport ( ) { return m_ErrorReport ; }
/// <summary>
/// Add string to report.
/// </summary>
/// <param name="s">The string to add</param>
virtual void AddToReport ( const string & s ) { if ( ! Contains ( m_ErrorReport , s ) ) m_ErrorReport . push_back ( s ) ; }
/// <summary>
/// Add a vector of strings to report.
/// </summary>
/// <param name="vec">The vector of strings to add</param>
virtual void AddToReport ( const vector < string > & vec ) { for ( auto & v : vec ) AddToReport ( v ) ; }
/// <summary>
/// Static function to dump a vector of strings passed in.
/// </summary>
/// <param name="errorReport">The vector of strings to dump</param>
static void StaticDumpErrorReport ( const vector < string > & errorReport ) { cout < < StaticErrorReportString ( errorReport ) ; }
/// <summary>
/// Static function to return the entire error report passed in as a single string.
/// </summary>
/// <param name="errorReport">The vector of strings to concatenate</param>
/// <returns>A string containing all strings in the vector passed in separated by newlines</returns>
static string StaticErrorReportString ( const vector < string > & errorReport )
{
stringstream ss ;
for ( auto & s : errorReport ) ss < < s < < " \n " ;
return ss . str ( ) ;
}
private :
vector < string > m_ErrorReport ;
} ;
/// <summary>
/// A base class for handling singletons that ensures only one instance exists, but
/// also deletes the instance after there are no more references to it.
/// This fixes the problem of the normal singleton pattern that uses a static function
/// variable. That pattern does not delete the instance until after main() exits
/// which can cause serious problems with certain libraries.
/// This class will delete before main exits.
/// Note that it still uses a local static variable because static templated
/// member variables cannot be exported across module boundaries.
/// Derived classes should inherit from this using the CRTP, and declare a friend to it.
/// They also should make their constructors private and destructors public.
/// Attribution: This class is a combination of
/// http://btorpey.github.io/blog/2014/02/12/shared-singletons/
/// and
/// http://enki-tech.blogspot.com/2012/08/c11-generic-singleton.html
/// </summary>
template < class T >
class Singleton
{
public :
/// <summary>
/// Create and return an instance of T.
/// </summary>
/// <param name="...args">The args to forward to the constructor of T</param>
/// <returns>A shared_ptr<T></returns>
template < typename . . . Args >
static shared_ptr < T > Instance ( Args . . . args )
{
auto & staticInstance = GetStaticInstance ( ) ;
auto temp = staticInstance . lock ( ) ;
if ( ! temp )
{
temp . reset ( new T ( std : : forward < Args > ( args ) . . . ) ) ;
staticInstance = temp ;
}
return temp ;
}
/// <summary>
/// For creating an object without passing parameters.
/// When the derived class has a default constructor, this should
/// not be called. This is only for when the derived class constructor
/// requires arguments. In that case, Instance() must first be called
/// with the proper values. Then once the singleton is constructed, this
/// can be called to just retrieve the object without having to worry about
/// parameters.
/// This is enforced by throwing if this has been called before Instance() is called.
/// </summary>
/// <returns>The constructed object</returns>
static std : : shared_ptr < T > DefInstance ( )
{
auto & staticInstance = GetStaticInstance ( ) ;
auto temp = staticInstance . lock ( ) ;
if ( ! temp )
throw " Cannot create singleton with defaults, must first call at least once with proper arguments. " ;
return temp ;
}
protected :
/// <summary>
/// Clever hack to get a static to behave like a member variable that can be seen between classes and functions in the hierarchy.
/// Also has the added benefit that it makes this work across module boundaries.
/// </summary>
/// <returns>static weak_ptr reference</returns>
static std : : weak_ptr < T > & GetStaticInstance ( )
{
static std : : weak_ptr < T > staticInstance ;
return staticInstance ;
}
} ;
//Use this if the body of the destructor will be implemented in a cpp file.
# define SINGLETON_DERIVED_DECL(x) \
friend class Singleton < x > ; \
public : \
~ x ( ) ; \
x ( const x & other ) = delete ; \
const x & operator = ( const x & other ) = delete //Semicolon deliberately omitted to force it on the caller.
2023-06-22 21:25:53 -04:00
//Use this if the body of the destructor will be implemented in a cpp file and the type is templated.
# define SINGLETON_DERIVED_DECL_T(x, T) \
friend class Singleton < x < T > > ; \
public : \
~ x ( ) ; \
x ( const x & other ) = delete ; \
const x & operator = ( const x & other ) = delete //Semicolon deliberately omitted to force it on the caller.
2023-04-25 19:59:54 -04:00
//Use this if the body of the destructor is empty and is will be implemented inline in the header file.
# define SINGLETON_DERIVED_IMPL(x) \
friend class Singleton < x > ; \
public : \
~ x ( ) { } \
x ( const x & other ) = delete ; \
const x & operator = ( const x & other ) = delete
2023-06-22 21:25:53 -04:00
//Use this if the body of the destructor is empty and is will be implemented inline in the header file and the type is templated.
# define SINGLETON_DERIVED_IMPL_T(x, T) \
friend class Singleton < x < T > > ; \
public : \
~ x ( ) { } \
x ( const x & other ) = delete ; \
const x & operator = ( const x & other ) = delete
2023-04-25 19:59:54 -04:00
/// <summary>
/// The calculations in some variations were changed from what they were in flam3/Apophysis to match Chaotica.
/// Some users prefer the old functionality, so provide an option to retain it.
/// </summary>
class EMBER_API Compat
{
public :
static bool m_Compat ;
} ;
/// <summary>
/// Open a file in binary mode and read its entire contents into a vector of bytes. Optionally null terminate.
/// </summary>
/// <param name="filename">The full path to the file to read</param>
/// <param name="buf">The string which will be populated with the file's contents</param>
/// <returns>True if successfully read and populated, else false</returns>
2024-05-08 09:31:55 -04:00
static bool ReadFile ( const char * filename , string & buf )
2023-04-25 19:59:54 -04:00
{
try
{
ifstream ifs ;
ifs . exceptions ( ifstream : : failbit ) ;
ifs . open ( filename , ios : : binary | ios : : ate ) ;
if ( const auto pos = ifs . tellg ( ) ) //Ensure it exists and wasn't empty.
{
2024-05-08 09:31:55 -04:00
buf . resize ( pos ) ;
2023-04-25 19:59:54 -04:00
ifs . seekg ( 0 , ios : : beg ) ;
ifs . read ( & buf [ 0 ] , pos ) ;
return true ;
}
}
catch ( const std : : exception & e )
{
cout < < " Error: Reading file " < < filename < < " failed: " < < e . what ( ) < < " \n " ;
}
catch ( . . . )
{
cout < < " Error: Reading file " < < filename < < " failed. \n " ;
}
return false ;
}
/// <summary>
/// Thin wrapper around std::advance that returns the advanced iterator.
/// Note the passed in iterator is constant so it will not be changed, unlike
/// std::advance does.
/// </summary>
/// <param name="it">A const reference to an iterator, a copy of which will be advanced.</param>
/// <param name="off">How far to move the iterator forward</param>
/// <returns>A copy of the passed in iterator, advanced the specified number of elements</returns>
template < class iter , class diff >
static iter Advance ( const iter & it , diff off )
{
auto temp = it ;
std : : advance ( temp , off ) ;
return temp ;
}
/// <summary>
/// Clear dest and copy all of the elements of container source with elements of type U to the container
/// dest with elements of type T.
/// </summary>
/// <param name="dest">The container of type Cdest with elements of type T to copy to</param>
/// <param name="source">The container of type Csource with elements of type U to copy from</param>
template < typename T , typename U , typename Dalloc , typename Salloc , template < typename , typename > class Cdest , template < typename , typename > class Csource >
static void CopyCont ( Cdest < T , Dalloc > & dest , const Csource < U , Salloc > & source )
{
dest . clear ( ) ;
dest . resize ( source . size ( ) ) ;
auto it1 = source . begin ( ) ;
auto it2 = dest . begin ( ) ;
for ( ; it1 ! = source . end ( ) ; it1 + + , it2 + + )
* it2 = static_cast < T > ( * it1 ) ; //Valid assignment operator between T and U types must be defined somewhere.
}
/// <summary>
/// Clear dest and copy all of the elements of container source with elements of type U to the container
/// dest with elements of type T.
/// Call a function on each element after it's been copied.
/// </summary>
/// <param name="dest">The container of type Cdest with elements of type T to copy to</param>
/// <param name="source">The container of type Csource with elements of type U to copy from</param>
/// <param name="perElementOperation">A function to call on each element after it's copied</param>
template < typename T , typename U , typename Dalloc , typename Salloc , template < typename , typename > class Cdest , template < typename , typename > class Csource >
static void CopyCont ( Cdest < T , Dalloc > & dest , const Csource < U , Salloc > & source , std : : function < void ( T & t ) > perElementOperation )
{
dest . clear ( ) ;
dest . resize ( source . size ( ) ) ;
auto it1 = source . begin ( ) ;
auto it2 = dest . begin ( ) ;
for ( ; it1 ! = source . end ( ) ; it1 + + , it2 + + )
{
* it2 = static_cast < T > ( * it1 ) ; //Valid assignment operator between T and U types must be defined somewhere.
perElementOperation ( * it2 ) ;
}
}
/// <summary>
/// Clear a container 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.
/// </summary>
/// <param name="vec">The vector to be cleared</param>
/// <param name="arrayDelete">Whether to call delete or delete []. Default: false.</param>
template < typename T , typename Alloc , template < typename , typename > class C >
static void ClearVec ( C < T * , Alloc > & cont , bool arrayDelete = false )
{
for ( auto & it : cont )
{
if ( it )
{
if ( arrayDelete )
delete [ ] it ;
else
delete it ;
}
it = nullptr ;
}
cont . clear ( ) ;
}
/// <summary>
/// Determine whether all elements in two containers are equal.
/// The container types do not have to match, but their element types do.
/// </summary>
/// <param name="c1">The first collection to compare</param>
/// <param name="c2">The second collection to compare</param>
/// <returns>True if the sizes and all elements in both collections are equal, else false.</returns>
template < typename T , typename C1Alloc , typename C2Alloc , template < typename , typename > class C1 , template < typename , typename > class C2 >
static bool Equal ( const C1 < T , C1Alloc > & c1 , const C2 < T , C2Alloc > & c2 )
{
bool equal = c1 . size ( ) = = c2 . size ( ) ;
if ( equal )
{
auto it1 = c1 . begin ( ) ;
auto it2 = c2 . begin ( ) ;
for ( ; it1 ! = c1 . end ( ) ; + + it1 , + + it2 )
{
if ( * it1 ! = * it2 )
{
equal = false ;
break ;
}
}
}
return equal ;
}
/// <summary>
/// Thin wrapper around passing a vector to memset() to relieve
/// the caller of having to pass the size.
/// </summary>
/// <param name="vec">The vector to memset</param>
/// <param name="val">The value to set each element to, default 0.</param>
template < typename T >
static inline void Memset ( vector < T > & vec , int val = 0 )
{
memset ( static_cast < void * > ( vec . data ( ) ) , val , SizeOf ( vec ) ) ;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="x">The value to return the floor of</param>
/// <returns>The floored value</returns>
template < typename T >
static inline intmax_t Floor ( T val ) noexcept
{
if ( val > = 0 )
{
return static_cast < intmax_t > ( val ) ;
}
else
{
intmax_t i = static_cast < intmax_t > ( val ) ; //Truncate.
return i - ( i > val ) ; //Convert trunc to floor.
}
}
/// <summary>
/// Clamp and return a value to be greater than or equal to a specified minimum and less than
/// or equal to a specified maximum.
/// </summary>
/// <param name="val">The value to be clamped</param>
/// <param name="min">A value which the clamped value must be greater than or equal to</param>
/// <param name="max">A value which the clamped value must be less than or equal to</param>
/// <returns>The clamped value</returns>
template < typename T >
static inline T Clamp ( T val , T min , T max ) noexcept
{
if ( val < min )
return min ;
else if ( val > max )
return max ;
else
return val ;
}
template < >
STATIC float Clamp < float > ( float val , float min , float max ) noexcept
{
if ( val < min )
return min ;
else if ( val > max )
return max ;
else if ( ! std : : isfinite ( val ) )
return min ;
else
return val ;
}
template < >
STATIC double Clamp < double > ( double val , double min , double max ) noexcept
{
if ( val < min )
return min ;
else if ( val > max )
return max ;
else if ( ! std : : isfinite ( val ) )
return min ;
else
return val ;
}
/// <summary>
/// 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).
/// </summary>
/// <param name="val">The value to be clamped</param>
/// <param name="min">A value which the clamped value must be greater than or equal to</param>
/// <param name="max">A value which the clamped value must be less than or equal to</param>
/// <returns>The clamped and modded value</returns>
template < typename T >
static inline T ClampMod ( T val , T min , T max ) noexcept
{
if ( val < min )
return min + fmod ( val - min , max - min ) ;
else if ( val > max )
return max - fmod ( max - val , max - min ) ;
else if ( ! std : : isfinite ( val ) )
return min ;
else
return val ;
}
/// <summary>
/// Similar to Clamp(), but clamps a reference value in place rather than returning.
/// </summary>
/// <param name="val">The reference value to be clamped in place</param>
/// <param name="min">A value which the clamped value must be greater than or equal to</param>
/// <param name="max">A value which the clamped value must be less than or equal to</param>
template < typename T >
static inline void ClampRef ( T & val , T min , T max ) noexcept
{
if ( val < min )
val = min ;
else if ( val > max )
val = max ;
}
template < >
STATIC void ClampRef < float > ( float & val , float min , float max ) noexcept
{
if ( val < min )
val = min ;
else if ( val > max )
val = max ;
else if ( ! std : : isfinite ( val ) )
val = min ;
}
template < >
STATIC void ClampRef < double > ( double & val , double min , double max ) noexcept
{
if ( val < min )
val = min ;
else if ( val > max )
val = max ;
else if ( ! std : : isfinite ( val ) )
val = min ;
}
/// <summary>
/// Similar to Clamp(), but clamps a reference value in place rather than returning.
/// </summary>
/// <param name="val">The reference value to be clamped in place</param>
/// <param name="gte">A value which the clamped value must be less than or equal to</param>
template < typename T >
static inline void ClampLteRef ( T & val , T lte ) noexcept
{
if ( val > lte )
val = lte ;
}
template < >
STATIC void ClampLteRef < float > ( float & val , float lte ) noexcept
{
if ( val > lte | | ! std : : isfinite ( val ) )
val = lte ;
}
template < >
STATIC void ClampLteRef < double > ( double & val , double lte ) noexcept
{
if ( val > lte | | ! std : : isfinite ( val ) )
val = lte ;
}
/// <summary>
/// Clamp and return a value to be greater than or equal to a specified value.
/// Useful for ensuring something is not less than zero.
/// </summary>
/// <param name="val">The value to be clamped</param>
/// <param name="gte">A value which the clamped value must be greater than or equal to</param>
/// <returns>The clamped value</returns>
template < typename T >
static inline T ClampGte ( T val , T gte ) noexcept
{
return ( val < gte ) ? gte : val ;
}
template < >
STATIC float ClampGte < float > ( float val , float gte ) noexcept
{
if ( val < gte | | ! std : : isfinite ( val ) )
return gte ;
else
return val ;
}
template < >
STATIC double ClampGte < double > ( double val , double gte ) noexcept
{
if ( val < gte | | ! std : : isfinite ( val ) )
return gte ;
else
return val ;
}
/// <summary>
/// Similar to Clamp(), but clamps a reference value in place rather than returning.
/// </summary>
/// <param name="val">The reference value to be clamped in place</param>
/// <param name="gte">A value which the clamped value must be greater than or equal to</param>
template < typename T >
static inline void ClampGteRef ( T & val , T gte ) noexcept
{
if ( val < gte )
val = gte ;
}
template < >
STATIC void ClampGteRef < float > ( float & val , float gte ) noexcept
{
if ( val < gte | | ! std : : isfinite ( val ) )
val = gte ;
}
template < >
STATIC void ClampGteRef < double > ( double & val , double gte ) noexcept
{
if ( val < gte | | ! std : : isfinite ( val ) )
val = gte ;
}
/// <summary>
/// Thin wrapper around a call to ClampGte() with a gte value of zero.
/// </summary>
/// <param name="val">The value to be clamped</param>
/// <returns>The clamped value</returns>
template < typename T >
static inline T ClampGte0 ( T val ) noexcept
{
return ClampGte < T > ( val , 0 ) ;
}
/// <summary>
/// Thin wrapper around a call to ClampGteRef() with a gte value of zero.
/// </summary>
/// <param name="val">The reference value to be clamped in place</param>
template < typename T >
static inline void ClampGte0Ref ( T & val ) noexcept
{
ClampGteRef < T > ( val , 0 ) ;
}
/// <summary>
/// Return a value rounded up or down. Works for positive and negative numbers.
/// </summary>
/// <param name="r">The value to round</param>
/// <returns>The rounded value</returns>
template < typename T >
static inline T Round ( T r ) noexcept
{
return ( r > 0 ) ? static_cast < T > ( Floor < T > ( r + T ( 0.5 ) ) ) : std : : ceil ( r - T ( 0.5 ) ) ;
}
/// <summary>
/// Never really understood what this did.
/// </summary>
/// <param name="r">The value to round</param>
/// <returns>The rounded value</returns>
template < typename T >
static inline T Round6 ( T r ) noexcept
{
r * = 1e6 ;
if ( r < 0 )
r - = 1 ;
return static_cast < T > ( 1e-6 * static_cast < int > ( r + T ( 0.5 ) ) ) ;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="v">The value to square</param>
/// <returns>The squared value</returns>
template < typename T >
static inline T Sqr ( T t ) noexcept
{
return t * t ;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="v">The value to cube</param>
/// <returns>The cubed value</returns>
template < typename T >
static inline T Cube ( T t ) noexcept
{
return t * t * t ;
}
template < typename T >
static inline T SafeTan ( T x ) noexcept
{
return x ;
}
template < >
STATIC float SafeTan < float > ( float x ) noexcept
{
return std : : tan ( Clamp < float > ( x , FLOAT_MIN_TAN , FLOAT_MAX_TAN ) ) ;
}
template < >
STATIC double SafeTan < double > ( double x ) noexcept
{
return std : : tan ( x ) ;
}
/// <summary>
/// Return EPS if the passed in value was zero, else return the value.
/// </summary>
/// <param name="x">The value</param>
/// <param name="y">The y distance</param>
/// <returns>EPS or the value if it was non-zero</returns>
template < typename T >
static inline T Zeps ( T x ) noexcept
{
return x = = 0 ? EPS : x ;
}
/// <summary>
/// Interpolate a given percentage between two values.
/// </summary>
/// <param name="a">The first value to interpolate between.</param>
/// <param name="b">The secod value to interpolate between.</param>
/// <param name="p">The percentage between the two values to calculate.</param>
/// <returns>The interpolated value.</returns>
template < typename T >
static inline T Lerp ( T a , T b , T p ) noexcept
{
return a + ( b - a ) * p ;
}
/// <summary>
/// Thin wrapper around calling xmlStrcmp() on an Xml tag to tell
/// if its name is a given value.
/// </summary>
/// <param name="name">The name of the tag of the to inspect</param>
/// <param name="val">The value compare against</param>
/// <returns>True if the comparison matched, else false</returns>
static inline bool Compare ( const xmlChar * name , const char * val )
{
return xmlStrcmp ( name , XC ( val ) ) ! = 0 ;
}
/// <summary>
/// Determine whether the specified value is very close to zero.
/// This is useful for determining equality of float/double types.
/// </summary>
/// <param name="val">The value to compare against</param>
/// <param name="tolerance">The tolerance. Default: 1e-6.</param>
/// <returns>True if the value was very close to zero, else false</returns>
template < typename T >
static inline bool IsNearZero ( T val , T tolerance = 1e-6 ) noexcept
{
return ( val > - tolerance & & val < tolerance ) ;
}
/// <summary>
/// Determine whether a specified value is very close to another value.
/// This is useful for determining equality of float/double types.
/// </summary>
/// <param name="val1">The first value.</param>
/// <param name="val2">The second value.</param>
/// <param name="tolerance">The tolerance. Default: 1e-6.</param>
/// <returns>True if the values were very close to each other, else false</returns>
template < typename T >
static inline bool IsClose ( T val1 , T val2 , T tolerance = 1e-6 ) noexcept
{
return IsNearZero ( val1 - val2 , tolerance ) ;
}
/// <summary>
/// Put an angular measurement in degrees into the range of -180 - 180.
/// </summary>
/// <param name="angle">The angle to normalize</param>
/// <returns>The normalized angle in a range of -180 - 180</returns>
template < typename T >
static inline T NormalizeDeg180 ( T angle ) noexcept
{
auto a = fmod ( angle , T ( 360 ) ) ;
if ( a > 180 )
a - = 360 ;
else if ( a < - 180 )
a + = 360 ;
return a ;
}
/// <summary>
/// Put an angular measurement in degrees into the range of 0 - 360.
/// </summary>
/// <param name="angle">The angle to normalize</param>
/// <returns>The normalized angle in a range of 0 - 360</returns>
template < typename T >
static inline T NormalizeDeg360 ( T angle ) noexcept
{
if ( angle > 360 | | angle < - 360 )
angle = fmod ( angle , T ( 360 ) ) ;
if ( angle < 0 )
angle + = 360 ;
return angle ;
}
/// <summary>
/// Convert an angle where 0 is up to a trigonometry style angle where 0 is to the right.
/// </summary>
/// <param name="Angle">The angle to convert</param>
/// <returns>The trig equivalent of the angle passed in</returns>
template < typename T >
static inline T ToTrigAngle ( T angle ) noexcept
{
return NormalizeDeg360 ( 90 - angle ) ;
}
/// <summary>
/// Determine whether the passed in string ends with the passed in suffix, case sensitive.
/// </summary>
/// <param name="str">The string to test</param>
/// <param name="suffix">The string to test for</param>
/// <returns>True if str ends with suffix, else false.</returns>
static bool EndsWith ( const std : : string & str , const std : : string & suffix )
{
return str . size ( ) > = suffix . size ( ) & &
str . compare ( str . size ( ) - suffix . size ( ) , suffix . size ( ) , suffix ) = = 0 ;
}
/// <summary>
/// Determine whether the passed in string starts with the passed in prefix, case sensitive.
/// </summary>
/// <param name="str">The string to test</param>
/// <param name="suffix">The string to test for</param>
--User changes
-Update various tooltips.
-Increase precision of affine and xaos spinners.
-Increase precision of fields written in Xml files to 8.
--Bug fixes
-When rendering on the CPU, if the number of threads didn't divide evenly into the number of rows, it would leave a blank spot on the last few rows.
-Fix numerous parsing bugs when reading .chaos files.
-Added compatibility fixes and/or optimizations to the following variations: asteria, bcircle, bcollide, bipolar, blob2, btransform, cell, circlecrop, circlecrop2, collideoscope, cpow2, cropn, cross, curl, depth_ngon2, depth_sine2, edisc, eRotate, escher, fan2, hex_rand, hypershift, hypershift2, hypertile1, julia, julian, julian2, juliaq, juliascope, lazyjess, log, loonie2, murl, murl2, npolar, oscilloscope2, perspective, phoenix_julia, sphericaln, squish, starblur, starblur2, truchet, truchet_glyph, waffle, wavesn.
2023-11-29 17:47:31 -05:00
/// <param name="ignoreCase">True to do a case insensitive comparisoin, else case sensitive. Default: false.</param>
2023-04-25 19:59:54 -04:00
/// <returns>True if str starts with suffix, else false.</returns>
--User changes
-Update various tooltips.
-Increase precision of affine and xaos spinners.
-Increase precision of fields written in Xml files to 8.
--Bug fixes
-When rendering on the CPU, if the number of threads didn't divide evenly into the number of rows, it would leave a blank spot on the last few rows.
-Fix numerous parsing bugs when reading .chaos files.
-Added compatibility fixes and/or optimizations to the following variations: asteria, bcircle, bcollide, bipolar, blob2, btransform, cell, circlecrop, circlecrop2, collideoscope, cpow2, cropn, cross, curl, depth_ngon2, depth_sine2, edisc, eRotate, escher, fan2, hex_rand, hypershift, hypershift2, hypertile1, julia, julian, julian2, juliaq, juliascope, lazyjess, log, loonie2, murl, murl2, npolar, oscilloscope2, perspective, phoenix_julia, sphericaln, squish, starblur, starblur2, truchet, truchet_glyph, waffle, wavesn.
2023-11-29 17:47:31 -05:00
static bool StartsWith ( const std : : string & str , const std : : string & prefix , bool ignoreCase = false )
2023-04-25 19:59:54 -04:00
{
--User changes
-Update various tooltips.
-Increase precision of affine and xaos spinners.
-Increase precision of fields written in Xml files to 8.
--Bug fixes
-When rendering on the CPU, if the number of threads didn't divide evenly into the number of rows, it would leave a blank spot on the last few rows.
-Fix numerous parsing bugs when reading .chaos files.
-Added compatibility fixes and/or optimizations to the following variations: asteria, bcircle, bcollide, bipolar, blob2, btransform, cell, circlecrop, circlecrop2, collideoscope, cpow2, cropn, cross, curl, depth_ngon2, depth_sine2, edisc, eRotate, escher, fan2, hex_rand, hypershift, hypershift2, hypertile1, julia, julian, julian2, juliaq, juliascope, lazyjess, log, loonie2, murl, murl2, npolar, oscilloscope2, perspective, phoenix_julia, sphericaln, squish, starblur, starblur2, truchet, truchet_glyph, waffle, wavesn.
2023-11-29 17:47:31 -05:00
if ( ignoreCase )
{
return str . size ( ) > = prefix . size ( ) & &
( _strnicmp ( str . c_str ( ) , prefix . c_str ( ) , std : : min ( str . length ( ) , prefix . length ( ) ) ) = = 0 ) ;
}
else
return str . size ( ) > = prefix . size ( ) & & str . compare ( 0 , prefix . size ( ) , prefix ) = = 0 ;
2023-04-25 19:59:54 -04:00
}
/// <summary>
/// Return a lower case copy of a string.
/// </summary>
/// <param name="str">The string to copy and make lower case</param>
/// <returns>The lower case string</returns>
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 ;
}
/// <summary>
/// Return an upper case copy of a string.
/// </summary>
/// <param name="str">The string to copy and make upper case</param>
/// <returns>The upper case string</returns>
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 ;
}
/// <summary>
/// Return a copy of a string with leading and trailing occurrences of a specified character removed.
/// The default character is a space.
/// </summary>
/// <param name="str">The string to trim</param>
/// <param name="ch">The character to trim. Default: space.</param>
/// <returns>The trimmed string</returns>
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 ;
}
/// <summary>
/// Split a string into tokens and place them in a vector.
/// </summary>
/// <param name="str">The string to split</param>
/// <param name="del">The delimiter to split the string on. Note that only one character has to match, so this is a good way to use multiple delimiters</param>
/// <param name="removeEmpty">True to omit empty strings, else false to keep them.</param>
/// <returns>The vector containing the split tokens</returns>
static vector < std : : string > Split ( const string & str , const string & del , bool removeEmpty = false )
{
size_t current = 0 ;
size_t next = std : : numeric_limits < size_t > : : max ( ) ;
vector < string > vec ;
do
{
current = next + 1 ;
next = str . find_first_of ( del , current ) ;
string ent ( Trim ( str . substr ( current , next - current ) ) ) ;
if ( ! removeEmpty | | ent . length ( ) > 0 )
vec . push_back ( ent ) ;
}
while ( next ! = string : : npos ) ;
return vec ;
}
/// <summary>
/// Return a copy of a file path string with the file portion removed.
/// If no path is present, such as having only a filename present, then
/// empty string is returned.
/// </summary>
/// <param name="filename">The string to retrieve the path from</param>
/// <returns>The path portion of the string</returns>
static string GetPath ( const string & filename )
{
const size_t lastSlash = filename . find_last_of ( " \\ / " ) ;
if ( lastSlash ! = std : : string : : npos )
return filename . substr ( 0 , lastSlash + 1 ) ;
else
return " " ;
}
/// <summary>
/// Return a copy of a file path string with the path portion removed.
/// </summary>
/// <param name="filename">The string to retrieve the filename from</param>
/// <returns>The filename portion of the string</returns>
static string GetFilename ( const string & filename )
{
string s ;
const size_t lastSlash = filename . find_last_of ( " \\ / " ) ;
if ( std : : string : : npos ! = lastSlash )
return filename . substr ( lastSlash + 1 , filename . size ( ) - lastSlash ) ;
else
return filename ;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="name">The name of the environment variable to query</param>
/// <param name="def">The default value to return if the environment variable was not present</param>
/// <returns>The value of the specified environment variable if found, else default</returns>
template < typename T >
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 ;
}
/// <summary>
/// Template specialization for Arg<>() with a type of bool.
/// </summary>
/// <param name="name">The name of the environment variable to query</param>
/// <param name="def">The default value to return if the environment variable was not present</param>
/// <returns>The value of the specified environment variable if found, else default</returns>
template < >
STATIC bool Arg < bool > ( char * name , bool def )
{
return ( Arg < int > ( name , - 999 ) ! = - 999 ) ? true : def ;
}
/// <summary>
/// Template specialization for Arg<>() with a type of string.
/// </summary>
/// <param name="name">The name of the environment variable to query</param>
/// <param name="def">The default value to return if the environment variable was not present</param>
/// <returns>The value of the specified environment variable if found, else default</returns>
template < >
STATIC string Arg < string > ( 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 ;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="source">Collection to replace values in</param>
/// <param name="find">The value to replace</param>
/// <param name="replace">The value to replace with</param>
/// <returns>The number of instances replaced</returns>
template < typename T >
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 ;
}
/// <summary>
/// Split a string into tokens and place them in a vector.
/// </summary>
/// <param name="str">The string to split</param>
/// <param name="del">The delimiter to split the string on</param>
/// <returns>The split strings, each as an element in a vector.</returns>
static vector < string > Split ( const string & str , char del )
{
string tok ;
vector < string > vec ;
stringstream ss ( str ) ;
while ( getline ( ss , tok , del ) )
vec . push_back ( tok ) ;
return vec ;
}
/// <summary>
/// Thin wrapper around joining a thread.
/// </summary>
/// <param name="th">The thread to join</param>
static void Join ( std : : thread & th )
{
if ( th . joinable ( ) )
th . join ( ) ;
}
/// <summary>
/// Thin wrapper around joining a vector of threads.
/// </summary>
/// <param name="vec">The vector of threads to join</param>
static void Join ( std : : vector < std : : thread > & vec )
{
for ( auto & it : vec )
Join ( it ) ;
}
/// <summary>
/// Return a character pointer to a version string composed of the EMBER_OS and EMBER_VERSION values.
/// </summary>
static inline const char * EmberVersion ( ) noexcept
{
return EMBER_OS " - " EMBER_VERSION ;
}
}