mirror of
https://bitbucket.org/mfeemster/fractorium.git
synced 2025-06-30 21:36:33 -04:00
Initial source commit
Initial source commit
This commit is contained in:
352
Source/Ember/Affine2D.cpp
Normal file
352
Source/Ember/Affine2D.cpp
Normal file
@ -0,0 +1,352 @@
|
||||
#include "EmberPch.h"
|
||||
#include "Affine2D.h"
|
||||
|
||||
namespace EmberNs
|
||||
{
|
||||
/// <summary>
|
||||
/// Default constructor which sets the matrix to the identity.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
Affine2D<T>::Affine2D()
|
||||
{
|
||||
MakeID();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which takes each column of the affine as a separate parameter.
|
||||
/// </summary>
|
||||
/// <param name="x">A and D</param>
|
||||
/// <param name="y">B and E</param>
|
||||
/// <param name="t">C and F</param>
|
||||
template <typename T>
|
||||
Affine2D<T>::Affine2D(v2T& x, v2T& y, v2T& t)
|
||||
{
|
||||
X(x);
|
||||
Y(y);
|
||||
O(t);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which takes all six of the affine values as parameters.
|
||||
/// </summary>
|
||||
/// <param name="xx">A</param>
|
||||
/// <param name="xy">D</param>
|
||||
/// <param name="yx">B</param>
|
||||
/// <param name="yy">E</param>
|
||||
/// <param name="tx">C</param>
|
||||
/// <param name="ty">F</param>
|
||||
template <typename T>
|
||||
Affine2D<T>::Affine2D(T xx, T xy, T yx, T yy, T tx, T ty)
|
||||
{
|
||||
A(xx);
|
||||
D(xy);
|
||||
B(yx);
|
||||
E(yy);
|
||||
C(tx);
|
||||
F(ty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which takes a 4x4 matrix and assigns the
|
||||
/// corresponding values in the 2x3 affine matrix.
|
||||
/// </summary>
|
||||
/// <param name="mat">The 4x4 affine matrix to read from</param>
|
||||
template <typename T>
|
||||
Affine2D<T>::Affine2D(m4T& mat)
|
||||
{
|
||||
A(mat[0][0]);
|
||||
B(mat[0][1]);
|
||||
C(mat[0][3]);
|
||||
D(mat[1][0]);
|
||||
E(mat[1][1]);
|
||||
F(mat[1][3]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make this affine transform the identity matrix.
|
||||
/// A and E = 1, all else 0.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
void Affine2D<T>::MakeID()
|
||||
{
|
||||
A(1);
|
||||
B(0);
|
||||
C(0);
|
||||
D(0);
|
||||
E(1);
|
||||
F(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine whether this affine transform is the identity matrix.
|
||||
/// </summary>
|
||||
/// <returns>True if A and E are equal to 1 and all others are 0, else false.</returns>
|
||||
template <typename T>
|
||||
bool Affine2D<T>::IsID() const
|
||||
{
|
||||
return (IsClose<T>(A(), 1)) &&
|
||||
(IsClose<T>(B(), 0)) &&
|
||||
(IsClose<T>(C(), 0)) &&
|
||||
(IsClose<T>(D(), 0)) &&
|
||||
(IsClose<T>(E(), 1)) &&
|
||||
(IsClose<T>(F(), 0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine whether this affine transform is all zeroes.
|
||||
/// </summary>
|
||||
/// <returns>True if all 6 elements equal zero, else false.</returns>
|
||||
template <typename T>
|
||||
bool Affine2D<T>::IsZero() const
|
||||
{
|
||||
return (IsClose<T>(A(), 0)) &&
|
||||
(IsClose<T>(B(), 0)) &&
|
||||
(IsClose<T>(C(), 0)) &&
|
||||
(IsClose<T>(D(), 0)) &&
|
||||
(IsClose<T>(E(), 0)) &&
|
||||
(IsClose<T>(F(), 0));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotate this affine transform around its origin by the specified angle in degrees.
|
||||
/// </summary>
|
||||
/// <param name="angle">The angle to rotate by</param>
|
||||
template <typename T>
|
||||
void Affine2D<T>::Rotate(T angle)
|
||||
{
|
||||
m4T origMat4 = ToMat4ColMajor(true);//Must center and use column major for glm to work.
|
||||
m4T newMat4 = glm::rotate(origMat4, angle * DEG_2_RAD_T, v3T(0, 0, 1));//Assuming only rotating around z.
|
||||
|
||||
A(newMat4[0][0]);//Use direct assignments instead of constructor to skip assigning C and F.
|
||||
B(newMat4[0][1]);
|
||||
D(newMat4[1][0]);
|
||||
E(newMat4[1][1]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move by v.
|
||||
/// </summary>
|
||||
/// <param name="v">The vec2 describing how far to move in the x and y directions</param>
|
||||
template <typename T>
|
||||
void Affine2D<T>::Translate(v2T& v)
|
||||
{
|
||||
O(O() + v);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotate and scale the X and Y components by a certain amount based on X.
|
||||
/// </summary>
|
||||
/// <param name="v">The vec2 describing how much to rotate and scale the X and Y components</param>
|
||||
template <typename T>
|
||||
void Affine2D<T>::RotateScaleXTo(v2T& v)
|
||||
{
|
||||
Affine2D<T> rs = CalcRotateScale(X(), v);
|
||||
|
||||
X(rs.TransformNormal(X()));
|
||||
Y(rs.TransformNormal(Y()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotate and scale the X and Y components by a certain amount based on Y.
|
||||
/// </summary>
|
||||
/// <param name="v">The vec2 describing how much to rotate and scale the X and Y components</param>
|
||||
template <typename T>
|
||||
void Affine2D<T>::RotateScaleYTo(v2T& v)
|
||||
{
|
||||
Affine2D<T> rs = CalcRotateScale(Y(), v);
|
||||
|
||||
X(rs.TransformNormal(X()));
|
||||
Y(rs.TransformNormal(Y()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the inverse of the 2x3 affine matrix.
|
||||
/// </summary>
|
||||
/// <returns>The inverse of this affine transform</returns>
|
||||
template <typename T>
|
||||
Affine2D<T> Affine2D<T>::Inverse() const
|
||||
{
|
||||
T det = A() * E() - D() * B();
|
||||
|
||||
return Affine2D<T>(E() / det, -D() / det,
|
||||
-B() / det, A() / det,
|
||||
(F() * B() - C() * E()) / det, (C() * D() - F() * A()) / det);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a vec2 gotten from transforming this affine transform
|
||||
/// by the vec2 passed in, but with a T component of 0, 0.
|
||||
/// </summary>
|
||||
/// <param name="v">The vec2 describing how much to transform by</param>
|
||||
/// <returns>The centered, transformed vec2</returns>
|
||||
template <typename T>
|
||||
typename v2T Affine2D<T>::TransformNormal(const v2T& v) const
|
||||
{
|
||||
return v2T(A() * v.x + B() * v.y, D() * v.x + E() * v.y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a vec2 gotten from transforming this affine transform
|
||||
/// by the vec2 passed in, and applying T translation.
|
||||
/// </summary>
|
||||
/// <param name="v">The vec2 describing how much to transform by</param>
|
||||
/// <returns>The translated, transformed vec2</returns>
|
||||
template <typename T>
|
||||
typename v2T Affine2D<T>::TransformVector(const v2T& v) const
|
||||
{
|
||||
return v2T(A() * v.x + B() * v.y + C(), D() * v.x + E() * v.y + F());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the X and Y components as a 2x2 matrix in column major order.
|
||||
/// </summary>
|
||||
/// <returns>The 2x2 matrix</returns>
|
||||
template <typename T>
|
||||
typename m2T Affine2D<T>::ToMat2ColMajor() const
|
||||
{
|
||||
return m2T(A(), B(),//Col0...
|
||||
D(), E());//1
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the X and Y components as a 2x2 matrix in row major order.
|
||||
/// </summary>
|
||||
/// <returns>The 2x2 matrix</returns>
|
||||
template <typename T>
|
||||
typename m2T Affine2D<T>::ToMat2RowMajor() const
|
||||
{
|
||||
return m2T(A(), D(),//Col0...
|
||||
B(), E());//1
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the 2x3 affine transform matrix as a 4x4 matrix in column major order.
|
||||
/// </summary>
|
||||
/// <param name="center">Whether to use T translation value or just 0 for center</param>
|
||||
/// <returns>The 4x4 matrix</returns>
|
||||
template <typename T>
|
||||
typename m4T Affine2D<T>::ToMat4ColMajor(bool center) const
|
||||
{
|
||||
m4T mat(A(), B(), 0, center ? 0 : C(),//Col0...
|
||||
D(), E(), 0, center ? 0 : F(),//1
|
||||
0, 0, 1, 0,//2
|
||||
0, 0, 0, 1);//3
|
||||
|
||||
return mat;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the 2x3 affine transform matrix as a 4x4 matrix in row major order.
|
||||
/// </summary>
|
||||
/// <param name="center">Whether to use T translation value or just 0 for center</param>
|
||||
/// <returns>The 4x4 matrix</returns>
|
||||
template <typename T>
|
||||
typename m4T Affine2D<T>::ToMat4RowMajor(bool center) const
|
||||
{
|
||||
m4T mat(A(), D(), 0, 0,
|
||||
B(), E(), 0, 0,
|
||||
0, 0, 1, 0,
|
||||
center ? 0 : C(), center ? 0 : F(), 0, 1);
|
||||
|
||||
return mat;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// == operator which tests if all fields are equal with another Affine2D.
|
||||
/// </summary>
|
||||
/// <param name="affine">The Affine2D to compare to</param>
|
||||
/// <returns>True if all fields are equal, else false</returns>
|
||||
template <typename T>
|
||||
bool Affine2D<T>::operator == (const Affine2D<T>& affine)
|
||||
{
|
||||
return IsClose(A(), affine.A()) &&
|
||||
IsClose(B(), affine.B()) &&
|
||||
IsClose(C(), affine.C()) &&
|
||||
IsClose(D(), affine.D()) &&
|
||||
IsClose(E(), affine.E()) &&
|
||||
IsClose(F(), affine.F());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// * operator to multiply this affine transform by another and return the result.
|
||||
/// </summary>
|
||||
/// <param name="affine">The Affine2D to multiply by</param>
|
||||
/// <returns>A new Affine2D which is the product of the multiplication</returns>
|
||||
template <typename T>
|
||||
Affine2D<T>& Affine2D<T>::operator * (const Affine2D<T>& affine)
|
||||
{
|
||||
X(TransformNormal(affine.X()));
|
||||
Y(TransformNormal(affine.Y()));
|
||||
O(TransformVector(affine.O()));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// * operator to multiply this affine transform by a vec2 and return the result as a vec2.
|
||||
/// </summary>
|
||||
/// <param name="v">The vec2 to multiply by</param>
|
||||
/// <returns>A new vec2 which is the product of the multiplication</returns>
|
||||
template <typename T>
|
||||
typename v2T Affine2D<T>::operator * (const v2T& v)
|
||||
{
|
||||
return TransformVector(v);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accessors.
|
||||
/// </summary>
|
||||
template <typename T> T Affine2D<T>::A() const { return m_Mat[0][0]; }//[0][0]//flam3
|
||||
template <typename T> T Affine2D<T>::B() const { return m_Mat[0][1]; }//[1][0]
|
||||
template <typename T> T Affine2D<T>::C() const { return m_Mat[0][2]; }//[2][0]
|
||||
template <typename T> T Affine2D<T>::D() const { return m_Mat[1][0]; }//[0][1]
|
||||
template <typename T> T Affine2D<T>::E() const { return m_Mat[1][1]; }//[1][1]
|
||||
template <typename T> T Affine2D<T>::F() const { return m_Mat[1][2]; }//[2][1]
|
||||
|
||||
template <typename T> void Affine2D<T>::A(T a) { m_Mat[0][0] = a; }
|
||||
template <typename T> void Affine2D<T>::B(T b) { m_Mat[0][1] = b; }
|
||||
template <typename T> void Affine2D<T>::C(T c) { m_Mat[0][2] = c; }
|
||||
template <typename T> void Affine2D<T>::D(T d) { m_Mat[1][0] = d; }
|
||||
template <typename T> void Affine2D<T>::E(T e) { m_Mat[1][1] = e; }
|
||||
template <typename T> void Affine2D<T>::F(T f) { m_Mat[1][2] = f; }
|
||||
|
||||
template <typename T> typename v2T Affine2D<T>::X() const { return v2T(A(), D()); }//X Axis.
|
||||
template <typename T> typename v2T Affine2D<T>::Y() const { return v2T(B(), E()); }//Y Axis.
|
||||
template <typename T> typename v2T Affine2D<T>::O() const { return v2T(C(), F()); }//Translation.
|
||||
|
||||
template <typename T> void Affine2D<T>::X(v2T& x) { A(x.x); D(x.y); }//X Axis.
|
||||
template <typename T> void Affine2D<T>::Y(v2T& y) { B(y.x); E(y.y); }//Y Axis.
|
||||
template <typename T> void Affine2D<T>::O(v2T& t) { C(t.x); F(t.y); }//Translation.
|
||||
|
||||
/// <summary>
|
||||
/// Rotate and scale this affine transform and return as a copy. Orginal is unchanged.
|
||||
/// </summary>
|
||||
/// <param name="from">The starting point to rotate and scale from</param>
|
||||
/// <param name="to">The ending point to rotate and scale to</param>
|
||||
/// <returns>The newly rotated and scalled Affine2D</returns>
|
||||
template <typename T>
|
||||
Affine2D<T> Affine2D<T>::CalcRotateScale(v2T& from, v2T& to)
|
||||
{
|
||||
T a, c;
|
||||
|
||||
CalcRSAC(from, to, a, c);
|
||||
return Affine2D<T>(a, c, -c, a, 0, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Never fully understood what this did or why it's named what it is.
|
||||
/// But it seems to handle some rotating and scaling.
|
||||
/// </summary>
|
||||
/// <param name="from">The starting point to rotate and scale from</param>
|
||||
/// <param name="to">The ending point to rotate and scale to</param>
|
||||
/// <param name="a">a</param>
|
||||
/// <param name="c">c</param>
|
||||
template <typename T>
|
||||
void Affine2D<T>::CalcRSAC(v2T& from, v2T& to, T& a, T& c)
|
||||
{
|
||||
T lsq = from.x * from.x + from.y * from.y;
|
||||
|
||||
a = (from.y * to.y + from.x * to.x) / lsq;
|
||||
c = (from.x * to.y - from.y * to.x) / lsq;
|
||||
}
|
||||
}
|
142
Source/Ember/Affine2D.h
Normal file
142
Source/Ember/Affine2D.h
Normal file
@ -0,0 +1,142 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utils.h"
|
||||
|
||||
/// <summary>
|
||||
/// Affine2D class.
|
||||
/// </summary>
|
||||
|
||||
namespace EmberNs
|
||||
{
|
||||
/// <summary>
|
||||
/// Uses matrix composition to handle the
|
||||
/// affine matrix. Taken almost entirely from
|
||||
/// Fractron, but using glm, and in C++.
|
||||
/// Note that the matrix layout differs from flam3 so it's best to use
|
||||
/// the A, B, C, D, E, F wrappers around the underlying matrix indices. But if the matrix must
|
||||
/// be accessed directly, the two are laid out as such:
|
||||
/// flam3: 3 columns of 2 rows each. Accessed col, row.
|
||||
/// [a(0,0)][b(1,0)][c(2,0)]
|
||||
/// [d(0,1)][e(1,1)][f(2,1)]
|
||||
/// Ember: 2 columns of 3 rows each. Accessed col, row.
|
||||
/// [a(0,0)][d(1,0)]
|
||||
/// [b(0,1)][e(1,1)]
|
||||
/// [c(0,2)][f(1,2)]
|
||||
/// Template argument expected to be float or double.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API Affine2D
|
||||
{
|
||||
public:
|
||||
Affine2D();
|
||||
|
||||
/// <summary>
|
||||
/// Default copy constructor.
|
||||
/// </summary>
|
||||
/// <param name="affine">The Affine2D object to copy</param>
|
||||
Affine2D(const Affine2D<T>& affine)
|
||||
{
|
||||
Affine2D<T>::operator=<T>(affine);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy constructor to copy an Affine2D object of type U.
|
||||
/// </summary>
|
||||
/// <param name="affine">The Affine2D object to copy</param>
|
||||
template <typename U>
|
||||
Affine2D(const Affine2D<U>& affine)
|
||||
{
|
||||
Affine2D<T>::operator=<U>(affine);
|
||||
}
|
||||
|
||||
Affine2D(v2T& x, v2T& y, v2T& t);
|
||||
Affine2D(T xx, T xy, T yx, T yy, T tx, T ty);
|
||||
Affine2D(m4T& mat);
|
||||
|
||||
/// <summary>
|
||||
/// Default assignment operator.
|
||||
/// </summary>
|
||||
/// <param name="affine">The Affine2D object to copy</param>
|
||||
Affine2D<T>& operator = (const Affine2D<T>& affine)
|
||||
{
|
||||
if (this != &affine)
|
||||
Affine2D<T>::operator=<T>(affine);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator to assign an Affine2D object of type U.
|
||||
/// </summary>
|
||||
/// <param name="affine">The Affine2D object to copy.</param>
|
||||
/// <returns>Reference to updated self</returns>
|
||||
template <typename U>
|
||||
Affine2D<T>& operator = (const Affine2D<U>& affine)
|
||||
{
|
||||
A(T(affine.A()));
|
||||
B(T(affine.B()));
|
||||
C(T(affine.C()));
|
||||
D(T(affine.D()));
|
||||
E(T(affine.E()));
|
||||
F(T(affine.F()));
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void MakeID();
|
||||
inline bool IsID() const;
|
||||
inline bool IsZero() const;
|
||||
inline void Rotate(T angle);
|
||||
inline void Translate(v2T& v);
|
||||
inline void RotateScaleXTo(v2T& v);
|
||||
inline void RotateScaleYTo(v2T& v);
|
||||
inline Affine2D<T> Inverse() const;
|
||||
inline v2T TransformNormal(const v2T& v) const;
|
||||
inline v2T TransformVector(const v2T& v) const;
|
||||
inline m2T ToMat2ColMajor() const;
|
||||
inline m2T ToMat2RowMajor() const;
|
||||
inline m4T ToMat4ColMajor(bool center = false) const;
|
||||
inline m4T ToMat4RowMajor(bool center = false) const;
|
||||
|
||||
bool operator == (const Affine2D<T>& affine);
|
||||
Affine2D<T>& operator * (const Affine2D<T>& affine);
|
||||
v2T operator * (const v2T& v);
|
||||
|
||||
inline T A() const;
|
||||
inline T B() const;
|
||||
inline T C() const;
|
||||
inline T D() const;
|
||||
inline T E() const;
|
||||
inline T F() const;
|
||||
|
||||
inline void A(T a);
|
||||
inline void B(T b);
|
||||
inline void C(T c);
|
||||
inline void D(T d);
|
||||
inline void E(T e);
|
||||
inline void F(T f);
|
||||
|
||||
inline v2T X() const;
|
||||
inline v2T Y() const;
|
||||
inline v2T O() const;
|
||||
|
||||
inline void X(v2T& x);
|
||||
inline void Y(v2T& y);
|
||||
inline void O(v2T& t);
|
||||
|
||||
//static Affine2D Identity();//Complains about inline.
|
||||
static inline Affine2D CalcRotateScale(v2T& from, v2T& to);
|
||||
static inline void CalcRSAC(v2T& from, v2T& to, T& a, T& c);
|
||||
|
||||
m23T m_Mat;
|
||||
};
|
||||
|
||||
//This class had to be implemented in a cpp file because the compiler was breaking.
|
||||
//So the explicit instantiation must be declared here rather than in Ember.cpp where
|
||||
//all of the other classes are done.
|
||||
template EMBER_API class Affine2D<float>;
|
||||
|
||||
#ifdef DO_DOUBLE
|
||||
template EMBER_API class Affine2D<double>;
|
||||
#endif
|
||||
}
|
252
Source/Ember/CarToRas.h
Normal file
252
Source/Ember/CarToRas.h
Normal file
@ -0,0 +1,252 @@
|
||||
#pragma once
|
||||
|
||||
#include "Point.h"
|
||||
|
||||
/// <summary>
|
||||
/// CarToRas class.
|
||||
/// </summary>
|
||||
|
||||
namespace EmberNs
|
||||
{
|
||||
/// <summary>
|
||||
/// When iterating, everything is positioned in terms of a carteseian plane with 0,0 in the center like so:
|
||||
/// [-1,1] [1,1]
|
||||
/// [-1,-1] [1,-1]
|
||||
/// However, when accumulating to the histogram, the data is stored in the traditional raster coordinate system
|
||||
/// of 0,0 at the top left and x,y at the bottom right. This class provides functionality to convert from one
|
||||
/// to the other and is used when accumulating a sub batch of iteration results to the histogram.
|
||||
/// Note the functions use reference arguments for the converted values because they are slightly faster than returning a value.
|
||||
/// Template argument expected to be float or double.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API CarToRas
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Empty constructor. This class should never be used unless it's been properly constructed with the constructor that takes arguments.
|
||||
/// </summary>
|
||||
CarToRas()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that takes arguments to set up the bounds and passes them to Init().
|
||||
/// </summary>
|
||||
/// <param name="carLlX">The lower left x of the cartesian plane</param>
|
||||
/// <param name="carLlY">The lower left y of the cartesian plane</param>
|
||||
/// <param name="carUrX">The upper right x of the cartesian plane</param>
|
||||
/// <param name="carUrY">The upper right y of the cartesian plane</param>
|
||||
/// <param name="rasW">The width in pixels of the raster image/histogram</param>
|
||||
/// <param name="rasH">The height in pixels of the raster image/histogram</param>
|
||||
/// <param name="aspectRatio">The aspect ratio, generally 1</param>
|
||||
CarToRas(T carLlX, T carLlY, T carUrX, T carUrY, unsigned int rasW, unsigned int rasH, T aspectRatio)
|
||||
{
|
||||
Init(carLlX, carLlY, carUrX, carUrY, rasW, rasH, aspectRatio);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default copy constructor.
|
||||
/// </summary>
|
||||
/// <param name="carToRas">The CarToRas object to copy</param>
|
||||
CarToRas(const CarToRas<T>& carToRas)
|
||||
{
|
||||
CarToRas<T>::operator=<T>(carToRas);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy constructor to copy a CarToRas object of type U.
|
||||
/// </summary>
|
||||
/// <param name="carToRas">The CarToRas object to copy</param>
|
||||
template <typename U>
|
||||
CarToRas(const CarToRas<U>& carToRas)
|
||||
{
|
||||
CarToRas<T>::operator=<U>(carToRas);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default assignment operator.
|
||||
/// </summary>
|
||||
/// <param name="carToRas">The CarToRas object to copy</param>
|
||||
CarToRas<T>& operator = (const CarToRas<T>& carToRas)
|
||||
{
|
||||
if (this != &carToRas)
|
||||
CarToRas<T>::operator=<T>(carToRas);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator to assign a CarToRas object of type U.
|
||||
/// </summary>
|
||||
/// <param name="carToRas">The CarToRas object to copy.</param>
|
||||
/// <returns>Reference to updated self</returns>
|
||||
template <typename U>
|
||||
CarToRas<T>& operator = (const CarToRas<U>& carToRas)
|
||||
{
|
||||
m_RasWidth = carToRas.RasWidth();
|
||||
m_RasHeight = carToRas.RasHeight();
|
||||
m_OneRow = T(carToRas.OneRow());
|
||||
m_OneCol = T(carToRas.OneCol());
|
||||
m_PixPerImageUnitW = T(carToRas.PixPerImageUnitW());
|
||||
m_RasLlX = T(carToRas.RasLlX());
|
||||
m_PixPerImageUnitH = T(carToRas.PixPerImageUnitH());
|
||||
m_RasLlY = T(carToRas.RasLlY());
|
||||
m_CarLlX = T(carToRas.CarLlX());
|
||||
m_CarLlY = T(carToRas.CarLlY());
|
||||
m_CarUrX = T(carToRas.CarUrX());
|
||||
m_CarUrY = T(carToRas.CarUrY());
|
||||
m_PadCarLlX = T(carToRas.PadCarLlX());
|
||||
m_PadCarLlY = T(carToRas.PadCarLlY());
|
||||
m_PadCarUrX = T(carToRas.PadCarUrX());
|
||||
m_PadCarUrY = T(carToRas.PadCarUrY());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the dimensions with the specified bounds.
|
||||
/// </summary>
|
||||
/// <param name="carLlX">The lower left x of the cartesian plane</param>
|
||||
/// <param name="carLlY">The lower left y of the cartesian plane</param>
|
||||
/// <param name="carUrX">The upper right x of the cartesian plane</param>
|
||||
/// <param name="carUrY">The upper right y of the cartesian plane</param>
|
||||
/// <param name="rasW">The width in pixels of the raster image/histogram</param>
|
||||
/// <param name="rasH">The height in pixels of the raster image/histogram</param>
|
||||
/// <param name="aspectRatio">The aspect ratio, generally 1</param>
|
||||
void Init(T carLlX, T carLlY, T carUrX, T carUrY, unsigned int rasW, unsigned int rasH, T aspectRatio)
|
||||
{
|
||||
m_RasWidth = rasW;
|
||||
m_RasHeight = rasH;
|
||||
|
||||
m_CarLlX = carLlX;
|
||||
m_CarLlY = carLlY;
|
||||
m_CarUrX = carUrX;
|
||||
m_CarUrY = carUrY;
|
||||
|
||||
T carW = m_CarUrX - m_CarLlX;//Right minus left.
|
||||
T carH = m_CarUrY - m_CarLlY;//Top minus bottom.
|
||||
T invSizeW = T(1.0) / carW;
|
||||
T invSizeH = T(1.0) / carH;
|
||||
|
||||
m_PixPerImageUnitW = (T)rasW * invSizeW;
|
||||
m_RasLlX = m_PixPerImageUnitW * carLlX;
|
||||
|
||||
m_PixPerImageUnitH = (T)rasH * invSizeH;
|
||||
m_RasLlY = m_PixPerImageUnitH * carLlY;
|
||||
|
||||
T m_OneRow = abs(m_CarUrY - m_CarLlY) / m_RasHeight;
|
||||
T m_OneCol = abs(m_CarUrX - m_CarLlX) / m_RasWidth;
|
||||
|
||||
m_PadCarLlX = m_CarLlX + m_OneCol;
|
||||
m_PadCarUrX = m_CarUrX - m_OneCol;
|
||||
|
||||
m_PadCarLlY = m_CarLlY + m_OneRow;
|
||||
m_PadCarUrY = m_CarUrY - m_OneRow;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a cartesian x, y coordinate to a raster x, y coordinate.
|
||||
/// This will flip the Y coordinate, so points that hit the bottom of the cartesian plane will
|
||||
/// be mapped to the top of the histogram and vice versa.
|
||||
/// There is a very slim chance that a point will be right on the border and will technically be in bounds, passing the InBounds() test,
|
||||
/// but ends up being mapped to a histogram bucket that is out of bounds due to roundoff error. Perform an additional check after this call to make sure the
|
||||
/// mapped point is in bounds.
|
||||
/// </summary>
|
||||
/// <param name="cartX">The cartesian x</param>
|
||||
/// <param name="cartY">The cartesian y</param>
|
||||
/// <param name="rasX">The converted raster x</param>
|
||||
/// <param name="rasY">The converted raster y</param>
|
||||
inline void Convert(T cartX, T cartY, unsigned int& rasX, unsigned int& rasY)
|
||||
{
|
||||
rasX = (unsigned int)(m_PixPerImageUnitW * cartX - m_RasLlX);
|
||||
rasY = (unsigned int)(m_RasLlY - (m_PixPerImageUnitH * cartY));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a cartesian x, y coordinate to a single raster buffer index.
|
||||
/// This will flip the Y coordinate, so points that hit the bottom of the cartesian plane will
|
||||
/// be mapped to the top of the histogram and vice versa.
|
||||
/// There is a very slim chance that a point will be right on the border and will technically be in bounds, passing the InBounds() test,
|
||||
/// but ends up being mapped to a histogram bucket that is out of bounds due to roundoff error. Perform an additional check after this call to make sure the
|
||||
/// mapped point is in bounds.
|
||||
/// </summary>
|
||||
/// <param name="cartX">The cartesian x</param>
|
||||
/// <param name="cartY">The cartesian y</param>
|
||||
/// <param name="singleBufferIndex">The converted single raster buffer index</param>
|
||||
inline void Convert(T cartX, T cartY, unsigned int& singleBufferIndex)
|
||||
{
|
||||
singleBufferIndex = (unsigned int)(m_PixPerImageUnitW * cartX - m_RasLlX) + (m_RasWidth * (unsigned int)(m_PixPerImageUnitH * cartY - m_RasLlY));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a cartesian x, y point to a single raster buffer index.
|
||||
/// This will flip the Y coordinate, so points that hit the bottom of the cartesian plane will
|
||||
/// be mapped to the top of the histogram and vice versa.
|
||||
/// This is the most efficient possible way of converting, consisting of only
|
||||
/// a multiply and subtract per coordinate element.
|
||||
/// There is a very slim chance that a point will be right on the border and will technically be in bounds, passing the InBounds() test,
|
||||
/// but ends up being mapped to a histogram bucket that is out of bounds due to roundoff error. Perform an additional check after this call to make sure the
|
||||
/// mapped point is in bounds.
|
||||
/// </summary>
|
||||
/// <param name="point">The cartesian y</param>
|
||||
/// <param name="singleBufferIndex">The converted single raster buffer index</param>
|
||||
inline void Convert(Point<T>& point, unsigned int& singleBufferIndex)
|
||||
{
|
||||
singleBufferIndex = (unsigned int)(m_PixPerImageUnitW * point.m_X - m_RasLlX) + (m_RasWidth * (unsigned int)(m_PixPerImageUnitH * point.m_Y - m_RasLlY));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if a point in the cartesian plane can be converted to a point within the raster plane.
|
||||
/// There is a very slim chance that a point will be right on the border and will technically be in bounds, passing the InBounds() test,
|
||||
/// but ends up being mapped to a histogram bucket that is out of bounds due to roundoff error. Perform an additional check after this call to make sure the
|
||||
/// mapped point is in bounds.
|
||||
/// </summary>
|
||||
/// <param name="point">The point to test</param>
|
||||
/// <returns>True if within bounds, else false</returns>
|
||||
inline bool InBounds(Point<T>& point)
|
||||
{
|
||||
//Debug check for hitting the very first pixel in the image.
|
||||
//if (point.m_Y > m_CarLlY && point.m_Y <= m_PadCarLlY && //Mapped to top row...
|
||||
// point.m_X > m_CarLlX && point.m_X <= m_PadCarLlX)//...first col.
|
||||
//{
|
||||
// cout << "First pixel hit." << endl;
|
||||
//}
|
||||
|
||||
return point.m_X >= m_CarLlX &&
|
||||
point.m_X < m_CarUrX &&
|
||||
point.m_Y < m_CarUrY &&
|
||||
point.m_Y >= m_CarLlY;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accessors.
|
||||
/// </summary>
|
||||
inline unsigned int RasWidth() const { return m_RasWidth; }
|
||||
inline unsigned int RasHeight() const { return m_RasHeight; }
|
||||
inline T OneRow() const { return m_OneRow; }
|
||||
inline T OneCol() const { return m_OneCol; }
|
||||
inline T PixPerImageUnitW() const { return m_PixPerImageUnitW; }
|
||||
inline T RasLlX() const { return m_RasLlX; }
|
||||
inline T PixPerImageUnitH() const { return m_PixPerImageUnitH; }
|
||||
inline T RasLlY() const { return m_RasLlY; }
|
||||
inline T CarLlX() const { return m_CarLlX; }
|
||||
inline T CarLlY() const { return m_CarLlY; }
|
||||
inline T CarUrX() const { return m_CarUrX; }
|
||||
inline T CarUrY() const { return m_CarUrY; }
|
||||
inline T PadCarLlX() const { return m_PadCarLlX; }
|
||||
inline T PadCarLlY() const { return m_PadCarLlY; }
|
||||
inline T PadCarUrX() const { return m_PadCarUrX; }
|
||||
inline T PadCarUrY() const { return m_PadCarUrY; }
|
||||
|
||||
private:
|
||||
unsigned int m_RasWidth, m_RasHeight;//The width and height of the raster image.
|
||||
T m_OneRow;//The distance that one raster row represents in the cartesian plane.
|
||||
T m_OneCol;//The distance that one raster column represents in the cartesian plane.
|
||||
T m_PixPerImageUnitW;//The number of columns in the raster plane that a horizontal distance of 1 in the cartesian plane represents. The higher the number, the more zoomed in.
|
||||
T m_RasLlX;//The lower left x of the raster image plane.
|
||||
T m_PixPerImageUnitH;//The number of rows in the raster plane that a vertical distance of 1 in the cartesian plane represents. The higher the number, the more zoomed in.
|
||||
T m_RasLlY;//The lower left y of the raster image plane.
|
||||
T m_CarLlX, m_CarLlY, m_CarUrX, m_CarUrY;//The bounds of the cartesian plane.
|
||||
T m_PadCarLlX, m_PadCarLlY, m_PadCarUrX, m_PadCarUrY;//The bounds of the cartesian plane padded by one raster row and column on each side.
|
||||
};
|
||||
}
|
340
Source/Ember/DensityFilter.h
Normal file
340
Source/Ember/DensityFilter.h
Normal file
@ -0,0 +1,340 @@
|
||||
#pragma once
|
||||
|
||||
#include "SpatialFilter.h"
|
||||
|
||||
/// <summary>
|
||||
/// DensityFilter class.
|
||||
/// </summary>
|
||||
|
||||
namespace EmberNs
|
||||
{
|
||||
/// <summary>
|
||||
/// A base class with virtual functions to allow both templating and polymorphism to work together.
|
||||
/// Derived classes will implement all of these functions.
|
||||
/// </summary>
|
||||
class EMBER_API DensityFilterBase
|
||||
{
|
||||
public:
|
||||
DensityFilterBase() { }
|
||||
virtual ~DensityFilterBase() { }
|
||||
|
||||
virtual int FilterWidth() { return 0; }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The density estimation filter is used after iterating, but before final accumulation.
|
||||
/// It's a variable width Gaussian filter, whose width is inversely proportional
|
||||
/// to the number of hits a given histogram cell has received.
|
||||
/// That means the fewer hits in a cell, the more blur is applied. The greater the hits,
|
||||
/// the less blur.
|
||||
/// Template argument expected to be float or double.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API DensityFilter : public DensityFilterBase
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor that assigns various fields but does not create the actual filter vector.
|
||||
/// This is done because filter creation could fail, so the user must manually call it
|
||||
/// after construction.
|
||||
/// </summary>
|
||||
/// <param name="minRad">The minimum filter radius</param>
|
||||
/// <param name="maxRad">The maximum filter radius</param>
|
||||
/// <param name="curve">The curve of the filter</param>
|
||||
/// <param name="supersample">The supersample of the ember this filter will be used with</param>
|
||||
DensityFilter(T minRad, T maxRad, T curve, unsigned int supersample)
|
||||
{
|
||||
m_MinRad = minRad;
|
||||
m_MaxRad = maxRad;
|
||||
m_Curve = curve;
|
||||
m_Supersample = supersample;
|
||||
m_MaxFilterIndex = 0;
|
||||
|
||||
//Make sure the values make sense.
|
||||
if (m_Curve <= 0.0)
|
||||
m_Curve = T(0.5);
|
||||
|
||||
if (m_MaxRad < m_MinRad)
|
||||
m_MaxRad = m_MinRad + 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy constructor.
|
||||
/// </summary>
|
||||
/// <param name="filter">The DensityFilter object to copy</param>
|
||||
DensityFilter(const DensityFilter<T>& filter)
|
||||
{
|
||||
*this = filter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator.
|
||||
/// </summary>
|
||||
/// <param name="filter">The DensityFilter object to copy.</param>
|
||||
/// <returns>Reference to updated self</returns>
|
||||
DensityFilter<T>& operator = (const DensityFilter<T>& filter)
|
||||
{
|
||||
if (this != &filter)
|
||||
{
|
||||
m_MinRad = filter.m_MinRad;
|
||||
m_MaxRad = filter.m_MaxRad;
|
||||
m_Curve = filter.m_Curve;
|
||||
m_Supersample = filter.m_Supersample;
|
||||
m_KernelSize = filter.m_KernelSize;
|
||||
m_MaxFilterIndex = filter.m_MaxFilterIndex;
|
||||
m_MaxFilteredCounts = filter.m_MaxFilteredCounts;
|
||||
m_FilterWidth = filter.m_FilterWidth;
|
||||
m_Coefs = filter.m_Coefs;
|
||||
m_Widths = filter.m_Widths;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create the filter vector of up to 10M entries.
|
||||
/// If more than that are requested, it isn't created and
|
||||
/// false is returned.
|
||||
/// </summary>
|
||||
/// <returns>True if success, else false.</returns>
|
||||
bool Create()
|
||||
{
|
||||
int i, j, w;
|
||||
int intFilterCount, maxIndex;
|
||||
int rowSize;
|
||||
int filterLoop;
|
||||
int keepThresh = 100;
|
||||
unsigned int filterCoefIndex = 0;
|
||||
T decFilterCount;
|
||||
T finalMinRad = m_MinRad * m_Supersample + 1;//Should scale the filter width by the oversample.
|
||||
T finalMaxRad = m_MaxRad * m_Supersample + 1;//The '+1' comes from the assumed distance to the first pixel.
|
||||
GaussianFilter<T> gaussianFilter(m_MaxRad, m_Supersample);
|
||||
|
||||
m_KernelSize = 0;
|
||||
m_MaxFilterIndex = 0;
|
||||
|
||||
//Calculate how many filter kernels are needed based on the decay function
|
||||
//
|
||||
// num filters = (de_max_width / de_min_width)^(1 / estimator_curve)
|
||||
//
|
||||
decFilterCount = pow(finalMaxRad / finalMinRad, T(1.0) / m_Curve);
|
||||
|
||||
if (decFilterCount > 1e7)//Too many filters.
|
||||
return false;
|
||||
|
||||
intFilterCount = (int)ceil(decFilterCount);
|
||||
|
||||
//Condense the smaller kernels to save space.
|
||||
if (intFilterCount > keepThresh)
|
||||
{
|
||||
maxIndex = (int)ceil(DE_THRESH + pow(T(intFilterCount - DE_THRESH), m_Curve)) + 1;
|
||||
m_MaxFilteredCounts = (int)pow(T(maxIndex - DE_THRESH), T(1.0) / m_Curve) + DE_THRESH;
|
||||
}
|
||||
else
|
||||
{
|
||||
maxIndex = intFilterCount;
|
||||
m_MaxFilteredCounts = maxIndex;
|
||||
}
|
||||
|
||||
//Allocate the memory for these filters and the hit/width lookup array.
|
||||
rowSize = (int)(2 * ceil(finalMaxRad) - 1);
|
||||
m_FilterWidth = (rowSize - 1) / 2;
|
||||
m_KernelSize = (m_FilterWidth + 1) * (2 + m_FilterWidth) / 2;
|
||||
|
||||
m_Coefs.resize(maxIndex * m_KernelSize);
|
||||
m_Widths.resize(maxIndex);
|
||||
|
||||
//Generate the filter coefficients.
|
||||
for (filterLoop = 0; filterLoop < maxIndex; filterLoop++)
|
||||
{
|
||||
int dej, dek;
|
||||
int coefIndex;
|
||||
T filterSum = 0.0;
|
||||
T filterVal;
|
||||
T filterHeight;
|
||||
T loopAdjust;
|
||||
|
||||
//Calculate the filter width for this number of hits in a bin.
|
||||
if (filterLoop < keepThresh)
|
||||
{
|
||||
filterHeight = (finalMaxRad / pow(T(filterLoop + 1), m_Curve));
|
||||
}
|
||||
else
|
||||
{
|
||||
loopAdjust = pow(T(filterLoop - keepThresh), (T(1.0) / m_Curve)) + keepThresh;
|
||||
filterHeight = (finalMaxRad / pow(loopAdjust + 1, m_Curve));
|
||||
}
|
||||
|
||||
//Once we've reached the min radius, don't populate any more.
|
||||
if (filterHeight <= finalMinRad)
|
||||
{
|
||||
filterHeight = finalMinRad;
|
||||
m_MaxFilterIndex = filterLoop;
|
||||
}
|
||||
|
||||
m_Widths[filterLoop] = filterHeight;
|
||||
|
||||
//Calculate norm of kernel separately (easier).
|
||||
for (dej = -m_FilterWidth; dej <= m_FilterWidth; dej++)
|
||||
{
|
||||
for (dek = -m_FilterWidth; dek <= m_FilterWidth; dek++)
|
||||
{
|
||||
filterVal = sqrt((T)(dej * dej + dek * dek)) / filterHeight;
|
||||
|
||||
//Only populate the coefs within this radius.
|
||||
if (filterVal <= 1.0)
|
||||
filterSum += gaussianFilter.Filter(gaussianFilter.Support() * filterVal);
|
||||
}
|
||||
}
|
||||
|
||||
coefIndex = filterLoop * m_KernelSize;
|
||||
|
||||
//Calculate the unique entries of the kernel.
|
||||
for (dej = 0; dej <= m_FilterWidth; dej++)
|
||||
{
|
||||
for (dek = 0; dek <= dej; dek++)
|
||||
{
|
||||
filterVal = sqrt(T(dej * dej + dek * dek)) / filterHeight;
|
||||
|
||||
//Only populate the coefs within this radius.
|
||||
if (filterVal > 1.0)
|
||||
m_Coefs[coefIndex] = 0.0;
|
||||
else
|
||||
m_Coefs[coefIndex] = gaussianFilter.Filter(gaussianFilter.Support() * filterVal) / filterSum;
|
||||
|
||||
coefIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_MaxFilterIndex > 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_MaxFilterIndex == 0)
|
||||
m_MaxFilterIndex = maxIndex - 1;
|
||||
|
||||
w = m_FilterWidth + 1;
|
||||
m_CoefIndices.resize(w * w);
|
||||
|
||||
//This will populate one quadrant of filter indices.
|
||||
//Really only need 1/8th, but that would require a sparse matrix.
|
||||
for (j = 0; j <= m_FilterWidth; j++)
|
||||
{
|
||||
for (i = 0; i <= j; i++, filterCoefIndex++)
|
||||
{
|
||||
if (j == 0 && i == 0)
|
||||
{
|
||||
m_CoefIndices[(j * w) + i] = filterCoefIndex;
|
||||
}
|
||||
else if (i == 0)
|
||||
{
|
||||
m_CoefIndices[(0 * w) + j] = filterCoefIndex;
|
||||
m_CoefIndices[(j * w) + 0] = filterCoefIndex;
|
||||
}
|
||||
else if (j == i)
|
||||
{
|
||||
m_CoefIndices[(j * w) + i] = filterCoefIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_CoefIndices[(i * w) + j] = filterCoefIndex;
|
||||
m_CoefIndices[(j * w) + i] = filterCoefIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return whether the requested dimensions are valid.
|
||||
/// Meaning, is the requested filter size less than or equal to 10M?
|
||||
/// </summary>
|
||||
/// <returns>True if requested filter size is less than or equal to 10M, else false.</returns>
|
||||
inline bool Valid() const
|
||||
{
|
||||
T finalMaxRad = m_MaxRad * m_Supersample + 1;
|
||||
T finalMinRad = m_MinRad * m_Supersample + 1;
|
||||
|
||||
return pow(finalMaxRad / finalMinRad, T(1.0) / m_Curve) <= 1e7;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a string representation of this density estimation filter.
|
||||
/// </summary>
|
||||
/// <returns>The string representation of this density estimation filter</returns>
|
||||
string ToString() const
|
||||
{
|
||||
unsigned int i, j, coefIndex = 0, w = m_FilterWidth + 1;
|
||||
stringstream ss;
|
||||
|
||||
ss
|
||||
<< "Density Filter:" << endl
|
||||
<< " Min radius: " << MinRad() << endl
|
||||
<< " Max radius: " << MaxRad() << endl
|
||||
<< " Curve: " << Curve() << endl
|
||||
<< " Kernel size: " << KernelSize() << endl
|
||||
<< " Max filter index: " << MaxFilterIndex() << endl
|
||||
<< "Max Filtered counts: " << MaxFilteredCounts() << endl
|
||||
<< " Filter width: " << FilterWidth() << endl;
|
||||
|
||||
ss << "Coefficients: " << endl;
|
||||
|
||||
for (i = 0; i < m_Widths.size(); i++)
|
||||
{
|
||||
for (coefIndex = 0; coefIndex < m_KernelSize; coefIndex++)
|
||||
ss << "Kernel[" << i << "].Coefs[" << coefIndex << "]: " << m_Coefs[(i * m_KernelSize) + coefIndex] << endl;
|
||||
}
|
||||
|
||||
ss << endl << "Widths: " << endl;
|
||||
|
||||
for (i = 0; i < m_Widths.size(); i++)
|
||||
{
|
||||
ss << "Widths[" << i << "]: " << m_Widths[i] << endl;
|
||||
}
|
||||
|
||||
for (i = 0; i < w; i++)
|
||||
{
|
||||
for (j = 0; j < w; j++)
|
||||
{
|
||||
cout << std::setw(2) << std::setfill('0') << m_CoefIndices[i * w + j] << "\t";
|
||||
}
|
||||
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accessors.
|
||||
/// </summary>
|
||||
inline T MinRad() const { return m_MinRad; }
|
||||
inline T MaxRad() const { return m_MaxRad; }
|
||||
inline T Curve() const { return m_Curve; }
|
||||
inline unsigned int KernelSize() const { return m_KernelSize; }
|
||||
inline unsigned int MaxFilterIndex() const { return m_MaxFilterIndex; }
|
||||
inline unsigned int MaxFilteredCounts() const { return m_MaxFilteredCounts; }
|
||||
virtual int FilterWidth() const { return m_FilterWidth; }
|
||||
inline unsigned int BufferSize() const { return (unsigned int)m_Widths.size(); }
|
||||
inline unsigned int CoefsSizeBytes() const { return BufferSize() * m_KernelSize * sizeof(T); }
|
||||
inline unsigned int WidthsSizeBytes() const { return BufferSize() * sizeof(T); }
|
||||
inline unsigned int CoefsIndicesSizeBytes() const { return unsigned int(m_CoefIndices.size() * sizeof(unsigned int)); }
|
||||
inline const T* Coefs() const { return m_Coefs.data(); }
|
||||
inline const T* Widths() const { return m_Widths.data(); }
|
||||
inline const unsigned int* CoefIndices() const { return m_CoefIndices.data(); }
|
||||
|
||||
private:
|
||||
T m_MinRad;
|
||||
T m_MaxRad;//The original specified filter radius.
|
||||
T m_Curve;
|
||||
unsigned int m_Supersample;
|
||||
unsigned int m_KernelSize;
|
||||
unsigned int m_MaxFilterIndex;
|
||||
unsigned int m_MaxFilteredCounts;
|
||||
int m_FilterWidth;//The new radius after scaling for super sample and rounding. This is what's actually used.
|
||||
vector<T> m_Coefs;
|
||||
vector<T> m_Widths;
|
||||
vector<unsigned int> m_CoefIndices;
|
||||
};
|
||||
}
|
20
Source/Ember/DllMain.cpp
Normal file
20
Source/Ember/DllMain.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
#include "EmberPch.h"
|
||||
|
||||
/// <summary>
|
||||
/// Generated by Visual Studio to make the DLL run properly.
|
||||
/// </summary>
|
||||
BOOL APIENTRY DllMain( HMODULE hModule,
|
||||
DWORD ul_reason_for_call,
|
||||
LPVOID lpReserved
|
||||
)
|
||||
{
|
||||
switch (ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
case DLL_PROCESS_DETACH:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
410
Source/Ember/Ember.cpp
Normal file
410
Source/Ember/Ember.cpp
Normal file
@ -0,0 +1,410 @@
|
||||
#include "EmberPch.h"
|
||||
#include "EmberDefines.h"
|
||||
#include "Isaac.h"
|
||||
#include "Ember.h"
|
||||
#include "Utils.h"
|
||||
#include "Iterator.h"
|
||||
#include "Palette.h"
|
||||
#include "PaletteList.h"
|
||||
#include "Point.h"
|
||||
#include "Variation.h"
|
||||
#include "Variations01.h"
|
||||
#include "Variations02.h"
|
||||
#include "Variations03.h"
|
||||
#include "Variations04.h"
|
||||
#include "Variations05.h"
|
||||
#include "VariationsDC.h"
|
||||
#include "VariationList.h"
|
||||
#include "Affine2D.h"
|
||||
#include "Xform.h"
|
||||
#include "EmberToXml.h"
|
||||
#include "XmlToEmber.h"
|
||||
#include "SpatialFilter.h"
|
||||
#include "DensityFilter.h"
|
||||
#include "TemporalFilter.h"
|
||||
#include "Interpolate.h"
|
||||
#include "Renderer.h"
|
||||
#include "Timing.h"
|
||||
#include "SheepTools.h"
|
||||
|
||||
/// <summary>
|
||||
/// Explicit instantiation of all templated classes which aren't implemented in cpp files.
|
||||
/// All new templated classes, such as new variations, must be added here.
|
||||
/// Additional instances of static class member variables.
|
||||
/// </summary>
|
||||
|
||||
namespace EmberNs
|
||||
{
|
||||
bool Timing::m_TimingInit = false;
|
||||
int Timing::m_ProcessorCount;
|
||||
LARGE_INTEGER Timing::m_Freq;
|
||||
auto_ptr<QTIsaac<ISAAC_SIZE, ISAAC_INT>> QTIsaac<ISAAC_SIZE, ISAAC_INT>::GlobalRand = auto_ptr<QTIsaac<ISAAC_SIZE, ISAAC_INT>>(new QTIsaac<ISAAC_SIZE, ISAAC_INT>());
|
||||
|
||||
#define EXPORTPREPOSTREGVAR(varName, T) \
|
||||
template EMBER_API class varName##Variation<T>; \
|
||||
template EMBER_API class Pre##varName##Variation<T>; \
|
||||
template EMBER_API class Post##varName##Variation<T>;
|
||||
|
||||
#define EXPORT_SINGLE_TYPE_EMBER(T) \
|
||||
template EMBER_API class Point<T>; \
|
||||
template EMBER_API class Color<T>; \
|
||||
template EMBER_API class Affine2D<T>; \
|
||||
template EMBER_API class Palette<T>; \
|
||||
template EMBER_API class PaletteList<T>; \
|
||||
template EMBER_API class Iterator<T>; \
|
||||
template EMBER_API class StandardIterator<T>; \
|
||||
template EMBER_API class XaosIterator<T>; \
|
||||
template EMBER_API class Xform<T>; \
|
||||
template EMBER_API class IteratorHelper<T>; \
|
||||
template EMBER_API class Variation<T>; \
|
||||
template EMBER_API class ParamWithName<T>; \
|
||||
template EMBER_API class ParametricVariation<T>; \
|
||||
EXPORTPREPOSTREGVAR(Linear, T) \
|
||||
EXPORTPREPOSTREGVAR(Sinusoidal, T) \
|
||||
EXPORTPREPOSTREGVAR(Spherical, T) \
|
||||
EXPORTPREPOSTREGVAR(Swirl, T) \
|
||||
EXPORTPREPOSTREGVAR(Horseshoe, T) \
|
||||
EXPORTPREPOSTREGVAR(Polar, T) \
|
||||
EXPORTPREPOSTREGVAR(Handkerchief, T) \
|
||||
EXPORTPREPOSTREGVAR(Heart, T) \
|
||||
EXPORTPREPOSTREGVAR(Disc, T) \
|
||||
EXPORTPREPOSTREGVAR(Spiral, T) \
|
||||
EXPORTPREPOSTREGVAR(Hyperbolic, T) \
|
||||
EXPORTPREPOSTREGVAR(Diamond, T) \
|
||||
EXPORTPREPOSTREGVAR(Ex, T) \
|
||||
EXPORTPREPOSTREGVAR(Julia, T) \
|
||||
EXPORTPREPOSTREGVAR(Bent, T) \
|
||||
EXPORTPREPOSTREGVAR(Waves, T) \
|
||||
EXPORTPREPOSTREGVAR(Fisheye, T) \
|
||||
EXPORTPREPOSTREGVAR(Popcorn, T) \
|
||||
EXPORTPREPOSTREGVAR(Exponential, T) \
|
||||
EXPORTPREPOSTREGVAR(Power, T) \
|
||||
EXPORTPREPOSTREGVAR(Cosine, T) \
|
||||
EXPORTPREPOSTREGVAR(Rings, T) \
|
||||
EXPORTPREPOSTREGVAR(Fan, T) \
|
||||
EXPORTPREPOSTREGVAR(Blob, T) \
|
||||
EXPORTPREPOSTREGVAR(Pdj, T) \
|
||||
EXPORTPREPOSTREGVAR(Fan2, T) \
|
||||
EXPORTPREPOSTREGVAR(Rings2, T) \
|
||||
EXPORTPREPOSTREGVAR(Eyefish, T) \
|
||||
EXPORTPREPOSTREGVAR(Bubble, T) \
|
||||
EXPORTPREPOSTREGVAR(Cylinder, T) \
|
||||
EXPORTPREPOSTREGVAR(Perspective, T) \
|
||||
EXPORTPREPOSTREGVAR(Noise, T) \
|
||||
EXPORTPREPOSTREGVAR(JuliaNGeneric, T) \
|
||||
EXPORTPREPOSTREGVAR(JuliaScope, T) \
|
||||
EXPORTPREPOSTREGVAR(Blur, T) \
|
||||
EXPORTPREPOSTREGVAR(GaussianBlur, T) \
|
||||
EXPORTPREPOSTREGVAR(RadialBlur, T) \
|
||||
EXPORTPREPOSTREGVAR(Pie, T) \
|
||||
EXPORTPREPOSTREGVAR(Ngon, T) \
|
||||
EXPORTPREPOSTREGVAR(Curl, T) \
|
||||
EXPORTPREPOSTREGVAR(Rectangles, T) \
|
||||
EXPORTPREPOSTREGVAR(Arch, T) \
|
||||
EXPORTPREPOSTREGVAR(Tangent, T) \
|
||||
EXPORTPREPOSTREGVAR(Square, T) \
|
||||
EXPORTPREPOSTREGVAR(Rays, T) \
|
||||
EXPORTPREPOSTREGVAR(Blade, T) \
|
||||
EXPORTPREPOSTREGVAR(Secant2, T) \
|
||||
EXPORTPREPOSTREGVAR(TwinTrian, T) \
|
||||
EXPORTPREPOSTREGVAR(Cross, T) \
|
||||
EXPORTPREPOSTREGVAR(Disc2, T) \
|
||||
EXPORTPREPOSTREGVAR(SuperShape, T) \
|
||||
EXPORTPREPOSTREGVAR(Flower, T) \
|
||||
EXPORTPREPOSTREGVAR(Conic, T) \
|
||||
EXPORTPREPOSTREGVAR(Parabola, T) \
|
||||
EXPORTPREPOSTREGVAR(Bent2, T) \
|
||||
EXPORTPREPOSTREGVAR(Bipolar, T) \
|
||||
EXPORTPREPOSTREGVAR(Boarders, T) \
|
||||
EXPORTPREPOSTREGVAR(Butterfly, T) \
|
||||
EXPORTPREPOSTREGVAR(Cell, T) \
|
||||
EXPORTPREPOSTREGVAR(Cpow, T) \
|
||||
EXPORTPREPOSTREGVAR(Curve, T) \
|
||||
EXPORTPREPOSTREGVAR(Edisc, T) \
|
||||
EXPORTPREPOSTREGVAR(Elliptic, T) \
|
||||
EXPORTPREPOSTREGVAR(Escher, T) \
|
||||
EXPORTPREPOSTREGVAR(Foci, T) \
|
||||
EXPORTPREPOSTREGVAR(LazySusan, T) \
|
||||
EXPORTPREPOSTREGVAR(Loonie, T) \
|
||||
EXPORTPREPOSTREGVAR(Modulus, T) \
|
||||
EXPORTPREPOSTREGVAR(Oscilloscope, T) \
|
||||
EXPORTPREPOSTREGVAR(Polar2, T) \
|
||||
EXPORTPREPOSTREGVAR(Popcorn2, T) \
|
||||
EXPORTPREPOSTREGVAR(Scry, T) \
|
||||
EXPORTPREPOSTREGVAR(Separation, T) \
|
||||
EXPORTPREPOSTREGVAR(Split, T) \
|
||||
EXPORTPREPOSTREGVAR(Splits, T) \
|
||||
EXPORTPREPOSTREGVAR(Stripes, T) \
|
||||
EXPORTPREPOSTREGVAR(Wedge, T) \
|
||||
EXPORTPREPOSTREGVAR(WedgeJulia, T) \
|
||||
EXPORTPREPOSTREGVAR(WedgeSph, T) \
|
||||
EXPORTPREPOSTREGVAR(Whorl, T) \
|
||||
EXPORTPREPOSTREGVAR(Waves2, T) \
|
||||
EXPORTPREPOSTREGVAR(Exp, T) \
|
||||
EXPORTPREPOSTREGVAR(Log, T) \
|
||||
EXPORTPREPOSTREGVAR(Sin, T) \
|
||||
EXPORTPREPOSTREGVAR(Cos, T) \
|
||||
EXPORTPREPOSTREGVAR(Tan, T) \
|
||||
EXPORTPREPOSTREGVAR(Sec, T) \
|
||||
EXPORTPREPOSTREGVAR(Csc, T) \
|
||||
EXPORTPREPOSTREGVAR(Cot, T) \
|
||||
EXPORTPREPOSTREGVAR(Sinh, T) \
|
||||
EXPORTPREPOSTREGVAR(Cosh, T) \
|
||||
EXPORTPREPOSTREGVAR(Tanh, T) \
|
||||
EXPORTPREPOSTREGVAR(Sech, T) \
|
||||
EXPORTPREPOSTREGVAR(Csch, T) \
|
||||
EXPORTPREPOSTREGVAR(Coth, T) \
|
||||
EXPORTPREPOSTREGVAR(Auger, T) \
|
||||
EXPORTPREPOSTREGVAR(Flux, T) \
|
||||
EXPORTPREPOSTREGVAR(Hemisphere, T) \
|
||||
EXPORTPREPOSTREGVAR(Epispiral, T) \
|
||||
EXPORTPREPOSTREGVAR(Bwraps, T) \
|
||||
EXPORTPREPOSTREGVAR(Extrude, T) \
|
||||
EXPORTPREPOSTREGVAR(BlurCircle, T) \
|
||||
EXPORTPREPOSTREGVAR(BlurZoom, T) \
|
||||
EXPORTPREPOSTREGVAR(BlurPixelize, T) \
|
||||
EXPORTPREPOSTREGVAR(Crop, T) \
|
||||
EXPORTPREPOSTREGVAR(BCircle, T) \
|
||||
EXPORTPREPOSTREGVAR(BlurLinear, T) \
|
||||
EXPORTPREPOSTREGVAR(BlurSquare, T) \
|
||||
EXPORTPREPOSTREGVAR(Boarders2, T) \
|
||||
EXPORTPREPOSTREGVAR(Cardioid, T) \
|
||||
EXPORTPREPOSTREGVAR(Checks, T) \
|
||||
EXPORTPREPOSTREGVAR(Circlize, T) \
|
||||
EXPORTPREPOSTREGVAR(Circlize2, T) \
|
||||
EXPORTPREPOSTREGVAR(CosWrap, T) \
|
||||
EXPORTPREPOSTREGVAR(DeltaA, T) \
|
||||
EXPORTPREPOSTREGVAR(Expo, T) \
|
||||
EXPORTPREPOSTREGVAR(FDisc, T) \
|
||||
EXPORTPREPOSTREGVAR(Fibonacci, T) \
|
||||
EXPORTPREPOSTREGVAR(Fibonacci2, T) \
|
||||
EXPORTPREPOSTREGVAR(Glynnia, T) \
|
||||
EXPORTPREPOSTREGVAR(GridOut, T) \
|
||||
EXPORTPREPOSTREGVAR(Hole, T) \
|
||||
EXPORTPREPOSTREGVAR(Hypertile, T) \
|
||||
EXPORTPREPOSTREGVAR(Hypertile1, T) \
|
||||
EXPORTPREPOSTREGVAR(Hypertile2, T) \
|
||||
EXPORTPREPOSTREGVAR(Hypertile3D, T) \
|
||||
EXPORTPREPOSTREGVAR(Hypertile3D1, T) \
|
||||
EXPORTPREPOSTREGVAR(Hypertile3D2, T) \
|
||||
EXPORTPREPOSTREGVAR(IDisc, T) \
|
||||
EXPORTPREPOSTREGVAR(Julian2, T) \
|
||||
EXPORTPREPOSTREGVAR(JuliaQ, T) \
|
||||
EXPORTPREPOSTREGVAR(Murl, T) \
|
||||
EXPORTPREPOSTREGVAR(Murl2, T) \
|
||||
EXPORTPREPOSTREGVAR(NPolar, T) \
|
||||
EXPORTPREPOSTREGVAR(Ortho, T) \
|
||||
EXPORTPREPOSTREGVAR(Poincare, T) \
|
||||
EXPORTPREPOSTREGVAR(Poincare3D, T) \
|
||||
EXPORTPREPOSTREGVAR(Polynomial, T) \
|
||||
EXPORTPREPOSTREGVAR(PSphere, T) \
|
||||
EXPORTPREPOSTREGVAR(Rational3, T) \
|
||||
EXPORTPREPOSTREGVAR(Ripple, T) \
|
||||
EXPORTPREPOSTREGVAR(Sigmoid, T) \
|
||||
EXPORTPREPOSTREGVAR(SinusGrid, T) \
|
||||
EXPORTPREPOSTREGVAR(Stwin, T) \
|
||||
EXPORTPREPOSTREGVAR(TwoFace, T) \
|
||||
EXPORTPREPOSTREGVAR(Unpolar, T) \
|
||||
EXPORTPREPOSTREGVAR(WavesN, T) \
|
||||
EXPORTPREPOSTREGVAR(XHeart, T) \
|
||||
EXPORTPREPOSTREGVAR(Barycentroid, T) \
|
||||
EXPORTPREPOSTREGVAR(BiSplit, T) \
|
||||
EXPORTPREPOSTREGVAR(Crescents, T) \
|
||||
EXPORTPREPOSTREGVAR(Mask, T) \
|
||||
EXPORTPREPOSTREGVAR(Cpow2, T) \
|
||||
EXPORTPREPOSTREGVAR(Curl3D, T) \
|
||||
EXPORTPREPOSTREGVAR(Disc3D, T) \
|
||||
EXPORTPREPOSTREGVAR(Funnel, T) \
|
||||
EXPORTPREPOSTREGVAR(Linear3D, T) \
|
||||
EXPORTPREPOSTREGVAR(PowBlock, T) \
|
||||
EXPORTPREPOSTREGVAR(Squirrel, T) \
|
||||
EXPORTPREPOSTREGVAR(Ennepers, T) \
|
||||
EXPORTPREPOSTREGVAR(SphericalN, T) \
|
||||
EXPORTPREPOSTREGVAR(Kaleidoscope, T) \
|
||||
EXPORTPREPOSTREGVAR(GlynnSim1, T) \
|
||||
EXPORTPREPOSTREGVAR(GlynnSim2, T) \
|
||||
EXPORTPREPOSTREGVAR(GlynnSim3, T) \
|
||||
EXPORTPREPOSTREGVAR(Starblur, T) \
|
||||
EXPORTPREPOSTREGVAR(Sineblur, T) \
|
||||
EXPORTPREPOSTREGVAR(Circleblur, T) \
|
||||
EXPORTPREPOSTREGVAR(CropN, T) \
|
||||
EXPORTPREPOSTREGVAR(ShredRad, T) \
|
||||
EXPORTPREPOSTREGVAR(Blob2, T) \
|
||||
EXPORTPREPOSTREGVAR(Julia3D, T) \
|
||||
EXPORTPREPOSTREGVAR(Julia3Dz, T) \
|
||||
EXPORTPREPOSTREGVAR(LinearT, T) \
|
||||
EXPORTPREPOSTREGVAR(LinearT3D, T) \
|
||||
EXPORTPREPOSTREGVAR(Ovoid, T) \
|
||||
EXPORTPREPOSTREGVAR(Ovoid3D, T) \
|
||||
EXPORTPREPOSTREGVAR(Spirograph, T) \
|
||||
EXPORTPREPOSTREGVAR(Petal, T) \
|
||||
EXPORTPREPOSTREGVAR(RoundSpher, T) \
|
||||
EXPORTPREPOSTREGVAR(RoundSpher3D, T) \
|
||||
EXPORTPREPOSTREGVAR(SpiralWing, T) \
|
||||
EXPORTPREPOSTREGVAR(Squarize, T) \
|
||||
EXPORTPREPOSTREGVAR(Sschecks, T) \
|
||||
EXPORTPREPOSTREGVAR(PhoenixJulia, T) \
|
||||
EXPORTPREPOSTREGVAR(Mobius, T) \
|
||||
EXPORTPREPOSTREGVAR(MobiusN, T) \
|
||||
EXPORTPREPOSTREGVAR(MobiusStrip, T) \
|
||||
EXPORTPREPOSTREGVAR(Lissajous, T) \
|
||||
EXPORTPREPOSTREGVAR(Svf, T) \
|
||||
EXPORTPREPOSTREGVAR(Target, T) \
|
||||
EXPORTPREPOSTREGVAR(Taurus, T) \
|
||||
EXPORTPREPOSTREGVAR(Collideoscope, T) \
|
||||
EXPORTPREPOSTREGVAR(BMod, T) \
|
||||
EXPORTPREPOSTREGVAR(BSwirl, T) \
|
||||
EXPORTPREPOSTREGVAR(BTransform, T) \
|
||||
EXPORTPREPOSTREGVAR(BCollide, T) \
|
||||
EXPORTPREPOSTREGVAR(Eclipse, T) \
|
||||
EXPORTPREPOSTREGVAR(FlipCircle, T) \
|
||||
EXPORTPREPOSTREGVAR(FlipY, T) \
|
||||
EXPORTPREPOSTREGVAR(ECollide, T) \
|
||||
EXPORTPREPOSTREGVAR(EJulia, T) \
|
||||
EXPORTPREPOSTREGVAR(EMod, T) \
|
||||
EXPORTPREPOSTREGVAR(EMotion, T) \
|
||||
EXPORTPREPOSTREGVAR(EPush, T) \
|
||||
EXPORTPREPOSTREGVAR(ERotate, T) \
|
||||
EXPORTPREPOSTREGVAR(EScale, T) \
|
||||
EXPORTPREPOSTREGVAR(ESwirl, T) \
|
||||
EXPORTPREPOSTREGVAR(LazyTravis, T) \
|
||||
EXPORTPREPOSTREGVAR(Squish, T) \
|
||||
EXPORTPREPOSTREGVAR(Circus, T) \
|
||||
EXPORTPREPOSTREGVAR(Tancos, T) \
|
||||
EXPORTPREPOSTREGVAR(Rippled, T) \
|
||||
EXPORTPREPOSTREGVAR(Flatten, T) \
|
||||
EXPORTPREPOSTREGVAR(Zblur, T) \
|
||||
EXPORTPREPOSTREGVAR(Blur3D, T) \
|
||||
EXPORTPREPOSTREGVAR(ZScale, T) \
|
||||
EXPORTPREPOSTREGVAR(ZTranslate, T) \
|
||||
EXPORTPREPOSTREGVAR(ZCone, T) \
|
||||
EXPORTPREPOSTREGVAR(Boarders2, T) \
|
||||
EXPORTPREPOSTREGVAR(RotateX, T) \
|
||||
EXPORTPREPOSTREGVAR(RotateY, T) \
|
||||
EXPORTPREPOSTREGVAR(RotateZ, T) \
|
||||
EXPORTPREPOSTREGVAR(MirrorX, T) \
|
||||
EXPORTPREPOSTREGVAR(MirrorY, T) \
|
||||
EXPORTPREPOSTREGVAR(MirrorZ, T) \
|
||||
EXPORTPREPOSTREGVAR(Depth, T) \
|
||||
EXPORTPREPOSTREGVAR(RBlur, T) \
|
||||
EXPORTPREPOSTREGVAR(JuliaNab, T) \
|
||||
EXPORTPREPOSTREGVAR(Sintrange, T) \
|
||||
EXPORTPREPOSTREGVAR(Voron, T) \
|
||||
EXPORTPREPOSTREGVAR(Waffle, T) \
|
||||
EXPORTPREPOSTREGVAR(Square3D, T) \
|
||||
EXPORTPREPOSTREGVAR(SuperShape3D, T) \
|
||||
EXPORTPREPOSTREGVAR(Sphyp3D, T) \
|
||||
EXPORTPREPOSTREGVAR(Circlecrop, T) \
|
||||
EXPORTPREPOSTREGVAR(Julian3Dx, T) \
|
||||
EXPORTPREPOSTREGVAR(Fourth, T) \
|
||||
EXPORTPREPOSTREGVAR(Mobiq, T) \
|
||||
EXPORTPREPOSTREGVAR(Spherivoid, T) \
|
||||
EXPORTPREPOSTREGVAR(Farblur, T) \
|
||||
EXPORTPREPOSTREGVAR(CurlSP, T) \
|
||||
EXPORTPREPOSTREGVAR(Heat, T) \
|
||||
EXPORTPREPOSTREGVAR(Interference2, T) \
|
||||
EXPORTPREPOSTREGVAR(Sinq, T) \
|
||||
EXPORTPREPOSTREGVAR(Sinhq, T) \
|
||||
EXPORTPREPOSTREGVAR(Secq, T) \
|
||||
EXPORTPREPOSTREGVAR(Sechq, T) \
|
||||
EXPORTPREPOSTREGVAR(Tanq, T) \
|
||||
EXPORTPREPOSTREGVAR(Tanhq, T) \
|
||||
EXPORTPREPOSTREGVAR(Cosq, T) \
|
||||
EXPORTPREPOSTREGVAR(Coshq, T) \
|
||||
EXPORTPREPOSTREGVAR(Cotq, T) \
|
||||
EXPORTPREPOSTREGVAR(Cothq, T) \
|
||||
EXPORTPREPOSTREGVAR(Cscq, T) \
|
||||
EXPORTPREPOSTREGVAR(Cschq, T) \
|
||||
EXPORTPREPOSTREGVAR(Estiq, T) \
|
||||
EXPORTPREPOSTREGVAR(Loq, T) \
|
||||
EXPORTPREPOSTREGVAR(Curvature, T) \
|
||||
EXPORTPREPOSTREGVAR(Qode, T) \
|
||||
EXPORTPREPOSTREGVAR(BlurHeart, T) \
|
||||
EXPORTPREPOSTREGVAR(Truchet, T) \
|
||||
EXPORTPREPOSTREGVAR(Gdoffs, T) \
|
||||
EXPORTPREPOSTREGVAR(Octagon, T) \
|
||||
EXPORTPREPOSTREGVAR(Trade, T) \
|
||||
EXPORTPREPOSTREGVAR(Juliac, T) \
|
||||
EXPORTPREPOSTREGVAR(Blade3D, T) \
|
||||
EXPORTPREPOSTREGVAR(Blob3D, T) \
|
||||
EXPORTPREPOSTREGVAR(Blocky, T) \
|
||||
EXPORTPREPOSTREGVAR(Bubble2, T) \
|
||||
EXPORTPREPOSTREGVAR(CircleLinear, T) \
|
||||
EXPORTPREPOSTREGVAR(CircleRand, T) \
|
||||
EXPORTPREPOSTREGVAR(CircleTrans1, T) \
|
||||
EXPORTPREPOSTREGVAR(Cubic3D, T) \
|
||||
EXPORTPREPOSTREGVAR(CubicLattice3D, T) \
|
||||
EXPORTPREPOSTREGVAR(Foci3D, T) \
|
||||
EXPORTPREPOSTREGVAR(Ho, T) \
|
||||
EXPORTPREPOSTREGVAR(Julia3Dq, T) \
|
||||
EXPORTPREPOSTREGVAR(Line, T) \
|
||||
EXPORTPREPOSTREGVAR(Loonie3D, T) \
|
||||
EXPORTPREPOSTREGVAR(Mcarpet, T) \
|
||||
EXPORTPREPOSTREGVAR(Waves23D, T) \
|
||||
EXPORTPREPOSTREGVAR(Pie3D, T) \
|
||||
EXPORTPREPOSTREGVAR(Popcorn23D, T) \
|
||||
EXPORTPREPOSTREGVAR(Sinusoidal3D, T) \
|
||||
EXPORTPREPOSTREGVAR(Scry3D, T) \
|
||||
EXPORTPREPOSTREGVAR(Shredlin, T) \
|
||||
EXPORTPREPOSTREGVAR(SplitBrdr, T) \
|
||||
EXPORTPREPOSTREGVAR(Wdisc, T) \
|
||||
EXPORTPREPOSTREGVAR(Falloff, T) \
|
||||
EXPORTPREPOSTREGVAR(Falloff2, T) \
|
||||
EXPORTPREPOSTREGVAR(Falloff3, T) \
|
||||
EXPORTPREPOSTREGVAR(Xtrb, T) \
|
||||
template EMBER_API class DCBubbleVariation<T>; \
|
||||
EXPORTPREPOSTREGVAR(DCCarpet, T) \
|
||||
EXPORTPREPOSTREGVAR(DCCube, T) \
|
||||
template EMBER_API class DCCylinderVariation<T>; \
|
||||
EXPORTPREPOSTREGVAR(DCGridOut, T) \
|
||||
template EMBER_API class DCLinearVariation<T>; \
|
||||
EXPORTPREPOSTREGVAR(DCZTransl, T) \
|
||||
EXPORTPREPOSTREGVAR(DCTriangle, T) \
|
||||
template EMBER_API class VariationList<T>; \
|
||||
template EMBER_API class SpatialFilter<T>; \
|
||||
template EMBER_API class GaussianFilter<T>; \
|
||||
template EMBER_API class HermiteFilter<T>; \
|
||||
template EMBER_API class BoxFilter<T>; \
|
||||
template EMBER_API class TriangleFilter<T>; \
|
||||
template EMBER_API class BellFilter<T>; \
|
||||
template EMBER_API class BsplineFilter<T>; \
|
||||
template EMBER_API class MitchellFilter<T>; \
|
||||
template EMBER_API class BlackmanFilter<T>; \
|
||||
template EMBER_API class CatromFilter<T>; \
|
||||
template EMBER_API class HanningFilter<T>; \
|
||||
template EMBER_API class HammingFilter<T>; \
|
||||
template EMBER_API class Lanczos3Filter<T>; \
|
||||
template EMBER_API class Lanczos2Filter<T>; \
|
||||
template EMBER_API class QuadraticFilter<T>; \
|
||||
template EMBER_API class DensityFilter<T>; \
|
||||
template EMBER_API class TemporalFilter<T>; \
|
||||
template EMBER_API class ExpTemporalFilter<T>; \
|
||||
template EMBER_API class GaussianTemporalFilter<T>; \
|
||||
template EMBER_API class BoxTemporalFilter<T>; \
|
||||
template EMBER_API class SpatialFilterCreator<T>; \
|
||||
template EMBER_API class TemporalFilterCreator<T>; \
|
||||
template EMBER_API class Interpolater<T>; \
|
||||
template EMBER_API class Ember<T>; \
|
||||
/*template EMBER_API class RenderCallback<T>;*/ \
|
||||
template EMBER_API class CarToRas<T>; \
|
||||
template EMBER_API class XmlToEmber<T>; \
|
||||
template EMBER_API class EmberToXml<T>; \
|
||||
bool PaletteList<T>::m_Init = false; \
|
||||
vector<Palette<T>> PaletteList<T>::m_Palettes = vector<Palette<T>>(); \
|
||||
bool XmlToEmber<T>::m_Init = false; \
|
||||
vector<pair<string, string>> XmlToEmber<T>::m_BadParamNames = vector<pair<string, string>>(); \
|
||||
vector<pair<string, string>> XmlToEmber<T>::m_BadVariationNames = vector<pair<string, string>>();
|
||||
|
||||
EXPORT_SINGLE_TYPE_EMBER(float)
|
||||
|
||||
#define EXPORT_TWO_TYPE_EMBER(T, bucketT) \
|
||||
template EMBER_API class Renderer<T, bucketT>; \
|
||||
template EMBER_API class SheepTools<T, bucketT>;
|
||||
|
||||
EXPORT_TWO_TYPE_EMBER(float, float)
|
||||
|
||||
#ifdef DO_DOUBLE
|
||||
EXPORT_SINGLE_TYPE_EMBER(double)
|
||||
EXPORT_TWO_TYPE_EMBER(double, double)
|
||||
#endif
|
||||
}
|
1607
Source/Ember/Ember.h
Normal file
1607
Source/Ember/Ember.h
Normal file
File diff suppressed because it is too large
Load Diff
83
Source/Ember/EmberDefines.h
Normal file
83
Source/Ember/EmberDefines.h
Normal file
@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
|
||||
#include "EmberPch.h"
|
||||
|
||||
/// <summary>
|
||||
/// Basic #defines used throughout the library.
|
||||
/// </summary>
|
||||
|
||||
//MSVC specific?
|
||||
#if defined(BUILDING_EMBER)
|
||||
#define EMBER_API __declspec(dllexport)
|
||||
#else
|
||||
#define EMBER_API __declspec(dllimport)
|
||||
#endif
|
||||
|
||||
#define RESTRICT __restrict//This might make things faster, unsure if it really does though.
|
||||
//#define RESTRICT
|
||||
|
||||
namespace EmberNs
|
||||
{
|
||||
//Wrap the sincos function for Macs and PC.
|
||||
#if defined(__APPLE__) || defined(_MSC_VER)
|
||||
#define sincos(x, s, c) *(s)=sin(x); *(c)=cos(x);
|
||||
#else
|
||||
extern void sincos(double x, double *s, double *c);
|
||||
#endif
|
||||
|
||||
#define EMBER_VERSION "0.4.0.2"
|
||||
#define EPS6 T(1e-6)
|
||||
#define EPS T(1e-10)//Apoplugin.h uses -20, but -10 seems to work fine.
|
||||
#define ISAAC_SIZE 4
|
||||
#define MEMALIGN 32
|
||||
#define DE_THRESH 100
|
||||
#define MAX_VARS_PER_XFORM 8
|
||||
#define DEG_2_RAD (M_PI / 180)
|
||||
#define RAD_2_DEG (180 / M_PI)
|
||||
#define DEG_2_RAD_T (T(M_PI) / T(180))
|
||||
#define RAD_2_DEG_T (T(180) / T(M_PI))
|
||||
#define M_2PI (T(M_PI * 2))
|
||||
#define M_3PI (T(M_PI * 3))
|
||||
#define SQRT5 T(2.2360679774997896964091736687313)
|
||||
#define M_PHI T(1.61803398874989484820458683436563)
|
||||
#define COLORMAP_LENGTH 256//These will need to change if 2D palette support is ever added, or variable sized palettes.
|
||||
#define COLORMAP_LENGTH_MINUS_1 255
|
||||
#define WHITE 255
|
||||
#define XC (const xmlChar*)
|
||||
#define BadVal(x) (((x) != (x)) || ((x) > 1e10) || ((x) < -1e10))
|
||||
#define Rint(A) floor((A) + (((A) < 0) ? T(-0.5) : T(0.5)))
|
||||
#define Vlen(x) (sizeof(x) / sizeof(*x))
|
||||
#define SQR(x) ((x) * (x))
|
||||
#define CUBE(x) ((x) * (x) * (x))
|
||||
#define TLOW std::numeric_limits<T>::lowest()
|
||||
#define TMAX std::numeric_limits<T>::max()
|
||||
|
||||
#ifndef acosh
|
||||
#define acosh(x) (log(x + sqrt(SQR(x) - 1)))//Remove this once you upgrade compilers to VS 2013 or later.//TODO
|
||||
#endif
|
||||
|
||||
#ifndef fma
|
||||
#define fma(x, y, z) ((x * y) + z)
|
||||
#endif
|
||||
|
||||
#define DO_DOUBLE 1//Comment this out for shorter build times during development. Always uncomment for release.
|
||||
|
||||
#define v2T glm::detail::tvec2<T, glm::defaultp>
|
||||
#define v3T glm::detail::tvec3<T, glm::defaultp>
|
||||
#define v4T glm::detail::tvec4<T, glm::defaultp>
|
||||
#define m2T glm::detail::tmat2x2<T, glm::defaultp>
|
||||
#define m3T glm::detail::tmat3x3<T, glm::defaultp>
|
||||
#define m4T glm::detail::tmat4x4<T, glm::defaultp>
|
||||
#define m23T glm::detail::tmat2x3<T, glm::defaultp>
|
||||
|
||||
enum eInterp : unsigned int { EMBER_INTERP_LINEAR = 0, EMBER_INTERP_SMOOTH = 1 };
|
||||
enum eAffineInterp : unsigned int { INTERP_LINEAR = 0, INTERP_LOG = 1, INTERP_COMPAT = 2, INTERP_OLDER = 3 };
|
||||
enum ePaletteMode : unsigned int { PALETTE_STEP = 0, PALETTE_LINEAR = 1 };
|
||||
enum ePaletteInterp : unsigned int { INTERP_HSV = 0, INTERP_SWEEP = 1 };
|
||||
enum eMotion : unsigned int { MOTION_SIN = 1, MOTION_TRIANGLE = 2, MOTION_HILL = 3 };
|
||||
enum eProcessAction : unsigned int { NOTHING = 0, ACCUM_ONLY = 1, FILTER_AND_ACCUM = 2, KEEP_ITERATING = 3, FULL_RENDER = 4 };
|
||||
enum eProcessState : unsigned int { NONE = 0, ITER_STARTED = 1, ITER_DONE = 2, FILTER_DONE = 3, ACCUM_DONE = 4 };
|
||||
enum eInteractiveFilter : unsigned int { FILTER_LOG = 0, FILTER_DE = 1 };
|
||||
enum eScaleType : unsigned int { SCALE_NONE = 0, SCALE_WIDTH = 1, SCALE_HEIGHT = 2 };
|
||||
enum eRenderStatus : unsigned int { RENDER_OK = 0, RENDER_ERROR = 1, RENDER_ABORT = 2 };
|
||||
}
|
1
Source/Ember/EmberPch.cpp
Normal file
1
Source/Ember/EmberPch.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "EmberPch.h"
|
62
Source/Ember/EmberPch.h
Normal file
62
Source/Ember/EmberPch.h
Normal file
@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
/// <summary>
|
||||
/// Precompiled header file. Place all system includes here with appropriate #defines for different operating systems and compilers.
|
||||
/// </summary>
|
||||
|
||||
#define NOMINMAX
|
||||
#define _USE_MATH_DEFINES
|
||||
|
||||
#ifdef _WIN32
|
||||
#define basename(x) _strdup(x)
|
||||
#define snprintf _snprintf
|
||||
#define snprintf_s _snprintf_s
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define EMBER_OS "WIN"
|
||||
|
||||
#include <SDKDDKVer.h>
|
||||
#include <windows.h>
|
||||
#elif __APPLE__
|
||||
#define EMBER_OS "OSX"
|
||||
#else
|
||||
#include <libgen.h>
|
||||
#include <unistd.h>
|
||||
#define EMBER_OS "LNX"
|
||||
#endif
|
||||
|
||||
//Standard headers.
|
||||
#include <algorithm>
|
||||
#include <complex>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
#include <malloc.h>
|
||||
#include <math.h>
|
||||
#include <numeric>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <vector>
|
||||
|
||||
//Third party headers.
|
||||
#include <libxml/parser.h>
|
||||
|
||||
//Intel's Threading Building Blocks is what's used for all threading.
|
||||
#include "tbb/task_group.h"
|
||||
#include "tbb/parallel_for.h"
|
||||
#include "tbb/task_scheduler_init.h"
|
||||
|
||||
#define GLM_FORCE_RADIANS
|
||||
|
||||
//glm is what's used for matrix math.
|
||||
#include "glm/glm.hpp"
|
||||
#include "glm/gtc/matrix_transform.hpp"
|
||||
#include "glm/gtc/type_ptr.hpp"
|
||||
#include "glm/gtx/string_cast.hpp"
|
||||
|
||||
using namespace tbb;
|
||||
using namespace std;
|
686
Source/Ember/EmberToXml.h
Normal file
686
Source/Ember/EmberToXml.h
Normal file
@ -0,0 +1,686 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utils.h"
|
||||
#include "PaletteList.h"
|
||||
#include "VariationList.h"
|
||||
#include "Ember.h"
|
||||
|
||||
/// <summary>
|
||||
/// EmberToXml class.
|
||||
/// </summary>
|
||||
|
||||
namespace EmberNs
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for converting ember objects to Xml documents.
|
||||
/// Support for saving one or more to a single file.
|
||||
/// Template argument expected to be float or double.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API EmberToXml : public EmberReport
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Empty constructor.
|
||||
/// </summary>
|
||||
EmberToXml()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the ember to the specified file.
|
||||
/// </summary>
|
||||
/// <param name="filename">Full path and filename</param>
|
||||
/// <param name="ember">The ember to save</param>
|
||||
/// <param name="printEditDepth">How deep the edit depth goes</param>
|
||||
/// <param name="doEdits">If true included edit tags, else don't.</param>
|
||||
/// <param name="intPalette">If true use integers instead of floating point numbers when embedding a non-hex formatted palette, else use floating point numbers.</param>
|
||||
/// <param name="hexPalette">If true, embed a hexadecimal palette instead of Xml Color tags, else use Xml color tags.</param>
|
||||
/// <param name="append">If true, append to the file if it already exists, else create a new file.</param>
|
||||
/// <param name="start">Whether a new file is to be started</param>
|
||||
/// <param name="finish">Whether an existing file is to be ended</param>
|
||||
/// <returns>True if successful, else false</returns>
|
||||
bool Save(string filename, Ember<T>& ember, unsigned int printEditDepth, bool doEdits, bool intPalette, bool hexPalette, bool append = false, bool start = false, bool finish = false)
|
||||
{
|
||||
vector<Ember<T>> vec;
|
||||
|
||||
vec.push_back(ember);
|
||||
return Save(filename, vec, printEditDepth, doEdits, intPalette, hexPalette, append, start, finish);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save a vector of embers to the specified file.
|
||||
/// </summary>
|
||||
/// <param name="filename">Full path and filename</param>
|
||||
/// <param name="embers">The vector of embers to save</param>
|
||||
/// <param name="printEditDepth">How deep the edit depth goes</param>
|
||||
/// <param name="doEdits">If true included edit tags, else don't.</param>
|
||||
/// <param name="intPalette">If true use integers instead of floating point numbers when embedding a non-hex formatted palette, else use floating point numbers.</param>
|
||||
/// <param name="hexPalette">If true, embed a hexadecimal palette instead of Xml Color tags, else use Xml color tags.</param>
|
||||
/// <param name="append">If true, append to the file if it already exists, else create a new file.</param>
|
||||
/// <param name="start">Whether a new file is to be started</param>
|
||||
/// <param name="finish">Whether an existing file is to be ended</param>
|
||||
/// <returns>True if successful, else false</returns>
|
||||
bool Save(string filename, vector<Ember<T>>& embers, unsigned int printEditDepth, bool doEdits, bool intPalette, bool hexPalette, bool append = false, bool start = false, bool finish = false)
|
||||
{
|
||||
bool b = false;
|
||||
string temp;
|
||||
ofstream f;
|
||||
|
||||
try
|
||||
{
|
||||
if (append)
|
||||
f.open(filename, std::ofstream::out | std::ofstream::app);//Appending allows us to write multiple embers to a single file.
|
||||
else
|
||||
f.open(filename);
|
||||
|
||||
if (f.is_open())
|
||||
{
|
||||
if ((append && start) || !append)
|
||||
{
|
||||
temp = "<flames>\n";
|
||||
f.write(temp.c_str(), temp.size());
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < embers.size(); i++)
|
||||
{
|
||||
string s = ToString(embers[i], "", printEditDepth, doEdits, intPalette, hexPalette);
|
||||
f.write(s.c_str(), s.size());
|
||||
}
|
||||
|
||||
if ((append && finish) || !append)
|
||||
{
|
||||
temp = "</flames>\n";
|
||||
f.write(temp.c_str(), temp.size());
|
||||
}
|
||||
|
||||
f.close();
|
||||
b = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "Error: Writing flame " << filename << " failed." << endl;
|
||||
b = false;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if (f.is_open())
|
||||
f.close();
|
||||
|
||||
b = false;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the Xml string representation of an ember.
|
||||
/// </summary>
|
||||
/// <param name="ember">The ember to create the Xml with</param>
|
||||
/// <param name="extraAttributes">If true, add extra attributes, else don't</param>
|
||||
/// <param name="printEditDepth">How deep the edit depth goes</param>
|
||||
/// <param name="doEdits">If true included edit tags, else don't.</param>
|
||||
/// <param name="intPalette">If true use integers instead of floating point numbers when embedding a non-hex formatted palette, else use floating point numbers.</param>
|
||||
/// <param name="hexPalette">If true, embed a hexadecimal palette instead of Xml Color tags, else use Xml color tags.</param>
|
||||
/// <returns>The Xml string representation of the passed in ember</returns>
|
||||
string ToString(Ember<T>& ember, string extraAttributes, unsigned int printEditDepth, bool doEdits, bool intPalette, bool hexPalette = true)
|
||||
{
|
||||
unsigned int i, j;
|
||||
ostringstream os;
|
||||
|
||||
os << "<flame version=\"EMBER-" << EmberVersion() << "\" time=\"" << ember.m_Time << "\"";
|
||||
|
||||
if (!ember.m_Name.empty())
|
||||
os << " name=\"" << ember.m_Name << "\"";
|
||||
|
||||
os << " size=\"" << ember.m_FinalRasW << " " << ember.m_FinalRasH << "\"";
|
||||
os << " center=\"" << ember.m_CenterX << " " << ember.m_CenterY << "\"";
|
||||
os << " scale=\"" << ember.m_PixelsPerUnit << "\"";
|
||||
|
||||
if (ember.m_Zoom != 0)
|
||||
os << " zoom=\"" << ember.m_Zoom << "\"";
|
||||
|
||||
os << " rotate=\"" << ember.m_Rotate << "\"";
|
||||
os << " supersample=\"" << max(1u, ember.m_Supersample) << "\"";
|
||||
os << " filter=\"" << ember.m_SpatialFilterRadius << "\"";
|
||||
|
||||
os << " filter_shape=\"" << ToLower(SpatialFilterCreator<T>::ToString(ember.m_SpatialFilterType)) << "\"";
|
||||
os << " temporal_filter_type=\"" << ToLower(TemporalFilterCreator<T>::ToString(ember.m_TemporalFilterType)) << "\"";
|
||||
|
||||
if (ember.m_TemporalFilterType == EXP_TEMPORAL_FILTER)
|
||||
os << " temporal_filter_exp=\"" << ember.m_TemporalFilterExp << "\"";
|
||||
|
||||
os << " temporal_filter_width=\"" << ember.m_TemporalFilterWidth << "\"";
|
||||
os << " quality=\"" << ember.m_Quality << "\"";
|
||||
os << " passes=\"" << ember.m_Passes << "\"";
|
||||
os << " temporal_samples=\"" << ember.m_TemporalSamples << "\"";
|
||||
os << " background=\"" << ember.m_Background.r << " " << ember.m_Background.g << " " << ember.m_Background.b << "\"";
|
||||
os << " brightness=\"" << ember.m_Brightness << "\"";
|
||||
os << " gamma=\"" << ember.m_Gamma << "\"";
|
||||
os << " highlight_power=\"" << ember.m_HighlightPower << "\"";
|
||||
os << " vibrancy=\"" << ember.m_Vibrancy << "\"";
|
||||
//os << " hue=\"" << ember.m_Hue << "\"";//Oddly enough, flam3 never wrote this value out.//ORIG
|
||||
os << " estimator_radius=\"" << ember.m_MaxRadDE << "\"";
|
||||
os << " estimator_minimum=\"" << ember.m_MinRadDE << "\"";
|
||||
os << " estimator_curve=\"" << ember.m_CurveDE << "\"";
|
||||
os << " gamma_threshold=\"" << ember.m_GammaThresh << "\"";
|
||||
os << " cam_zpos=\"" << ember.m_CamZPos << "\"";
|
||||
os << " cam_persp=\"" << ember.m_CamPerspective << "\"";
|
||||
os << " cam_yaw=\"" << ember.m_CamYaw << "\"";
|
||||
os << " cam_pitch=\"" << ember.m_CamPitch << "\"";
|
||||
os << " cam_dof=\"" << ember.m_CamDepthBlur << "\"";
|
||||
|
||||
if (ember.m_PaletteMode == PALETTE_STEP)
|
||||
os << " palette_mode=\"step\"";
|
||||
else if (ember.m_PaletteMode == PALETTE_LINEAR)
|
||||
os << " palette_mode=\"linear\"";
|
||||
|
||||
if (ember.m_Interp == EMBER_INTERP_SMOOTH)
|
||||
os << " interpolation=\"smooth\"";
|
||||
|
||||
if (ember.m_AffineInterp == INTERP_LINEAR)
|
||||
os << " interpolation_type=\"linear\"";
|
||||
else if (ember.m_AffineInterp == INTERP_LOG)
|
||||
os << " interpolation_type=\"log\"";
|
||||
else if (ember.m_AffineInterp == INTERP_COMPAT)
|
||||
os << " interpolation_type=\"old\"";
|
||||
else if (ember.m_AffineInterp == INTERP_OLDER)
|
||||
os << " interpolation_type=\"older\"";
|
||||
|
||||
if (ember.m_PaletteInterp == INTERP_SWEEP)
|
||||
os << " palette_interpolation=\"sweep\"";
|
||||
|
||||
if (!extraAttributes.empty())
|
||||
os << " " << extraAttributes;
|
||||
|
||||
os << ">\n";
|
||||
|
||||
//This is a grey area, what to do about symmetry to avoid duplicating the symmetry xforms when reading back?//TODO//BUG.
|
||||
//if (ember.m_Symmetry)
|
||||
// os << " <symmetry kind=\"" << ember.m_Symmetry << "\"/>\n";
|
||||
|
||||
for (i = 0; i < ember.XformCount(); i++)
|
||||
os << ToString(*ember.GetXform(i), ember.XformCount(), false, false);//Not final, don't do motion.
|
||||
|
||||
if (ember.UseFinalXform())
|
||||
os << ToString(*ember.NonConstFinalXform(), ember.XformCount(), true, false);//Final, don't do motion.
|
||||
|
||||
if (hexPalette)
|
||||
{
|
||||
os << " <palette count=\"256\" format=\"RGB\">\n";
|
||||
|
||||
for (i = 0; i < 32; i++)
|
||||
{
|
||||
os << " ";
|
||||
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
int idx = 8 * i + j;
|
||||
|
||||
os << hex << setw(2) << setfill('0') << (int)Rint(ember.m_Palette[idx][0] * 255);
|
||||
os << hex << setw(2) << setfill('0') << (int)Rint(ember.m_Palette[idx][1] * 255);
|
||||
os << hex << setw(2) << setfill('0') << (int)Rint(ember.m_Palette[idx][2] * 255);
|
||||
}
|
||||
|
||||
os << endl;
|
||||
}
|
||||
|
||||
os << " </palette>\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
double r = ember.m_Palette[i][0] * 255;
|
||||
double g = ember.m_Palette[i][1] * 255;
|
||||
double b = ember.m_Palette[i][2] * 255;
|
||||
double a = ember.m_Palette[i][3] * 255;
|
||||
|
||||
os << " ";
|
||||
//The original used a precision of 6 which is totally unnecessary, use 2.
|
||||
if (IsClose(a, 255.0))
|
||||
{
|
||||
if (intPalette)
|
||||
os << "<color index=\"" << i << "\" rgb=\"" << (int)Rint(r) << " " << (int)Rint(g) << " " << (int)Rint(b) << "\"/>";
|
||||
else
|
||||
os << "<color index=\"" << i << "\" rgb=\"" << std::fixed << std::setprecision(2) << r << " " << g << " " << b << "\"/>";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (intPalette)
|
||||
os << " <color index=\"" << i << "\" rgba=\"" << (int)Rint(r) << " " << (int)Rint(g) << " " << (int)Rint(b) << " " << (int)Rint(a) << "\"/>";
|
||||
else
|
||||
os << " <color index=\"" << i << "\" rgba=\"" << std::fixed << std::setprecision(2) << r << " " << g << " " << b << " " << a << "\"/>";
|
||||
}
|
||||
|
||||
os << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (doEdits && ember.m_Edits != NULL)
|
||||
os << ToString(xmlDocGetRootElement(ember.m_Edits), 1, true, printEditDepth);
|
||||
|
||||
os << "</flame>\n";
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new editdoc optionally based on parents passed in.
|
||||
/// This is used when an ember is made out of some mutation or edit from one or two existing embers and
|
||||
/// the user wants to capture the genetic lineage history information in the edit doc of the new ember.
|
||||
/// </summary>
|
||||
/// <param name="parent0">The first parent, optionally NULL.</param>
|
||||
/// <param name="parent1">The second parent, optionally NULL.</param>
|
||||
/// <param name="action">The action that was taken to create the new ember</param>
|
||||
/// <param name="nick">The nickname of the author</param>
|
||||
/// <param name="url">The Url of the author</param>
|
||||
/// <param name="id">The id of the author</param>
|
||||
/// <param name="comment">The comment to include</param>
|
||||
/// <param name="sheepGen">The sheep generation used if > 0. Default: 0.</param>
|
||||
/// <param name="sheepId">The sheep id used if > 0. Default: 0.</param>
|
||||
/// <returns></returns>
|
||||
xmlDocPtr CreateNewEditdoc(Ember<T>* parent0, Ember<T>* parent1, string action, string nick, string url, string id, string comment, int sheepGen = 0, int sheepId = 0)
|
||||
{
|
||||
char timeString[128];
|
||||
char buffer[128];
|
||||
char commentString[128];
|
||||
tm localt;
|
||||
time_t myTime;
|
||||
xmlDocPtr commentDoc = NULL;
|
||||
xmlDocPtr doc = xmlNewDoc(XC "1.0");
|
||||
xmlNodePtr rootNode = NULL, node = NULL, nodeCopy = NULL;
|
||||
xmlNodePtr rootComment = NULL;
|
||||
|
||||
//Create the root node, called "edit".
|
||||
rootNode = xmlNewNode(NULL, XC "edit");
|
||||
xmlDocSetRootElement(doc, rootNode);
|
||||
|
||||
//Add the edit attributes.
|
||||
//Date.
|
||||
myTime = time(NULL);
|
||||
localtime_s(&localt, &myTime);
|
||||
strftime(timeString, 128, "%a %b %d %H:%M:%S %z %Y", &localt);//XXX use standard time format including timezone.
|
||||
xmlNewProp(rootNode, XC "date", XC timeString);
|
||||
|
||||
//Nick.
|
||||
if (nick != "")
|
||||
xmlNewProp(rootNode, XC "nick", XC nick.c_str());
|
||||
|
||||
//Url.
|
||||
if (url != "")
|
||||
xmlNewProp(rootNode, XC "url", XC url.c_str());
|
||||
|
||||
if (id != "")
|
||||
xmlNewProp(rootNode, XC "id", XC id.c_str());
|
||||
|
||||
//Action.
|
||||
xmlNewProp(rootNode, XC "action", XC action.c_str());
|
||||
|
||||
//Sheep info.
|
||||
if (sheepGen > 0 && sheepId > 0)
|
||||
{
|
||||
//Create a child node of the root node called sheep.
|
||||
node = xmlNewChild(rootNode, NULL, XC "sheep", NULL);
|
||||
|
||||
//Create the sheep attributes.
|
||||
sprintf_s(buffer, 128, "%d", sheepGen);
|
||||
xmlNewProp(node, XC "generation", XC buffer);
|
||||
|
||||
sprintf_s(buffer, 128, "%d", sheepId);
|
||||
xmlNewProp(node, XC "id", XC buffer);
|
||||
}
|
||||
|
||||
//Check for the parents.
|
||||
//If parent 0 not specified, this is a randomly generated genome.
|
||||
if (parent0)
|
||||
{
|
||||
if (parent0->m_Edits)
|
||||
{
|
||||
//Copy the node from the parent.
|
||||
node = xmlDocGetRootElement(parent0->m_Edits);
|
||||
nodeCopy = xmlCopyNode(node, 1);
|
||||
AddFilenameWithoutAmpersand(nodeCopy, parent0->m_ParentFilename);
|
||||
sprintf_s(buffer, 128, "%d", parent0->m_Index);
|
||||
xmlNewProp(nodeCopy, XC "index", XC buffer);
|
||||
xmlAddChild(rootNode, nodeCopy);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Insert a (parent has no edit) message.
|
||||
nodeCopy = xmlNewChild(rootNode, NULL, XC "edit", NULL);
|
||||
AddFilenameWithoutAmpersand(nodeCopy, parent0->m_ParentFilename);
|
||||
sprintf_s(buffer, 128, "%d", parent0->m_Index);
|
||||
xmlNewProp(nodeCopy, XC "index", XC buffer);
|
||||
}
|
||||
}
|
||||
|
||||
if (parent1)
|
||||
{
|
||||
if (parent1->m_Edits)
|
||||
{
|
||||
//Copy the node from the parent.
|
||||
node = xmlDocGetRootElement(parent1->m_Edits);
|
||||
nodeCopy = xmlCopyNode(node, 1);
|
||||
AddFilenameWithoutAmpersand(nodeCopy, parent1->m_ParentFilename);
|
||||
sprintf_s(buffer, 128, "%d", parent1->m_Index);
|
||||
xmlNewProp(nodeCopy, XC "index", XC buffer);
|
||||
xmlAddChild(rootNode, nodeCopy);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Insert a (parent has no edit) message.
|
||||
nodeCopy = xmlNewChild(rootNode, NULL, XC "edit",NULL);
|
||||
AddFilenameWithoutAmpersand(nodeCopy, parent1->m_ParentFilename);
|
||||
sprintf_s(buffer, 128, "%d", parent1->m_Index);
|
||||
xmlNewProp(nodeCopy, XC "index", XC buffer);
|
||||
}
|
||||
}
|
||||
|
||||
//Comment string:
|
||||
//This one's hard, since the comment string must be treated as
|
||||
//a valid XML document. Create a new document using the comment
|
||||
//string as the in-memory document, and then copy all children of
|
||||
//the root node into the edit structure
|
||||
//Parsing the comment string should be done once and then copied
|
||||
//for each new edit doc, but that's for later.
|
||||
if (comment != "")
|
||||
{
|
||||
sprintf_s(commentString, 128, "<comm>%s</comm>", comment.c_str());
|
||||
commentDoc = xmlReadMemory(commentString, (int)strlen(commentString), "comment.env", NULL, XML_PARSE_NONET);
|
||||
|
||||
//Check for errors.
|
||||
if (commentDoc != NULL)
|
||||
{
|
||||
|
||||
//Loop through the children of the new document and copy them into the rootNode.
|
||||
rootComment = xmlDocGetRootElement(commentDoc);
|
||||
|
||||
for (node = rootComment->children; node; node = node->next)
|
||||
{
|
||||
nodeCopy = xmlCopyNode(node, 1);
|
||||
xmlAddChild(rootNode, nodeCopy);
|
||||
}
|
||||
|
||||
//Free the created document.
|
||||
xmlFreeDoc(commentDoc);
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "Failed to parse comment into Xml." << endl;
|
||||
}
|
||||
}
|
||||
|
||||
//Return the xml doc.
|
||||
return doc;
|
||||
}
|
||||
|
||||
private:
|
||||
/// <summary>
|
||||
/// Return the Xml string representation of an xform.
|
||||
/// </summary>
|
||||
/// <param name="xform">The xform to create the Xml with</param>
|
||||
/// <param name="xformCount">The number of non-final xforms in the ember to which this xform belongs. Used for xaos.</param>
|
||||
/// <param name="isFinal">True if the xform is the final xform in the ember, else false.</param>
|
||||
/// <param name="doMotion">If true, include motion elements in the Xml string, else omit.</param>
|
||||
/// <returns>The Xml string representation of the passed in xform</returns>
|
||||
string ToString(Xform<T>& xform, unsigned int xformCount, bool isFinal, bool doMotion)
|
||||
{
|
||||
unsigned int i, j;
|
||||
ostringstream os;
|
||||
|
||||
if (doMotion)
|
||||
{
|
||||
os << " <motion motion_frequency=\"" << xform.m_MotionFreq << "\" ";
|
||||
|
||||
if (xform.m_MotionFunc == MOTION_SIN)
|
||||
os << "motion_function=\"sin\" ";
|
||||
else if (xform.m_MotionFunc == MOTION_TRIANGLE)
|
||||
os << "motion_function=\"triangle\" ";
|
||||
else if (xform.m_MotionFunc== MOTION_HILL)
|
||||
os << "motion_function=\"hill\" ";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isFinal)
|
||||
os << " <finalxform ";
|
||||
else
|
||||
os << " <xform weight=\"" << xform.m_Weight << "\" ";
|
||||
}
|
||||
|
||||
if (!doMotion)
|
||||
{
|
||||
os << "color=\"" << xform.m_ColorX << "\" ";
|
||||
//os << "color=\"" << xform.m_ColorX << " " << xform.m_ColorY << "\" ";
|
||||
os << "var_color=\"" << xform.m_DirectColor << "\" ";
|
||||
os << "color_speed=\"" << xform.m_ColorSpeed << "\" ";
|
||||
os << "symmetry=\"" << xform.m_ColorSpeed << "\" ";//Legacy support.
|
||||
|
||||
string s = xform.m_Name;
|
||||
|
||||
std::replace(s.begin(), s.end(), ' ', '_');
|
||||
os << "name=\"" << s << "\" ";//Flam3 didn't do this, but Apo does.
|
||||
|
||||
if (!isFinal)
|
||||
os << "animate=\"" << xform.m_Animate << "\" ";
|
||||
}
|
||||
|
||||
//Variation writing order differs slightly from the original to make it a bit more readable.
|
||||
//The original wrote out all of the variation names and weights. Then wrote out the parameters for
|
||||
//the parametric variations. Here, write out the params immediately after each parametric variation
|
||||
//so they are more closely grouped with the variation they apply to, rather than being all grouped at the end.
|
||||
for (i = 0; i < xform.TotalVariationCount(); i++)
|
||||
{
|
||||
Variation<T>* var = xform.GetVariation(i);
|
||||
ParametricVariation<T>* parVar = dynamic_cast<ParametricVariation<T>*>(var);
|
||||
|
||||
if (var->m_Weight != 0)
|
||||
{
|
||||
os << var->Name() << "=\"" << var->m_Weight << "\" ";
|
||||
|
||||
if (parVar)
|
||||
{
|
||||
ParamWithName<T>* params = parVar->Params();
|
||||
|
||||
for (j = 0; j < parVar->ParamCount(); j++)
|
||||
{
|
||||
if ((!doMotion || (doMotion && (params[j].ParamVal() != 0))) && !params[j].IsPrecalc())
|
||||
os << params[j].Name() << "=\"" << params[j].ParamVal() << "\" ";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!doMotion || (doMotion && !xform.m_Affine.IsZero()))
|
||||
{
|
||||
os << "coefs=\"" << xform.m_Affine.A() << " " << xform.m_Affine.D() << " " << xform.m_Affine.B() << " "
|
||||
<< xform.m_Affine.E() << " " << xform.m_Affine.C() << " " << xform.m_Affine.F() << "\"";
|
||||
}
|
||||
|
||||
if ((!doMotion && !xform.m_Post.IsID()) || (doMotion && !xform.m_Post.IsZero()))
|
||||
{
|
||||
os << " post=\"" << xform.m_Post.A() << " " << xform.m_Post.D() << " " << xform.m_Post.B() << " "
|
||||
<< xform.m_Post.E() << " " << xform.m_Post.C() << " " << xform.m_Post.F() << "\"";
|
||||
}
|
||||
|
||||
//Original only printed xaos values that were not 1. Here, print them all out if any are present.
|
||||
if (!isFinal && !doMotion && xform.XaosPresent())
|
||||
{
|
||||
os << " chaos=\"";
|
||||
|
||||
for (i = 0; i < xformCount; i++)
|
||||
os << xform.Xaos(i) << " ";
|
||||
|
||||
os << "\"";
|
||||
}
|
||||
|
||||
if (!doMotion)
|
||||
os << " opacity=\"" << xform.m_Opacity << "\"";
|
||||
|
||||
if (!doMotion && !xform.m_Motion.empty())
|
||||
{
|
||||
os << ">\n";
|
||||
|
||||
for (i = 0; i < xform.m_Motion.size(); i++)
|
||||
os << ToString(xform.m_Motion[i], 0, false, true);
|
||||
|
||||
if (isFinal)//Fixed to properly close final.//SMOULDER
|
||||
os << " </finalxform>\n";
|
||||
else
|
||||
os << " </xform>\n";
|
||||
}
|
||||
else
|
||||
os << "/>\n";
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return an edit node Xml string.
|
||||
/// </summary>
|
||||
/// <param name="editNode">The edit node to get the string for</param>
|
||||
/// <param name="tabs">How many tabs to use</param>
|
||||
/// <param name="formatting">If true, include newlines and tabs, else don't.</param>
|
||||
/// <param name="printEditDepth">How deep the edit depth goes</param>
|
||||
/// <returns>The edit node Xml string</returns>
|
||||
string ToString(xmlNodePtr editNode, unsigned int tabs, bool formatting, unsigned int printEditDepth)
|
||||
{
|
||||
bool indentPrinted = false;
|
||||
char* tabString = " ", *attStr;
|
||||
unsigned int ti, editOrSheep = 0;
|
||||
xmlAttrPtr attPtr = NULL, curAtt = NULL;
|
||||
xmlNodePtr childPtr = NULL, curChild = NULL;
|
||||
ostringstream os;
|
||||
|
||||
if (printEditDepth > 0 && tabs > printEditDepth)
|
||||
return "";
|
||||
|
||||
//If this node is an XML_ELEMENT_NODE, print it and its attributes.
|
||||
if (editNode->type == XML_ELEMENT_NODE)
|
||||
{
|
||||
//Print the node at the tab specified.
|
||||
if (formatting)
|
||||
for (ti = 0; ti < tabs; ti++)
|
||||
os << tabString;
|
||||
|
||||
os << "<" << editNode->name;
|
||||
|
||||
//This can either be an edit node or a sheep node.
|
||||
//If it's an edit node, add one to the tab.
|
||||
if (!Compare(editNode->name, "edit"))
|
||||
{
|
||||
editOrSheep = 1;
|
||||
tabs++;
|
||||
}
|
||||
else if (!Compare(editNode->name, "sheep"))
|
||||
editOrSheep = 2;
|
||||
else
|
||||
editOrSheep = 0;
|
||||
|
||||
//Print the attributes.
|
||||
attPtr = editNode->properties;
|
||||
|
||||
for (curAtt = attPtr; curAtt; curAtt = curAtt->next)
|
||||
{
|
||||
attStr = (char*)xmlGetProp(editNode, curAtt->name);
|
||||
os << " " << curAtt->name << "=\"" << attStr << "\"";
|
||||
xmlFree(attStr);
|
||||
}
|
||||
|
||||
//Does this node have children?
|
||||
if (!editNode->children || (printEditDepth > 0 && tabs > printEditDepth))
|
||||
{
|
||||
//Close the tag and subtract the tab.
|
||||
os << "/>";
|
||||
|
||||
if (formatting)
|
||||
os << "\n";
|
||||
|
||||
tabs--;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Close the tag.
|
||||
os << ">";
|
||||
|
||||
if (formatting)
|
||||
os << "\n";
|
||||
|
||||
//Loop through the children and print them.
|
||||
childPtr = editNode->children;
|
||||
indentPrinted = false;
|
||||
|
||||
for (curChild = childPtr; curChild; curChild = curChild->next)
|
||||
{
|
||||
//If child is an element, indent first and then print it.
|
||||
if (curChild->type == XML_ELEMENT_NODE &&
|
||||
(!Compare(curChild->name, "edit") || !Compare(curChild->name, "sheep")))
|
||||
{
|
||||
if (indentPrinted)
|
||||
{
|
||||
indentPrinted = false;
|
||||
os << "\n";
|
||||
}
|
||||
|
||||
os << ToString(curChild, tabs, true, printEditDepth);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Child is a text node, don't want to indent more than once.
|
||||
if (xmlIsBlankNode(curChild))
|
||||
continue;
|
||||
|
||||
if (!indentPrinted && formatting)
|
||||
{
|
||||
for (ti = 0; ti < tabs; ti++)
|
||||
os << tabString;
|
||||
|
||||
indentPrinted = true;
|
||||
}
|
||||
|
||||
//Print nodes without formatting.
|
||||
os << ToString(curChild, tabs, false, printEditDepth);
|
||||
}
|
||||
}
|
||||
|
||||
if (indentPrinted && formatting)
|
||||
os << "\n";
|
||||
|
||||
tabs--;//Tab out.
|
||||
|
||||
if (formatting)
|
||||
for (ti = 0; ti < tabs; ti++)
|
||||
os << tabString;
|
||||
|
||||
os << "</" << editNode->name << ">";//Close the tag.
|
||||
|
||||
if (formatting)
|
||||
os << "\n";
|
||||
}
|
||||
}
|
||||
else if (editNode->type == XML_TEXT_NODE)
|
||||
{
|
||||
string s((char*)xmlNodeGetContent(editNode));
|
||||
os << Trim(s);
|
||||
}
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
void AddFilenameWithoutAmpersand(xmlNodePtr node, string& filename)
|
||||
{
|
||||
if (filename.find_first_of('&') != std::string::npos)
|
||||
{
|
||||
string filenameWithoutAmpersands = filename;
|
||||
|
||||
FindAndReplace<string>(filenameWithoutAmpersands, "&", "&");
|
||||
xmlNewProp(node, XC "filename", XC filenameWithoutAmpersands.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
xmlNewProp(node, XC "filename", XC filename.c_str());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
1018
Source/Ember/Interpolate.h
Normal file
1018
Source/Ember/Interpolate.h
Normal file
File diff suppressed because it is too large
Load Diff
386
Source/Ember/Isaac.h
Normal file
386
Source/Ember/Isaac.h
Normal file
@ -0,0 +1,386 @@
|
||||
#pragma once
|
||||
|
||||
#include "EmberDefines.h"
|
||||
|
||||
/// <summary>
|
||||
/// C++ TEMPLATE VERSION OF Robert J. Jenkins Jr.'s
|
||||
/// ISAAC Random Number Generator.
|
||||
///
|
||||
/// Ported from vanilla C to to template C++ class
|
||||
/// by Quinn Tyler Jackson on 16-23 July 1998.
|
||||
///
|
||||
/// quinn@qtj.net
|
||||
///
|
||||
/// The function for the expected period of this
|
||||
/// random number generator, according to Jenkins is:
|
||||
///
|
||||
/// f(a,b) = 2**((a+b*(3+2^^a)-1)
|
||||
///
|
||||
/// (where a is ALPHA and b is bitwidth)
|
||||
///
|
||||
/// So, for a bitwidth of 32 and an ALPHA of 8,
|
||||
/// the expected period of ISAAC is:
|
||||
///
|
||||
/// 2^^(8+32*(3+2^^8)-1) = 2^^8295
|
||||
///
|
||||
/// Jackson has been able to run implementations
|
||||
/// with an ALPHA as high as 16, or
|
||||
///
|
||||
/// 2^^2097263
|
||||
///
|
||||
/// -Modified by Matt Feemster to eliminate needless dynamic memory allocation and virtual functions and bring inline with Ember coding style.
|
||||
/// </summary>
|
||||
|
||||
#ifndef __ISAAC64
|
||||
typedef unsigned long int ISAAC_INT;
|
||||
const ISAAC_INT GOLDEN_RATIO = ISAAC_INT(0x9e3779b9);
|
||||
#else
|
||||
typedef unsigned __int64 ISAAC_INT;
|
||||
const ISAAC_INT GOLDEN_RATIO = ISAAC_INT(0x9e3779b97f4a7c13);
|
||||
#endif
|
||||
|
||||
namespace EmberNs
|
||||
{
|
||||
/// <summary>
|
||||
/// QTIsaac class which allows using ISAAC in an OOP manner.
|
||||
/// </summary>
|
||||
template <int ALPHA = 4, class T = ISAAC_INT>
|
||||
class EMBER_API QTIsaac
|
||||
{
|
||||
public:
|
||||
typedef unsigned char byte;
|
||||
enum { N = (1 << ALPHA) };
|
||||
|
||||
/// <summary>
|
||||
/// Global ISAAC RNG to be used from anywhere. This is not thread safe, so take caution to only
|
||||
/// use it when no other threads are.
|
||||
/// </summary>
|
||||
static auto_ptr<QTIsaac<ALPHA, ISAAC_INT>> GlobalRand;
|
||||
|
||||
/// <summary>
|
||||
/// The structure which holds all of the random information.
|
||||
/// </summary>
|
||||
struct EMBER_API randctx
|
||||
{
|
||||
T randcnt;
|
||||
T randrsl[N];
|
||||
T randmem[N];
|
||||
T randa;
|
||||
T randb;
|
||||
T randc;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which initialized the random context using the values passed in.
|
||||
/// Leaving these as their defaults is fine, and will still give different
|
||||
/// results because time is internally used if they are default.
|
||||
/// However, specifying specific values is useful if you want to duplicate
|
||||
/// a sequence of random numbers.
|
||||
/// </summary>
|
||||
/// <param name="a">First random seed. Default: 0.</param>
|
||||
/// <param name="b">Second random seed. Default: 0.</param>
|
||||
/// <param name="c">Third random seed. Default: 0.</param>
|
||||
/// <param name="s">Pointer to a buffer of 256 random integer seeds. Default: NULL.</param>
|
||||
QTIsaac(T a = 0, T b = 0, T c = 0, T* s = NULL)
|
||||
{
|
||||
Srand(a, b, c, s);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the next random integer.
|
||||
/// </summary>
|
||||
/// <returns>The next random integer</returns>
|
||||
inline T Rand()
|
||||
{
|
||||
return (m_Rc.randcnt++ == N ? (Isaac(&m_Rc), m_Rc.randcnt=0, m_Rc.randrsl[m_Rc.randcnt]) : m_Rc.randrsl[m_Rc.randcnt]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the next random integer between 0 and the value passed in.
|
||||
/// </summary>
|
||||
/// <returns>A value one greater than the maximum value that will be returned</returns>
|
||||
inline T Rand(T upper)
|
||||
{
|
||||
return (upper == 0) ? Rand() : Rand() % upper;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a random floating point value between the specified minimum and maximum.
|
||||
/// Template argument expected to be float or double.
|
||||
/// </summary>
|
||||
/// <param name="fMin">The minimum value allowed, inclusive.</param>
|
||||
/// <param name="fMax">The maximum value allowed, inclusive.</param>
|
||||
/// <returns>A new random floating point value within the specified range, inclusive.</returns>
|
||||
template<typename floatType>
|
||||
inline floatType Frand(floatType fMin, floatType fMax)
|
||||
{
|
||||
floatType f = (floatType)Rand() / (floatType)std::numeric_limits<T>::max();
|
||||
return fMin + (f * (fMax - fMin));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Thin wrapper around a call to Frand() with a range of 0-1.
|
||||
/// Template argument expected to be float or double.
|
||||
/// </summary>
|
||||
/// <returns>A new random number in the range of 0-1, inclusive.</returns>
|
||||
template<typename floatType>
|
||||
inline floatType Frand01()
|
||||
{
|
||||
return Frand<floatType>(floatType(0), floatType(1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Thin wrapper around a call to Frand() with a range of -1-1.
|
||||
/// Template argument expected to be float or double.
|
||||
/// </summary>
|
||||
/// <returns>A new random number in the range of -1-1, inclusive.</returns>
|
||||
template<typename floatType>
|
||||
inline floatType Frand11()
|
||||
{
|
||||
return Frand<floatType>(floatType(-1), floatType(1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Not sure what this does.
|
||||
/// </summary>
|
||||
/// <returns>Something that is golden</returns>
|
||||
template<typename floatType>
|
||||
inline floatType GoldenBit()
|
||||
{
|
||||
return RandBit() ? floatType(0.38196) : floatType(0.61804);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a random 0 or 1.
|
||||
/// </summary>
|
||||
/// <returns>A random 0 or 1</returns>
|
||||
inline unsigned int RandBit()
|
||||
{
|
||||
return Rand() & 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A different way of getting a floating point rand in the range -1-1.
|
||||
/// Flam3 used this but it seems unnecessary now, keep around if it's ever needed.
|
||||
/// </summary>
|
||||
/// <returns>A new random number in the range of -1-1, inclusive.</returns>
|
||||
//double drand11()
|
||||
//{
|
||||
// return (((int)Rand() & 0xfffffff) - 0x7ffffff) / (double) 0x7ffffff;
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a random context.
|
||||
/// Unsure exacly how this works, but it does.
|
||||
/// </summary>
|
||||
/// <param name="ctx">The random context to initialize</param>
|
||||
/// <param name="useSeed">Whether to use the seeds passed in to the constructor, else zero.</param>
|
||||
void RandInit(randctx* ctx, bool useSeed)
|
||||
{
|
||||
int i;
|
||||
T a, b, c, d, e, f, g, h;
|
||||
T* m = ctx->randmem;
|
||||
T* r = ctx->randrsl;
|
||||
|
||||
a = b = c = d = e = f = g = h = GOLDEN_RATIO;
|
||||
|
||||
if (!useSeed)
|
||||
{
|
||||
ctx->randa = 0;
|
||||
ctx->randb = 0;
|
||||
ctx->randc = 0;
|
||||
}
|
||||
|
||||
//Scramble it.
|
||||
for (i = 0; i < 4; ++i)
|
||||
{
|
||||
Shuffle(a, b, c, d, e, f, g, h);
|
||||
}
|
||||
|
||||
if (useSeed)
|
||||
{
|
||||
//Initialize using the contents of r[] as the seed.
|
||||
for (i = 0; i < N; i += 8)
|
||||
{
|
||||
a += r[i ]; b += r[i + 1]; c += r[i + 2]; d += r[i + 3];
|
||||
e += r[i + 4]; f += r[i + 5]; g += r[i + 6]; h += r[i + 7];
|
||||
|
||||
Shuffle(a, b, c, d, e, f, g, h);
|
||||
|
||||
m[i ] = a; m[i + 1] = b; m[i + 2] = c; m[i + 3] = d;
|
||||
m[i + 4] = e; m[i + 5] = f; m[i + 6] = g; m[i + 7] = h;
|
||||
}
|
||||
|
||||
//Do a second pass to make all of the seed affect all of m.
|
||||
for (i = 0; i < N; i += 8)
|
||||
{
|
||||
a += m[i ]; b += m[i + 1]; c += m[i + 2]; d += m[i + 3];
|
||||
e += m[i + 4]; f += m[i + 5]; g += m[i + 6]; h += m[i + 7];
|
||||
|
||||
Shuffle(a, b, c, d, e, f, g, h);
|
||||
|
||||
m[i ] = a; m[i + 1] = b; m[i + 2] = c; m[i + 3] = d;
|
||||
m[i + 4] = e; m[i + 5] = f; m[i + 6] = g; m[i + 7] = h;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Fill in mm[] with messy stuff.
|
||||
Shuffle(a, b, c, d, e, f, g, h);
|
||||
|
||||
m[i ] = a; m[i + 1] = b; m[i + 2] = c; m[i + 3] = d;
|
||||
m[i + 4] = e; m[i + 5] = f; m[i + 6] = g; m[i + 7] = h;
|
||||
}
|
||||
|
||||
Isaac(ctx); //Fill in the first set of results.
|
||||
ctx->randcnt = 0;//Prepare to use the first set of results.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the seeds of the member random context using the specified seeds.
|
||||
/// If s is null, time plus index up to 256 is used for the random buffer.
|
||||
/// </summary>
|
||||
/// <param name="a">First random seed. Default: 0.</param>
|
||||
/// <param name="b">Second random seed. Default: 0.</param>
|
||||
/// <param name="c">Third random seed. Default: 0.</param>
|
||||
/// <param name="s">Pointer to a buffer of 256 random integer seeds. Default: NULL.</param>
|
||||
void Srand(T a = 0, T b = 0, T c = 0, T* s = NULL)
|
||||
{
|
||||
if (s == NULL)//Default to using time plus index as the seed if s was NULL.
|
||||
{
|
||||
for (int i = 0; i < N; i++)
|
||||
m_Rc.randrsl[i] = (T)time(0) + i;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < N; i++)
|
||||
m_Rc.randrsl[i] = s[i];
|
||||
}
|
||||
|
||||
if (a == 0 && b == 0 && c == 0)
|
||||
{
|
||||
m_Rc.randa = (T)time(0);
|
||||
m_Rc.randb = (T)time(0) * (T)time(0);
|
||||
m_Rc.randc = (T)time(0) * (T)time(0) * (T)time(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Rc.randa = a;
|
||||
m_Rc.randb = b;
|
||||
m_Rc.randc = c;
|
||||
}
|
||||
|
||||
RandInit(&m_Rc, true);
|
||||
}
|
||||
|
||||
protected:
|
||||
/// <summary>
|
||||
/// Compute the next batch of random numbers for a random context.
|
||||
/// </summary>
|
||||
/// <param name="ctx">The context to populate.</param>
|
||||
void Isaac(randctx* ctx)
|
||||
{
|
||||
T x,y;
|
||||
|
||||
T* mm = ctx->randmem;
|
||||
T* r = ctx->randrsl;
|
||||
|
||||
T a = (ctx->randa);
|
||||
T b = (ctx->randb + (++ctx->randc));
|
||||
|
||||
T* m = mm;
|
||||
T* m2 = (m + (N / 2));
|
||||
T* mend = m2;
|
||||
|
||||
for(; m < mend; )
|
||||
{
|
||||
#ifndef __ISAAC64
|
||||
RngStep((a << 13), a, b, mm, m, m2, r, x, y);
|
||||
RngStep((a >> 6) , a, b, mm, m, m2, r, x, y);
|
||||
RngStep((a << 2) , a, b, mm, m, m2, r, x, y);
|
||||
RngStep((a >> 16), a, b, mm, m, m2, r, x, y);
|
||||
#else // __ISAAC64
|
||||
RngStep(~(a ^ (a << 21)), a, b, mm, m, m2, r, x, y);
|
||||
RngStep( a ^ (a >> 5) , a, b, mm, m, m2, r, x, y);
|
||||
RngStep( a ^ (a << 12) , a, b, mm, m, m2, r, x, y);
|
||||
RngStep( a ^ (a >> 33) , a, b, mm, m, m2, r, x, y);
|
||||
#endif // __ISAAC64
|
||||
}
|
||||
|
||||
m2 = mm;
|
||||
|
||||
for(; m2<mend;)
|
||||
{
|
||||
#ifndef __ISAAC64
|
||||
RngStep((a << 13), a, b, mm, m, m2, r, x, y);
|
||||
RngStep((a >> 6) , a, b, mm, m, m2, r, x, y);
|
||||
RngStep((a << 2) , a, b, mm, m, m2, r, x, y);
|
||||
RngStep((a >> 16), a, b, mm, m, m2, r, x, y);
|
||||
#else // __ISAAC64
|
||||
RngStep(~(a ^ (a << 21)), a, b, mm, m, m2, r, x, y);
|
||||
RngStep( a ^ (a >> 5) , a, b, mm, m, m2, r, x, y);
|
||||
RngStep( a ^ (a << 12) , a, b, mm, m, m2, r, x, y);
|
||||
RngStep( a ^ (a >> 33) , a, b, mm, m, m2, r, x, y);
|
||||
#endif // __ISAAC64
|
||||
}
|
||||
|
||||
ctx->randb = b;
|
||||
ctx->randa = a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a value using indirection.
|
||||
/// </summary>
|
||||
/// <param name="mm">The buffer.</param>
|
||||
/// <param name="x">The offset.</param>
|
||||
/// <returns>A new value</returns>
|
||||
inline T Ind(T* mm, T x)
|
||||
{
|
||||
#ifndef __ISAAC64
|
||||
return (*(T*)((byte*)(mm) + ((x) & ((N - 1) << 2))));
|
||||
#else // __ISAAC64
|
||||
return (*(T*)((byte*)(mm) + ((x) & ((N - 1) << 3))));
|
||||
#endif // __ISAAC64
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsure what this does.
|
||||
/// </summary>
|
||||
void RngStep(T mix, T& a, T& b, T*& mm, T*& m, T*& m2, T*& r, T& x, T& y)
|
||||
{
|
||||
x = *m;
|
||||
a = (a ^ (mix)) + *(m2++);
|
||||
*(m++) = y = Ind(mm, x) + a + b;
|
||||
*(r++) = b = Ind(mm, y >> ALPHA) + x;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsure what this does.
|
||||
/// </summary>
|
||||
void Shuffle(T& a, T& b, T& c, T& d, T& e, T& f, T& g, T& h)
|
||||
{
|
||||
#ifndef __ISAAC64
|
||||
a ^= b << 11; d += a; b += c;
|
||||
b ^= c >> 2; e += b; c += d;
|
||||
c ^= d << 8; f += c; d += e;
|
||||
d ^= e >> 16; g += d; e += f;
|
||||
e ^= f << 10; h += e; f += g;
|
||||
f ^= g >> 4; a += f; g += h;
|
||||
g ^= h << 8; b += g; h += a;
|
||||
h ^= a >> 9; c += h; a += b;
|
||||
#else // __ISAAC64
|
||||
a -= e; f ^= h >> 9; h += a;
|
||||
b -= f; g ^= a << 9; a += b;
|
||||
c -= g; h ^= b >> 23; b += c;
|
||||
d -= h; a ^= c << 15; c += d;
|
||||
e -= a; b ^= d >> 14; d += e;
|
||||
f -= b; c ^= e << 20; e += f;
|
||||
g -= c; d ^= f >> 17; f += g;
|
||||
h -= d; e ^= g << 14; g += h;
|
||||
#endif // __ISAAC64
|
||||
}
|
||||
|
||||
private:
|
||||
randctx m_Rc;//The random context which holds all of the seed and state information as well as the random number values.
|
||||
};
|
||||
}
|
541
Source/Ember/Iterator.h
Normal file
541
Source/Ember/Iterator.h
Normal file
@ -0,0 +1,541 @@
|
||||
#pragma once
|
||||
|
||||
#include "Ember.h"
|
||||
|
||||
/// <summary>
|
||||
/// Iterator and derived classes.
|
||||
/// </summary>
|
||||
|
||||
//#define CHOOSE_XFORM_GRAIN 256
|
||||
#define CHOOSE_XFORM_GRAIN 10000//The size of xform random selection buffer. Multiply by the (number of non-final xforms present + 1) if xaos is used.
|
||||
|
||||
namespace EmberNs
|
||||
{
|
||||
/// <summary>
|
||||
/// Iterator base class.
|
||||
/// Iterating is one loop level outside of the inner xform application loop so it's still very important
|
||||
/// to take every optimization possible here.
|
||||
/// The original had many temporary assignments in order to feed the output of the current iteration
|
||||
/// into the input of the next iteration. All unneccessary temporary assignments are eliminated by simply using i and i + 1
|
||||
/// as the input and output indices on the samples array passed to Xform.Apply().
|
||||
/// Note that the samples array is assigned to while fusing. Although this technically doesn't make sense
|
||||
/// since values computed during fusing get thrown out, it doesn't matter because it will get overwritten
|
||||
/// in the actual loop below it since the index counter is reset to zero when fusing is complete.
|
||||
/// Flam3 needlessly computed the final xform on each fuse iteration only to throw it away. It's omitted here as an optimization.
|
||||
/// Rather than place many conditionals inside the iteration loop, they are broken into separate classes depending
|
||||
/// on what's contained in the ember's xforms.
|
||||
/// The biggest difference is whether xaos is present or not it requires extra work when picking
|
||||
/// the next random xform to use. Further, each of those is broken into two loops, one for embers with a final xform
|
||||
/// and one without.
|
||||
/// Last, the fuse loop and real loop are separated and duplicated to omit the conditional check for fuse inside the real loop.
|
||||
/// Although this makes this file about four times as verbose as it would normally be, it does lead to performance improvements.
|
||||
/// Template argument expected to be float or double.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API Iterator
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Empty constructor.
|
||||
/// </summary>
|
||||
Iterator()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Empty virtual destructor so proper derived class destructors get called.
|
||||
/// </summary>
|
||||
virtual ~Iterator()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accessors.
|
||||
/// </summary>
|
||||
const unsigned char* XformDistributions() const { return m_XformDistributions.empty() ? NULL : &m_XformDistributions[0]; }
|
||||
const unsigned int XformDistributionsSize() const { return (unsigned int)m_XformDistributions.size(); }
|
||||
|
||||
/// <summary>
|
||||
/// Virtual empty iteration function that will be overidden in derived iterator classes.
|
||||
/// </summary>
|
||||
/// <param name="ember">The ember whose xforms will be applied</param>
|
||||
/// <param name="count">The number of iterations to do</param>
|
||||
/// <param name="skip">The number of times to fuse</param>
|
||||
/// <param name="samples">The buffer to store the output points</param>
|
||||
/// <param name="rand">The random context to use</param>
|
||||
/// <returns>The number of bad values</returns>
|
||||
virtual unsigned int Iterate(Ember<T>& ember, unsigned int count, unsigned int skip, Point<T>* samples, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) { return 0; }
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the xform selection vector by normalizing the weights of all xforms and
|
||||
/// setting the corresponding percentage of elements in the vector to each xform's index in its
|
||||
/// parent ember.
|
||||
/// Note that this method of looking up and index in a vector is how flam3 did it and is about 10%
|
||||
/// faster than using a while loop to check a random number against a normalized weight.
|
||||
/// Also, the ember used to initialize this must be the same ember, unchanged, used to iterate.
|
||||
/// If one is passed to this function, its parameters are changed and then it's passed to Iterate(),
|
||||
/// the behavior is undefined.
|
||||
/// </summary>
|
||||
/// <param name="ember">The ember whose xforms will be used to populate the distribution vector</param>
|
||||
/// <returns>True if success, else false.</returns>
|
||||
bool InitDistributions(Ember<T>& ember)
|
||||
{
|
||||
unsigned int i, j = 0;
|
||||
unsigned int distribCount = ember.XaosPresent() ? (unsigned int)ember.XformCount() + 1 : 1;
|
||||
const Xform<T>* xforms = ember.Xforms();
|
||||
|
||||
if (m_XformDistributions.size() < CHOOSE_XFORM_GRAIN * distribCount)
|
||||
m_XformDistributions.resize(CHOOSE_XFORM_GRAIN * distribCount);
|
||||
|
||||
if (m_XformDistributions.size() < CHOOSE_XFORM_GRAIN * distribCount)
|
||||
return false;
|
||||
|
||||
for (unsigned int distrib = 0; distrib < distribCount; distrib++)
|
||||
{
|
||||
T totalDensity = 0;
|
||||
|
||||
//First find the total densities of all xforms.
|
||||
for (i = 0; i < ember.XformCount(); i++)
|
||||
{
|
||||
T d = xforms[i].m_Weight;
|
||||
|
||||
if (distrib > 0)
|
||||
d *= xforms[distrib - 1].Xaos(i);
|
||||
|
||||
//Original returned false if any xform had 0 density, it's allowed here
|
||||
//because it can be useful when experimenting to test the effects of removing an
|
||||
//xform by setting its probability to 0.
|
||||
|
||||
totalDensity += d;
|
||||
}
|
||||
|
||||
//Original returned false if all were 0, but it's allowed here
|
||||
//which will just end up setting all elements to 0 which means
|
||||
//only the first xform will get used.
|
||||
|
||||
//Calculate how much of a fraction of a the total density each element represents.
|
||||
T densityPerElement = totalDensity / CHOOSE_XFORM_GRAIN;
|
||||
j = 0;
|
||||
|
||||
//Assign xform indices in order to each element of m_XformDistributions.
|
||||
//The number of elements assigned a given index is proportional to that xform's
|
||||
//density relative to the sum of all densities.
|
||||
for (i = 0; i < ember.XformCount(); i++)
|
||||
{
|
||||
T tempDensity = 0;
|
||||
T currentDensityLimit = xforms[i].m_Weight;
|
||||
|
||||
if (distrib > 0)
|
||||
currentDensityLimit *= xforms[distrib - 1].Xaos(i);
|
||||
|
||||
//Populate points corresponding to this xform's weight/density.
|
||||
//Also check that j is within the bounds of the distribution array just to be safe in the case of a rounding error.
|
||||
while (tempDensity <= currentDensityLimit && j < CHOOSE_XFORM_GRAIN)
|
||||
{
|
||||
m_XformDistributions[(distrib * CHOOSE_XFORM_GRAIN) + j] = i;
|
||||
tempDensity += densityPerElement;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
/// <summary>
|
||||
/// When iterating, if the computed location of the point is either very close to zero, or very close to infinity,
|
||||
/// it's considered a bad value. In that case, a new random input point is fed into a new randomly chosen xform. This
|
||||
/// process is repeated up to 5 times until a good value is computed. If after 5 tries, a good value is not found, then
|
||||
/// the coordinates of the output point are just set to a random number between -1 and 1.
|
||||
/// </summary>
|
||||
/// <param name="xforms">The xforms array</param>
|
||||
/// <param name="badVals">The counter for the total number of bad values this sub batch</param>
|
||||
/// <param name="point">The point which initially had the bad values and which will store the newly computed values</param>
|
||||
/// <param name="rand">The random context this iterator is using</param>
|
||||
/// <returns>True if a good value was computed within 5 tries, else false</returns>
|
||||
inline bool DoBadVals(Xform<T>* xforms, unsigned int& badVals, Point<T>* point, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand)
|
||||
{
|
||||
unsigned int xformIndex, consec = 0;
|
||||
Point<T> firstBadPoint;
|
||||
|
||||
while (consec < 5)
|
||||
{
|
||||
consec++;
|
||||
badVals++;
|
||||
firstBadPoint.m_X = rand.Frand11<T>();//Re-randomize points, but keep the computed color and viz.
|
||||
firstBadPoint.m_Y = rand.Frand11<T>();
|
||||
firstBadPoint.m_Z = 0;
|
||||
firstBadPoint.m_ColorX = point->m_ColorX;
|
||||
firstBadPoint.m_VizAdjusted = point->m_VizAdjusted;
|
||||
|
||||
xformIndex = NextXformFromIndex(rand.Rand());
|
||||
|
||||
if (!xforms[xformIndex].Apply(&firstBadPoint, point, rand))
|
||||
return true;
|
||||
}
|
||||
|
||||
//After 5 tries, nothing worked, so just assign random values between -1 and 1.
|
||||
if (consec == 5)
|
||||
{
|
||||
point->m_X = rand.Frand11<T>();
|
||||
point->m_Y = rand.Frand11<T>();
|
||||
point->m_Z = 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply the final xform.
|
||||
/// Note that as stated in the paper, the output of the final xform is not fed back into the next iteration.
|
||||
/// Rather, only the value computed from the randomly chosen xform is. However, the output of the final xform
|
||||
/// is still saved in the output samples buffer and accumulated to the histogram later.
|
||||
/// </summary>
|
||||
/// <param name="ember">The ember being iterated</param>
|
||||
/// <param name="tempPoint">The input point</param>
|
||||
/// <param name="sample">The output point</param>
|
||||
/// <param name="rand">The random context to use.</param>
|
||||
inline void DoFinalXform(Ember<T>& ember, Point<T>& tempPoint, Point<T>* sample, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand)
|
||||
{
|
||||
if (IsClose<T>(ember.FinalXform()->m_Opacity, 1) || rand.Frand01<T>() < ember.FinalXform()->m_Opacity)
|
||||
{
|
||||
T tempVizAdjusted = tempPoint.m_VizAdjusted;
|
||||
|
||||
ember.NonConstFinalXform()->Apply(&tempPoint, sample, rand);
|
||||
sample->m_VizAdjusted = tempVizAdjusted;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve an element in the distributions vector between 0 and CHOOSE_XFORM_GRAIN which will
|
||||
/// contain the index of the next xform to use. When xaos is prsent, the offset is the index in
|
||||
/// the ember of the previous xform used when.
|
||||
/// </summary>
|
||||
/// <param name="index">The index to retrieve</param>
|
||||
/// <param name="distribOffset">When xaos is prsent, the index of the previous xform used. Default: 0 (xaos not present).</param>
|
||||
/// <returns></returns>
|
||||
unsigned int NextXformFromIndex(unsigned int index, unsigned int distribOffset = 0)
|
||||
{
|
||||
return (unsigned int)m_XformDistributions[(index % CHOOSE_XFORM_GRAIN) + (CHOOSE_XFORM_GRAIN * distribOffset)];
|
||||
}
|
||||
|
||||
vector<unsigned char> m_XformDistributions;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Derived iterator class for embers whose xforms do not use xaos.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API StandardIterator : public Iterator<T>
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Empty constructor.
|
||||
/// </summary>
|
||||
StandardIterator()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridden virtual function which iterates an ember a given number of times and does not use xaos.
|
||||
/// </summary>
|
||||
/// <param name="ember">The ember whose xforms will be applied</param>
|
||||
/// <param name="count">The number of iterations to do</param>
|
||||
/// <param name="skip">The number of times to fuse</param>
|
||||
/// <param name="samples">The buffer to store the output points</param>
|
||||
/// <param name="rand">The random context to use</param>
|
||||
/// <returns>The number of bad values</returns>
|
||||
virtual unsigned int Iterate(Ember<T>& ember, unsigned int count, unsigned int skip, Point<T>* samples, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand)
|
||||
{
|
||||
unsigned int i, badVals = 0;
|
||||
Point<T> tempPoint, p1;
|
||||
Xform<T>* xforms = ember.NonConstXforms();
|
||||
|
||||
if (ember.ProjBits())
|
||||
{
|
||||
if (ember.UseFinalXform())
|
||||
{
|
||||
p1 = samples[0];
|
||||
|
||||
for (i = 0; i < skip; i++)//Fuse.
|
||||
{
|
||||
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p1, rand))
|
||||
DoBadVals(xforms, badVals, &p1, rand);
|
||||
}
|
||||
|
||||
DoFinalXform(ember, p1, samples, rand);//Apply to last fuse point and store as the first element in samples.
|
||||
ember.Proj(samples[0], rand);
|
||||
|
||||
for (i = 1; i < count; i++)//Real loop.
|
||||
{
|
||||
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p1, rand))
|
||||
DoBadVals(xforms, badVals, &p1, rand);
|
||||
|
||||
DoFinalXform(ember, p1, samples + i, rand);
|
||||
ember.Proj(samples[i], rand);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
p1 = samples[0];
|
||||
|
||||
for (i = 0; i < skip; i++)//Fuse.
|
||||
{
|
||||
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p1, rand))
|
||||
DoBadVals(xforms, badVals, &p1, rand);
|
||||
}
|
||||
|
||||
samples[0] = p1;
|
||||
ember.Proj(samples[0], rand);
|
||||
|
||||
for (i = 1; i < count; i++)//Real loop.
|
||||
{
|
||||
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &samples[i], rand))
|
||||
DoBadVals(xforms, badVals, samples + i, rand);
|
||||
|
||||
p1 = samples[i];
|
||||
ember.Proj(samples[i], rand);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ember.UseFinalXform())
|
||||
{
|
||||
p1 = samples[0];
|
||||
|
||||
for (i = 0; i < skip; i++)//Fuse.
|
||||
{
|
||||
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p1, rand))
|
||||
DoBadVals(xforms, badVals, &p1, rand);
|
||||
}
|
||||
|
||||
DoFinalXform(ember, p1, samples, rand);//Apply to last fuse point and store as the first element in samples.
|
||||
|
||||
for (i = 1; i < count; i++)//Real loop.
|
||||
{
|
||||
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p1, rand))//Feed the resulting value of applying the randomly selected xform back into the next iter, and not the result of applying the final xform.
|
||||
DoBadVals(xforms, badVals, &p1, rand);
|
||||
|
||||
DoFinalXform(ember, p1, samples + i, rand);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
p1 = samples[0];
|
||||
|
||||
for (i = 0; i < skip; i++)//Fuse.
|
||||
{
|
||||
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p1, rand))
|
||||
DoBadVals(xforms, badVals, &p1, rand);
|
||||
}
|
||||
|
||||
samples[0] = p1;
|
||||
|
||||
for (i = 0; i < count - 1; i++)//Real loop.
|
||||
if (xforms[NextXformFromIndex(rand.Rand())].Apply(samples + i, samples + i + 1, rand))
|
||||
DoBadVals(xforms, badVals, samples + i + 1, rand);
|
||||
}
|
||||
}
|
||||
|
||||
return badVals;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Derived iterator class for embers whose xforms use xaos.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API XaosIterator : public Iterator<T>
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Empty constructor.
|
||||
/// </summary>
|
||||
XaosIterator()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for bad values similar to the one in the base class, except it takes the last xform used
|
||||
/// as a parameter and saves the xform used back out because this iterator is meant to be used with xaos.
|
||||
/// </summary>
|
||||
/// <param name="xforms">The xforms array</param>
|
||||
/// <param name="xformIndex">Index of the last used xform before calling this function</param>
|
||||
/// <param name="lastXformUsed">The saved index of the last xform used within this function</param>
|
||||
/// <param name="badVals">The counter for the total number of bad values this sub batch</param>
|
||||
/// <param name="point">The point which initially had the bad values and which will store the newly computed values</param>
|
||||
/// <param name="rand">The random context this iterator is using</param>
|
||||
/// <returns>True if a good value was computed within 5 tries, else false</returns>
|
||||
inline bool DoBadVals(Xform<T>* xforms, unsigned int& xformIndex, unsigned int lastXformUsed, unsigned int& badVals, Point<T>* point, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand)
|
||||
{
|
||||
unsigned int consec = 0;
|
||||
Point<T> firstBadPoint;
|
||||
|
||||
while (consec < 5)
|
||||
{
|
||||
consec++;
|
||||
badVals++;
|
||||
firstBadPoint.m_X = rand.Frand11<T>();//Re-randomize points, but keep the computed color and viz.
|
||||
firstBadPoint.m_Y = rand.Frand11<T>();
|
||||
firstBadPoint.m_Z = 0;
|
||||
firstBadPoint.m_ColorX = point->m_ColorX;
|
||||
firstBadPoint.m_VizAdjusted = point->m_VizAdjusted;
|
||||
|
||||
xformIndex = NextXformFromIndex(rand.Rand(), lastXformUsed);
|
||||
|
||||
if (!xforms[xformIndex].Apply(&firstBadPoint, point, rand))
|
||||
return true;
|
||||
}
|
||||
|
||||
//After 5 tries, nothing worked, so just assign random.
|
||||
if (consec == 5)
|
||||
{
|
||||
point->m_X = rand.Frand11<T>();
|
||||
point->m_Y = rand.Frand11<T>();
|
||||
point->m_Z = 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overridden virtual function which iterates an ember a given number of times and uses xaos.
|
||||
/// </summary>
|
||||
/// <param name="ember">The ember whose xforms will be applied</param>
|
||||
/// <param name="count">The number of iterations to do</param>
|
||||
/// <param name="skip">The number of times to fuse</param>
|
||||
/// <param name="samples">The buffer to store the output points</param>
|
||||
/// <param name="rand">The random context to use</param>
|
||||
/// <returns>The number of bad values</returns>
|
||||
virtual unsigned int Iterate(Ember<T>& ember, unsigned int count, unsigned int skip, Point<T>* samples, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand)
|
||||
{
|
||||
unsigned int i, xformIndex;
|
||||
unsigned int lastXformUsed = 0;
|
||||
unsigned int badVals = 0;
|
||||
Point<T> tempPoint, p1;
|
||||
Xform<T>* xforms = ember.NonConstXforms();
|
||||
|
||||
if (ember.ProjBits())
|
||||
{
|
||||
if (ember.UseFinalXform())
|
||||
{
|
||||
p1 = samples[0];
|
||||
|
||||
for (i = 0; i < skip; i++)//Fuse.
|
||||
{
|
||||
xformIndex = NextXformFromIndex(rand.Rand(), lastXformUsed);
|
||||
|
||||
if (xforms[xformIndex].Apply(&p1, &p1, rand))
|
||||
DoBadVals(xforms, xformIndex, lastXformUsed, badVals, &p1, rand);
|
||||
|
||||
lastXformUsed = xformIndex + 1;//Store the last used transform.
|
||||
}
|
||||
|
||||
DoFinalXform(ember, p1, samples, rand);//Apply to last fuse point and store as the first element in samples.
|
||||
ember.Proj(samples[0], rand);
|
||||
|
||||
for (i = 1; i < count; i++)//Real loop.
|
||||
{
|
||||
xformIndex = NextXformFromIndex(rand.Rand(), lastXformUsed);
|
||||
|
||||
if (xforms[xformIndex].Apply(&p1, &p1, rand))//Feed the resulting value of applying the randomly selected xform back into the next iter, and not the result of applying the final xform.
|
||||
DoBadVals(xforms, xformIndex, lastXformUsed, badVals, &p1, rand);
|
||||
|
||||
DoFinalXform(ember, p1, samples + i, rand);
|
||||
ember.Proj(samples[i], rand);
|
||||
lastXformUsed = xformIndex + 1;//Store the last used transform.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
p1 = samples[0];
|
||||
|
||||
for (i = 0; i < skip; i++)//Fuse.
|
||||
{
|
||||
xformIndex = NextXformFromIndex(rand.Rand(), lastXformUsed);
|
||||
|
||||
if (xforms[xformIndex].Apply(&p1, &p1, rand))
|
||||
DoBadVals(xforms, xformIndex, lastXformUsed, badVals, &p1, rand);
|
||||
|
||||
lastXformUsed = xformIndex + 1;//Store the last used transform.
|
||||
}
|
||||
|
||||
ember.Proj(p1, rand);
|
||||
samples[0] = p1;
|
||||
|
||||
for (i = 1; i < count; i++)//Real loop.
|
||||
{
|
||||
xformIndex = NextXformFromIndex(rand.Rand(), lastXformUsed);
|
||||
|
||||
if (xforms[xformIndex].Apply(&p1, &p1, rand))
|
||||
DoBadVals(xforms, xformIndex, lastXformUsed, badVals, &p1, rand);
|
||||
|
||||
samples[i] = p1;
|
||||
ember.Proj(samples[i], rand);
|
||||
lastXformUsed = xformIndex + 1;//Store the last used transform.
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ember.UseFinalXform())
|
||||
{
|
||||
p1 = samples[0];
|
||||
|
||||
for (i = 0; i < skip; i++)//Fuse.
|
||||
{
|
||||
xformIndex = NextXformFromIndex(rand.Rand(), lastXformUsed);
|
||||
|
||||
if (xforms[xformIndex].Apply(&p1, &p1, rand))
|
||||
DoBadVals(xforms, xformIndex, lastXformUsed, badVals, &p1, rand);
|
||||
|
||||
lastXformUsed = xformIndex + 1;//Store the last used transform.
|
||||
}
|
||||
|
||||
DoFinalXform(ember, p1, samples, rand);//Apply to last fuse point and store as the first element in samples.
|
||||
|
||||
for (i = 1; i < count; i++)//Real loop.
|
||||
{
|
||||
xformIndex = NextXformFromIndex(rand.Rand(), lastXformUsed);
|
||||
|
||||
if (xforms[xformIndex].Apply(&p1, &p1, rand))//Feed the resulting value of applying the randomly selected xform back into the next iter, and not the result of applying the final xform.
|
||||
DoBadVals(xforms, xformIndex, lastXformUsed, badVals, &p1, rand);
|
||||
|
||||
DoFinalXform(ember, p1, samples + i, rand);
|
||||
lastXformUsed = xformIndex + 1;//Store the last used transform.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
p1 = samples[0];
|
||||
|
||||
for (i = 0; i < skip; i++)//Fuse.
|
||||
{
|
||||
xformIndex = NextXformFromIndex(rand.Rand(), lastXformUsed);
|
||||
|
||||
if (xforms[xformIndex].Apply(&p1, &p1, rand))
|
||||
DoBadVals(xforms, xformIndex, lastXformUsed, badVals, &p1, rand);
|
||||
|
||||
lastXformUsed = xformIndex + 1;//Store the last used transform.
|
||||
}
|
||||
|
||||
samples[0] = p1;
|
||||
|
||||
for (i = 0; i < count - 1; i++)//Real loop.
|
||||
{
|
||||
xformIndex = NextXformFromIndex(rand.Rand(), lastXformUsed);
|
||||
|
||||
if (xforms[xformIndex].Apply(samples + i, samples + i + 1, rand))
|
||||
DoBadVals(xforms, xformIndex, lastXformUsed, badVals, samples + i + 1, rand);
|
||||
|
||||
lastXformUsed = xformIndex + 1;//Store the last used transform.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return badVals;
|
||||
}
|
||||
};
|
||||
}
|
571
Source/Ember/Palette.h
Normal file
571
Source/Ember/Palette.h
Normal file
@ -0,0 +1,571 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utils.h"
|
||||
#include "Isaac.h"
|
||||
|
||||
/// <summary>
|
||||
/// Palette class.
|
||||
/// </summary>
|
||||
|
||||
namespace EmberNs
|
||||
{
|
||||
/// <summary>
|
||||
/// The palette stores a set of 256 colors which are what get accumulated to the histogram
|
||||
/// for each iteration. The colors come from either the main palette Xml file or directly
|
||||
/// from the ember parameter file. Either way, they come in as 0-255 and get normalized to 0-1.
|
||||
/// In the future, 2D palette support might be added in which case this class will have to be modified.
|
||||
/// Template argument expected to be float or double.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API Palette
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor which sets the palette index to random and allocates space to hold the color entries.
|
||||
/// </summary>
|
||||
Palette()
|
||||
{
|
||||
m_Name = "-";
|
||||
m_Index = -1;
|
||||
m_Entries.resize(COLORMAP_LENGTH);
|
||||
Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor that takes a name various parameters. If no color buffer is specified, a default is used.
|
||||
/// This is a safety fallback, and it's highly recommended to always supply a buffer of color entries.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the palette</param>
|
||||
/// <param name="index">The index in the palette file</param>
|
||||
/// <param name="size">The size of the palette which should be 256</param>
|
||||
/// <param name="xmlPaletteEntries">A pointer to 256 color entries</param>
|
||||
Palette(string name, int index, unsigned int size, v4T* xmlPaletteEntries)
|
||||
{
|
||||
m_Name = name;
|
||||
m_Index = index;
|
||||
m_Entries.resize(size);
|
||||
|
||||
if (xmlPaletteEntries)
|
||||
{
|
||||
memcpy(&m_Entries[0], xmlPaletteEntries, Size() * sizeof(m_Entries[0]));
|
||||
}
|
||||
else//They passed in null, so just fill with hard coded values so they at least have something.
|
||||
{
|
||||
//Palette 15 used in the test ember file.
|
||||
unsigned char palette15[COLORMAP_LENGTH * 4] = {
|
||||
0x00, 0xda, 0xde, 0xbc, 0x00, 0xee, 0xe6, 0xc5, 0x00, 0xee, 0xf2, 0xce, 0x00, 0xee, 0xf2, 0xcf, 0x00, 0xe6, 0xee, 0xe1, 0x00, 0xea, 0xee, 0xd8, 0x00, 0xf2, 0xf1, 0xeb, 0x00, 0xf2, 0xf5, 0xd8,
|
||||
0x00, 0xe6, 0xf2, 0xce, 0x00, 0xde, 0xea, 0xc5, 0x00, 0xd6, 0xda, 0xc6, 0x00, 0xce, 0xd2, 0xbc, 0x00, 0xc2, 0xca, 0xa9, 0x00, 0xbe, 0xca, 0xa0, 0x00, 0xce, 0xd6, 0xaa, 0x00, 0xde, 0xe2, 0xc5,
|
||||
0x00, 0xea, 0xed, 0xce, 0x00, 0xea, 0xf2, 0xc5, 0x00, 0xde, 0xe2, 0xc5, 0x00, 0xc2, 0xca, 0xaa, 0x00, 0xae, 0xbe, 0xaa, 0x00, 0xa5, 0xb2, 0x96, 0x00, 0xa2, 0xa9, 0x8d, 0x00, 0x96, 0xa2, 0x84,
|
||||
0x00, 0x8d, 0x8d, 0x7a, 0x00, 0x85, 0x89, 0x71, 0x00, 0x85, 0x8d, 0x71, 0x00, 0x85, 0x85, 0x67, 0x00, 0x79, 0x7d, 0x67, 0x00, 0x79, 0x7d, 0x67, 0x00, 0x71, 0x79, 0x5e, 0x00, 0x65, 0x6d, 0x55,
|
||||
0x00, 0x4d, 0x5d, 0x42, 0x00, 0x34, 0x40, 0x25, 0x00, 0x30, 0x40, 0x25, 0x00, 0x30, 0x38, 0x1c, 0x00, 0x2c, 0x3c, 0x1c, 0x00, 0x2c, 0x34, 0x1c, 0x00, 0x24, 0x2c, 0x12, 0x00, 0x24, 0x24, 0x00,
|
||||
0x00, 0x24, 0x2c, 0x09, 0x00, 0x28, 0x34, 0x09, 0x00, 0x38, 0x40, 0x12, 0x00, 0x30, 0x40, 0x1c, 0x00, 0x40, 0x50, 0x2f, 0x00, 0x55, 0x69, 0x42, 0x00, 0x65, 0x75, 0x55, 0x00, 0x6c, 0x7d, 0x5e,
|
||||
0x00, 0x74, 0x8d, 0x71, 0x00, 0x74, 0x89, 0x84, 0x00, 0x74, 0x8d, 0x84, 0x00, 0x78, 0x8d, 0x84, 0x00, 0x79, 0x89, 0x7a, 0x00, 0x79, 0x85, 0x71, 0x00, 0x75, 0x7d, 0x67, 0x00, 0x71, 0x79, 0x5e,
|
||||
0x00, 0x6c, 0x71, 0x5e, 0x00, 0x6d, 0x70, 0x5e, 0x00, 0x6c, 0x79, 0x5e, 0x00, 0x68, 0x75, 0x5e, 0x00, 0x69, 0x71, 0x55, 0x00, 0x6d, 0x75, 0x55, 0x00, 0x6d, 0x75, 0x55, 0x00, 0x69, 0x71, 0x55,
|
||||
0x00, 0x65, 0x71, 0x55, 0x00, 0x69, 0x6d, 0x55, 0x00, 0x64, 0x71, 0x5e, 0x00, 0x68, 0x70, 0x67, 0x00, 0x68, 0x70, 0x67, 0x00, 0x68, 0x6c, 0x67, 0x00, 0x6c, 0x6c, 0x5e, 0x00, 0x71, 0x71, 0x5e,
|
||||
0x00, 0x79, 0x79, 0x67, 0x00, 0x81, 0x85, 0x71, 0x00, 0x7d, 0x91, 0x71, 0x00, 0x85, 0x92, 0x7a, 0x00, 0x85, 0x92, 0x7a, 0x00, 0x7d, 0x92, 0x84, 0x00, 0x79, 0x92, 0x84, 0x00, 0x78, 0x92, 0x8d,
|
||||
0x00, 0x78, 0x8d, 0x8d, 0x00, 0x74, 0x8d, 0x84, 0x00, 0x74, 0x92, 0x84, 0x00, 0x75, 0x92, 0x7a, 0x00, 0x6c, 0x85, 0x67, 0x00, 0x64, 0x79, 0x5e, 0x00, 0x59, 0x69, 0x4b, 0x00, 0xaa, 0x57, 0x00,
|
||||
0x00, 0x38, 0x44, 0x1c, 0x00, 0x30, 0x3c, 0x1c, 0x00, 0x2c, 0x3c, 0x1c, 0x00, 0x34, 0x40, 0x25, 0x00, 0x50, 0x61, 0x4b, 0x00, 0x5d, 0x6d, 0x5e, 0x00, 0x64, 0x71, 0x5e, 0x00, 0x60, 0x71, 0x5e,
|
||||
0x00, 0x60, 0x75, 0x5e, 0x00, 0x68, 0x75, 0x5e, 0x00, 0x6c, 0x79, 0x5e, 0x00, 0x6c, 0x79, 0x5e, 0x00, 0x71, 0x79, 0x67, 0x00, 0x70, 0x79, 0x67, 0x00, 0x6c, 0x7d, 0x67, 0x00, 0x68, 0x79, 0x67,
|
||||
0x00, 0x6c, 0x79, 0x67, 0x00, 0x6c, 0x75, 0x67, 0x00, 0x71, 0x75, 0x5e, 0x00, 0x71, 0x75, 0x5e, 0x00, 0x75, 0x79, 0x5e, 0x00, 0x75, 0x7d, 0x5e, 0x00, 0x81, 0x8d, 0x5e, 0x00, 0x8d, 0x92, 0x5e,
|
||||
0x00, 0x8d, 0x92, 0x67, 0x00, 0x9a, 0x9a, 0x71, 0x00, 0x9a, 0xa2, 0x7a, 0x00, 0x9a, 0xa2, 0x7a, 0x00, 0x9a, 0xa1, 0x7a, 0x00, 0x92, 0x9a, 0x71, 0x00, 0x89, 0x92, 0x67, 0x00, 0x81, 0x85, 0x5e,
|
||||
0x00, 0x7d, 0x7d, 0x55, 0x00, 0x69, 0x79, 0x4b, 0x00, 0x61, 0x6d, 0x42, 0x00, 0x44, 0x4c, 0x25, 0x00, 0x38, 0x44, 0x1c, 0x00, 0x40, 0x51, 0x25, 0x00, 0x45, 0x4d, 0x25, 0x00, 0x71, 0x6d, 0x42,
|
||||
0x00, 0x79, 0x7d, 0x4b, 0x00, 0x81, 0x7d, 0x55, 0x00, 0x79, 0x79, 0x55, 0x00, 0x6d, 0x75, 0x55, 0x00, 0x69, 0x7d, 0x55, 0x00, 0x6c, 0x79, 0x5e, 0x00, 0x65, 0x79, 0x54, 0x00, 0x68, 0x79, 0x5e,
|
||||
0x00, 0x64, 0x79, 0x67, 0x00, 0x64, 0x79, 0x67, 0x00, 0x68, 0x75, 0x5e, 0x00, 0x64, 0x71, 0x5e, 0x00, 0x64, 0x6c, 0x5e, 0x00, 0x65, 0x6d, 0x55, 0x00, 0x4d, 0x58, 0x42, 0x00, 0x34, 0x40, 0x25,
|
||||
0x00, 0x2c, 0x38, 0x1c, 0x00, 0x20, 0x28, 0x1c, 0x00, 0x1c, 0x14, 0x09, 0x00, 0x18, 0x18, 0x00, 0x00, 0x04, 0x14, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x0c, 0x18, 0x00, 0x00, 0x1c, 0x28, 0x09,
|
||||
0x00, 0x24, 0x30, 0x12, 0x00, 0x3c, 0x44, 0x25, 0x00, 0x5d, 0x65, 0x55, 0x00, 0x75, 0x79, 0x55, 0x00, 0x85, 0x89, 0x5e, 0x00, 0x89, 0x91, 0x71, 0x00, 0x96, 0xa2, 0x71, 0x00, 0x9a, 0xa2, 0x7a,
|
||||
0x00, 0x9e, 0xaa, 0x7a, 0x00, 0x9e, 0xaa, 0x7a, 0x00, 0xaa, 0xae, 0x71, 0x00, 0xa6, 0xaa, 0x7a, 0x00, 0xa2, 0xaa, 0x7a, 0x00, 0xa1, 0xa5, 0x7a, 0x00, 0x96, 0x9e, 0x7a, 0x00, 0x85, 0x96, 0x7a,
|
||||
0x00, 0x81, 0x92, 0x7a, 0x00, 0x78, 0x92, 0x7a, 0x00, 0x75, 0x92, 0x7a, 0x00, 0x75, 0x8d, 0x7a, 0x00, 0x70, 0x81, 0x67, 0x00, 0x7d, 0x7d, 0x67, 0x00, 0x89, 0x89, 0x67, 0x00, 0x92, 0x9a, 0x71,
|
||||
0x00, 0x9e, 0xaa, 0x7a, 0x00, 0xaa, 0xb6, 0x84, 0x00, 0xb2, 0xb6, 0x8d, 0x00, 0xb6, 0xba, 0x97, 0x00, 0xc2, 0xca, 0x97, 0x00, 0xb2, 0xbe, 0x8d, 0x00, 0xb2, 0xb6, 0x8d, 0x00, 0xaa, 0xb2, 0x8d,
|
||||
0x00, 0xa2, 0xae, 0x84, 0x00, 0x9a, 0xa6, 0x7a, 0x00, 0x92, 0x9e, 0x7a, 0x00, 0x85, 0x9a, 0x7a, 0x00, 0x7d, 0x96, 0x7a, 0x00, 0x7d, 0x92, 0x7a, 0x00, 0x7d, 0x92, 0x84, 0x00, 0x7d, 0x92, 0x84,
|
||||
0x00, 0x81, 0x96, 0x84, 0x00, 0x85, 0x96, 0x84, 0x00, 0x85, 0x96, 0x84, 0x00, 0x81, 0x92, 0x84, 0x00, 0x85, 0x9a, 0x84, 0x00, 0x85, 0x9a, 0x84, 0x00, 0x8d, 0x9a, 0x84, 0x00, 0x92, 0x96, 0x84,
|
||||
0x00, 0x9e, 0xa9, 0x84, 0x00, 0xae, 0xb2, 0x84, 0x00, 0xaa, 0xba, 0x84, 0x00, 0xb2, 0xbe, 0x8d, 0x00, 0xb6, 0xc2, 0xa0, 0x00, 0xc6, 0xca, 0xa0, 0x00, 0xc6, 0xce, 0xaa, 0x00, 0xd6, 0xda, 0xb3,
|
||||
0x00, 0xda, 0xe2, 0xc5, 0x00, 0xd2, 0xd6, 0xbc, 0x00, 0xbe, 0xc2, 0xa0, 0x00, 0xaa, 0xb6, 0x8d, 0x00, 0x9e, 0xa6, 0x7a, 0x00, 0x92, 0x9a, 0x71, 0x00, 0x89, 0x89, 0x71, 0x00, 0x81, 0x7d, 0x67,
|
||||
0x00, 0x7d, 0x7d, 0x67, 0x00, 0x81, 0x78, 0x67, 0x00, 0x7d, 0x7d, 0x5e, 0x00, 0x79, 0x79, 0x5e, 0x00, 0x79, 0x81, 0x5e, 0x00, 0x81, 0x7d, 0x67, 0x00, 0x81, 0x7d, 0x67, 0x00, 0x81, 0x81, 0x67,
|
||||
0x00, 0x81, 0x89, 0x71, 0x00, 0x85, 0x91, 0x7a, 0x00, 0x89, 0x92, 0x7a, 0x00, 0x96, 0x9d, 0x7a, 0x00, 0x96, 0x9e, 0x7a, 0x00, 0x92, 0x96, 0x84, 0x00, 0x96, 0x9a, 0x8d, 0x00, 0x92, 0x92, 0x84,
|
||||
0x00, 0x89, 0x91, 0x84, 0x00, 0x81, 0x92, 0x84, 0x00, 0x7d, 0x92, 0x8d, 0x00, 0x78, 0x92, 0x8d, 0x00, 0x74, 0x92, 0x8d, 0x00, 0x78, 0x92, 0x8d, 0x00, 0x78, 0x96, 0x97, 0x00, 0x81, 0x96, 0x8d,
|
||||
0x00, 0x81, 0x96, 0x8d, 0x00, 0x81, 0x9a, 0x8d, 0x00, 0x85, 0x9a, 0x8d, 0x00, 0x89, 0x9e, 0x8d, 0x00, 0x89, 0x9e, 0x8d, 0x00, 0x8d, 0xa2, 0x97, 0x00, 0x95, 0xa2, 0x97, 0x00, 0x8d, 0xa2, 0x97,
|
||||
0x00, 0x96, 0xa6, 0x8d, 0x00, 0x9a, 0xa1, 0x8d, 0x00, 0x9e, 0xa9, 0x84, 0x00, 0x9e, 0xa6, 0x7a, 0x00, 0xa2, 0xa5, 0x71, 0x00, 0x9e, 0xa6, 0x71, 0x00, 0x9a, 0xa6, 0x71, 0x00, 0x95, 0x9d, 0x71 };
|
||||
|
||||
for (unsigned int i = 0; i < size; i++)
|
||||
{
|
||||
m_Entries[i].a = (T)palette15[i * 4 + 0];
|
||||
m_Entries[i].r = (T)palette15[i * 4 + 1];
|
||||
m_Entries[i].g = (T)palette15[i * 4 + 2];
|
||||
m_Entries[i].b = (T)palette15[i * 4 + 3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default copy constructor.
|
||||
/// </summary>
|
||||
/// <param name="palette">The Palette object to copy</param>
|
||||
Palette(const Palette<T>& palette)
|
||||
{
|
||||
Palette<T>::operator=<T>(palette);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy constructor to copy a Palette object of type U.
|
||||
/// </summary>
|
||||
/// <param name="palette">The Palette object to copy</param>
|
||||
template <typename U>
|
||||
Palette(const Palette<U>& palette)
|
||||
{
|
||||
Palette<T>::operator=<U>(palette);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default assignment operator.
|
||||
/// </summary>
|
||||
/// <param name="palette">The Palette object to copy</param>
|
||||
Palette<T>& operator = (const Palette<T>& palette)
|
||||
{
|
||||
if (this != &palette)
|
||||
Palette<T>::operator=<T>(palette);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator to assign a Palette object of type U.
|
||||
/// </summary>
|
||||
/// <param name="palette">The Palette object to copy</param>
|
||||
/// <returns>Reference to updated self</returns>
|
||||
template <typename U>
|
||||
Palette<T>& operator = (const Palette<U>& palette)
|
||||
{
|
||||
m_Index = palette.m_Index;
|
||||
m_Name = palette.m_Name;
|
||||
CopyVec(m_Entries, palette.m_Entries);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convenience [] operator to index into the color entries vector.
|
||||
/// </summary>
|
||||
/// <param name="i">The index to get</param>
|
||||
/// <returns>The color value at the specified index</returns>
|
||||
v4T& operator[] (size_t i)
|
||||
{
|
||||
return m_Entries[i];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convenience * operator to get a pointer to the beginning of the color entries vector.
|
||||
/// </summary>
|
||||
/// <returns>The address of the first element in the color entries vector</returns>
|
||||
inline v4T* operator() (void)
|
||||
{
|
||||
return &m_Entries[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The size of the color entries vector.
|
||||
/// </summary>
|
||||
/// <returns>The size of the color entries vector</returns>
|
||||
size_t Size() { return m_Entries.size(); }
|
||||
|
||||
/// <summary>
|
||||
/// Set all colors to either black or white, including the alpha channel.
|
||||
/// </summary>
|
||||
/// <param name="black">Set all colors to black if true, else white</param>
|
||||
void Clear(bool black = true)
|
||||
{
|
||||
for (glm::length_t i = 0; i < Size(); i++)
|
||||
{
|
||||
for (glm::length_t j = 0; j < 4; j++)
|
||||
{
|
||||
if (black)
|
||||
m_Entries[i][j] = 0;
|
||||
else
|
||||
m_Entries[i][j] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make a copy of this palette, adjust for hue and store in the passed in palette.
|
||||
/// This is used because one way an ember Xml can specify color is with an index in the
|
||||
/// palette Xml file and a hue rotation value.
|
||||
/// </summary>
|
||||
/// <param name="palette">The palette to store the results in</param>
|
||||
/// <param name="hue">The hue rotation to apply</param>
|
||||
void MakeHueAdjustedPalette(Palette<T>& palette, T hue)
|
||||
{
|
||||
palette.m_Index = m_Index;
|
||||
palette.m_Name = m_Name;
|
||||
palette.m_Entries.resize(Size());
|
||||
|
||||
for (unsigned int i = 0; i < Size(); i++)
|
||||
{
|
||||
size_t ii = (i * 256) / COLORMAP_LENGTH;
|
||||
T rgb[3], hsv[3];
|
||||
|
||||
rgb[0] = m_Entries[ii].r;
|
||||
rgb[1] = m_Entries[ii].g;
|
||||
rgb[2] = m_Entries[ii].b;
|
||||
|
||||
RgbToHsv(rgb, hsv);
|
||||
hsv[0] += hue * T(6.0);
|
||||
HsvToRgb(hsv, rgb);
|
||||
|
||||
//Alpha serves as merely a hit counter that gets incremented by 1 each time, see Renderer::Accumulate() for its usage.
|
||||
//Removing it saves no memory since it's 16 byte aligned. This also means alpha is not used.
|
||||
palette[i].r = rgb[0];
|
||||
palette[i].g = rgb[1];
|
||||
palette[i].b = rgb[2];
|
||||
palette[i].a = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// More advanced adjustment than MakeHueAdjustedPalette() provides.
|
||||
/// Adjustments are applied in the order:
|
||||
/// Frequency, index rotation, hue rotation, saturation, brightness, contrast, blur.
|
||||
/// </summary>
|
||||
/// <param name="palette">The palette to store the result in</param>
|
||||
/// <param name="rot">Index rotation.</param>
|
||||
/// <param name="hue">Hue rotation -5 - 5</param>
|
||||
/// <param name="sat">Saturation 0 - 1</param>
|
||||
/// <param name="bright">Brightness 0 - 1</param>
|
||||
/// <param name="cont">Contrast -1 - 2</param>
|
||||
/// <param name="blur">Blur 0 - 127</param>
|
||||
/// <param name="freq">Frequency 1 - 10</param>
|
||||
void MakeAdjustedPalette(Palette<T>& palette, int rot, T hue, T sat, T bright, T cont, unsigned int blur, unsigned int freq)
|
||||
{
|
||||
T rgb[3], hsv[3];
|
||||
|
||||
if (freq > 1)
|
||||
{
|
||||
size_t n = Size() / freq;
|
||||
|
||||
for (size_t j = 0; j <= freq; j++)
|
||||
{
|
||||
for (size_t i = 0; i <= n; i++)
|
||||
{
|
||||
if ((i + j * n) < Size())
|
||||
{
|
||||
palette[i + j * n].r = m_Entries[i * freq].r;
|
||||
palette[i + j * n].g = m_Entries[i * freq].g;
|
||||
palette[i + j * n].b = m_Entries[i * freq].b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
palette.m_Name = m_Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
palette = *this;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < Size(); i++)
|
||||
{
|
||||
size_t ii = (i * 256) / COLORMAP_LENGTH;
|
||||
|
||||
rgb[0] = palette[(COLORMAP_LENGTH + ii - rot) % COLORMAP_LENGTH].r;//Rotation.
|
||||
rgb[1] = palette[(COLORMAP_LENGTH + ii - rot) % COLORMAP_LENGTH].g;
|
||||
rgb[2] = palette[(COLORMAP_LENGTH + ii - rot) % COLORMAP_LENGTH].b;
|
||||
RgbToHsv(rgb, hsv);
|
||||
hsv[0] += hue * T(6.0);//Hue.
|
||||
hsv[1] = Clamp<T>(hsv[1] + sat, 0, 1);//Saturation.
|
||||
HsvToRgb(hsv, rgb);
|
||||
rgb[0] = Clamp<T>(rgb[0] + bright, 0, 1);//Brightness.
|
||||
rgb[1] = Clamp<T>(rgb[1] + bright, 0, 1);
|
||||
rgb[2] = Clamp<T>(rgb[2] + bright, 0, 1);
|
||||
rgb[0] = Clamp<T>(((rgb[0] - T(0.5)) * (cont + T(1.0))) + T(0.5), 0, 1);//Contrast.
|
||||
rgb[1] = Clamp<T>(((rgb[1] - T(0.5)) * (cont + T(1.0))) + T(0.5), 0, 1);
|
||||
rgb[2] = Clamp<T>(((rgb[2] - T(0.5)) * (cont + T(1.0))) + T(0.5), 0, 1);
|
||||
|
||||
//Alpha serves as merely a hit counter that gets incremented by 1 each time, see Renderer::Accumulate() for its usage.
|
||||
//Removing it saves no memory since it's 16 byte aligned.
|
||||
palette[i].r = rgb[0];
|
||||
palette[i].g = rgb[1];
|
||||
palette[i].b = rgb[2];
|
||||
palette[i].a = 1;
|
||||
}
|
||||
|
||||
if (blur > 0)
|
||||
{
|
||||
Palette<T> blurPal = palette;
|
||||
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
int n = -1;
|
||||
|
||||
rgb[0] = 0;
|
||||
rgb[1] = 0;
|
||||
rgb[2] = 0;
|
||||
|
||||
for (int j = i - (int)blur; j <= i + (int)blur; j++)
|
||||
{
|
||||
n++;
|
||||
int k = (256 + j) % 256;
|
||||
|
||||
if (k != i)
|
||||
{
|
||||
rgb[0] = rgb[0] + blurPal[k].r;
|
||||
rgb[1] = rgb[1] + blurPal[k].g;
|
||||
rgb[2] = rgb[2] + blurPal[k].b;
|
||||
}
|
||||
}
|
||||
|
||||
if (n != 0)
|
||||
{
|
||||
palette[i].r = rgb[0] / n;
|
||||
palette[i].g = rgb[1] / n;
|
||||
palette[i].b = rgb[2] / n;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make a copy of this palette and multiply all RGB values by a scalar.
|
||||
/// </summary>
|
||||
/// <param name="palette">The palette to store the result in</param>
|
||||
/// <param name="colorScalar">The color scalar to multiply each RGB value by</param>
|
||||
template<typename bucketT>
|
||||
void MakeDmap(Palette<bucketT>& palette, T colorScalar = 1)
|
||||
{
|
||||
palette.m_Index = m_Index;
|
||||
palette.m_Name = m_Name;
|
||||
|
||||
if (palette.Size() != Size())
|
||||
palette.m_Entries.resize(Size());
|
||||
|
||||
for (unsigned int j = 0; j < palette.Size(); j++)
|
||||
{
|
||||
palette.m_Entries[j] = m_Entries[j] * colorScalar;
|
||||
palette.m_Entries[j].a = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make a buffer with the color values of this palette scaled to 255
|
||||
/// and repeated for a number of rows.
|
||||
/// Convenience function for displaying this palette on a GUI.
|
||||
/// </summary>
|
||||
/// <param name="height">The height of the output block</param>
|
||||
/// <returns>A vector holding the color values</returns>
|
||||
vector<unsigned char> MakeRgbPaletteBlock(unsigned int height)
|
||||
{
|
||||
size_t width = Size();
|
||||
vector<unsigned char> v(height * width * 3);
|
||||
|
||||
if (v.size() == (height * Size() * 3))
|
||||
{
|
||||
for (unsigned int i = 0; i < height; i++)
|
||||
{
|
||||
for (unsigned int j = 0; j < width; j++)
|
||||
{
|
||||
v[(width * 3 * i) + (j * 3)] = (unsigned char)(m_Entries[j][0] * T(255));//Palettes are as [0..1], so convert to [0..255] here since it's for GUI display.
|
||||
v[(width * 3 * i) + (j * 3) + 1] = (unsigned char)(m_Entries[j][1] * T(255));
|
||||
v[(width * 3 * i) + (j * 3) + 2] = (unsigned char)(m_Entries[j][2] * T(255));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert RGB to HSV.
|
||||
/// </summary>
|
||||
/// <param name="r">Red 0 - 1</param>
|
||||
/// <param name="g">Green 0 - 1</param>
|
||||
/// <param name="b">Blue 0 - 1</param>
|
||||
/// <param name="h">Hue 0 - 6</param>
|
||||
/// <param name="s">Saturation 0 - 1</param>
|
||||
/// <param name="v">Value 0 - 1</param>
|
||||
static void RgbToHsv(T r, T g, T b, T& h, T& s, T& v)
|
||||
{
|
||||
T max, min, del, rc, gc, bc;
|
||||
|
||||
max = std::max(std::max(r, g), b);//Compute maximum of r, g, b.
|
||||
min = std::min(std::min(r, g), b);//Compute minimum of r, g, b.
|
||||
|
||||
del = max - min;
|
||||
v = max;
|
||||
s = (max != 0) ? (del / max) : 0;
|
||||
h = 0;
|
||||
|
||||
if (s != 0)
|
||||
{
|
||||
rc = (max - r) / del;
|
||||
gc = (max - g) / del;
|
||||
bc = (max - b) / del;
|
||||
|
||||
if (r == max)
|
||||
h = bc - gc;
|
||||
else if (g == max)
|
||||
h = 2 + rc - bc;
|
||||
else if (b == max)
|
||||
h = 4 + gc - rc;
|
||||
|
||||
if (h < 0)
|
||||
h += 6;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper around RgbToHsv() which takes buffers as parameters instead of individual components.
|
||||
/// </summary>
|
||||
/// <param name="rgb">The RGB buffer</param>
|
||||
/// <param name="hsv">The HSV buffer</param>
|
||||
static void RgbToHsv(T* rgb, T* hsv)
|
||||
{
|
||||
RgbToHsv(rgb[0], rgb[1], rgb[2], hsv[0], hsv[1], hsv[2]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert HSV to RGB.
|
||||
/// </summary>
|
||||
/// <param name="h">Hue 0 - 6</param>
|
||||
/// <param name="s">Saturation 0 - 1</param>
|
||||
/// <param name="v">Value 0 - 1</param>
|
||||
/// <param name="r">Red 0 - 1</param>
|
||||
/// <param name="g">Green 0 - 1</param>
|
||||
/// <param name="b">Blue 0 - 1</param>
|
||||
static void HsvToRgb(T h, T s, T v, T& r, T& g, T& b)
|
||||
{
|
||||
int j;
|
||||
T f, p, q, t;
|
||||
|
||||
while (h >= 6)
|
||||
h -= 6;
|
||||
|
||||
while (h < 0)
|
||||
h += 6;
|
||||
|
||||
j = Floor<T>(h);
|
||||
f = h - j;
|
||||
p = v * (1 - s);
|
||||
q = v * (1 - (s * f));
|
||||
t = v * (1 - (s * (1 - f)));
|
||||
|
||||
switch (j)
|
||||
{
|
||||
case 0: r = v; g = t; b = p; break;
|
||||
case 1: r = q; g = v; b = p; break;
|
||||
case 2: r = p; g = v; b = t; break;
|
||||
case 3: r = p; g = q; b = v; break;
|
||||
case 4: r = t; g = p; b = v; break;
|
||||
case 5: r = v; g = p; b = q; break;
|
||||
default: r = v; g = t; b = p; break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper around HsvToRgb() which takes buffers as parameters instead of individual components.
|
||||
/// </summary>
|
||||
/// <param name="hsv">The HSV buffer</param>
|
||||
/// <param name="rgb">The RGB buffer</param>
|
||||
static void HsvToRgb(T* hsv, T* rgb)
|
||||
{
|
||||
HsvToRgb(hsv[0], hsv[1], hsv[2], rgb[0], rgb[1], rgb[2]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the alpha.
|
||||
/// Used for gamma correction in final accumulation.
|
||||
/// Not the slightest clue what this is doing.
|
||||
/// </summary>
|
||||
/// <param name="density">Density</param>
|
||||
/// <param name="gamma">Gamma</param>
|
||||
/// <param name="linrange">Linear range</param>
|
||||
/// <returns>Alpha</returns>
|
||||
static T CalcAlpha(T density, T gamma, T linrange)
|
||||
{
|
||||
T frac, alpha;
|
||||
T funcval = pow(linrange, gamma);
|
||||
|
||||
if (density > 0)
|
||||
{
|
||||
if (density < linrange)
|
||||
{
|
||||
frac = density / linrange;
|
||||
alpha = (T(1.0) - frac) * density * (funcval / linrange) + frac * pow(density, gamma);
|
||||
}
|
||||
else
|
||||
alpha = pow(density, gamma);
|
||||
}
|
||||
else
|
||||
alpha = 0;
|
||||
|
||||
return alpha;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the new RGB and stores in the supplied buffer.
|
||||
/// Used for gamma correction in final accumulation.
|
||||
/// Not the slightest clue what this is doing.
|
||||
/// </summary>
|
||||
/// <param name="cBuf">The input RGB color buffer 0 - 1</param>
|
||||
/// <param name="ls">Log scaling</param>
|
||||
/// <param name="highPow">Highlight power, -1 - 1</param>
|
||||
/// <param name="newRgb">Newly computed RGB value</param>
|
||||
template<typename bucketT>
|
||||
static void CalcNewRgb(bucketT* cBuf, T ls, T highPow, bucketT* newRgb)
|
||||
{
|
||||
int rgbi;
|
||||
T newls, lsratio;
|
||||
bucketT newhsv[3];
|
||||
T maxa, maxc;
|
||||
T adjustedHighlight;
|
||||
|
||||
if (ls == 0 || (cBuf[0] == 0 && cBuf[1] == 0 && cBuf[2] == 0))
|
||||
{
|
||||
newRgb[0] = 0;
|
||||
newRgb[1] = 0;
|
||||
newRgb[2] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
//Identify the most saturated channel.
|
||||
maxc = max(max(cBuf[0], cBuf[1]), cBuf[2]);
|
||||
maxa = ls * maxc;
|
||||
|
||||
//If a channel is saturated and highlight power is non-negative
|
||||
//modify the color to prevent hue shift.
|
||||
if (maxa > 255 && highPow >= 0)
|
||||
{
|
||||
newls = T(255.0) / maxc;
|
||||
lsratio = pow(newls / ls, highPow);
|
||||
|
||||
//Calculate the max-value color (ranged 0 - 1).
|
||||
for (rgbi = 0; rgbi < 3; rgbi++)
|
||||
newRgb[rgbi] = (bucketT)newls * cBuf[rgbi] / bucketT(255.0);
|
||||
|
||||
//Reduce saturation by the lsratio.
|
||||
Palette<bucketT>::RgbToHsv(newRgb, newhsv);
|
||||
newhsv[1] *= (bucketT)lsratio;
|
||||
Palette<bucketT>::HsvToRgb(newhsv, newRgb);
|
||||
|
||||
for (rgbi = 0; rgbi < 3; rgbi++)
|
||||
newRgb[rgbi] *= T(255.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
newls = T(255.0) / maxc;
|
||||
adjustedHighlight = -highPow;
|
||||
|
||||
if (adjustedHighlight > 1)
|
||||
adjustedHighlight = 1;
|
||||
|
||||
if (maxa <= 255)
|
||||
adjustedHighlight = 1;
|
||||
|
||||
//Calculate the max-value color (ranged 0 - 1) interpolated with the old behavior.
|
||||
for (rgbi = 0; rgbi < 3; rgbi++)
|
||||
newRgb[rgbi] = bucketT((T(1.0) - adjustedHighlight) * newls + adjustedHighlight * ls) * cBuf[rgbi];
|
||||
}
|
||||
}
|
||||
|
||||
int m_Index;//Index in the xml palette file of this palette, use -1 for random.
|
||||
string m_Name;//Name of this palette.
|
||||
vector<v4T> m_Entries;//Storage for the color values.
|
||||
};
|
||||
}
|
226
Source/Ember/PaletteList.h
Normal file
226
Source/Ember/PaletteList.h
Normal file
@ -0,0 +1,226 @@
|
||||
#pragma once
|
||||
|
||||
#include "Palette.h"
|
||||
|
||||
/// <summary>
|
||||
/// PaletteList class.
|
||||
/// </summary>
|
||||
|
||||
namespace EmberNs
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds a list of palettes read from an Xml file. Since the default list from flam3-palettes.xml is fairly large at 700 palettes,
|
||||
/// the list member is kept as a static. This class derives from EmberReport in order to report any errors that occurred while reading the Xml.
|
||||
/// Note that although the Xml color values are expected to be 0-255, they are converted and stored as normalized colors, with values from 0-1.
|
||||
/// Template argument expected to be float or double.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API PaletteList : public EmberReport
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Empty constructor which does nothing.
|
||||
/// </summary>
|
||||
PaletteList()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read an Xml palette file into memory.
|
||||
/// This must be called before any palette file usage.
|
||||
/// </summary>
|
||||
/// <param name="filename">The full path to the file to read</param>
|
||||
/// <param name="force">If true, override the initialization state and force a read, else observe the initialization state.</param>
|
||||
/// <returns>The initialization state</returns>
|
||||
bool Init(string filename, bool force = false)
|
||||
{
|
||||
if (!m_Init || force)
|
||||
{
|
||||
const char* loc = __FUNCTION__;
|
||||
|
||||
m_Init = false;
|
||||
m_Palettes.clear();
|
||||
m_ErrorReport.clear();
|
||||
string buf;
|
||||
|
||||
if (ReadFile(filename.c_str(), buf))
|
||||
{
|
||||
xmlDocPtr doc = xmlReadMemory((const char*)buf.data(), (int)buf.size(), filename.c_str(), NULL, XML_PARSE_NONET);
|
||||
|
||||
if (doc != NULL)
|
||||
{
|
||||
xmlNode* rootNode = xmlDocGetRootElement(doc);
|
||||
|
||||
m_Palettes.reserve(buf.size() / 2048);//Roughly what it takes per palette.
|
||||
ParsePalettes(rootNode);
|
||||
xmlFreeDoc(doc);
|
||||
m_Init = m_ErrorReport.empty();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ErrorReport.push_back(string(loc) + " : Couldn't load xml doc");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ErrorReport.push_back(string(loc) + " : Couldn't read palette file " + filename);
|
||||
}
|
||||
}
|
||||
|
||||
return m_Init;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the palette at a specified index.
|
||||
/// </summary>
|
||||
/// <param name="i">The index of the palette to read. A value of -1 indicates a random palette.</param>
|
||||
/// <returns>A pointer to the requested palette if the index was in range, else NULL.</returns>
|
||||
Palette<T>* GetPalette(int i)
|
||||
{
|
||||
if (!m_Palettes.empty())
|
||||
{
|
||||
if (i == -1)
|
||||
return &m_Palettes[QTIsaac<ISAAC_SIZE, ISAAC_INT>::GlobalRand->Rand() % Count()];
|
||||
else if (i < (int)m_Palettes.size())
|
||||
return &m_Palettes[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a pointer to a palette with a specified name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the palette to retrieve</param>
|
||||
/// <returns>A pointer to the palette if found, else NULL</returns>
|
||||
Palette<T>* GetPaletteByName(string& name)
|
||||
{
|
||||
for (unsigned int i = 0; i < Count(); i++)
|
||||
if (m_Palettes[i].m_Name == name)
|
||||
return &m_Palettes[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a copy of the palette at a specified index with its hue adjusted by the specified amount.
|
||||
/// </summary>
|
||||
/// <param name="i">The index of the palette to read. A value of -1 indicates a random palette.</param>
|
||||
/// <param name="hue">The hue adjustment to apply</param>
|
||||
/// <param name="palette">The palette to store the output</param>
|
||||
/// <returns>True if successful, else false.</returns>
|
||||
bool GetHueAdjustedPalette(int i, T hue, Palette<T>& palette)
|
||||
{
|
||||
bool b = false;
|
||||
Palette<T>* unadjustedPal = GetPalette(i);
|
||||
|
||||
if (unadjustedPal)
|
||||
{
|
||||
unadjustedPal->MakeHueAdjustedPalette(palette, hue);
|
||||
b = true;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear the palette list and reset the initialization state.
|
||||
/// </summary>
|
||||
void Clear()
|
||||
{
|
||||
m_Palettes.clear();
|
||||
m_Init = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accessors.
|
||||
/// </summary>
|
||||
bool Init() { return m_Init; }
|
||||
unsigned int Count() { return (unsigned int)m_Palettes.size(); }
|
||||
|
||||
private:
|
||||
/// <summary>
|
||||
/// Parses an Xml node for all palettes present and stores in the palette list.
|
||||
/// Note that although the Xml color values are expected to be 0-255, they are converted and
|
||||
/// stored as normalized colors, with values from 0-1.
|
||||
/// </summary>
|
||||
/// <param name="node">The parent note of all palettes in the Xml file.</param>
|
||||
void ParsePalettes(xmlNode* node)
|
||||
{
|
||||
bool hexError = false;
|
||||
char* val;
|
||||
const char* loc = __FUNCTION__;
|
||||
xmlAttrPtr attr;
|
||||
|
||||
while (node)
|
||||
{
|
||||
if (node->type == XML_ELEMENT_NODE && !Compare(node->name, "palette"))
|
||||
{
|
||||
attr = node->properties;
|
||||
Palette<T> palette;
|
||||
|
||||
while (attr)
|
||||
{
|
||||
val = (char*)xmlGetProp(node, attr->name);
|
||||
|
||||
if (!Compare(attr->name, "data"))
|
||||
{
|
||||
int colorIndex = 0;
|
||||
int r, g, b;
|
||||
int colorCount = 0;
|
||||
hexError = false;
|
||||
|
||||
do
|
||||
{
|
||||
int ret = sscanf_s((char*)&(val[colorIndex]),"00%2x%2x%2x", &r, &g, &b);
|
||||
|
||||
if (ret != 3)
|
||||
{
|
||||
m_ErrorReport.push_back(string(loc) + " : Problem reading hexadecimal color data " + string(&val[colorIndex]));
|
||||
hexError = true;
|
||||
break;
|
||||
}
|
||||
|
||||
colorIndex += 8;
|
||||
|
||||
while (isspace((int)val[colorIndex]))
|
||||
colorIndex++;
|
||||
|
||||
palette[colorCount].r = T(r) / T(255);//Store as normalized colors in the range of 0-1.
|
||||
palette[colorCount].g = T(g) / T(255);
|
||||
palette[colorCount].b = T(b) / T(255);
|
||||
|
||||
colorCount++;
|
||||
} while (colorCount < COLORMAP_LENGTH);
|
||||
}
|
||||
else if (!Compare(attr->name, "number"))
|
||||
{
|
||||
palette.m_Index = atoi(val);
|
||||
}
|
||||
else if (!Compare(attr->name, "name"))
|
||||
{
|
||||
palette.m_Name = string(val);
|
||||
}
|
||||
|
||||
xmlFree(val);
|
||||
attr = attr->next;
|
||||
}
|
||||
|
||||
if (!hexError)
|
||||
{
|
||||
m_Palettes.push_back(palette);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ParsePalettes(node->children);
|
||||
}
|
||||
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
static bool m_Init;//Initialized to false in Ember.cpp, and will be set to true upon successful reading of an Xml palette file.
|
||||
static vector<Palette<T>> m_Palettes;//The vector that stores the palettes.
|
||||
};
|
||||
}
|
217
Source/Ember/Point.h
Normal file
217
Source/Ember/Point.h
Normal file
@ -0,0 +1,217 @@
|
||||
#pragma once
|
||||
|
||||
#include "EmberDefines.h"
|
||||
#include "Affine2D.h"
|
||||
#include "Timing.h"
|
||||
|
||||
/// <summary>
|
||||
/// Basic point and color structures used in iteration.
|
||||
/// </summary>
|
||||
|
||||
namespace EmberNs
|
||||
{
|
||||
/// <summary>
|
||||
/// The point used to store the result of each iteration, which is
|
||||
/// a spatial coordinate, a color index/coordinate and a visibility value.
|
||||
/// Note that a Y color coordinate is not used at the moment because
|
||||
/// only 1D palettes are supported like the original. However, in the future
|
||||
/// 2D palettes may be supported like Fractron does.
|
||||
/// Template argument expected to be float or double.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API Point
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor to initialize spatial and color coordinates to zero, with full visibility.
|
||||
/// </summary>
|
||||
Point()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default copy constructor.
|
||||
/// </summary>
|
||||
/// <param name="point">The Point object to copy</param>
|
||||
Point(const Point<T>& point)
|
||||
{
|
||||
Point<T>::operator=<T>(point);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy constructor to copy a Point object of type U.
|
||||
/// </summary>
|
||||
/// <param name="point">The Point object to copy</param>
|
||||
template <typename U>
|
||||
Point(const Point<U>& point)
|
||||
{
|
||||
Point<T>::operator=<U>(point);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default assignment operator.
|
||||
/// </summary>
|
||||
/// <param name="point">The Point object to copy</param>
|
||||
Point<T>& operator = (const Point<T>& point)
|
||||
{
|
||||
if (this != &point)
|
||||
Point<T>::operator=<T>(point);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator to assign a Point object of type U.
|
||||
/// </summary>
|
||||
/// <param name="point">The Point object to copy.</param>
|
||||
/// <returns>Reference to updated self</returns>
|
||||
template <typename U>
|
||||
Point<T>& operator = (const Point<U>& point)
|
||||
{
|
||||
m_X = point.m_X;
|
||||
m_Y = point.m_Y;
|
||||
m_Z = point.m_Z;
|
||||
m_ColorX = point.m_ColorX;
|
||||
//m_ColorY = point.m_ColorY;
|
||||
m_VizAdjusted = point.m_VizAdjusted;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set spatial and color coordinates to zero, with full visibility.
|
||||
/// </summary>
|
||||
void Init()
|
||||
{
|
||||
m_X = 0;
|
||||
m_Y = 0;
|
||||
m_Z = 0;
|
||||
m_ColorX = 0;
|
||||
//m_ColorY = 0;
|
||||
m_VizAdjusted = 1;
|
||||
}
|
||||
|
||||
T m_X;
|
||||
T m_Y;
|
||||
T m_Z;
|
||||
T m_ColorX;
|
||||
//T m_ColorY;
|
||||
T m_VizAdjusted;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Comparer used for sorting the results of iteration by their spatial x coordinates.
|
||||
/// </summary>
|
||||
/// <param name="a">The first point to compare</param>
|
||||
/// <param name="b">The second point to compare</param>
|
||||
/// <returns>1 if the first point had an x coordinate less than the second point, else 0</returns>
|
||||
template <typename T>
|
||||
static int SortPointByX(const Point<T>& a, const Point<T>& b)
|
||||
{
|
||||
return a.m_X < b.m_X;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Comparer used for sorting the results of iteration by their spatial y coordinates.
|
||||
/// </summary>
|
||||
/// <param name="a">The first point to compare</param>
|
||||
/// <param name="b">The second point to compare</param>
|
||||
/// <returns>1 if the first point had an y coordinate less than the second point, else 0</returns>
|
||||
template <typename T>
|
||||
static int SortPointByY(const Point<T>& a, const Point<T>& b)
|
||||
{
|
||||
return a.m_Y < b.m_Y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Thin override of a glm::vec4 which adds a couple of functions
|
||||
/// specific to color handling.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API Color : public v4T
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor to set color values to zero, with full visibility.
|
||||
/// </summary>
|
||||
Color()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default copy constructor.
|
||||
/// </summary>
|
||||
/// <param name="color">The Color object to copy</param>
|
||||
Color(const Color<T>& color)
|
||||
{
|
||||
Color<T>::operator=<T>(color);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy constructor to copy a Color object of type U.
|
||||
/// </summary>
|
||||
/// <param name="color">The Color object to copy</param>
|
||||
template <typename U>
|
||||
Color(const Color<U>& color)
|
||||
{
|
||||
Color<T>::operator=<U>(color);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default assignment operator.
|
||||
/// </summary>
|
||||
/// <param name="color">The Color object to copy</param>
|
||||
Color<T>& operator = (const Color<T>& color)
|
||||
{
|
||||
if (this != &color)
|
||||
Color<T>::operator=<T>(color);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator to assign a Color object of type U.
|
||||
/// </summary>
|
||||
/// <param name="color">The Color object to copy.</param>
|
||||
/// <returns>Reference to updated self</returns>
|
||||
template <typename U>
|
||||
Color<T>& operator = (const Color<U>& color)
|
||||
{
|
||||
v4T::operator=<U>(color);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Member-wise constructor.
|
||||
/// </summary>
|
||||
Color(T rr, T gg, T bb, T aa)
|
||||
: v4T(rr, gg, bb, aa)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set color values and visibility to zero.
|
||||
/// </summary>
|
||||
inline void Clear()
|
||||
{
|
||||
r = 0;
|
||||
g = 0;
|
||||
b = 0;
|
||||
a = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set color values to zero, with full visibility.
|
||||
/// </summary>
|
||||
/// <param name="norm">If norm is true, the color fields are expected to have a range of 0-1, else 0-255</param>
|
||||
inline void Reset(bool norm = true)
|
||||
{
|
||||
r = 0;
|
||||
g = 0;
|
||||
b = 0;
|
||||
a = norm ? T(1) : T(255);
|
||||
}
|
||||
};
|
||||
}
|
2222
Source/Ember/Renderer.cpp
Normal file
2222
Source/Ember/Renderer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
412
Source/Ember/Renderer.h
Normal file
412
Source/Ember/Renderer.h
Normal file
@ -0,0 +1,412 @@
|
||||
#pragma once
|
||||
|
||||
#include "Ember.h"
|
||||
#include "Iterator.h"
|
||||
#include "Utils.h"
|
||||
#include "SpatialFilter.h"
|
||||
#include "DensityFilter.h"
|
||||
#include "TemporalFilter.h"
|
||||
#include "Interpolate.h"
|
||||
#include "CarToRas.h"
|
||||
#include "EmberToXml.h"
|
||||
|
||||
/// <summary>
|
||||
/// Renderer, RenderCallback and EmberStats classes.
|
||||
/// </summary>
|
||||
|
||||
namespace EmberNs
|
||||
{
|
||||
/// <summary>
|
||||
/// Function pointers present a major restriction when dealing
|
||||
/// with member functions, and that is they can only point to
|
||||
/// static ones. So instead of a straight function pointer, use
|
||||
/// a callback class with a single virtual callback
|
||||
/// member function.
|
||||
/// Template argument expected to be float or double.
|
||||
/// </summary>
|
||||
class EMBER_API RenderCallback
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Virtual destructor to ensure anything declared in derived classes gets cleaned up.
|
||||
/// </summary>
|
||||
virtual ~RenderCallback() { }
|
||||
|
||||
/// <summary>
|
||||
/// Empty progress function to be implemented in derived classes to take action on progress updates.
|
||||
/// </summary>
|
||||
/// <param name="ember">The ember currently being rendered</param>
|
||||
/// <param name="foo">An extra dummy parameter</param>
|
||||
/// <param name="fraction">The progress fraction from 0-100</param>
|
||||
/// <param name="stage">The stage of iteration. 1 is iterating, 2 is density filtering, 2 is final accumulation.</param>
|
||||
/// <param name="etaMs">The estimated milliseconds to completion of the current stage</param>
|
||||
/// <returns>Override should return 0 if an abort is requested, else 1 to continue rendering</returns>
|
||||
virtual int ProgressFunc(Ember<float>& ember, void* foo, double fraction, int stage, double etaMs) { return 0; }
|
||||
virtual int ProgressFunc(Ember<double>& ember, void* foo, double fraction, int stage, double etaMs) { return 0; }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Render statistics for the number of iterations ran,
|
||||
/// number of bad values calculated during iteration, and
|
||||
/// the total time for the entire render from the start of
|
||||
/// iteration to the end of final accumulation.
|
||||
/// </summary>
|
||||
class EMBER_API EmberStats
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor which sets all values to 0.
|
||||
/// </summary>
|
||||
EmberStats()
|
||||
{
|
||||
m_Iters = 0;
|
||||
m_Badvals = 0;
|
||||
m_RenderSeconds = 0;
|
||||
}
|
||||
|
||||
unsigned __int64 m_Iters, m_Badvals;
|
||||
double m_RenderSeconds;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// The types of available renderers.
|
||||
/// Add more in the future as different rendering methods are experimented with.
|
||||
/// Possible values might be: CPU+OpenGL, Particle, Inverse.
|
||||
/// </summary>
|
||||
enum eRendererType { CPU_RENDERER, OPENCL_RENDERER };
|
||||
|
||||
/// <summary>
|
||||
/// A base class with virtual functions to allow both templating and polymorphism to work together.
|
||||
/// Derived classes will implement all of these functions.
|
||||
/// Note that functions which return a decimal number use the most precise type, double.
|
||||
/// </summary>
|
||||
class EMBER_API RendererBase : public EmberReport
|
||||
{
|
||||
public:
|
||||
RendererBase() { }
|
||||
virtual ~RendererBase() { }
|
||||
virtual void SetEmber(Ember<float>& ember, eProcessAction action = FULL_RENDER) { }
|
||||
virtual void SetEmber(vector<Ember<float>>& embers) { }
|
||||
virtual void SetEmber(Ember<double>& ember, eProcessAction action = FULL_RENDER) { }
|
||||
virtual void SetEmber(vector<Ember<double>>& embers) { }
|
||||
virtual void Callback(RenderCallback* callback) { }
|
||||
virtual bool CreateSpatialFilter(bool& newAlloc) { return false; }
|
||||
virtual bool CreateTemporalFilter(bool& newAlloc) { return false; }
|
||||
virtual void ComputeBounds() { }
|
||||
virtual bool Ok() const { return false; }
|
||||
virtual void Reset() { }
|
||||
virtual void EnterRender() { }
|
||||
virtual void LeaveRender() { }
|
||||
virtual void EnterFinalAccum() { }
|
||||
virtual void LeaveFinalAccum() { }
|
||||
virtual void EnterResize() { }
|
||||
virtual void LeaveResize() { }
|
||||
virtual void Abort() { }
|
||||
virtual bool Aborted() { return false; }
|
||||
virtual bool InRender() { return false; }
|
||||
virtual bool InFinalAccum() { return false; }
|
||||
virtual unsigned int NumChannels() const { return 0; }
|
||||
virtual void NumChannels(unsigned int numChannels) { }
|
||||
virtual eRendererType RendererType() const { return CPU_RENDERER; }
|
||||
virtual void ReclaimOnResize(bool reclaimOnResize) { }
|
||||
virtual bool EarlyClip() const { return false; }
|
||||
virtual void EarlyClip(bool earlyClip) { }
|
||||
virtual void ThreadCount(unsigned int threads, const char* seedString = NULL) { }
|
||||
virtual void Transparency(bool transparency) { }
|
||||
virtual void InteractiveFilter(eInteractiveFilter filter) { }
|
||||
virtual unsigned int FinalRasW() const { return 0; }
|
||||
virtual unsigned int FinalRasH() const { return 0; }
|
||||
virtual unsigned int SuperRasW() const { return 0; }
|
||||
virtual unsigned int SuperRasH() const { return 0; }
|
||||
virtual unsigned int FinalBufferSize() const { return 0; }
|
||||
virtual unsigned int GutterWidth() const { return 0; }
|
||||
virtual double ScaledQuality() const { return 0; }
|
||||
virtual double LowerLeftX(bool gutter = true) const { return 0; }
|
||||
virtual double LowerLeftY(bool gutter = true) const { return 0; }
|
||||
virtual double UpperRightX(bool gutter = true) const { return 0; }
|
||||
virtual double UpperRightY(bool gutter = true) const { return 0; }
|
||||
virtual unsigned __int64 MemoryRequired(bool includeFinal) { return 0; }
|
||||
virtual unsigned __int64 MemoryAvailable() { return 0; }
|
||||
virtual bool PrepFinalAccumVector(vector<unsigned char>& pixels) { return false; }
|
||||
virtual eProcessState ProcessState() const { return NONE; }
|
||||
virtual eProcessAction ProcessAction() const { return NOTHING; }
|
||||
virtual EmberStats Stats() const { EmberStats stats; return stats; }
|
||||
virtual eRenderStatus Run(vector<unsigned char>& finalImage, double time = 0, unsigned int subBatchCountOverride = 0, bool forceOutput = false, size_t finalOffset = 0) { return RENDER_ERROR; }
|
||||
virtual EmberImageComments ImageComments(unsigned int printEditDepth = 0, bool intPalette = false, bool hexPalette = true) { EmberImageComments comments; return comments; }
|
||||
virtual DensityFilterBase* GetDensityFilter() { return NULL; }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Renderer is the main class where all of the execution takes place.
|
||||
/// It is intended that the program have one instance of it that it
|
||||
/// keeps around for its duration. After a user sets up an ember, it's passed
|
||||
/// in to be rendered.
|
||||
/// This class derives from EmberReport, so the caller is able
|
||||
/// to retrieve a text dump of error information if any errors occur.
|
||||
/// The final image output vector is also passed in because the calling code has more
|
||||
/// use for it than this class does.
|
||||
/// Several functions are made virtual and have a default CPU-based implementation
|
||||
/// that roughly matches what flam3 did. However they can be overriden in derived classes
|
||||
/// to provide alternative rendering implementations, such as using the GPU.
|
||||
/// Since this is a templated class, it's supposed to be entirely implemented in this .h file.
|
||||
/// However, VC++ 2010 has very crippled support for lambdas, which Renderer makes use of.
|
||||
/// If too many lambdas are used in a .h file, it will crash the compiler when another library
|
||||
/// tries to link to it. To work around the bug, only declarations are here and all implementations
|
||||
/// are in the .cpp file. It's unclear at what point it starts/stops working. But it seems that once
|
||||
/// enough code is placed in the .h file, the compiler crashes. So for the sake of consistency, everything
|
||||
/// is moved to the .cpp, even simple getters. One drawback however, is that the class must be
|
||||
/// explicitly exported at the bottom of the file.
|
||||
/// Also, despite explicitly doing this, the compiler throws a C4661 warning
|
||||
/// for every single function in this class, saying it can't find the implementation. This warning
|
||||
/// can be safely ignored.
|
||||
/// Template argument T expected to be float or double.
|
||||
/// Template argument bucketT was originally used to experiment with different types for the histogram, however
|
||||
/// the only types that work are float and double, so it's useless and should always match what T is.
|
||||
/// Mismatched types between T and bucketT are undefined.
|
||||
/// </summary>
|
||||
template <typename T, typename bucketT>
|
||||
class EMBER_API Renderer : public RendererBase
|
||||
{
|
||||
public:
|
||||
Renderer();
|
||||
virtual ~Renderer();
|
||||
|
||||
virtual void ComputeBounds();
|
||||
void ComputeCamera();
|
||||
void ChangeVal(std::function<void (void)> func, eProcessAction action);
|
||||
virtual void SetEmber(Ember<T>& ember, eProcessAction action = FULL_RENDER);
|
||||
virtual void SetEmber(vector<Ember<T>>& embers);
|
||||
void AddEmber(Ember<T>& ember);
|
||||
bool CreateTemporalFilter(bool& newAlloc);
|
||||
bool AssignIterator();
|
||||
virtual bool PrepFinalAccumVector(vector<unsigned char>& pixels);
|
||||
virtual eRenderStatus Run(vector<unsigned char>& finalImage, double time = 0, unsigned int subBatchCountOverride = 0, bool forceOutput = false, size_t finalOffset = 0);
|
||||
virtual EmberImageComments ImageComments(unsigned int printEditDepth = 0, bool intPalette = false, bool hexPalette = true);
|
||||
virtual unsigned __int64 MemoryRequired(bool includeFinal);
|
||||
|
||||
//Virtual functions to be overriden in derived renderers that use the GPU.
|
||||
virtual unsigned __int64 MemoryAvailable();
|
||||
virtual void Reset();
|
||||
virtual bool Ok() const;
|
||||
virtual bool CreateDEFilter(bool& newAlloc);
|
||||
virtual bool CreateSpatialFilter(bool& newAlloc);
|
||||
virtual unsigned int SubBatchSize() const;
|
||||
virtual void SubBatchSize(unsigned int sbs);
|
||||
virtual unsigned int NumChannels() const;
|
||||
virtual void NumChannels(unsigned int numChannels);
|
||||
virtual eRendererType RendererType() const;
|
||||
virtual unsigned int ThreadCount() const;
|
||||
virtual void ThreadCount(unsigned int threads, const char* seedString = NULL);
|
||||
virtual void Callback(RenderCallback* callback);
|
||||
|
||||
protected:
|
||||
//Virtual functions to be overriden in derived renderers that use the GPU, but not accessed outside.
|
||||
virtual void MakeDmap(T colorScalar);
|
||||
virtual bool Alloc();
|
||||
virtual bool ResetBuckets(bool resetHist = true, bool resetAccum = true);
|
||||
virtual eRenderStatus LogScaleDensityFilter();
|
||||
virtual eRenderStatus GaussianDensityFilter();
|
||||
virtual eRenderStatus AccumulatorToFinalImage(vector<unsigned char>& pixels, size_t finalOffset);
|
||||
virtual eRenderStatus AccumulatorToFinalImage(unsigned char* pixels, size_t finalOffset);
|
||||
virtual EmberStats Iterate(unsigned __int64 iterCount, unsigned int pass, unsigned int temporalSample);
|
||||
|
||||
public:
|
||||
//Accessors for render properties.
|
||||
vector<QTIsaac<ISAAC_SIZE, ISAAC_INT>> RandVec();
|
||||
bool RandVec(vector<QTIsaac<ISAAC_SIZE, ISAAC_INT>>& randVec);
|
||||
|
||||
inline bool LockAccum() const;
|
||||
void LockAccum(bool lockAccum);
|
||||
|
||||
virtual bool EarlyClip() const;
|
||||
virtual void EarlyClip(bool earlyClip);
|
||||
|
||||
inline bool InsertPalette() const;
|
||||
void InsertPalette(bool insertPalette);
|
||||
|
||||
inline bool ReclaimOnResize() const;
|
||||
virtual void ReclaimOnResize(bool reclaimOnResize);
|
||||
|
||||
inline bool Transparency() const;
|
||||
virtual void Transparency(bool transparency);
|
||||
|
||||
inline unsigned int BytesPerChannel() const;
|
||||
void BytesPerChannel(unsigned int bytesPerChannel);
|
||||
|
||||
inline T PixelAspectRatio() const;
|
||||
void PixelAspectRatio(T pixelAspectRatio);
|
||||
|
||||
inline eInteractiveFilter InteractiveFilter() const;
|
||||
virtual void InteractiveFilter(eInteractiveFilter filter);
|
||||
|
||||
//Threading control.
|
||||
virtual void EnterRender();
|
||||
virtual void LeaveRender();
|
||||
virtual void EnterFinalAccum();
|
||||
virtual void LeaveFinalAccum();
|
||||
virtual void EnterResize();
|
||||
virtual void LeaveResize();
|
||||
virtual void Abort();
|
||||
virtual bool Aborted();
|
||||
virtual bool InRender();
|
||||
virtual bool InFinalAccum();
|
||||
|
||||
//Renderer properties, getters only.
|
||||
virtual unsigned int SuperRasW() const;
|
||||
virtual unsigned int SuperRasH() const;
|
||||
inline unsigned int SuperSize() const;
|
||||
virtual unsigned int FinalBufferSize() const;
|
||||
inline unsigned int FinalRowSize() const;
|
||||
inline unsigned int FinalDimensions() const;
|
||||
inline unsigned int PixelSize() const;
|
||||
virtual unsigned int GutterWidth() const;
|
||||
inline unsigned int DensityFilterOffset() const;
|
||||
virtual double ScaledQuality() const;
|
||||
inline T Scale() const;
|
||||
inline T PixelsPerUnitX() const;
|
||||
inline T PixelsPerUnitY() const;
|
||||
virtual double LowerLeftX(bool gutter = true) const;
|
||||
virtual double LowerLeftY(bool gutter = true) const;
|
||||
virtual double UpperRightX(bool gutter = true) const;
|
||||
virtual double UpperRightY(bool gutter = true) const;
|
||||
inline T K1() const;
|
||||
inline T K2() const;
|
||||
inline unsigned __int64 TotalIterCount() const;
|
||||
inline unsigned __int64 ItersPerTemporalSample() const;
|
||||
virtual eProcessState ProcessState() const;
|
||||
virtual eProcessAction ProcessAction() const;
|
||||
virtual EmberStats Stats() const;
|
||||
inline const CarToRas<T>* CoordMap() const;
|
||||
inline glm::detail::tvec4<bucketT, glm::defaultp>* HistBuckets();
|
||||
inline glm::detail::tvec4<bucketT, glm::defaultp>* AccumulatorBuckets();
|
||||
inline SpatialFilter<T>* GetSpatialFilter();
|
||||
inline TemporalFilter<T>* GetTemporalFilter();
|
||||
virtual DensityFilter<T>* GetDensityFilter();
|
||||
|
||||
//Ember wrappers, getters only.
|
||||
inline bool XaosPresent();
|
||||
virtual inline unsigned int FinalRasW() const;
|
||||
virtual inline unsigned int FinalRasH() const;
|
||||
inline unsigned int Supersample() const;
|
||||
inline unsigned int Passes() const;
|
||||
inline unsigned int TemporalSamples() const;
|
||||
inline unsigned int PaletteIndex() const;
|
||||
inline T Time() const;
|
||||
inline T Quality() const;
|
||||
inline T SpatialFilterRadius() const;
|
||||
inline T PixelsPerUnit() const;
|
||||
inline T Zoom() const;
|
||||
inline T CenterX() const;
|
||||
inline T CenterY() const;
|
||||
inline T Rotate() const;
|
||||
inline T Hue() const;
|
||||
inline T Brightness() const;
|
||||
inline T Contrast() const;
|
||||
inline T Gamma() const;
|
||||
inline T Vibrancy() const;
|
||||
inline T GammaThresh() const;
|
||||
inline T HighlightPower() const;
|
||||
inline Color<T> Background() const;
|
||||
inline const Xform<T>* Xforms() const;
|
||||
inline Xform<T>* NonConstXforms();
|
||||
inline unsigned int XformCount() const;
|
||||
inline const Xform<T>* FinalXform() const;
|
||||
inline Xform<T>* NonConstFinalXform();
|
||||
inline bool UseFinalXform() const;
|
||||
inline const Palette<T>* GetPalette() const;
|
||||
inline ePaletteMode PaletteMode() const;
|
||||
|
||||
//Iterator wrappers.
|
||||
const unsigned char* XformDistributions() const;
|
||||
const unsigned int XformDistributionsSize() const;
|
||||
Point<T>* Samples(unsigned int threadIndex) const;
|
||||
|
||||
void* m_ProgressParameter;
|
||||
|
||||
protected:
|
||||
//Non-virtual functions that might be needed by a derived class.
|
||||
void PrepFinalAccumVals(Color<T>& background, T& g, T& linRange, T& vibrancy);
|
||||
|
||||
private:
|
||||
//Miscellaneous functions used only in this class.
|
||||
void Accumulate(Point<T>* samples, unsigned int sampleCount, const Palette<bucketT>* palette);
|
||||
inline void AddToAccum(const glm::detail::tvec4<bucketT, glm::defaultp>& bucket, int i, int ii, int j, int jj);
|
||||
template <typename accumT> void GammaCorrection(glm::detail::tvec4<bucketT, glm::defaultp>& bucket, Color<T>& background, T g, T linRange, T vibrancy, bool doAlpha, bool scale, accumT* correctedChannels);
|
||||
|
||||
protected:
|
||||
bool m_EarlyClip;
|
||||
bool m_Transparency;
|
||||
unsigned int m_SuperRasW;
|
||||
unsigned int m_SuperRasH;
|
||||
unsigned int m_SuperSize;
|
||||
unsigned int m_GutterWidth;
|
||||
unsigned int m_DensityFilterOffset;
|
||||
unsigned int m_NumChannels;
|
||||
unsigned int m_BytesPerChannel;
|
||||
unsigned int m_SubBatchSize;
|
||||
unsigned int m_ThreadsToUse;
|
||||
T m_ScaledQuality;
|
||||
T m_Scale;
|
||||
T m_PixelsPerUnitX;
|
||||
T m_PixelsPerUnitY;
|
||||
T m_PixelAspectRatio;
|
||||
T m_LowerLeftX;
|
||||
T m_LowerLeftY;
|
||||
T m_UpperRightX;
|
||||
T m_UpperRightY;
|
||||
T m_K1;
|
||||
T m_K2;
|
||||
T m_Vibrancy;//Accumulate these after each temporal sample.
|
||||
T m_Gamma;
|
||||
Color<T> m_Background;
|
||||
Affine2D<T> m_RotMat;
|
||||
|
||||
volatile bool m_Abort;
|
||||
bool m_LockAccum;
|
||||
bool m_InRender;
|
||||
bool m_InFinalAccum;
|
||||
bool m_InsertPalette;
|
||||
bool m_ReclaimOnResize;
|
||||
unsigned int m_VibGamCount;
|
||||
unsigned int m_LastPass;
|
||||
unsigned int m_LastTemporalSample;
|
||||
unsigned __int64 m_LastIter;
|
||||
double m_LastIterPercent;
|
||||
eProcessAction m_ProcessAction;
|
||||
eProcessState m_ProcessState;
|
||||
eInteractiveFilter m_InteractiveFilter;
|
||||
EmberStats m_Stats;
|
||||
Ember<T> m_Ember;
|
||||
Ember<T> m_TempEmber;
|
||||
Ember<T> m_LastEmber;
|
||||
vector<Ember<T>> m_Embers;
|
||||
CarToRas<T> m_CarToRas;
|
||||
RenderCallback* m_Callback;
|
||||
Iterator<T>* m_Iterator;
|
||||
auto_ptr<StandardIterator<T>> m_StandardIterator;
|
||||
auto_ptr<XaosIterator<T>> m_XaosIterator;
|
||||
Palette<bucketT> m_Dmap;
|
||||
vector<glm::detail::tvec4<bucketT, glm::defaultp>> m_HistBuckets;
|
||||
vector<glm::detail::tvec4<bucketT, glm::defaultp>> m_AccumulatorBuckets;
|
||||
auto_ptr<SpatialFilter<T>> m_SpatialFilter;
|
||||
auto_ptr<TemporalFilter<T>> m_TemporalFilter;
|
||||
auto_ptr<DensityFilter<T>> m_DensityFilter;
|
||||
vector<vector<Point<T>>> m_Samples;
|
||||
vector<unsigned __int64> m_SubBatch;
|
||||
vector<unsigned __int64> m_BadVals;
|
||||
vector<QTIsaac<ISAAC_SIZE, ISAAC_INT>> m_Rand;
|
||||
tbb::task_group m_TaskGroup;
|
||||
CriticalSection m_RenderingCs, m_AccumCs, m_FinalAccumCs, m_ResizeCs;
|
||||
Timing m_RenderTimer, m_ProgressTimer;
|
||||
EmberToXml<T> m_EmberToXml;
|
||||
};
|
||||
|
||||
//This class had to be implemented in a cpp file because the compiler was breaking.
|
||||
//So the explicit instantiation must be declared here rather than in Ember.cpp where
|
||||
//all of the other classes are done.
|
||||
template EMBER_API class Renderer<float, float>;
|
||||
|
||||
#ifdef DO_DOUBLE
|
||||
template EMBER_API class Renderer<double, double>;
|
||||
#endif
|
||||
}
|
1394
Source/Ember/SheepTools.h
Normal file
1394
Source/Ember/SheepTools.h
Normal file
File diff suppressed because it is too large
Load Diff
909
Source/Ember/SpatialFilter.h
Normal file
909
Source/Ember/SpatialFilter.h
Normal file
@ -0,0 +1,909 @@
|
||||
#pragma once
|
||||
|
||||
#include "EmberDefines.h"
|
||||
|
||||
/// <summary>
|
||||
/// SpatialFilter base, derived and factory classes.
|
||||
/// </summary>
|
||||
|
||||
namespace EmberNs
|
||||
{
|
||||
/// <summary>
|
||||
/// The types of spatial filters available.
|
||||
/// </summary>
|
||||
enum eSpatialFilterType
|
||||
{
|
||||
GAUSSIAN_SPATIAL_FILTER = 0,
|
||||
HERMITE_SPATIAL_FILTER = 1,
|
||||
BOX_SPATIAL_FILTER = 2,
|
||||
TRIANGLE_SPATIAL_FILTER = 3,
|
||||
BELL_SPATIAL_FILTER = 4,
|
||||
BSPLINE_SPATIAL_FILTER = 5,
|
||||
LANCZOS3_SPATIAL_FILTER = 6,
|
||||
LANCZOS2_SPATIAL_FILTER = 7,
|
||||
MITCHELL_SPATIAL_FILTER = 8,
|
||||
BLACKMAN_SPATIAL_FILTER = 9,
|
||||
CATROM_SPATIAL_FILTER = 10,
|
||||
HAMMING_SPATIAL_FILTER = 11,
|
||||
HANNING_SPATIAL_FILTER = 12,
|
||||
QUADRATIC_SPATIAL_FILTER = 13
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Spatial filtering is done in the final accumulation stage to add some additional
|
||||
/// bluring to smooth out noisy areas.
|
||||
/// The bulk of the work is done in this base class Create() function.
|
||||
/// Because it calls the virtual Filter() function, it cannot be automatically called in the constructor.
|
||||
/// So the caller must manually call it after constructing the filter object.
|
||||
/// Each derived class will implement an override of Filter() which
|
||||
/// contains the specific filter calculation for the algorithm whose name the class matches.
|
||||
/// Template argument expected to be float or double.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API SpatialFilter
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Assign basic parameters for creating a spatial filter. The caller must still call Create().
|
||||
/// </summary>
|
||||
/// <param name="filterType">Type of filter to create</param>
|
||||
/// <param name="support">Miscellaneous value</param>
|
||||
/// <param name="filterRadius">The filter radius</param>
|
||||
/// <param name="superSample">The supersample of the ember being rendered</param>
|
||||
/// <param name="pixelAspectRatio">The pixel aspect ratio being used to render. Default: 1.</param>
|
||||
SpatialFilter(eSpatialFilterType filterType, T support, T filterRadius, unsigned int superSample, T pixelAspectRatio = T(1.0))
|
||||
{
|
||||
m_FilterType = filterType;
|
||||
m_Support = support;
|
||||
m_FilterRadius = filterRadius;
|
||||
m_Supersample = superSample;
|
||||
m_PixelAspectRatio = pixelAspectRatio;
|
||||
//Sadly, cannot call create here because it calls the Filter() virtual function and unlike C#, the vtables
|
||||
//are not yet set up in C++ constructors. The code that instantiates this object must explicitly call Create().
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy constructor.
|
||||
/// </summary>
|
||||
/// <param name="filter">The SpatialFilter object to copy</param>
|
||||
SpatialFilter(const SpatialFilter<T>& filter)
|
||||
{
|
||||
*this = filter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Virtual destructor so derived class destructors get called.
|
||||
/// </summary>
|
||||
virtual ~SpatialFilter()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator.
|
||||
/// </summary>
|
||||
/// <param name="filter">The SpatialFilter object to copy.</param>
|
||||
/// <returns>Reference to updated self</returns>
|
||||
SpatialFilter<T>& operator = (const SpatialFilter<T>& filter)
|
||||
{
|
||||
if (this != &filter)
|
||||
{
|
||||
m_FinalFilterWidth = filter.m_FinalFilterWidth;
|
||||
m_Supersample = filter.m_Supersample;
|
||||
m_Support = filter.m_Support;
|
||||
m_FilterRadius = filter.m_FilterRadius;
|
||||
m_PixelAspectRatio = filter.m_PixelAspectRatio;
|
||||
m_FilterType = filter.m_FilterType;
|
||||
m_Filter = filter.m_Filter;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allocates and populates the filter buffer with virtual calls to derived Filter() functions.
|
||||
/// The caller must manually call this after construction.
|
||||
/// </summary>
|
||||
void Create()
|
||||
{
|
||||
T fw = T(2.0) * m_Support * m_Supersample * m_FilterRadius / m_PixelAspectRatio;
|
||||
T adjust, ii, jj;
|
||||
|
||||
int fwidth = ((int)fw) + 1;
|
||||
int i, j;
|
||||
|
||||
//Make sure the filter kernel has same parity as oversample.
|
||||
if ((fwidth ^ m_Supersample) & 1)
|
||||
fwidth++;
|
||||
|
||||
//Calculate the coordinate scaling factor for the kernel values.
|
||||
if (fw > 0.0)
|
||||
adjust = m_Support * fwidth / fw;
|
||||
else
|
||||
adjust = T(1.0);
|
||||
|
||||
m_Filter.resize(fwidth * fwidth);
|
||||
|
||||
//Fill in the coefs.
|
||||
for (i = 0; i < fwidth; i++)
|
||||
{
|
||||
for (j = 0; j < fwidth; j++)
|
||||
{
|
||||
//Calculate the function inputs for the kernel function.
|
||||
ii = ((T(2.0) * i + T(1.0)) / T(fwidth) - T(1.0)) * adjust;
|
||||
jj = ((T(2.0) * j + T(1.0)) / T(fwidth) - T(1.0)) * adjust;
|
||||
|
||||
//Adjust for aspect ratio.
|
||||
jj /= m_PixelAspectRatio;
|
||||
|
||||
m_Filter[i + j * fwidth] = Filter(ii) * Filter(jj);//Call virtual Filter(), implemented in specific derived filter classes.
|
||||
}
|
||||
}
|
||||
|
||||
//Normalize, and return a bad value if the values were too small.
|
||||
if (!Normalize())
|
||||
m_FinalFilterWidth = -1;
|
||||
else
|
||||
m_FinalFilterWidth = fwidth;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a string representation of this filter.
|
||||
/// </summary>
|
||||
/// <returns>The string representation of this filter</returns>
|
||||
string ToString() const
|
||||
{
|
||||
unsigned int i;
|
||||
stringstream ss;
|
||||
|
||||
ss
|
||||
<< "Spatial Filter:" << endl
|
||||
<< " Support: " << m_Support << endl
|
||||
<< " Filter radius: " << m_FilterRadius << endl
|
||||
<< " Supersample: " << m_Supersample << endl
|
||||
<< "Pixel aspect ratio: " << m_PixelAspectRatio << endl
|
||||
<< "Final filter width: " << m_FinalFilterWidth << endl
|
||||
<< "Filter buffer size: " << m_Filter.size() << endl;
|
||||
|
||||
ss << "Filter: " << endl;
|
||||
|
||||
for (i = 0; i < m_Filter.size(); i++)
|
||||
{
|
||||
ss << "Filter[" << i << "]: " << m_Filter[i] << endl;
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accessors.
|
||||
/// </summary>
|
||||
void Apply() { }
|
||||
inline int FinalFilterWidth() const { return m_FinalFilterWidth; }
|
||||
inline unsigned int Supersample() const { return m_Supersample; }
|
||||
inline unsigned int BufferSize() const { return (unsigned int)m_Filter.size(); }
|
||||
inline unsigned int BufferSizeBytes() const { return BufferSize() * sizeof(T); }
|
||||
inline T Support() const { return m_Support; }
|
||||
inline T FilterRadius() const { return m_FilterRadius; }
|
||||
inline T PixelAspectRatio() const { return m_PixelAspectRatio; }
|
||||
inline eSpatialFilterType FilterType() const { return m_FilterType; }
|
||||
inline T* Filter() { return m_Filter.data(); }
|
||||
inline const T& operator[] (size_t index) const { return m_Filter[index]; }
|
||||
virtual T Filter(T t) const = 0;
|
||||
|
||||
protected:
|
||||
/// <summary>
|
||||
/// Calculation function used in Lanczos filters.
|
||||
/// </summary>
|
||||
/// <param name="x">The x</param>
|
||||
/// <returns>The calculated value</returns>
|
||||
static T Sinc(T x)
|
||||
{
|
||||
x *= T(M_PI);
|
||||
|
||||
if (x != 0)
|
||||
return sin(x) / x;
|
||||
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
private:
|
||||
/// <summary>
|
||||
/// Normalize all filter values.
|
||||
/// </summary>
|
||||
/// <returns>True if any value was non-zero, else false if all were zero.</returns>
|
||||
bool Normalize()
|
||||
{
|
||||
size_t i;
|
||||
T t = T(0.0);
|
||||
|
||||
for (i = 0; i < m_Filter.size(); i++)
|
||||
t += m_Filter[i];
|
||||
|
||||
if (t == 0.0)
|
||||
return false;
|
||||
|
||||
t = T(1.0) / t;
|
||||
|
||||
for (i = 0; i < m_Filter.size(); i++)
|
||||
m_Filter[i] *= t;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int m_FinalFilterWidth;//The final width that the filter ends up being.
|
||||
unsigned int m_Supersample;//The supersample value of the ember using this filter to render.
|
||||
T m_Support;//Extra value.
|
||||
T m_FilterRadius;//The requested filter radius.
|
||||
T m_PixelAspectRatio;//The aspect ratio of the ember using this filter to render, usually 1.
|
||||
eSpatialFilterType m_FilterType;//The type of filter this is.
|
||||
vector<T> m_Filter;//The vector holding the calculated filter values.
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Derivation for Gaussian filter.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API GaussianFilter : public SpatialFilter<T>
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor which does nothing but pass values to the base class. The caller must still call Create().
|
||||
/// Support = 1.5.
|
||||
/// </summary>
|
||||
/// <param name="filterRadius">The filter radius</param>
|
||||
/// <param name="superSample">The supersample of the ember being rendered</param>
|
||||
/// <param name="pixelAspectRatio">The pixel aspect ratio being used to render. Default: 1.</param>
|
||||
GaussianFilter(T filterRadius, unsigned int superSample, T pixelAspectRatio = T(1.0))
|
||||
: SpatialFilter<T>(GAUSSIAN_SPATIAL_FILTER, T(1.5), filterRadius, superSample, pixelAspectRatio) { }
|
||||
|
||||
/// <summary>
|
||||
/// Apply Gaussian filter to t parameter and return.
|
||||
/// </summary>
|
||||
/// <param name="t">The value to apply the filter to</param>
|
||||
/// <returns>The filtered value</returns>
|
||||
virtual T Filter(T t) const
|
||||
{
|
||||
return exp(-2 * t * t) * sqrt(2 / T(M_PI));
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Derivation for Hermite filter.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API HermiteFilter : public SpatialFilter<T>
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor which does nothing but pass values to the base class. The caller must still call Create().
|
||||
/// Support = 1.
|
||||
/// </summary>
|
||||
/// <param name="filterRadius">The filter radius</param>
|
||||
/// <param name="superSample">The supersample of the ember being rendered</param>
|
||||
/// <param name="pixelAspectRatio">The pixel aspect ratio being used to render. Default: 1.</param>
|
||||
HermiteFilter(T filterRadius, unsigned int superSample, T pixelAspectRatio = T(1.0))
|
||||
: SpatialFilter<T>(HERMITE_SPATIAL_FILTER, T(1.0), filterRadius, superSample, pixelAspectRatio) { }
|
||||
|
||||
/// <summary>
|
||||
/// Apply Hermite filter to t parameter and return.
|
||||
/// f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1.
|
||||
/// </summary>
|
||||
/// <param name="t">The value to apply the filter to</param>
|
||||
/// <returns>The filtered value</returns>
|
||||
virtual T Filter(T t) const
|
||||
{
|
||||
if (t < 0)
|
||||
t = -t;
|
||||
|
||||
if (t < 1)
|
||||
return ((T(2.0) * t - T(3.0)) * t * t + T(1.0));
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Derivation for Box filter.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API BoxFilter : public SpatialFilter<T>
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor which does nothing but pass values to the base class. The caller must still call Create().
|
||||
/// Support = 0.5.
|
||||
/// </summary>
|
||||
/// <param name="filterRadius">The filter radius</param>
|
||||
/// <param name="superSample">The supersample of the ember being rendered</param>
|
||||
/// <param name="pixelAspectRatio">The pixel aspect ratio being used to render. Default: 1.</param>
|
||||
BoxFilter(T filterRadius, unsigned int superSample, T pixelAspectRatio = T(1.0))
|
||||
: SpatialFilter<T>(BOX_SPATIAL_FILTER, T(0.5), filterRadius, superSample, pixelAspectRatio) { }
|
||||
|
||||
/// <summary>
|
||||
/// Apply Box filter to t parameter and return.
|
||||
/// </summary>
|
||||
/// <param name="t">The value to apply the filter to</param>
|
||||
/// <returns>The filtered value</returns>
|
||||
virtual T Filter(T t) const
|
||||
{
|
||||
if ((t > T(-0.5)) && (t <= T(0.5)))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Derivation for Triangle filter.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API TriangleFilter : public SpatialFilter<T>
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor which does nothing but pass values to the base class. The caller must still call Create().
|
||||
/// Support = 1.
|
||||
/// </summary>
|
||||
/// <param name="filterRadius">The filter radius</param>
|
||||
/// <param name="superSample">The supersample of the ember being rendered</param>
|
||||
/// <param name="pixelAspectRatio">The pixel aspect ratio being used to render. Default: 1.</param>
|
||||
TriangleFilter(T filterRadius, unsigned int superSample, T pixelAspectRatio = T(1.0))
|
||||
: SpatialFilter<T>(TRIANGLE_SPATIAL_FILTER, T(1.0), filterRadius, superSample, pixelAspectRatio) { }
|
||||
|
||||
/// <summary>
|
||||
/// Apply Triangle filter to t parameter and return.
|
||||
/// </summary>
|
||||
/// <param name="t">The value to apply the filter to</param>
|
||||
/// <returns>The filtered value</returns>
|
||||
virtual T Filter(T t) const
|
||||
{
|
||||
if (t < 0)
|
||||
t = -t;
|
||||
|
||||
if (t < 1)
|
||||
return 1 - t;
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Derivation for Bell filter.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API BellFilter : public SpatialFilter<T>
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor which does nothing but pass values to the base class. The caller must still call Create().
|
||||
/// Support = 1.5.
|
||||
/// </summary>
|
||||
/// <param name="filterRadius">The filter radius</param>
|
||||
/// <param name="superSample">The supersample of the ember being rendered</param>
|
||||
/// <param name="pixelAspectRatio">The pixel aspect ratio being used to render. Default: 1.</param>
|
||||
BellFilter(T filterRadius, unsigned int superSample, T pixelAspectRatio = T(1.0))
|
||||
: SpatialFilter<T>(BELL_SPATIAL_FILTER, T(1.5), filterRadius, superSample, pixelAspectRatio) { }
|
||||
|
||||
/// <summary>
|
||||
/// Apply Bell filter to t parameter and return.
|
||||
/// </summary>
|
||||
/// <param name="t">The value to apply the filter to</param>
|
||||
/// <returns>The filtered value</returns>
|
||||
virtual T Filter(T t) const
|
||||
{
|
||||
//box (*) box (*) box.
|
||||
if (t < 0)
|
||||
t = -t;
|
||||
|
||||
if (t < T(0.5))
|
||||
return (T(0.75) - (t * t));
|
||||
|
||||
if (t < T(1.5))
|
||||
{
|
||||
t = (t - T(1.5));
|
||||
return (T(0.5) * (t * t));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Derivation for B Spline filter.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API BsplineFilter : public SpatialFilter<T>
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor which does nothing but pass values to the base class. The caller must still call Create().
|
||||
/// Support = 2.
|
||||
/// </summary>
|
||||
/// <param name="filterRadius">The filter radius</param>
|
||||
/// <param name="superSample">The supersample of the ember being rendered</param>
|
||||
/// <param name="pixelAspectRatio">The pixel aspect ratio being used to render. Default: 1.</param>
|
||||
BsplineFilter(T filterRadius, unsigned int superSample, T pixelAspectRatio = T(1.0))
|
||||
: SpatialFilter<T>(BSPLINE_SPATIAL_FILTER, T(2.0), filterRadius, superSample, pixelAspectRatio) { }
|
||||
|
||||
/// <summary>
|
||||
/// Apply B Spline filter to t parameter and return.
|
||||
/// </summary>
|
||||
/// <param name="t">The value to apply the filter to</param>
|
||||
/// <returns>The filtered value</returns>
|
||||
virtual T Filter(T t) const
|
||||
{
|
||||
//box (*) box (*) box (*) box.
|
||||
T tt;
|
||||
|
||||
if (t < 0)
|
||||
t = -t;
|
||||
|
||||
if (t < 1)
|
||||
{
|
||||
tt = t * t;
|
||||
return ((T(0.5) * tt * t) - tt + (T(2.0) / T(3.0)));
|
||||
}
|
||||
else if (t < 2)
|
||||
{
|
||||
t = 2 - t;
|
||||
return ((T(1.0) / T(6.0)) * (t * t * t));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Derivation for Lanczos 3 filter.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API Lanczos3Filter : public SpatialFilter<T>
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor which does nothing but pass values to the base class. The caller must still call Create().
|
||||
/// Support = 3.
|
||||
/// </summary>
|
||||
/// <param name="filterRadius">The filter radius</param>
|
||||
/// <param name="superSample">The supersample of the ember being rendered</param>
|
||||
/// <param name="pixelAspectRatio">The pixel aspect ratio being used to render. Default: 1.</param>
|
||||
Lanczos3Filter(T filterRadius, unsigned int superSample, T pixelAspectRatio = T(1.0))
|
||||
: SpatialFilter<T>(LANCZOS3_SPATIAL_FILTER, T(3.0), filterRadius, superSample, pixelAspectRatio) { }
|
||||
|
||||
/// <summary>
|
||||
/// Apply Lanczos 3 filter to t parameter and return.
|
||||
/// </summary>
|
||||
/// <param name="t">The value to apply the filter to</param>
|
||||
/// <returns>The filtered value</returns>
|
||||
virtual T Filter(T t) const
|
||||
{
|
||||
if (t < 0)
|
||||
t = -t;
|
||||
|
||||
if (t < 3)
|
||||
return Sinc(t) * Sinc(t / 3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Derivation for Lanczos 2 filter.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API Lanczos2Filter : public SpatialFilter<T>
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor which does nothing but pass values to the base class. The caller must still call Create().
|
||||
/// Support = 2.
|
||||
/// </summary>
|
||||
/// <param name="filterRadius">The filter radius</param>
|
||||
/// <param name="superSample">The supersample of the ember being rendered</param>
|
||||
/// <param name="pixelAspectRatio">The pixel aspect ratio being used to render. Default: 1.</param>
|
||||
Lanczos2Filter(T filterRadius, unsigned int superSample, T pixelAspectRatio = T(1.0))
|
||||
: SpatialFilter<T>(LANCZOS2_SPATIAL_FILTER, T(2.0), filterRadius, superSample, pixelAspectRatio) { }
|
||||
|
||||
/// <summary>
|
||||
/// Apply Lanczos 2 filter to t parameter and return.
|
||||
/// </summary>
|
||||
/// <param name="t">The value to apply the filter to</param>
|
||||
/// <returns>The filtered value</returns>
|
||||
virtual T Filter(T t) const
|
||||
{
|
||||
if (t < 0)
|
||||
t = -t;
|
||||
|
||||
if (t < 2)
|
||||
return Sinc(t) * Sinc(t / 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Derivation for Mitchell filter.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API MitchellFilter : public SpatialFilter<T>
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor which does nothing but pass values to the base class. The caller must still call Create().
|
||||
/// Support = 2.
|
||||
/// </summary>
|
||||
/// <param name="filterRadius">The filter radius</param>
|
||||
/// <param name="superSample">The supersample of the ember being rendered</param>
|
||||
/// <param name="pixelAspectRatio">The pixel aspect ratio being used to render. Default: 1.</param>
|
||||
MitchellFilter(T filterRadius, unsigned int superSample, T pixelAspectRatio = T(1.0))
|
||||
: SpatialFilter<T>(MITCHELL_SPATIAL_FILTER, T(2.0), filterRadius, superSample, pixelAspectRatio) { }
|
||||
|
||||
/// <summary>
|
||||
/// Apply Mitchell filter to t parameter and return.
|
||||
/// </summary>
|
||||
/// <param name="t">The value to apply the filter to</param>
|
||||
/// <returns>The filtered value</returns>
|
||||
virtual T Filter(T t) const
|
||||
{
|
||||
T tt = t * t;
|
||||
const T b = T(1) / T(3);
|
||||
const T c = T(1) / T(3);
|
||||
|
||||
if (t < 0)
|
||||
t = -t;
|
||||
|
||||
if (t < 1)
|
||||
{
|
||||
t = (((T(12.0) - T(9.0) * b - T(6.0) * c) * (t * tt))
|
||||
+ ((T(-18.0) + T(12.0) * b + T(6.0) * c) * tt)
|
||||
+ (T(6.0) - 2 * b));
|
||||
|
||||
return t / 6;
|
||||
}
|
||||
else if (t < 2)
|
||||
{
|
||||
t = (((T(-1.0) * b - T(6.0) * c) * (t * tt))
|
||||
+ ((T(6.0) * b + T(30.0) * c) * tt)
|
||||
+ ((T(-12.0) * b - T(48.0) * c) * t)
|
||||
+ (T(8.0) * b + 24 * c));
|
||||
|
||||
return t / 6;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Derivation for Blackman filter.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API BlackmanFilter : public SpatialFilter<T>
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor which does nothing but pass values to the base class. The caller must still call Create().
|
||||
/// Support = 1.
|
||||
/// </summary>
|
||||
/// <param name="filterRadius">The filter radius</param>
|
||||
/// <param name="superSample">The supersample of the ember being rendered</param>
|
||||
/// <param name="pixelAspectRatio">The pixel aspect ratio being used to render. Default: 1.</param>
|
||||
BlackmanFilter(T filterRadius, unsigned int superSample, T pixelAspectRatio = T(1.0))
|
||||
: SpatialFilter<T>(BLACKMAN_SPATIAL_FILTER, T(1.0), filterRadius, superSample, pixelAspectRatio) { }
|
||||
|
||||
/// <summary>
|
||||
/// Apply Blackman filter to t parameter and return.
|
||||
/// </summary>
|
||||
/// <param name="t">The value to apply the filter to</param>
|
||||
/// <returns>The filtered value</returns>
|
||||
virtual T Filter(T t) const
|
||||
{
|
||||
return (T(0.42) + T(0.5) * cos(T(M_PI) * t) + T(0.08) * cos(2 * T(M_PI) * t));
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Derivation for Catmull-Rom filter.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API CatromFilter : public SpatialFilter<T>
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor which does nothing but pass values to the base class. The caller must still call Create().
|
||||
/// Support = 2.
|
||||
/// </summary>
|
||||
/// <param name="filterRadius">The filter radius</param>
|
||||
/// <param name="superSample">The supersample of the ember being rendered</param>
|
||||
/// <param name="pixelAspectRatio">The pixel aspect ratio being used to render. Default: 1.</param>
|
||||
CatromFilter(T filterRadius, unsigned int superSample, T pixelAspectRatio = T(1.0))
|
||||
: SpatialFilter<T>(CATROM_SPATIAL_FILTER, T(2.0), filterRadius, superSample, pixelAspectRatio) { }
|
||||
|
||||
/// <summary>
|
||||
/// Apply Catmull-Rom filter to t parameter and return.
|
||||
/// </summary>
|
||||
/// <param name="t">The value to apply the filter to</param>
|
||||
/// <returns>The filtered value</returns>
|
||||
virtual T Filter(T t) const
|
||||
{
|
||||
if (t < 0)
|
||||
return 0;
|
||||
|
||||
if (t < -1)
|
||||
return T(0.5) * (4 + t * (8 + t * (5 + t)));
|
||||
|
||||
if (t < 0)
|
||||
return T(0.5) * (2 + t * t * (-5 - 3 * t));
|
||||
|
||||
if (t < 1)
|
||||
return T(0.5) * (2 + t * t * (-5 + 3 * t));
|
||||
|
||||
if (t < 2)
|
||||
return T(0.5) * (4 + t * (-8 + t * (5 - t)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Derivation for Hamming filter.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API HammingFilter : public SpatialFilter<T>
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor which does nothing but pass values to the base class. The caller must still call Create().
|
||||
/// Support = 1.
|
||||
/// </summary>
|
||||
/// <param name="filterRadius">The filter radius</param>
|
||||
/// <param name="superSample">The supersample of the ember being rendered</param>
|
||||
/// <param name="pixelAspectRatio">The pixel aspect ratio being used to render. Default: 1.</param>
|
||||
HammingFilter(T filterRadius, unsigned int superSample, T pixelAspectRatio = T(1.0))
|
||||
: SpatialFilter<T>(HAMMING_SPATIAL_FILTER, T(1.0), filterRadius, superSample, pixelAspectRatio) { }
|
||||
|
||||
/// <summary>
|
||||
/// Apply Hamming filter to t parameter and return.
|
||||
/// </summary>
|
||||
/// <param name="t">The value to apply the filter to</param>
|
||||
/// <returns>The filtered value</returns>
|
||||
virtual T Filter(T t) const
|
||||
{
|
||||
return T(0.54) + T(0.46) * cos(T(M_PI) * t);
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Derivation for Hanning filter.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API HanningFilter : public SpatialFilter<T>
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor which does nothing but pass values to the base class. The caller must still call Create().
|
||||
/// Support = 1.
|
||||
/// </summary>
|
||||
/// <param name="filterRadius">The filter radius</param>
|
||||
/// <param name="superSample">The supersample of the ember being rendered</param>
|
||||
/// <param name="pixelAspectRatio">The pixel aspect ratio being used to render. Default: 1.</param>
|
||||
HanningFilter(T filterRadius, unsigned int superSample, T pixelAspectRatio = T(1.0))
|
||||
: SpatialFilter<T>(HANNING_SPATIAL_FILTER, T(1.0), filterRadius, superSample, pixelAspectRatio) { }
|
||||
|
||||
/// <summary>
|
||||
/// Apply Hanning filter to t parameter and return.
|
||||
/// </summary>
|
||||
/// <param name="t">The value to apply the filter to</param>
|
||||
/// <returns>The filtered value</returns>
|
||||
virtual T Filter(T t) const
|
||||
{
|
||||
return T(0.5) + T(0.5) * cos(T(M_PI) * t);
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Derivation for Quadratic filter.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API QuadraticFilter : public SpatialFilter<T>
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor which does nothing but pass values to the base class. The caller must still call Create().
|
||||
/// Support = 1.5.
|
||||
/// </summary>
|
||||
/// <param name="filterRadius">The filter radius</param>
|
||||
/// <param name="superSample">The supersample of the ember being rendered</param>
|
||||
/// <param name="pixelAspectRatio">The pixel aspect ratio being used to render. Default: 1.</param>
|
||||
QuadraticFilter(T filterRadius, unsigned int superSample, T pixelAspectRatio = T(1.0))
|
||||
: SpatialFilter<T>(QUADRATIC_SPATIAL_FILTER, T(1.5), filterRadius, superSample, pixelAspectRatio) { }
|
||||
|
||||
/// <summary>
|
||||
/// Apply Quadratic filter to t parameter and return.
|
||||
/// </summary>
|
||||
/// <param name="t">The value to apply the filter to</param>
|
||||
/// <returns>The filtered value</returns>
|
||||
virtual T Filter(T t) const
|
||||
{
|
||||
if (t < -1.5)
|
||||
return 0.0;
|
||||
|
||||
if (t < -0.5)
|
||||
return T(0.5) * (t + T(1.5)) * (t + T(1.5));
|
||||
|
||||
if (t < 0.5)
|
||||
return T(0.75) - (t * t);
|
||||
|
||||
if (t < 1.5)
|
||||
return T(0.5) * (t - T(1.5)) * (t - T(1.5));
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Convenience class to assist in converting between filter names and the filter objects themselves.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API SpatialFilterCreator
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Creates the specified filter type based on the filterType enum parameter.
|
||||
/// </summary>
|
||||
/// <param name="filterType">Type of the filter</param>
|
||||
/// <param name="filterRadius">The filter radius</param>
|
||||
/// <param name="superSample">The supersample value of the ember using this filter to render</param>
|
||||
/// <param name="pixelAspectRatio">The aspect ratio of the ember using this filter to render. Default: 1.</param>
|
||||
/// <returns>A pointer to the newly created filter object</returns>
|
||||
static SpatialFilter<T>* Create(eSpatialFilterType filterType, T filterRadius, unsigned int superSample, T pixelAspectRatio = 1)
|
||||
{
|
||||
SpatialFilter<T>* filter = NULL;
|
||||
|
||||
if (filterType == GAUSSIAN_SPATIAL_FILTER)
|
||||
filter = new GaussianFilter<T>(filterRadius, superSample, pixelAspectRatio);
|
||||
else if (filterType == HERMITE_SPATIAL_FILTER)
|
||||
filter = new HermiteFilter<T>(filterRadius, superSample, pixelAspectRatio);
|
||||
else if (filterType == BOX_SPATIAL_FILTER)
|
||||
filter = new BoxFilter<T>(filterRadius, superSample, pixelAspectRatio);
|
||||
else if (filterType == TRIANGLE_SPATIAL_FILTER)
|
||||
filter = new TriangleFilter<T>(filterRadius, superSample, pixelAspectRatio);
|
||||
else if (filterType == BELL_SPATIAL_FILTER)
|
||||
filter = new BellFilter<T>(filterRadius, superSample, pixelAspectRatio);
|
||||
else if (filterType == BSPLINE_SPATIAL_FILTER)
|
||||
filter = new BsplineFilter<T>(filterRadius, superSample, pixelAspectRatio);
|
||||
else if (filterType == LANCZOS3_SPATIAL_FILTER)
|
||||
filter = new Lanczos3Filter<T>(filterRadius, superSample, pixelAspectRatio);
|
||||
else if (filterType == LANCZOS2_SPATIAL_FILTER)
|
||||
filter = new Lanczos2Filter<T>(filterRadius, superSample, pixelAspectRatio);
|
||||
else if (filterType == MITCHELL_SPATIAL_FILTER)
|
||||
filter = new MitchellFilter<T>(filterRadius, superSample, pixelAspectRatio);
|
||||
else if (filterType == BLACKMAN_SPATIAL_FILTER)
|
||||
filter = new BlackmanFilter<T>(filterRadius, superSample, pixelAspectRatio);
|
||||
else if (filterType == CATROM_SPATIAL_FILTER)
|
||||
filter = new CatromFilter<T>(filterRadius, superSample, pixelAspectRatio);
|
||||
else if (filterType == HAMMING_SPATIAL_FILTER)
|
||||
filter = new HammingFilter<T>(filterRadius, superSample, pixelAspectRatio);
|
||||
else if (filterType == HANNING_SPATIAL_FILTER)
|
||||
filter = new HanningFilter<T>(filterRadius, superSample, pixelAspectRatio);
|
||||
else if (filterType == QUADRATIC_SPATIAL_FILTER)
|
||||
filter = new QuadraticFilter<T>(filterRadius, superSample, pixelAspectRatio);
|
||||
else
|
||||
filter = new GaussianFilter<T>(filterRadius, superSample, pixelAspectRatio);
|
||||
|
||||
if (filter)
|
||||
filter->Create();
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a string vector of the available filter types.
|
||||
/// </summary>
|
||||
/// <returns>A vector of strings populated with the available filter types</returns>
|
||||
static vector<string> FilterTypes()
|
||||
{
|
||||
vector<string> v;
|
||||
|
||||
v.reserve(14);
|
||||
v.push_back("Gaussian");
|
||||
v.push_back("Hermite");
|
||||
v.push_back("Box");
|
||||
v.push_back("Triangle");
|
||||
v.push_back("Bell");
|
||||
v.push_back("Bspline");
|
||||
v.push_back("Lanczos3");
|
||||
v.push_back("Lanczos2");
|
||||
v.push_back("Mitchell");
|
||||
v.push_back("Blackman");
|
||||
v.push_back("Catrom");
|
||||
v.push_back("Hamming");
|
||||
v.push_back("Hanning");
|
||||
v.push_back("Quadratic");
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert between the filter name string and its type enum.
|
||||
/// </summary>
|
||||
/// <param name="filterType">The string name of the filter</param>
|
||||
/// <returns>The filter type enum</returns>
|
||||
static eSpatialFilterType FromString(string filterType)
|
||||
{
|
||||
if (!_stricmp(filterType.c_str(), "Gaussian"))
|
||||
return GAUSSIAN_SPATIAL_FILTER;
|
||||
else if (!_stricmp(filterType.c_str(), "Hermite"))
|
||||
return HERMITE_SPATIAL_FILTER;
|
||||
else if (!_stricmp(filterType.c_str(), "Box"))
|
||||
return BOX_SPATIAL_FILTER;
|
||||
else if (!_stricmp(filterType.c_str(), "Triangle"))
|
||||
return TRIANGLE_SPATIAL_FILTER;
|
||||
else if (!_stricmp(filterType.c_str(), "Bell"))
|
||||
return BELL_SPATIAL_FILTER;
|
||||
else if (!_stricmp(filterType.c_str(), "Bspline"))
|
||||
return BSPLINE_SPATIAL_FILTER;
|
||||
else if (!_stricmp(filterType.c_str(), "Lanczos3"))
|
||||
return LANCZOS3_SPATIAL_FILTER;
|
||||
else if (!_stricmp(filterType.c_str(), "Lanczos2"))
|
||||
return LANCZOS2_SPATIAL_FILTER;
|
||||
else if (!_stricmp(filterType.c_str(), "Mitchell"))
|
||||
return MITCHELL_SPATIAL_FILTER;
|
||||
else if (!_stricmp(filterType.c_str(), "Blackman"))
|
||||
return BLACKMAN_SPATIAL_FILTER;
|
||||
else if (!_stricmp(filterType.c_str(), "Catrom"))
|
||||
return CATROM_SPATIAL_FILTER;
|
||||
else if (!_stricmp(filterType.c_str(), "Hamming"))
|
||||
return HAMMING_SPATIAL_FILTER;
|
||||
else if (!_stricmp(filterType.c_str(), "Hanning"))
|
||||
return HANNING_SPATIAL_FILTER;
|
||||
else if (!_stricmp(filterType.c_str(), "Quadratic"))
|
||||
return QUADRATIC_SPATIAL_FILTER;
|
||||
else
|
||||
return GAUSSIAN_SPATIAL_FILTER;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert between the filter type enum and its name string.
|
||||
/// </summary>
|
||||
/// <param name="eTemporalFilterType">The filter type enum</param>
|
||||
/// <returns>The string name of the filter</returns>
|
||||
static string ToString(eSpatialFilterType filterType)
|
||||
{
|
||||
string filter;
|
||||
|
||||
if (filterType == GAUSSIAN_SPATIAL_FILTER)
|
||||
filter = "Gaussian";
|
||||
else if (filterType == HERMITE_SPATIAL_FILTER)
|
||||
filter = "Hermite";
|
||||
else if (filterType == BOX_SPATIAL_FILTER)
|
||||
filter = "Box";
|
||||
else if (filterType == TRIANGLE_SPATIAL_FILTER)
|
||||
filter = "Triangle";
|
||||
else if (filterType == BELL_SPATIAL_FILTER)
|
||||
filter = "Bell";
|
||||
else if (filterType == BSPLINE_SPATIAL_FILTER)
|
||||
filter = "Bspline";
|
||||
else if (filterType == LANCZOS3_SPATIAL_FILTER)
|
||||
filter = "Lanczos3";
|
||||
else if (filterType == LANCZOS2_SPATIAL_FILTER)
|
||||
filter = "Lanczos2";
|
||||
else if (filterType == MITCHELL_SPATIAL_FILTER)
|
||||
filter = "Mitchell";
|
||||
else if (filterType == BLACKMAN_SPATIAL_FILTER)
|
||||
filter = "Blackman";
|
||||
else if (filterType == CATROM_SPATIAL_FILTER)
|
||||
filter = "Catrom";
|
||||
else if (filterType == HAMMING_SPATIAL_FILTER)
|
||||
filter = "Hamming";
|
||||
else if (filterType == HANNING_SPATIAL_FILTER)
|
||||
filter = "Hanning";
|
||||
else if (filterType == QUADRATIC_SPATIAL_FILTER)
|
||||
filter = "Quadratic";
|
||||
else
|
||||
filter = "Gaussian";
|
||||
|
||||
return filter;
|
||||
}
|
||||
};
|
||||
}
|
347
Source/Ember/TemporalFilter.h
Normal file
347
Source/Ember/TemporalFilter.h
Normal file
@ -0,0 +1,347 @@
|
||||
#pragma once
|
||||
|
||||
#include "EmberDefines.h"
|
||||
|
||||
/// <summary>
|
||||
/// TemporalFilter base, derived and factory classes.
|
||||
/// </summary>
|
||||
|
||||
namespace EmberNs
|
||||
{
|
||||
/// <summary>
|
||||
/// The types of temporal filters available.
|
||||
/// </summary>
|
||||
enum eTemporalFilterType
|
||||
{
|
||||
BOX_TEMPORAL_FILTER = 0,
|
||||
GAUSSIAN_TEMPORAL_FILTER = 1,
|
||||
EXP_TEMPORAL_FILTER = 2
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Temporal filter is for doing motion blur while rendering a series of frames for animation.
|
||||
/// The filter created is used as a vector of scalar values to multiply the time value by in between embers.
|
||||
/// There are three possible types: Gaussian, Box and Exp.
|
||||
/// Template argument expected to be float or double.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API TemporalFilter
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor to set up basic filtering parameters, allocate buffers and calculate deltas.
|
||||
/// Derived class constructors will complete the final part of filter setup.
|
||||
/// </summary>
|
||||
/// <param name="filterType">Type of the filter.</param>
|
||||
/// <param name="passes">The number of passes used in the ember being rendered</param>
|
||||
/// <param name="temporalSamples">The number of temporal samples in the ember being rendered</param>
|
||||
/// <param name="filterWidth">The width of the filter.</param>
|
||||
TemporalFilter(eTemporalFilterType filterType, unsigned int passes, unsigned int temporalSamples, T filterWidth)
|
||||
{
|
||||
unsigned int i, steps = passes * temporalSamples;
|
||||
|
||||
m_Deltas.resize(steps);
|
||||
m_Filter.resize(steps);
|
||||
m_FilterType = filterType;
|
||||
|
||||
if (steps == 1)
|
||||
{
|
||||
m_SumFilt = 1;
|
||||
m_Deltas[0] = 0;
|
||||
m_Filter[0] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Define the temporal deltas.
|
||||
for (i = 0; i < steps; i++)
|
||||
m_Deltas[i] = (T(i) / T(steps - 1) - T(0.5)) * filterWidth;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy constructor.
|
||||
/// </summary>
|
||||
/// <param name="filter">The TemporalFilter object to copy</param>
|
||||
TemporalFilter(const TemporalFilter<T>& filter)
|
||||
{
|
||||
*this = filter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Virtual destructor so derived class destructors get called.
|
||||
/// </summary>
|
||||
virtual ~TemporalFilter()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator.
|
||||
/// </summary>
|
||||
/// <param name="filter">The TemporalFilter object to copy.</param>
|
||||
/// <returns>Reference to updated self</returns>
|
||||
TemporalFilter<T>& operator = (const TemporalFilter<T>& filter)
|
||||
{
|
||||
if (this != &filter)
|
||||
{
|
||||
m_SumFilt = filter.m_SumFilt;
|
||||
m_Deltas = filter.m_Deltas;
|
||||
m_Filter = filter.m_Filter;
|
||||
m_FilterType = filter.m_FilterType;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a string representation of this filter.
|
||||
/// </summary>
|
||||
/// <returns>The string representation of this filter</returns>
|
||||
string ToString() const
|
||||
{
|
||||
unsigned int i;
|
||||
stringstream ss;
|
||||
|
||||
ss << "Temporal Filter:" << endl
|
||||
<< " Size: " << Size() << endl
|
||||
<< " Type: " << TemporalFilterCreator<T>::ToString(m_FilterType) << endl
|
||||
<< " Sum Filt: " << SumFilt() << endl;
|
||||
|
||||
ss << "Deltas: " << endl;
|
||||
|
||||
for (i = 0; i < m_Deltas.size(); i++)
|
||||
{
|
||||
ss << "Deltas[" << i << "]: " << m_Deltas[i] << endl;
|
||||
}
|
||||
|
||||
ss << "Filter: " << endl;
|
||||
|
||||
for (i = 0; i < m_Filter.size(); i++)
|
||||
{
|
||||
ss << "Filter[" << i << "]: " << m_Filter[i] << endl;
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accessors.
|
||||
/// </summary>
|
||||
size_t Size() const { return m_Filter.size(); }
|
||||
T SumFilt() const { return m_SumFilt; }
|
||||
T* Deltas() { return &m_Deltas[0]; }
|
||||
T* Filter() { return &m_Filter[0]; }
|
||||
eTemporalFilterType FilterType() const { return m_FilterType; }
|
||||
|
||||
protected:
|
||||
/// <summary>
|
||||
/// Normalize the filter and the sum filt.
|
||||
/// </summary>
|
||||
/// <param name="maxFilt">The maximum filter value contained in the filter vector after it was created</param>
|
||||
void FinishFilter(T maxFilt)
|
||||
{
|
||||
m_SumFilt = 0;
|
||||
|
||||
for (unsigned int i = 0; i < Size(); i++)
|
||||
{
|
||||
m_Filter[i] /= maxFilt;
|
||||
m_SumFilt += m_Filter[i];
|
||||
}
|
||||
|
||||
m_SumFilt /= Size();
|
||||
}
|
||||
|
||||
T m_SumFilt;//The sum of all filter values.
|
||||
vector<T> m_Deltas;//Delta vector.
|
||||
vector<T> m_Filter;//Filter vector.
|
||||
eTemporalFilterType m_FilterType;//The type of filter this is.
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Derivation which implements the Exp filter.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API ExpTemporalFilter : public TemporalFilter<T>
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor to create an Exp filter.
|
||||
/// </summary>
|
||||
/// <param name="passes">The number of passes used in the ember being rendered</param>
|
||||
/// <param name="temporalSamples">The number of temporal samples in the ember being rendered</param>
|
||||
/// <param name="filterWidth">The width of the filter.</param>
|
||||
/// <param name="filterExp">The filter exp.</param>
|
||||
ExpTemporalFilter(unsigned int passes, unsigned int temporalSamples, T filterWidth, T filterExp)
|
||||
: TemporalFilter<T>(BOX_TEMPORAL_FILTER, passes, temporalSamples, filterWidth)
|
||||
{
|
||||
if (Size() > 1)
|
||||
{
|
||||
T slpx, maxFilt = 0;
|
||||
|
||||
for (unsigned int i = 0; i < Size(); i++)
|
||||
{
|
||||
if (filterExp >= 0)
|
||||
slpx = (T(i) + 1) / Size();
|
||||
else
|
||||
slpx = T(Size() - i) / Size();
|
||||
|
||||
//Scale the color based on these values.
|
||||
m_Filter[i] = pow(slpx, fabs(filterExp));
|
||||
|
||||
//Keep the max.
|
||||
if (m_Filter[i] > maxFilt)
|
||||
maxFilt = m_Filter[i];
|
||||
}
|
||||
|
||||
FinishFilter(maxFilt);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Derivation which implements the Gaussian filter.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API GaussianTemporalFilter : public TemporalFilter<T>
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor to create a Gaussian filter.
|
||||
/// </summary>
|
||||
/// <param name="passes">The number of passes used in the ember being rendered</param>
|
||||
/// <param name="temporalSamples">The number of temporal samples in the ember being rendered</param>
|
||||
/// <param name="filterWidth">The width of the filter.</param>
|
||||
GaussianTemporalFilter(unsigned int passes, unsigned int temporalSamples, T filterWidth)
|
||||
: TemporalFilter<T>(GAUSSIAN_TEMPORAL_FILTER, passes, temporalSamples, filterWidth)
|
||||
{
|
||||
if (Size() > 1)
|
||||
{
|
||||
T maxFilt = 0, halfSteps = T(Size()) / T(2);
|
||||
GaussianFilter<T> gaussian(1, 1);//Just pass dummy values, they are unused in this case.
|
||||
|
||||
for (unsigned int i = 0; i < Size(); i++)
|
||||
{
|
||||
m_Filter[i] = gaussian.Filter(gaussian.Support() * fabs(i - halfSteps) / halfSteps);
|
||||
|
||||
//Keep the max.
|
||||
if (m_Filter[i] > maxFilt)
|
||||
maxFilt = m_Filter[i];
|
||||
}
|
||||
|
||||
FinishFilter(maxFilt);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Derivation which implements the Box filter.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API BoxTemporalFilter : public TemporalFilter<T>
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor to create a Box filter.
|
||||
/// </summary>
|
||||
/// <param name="passes">The number of passes used in the ember being rendered</param>
|
||||
/// <param name="temporalSamples">The number of temporal samples in the ember being rendered</param>
|
||||
/// <param name="filterWidth">The width of the filter.</param>
|
||||
BoxTemporalFilter(unsigned int passes, unsigned int temporalSamples, T filterWidth)
|
||||
: TemporalFilter<T>(BOX_TEMPORAL_FILTER, passes, temporalSamples, filterWidth)
|
||||
{
|
||||
if (Size() > 1)
|
||||
{
|
||||
for (unsigned int i = 0; i < Size(); i++)
|
||||
m_Filter[i] = 1;
|
||||
|
||||
FinishFilter(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Convenience class to assist in converting between filter names and the filter objects themselves.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API TemporalFilterCreator
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Creates the specified filter type based on the filterType enum parameter.
|
||||
/// </summary>
|
||||
/// <param name="filterType">Type of the filter</param>
|
||||
/// <param name="passes">The number of passes used in the ember being rendered</param>
|
||||
/// <param name="temporalSamples">The number of temporal samples in the ember being rendered</param>
|
||||
/// <param name="filterWidth">The width of the filter</param>
|
||||
/// <param name="filterExp">The filter exp, only used with Exp filter, otherwise ignored.</param>
|
||||
/// <returns>A pointer to the newly created filter object</returns>
|
||||
static TemporalFilter<T>* Create(eTemporalFilterType filterType, unsigned int passes, unsigned int temporalSamples, T filterWidth, T filterExp = 1)
|
||||
{
|
||||
TemporalFilter<T>* filter = NULL;
|
||||
|
||||
if (filterType == BOX_TEMPORAL_FILTER)
|
||||
filter = new BoxTemporalFilter<T>(passes, temporalSamples, filterWidth);
|
||||
else if (filterType == GAUSSIAN_TEMPORAL_FILTER)
|
||||
filter = new GaussianTemporalFilter<T>(passes, temporalSamples, filterWidth);
|
||||
else if (filterType == EXP_TEMPORAL_FILTER)
|
||||
filter = new ExpTemporalFilter<T>(passes, temporalSamples, filterWidth, filterExp);
|
||||
else
|
||||
filter = new BoxTemporalFilter<T>(passes, temporalSamples, filterWidth);//Default to box if bad enum passed in.
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a string vector of the available filter types.
|
||||
/// </summary>
|
||||
/// <returns>A vector of strings populated with the available filter types</returns>
|
||||
static vector<string> FilterTypes()
|
||||
{
|
||||
vector<string> v;
|
||||
|
||||
v.reserve(3);
|
||||
v.push_back("Box");
|
||||
v.push_back("Gaussian");
|
||||
v.push_back("Exp");
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert between the filter name string and its type enum.
|
||||
/// </summary>
|
||||
/// <param name="filterType">The string name of the filter</param>
|
||||
/// <returns>The filter type enum</returns>
|
||||
static eTemporalFilterType FromString(string filterType)
|
||||
{
|
||||
if (!_stricmp(filterType.c_str(), "box"))
|
||||
return BOX_TEMPORAL_FILTER;
|
||||
else if (!_stricmp(filterType.c_str(), "gaussian"))
|
||||
return GAUSSIAN_TEMPORAL_FILTER;
|
||||
else if (!_stricmp(filterType.c_str(), "exp"))
|
||||
return EXP_TEMPORAL_FILTER;
|
||||
else
|
||||
return BOX_TEMPORAL_FILTER;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert between the filter type enum and its name string.
|
||||
/// </summary>
|
||||
/// <param name="eTemporalFilterType">The filter type enum</param>
|
||||
/// <returns>The string name of the filter</returns>
|
||||
static string ToString(eTemporalFilterType filterType)
|
||||
{
|
||||
string filter;
|
||||
|
||||
if (filterType == BOX_TEMPORAL_FILTER)
|
||||
filter = "Box";
|
||||
else if (filterType == GAUSSIAN_TEMPORAL_FILTER)
|
||||
filter = "Gaussian";
|
||||
else if (filterType == EXP_TEMPORAL_FILTER)
|
||||
filter = "Exp";
|
||||
else
|
||||
filter = "Box";
|
||||
|
||||
return filter;
|
||||
}
|
||||
};
|
||||
}
|
7
Source/Ember/Timing.cpp
Normal file
7
Source/Ember/Timing.cpp
Normal file
@ -0,0 +1,7 @@
|
||||
#include "stdafx.h"
|
||||
#include "Timing.h"
|
||||
|
||||
namespace Flam3
|
||||
{
|
||||
|
||||
}
|
231
Source/Ember/Timing.h
Normal file
231
Source/Ember/Timing.h
Normal file
@ -0,0 +1,231 @@
|
||||
#pragma once
|
||||
|
||||
#include "EmberDefines.h"
|
||||
|
||||
/// <summary>
|
||||
/// Timing and CriticalSection classes.
|
||||
/// </summary>
|
||||
|
||||
namespace EmberNs
|
||||
{
|
||||
/// <summary>
|
||||
/// Since the algorithm is so computationally intensive, timing and benchmarking are an integral portion
|
||||
/// of both the development process and the execution results. This class provides an easy way to time
|
||||
/// things by simply calling its Tic() and Toc() member functions. It also assists with formatting the
|
||||
/// elapsed time as a string.
|
||||
/// </summary>
|
||||
class EMBER_API Timing
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor that takes an optional precision argument which specifies how many digits after the decimal place should be printed for seconds.
|
||||
/// As a convenience, the Tic() function is called automatically.
|
||||
/// </summary>
|
||||
/// <param name="precision">The precision of the seconds field of the elapsed time. Default: 2.</param>
|
||||
Timing(int precision = 2)
|
||||
{
|
||||
m_Precision = precision;
|
||||
Init();
|
||||
Tic();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the begin time.
|
||||
/// </summary>
|
||||
/// <returns>The quad part of the begin time cast to a double</returns>
|
||||
double Tic()
|
||||
{
|
||||
QueryPerformanceCounter(&m_BeginTime);
|
||||
return BeginTime();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the end time and optionally output a string showing the elapsed time.
|
||||
/// </summary>
|
||||
/// <param name="str">The string to output. Default: NULL.</param>
|
||||
/// <param name="fullString">If true, output the string verbatim, else output the text " processing time: " in between str and the formatted time.</param>
|
||||
/// <returns>The elapsed time in milliseconds as a double</returns>
|
||||
double Toc(const char* str = NULL, bool fullString = false)
|
||||
{
|
||||
QueryPerformanceCounter(&m_EndTime);
|
||||
double ms = ElapsedTime();
|
||||
|
||||
if (str != NULL)
|
||||
{
|
||||
cout << string(str) << (fullString ? "" : " processing time: ") << Format(ms) << endl;
|
||||
}
|
||||
|
||||
return ms;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the quad part of the begin time as a double.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
double BeginTime() { return (double)m_BeginTime.QuadPart; }
|
||||
|
||||
/// <summary>
|
||||
/// Return the quad part of the end time as a double.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
double EndTime() { return (double)m_EndTime.QuadPart; }
|
||||
|
||||
/// <summary>
|
||||
/// Return the elapsed time in milliseconds.
|
||||
/// </summary>
|
||||
/// <returns>The elapsed time in milliseconds as a double</returns>
|
||||
double ElapsedTime() { return double(m_EndTime.QuadPart - m_BeginTime.QuadPart) * 1000.0 / double(m_Freq.QuadPart); }
|
||||
|
||||
/// <summary>
|
||||
/// Formats a specified milliseconds value as a string.
|
||||
/// This uses some intelligence to determine what to return depending on how much time has elapsed.
|
||||
/// Days, hours and minutes are only included if 1 or more of them has elapsed. Seconds are always
|
||||
/// included as a decimal value with the precision the user specified in the constructor.
|
||||
/// </summary>
|
||||
/// <param name="ms">The ms</param>
|
||||
/// <returns>The formatted string</returns>
|
||||
string Format(double ms)
|
||||
{
|
||||
stringstream ss;
|
||||
|
||||
double x = ms / 1000;
|
||||
double secs = fmod(x, 60);
|
||||
x /= 60;
|
||||
double mins = fmod(x, 60);
|
||||
x /= 60;
|
||||
double hours = fmod(x, 24);
|
||||
x /= 24;
|
||||
double days = x;
|
||||
|
||||
if (days >= 1)
|
||||
ss << (int)days << "d ";
|
||||
|
||||
if (hours >= 1)
|
||||
ss << (int)hours << "h ";
|
||||
|
||||
if (mins >= 1)
|
||||
ss << (int)mins << "m ";
|
||||
|
||||
ss << std::fixed << std::setprecision(m_Precision) << secs << "s";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the frequency of the clock as a double.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
static double Freq()
|
||||
{
|
||||
Init();
|
||||
return (double)m_Freq.QuadPart;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the number of cores in the system.
|
||||
/// </summary>
|
||||
/// <returns>The number of cores in the system</returns>
|
||||
static int ProcessorCount()
|
||||
{
|
||||
Init();
|
||||
return m_ProcessorCount;
|
||||
}
|
||||
|
||||
private:
|
||||
/// <summary>
|
||||
/// Query and store the performance info of the system.
|
||||
/// Since it will never change it only needs to be queried once.
|
||||
/// This is achieved by keeping static state and performance variables.
|
||||
/// </summary>
|
||||
static void Init()
|
||||
{
|
||||
if (!m_TimingInit)
|
||||
{
|
||||
SYSTEM_INFO sysinfo;
|
||||
|
||||
QueryPerformanceFrequency(&m_Freq);
|
||||
GetSystemInfo(&sysinfo);
|
||||
m_ProcessorCount = sysinfo.dwNumberOfProcessors;
|
||||
m_TimingInit = true;
|
||||
}
|
||||
}
|
||||
|
||||
int m_Precision;//How many digits after the decimal place to print for seconds.
|
||||
LARGE_INTEGER m_BeginTime;//The start of the timing, set with Tic().
|
||||
LARGE_INTEGER m_EndTime;//The end of the timing, set with Toc().
|
||||
static bool m_TimingInit;//Whether the performance info has bee queried.
|
||||
static int m_ProcessorCount;//The number of cores on the system, set in Init().
|
||||
static LARGE_INTEGER m_Freq;//The clock frequency, set in Init().
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Cross platform critical section class which can be used for thread locking.
|
||||
/// </summary>
|
||||
class EMBER_API CriticalSection
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor which initialized the underlying CRITICAL_SECTION object.
|
||||
/// </summary>
|
||||
CriticalSection() { InitializeCriticalSection(&m_CriticalSection); }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which initialized the underlying CRITICAL_SECTION object
|
||||
/// with the specified spin count value.
|
||||
/// </summary>
|
||||
/// <param name="spinCount">The spin count.</param>
|
||||
CriticalSection(DWORD spinCount) { InitializeCriticalSectionAndSpinCount(&m_CriticalSection, spinCount); }
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the underlying CRITICAL_SECTION object.
|
||||
/// </summary>
|
||||
~CriticalSection() { DeleteCriticalSection(&m_CriticalSection); }
|
||||
|
||||
/// <summary>
|
||||
/// Lock the critical section.
|
||||
/// </summary>
|
||||
void Enter() { EnterCriticalSection(&m_CriticalSection); }
|
||||
|
||||
/// <summary>
|
||||
/// Unlock the critical section.
|
||||
/// </summary>
|
||||
void Leave() { LeaveCriticalSection(&m_CriticalSection); }
|
||||
|
||||
private:
|
||||
CRITICAL_SECTION m_CriticalSection;//The Windows specific critical section object.
|
||||
|
||||
#else
|
||||
|
||||
/// <summary>
|
||||
/// Constructor which initialized the underlying pthread_mutex_t object.
|
||||
/// </summary>
|
||||
CriticalSection()
|
||||
{
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
|
||||
pthread_mutex_init(&m_CriticalSection, &attr);
|
||||
pthread_mutexattr_destroy(&attr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the underlying pthread_mutex_t object.
|
||||
/// </summary>
|
||||
~CriticalSection() { pthread_mutex_destroy(&m_CriticalSection); }
|
||||
|
||||
/// <summary>
|
||||
/// Lock the critical section.
|
||||
/// </summary>
|
||||
void Enter() { pthread_mutex_lock(&m_CriticalSection); }
|
||||
|
||||
/// <summary>
|
||||
/// Unlock the critical section.
|
||||
/// </summary>
|
||||
void Leave() { pthread_mutex_unlock(&m_CriticalSection); }
|
||||
|
||||
private:
|
||||
pthread_mutex_t m_CriticalSection;//The *nix/pthread specific critical section object.
|
||||
|
||||
#endif
|
||||
};
|
||||
}
|
887
Source/Ember/Utils.h
Normal file
887
Source/Ember/Utils.h
Normal file
@ -0,0 +1,887 @@
|
||||
#pragma once
|
||||
|
||||
#include "Isaac.h"
|
||||
|
||||
/// <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
|
||||
{
|
||||
/// <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>
|
||||
/// 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>
|
||||
/// 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() { 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(string 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(vector<string>& vec) { m_ErrorReport.insert(m_ErrorReport.end(), vec.begin(), vec.end()); }
|
||||
|
||||
/// <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(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(vector<string>& errorReport)
|
||||
{
|
||||
stringstream ss;
|
||||
|
||||
std::for_each(errorReport.begin() , errorReport.end() , [&](string s) { ss << s << endl; });
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
protected:
|
||||
vector<string> m_ErrorReport;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Open a file in binary mode and read its entire contents into a vector of unsigned chars. Optionally null terminate.
|
||||
/// </summary>
|
||||
/// <param name="filename">The full path to the file to read</param>
|
||||
/// <param name="buf">The vector which will be populated with the file's contents</param>
|
||||
/// <param name="nullTerminate">Whether to append a NULL character as the last element of the vector. Needed when reading text files. Default: true.</param>
|
||||
/// <returns>True if successfully read and populated, else false</returns>
|
||||
static bool ReadFile(const char* filename, string& buf, bool nullTerminate = true)
|
||||
{
|
||||
bool b = false;
|
||||
FILE* f;
|
||||
|
||||
try
|
||||
{
|
||||
fopen_s(&f, filename, "rb");//Open in binary mode.
|
||||
|
||||
if (f != NULL)
|
||||
{
|
||||
struct _stat statBuf;
|
||||
int statResult = _fstat(f->_file, &statBuf);//Get data associated with file.
|
||||
|
||||
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 NULL.
|
||||
|
||||
if (buf.size() == 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 == statBuf.st_size)//Ensure the number of bytes read matched what was requested.
|
||||
{
|
||||
if (nullTerminate)//Optionally NULL terminate if they want to treat it as a string.
|
||||
buf[buf.size() - 1] = NULL;
|
||||
|
||||
b = true;//Success.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if (f != NULL)
|
||||
fclose(f);
|
||||
|
||||
b = false;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="dest">The vector of type T to copy to</param>
|
||||
/// <param name="source">The vector of type U to copy from</param>
|
||||
template <typename T, typename U>
|
||||
void CopyVec(vector<T>& dest, const vector<U>& source)
|
||||
{
|
||||
dest.clear();
|
||||
dest.resize(source.size());
|
||||
|
||||
for (size_t i = 0; i < source.size(); i++)
|
||||
dest[i] = T(source[i]);//Valid assignment operator between T and U types must be defined somewhere.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear a vector of pointers to any type by checking each element for NULL 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>
|
||||
static void ClearVec(vector<T*>& vec, bool arrayDelete = false)
|
||||
{
|
||||
for (unsigned int i = 0; i < vec.size(); i++)
|
||||
{
|
||||
if (vec[i] != NULL)
|
||||
{
|
||||
if (arrayDelete)
|
||||
delete [] vec[i];
|
||||
else
|
||||
delete vec[i];
|
||||
}
|
||||
|
||||
vec[i] = NULL;
|
||||
}
|
||||
|
||||
vec.clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert an RGBA buffer to an RGB buffer.
|
||||
/// </summary>
|
||||
/// <param name="rgba">The RGBA buffer</param>
|
||||
/// <param name="rgb">The RGB buffer</param>
|
||||
/// <param name="width">The width of the image in pixels</param>
|
||||
/// <param name="height">The height of the image in pixels</param>
|
||||
static void RgbaToRgb(vector<unsigned char>& rgba, vector<unsigned char>& rgb, unsigned int width, unsigned int height)
|
||||
{
|
||||
rgb.resize(width * height * 3);
|
||||
|
||||
for (unsigned int i = 0, j = 0; i < (width * height * 4); i += 4, j += 3)
|
||||
{
|
||||
rgb[j] = rgba[i];
|
||||
rgb[j + 1] = rgba[i + 1];
|
||||
rgb[j + 2] = rgba[i + 2];
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
if (val < min)
|
||||
return min;
|
||||
else if (val > max)
|
||||
return max;
|
||||
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)
|
||||
{
|
||||
if (val < min)
|
||||
return min + fmod(val - min, max - min);
|
||||
else if (val > max)
|
||||
return max - fmod(max - val, max - 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)
|
||||
{
|
||||
if (val < min)
|
||||
val = min;
|
||||
else if (val > max)
|
||||
val = max;
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
if (val > lte)
|
||||
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)
|
||||
{
|
||||
return (val < gte) ? gte : 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)
|
||||
{
|
||||
if (val < gte)
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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>
|
||||
T Round(T r)
|
||||
{
|
||||
return (r > 0) ? (T)Floor<T>(r + T(0.5)) : ceil(r - T(0.5));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Special rounding for certain variations, gotten from Apophysis.
|
||||
/// </summary>
|
||||
/// <param name="x">The value to round</param>
|
||||
/// <returns>The rounded value</returns>
|
||||
inline float LRint(float x)
|
||||
{
|
||||
int temp = (x >= 0 ? (int)(x + 0.5f) : (int)(x - 0.5f));
|
||||
return (float)temp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Special rounding for certain variations, gotten from Apophysis.
|
||||
/// </summary>
|
||||
/// <param name="x">The value to round</param>
|
||||
/// <returns>The rounded value</returns>
|
||||
inline double LRint(double x)
|
||||
{
|
||||
__int64 temp = (x >= 0 ? (__int64)(x + 0.5) : (__int64)(x - 0.5));
|
||||
return (double)temp;
|
||||
}
|
||||
|
||||
/// <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 int Floor(T val)
|
||||
{
|
||||
if (val >= 0)
|
||||
{
|
||||
return (int)val;
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = (int)val;//Truncate.
|
||||
return i - (i > val);//Convert trunc to floor.
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
r *= 1e6;
|
||||
|
||||
if (r < 0)
|
||||
r -= 1;
|
||||
|
||||
return T(1e-6 * (int)(r + T(0.5)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return -1 if the value is less than 0, 1 if it's greater and
|
||||
/// 0 if it's equal to 0.
|
||||
/// </summary>
|
||||
/// <param name="v">The value to inspect</param>
|
||||
/// <returns>-1, 0 or 1</returns>
|
||||
template <typename T>
|
||||
static inline T Sign(T v)
|
||||
{
|
||||
return (v < 0) ? T(-1) : (v > 0) ? T(1) : T(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="v">The value to inspect</param>
|
||||
/// <returns>-1 or 1</returns>
|
||||
template <typename T>
|
||||
static inline T SignNz(T v)
|
||||
{
|
||||
return (v < 0) ? T(-1) : T(1);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
return t * t;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
static inline T SafeSqrt(T x)
|
||||
{
|
||||
if (x <= 0)
|
||||
return 0;
|
||||
|
||||
return sqrt(x);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
return t * t * t;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the hypotenuse of the passed in values.
|
||||
/// </summary>
|
||||
/// <param name="x">The x distance</param>
|
||||
/// <param name="y">The y distance</param>
|
||||
/// <returns>The hypotenuse</returns>
|
||||
template <typename T>
|
||||
static inline T Hypot(T x, T y)
|
||||
{
|
||||
return sqrt(SQR(x) + SQR(y));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spread the values.
|
||||
/// </summary>
|
||||
/// <param name="x">The x distance</param>
|
||||
/// <param name="y">The y distance</param>
|
||||
/// <returns>The spread</returns>
|
||||
template <typename T>
|
||||
static inline T Spread(T x, T y)
|
||||
{
|
||||
return Hypot<T>(x, y) * ((x) > 0 ? 1 : -1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsure.
|
||||
/// </summary>
|
||||
/// <param name="x">The x distance</param>
|
||||
/// <param name="y">The y distance</param>
|
||||
/// <returns>The powq4</returns>
|
||||
template <typename T>
|
||||
static inline T Powq4(T x, T y)
|
||||
{
|
||||
return pow(fabs(x), y) * SignNz(x);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsure.
|
||||
/// </summary>
|
||||
/// <param name="x">The x distance</param>
|
||||
/// <param name="y">The y distance</param>
|
||||
/// <returns>The powq4c</returns>
|
||||
template <typename T>
|
||||
static inline T Powq4c(T x, T y)
|
||||
{
|
||||
return y == 1 ? x : Powq4(x, y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return EPS6 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>EPS6 or the value if it was non-zero</returns>
|
||||
template <typename T>
|
||||
static inline T Zeps(T x)
|
||||
{
|
||||
return x == 0 ? EPS6 : 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)
|
||||
{
|
||||
return a + (b - a) * p;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Thin wrapper around a call to modf that discards the integer portion
|
||||
/// and returns the signed fractional portion.
|
||||
/// </summary>
|
||||
/// <param name="v">The value to retrieve the signed fractional portion of.</param>
|
||||
/// <returns>The signed fractional portion of v.</returns>
|
||||
template <typename T>
|
||||
static inline T Fabsmod(T v)
|
||||
{
|
||||
T dummy;
|
||||
|
||||
return modf(v, &dummy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsure.
|
||||
/// </summary>
|
||||
/// <param name="p">Unsure.</param>
|
||||
/// <param name="amp">Unsure.</param>
|
||||
/// <param name="ph">Unsure.</param>
|
||||
/// <returns>Unsure.</returns>
|
||||
template <typename T>
|
||||
static inline T Fosc(T p, T amp, T ph)
|
||||
{
|
||||
return T(0.5) - cos(p * amp + ph) * T(0.5);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsure.
|
||||
/// </summary>
|
||||
/// <param name="p">Unsure.</param>
|
||||
/// <param name="ph">Unsure.</param>
|
||||
/// <returns>Unsure.</returns>
|
||||
template <typename T>
|
||||
static inline T Foscn(T p, T ph)
|
||||
{
|
||||
return T(0.5) - cos(p + ph) * T(0.5);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log scale from Apophysis.
|
||||
/// </summary>
|
||||
/// <param name="x">The value to log scale</param>
|
||||
/// <returns>The log scaled value</returns>
|
||||
template <typename T>
|
||||
static inline T LogScale(T x)
|
||||
{
|
||||
return x == 0 ? 0 : log((fabs(x) + 1) * T(M_E)) * SignNz(x) / T(M_E);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log map from Apophysis.
|
||||
/// </summary>
|
||||
/// <param name="x">The value to log map</param>
|
||||
/// <returns>The log mapped value</returns>
|
||||
template <typename T>
|
||||
static inline T LogMap(T x)
|
||||
{
|
||||
return x == 0 ? 0 : (T(M_E) + log(x * T(M_E))) * T(0.25) * SignNz(x);
|
||||
}
|
||||
|
||||
/// <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, 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)
|
||||
{
|
||||
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 bool IsClose(T val1, T val2, T tolerance = 1e-6)
|
||||
{
|
||||
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)
|
||||
{
|
||||
angle = fmod(angle, 360);
|
||||
|
||||
if (angle > 180)
|
||||
{
|
||||
angle -= 360;
|
||||
}
|
||||
else if (angle < -180)
|
||||
{
|
||||
angle += 360;
|
||||
}
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
if (angle > 360 || angle < -360)
|
||||
angle = fmod(angle, 360);
|
||||
|
||||
if (angle < 0)
|
||||
angle += 360;
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
/// <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 inline string ToLower(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 inline string ToUpper(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 inline string Trim(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>
|
||||
/// 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 T Arg(char* name, T def)
|
||||
{
|
||||
T t;
|
||||
return t;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Template specialization for Arg<>() with a type of int.
|
||||
/// </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 int Arg<int>(char* name, int def)
|
||||
{
|
||||
char* ch;
|
||||
int returnVal;
|
||||
size_t len;
|
||||
errno_t err = _dupenv_s(&ch, &len, name);
|
||||
|
||||
if (err || !ch)
|
||||
returnVal = def;
|
||||
else
|
||||
returnVal = atoi(ch);
|
||||
|
||||
free(ch);
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Template specialization for Arg<>() with a type of unsigned int.
|
||||
/// </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 unsigned int Arg<unsigned int>(char* name, unsigned int def)
|
||||
{
|
||||
return Arg<int>(name, (int)def);
|
||||
}
|
||||
|
||||
/// <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 double.
|
||||
/// </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 double Arg<double>(char* name, double def)
|
||||
{
|
||||
char* ch;
|
||||
double returnVal;
|
||||
size_t len;
|
||||
errno_t err = _dupenv_s(&ch, &len, name);
|
||||
|
||||
if (err || !ch)
|
||||
returnVal = def;
|
||||
else
|
||||
returnVal = atof(ch);
|
||||
|
||||
free(ch);
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
/// <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;
|
||||
size_t len;
|
||||
errno_t err = _dupenv_s(&ch, &len, name);
|
||||
|
||||
if (err || !ch)
|
||||
{
|
||||
if (def != "")
|
||||
returnVal = def;
|
||||
}
|
||||
else
|
||||
returnVal = string(ch);
|
||||
|
||||
free(ch);
|
||||
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>
|
||||
unsigned int inline FindAndReplace(T& source, const T& find, const T& replace)
|
||||
{
|
||||
unsigned int 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>
|
||||
/// Return a character pointer to a version string composed of the EMBER_OS and EMBER_VERSION values.
|
||||
/// </summary>
|
||||
static char* EmberVersion()
|
||||
{
|
||||
return EMBER_OS "-" EMBER_VERSION;
|
||||
}
|
||||
}
|
2168
Source/Ember/Variation.h
Normal file
2168
Source/Ember/Variation.h
Normal file
File diff suppressed because it is too large
Load Diff
537
Source/Ember/VariationList.h
Normal file
537
Source/Ember/VariationList.h
Normal file
@ -0,0 +1,537 @@
|
||||
#pragma once
|
||||
|
||||
#include "Variations01.h"
|
||||
#include "Variations02.h"
|
||||
#include "Variations03.h"
|
||||
#include "Variations04.h"
|
||||
#include "Variations05.h"
|
||||
#include "VariationsDC.h"
|
||||
|
||||
/// <summary>
|
||||
/// VariationList class.
|
||||
/// </summary>
|
||||
|
||||
namespace EmberNs
|
||||
{
|
||||
/// <summary>
|
||||
/// Since the list of variations is numerous, it's convenient to be able to make copies
|
||||
/// of specific ones. This class holds a list of pointers to variation objects for every
|
||||
/// variation available. Similar to the PaletteList class, a caller can look up a variation
|
||||
/// by name or ID and retrieve a copy of it.
|
||||
/// All variations are deleted upon destruction.
|
||||
/// Template argument expected to be float or double.
|
||||
/// </summary>
|
||||
template <typename T>
|
||||
class EMBER_API VariationList
|
||||
{
|
||||
#define ADDPREPOSTREGVAR(varName) \
|
||||
m_Variations.push_back(new varName##Variation<T>()); \
|
||||
m_Variations.push_back(new Pre##varName##Variation<T>()); \
|
||||
m_Variations.push_back(new Post##varName##Variation<T>());
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Constructor which initializes all of the variation objects and stores them in the list.
|
||||
/// </summary>
|
||||
VariationList()
|
||||
{
|
||||
m_Variations.reserve(900);//Change this as the list grows.
|
||||
ADDPREPOSTREGVAR(Linear)
|
||||
ADDPREPOSTREGVAR(Sinusoidal)
|
||||
ADDPREPOSTREGVAR(Spherical)
|
||||
ADDPREPOSTREGVAR(Swirl)
|
||||
ADDPREPOSTREGVAR(Horseshoe)
|
||||
ADDPREPOSTREGVAR(Polar)
|
||||
ADDPREPOSTREGVAR(Handkerchief)
|
||||
ADDPREPOSTREGVAR(Heart)
|
||||
ADDPREPOSTREGVAR(Disc)
|
||||
ADDPREPOSTREGVAR(Spiral)
|
||||
ADDPREPOSTREGVAR(Hyperbolic)
|
||||
ADDPREPOSTREGVAR(Diamond)
|
||||
ADDPREPOSTREGVAR(Ex)
|
||||
ADDPREPOSTREGVAR(Julia)
|
||||
ADDPREPOSTREGVAR(Bent)
|
||||
ADDPREPOSTREGVAR(Waves)
|
||||
ADDPREPOSTREGVAR(Fisheye)
|
||||
ADDPREPOSTREGVAR(Popcorn)
|
||||
ADDPREPOSTREGVAR(Exponential)
|
||||
ADDPREPOSTREGVAR(Power)
|
||||
ADDPREPOSTREGVAR(Cosine)
|
||||
ADDPREPOSTREGVAR(Rings)
|
||||
ADDPREPOSTREGVAR(Fan)
|
||||
ADDPREPOSTREGVAR(Blob)
|
||||
ADDPREPOSTREGVAR(Pdj)
|
||||
ADDPREPOSTREGVAR(Fan2)
|
||||
ADDPREPOSTREGVAR(Rings2)
|
||||
ADDPREPOSTREGVAR(Eyefish)
|
||||
ADDPREPOSTREGVAR(Bubble)
|
||||
ADDPREPOSTREGVAR(Cylinder)
|
||||
ADDPREPOSTREGVAR(Perspective)
|
||||
ADDPREPOSTREGVAR(Noise)
|
||||
ADDPREPOSTREGVAR(JuliaNGeneric)
|
||||
ADDPREPOSTREGVAR(JuliaScope)
|
||||
ADDPREPOSTREGVAR(Blur)
|
||||
ADDPREPOSTREGVAR(GaussianBlur)
|
||||
ADDPREPOSTREGVAR(RadialBlur)
|
||||
ADDPREPOSTREGVAR(Pie)
|
||||
ADDPREPOSTREGVAR(Ngon)
|
||||
ADDPREPOSTREGVAR(Curl)
|
||||
ADDPREPOSTREGVAR(Rectangles)
|
||||
ADDPREPOSTREGVAR(Arch)
|
||||
ADDPREPOSTREGVAR(Tangent)
|
||||
ADDPREPOSTREGVAR(Square)
|
||||
ADDPREPOSTREGVAR(Rays)
|
||||
ADDPREPOSTREGVAR(Blade)
|
||||
ADDPREPOSTREGVAR(Secant2)
|
||||
ADDPREPOSTREGVAR(TwinTrian)
|
||||
ADDPREPOSTREGVAR(Cross)
|
||||
ADDPREPOSTREGVAR(Disc2)
|
||||
ADDPREPOSTREGVAR(SuperShape)
|
||||
ADDPREPOSTREGVAR(Flower)
|
||||
ADDPREPOSTREGVAR(Conic)
|
||||
ADDPREPOSTREGVAR(Parabola)
|
||||
ADDPREPOSTREGVAR(Bent2)
|
||||
ADDPREPOSTREGVAR(Bipolar)
|
||||
ADDPREPOSTREGVAR(Boarders)
|
||||
ADDPREPOSTREGVAR(Butterfly)
|
||||
ADDPREPOSTREGVAR(Cell)
|
||||
ADDPREPOSTREGVAR(Cpow)
|
||||
ADDPREPOSTREGVAR(Curve)
|
||||
ADDPREPOSTREGVAR(Edisc)
|
||||
ADDPREPOSTREGVAR(Elliptic)
|
||||
ADDPREPOSTREGVAR(Escher)
|
||||
ADDPREPOSTREGVAR(Foci)
|
||||
ADDPREPOSTREGVAR(LazySusan)
|
||||
ADDPREPOSTREGVAR(Loonie)
|
||||
ADDPREPOSTREGVAR(Modulus)
|
||||
ADDPREPOSTREGVAR(Oscilloscope)
|
||||
ADDPREPOSTREGVAR(Polar2)
|
||||
ADDPREPOSTREGVAR(Popcorn2)
|
||||
ADDPREPOSTREGVAR(Scry)
|
||||
ADDPREPOSTREGVAR(Separation)
|
||||
ADDPREPOSTREGVAR(Split)
|
||||
ADDPREPOSTREGVAR(Splits)
|
||||
ADDPREPOSTREGVAR(Stripes)
|
||||
ADDPREPOSTREGVAR(Wedge)
|
||||
ADDPREPOSTREGVAR(WedgeJulia)
|
||||
ADDPREPOSTREGVAR(WedgeSph)
|
||||
ADDPREPOSTREGVAR(Whorl)
|
||||
ADDPREPOSTREGVAR(Waves2)
|
||||
ADDPREPOSTREGVAR(Exp)
|
||||
ADDPREPOSTREGVAR(Log)
|
||||
ADDPREPOSTREGVAR(Sin)
|
||||
ADDPREPOSTREGVAR(Cos)
|
||||
ADDPREPOSTREGVAR(Tan)
|
||||
ADDPREPOSTREGVAR(Sec)
|
||||
ADDPREPOSTREGVAR(Csc)
|
||||
ADDPREPOSTREGVAR(Cot)
|
||||
ADDPREPOSTREGVAR(Sinh)
|
||||
ADDPREPOSTREGVAR(Cosh)
|
||||
ADDPREPOSTREGVAR(Tanh)
|
||||
ADDPREPOSTREGVAR(Sech)
|
||||
ADDPREPOSTREGVAR(Csch)
|
||||
ADDPREPOSTREGVAR(Coth)
|
||||
ADDPREPOSTREGVAR(Auger)
|
||||
ADDPREPOSTREGVAR(Flux)
|
||||
ADDPREPOSTREGVAR(Hemisphere)
|
||||
ADDPREPOSTREGVAR(Epispiral)
|
||||
ADDPREPOSTREGVAR(Bwraps)
|
||||
ADDPREPOSTREGVAR(BlurCircle)
|
||||
ADDPREPOSTREGVAR(BlurZoom)
|
||||
ADDPREPOSTREGVAR(BlurPixelize)
|
||||
ADDPREPOSTREGVAR(Crop)
|
||||
ADDPREPOSTREGVAR(BCircle)
|
||||
ADDPREPOSTREGVAR(BlurLinear)
|
||||
ADDPREPOSTREGVAR(BlurSquare)
|
||||
ADDPREPOSTREGVAR(Boarders2)
|
||||
ADDPREPOSTREGVAR(Cardioid)
|
||||
ADDPREPOSTREGVAR(Checks)
|
||||
ADDPREPOSTREGVAR(Circlize)
|
||||
ADDPREPOSTREGVAR(Circlize2)
|
||||
ADDPREPOSTREGVAR(CosWrap)
|
||||
ADDPREPOSTREGVAR(DeltaA)
|
||||
ADDPREPOSTREGVAR(Expo)
|
||||
ADDPREPOSTREGVAR(Extrude)
|
||||
ADDPREPOSTREGVAR(FDisc)
|
||||
ADDPREPOSTREGVAR(Fibonacci)
|
||||
ADDPREPOSTREGVAR(Fibonacci2)
|
||||
ADDPREPOSTREGVAR(Glynnia)
|
||||
ADDPREPOSTREGVAR(GridOut)
|
||||
ADDPREPOSTREGVAR(Hole)
|
||||
ADDPREPOSTREGVAR(Hypertile)
|
||||
ADDPREPOSTREGVAR(Hypertile1)
|
||||
ADDPREPOSTREGVAR(Hypertile2)
|
||||
ADDPREPOSTREGVAR(Hypertile3D)
|
||||
ADDPREPOSTREGVAR(Hypertile3D1)
|
||||
ADDPREPOSTREGVAR(Hypertile3D2)
|
||||
ADDPREPOSTREGVAR(IDisc)
|
||||
ADDPREPOSTREGVAR(Julian2)
|
||||
ADDPREPOSTREGVAR(JuliaQ)
|
||||
ADDPREPOSTREGVAR(Murl)
|
||||
ADDPREPOSTREGVAR(Murl2)
|
||||
ADDPREPOSTREGVAR(NPolar)
|
||||
ADDPREPOSTREGVAR(Ortho)
|
||||
ADDPREPOSTREGVAR(Poincare)
|
||||
ADDPREPOSTREGVAR(Poincare3D)
|
||||
ADDPREPOSTREGVAR(Polynomial)
|
||||
ADDPREPOSTREGVAR(PSphere)
|
||||
ADDPREPOSTREGVAR(Rational3)
|
||||
ADDPREPOSTREGVAR(Ripple)
|
||||
ADDPREPOSTREGVAR(Sigmoid)
|
||||
ADDPREPOSTREGVAR(SinusGrid)
|
||||
ADDPREPOSTREGVAR(Stwin)
|
||||
ADDPREPOSTREGVAR(TwoFace)
|
||||
ADDPREPOSTREGVAR(Unpolar)
|
||||
ADDPREPOSTREGVAR(WavesN)
|
||||
ADDPREPOSTREGVAR(XHeart)
|
||||
ADDPREPOSTREGVAR(Barycentroid)
|
||||
ADDPREPOSTREGVAR(BiSplit)
|
||||
ADDPREPOSTREGVAR(Crescents)
|
||||
ADDPREPOSTREGVAR(Mask)
|
||||
ADDPREPOSTREGVAR(Cpow2)
|
||||
ADDPREPOSTREGVAR(Curl3D)
|
||||
ADDPREPOSTREGVAR(Disc3D)
|
||||
ADDPREPOSTREGVAR(Funnel)
|
||||
ADDPREPOSTREGVAR(Linear3D)
|
||||
ADDPREPOSTREGVAR(PowBlock)
|
||||
ADDPREPOSTREGVAR(Squirrel)
|
||||
ADDPREPOSTREGVAR(Ennepers)
|
||||
ADDPREPOSTREGVAR(SphericalN)
|
||||
ADDPREPOSTREGVAR(Kaleidoscope)
|
||||
ADDPREPOSTREGVAR(GlynnSim1)
|
||||
ADDPREPOSTREGVAR(GlynnSim2)
|
||||
ADDPREPOSTREGVAR(GlynnSim3)
|
||||
ADDPREPOSTREGVAR(Starblur)
|
||||
ADDPREPOSTREGVAR(Sineblur)
|
||||
ADDPREPOSTREGVAR(Circleblur)
|
||||
ADDPREPOSTREGVAR(CropN)
|
||||
ADDPREPOSTREGVAR(ShredRad)
|
||||
ADDPREPOSTREGVAR(Blob2)
|
||||
ADDPREPOSTREGVAR(Julia3D)
|
||||
ADDPREPOSTREGVAR(Julia3Dz)
|
||||
ADDPREPOSTREGVAR(LinearT)
|
||||
ADDPREPOSTREGVAR(LinearT3D)
|
||||
ADDPREPOSTREGVAR(Ovoid)
|
||||
ADDPREPOSTREGVAR(Ovoid3D)
|
||||
ADDPREPOSTREGVAR(Spirograph)
|
||||
ADDPREPOSTREGVAR(Petal)
|
||||
ADDPREPOSTREGVAR(RoundSpher)
|
||||
ADDPREPOSTREGVAR(RoundSpher3D)
|
||||
ADDPREPOSTREGVAR(SpiralWing)
|
||||
ADDPREPOSTREGVAR(Squarize)
|
||||
ADDPREPOSTREGVAR(Sschecks)
|
||||
ADDPREPOSTREGVAR(PhoenixJulia)
|
||||
ADDPREPOSTREGVAR(Mobius)
|
||||
ADDPREPOSTREGVAR(MobiusN)
|
||||
ADDPREPOSTREGVAR(MobiusStrip)
|
||||
ADDPREPOSTREGVAR(Lissajous)
|
||||
ADDPREPOSTREGVAR(Svf)
|
||||
ADDPREPOSTREGVAR(Target)
|
||||
ADDPREPOSTREGVAR(Taurus)
|
||||
ADDPREPOSTREGVAR(Collideoscope)
|
||||
ADDPREPOSTREGVAR(BMod)
|
||||
ADDPREPOSTREGVAR(BSwirl)
|
||||
ADDPREPOSTREGVAR(BTransform)
|
||||
ADDPREPOSTREGVAR(BCollide)
|
||||
ADDPREPOSTREGVAR(Eclipse)
|
||||
ADDPREPOSTREGVAR(FlipCircle)
|
||||
ADDPREPOSTREGVAR(FlipY)
|
||||
ADDPREPOSTREGVAR(ECollide)
|
||||
ADDPREPOSTREGVAR(EJulia)
|
||||
ADDPREPOSTREGVAR(EMod)
|
||||
ADDPREPOSTREGVAR(EMotion)
|
||||
ADDPREPOSTREGVAR(EPush)
|
||||
ADDPREPOSTREGVAR(ERotate)
|
||||
ADDPREPOSTREGVAR(EScale)
|
||||
ADDPREPOSTREGVAR(ESwirl)
|
||||
ADDPREPOSTREGVAR(LazyTravis)
|
||||
ADDPREPOSTREGVAR(Squish)
|
||||
ADDPREPOSTREGVAR(Circus)
|
||||
ADDPREPOSTREGVAR(Tancos)
|
||||
ADDPREPOSTREGVAR(Rippled)
|
||||
ADDPREPOSTREGVAR(RotateX)
|
||||
ADDPREPOSTREGVAR(RotateY)
|
||||
ADDPREPOSTREGVAR(RotateZ)
|
||||
ADDPREPOSTREGVAR(Flatten)
|
||||
ADDPREPOSTREGVAR(Zblur)
|
||||
ADDPREPOSTREGVAR(Blur3D)
|
||||
ADDPREPOSTREGVAR(ZScale)
|
||||
ADDPREPOSTREGVAR(ZTranslate)
|
||||
ADDPREPOSTREGVAR(ZCone)
|
||||
ADDPREPOSTREGVAR(MirrorX)
|
||||
ADDPREPOSTREGVAR(MirrorY)
|
||||
ADDPREPOSTREGVAR(MirrorZ)
|
||||
ADDPREPOSTREGVAR(Depth)
|
||||
ADDPREPOSTREGVAR(Spherical3D)
|
||||
ADDPREPOSTREGVAR(RBlur)
|
||||
ADDPREPOSTREGVAR(JuliaNab)
|
||||
ADDPREPOSTREGVAR(Sintrange)
|
||||
ADDPREPOSTREGVAR(Voron)
|
||||
ADDPREPOSTREGVAR(Waffle)
|
||||
ADDPREPOSTREGVAR(Square3D)
|
||||
ADDPREPOSTREGVAR(SuperShape3D)
|
||||
ADDPREPOSTREGVAR(Sphyp3D)
|
||||
ADDPREPOSTREGVAR(Circlecrop)
|
||||
ADDPREPOSTREGVAR(Julian3Dx)
|
||||
ADDPREPOSTREGVAR(Fourth)
|
||||
ADDPREPOSTREGVAR(Mobiq)
|
||||
ADDPREPOSTREGVAR(Spherivoid)
|
||||
ADDPREPOSTREGVAR(Farblur)
|
||||
ADDPREPOSTREGVAR(CurlSP)
|
||||
ADDPREPOSTREGVAR(Heat)
|
||||
ADDPREPOSTREGVAR(Interference2)
|
||||
ADDPREPOSTREGVAR(Sinq)
|
||||
ADDPREPOSTREGVAR(Sinhq)
|
||||
ADDPREPOSTREGVAR(Secq)
|
||||
ADDPREPOSTREGVAR(Sechq)
|
||||
ADDPREPOSTREGVAR(Tanq)
|
||||
ADDPREPOSTREGVAR(Tanhq)
|
||||
ADDPREPOSTREGVAR(Cosq)
|
||||
ADDPREPOSTREGVAR(Coshq)
|
||||
ADDPREPOSTREGVAR(Cotq)
|
||||
ADDPREPOSTREGVAR(Cothq)
|
||||
ADDPREPOSTREGVAR(Cscq)
|
||||
ADDPREPOSTREGVAR(Cschq)
|
||||
ADDPREPOSTREGVAR(Estiq)
|
||||
ADDPREPOSTREGVAR(Loq)
|
||||
ADDPREPOSTREGVAR(Curvature)
|
||||
ADDPREPOSTREGVAR(Qode)
|
||||
ADDPREPOSTREGVAR(BlurHeart)
|
||||
ADDPREPOSTREGVAR(Truchet)
|
||||
ADDPREPOSTREGVAR(Gdoffs)
|
||||
ADDPREPOSTREGVAR(Octagon)
|
||||
ADDPREPOSTREGVAR(Trade)
|
||||
ADDPREPOSTREGVAR(Juliac)
|
||||
ADDPREPOSTREGVAR(Blade3D)
|
||||
ADDPREPOSTREGVAR(Blob3D)
|
||||
ADDPREPOSTREGVAR(Blocky)
|
||||
ADDPREPOSTREGVAR(Bubble2)
|
||||
ADDPREPOSTREGVAR(CircleLinear)
|
||||
ADDPREPOSTREGVAR(CircleRand)
|
||||
ADDPREPOSTREGVAR(CircleTrans1)
|
||||
ADDPREPOSTREGVAR(Cubic3D)
|
||||
ADDPREPOSTREGVAR(CubicLattice3D)
|
||||
ADDPREPOSTREGVAR(Foci3D)
|
||||
ADDPREPOSTREGVAR(Ho)
|
||||
ADDPREPOSTREGVAR(Julia3Dq)
|
||||
ADDPREPOSTREGVAR(Line)
|
||||
ADDPREPOSTREGVAR(Loonie3D)
|
||||
ADDPREPOSTREGVAR(Mcarpet)
|
||||
ADDPREPOSTREGVAR(Waves23D)
|
||||
ADDPREPOSTREGVAR(Pie3D)
|
||||
ADDPREPOSTREGVAR(Popcorn23D)
|
||||
ADDPREPOSTREGVAR(Sinusoidal3D)
|
||||
ADDPREPOSTREGVAR(Scry3D)
|
||||
ADDPREPOSTREGVAR(Shredlin)
|
||||
ADDPREPOSTREGVAR(SplitBrdr)
|
||||
ADDPREPOSTREGVAR(Wdisc)
|
||||
ADDPREPOSTREGVAR(Falloff)
|
||||
ADDPREPOSTREGVAR(Falloff2)
|
||||
ADDPREPOSTREGVAR(Falloff3)
|
||||
ADDPREPOSTREGVAR(Xtrb)
|
||||
//ADDPREPOSTREGVAR(LinearXZ)
|
||||
//ADDPREPOSTREGVAR(LinearYZ)
|
||||
|
||||
//DC are special.
|
||||
m_Variations.push_back(new DCBubbleVariation<T>());
|
||||
ADDPREPOSTREGVAR(DCCarpet)
|
||||
ADDPREPOSTREGVAR(DCCube)
|
||||
m_Variations.push_back(new DCCylinderVariation<T>());
|
||||
ADDPREPOSTREGVAR(DCGridOut)
|
||||
m_Variations.push_back(new DCLinearVariation<T>());
|
||||
ADDPREPOSTREGVAR(DCTriangle)
|
||||
ADDPREPOSTREGVAR(DCZTransl)
|
||||
|
||||
std::for_each(m_Variations.begin(), m_Variations.end(), [&](Variation<T>* var) { var->Precalc(); });
|
||||
std::sort(m_Variations.begin(), m_Variations.end(), [&](const Variation<T>* var1, const Variation<T>* var2) { return var1->VariationId() < var2->VariationId(); });
|
||||
|
||||
m_RegVariations.reserve(m_Variations.size() / 3);
|
||||
m_PreVariations.reserve(m_Variations.size() / 3);
|
||||
m_PostVariations.reserve(m_Variations.size() / 3);
|
||||
|
||||
std::for_each(m_Variations.begin(), m_Variations.end(), [&](Variation<T>* var) { if (var->VarType() == VARTYPE_REG) m_RegVariations.push_back(var); });
|
||||
std::for_each(m_Variations.begin(), m_Variations.end(), [&](Variation<T>* var) { if (var->VarType() == VARTYPE_PRE) m_PreVariations.push_back(var); });
|
||||
std::for_each(m_Variations.begin(), m_Variations.end(), [&](Variation<T>* var) { if (var->VarType() == VARTYPE_POST) m_PostVariations.push_back(var); });
|
||||
|
||||
//Keep a list of which variations derive from ParametricVariation.
|
||||
//Note that these are not new copies, rather just pointers to the original instances in m_Variations.
|
||||
for (unsigned int i = 0; i < m_Variations.size(); i++)
|
||||
{
|
||||
if (ParametricVariation<T>* parVar = dynamic_cast<ParametricVariation<T>*>(m_Variations[i]))
|
||||
m_ParametricVariations.push_back(parVar);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete each element of the list.
|
||||
/// </summary>
|
||||
~VariationList()
|
||||
{
|
||||
ClearVec(m_Variations);//No need to delete parametric because they point to the entries in original vector.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a pointer to the variation at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="index">The index in the list to retrieve</param>
|
||||
/// <returns>A pointer to the variation at the index if in range, else NULL.</returns>
|
||||
Variation<T>* GetVariation(size_t index) { return index < m_Variations.size() ? m_Variations[index] : NULL; }
|
||||
|
||||
/// <summary>
|
||||
/// Get a pointer to the variation of a specified type at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="index">The index in the list to retrieve</param>
|
||||
/// <param name="varType">The type of variation to retrieve</param>
|
||||
/// <returns>A pointer to the variation of the specified type at the index if in range, else NULL.</returns>
|
||||
Variation<T>* GetVariation(size_t index, eVariationType varType)
|
||||
{
|
||||
if (varType == VARTYPE_REG)
|
||||
return index < m_RegVariations.size() ? m_RegVariations[index] : NULL;
|
||||
else if (varType == VARTYPE_PRE)
|
||||
return index < m_PreVariations.size() ? m_PreVariations[index] : NULL;
|
||||
else if (varType == VARTYPE_POST)
|
||||
return index < m_PostVariations.size() ? m_PostVariations[index] : NULL;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a pointer to a copy of the variation at the specified index.
|
||||
/// Optionally specify a weight to assign the new copy.
|
||||
/// </summary>
|
||||
/// <param name="index">The index in the list to make a copy of</param>
|
||||
/// <param name="weight">The weight to assign the new copy. Default: 1</param>
|
||||
/// <returns>A pointer to the variation at the index if in range, else NULL.</returns>
|
||||
Variation<T>* GetVariationCopy(size_t index, T weight = 1) { return MakeCopyWithWeight(GetVariation(index), weight); }
|
||||
Variation<T>* GetVariationCopy(size_t index, eVariationType varType, T weight = 1) { return MakeCopyWithWeight(GetVariation(index, varType), weight); }
|
||||
|
||||
/// <summary>
|
||||
/// Get a pointer to the variation with the specified ID.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID to search for</param>
|
||||
/// <returns>A pointer to the variation if found, else NULL.</returns>
|
||||
Variation<T>* GetVariation(eVariationId id)
|
||||
{
|
||||
for (unsigned int i = 0; i < m_Variations.size() && m_Variations[i] != NULL; i++)
|
||||
if (id == m_Variations[i]->VariationId())
|
||||
return m_Variations[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a pointer to a copy of the variation with the specified ID.
|
||||
/// Optionally specify a weight to assign the new copy.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the variation in the list to make a copy of</param>
|
||||
/// <param name="weight">The weight to assign the new copy. Default: 1</param>
|
||||
/// <returns>A pointer to the variation with a matching ID, else NULL.</returns>
|
||||
Variation<T>* GetVariationCopy(eVariationId id, T weight = 1) { return MakeCopyWithWeight(GetVariation(id), weight); }
|
||||
|
||||
/// <summary>
|
||||
/// Get a pointer to the variation with the specified name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name to search for</param>
|
||||
/// <returns>A pointer to the variation if found, else NULL.</returns>
|
||||
Variation<T>* GetVariation(string name)
|
||||
{
|
||||
for (unsigned int i = 0; i < m_Variations.size() && m_Variations[i] != NULL; i++)
|
||||
if (name == m_Variations[i]->Name())
|
||||
return m_Variations[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a pointer to a copy of the variation with the specified name.
|
||||
/// Optionally specify a weight to assign the new copy.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the variation in the list to make a copy of</param>
|
||||
/// <param name="weight">The weight to assign the new copy. Default: 1</param>
|
||||
/// <returns>A pointer to the variation with a matching name, else NULL.</returns>
|
||||
Variation<T>* GetVariationCopy(string name, T weight = 1) { return MakeCopyWithWeight(GetVariation(name), weight); }
|
||||
|
||||
/// <summary>
|
||||
/// Get a parametric variation at the specified index.
|
||||
/// Note this is the index in the parametric variations list, not in the master list.
|
||||
/// </summary>
|
||||
/// <param name="index">The index in the parametric variations list to retrieve</param>
|
||||
/// <returns>The parametric variation at the index specified if in range, else NULL.</returns>
|
||||
ParametricVariation<T>* GetParametricVariation(size_t index) { return index < m_ParametricVariations.size() ? m_ParametricVariations[index] : NULL; }
|
||||
|
||||
/// <summary>
|
||||
/// Get a parametric variation with the specified name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the variation in the parametric variations list to retrieve</param>
|
||||
/// <returns>The parametric variation with a matching name, else NULL.</returns>
|
||||
ParametricVariation<T>* GetParametricVariation(string name)
|
||||
{
|
||||
for (unsigned int i = 0; i < m_ParametricVariations.size() && m_ParametricVariations[i] != NULL; i++)
|
||||
if (name == m_ParametricVariations[i]->Name())
|
||||
return m_ParametricVariations[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the index of the variation with the specified name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the variation whose index is returned</param>
|
||||
/// <returns>The index of the variation with the matching name, else -1</returns>
|
||||
int GetVariationIndex(string name)
|
||||
{
|
||||
for (unsigned int i = 0; i < m_Variations.size() && m_Variations[i] != NULL; i++)
|
||||
if (name == m_Variations[i]->Name())
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accessors.
|
||||
/// </summary>
|
||||
size_t Size() { return m_Variations.size(); }
|
||||
size_t RegSize() { return m_RegVariations.size(); }
|
||||
size_t PreSize() { return m_PreVariations.size(); }
|
||||
size_t PostSize() { return m_PostVariations.size(); }
|
||||
size_t ParametricSize() { return m_ParametricVariations.size(); }
|
||||
|
||||
private:
|
||||
/// <summary>
|
||||
/// Make a dyncamically allocated copy of a variation and assign it a specified weight.
|
||||
/// Return a pointer to the new copy.
|
||||
/// </summary>
|
||||
/// <param name="var">The variation to copy</param>
|
||||
/// <param name="weight">The weight to assign it</param>
|
||||
/// <returns>A pointer to the new variation copy if success, else NULL.</returns>
|
||||
Variation<T>* MakeCopyWithWeight(Variation<T>* var, T weight)
|
||||
{
|
||||
if (var)
|
||||
{
|
||||
Variation<T>* var2 = var->Copy();
|
||||
|
||||
var2->m_Weight = weight;
|
||||
return var2;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assignment operator which does nothing since these are non-copyable.
|
||||
/// Do not provide a copy constructor and ensure the assignment operator does nothing.
|
||||
/// </summary>
|
||||
/// <param name="varList">The VariationList object which won't be copied</param>
|
||||
/// <returns>Reference to unchanged self</returns>
|
||||
VariationList<T>& operator = (const VariationList<T>& varList)
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
vector<Variation<T>*> m_Variations;//A list of pointers to dynamically allocated variation objects.
|
||||
vector<Variation<T>*> m_RegVariations;
|
||||
vector<Variation<T>*> m_PreVariations;
|
||||
vector<Variation<T>*> m_PostVariations;
|
||||
vector<ParametricVariation<T>*> m_ParametricVariations;//A list of pointers to elements in m_Variations which are derived from ParametricVariation.
|
||||
};
|
||||
}
|
6296
Source/Ember/Variations01.h
Normal file
6296
Source/Ember/Variations01.h
Normal file
File diff suppressed because it is too large
Load Diff
5756
Source/Ember/Variations02.h
Normal file
5756
Source/Ember/Variations02.h
Normal file
File diff suppressed because it is too large
Load Diff
4596
Source/Ember/Variations03.h
Normal file
4596
Source/Ember/Variations03.h
Normal file
File diff suppressed because it is too large
Load Diff
5233
Source/Ember/Variations04.h
Normal file
5233
Source/Ember/Variations04.h
Normal file
File diff suppressed because it is too large
Load Diff
3253
Source/Ember/Variations05.h
Normal file
3253
Source/Ember/Variations05.h
Normal file
File diff suppressed because it is too large
Load Diff
1039
Source/Ember/VariationsDC.h
Normal file
1039
Source/Ember/VariationsDC.h
Normal file
File diff suppressed because it is too large
Load Diff
1173
Source/Ember/Xform.h
Normal file
1173
Source/Ember/Xform.h
Normal file
File diff suppressed because it is too large
Load Diff
1350
Source/Ember/XmlToEmber.h
Normal file
1350
Source/Ember/XmlToEmber.h
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user