--User changes

-Add new preset dimensions to the right click menu of the width and height fields in the editor.
-Change QSS stylesheets to properly handle tabs.
-Make tabs rectangular by default. For some reason, they had always been triangular.

--Bug fixes
 -Incremental rendering times in the editor were wrong.

--Code changes
 -Migrate to Qt6. There is probably more work to be done here.
-Migrate to VS2022.
-Migrate to Wix 4 installer.
-Change installer to install to program files for all users.
-Fix many VS2022 code analysis warnings.
-No longer use byte typedef, because std::byte is now a type. Revert all back to unsigned char.
-Upgrade OpenCL headers to version 3.0 and keep locally now rather than trying to look for system files.
-No longer link to Nvidia or AMD specific OpenCL libraries. Use the generic installer located at OCL_ROOT too.
-Add the ability to change OpenCL grid dimensions. This was attempted for investigating possible performance improvments, but made no difference.

This has not been verified on Linux or Mac yet.
This commit is contained in:
Person
2023-04-25 17:59:54 -06:00
parent 64d4470b12
commit 1dfbd4eff2
306 changed files with 514515 additions and 491207 deletions
+452 -452
View File
@@ -1,452 +1,452 @@
#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>
/// Default copy constructor.
/// </summary>
/// <param name="affine">The Affine2D object to copy</param>
template <typename T>
Affine2D<T>::Affine2D(const Affine2D<T>& affine)
{
Affine2D<T>::operator=<T>(affine);
}
/// <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>
/// Default assignment operator.
/// </summary>
/// <param name="affine">The Affine2D object to copy</param>
template <typename T>
Affine2D<T>& Affine2D<T>::operator = (const Affine2D<T>& affine)
{
if (this != &affine)
Affine2D<T>::operator=<T>(affine);
return *this;
}
/// <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) const
{
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 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) const
{
return TransformVector(v);
}
/// <summary>
/// Return a copy of the object with all values A-F scaled by the specified amount.
/// </summary>
/// <param name="amount">The amount to scale by</param>
/// <returns>A new Affine2D which a scaled copy of this instance</returns>
template <typename T>
Affine2D<T> Affine2D<T>:: operator * (T t) const
{
return Affine2D<T>(A() * t,
D() * t,
B() * t,
E() * t,
C() * t,
F() * t);
}
/// <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 (A() == 1) &&
(B() == 0) &&
(C() == 0) &&
(D() == 0) &&
(E() == 1) &&
(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 (A() == 0) &&
(B() == 0) &&
(C() == 0) &&
(D() == 0) &&
(E() == 0) &&
(F() == 0);
}
/// <summary>
/// Determine whether this affine transform was deliberately set to all empty values.
/// </summary>
/// <returns>True if all 6 elements equal zero, else false.</returns>
template <typename T>
bool Affine2D<T>::IsEmpty() const
{
return (IsClose<T>(A(), EMPTYFIELD)) &&
(IsClose<T>(B(), EMPTYFIELD)) &&
(IsClose<T>(C(), EMPTYFIELD)) &&
(IsClose<T>(D(), EMPTYFIELD)) &&
(IsClose<T>(E(), EMPTYFIELD)) &&
(IsClose<T>(F(), EMPTYFIELD));
}
/// <summary>
/// Scales all values A-F by the specified amount.
/// </summary>
/// <param name="amount">The amount to scale by</param>
template <typename T>
void Affine2D<T>::Scale(T amount)
{
A(A() * amount);
B(B() * amount);
C(C() * amount);
D(D() * amount);
E(E() * amount);
F(F() * amount);
}
/// <summary>
/// Scales all values A,B,D,E by the specified amount.
/// </summary>
/// <param name="amount">The amount to scale by</param>
template <typename T>
void Affine2D<T>::ScaleXY(T amount)
{
A(A() * amount);
B(B() * amount);
D(D() * amount);
E(E() * amount);
}
/// <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 rad)
{
const m4T origMat4 = ToMat4ColMajor(true);//Must center and use column major for glm to work.
const m4T newMat4 = glm::rotate(origMat4, rad, 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]);
}
template <typename T>
void Affine2D<T>::RotateTrans(T rad)
{
const m4T origMat4 = TransToMat4ColMajor();//Only put translation in this matrix.
const m4T newMat4 = glm::rotate(origMat4, rad, v3T(0, 0, 1));//Assuming only rotating around z.
C(newMat4[0][3]);//Use direct assignments instead of constructor to skip assigning A, B, D, E.
F(newMat4[1][3]);
}
/// <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(const 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(const v2T& v)
{
const 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(const v2T& v)
{
const 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
{
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
{
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
{
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;
}
template <typename T>
typename m4T Affine2D<T>::TransToMat4ColMajor() const
{
const m4T mat(1, 0, 0, C(), //Col0...
0, 1, 0, F(), //1
0, 0, 1, 0, //2
0, 0, 0, 1);//3
return mat;
}
/// <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(const v2T& x) { A(x.x); D(x.y); }//X Axis.
template <typename T> void Affine2D<T>::Y(const v2T& y) { B(y.x); E(y.y); }//Y Axis.
template <typename T> void Affine2D<T>::O(const v2T& t) { C(t.x); F(t.y); }//Translation.
template <typename T>
string Affine2D<T>::ToString() const
{
ostringstream ss;
ss << "A: " << A() << " "
<< "B: " << B() << " "
<< "C: " << C()
<< "\nD: " << D() << " "
<< "E: " << E() << " "
<< "F: " << F();
return ss.str();
}
/// <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(const v2T& from, const 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(const v2T& from, const v2T& to, T& a, T& c)
{
const 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;
}
//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
}
#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>
/// Default copy constructor.
/// </summary>
/// <param name="affine">The Affine2D object to copy</param>
template <typename T>
Affine2D<T>::Affine2D(const Affine2D<T>& affine)
{
Affine2D<T>::operator=<T>(affine);
}
/// <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>
/// Default assignment operator.
/// </summary>
/// <param name="affine">The Affine2D object to copy</param>
template <typename T>
Affine2D<T>& Affine2D<T>::operator = (const Affine2D<T>& affine)
{
if (this != &affine)
Affine2D<T>::operator=<T>(affine);
return *this;
}
/// <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) const
{
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 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) const
{
return TransformVector(v);
}
/// <summary>
/// Return a copy of the object with all values A-F scaled by the specified amount.
/// </summary>
/// <param name="amount">The amount to scale by</param>
/// <returns>A new Affine2D which a scaled copy of this instance</returns>
template <typename T>
Affine2D<T> Affine2D<T>:: operator * (T t) const
{
return Affine2D<T>(A() * t,
D() * t,
B() * t,
E() * t,
C() * t,
F() * t);
}
/// <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 (A() == 1) &&
(B() == 0) &&
(C() == 0) &&
(D() == 0) &&
(E() == 1) &&
(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 (A() == 0) &&
(B() == 0) &&
(C() == 0) &&
(D() == 0) &&
(E() == 0) &&
(F() == 0);
}
/// <summary>
/// Determine whether this affine transform was deliberately set to all empty values.
/// </summary>
/// <returns>True if all 6 elements equal zero, else false.</returns>
template <typename T>
bool Affine2D<T>::IsEmpty() const
{
return (IsClose<T>(A(), EMPTYFIELD)) &&
(IsClose<T>(B(), EMPTYFIELD)) &&
(IsClose<T>(C(), EMPTYFIELD)) &&
(IsClose<T>(D(), EMPTYFIELD)) &&
(IsClose<T>(E(), EMPTYFIELD)) &&
(IsClose<T>(F(), EMPTYFIELD));
}
/// <summary>
/// Scales all values A-F by the specified amount.
/// </summary>
/// <param name="amount">The amount to scale by</param>
template <typename T>
void Affine2D<T>::Scale(T amount)
{
A(A() * amount);
B(B() * amount);
C(C() * amount);
D(D() * amount);
E(E() * amount);
F(F() * amount);
}
/// <summary>
/// Scales all values A,B,D,E by the specified amount.
/// </summary>
/// <param name="amount">The amount to scale by</param>
template <typename T>
void Affine2D<T>::ScaleXY(T amount)
{
A(A() * amount);
B(B() * amount);
D(D() * amount);
E(E() * amount);
}
/// <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 rad)
{
const m4T origMat4 = ToMat4ColMajor(true);//Must center and use column major for glm to work.
const m4T newMat4 = glm::rotate(origMat4, rad, 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]);
}
template <typename T>
void Affine2D<T>::RotateTrans(T rad)
{
const m4T origMat4 = TransToMat4ColMajor();//Only put translation in this matrix.
const m4T newMat4 = glm::rotate(origMat4, rad, v3T(0, 0, 1));//Assuming only rotating around z.
C(newMat4[0][3]);//Use direct assignments instead of constructor to skip assigning A, B, D, E.
F(newMat4[1][3]);
}
/// <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(const 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(const v2T& v)
{
const 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(const v2T& v)
{
const 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
{
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
{
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
{
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;
}
template <typename T>
typename m4T Affine2D<T>::TransToMat4ColMajor() const
{
const m4T mat(1, 0, 0, C(), //Col0...
0, 1, 0, F(), //1
0, 0, 1, 0, //2
0, 0, 0, 1);//3
return mat;
}
/// <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(const v2T& x) { A(x.x); D(x.y); }//X Axis.
template <typename T> void Affine2D<T>::Y(const v2T& y) { B(y.x); E(y.y); }//Y Axis.
template <typename T> void Affine2D<T>::O(const v2T& t) { C(t.x); F(t.y); }//Translation.
template <typename T>
string Affine2D<T>::ToString() const
{
ostringstream ss;
ss << "A: " << A() << " "
<< "B: " << B() << " "
<< "C: " << C()
<< "\nD: " << D() << " "
<< "E: " << E() << " "
<< "F: " << F();
return ss.str();
}
/// <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(const v2T& from, const 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(const v2T& from, const v2T& to, T& a, T& c)
{
const 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;
}
//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
}
+125 -125
View File
@@ -1,125 +1,125 @@
#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();
Affine2D(const Affine2D<T>& affine);
/// <summary>
/// Copy constructor to copy an Affine2D object of type U.
/// Special case that must be here in the header because it has
/// a second template parameter.
/// </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);
Affine2D<T>& operator = (const Affine2D<T>& affine);
/// <summary>
/// Assignment operator to assign an Affine2D object of type U.
/// Special case that must be here in the header because it has
/// a second template parameter.
/// </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(static_cast<T>(affine.A()));
B(static_cast<T>(affine.B()));
C(static_cast<T>(affine.C()));
D(static_cast<T>(affine.D()));
E(static_cast<T>(affine.E()));
F(static_cast<T>(affine.F()));
return *this;
}
bool operator == (const Affine2D<T>& affine) const;
v2T operator * (const v2T& v) const;
Affine2D<T> operator * (T t) const;
void MakeID();
bool IsID() const;
bool IsZero() const;
bool IsEmpty() const;
void Scale(T amount);
void ScaleXY(T amount);
Affine2D<T> ScaleCopy(T amount);
void Rotate(T rad);
void RotateTrans(T rad);
void Translate(const v2T& v);
void RotateScaleXTo(const v2T& v);
void RotateScaleYTo(const v2T& v);
Affine2D<T> Inverse() const;
v2T TransformNormal(const v2T& v) const;
v2T TransformVector(const v2T& v) const;
m2T ToMat2ColMajor() const;
m2T ToMat2RowMajor() const;
m4T ToMat4ColMajor(bool center = false) const;
m4T ToMat4RowMajor(bool center = false) const;
m4T TransToMat4ColMajor() const;
//Note that returning a copy is actually faster than a const ref&.
T A() const;
T B() const;
T C() const;
T D() const;
T E() const;
T F() const;
void A(T a);
void B(T b);
void C(T c);
void D(T d);
void E(T e);
void F(T f);
v2T X() const;
v2T Y() const;
v2T O() const;
void X(const v2T& x);
void Y(const v2T& y);
void O(const v2T& t);
string ToString() const;
static Affine2D CalcRotateScale(const v2T& from, const v2T& to);
static void CalcRSAC(const v2T& from, const v2T& to, T& a, T& c);
m23T m_Mat;
};
}
#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();
Affine2D(const Affine2D<T>& affine);
/// <summary>
/// Copy constructor to copy an Affine2D object of type U.
/// Special case that must be here in the header because it has
/// a second template parameter.
/// </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);
Affine2D<T>& operator = (const Affine2D<T>& affine);
/// <summary>
/// Assignment operator to assign an Affine2D object of type U.
/// Special case that must be here in the header because it has
/// a second template parameter.
/// </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(static_cast<T>(affine.A()));
B(static_cast<T>(affine.B()));
C(static_cast<T>(affine.C()));
D(static_cast<T>(affine.D()));
E(static_cast<T>(affine.E()));
F(static_cast<T>(affine.F()));
return *this;
}
bool operator == (const Affine2D<T>& affine) const;
v2T operator * (const v2T& v) const;
Affine2D<T> operator * (T t) const;
void MakeID();
bool IsID() const;
bool IsZero() const;
bool IsEmpty() const;
void Scale(T amount);
void ScaleXY(T amount);
Affine2D<T> ScaleCopy(T amount);
void Rotate(T rad);
void RotateTrans(T rad);
void Translate(const v2T& v);
void RotateScaleXTo(const v2T& v);
void RotateScaleYTo(const v2T& v);
Affine2D<T> Inverse() const;
v2T TransformNormal(const v2T& v) const;
v2T TransformVector(const v2T& v) const;
m2T ToMat2ColMajor() const;
m2T ToMat2RowMajor() const;
m4T ToMat4ColMajor(bool center = false) const;
m4T ToMat4RowMajor(bool center = false) const;
m4T TransToMat4ColMajor() const;
//Note that returning a copy is actually faster than a const ref&.
T A() const;
T B() const;
T C() const;
T D() const;
T E() const;
T F() const;
void A(T a);
void B(T b);
void C(T c);
void D(T d);
void E(T e);
void F(T f);
v2T X() const;
v2T Y() const;
v2T O() const;
void X(const v2T& x);
void Y(const v2T& y);
void O(const v2T& t);
string ToString() const;
static Affine2D CalcRotateScale(const v2T& from, const v2T& to);
static void CalcRSAC(const v2T& from, const v2T& to, T& a, T& c);
m23T m_Mat;
};
}
+273 -273
View File
@@ -1,273 +1,273 @@
#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() = default;
/// <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, size_t rasW, size_t 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());
m_CarHalfX = T(carToRas.CarHalfX());
m_CarHalfY = T(carToRas.CarHalfY());
m_CarCenterX = T(carToRas.CarCenterX());
m_CarCenterY = T(carToRas.CarCenterY());
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, size_t rasW, size_t 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 = static_cast<T>(rasW) * invSizeW;
m_RasLlX = m_PixPerImageUnitW * carLlX;
m_PixPerImageUnitH = static_cast<T>(rasH) * invSizeH;
m_RasLlY = m_PixPerImageUnitH * carLlY;
m_OneRow = std::abs(m_CarUrY - m_CarLlY) / m_RasHeight;
m_OneCol = std::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;
m_CarHalfX = (m_CarUrX - m_CarLlX) / 2;
m_CarHalfY = (m_CarUrY - m_CarLlY) / 2;
m_CarCenterX = m_CarLlX + m_CarHalfX;
m_CarCenterY = m_CarLlY + m_CarHalfY;
}
/// <summary>
/// Assign values to the cached half width/height fields.
/// This is only done manually here and is used when rendering strips
/// because a cached copy of these is required because the real values
/// change with the assignment of each temporary strip ember object.
/// </summary>
/// <param name="x">The cached value equal to half of the cartesian width of the x plane</param>
/// <param name="y">The cached value equal to half of the cartesian width of the y plane</param>
void UpdateCachedHalf(T x, T y)
{
m_CachedCarHalfX = x;
m_CachedCarHalfY = y;
}
/// <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, size_t& rasX, size_t& rasY)
{
rasX = static_cast<size_t>(m_PixPerImageUnitW * cartX - m_RasLlX);
rasY = static_cast<size_t>(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, size_t& singleBufferIndex)
{
singleBufferIndex = static_cast<size_t>(m_PixPerImageUnitW * cartX - m_RasLlX) + (m_RasWidth * static_cast<size_t>(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, size_t& singleBufferIndex)
{
//singleBufferIndex = static_cast<size_t>(Round(m_PixPerImageUnitW * point.m_X - m_RasLlX) + (m_RasWidth * Round(m_PixPerImageUnitH * point.m_Y - m_RasLlY)));
singleBufferIndex = static_cast<size_t>(m_PixPerImageUnitW * point.m_X - m_RasLlX) + (m_RasWidth * static_cast<size_t>(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.\n";
//}
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 size_t RasWidth() const { return m_RasWidth; }
inline size_t 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; }
inline T CarHalfX() const { return m_CarHalfX; }
inline T CarHalfY() const { return m_CarHalfY; }
inline T CachedCarHalfX() const { return m_CachedCarHalfX; }
inline T CachedCarHalfY() const { return m_CachedCarHalfY; }
inline T CarCenterX() const { return m_CarCenterX; }
inline T CarCenterY() const { return m_CarCenterY; }
private:
size_t 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.
T m_CarHalfX, m_CarHalfY;//The distance from the center of the of the cartesian plane to the edges.
T m_CachedCarHalfX, m_CachedCarHalfY;//The cahced distance from the center of the of the cartesian plane to the edges, needed when rendering strips.
T m_CarCenterX, m_CarCenterY;//The center of the cartesian plane.
};
}
#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() = default;
/// <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, size_t rasW, size_t 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());
m_CarHalfX = T(carToRas.CarHalfX());
m_CarHalfY = T(carToRas.CarHalfY());
m_CarCenterX = T(carToRas.CarCenterX());
m_CarCenterY = T(carToRas.CarCenterY());
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, size_t rasW, size_t 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 = static_cast<T>(rasW) * invSizeW;
m_RasLlX = m_PixPerImageUnitW * carLlX;
m_PixPerImageUnitH = static_cast<T>(rasH) * invSizeH;
m_RasLlY = m_PixPerImageUnitH * carLlY;
m_OneRow = std::abs(m_CarUrY - m_CarLlY) / m_RasHeight;
m_OneCol = std::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;
m_CarHalfX = (m_CarUrX - m_CarLlX) / 2;
m_CarHalfY = (m_CarUrY - m_CarLlY) / 2;
m_CarCenterX = m_CarLlX + m_CarHalfX;
m_CarCenterY = m_CarLlY + m_CarHalfY;
}
/// <summary>
/// Assign values to the cached half width/height fields.
/// This is only done manually here and is used when rendering strips
/// because a cached copy of these is required because the real values
/// change with the assignment of each temporary strip ember object.
/// </summary>
/// <param name="x">The cached value equal to half of the cartesian width of the x plane</param>
/// <param name="y">The cached value equal to half of the cartesian width of the y plane</param>
void UpdateCachedHalf(T x, T y)
{
m_CachedCarHalfX = x;
m_CachedCarHalfY = y;
}
/// <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, size_t& rasX, size_t& rasY)
{
rasX = static_cast<size_t>(m_PixPerImageUnitW * cartX - m_RasLlX);
rasY = static_cast<size_t>(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, size_t& singleBufferIndex)
{
singleBufferIndex = static_cast<size_t>(m_PixPerImageUnitW * cartX - m_RasLlX) + (m_RasWidth * static_cast<size_t>(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, size_t& singleBufferIndex)
{
//singleBufferIndex = static_cast<size_t>(Round(m_PixPerImageUnitW * point.m_X - m_RasLlX) + (m_RasWidth * Round(m_PixPerImageUnitH * point.m_Y - m_RasLlY)));
singleBufferIndex = static_cast<size_t>(m_PixPerImageUnitW * point.m_X - m_RasLlX) + (m_RasWidth * static_cast<size_t>(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.\n";
//}
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 size_t RasWidth() const { return m_RasWidth; }
inline size_t 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; }
inline T CarHalfX() const { return m_CarHalfX; }
inline T CarHalfY() const { return m_CarHalfY; }
inline T CachedCarHalfX() const { return m_CachedCarHalfX; }
inline T CachedCarHalfY() const { return m_CachedCarHalfY; }
inline T CarCenterX() const { return m_CarCenterX; }
inline T CarCenterY() const { return m_CarCenterY; }
private:
size_t 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.
T m_CarHalfX, m_CarHalfY;//The distance from the center of the of the cartesian plane to the edges.
T m_CachedCarHalfX, m_CachedCarHalfY;//The cahced distance from the center of the of the cartesian plane to the edges, needed when rendering strips.
T m_CarCenterX, m_CarCenterY;//The center of the cartesian plane.
};
}
+273 -273
View File
@@ -1,273 +1,273 @@
#pragma once
#include "Utils.h"
#include "Isaac.h"
#include "Curves.h"
#define CURVE_POINTS 5
/// <summary>
/// Curves class.
/// </summary>
namespace EmberNs
{
/// <summary>
/// The b-spline curves used to adjust the colors during final accumulation.
/// This functionality was gotten inferred from Chaotica.
/// Note this is now incompatible with Apophysis, which uses Bezier curves instead.
/// </summary>
template <typename T>
class EMBER_API Curves
{
public:
/// <summary>
/// Constructor which sets the curve and weight values to their defaults.
/// </summary>
Curves(bool init = false)
{
if (init)
Init();
else
Clear();
}
/// <summary>
/// Default copy constructor.
/// </summary>
/// <param name="curves">The Curves object to copy</param>
Curves(const Curves<T>& curves)
{
Curves<T>::operator=<T>(curves);
}
/// <summary>
/// Copy constructor to copy a Curves object of type U.
/// Special case that must be here in the header because it has
/// a second template parameter.
/// </summary>
/// <param name="curves">The Curves object to copy</param>
template <typename U>
Curves(const Curves<U>& curves)
{
Curves<T>::operator=<U>(curves);
}
/// <summary>
/// Default assignment operator.
/// </summary>
/// <param name="curves">The Curves object to copy</param>
Curves<T>& operator = (const Curves<T>& curves)
{
if (this != &curves)
Curves<T>::operator=<T>(curves);
return *this;
}
/// <summary>
/// Assignment operator to assign a Curves object of type U.
/// </summary>
/// <param name="curves">The Curves object to copy</param>
/// <returns>Reference to updated self</returns>
template <typename U>
Curves<T>& operator = (const Curves<U>& curves)
{
int i = 0;
for (auto& pp : curves.m_Points)
{
int j = 0;
m_Points[i].clear();
for (auto& p : pp)
{
m_Points[i].push_back(p);
j++;
}
i++;
}
i = 0;
return *this;
}
/// <summary>
/// Unary addition operator to add a Curves<T> object to this one.
/// </summary>
/// <param name="curves">The Curves object to add</param>
/// <returns>Reference to updated self</returns>
template <typename U>
Curves<T>& operator += (const Curves<U>& curves)
{
int i = 0;
for (auto& pp : m_Points)
{
int j = 0;
for (auto& p : pp)
{
if (j < curves.m_Points[i].size())
p += curves.m_Points[i][j];
else
break;
j++;
}
i++;
}
return *this;
}
/// <summary>
/// Unary multiplication operator to multiply this object by another Curves<T> object.
/// </summary>
/// <param name="curves">The Curves object to multiply this one by</param>
/// <returns>Reference to updated self</returns>
template <typename U>
Curves<T>& operator *= (const Curves<U>& curves)
{
int i = 0;
for (auto& pp : m_Points)
{
int j = 0;
for (auto& p : pp)
{
if (j < curves.m_Points[i].size())
p *= curves.m_Points[i][j];
else
break;
j++;
}
i++;
}
return *this;
}
/// <summary>
/// Unary multiplication operator to multiply this object by a scalar of type T.
/// </summary>
/// <param name="t">The scalar to multiply this object by</param>
/// <returns>Reference to updated self</returns>
template <typename U>
Curves<T>& operator *= (const U& t)
{
for (auto& pp : m_Points)
for (auto& p : pp)
p *= T(t);
return *this;
}
/// <summary>
/// Set the curve and weight values to their default state.
/// </summary>
void Init()
{
for (size_t i = 0; i < 4; i++)
Init(i);
}
/// <summary>
/// Set a specific curve and its weight value to their default state.
/// </summary>
void Init(size_t i)
{
if (i < 4)
{
m_Points[i].resize(5);
m_Points[i][0] = v2T{ 0 };
m_Points[i][1] = v2T{ static_cast<T>(0.25) };
m_Points[i][2] = v2T{ static_cast<T>(0.50) };
m_Points[i][3] = v2T{ static_cast<T>(0.75) };
m_Points[i][4] = v2T{ 1 };
}
}
/// <summary>
/// Set the curve and weight values to an empty state.
/// </summary>
void Clear()
{
for (auto& p : m_Points)
p.clear();
}
/// <summary>
/// Whether any points are not the default.
/// </summary>
/// <returns>True if any point has been set to a value other than the default, else false.</returns>
bool CurvesSet()
{
bool set = false;
for (size_t i = 0; i < 4; i++)
{
if (m_Points[i].size() != CURVE_POINTS)
{
set = true;
break;
}
if ((m_Points[i][0] != v2T(0)) ||
(m_Points[i][1] != v2T(static_cast<T>(0.25))) ||
(m_Points[i][2] != v2T(static_cast<T>(0.50))) ||
(m_Points[i][3] != v2T(static_cast<T>(0.75))) ||
(m_Points[i][4] != v2T(1))
)
{
set = true;
break;
}
}
return set;
}
public:
std::array<std::vector<v2T>, 4> m_Points;
};
//Must declare this outside of the class to provide for both orders of parameters.
/// <summary>
/// Multiplication operator to multiply a Curves<T> object by a scalar of type U.
/// </summary>
/// <param name="curves">The curves object to multiply</param>
/// <param name="t">The scalar to multiply curves by by</param>
/// <returns>Copy of new Curves<T></returns>
template <typename T, typename U>
Curves<T> operator * (const Curves<T>& curves, const U& t)
{
T tt = T(t);
Curves<T> c(curves);
for (auto& pp : c.m_Points)
for (auto& p : pp)
p *= tt;
return c;
}
/// <summary>
/// Multiplication operator for reverse order.
/// </summary>
/// <param name="t">The scalar to multiply curves by by</param>
/// <param name="curves">The curves object to multiply</param>
/// <returns>Copy of new Curves<T></returns>
template <typename T, typename U>
Curves<T> operator * (const U& t, const Curves<T>& curves)
{
return curves * t;
}
}
#pragma once
#include "Utils.h"
#include "Isaac.h"
#include "Curves.h"
#define CURVE_POINTS 5
/// <summary>
/// Curves class.
/// </summary>
namespace EmberNs
{
/// <summary>
/// The b-spline curves used to adjust the colors during final accumulation.
/// This functionality was gotten inferred from Chaotica.
/// Note this is now incompatible with Apophysis, which uses Bezier curves instead.
/// </summary>
template <typename T>
class EMBER_API Curves
{
public:
/// <summary>
/// Constructor which sets the curve and weight values to their defaults.
/// </summary>
Curves(bool init = false)
{
if (init)
Init();
else
Clear();
}
/// <summary>
/// Default copy constructor.
/// </summary>
/// <param name="curves">The Curves object to copy</param>
Curves(const Curves<T>& curves)
{
Curves<T>::operator=<T>(curves);
}
/// <summary>
/// Copy constructor to copy a Curves object of type U.
/// Special case that must be here in the header because it has
/// a second template parameter.
/// </summary>
/// <param name="curves">The Curves object to copy</param>
template <typename U>
Curves(const Curves<U>& curves)
{
Curves<T>::operator=<U>(curves);
}
/// <summary>
/// Default assignment operator.
/// </summary>
/// <param name="curves">The Curves object to copy</param>
Curves<T>& operator = (const Curves<T>& curves)
{
if (this != &curves)
Curves<T>::operator=<T>(curves);
return *this;
}
/// <summary>
/// Assignment operator to assign a Curves object of type U.
/// </summary>
/// <param name="curves">The Curves object to copy</param>
/// <returns>Reference to updated self</returns>
template <typename U>
Curves<T>& operator = (const Curves<U>& curves)
{
int i = 0;
for (auto& pp : curves.m_Points)
{
int j = 0;
m_Points[i].clear();
for (auto& p : pp)
{
m_Points[i].push_back(p);
j++;
}
i++;
}
i = 0;
return *this;
}
/// <summary>
/// Unary addition operator to add a Curves<T> object to this one.
/// </summary>
/// <param name="curves">The Curves object to add</param>
/// <returns>Reference to updated self</returns>
template <typename U>
Curves<T>& operator += (const Curves<U>& curves)
{
int i = 0;
for (auto& pp : m_Points)
{
int j = 0;
for (auto& p : pp)
{
if (j < curves.m_Points[i].size())
p += curves.m_Points[i][j];
else
break;
j++;
}
i++;
}
return *this;
}
/// <summary>
/// Unary multiplication operator to multiply this object by another Curves<T> object.
/// </summary>
/// <param name="curves">The Curves object to multiply this one by</param>
/// <returns>Reference to updated self</returns>
template <typename U>
Curves<T>& operator *= (const Curves<U>& curves)
{
int i = 0;
for (auto& pp : m_Points)
{
int j = 0;
for (auto& p : pp)
{
if (j < curves.m_Points[i].size())
p *= curves.m_Points[i][j];
else
break;
j++;
}
i++;
}
return *this;
}
/// <summary>
/// Unary multiplication operator to multiply this object by a scalar of type T.
/// </summary>
/// <param name="t">The scalar to multiply this object by</param>
/// <returns>Reference to updated self</returns>
template <typename U>
Curves<T>& operator *= (const U& t)
{
for (auto& pp : m_Points)
for (auto& p : pp)
p *= T(t);
return *this;
}
/// <summary>
/// Set the curve and weight values to their default state.
/// </summary>
void Init()
{
for (size_t i = 0; i < 4; i++)
Init(i);
}
/// <summary>
/// Set a specific curve and its weight value to their default state.
/// </summary>
void Init(size_t i)
{
if (i < 4)
{
m_Points[i].resize(5);
m_Points[i][0] = v2T{ 0 };
m_Points[i][1] = v2T{ static_cast<T>(0.25) };
m_Points[i][2] = v2T{ static_cast<T>(0.50) };
m_Points[i][3] = v2T{ static_cast<T>(0.75) };
m_Points[i][4] = v2T{ 1 };
}
}
/// <summary>
/// Set the curve and weight values to an empty state.
/// </summary>
void Clear()
{
for (auto& p : m_Points)
p.clear();
}
/// <summary>
/// Whether any points are not the default.
/// </summary>
/// <returns>True if any point has been set to a value other than the default, else false.</returns>
bool CurvesSet()
{
bool set = false;
for (size_t i = 0; i < 4; i++)
{
if (m_Points[i].size() != CURVE_POINTS)
{
set = true;
break;
}
if ((m_Points[i][0] != v2T(0)) ||
(m_Points[i][1] != v2T(static_cast<T>(0.25))) ||
(m_Points[i][2] != v2T(static_cast<T>(0.50))) ||
(m_Points[i][3] != v2T(static_cast<T>(0.75))) ||
(m_Points[i][4] != v2T(1))
)
{
set = true;
break;
}
}
return set;
}
public:
std::array<std::vector<v2T>, 4> m_Points;
};
//Must declare this outside of the class to provide for both orders of parameters.
/// <summary>
/// Multiplication operator to multiply a Curves<T> object by a scalar of type U.
/// </summary>
/// <param name="curves">The curves object to multiply</param>
/// <param name="t">The scalar to multiply curves by by</param>
/// <returns>Copy of new Curves<T></returns>
template <typename T, typename U>
Curves<T> operator * (const Curves<T>& curves, const U& t)
{
T tt = T(t);
Curves<T> c(curves);
for (auto& pp : c.m_Points)
for (auto& p : pp)
p *= tt;
return c;
}
/// <summary>
/// Multiplication operator for reverse order.
/// </summary>
/// <param name="t">The scalar to multiply curves by by</param>
/// <param name="curves">The curves object to multiply</param>
/// <returns>Copy of new Curves<T></returns>
template <typename T, typename U>
Curves<T> operator * (const U& t, const Curves<T>& curves)
{
return curves * t;
}
}
+341 -341
View File
@@ -1,341 +1,341 @@
#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 intmax_t FilterWidth() const { 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, size_t 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 = static_cast<T>(0.5);
if (m_MaxRad < m_MinRad)
m_MaxRad = m_MinRad + 1;
//Ensure it's valid.
while (!Valid())
{
m_Curve += static_cast<T>(0.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()
{
size_t w;
int intFilterCount, maxIndex;
int rowSize;
size_t filterLoop;
int keepThresh = 100;
uint 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 = std::pow(finalMaxRad / finalMinRad, static_cast<T>(1) / m_Curve);
if (decFilterCount > 1e7)//Too many filters.
return false;
intFilterCount = static_cast<int>(ceil(decFilterCount));
//Condense the smaller kernels to save space.
if (intFilterCount > keepThresh)
{
maxIndex = static_cast<int>(ceil(DE_THRESH + std::pow(static_cast<T>(intFilterCount - DE_THRESH), m_Curve))) + 1;
m_MaxFilteredCounts = static_cast<int>(std::pow(static_cast<T>(maxIndex - DE_THRESH), static_cast<T>(1) / m_Curve)) + DE_THRESH;
}
else
{
maxIndex = intFilterCount;
m_MaxFilteredCounts = maxIndex;
}
//Allocate the memory for these filters and the hit/width lookup array.
rowSize = static_cast<int>(2 * std::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++)
{
intmax_t dej, dek;
size_t 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 / std::pow(static_cast<T>(filterLoop + 1), m_Curve));
}
else
{
loopAdjust = std::pow(static_cast<T>(filterLoop - keepThresh), (static_cast<T>(1) / m_Curve)) + keepThresh;
filterHeight = (finalMaxRad / std::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 = std::sqrt(static_cast<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 = std::sqrt(static_cast<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 (intmax_t j = 0; j <= m_FilterWidth; j++)
{
for (intmax_t 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 std::pow(finalMaxRad / finalMinRad, static_cast<T>(1) / 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
{
size_t i, j, coefIndex = 0, w = m_FilterWidth + 1;
stringstream ss;
ss
<< "Density Filter:"
<< "\n Min radius: " << MinRad()
<< "\n Max radius: " << MaxRad()
<< "\n Curve: " << Curve()
<< "\n Kernel size: " << KernelSize()
<< "\n Max filter index: " << MaxFilterIndex()
<< "\nMax Filtered counts: " << MaxFilteredCounts()
<< "\n Filter width: " << FilterWidth();
ss << "\nCoefficients: \n";
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] << "\n";
}
ss << "\nWidths: \n";
for (i = 0; i < m_Widths.size(); i++)
{
ss << "Widths[" << i << "]: " << m_Widths[i] << "\n";
}
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 << "\n";
}
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 size_t Supersample() const { return m_Supersample; }
inline size_t KernelSize() const { return m_KernelSize; }
inline size_t MaxFilterIndex() const { return m_MaxFilterIndex; }
inline size_t MaxFilteredCounts() const { return m_MaxFilteredCounts; }
virtual intmax_t FilterWidth() const override { return m_FilterWidth; }
inline size_t BufferSize() const { return m_Widths.size(); }
inline size_t CoefsSizeBytes() const { return BufferSize() * m_KernelSize * sizeof(T); }
inline size_t WidthsSizeBytes() const { return SizeOf(m_Widths); }
inline size_t CoefsIndicesSizeBytes() const { return SizeOf(m_CoefIndices); }
inline const T* Coefs() const { return m_Coefs.data(); }
inline const T* Widths() const { return m_Widths.data(); }
inline const uint* CoefIndices() const { return m_CoefIndices.data(); }
private:
T m_MinRad;
T m_MaxRad;//The original specified filter radius.
T m_Curve;
size_t m_Supersample;
size_t m_KernelSize;
size_t m_MaxFilterIndex;
size_t m_MaxFilteredCounts;
intmax_t 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<uint> m_CoefIndices;
};
}
#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 intmax_t FilterWidth() const { 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, size_t 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 = static_cast<T>(0.5);
if (m_MaxRad < m_MinRad)
m_MaxRad = m_MinRad + 1;
//Ensure it's valid.
while (!Valid())
{
m_Curve += static_cast<T>(0.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()
{
size_t w;
int intFilterCount, maxIndex;
int rowSize;
size_t filterLoop;
int keepThresh = 100;
uint 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 = std::pow(finalMaxRad / finalMinRad, static_cast<T>(1) / m_Curve);
if (decFilterCount > 1e7)//Too many filters.
return false;
intFilterCount = static_cast<int>(ceil(decFilterCount));
//Condense the smaller kernels to save space.
if (intFilterCount > keepThresh)
{
maxIndex = static_cast<int>(ceil(DE_THRESH + std::pow(static_cast<T>(intFilterCount - DE_THRESH), m_Curve))) + 1;
m_MaxFilteredCounts = static_cast<int>(std::pow(static_cast<T>(maxIndex - DE_THRESH), static_cast<T>(1) / m_Curve)) + DE_THRESH;
}
else
{
maxIndex = intFilterCount;
m_MaxFilteredCounts = maxIndex;
}
//Allocate the memory for these filters and the hit/width lookup array.
rowSize = static_cast<int>(2 * std::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++)
{
intmax_t dej, dek;
size_t 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 / std::pow(static_cast<T>(filterLoop + 1), m_Curve));
}
else
{
loopAdjust = std::pow(static_cast<T>(filterLoop - keepThresh), (static_cast<T>(1) / m_Curve)) + keepThresh;
filterHeight = (finalMaxRad / std::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 = std::sqrt(static_cast<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 = std::sqrt(static_cast<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 (intmax_t j = 0; j <= m_FilterWidth; j++)
{
for (intmax_t 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 std::pow(finalMaxRad / finalMinRad, static_cast<T>(1) / 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
{
size_t i, j, coefIndex = 0, w = m_FilterWidth + 1;
stringstream ss;
ss
<< "Density Filter:"
<< "\n Min radius: " << MinRad()
<< "\n Max radius: " << MaxRad()
<< "\n Curve: " << Curve()
<< "\n Kernel size: " << KernelSize()
<< "\n Max filter index: " << MaxFilterIndex()
<< "\nMax Filtered counts: " << MaxFilteredCounts()
<< "\n Filter width: " << FilterWidth();
ss << "\nCoefficients: \n";
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] << "\n";
}
ss << "\nWidths: \n";
for (i = 0; i < m_Widths.size(); i++)
{
ss << "Widths[" << i << "]: " << m_Widths[i] << "\n";
}
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 << "\n";
}
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 size_t Supersample() const { return m_Supersample; }
inline size_t KernelSize() const { return m_KernelSize; }
inline size_t MaxFilterIndex() const { return m_MaxFilterIndex; }
inline size_t MaxFilteredCounts() const { return m_MaxFilteredCounts; }
virtual intmax_t FilterWidth() const override { return m_FilterWidth; }
inline size_t BufferSize() const { return m_Widths.size(); }
inline size_t CoefsSizeBytes() const { return BufferSize() * m_KernelSize * sizeof(T); }
inline size_t WidthsSizeBytes() const { return SizeOf(m_Widths); }
inline size_t CoefsIndicesSizeBytes() const { return SizeOf(m_CoefIndices); }
inline const T* Coefs() const { return m_Coefs.data(); }
inline const T* Widths() const { return m_Widths.data(); }
inline const uint* CoefIndices() const { return m_CoefIndices.data(); }
private:
T m_MinRad;
T m_MaxRad;//The original specified filter radius.
T m_Curve;
size_t m_Supersample;
size_t m_KernelSize;
size_t m_MaxFilterIndex;
size_t m_MaxFilteredCounts;
intmax_t 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<uint> m_CoefIndices;
};
}
+22 -22
View File
@@ -1,22 +1,22 @@
#include "EmberPch.h"
#ifdef _WIN32
/// <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;
}
#endif
#include "EmberPch.h"
#ifdef _WIN32
/// <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;
}
#endif
+545 -545
View File
File diff suppressed because it is too large Load Diff
+1934 -1934
View File
File diff suppressed because it is too large Load Diff
+350 -350
View File
@@ -1,350 +1,350 @@
#pragma once
#include "EmberPch.h"
/// <summary>
/// Basic #defines used throughout the library.
/// </summary>
#ifdef _WIN32
#if defined(BUILDING_EMBER)
#define EMBER_API __declspec(dllexport)
#else
#define EMBER_API __declspec(dllimport)
#endif
#else
#define EMBER_API
#define fopen_s(pFile,filename,mode) ((*(pFile)=fopen((filename),(mode)))==nullptr)
#define _stat stat
#define _fstat fstat
#define _stricmp strcasecmp
typedef int errno_t;
#endif
#define RESTRICT __restrict//This might make things faster, unsure if it really does though.
//#define RESTRICT
//Wrap the sincos function for Macs and PC.
#if defined(__APPLE__) || defined(_MSC_VER)
#define sincos(x, s, c) *(s)=std::sin(x); *(c)=std::cos(x);
#else
static void sincos(float x, float* s, float* c)
{
*s = std::sin(x);
*c = std::cos(x);
}
#endif
namespace EmberNs
{
#define EMBER_VERSION "22.21.4.2"
//#define FLAM3_COMPAT 1//Uncomment this if you want full compatibility with flam3 regarding some of the trig-based variations in Variations01.h
#define EPS6 T(1e-6)
#define EPS std::numeric_limits<T>::epsilon()//Apoplugin.h uses -20, but it's more mathematically correct to do it this way.
#define ISAAC_SIZE 4
#define MEMALIGN 32
#define DE_THRESH 100
#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 M_PI2 (T{M_PI_2})
#define M_PI4 (T{M_PI_4})
#define M_SQRT3 T(1.7320508075688772935274463415059)
#define M_SQRT3_2 T(0.86602540378443864676372317075294)
#define M_SQRT3_3 T(0.57735026918962576450914878050196)
#define M_SQRT5 T(2.2360679774997896964091736687313)
#define M_PHI T(1.61803398874989484820458683436563)
#define M_1_2PI T(0.15915494309189533576888376337251)
#define M_PI3 T(1.0471975511965977461542144610932)
#define M_PI6 T(0.52359877559829887307710723054658)
#define COLORMAP_LENGTH 256//These will need to change if 2D palette support is ever added, or variable sized palettes.
#define WHITE 255
#define DEFAULT_SBS (1024 * 10)
//#define XC(c) ((const xmlChar*)(c))
#define XC(c) (reinterpret_cast<const xmlChar*>(c))
#define CX(c) (reinterpret_cast<char*>(c))
#define CCX(c) (reinterpret_cast<const char*>(c))
#define BadVal(x) (std::isnan(x))
#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()
#define FLOAT_MAX_TAN 8388607.0f
#define FLOAT_MIN_TAN -FLOAT_MAX_TAN
#define CURVES_LENGTH 65536
#define CURVES_LENGTH_M1 65535.0f
#define ONE_OVER_CURVES_LENGTH_M1 1.525902189669e-5f
#define EMPTYFIELD -9999
typedef unsigned char et;
typedef std::lock_guard <std::recursive_mutex> rlg;
/// <summary>
/// Thin wrapper around getting the current time in milliseconds.
/// </summary>
typedef std::chrono::high_resolution_clock Clock;
typedef std::chrono::duration<double, std::ratio<1, 1000>> DoubleMs;
typedef std::chrono::time_point<Clock, DoubleMs> DoubleMsTimePoint;
static inline DoubleMsTimePoint NowMsD() { return time_point_cast<DoubleMs>(Clock::now()); }
static inline size_t NowMs() { return duration_cast<milliseconds>(Clock::now().time_since_epoch()).count(); }
#ifndef byte
typedef unsigned char byte;
#endif
#define DO_DOUBLE 1//Comment this out for shorter build times during development. Always uncomment for release.
//#define ISAAC_FLAM3_DEBUG 1//This is almost never needed, but is very useful when troubleshooting difficult bugs. Enable it to do a side by side comparison with flam3.
//These two must always match.
#ifdef _WIN32
#define ALIGN __declspec(align(16))
#define STATIC static
#else
#define ALIGN __attribute__ ((aligned (16)))
#define STATIC
#endif
#define ALIGN_CL "((aligned (16)))"//The extra parens are necessary.
#if GLM_VERSION >= 96
#define v2T glm::tvec2<T, glm::defaultp>
#define v3T glm::tvec3<T, glm::defaultp>
#define v4T glm::tvec4<T, glm::defaultp>
#define v2F glm::tvec2<float, glm::defaultp>
#define v4F glm::tvec4<float, glm::defaultp>
#define v4D glm::tvec4<double, glm::defaultp>
#define v4bT glm::tvec4<bucketT, glm::defaultp>
#define m2T glm::tmat2x2<T, glm::defaultp>
#define m3T glm::tmat3x3<T, glm::defaultp>
#define m4T glm::tmat4x4<T, glm::defaultp>
#define m23T glm::tmat2x3<T, glm::defaultp>
typedef vector<glm::tvec4<float, glm::defaultp>> vv4F;
#else
#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 v2F glm::detail::tvec2<float, glm::defaultp>
#define v4F glm::detail::tvec4<float, glm::defaultp>
#define v4D glm::detail::tvec4<double, glm::defaultp>
#define v4bT glm::detail::tvec4<bucketT, 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>
typedef vector<glm::detail::tvec4<float, glm::defaultp>> vv4F;
#endif
enum class eInterp : et { EMBER_INTERP_LINEAR = 0, EMBER_INTERP_SMOOTH = 1 };
enum class eAffineInterp : et { AFFINE_INTERP_LINEAR = 0, AFFINE_INTERP_LOG = 1, AFFINE_INTERP_COMPAT = 2, AFFINE_INTERP_OLDER = 3 };
enum class ePaletteMode : et { PALETTE_STEP = 0, PALETTE_LINEAR = 1 };
enum class ePaletteInterp : et { INTERP_HSV = 0, INTERP_SWEEP = 1 };
enum class eMotion : et { MOTION_SIN = 1, MOTION_TRIANGLE = 2, MOTION_HILL = 3, MOTION_SAW = 4 };
enum class eProcessAction : et { NOTHING = 0, ACCUM_ONLY = 1, FILTER_AND_ACCUM = 2, KEEP_ITERATING = 3, FULL_RENDER = 4 };
enum class eProcessState : et { NONE = 0, ITER_STARTED = 1, ITER_DONE = 2, FILTER_DONE = 3, ACCUM_DONE = 4 };
enum class eInteractiveFilter : et { FILTER_LOG = 0, FILTER_DE = 1 };
enum class eScaleType : et { SCALE_NONE = 0, SCALE_WIDTH = 1, SCALE_HEIGHT = 2 };
enum class eRenderStatus : et { RENDER_OK = 0, RENDER_ERROR = 1, RENDER_ABORT = 2 };
enum class eEmberMotionParam : et//These must remain in this order forever.
{
FLAME_MOTION_NONE,
FLAME_MOTION_ZOOM,
FLAME_MOTION_ZPOS,
FLAME_MOTION_PERSPECTIVE,
FLAME_MOTION_YAW,
FLAME_MOTION_PITCH,
FLAME_MOTION_DEPTH_BLUR,
FLAME_MOTION_CENTER_X,
FLAME_MOTION_CENTER_Y,
FLAME_MOTION_ROTATE,
FLAME_MOTION_BRIGHTNESS,
FLAME_MOTION_GAMMA,
FLAME_MOTION_GAMMA_THRESH,
FLAME_MOTION_HIGHLIGHT_POWER,
FLAME_MOTION_K2,
FLAME_MOTION_RAND_RANGE,
FLAME_MOTION_BACKGROUND_R,
FLAME_MOTION_BACKGROUND_G,
FLAME_MOTION_BACKGROUND_B,
FLAME_MOTION_VIBRANCY,
FLAME_MOTION_BLUR_CURVE
};
/// <summary>
/// Thin wrapper to allow << operator on interp type.
/// </summary>
/// <param name="stream">The stream to insert into</param>
/// <param name="t">The type whose string representation will be inserted into the stream</param>
/// <returns></returns>
static std::ostream& operator<<(std::ostream& stream, const eInterp& t)
{
switch (t)
{
case EmberNs::eInterp::EMBER_INTERP_LINEAR:
stream << "linear";
break;
case EmberNs::eInterp::EMBER_INTERP_SMOOTH:
stream << "smooth";
break;
default:
stream << "error";
break;
}
return stream;
}
/// <summary>
/// Thin wrapper to allow << operator on affine interp type.
/// </summary>
/// <param name="stream">The stream to insert into</param>
/// <param name="t">The type whose string representation will be inserted into the stream</param>
/// <returns></returns>
static std::ostream& operator<<(std::ostream& stream, const eAffineInterp& t)
{
switch (t)
{
case EmberNs::eAffineInterp::AFFINE_INTERP_LINEAR:
stream << "linear";
break;
case EmberNs::eAffineInterp::AFFINE_INTERP_LOG:
stream << "log";
break;
case EmberNs::eAffineInterp::AFFINE_INTERP_COMPAT:
stream << "compat";
break;
case EmberNs::eAffineInterp::AFFINE_INTERP_OLDER:
stream << "older";
break;
default:
stream << "error";
break;
}
return stream;
}
/// <summary>
/// Thin wrapper to allow << operator on palette mode type.
/// </summary>
/// <param name="stream">The stream to insert into</param>
/// <param name="t">The type whose string representation will be inserted into the stream</param>
/// <returns></returns>
static std::ostream& operator<<(std::ostream& stream, const ePaletteMode& t)
{
switch (t)
{
case EmberNs::ePaletteMode::PALETTE_STEP:
stream << "step";
break;
case EmberNs::ePaletteMode::PALETTE_LINEAR:
stream << "linear";
break;
default:
stream << "error";
break;
}
return stream;
}
/// <summary>
/// Thin wrapper to allow << operator on palette interp type.
/// </summary>
/// <param name="stream">The stream to insert into</param>
/// <param name="t">The type whose string representation will be inserted into the stream</param>
/// <returns></returns>
static std::ostream& operator<<(std::ostream& stream, const ePaletteInterp& t)
{
switch (t)
{
case EmberNs::ePaletteInterp::INTERP_HSV:
stream << "hsv";
break;
case EmberNs::ePaletteInterp::INTERP_SWEEP:
stream << "sweep";
break;
default:
stream << "error";
break;
}
return stream;
}
/// <summary>
/// Thin wrapper to allow << operator on scale type.
/// </summary>
/// <param name="stream">The stream to insert into</param>
/// <param name="t">The type whose string representation will be inserted into the stream</param>
/// <returns></returns>
static std::ostream& operator<<(std::ostream& stream, const eScaleType& t)
{
switch (t)
{
case EmberNs::eScaleType::SCALE_NONE:
stream << "none";
break;
case EmberNs::eScaleType::SCALE_WIDTH:
stream << "width";
break;
case EmberNs::eScaleType::SCALE_HEIGHT:
stream << "height";
break;
default:
stream << "error";
break;
}
return stream;
}
/// <summary>
/// Thin wrapper to allow << operator on motion type.
/// </summary>
/// <param name="stream">The stream to insert into</param>
/// <param name="t">The type whose string representation will be inserted into the stream</param>
/// <returns></returns>
static std::ostream& operator<<(std::ostream& stream, const eMotion& t)
{
switch (t)
{
case EmberNs::eMotion::MOTION_SIN:
stream << "sin";
break;
case EmberNs::eMotion::MOTION_TRIANGLE:
stream << "triangle";
break;
case EmberNs::eMotion::MOTION_HILL:
stream << "hill";
break;
case EmberNs::eMotion::MOTION_SAW:
stream << "saw";
break;
default:
stream << "error";
break;
}
return stream;
}
}
#pragma once
#include "EmberPch.h"
/// <summary>
/// Basic #defines used throughout the library.
/// </summary>
#ifdef _WIN32
#if defined(BUILDING_EMBER)
#define EMBER_API __declspec(dllexport)
#else
#define EMBER_API __declspec(dllimport)
#endif
#else
#define EMBER_API
#define fopen_s(pFile,filename,mode) ((*(pFile)=fopen((filename),(mode)))==nullptr)
#define _stat stat
#define _fstat fstat
#define _stricmp strcasecmp
typedef int errno_t;
#endif
#define RESTRICT __restrict//This might make things faster, unsure if it really does though.
//#define RESTRICT
//Wrap the sincos function for Macs and PC.
#if defined(__APPLE__) || defined(_MSC_VER)
#define sincos(x, s, c) *(s)=std::sin(x); *(c)=std::cos(x);
#else
static void sincos(float x, float* s, float* c)
{
*s = std::sin(x);
*c = std::cos(x);
}
#endif
namespace EmberNs
{
#define EMBER_VERSION "22.21.4.2"
//#define FLAM3_COMPAT 1//Uncomment this if you want full compatibility with flam3 regarding some of the trig-based variations in Variations01.h
#define EPS6 T(1e-6)
#define EPS std::numeric_limits<T>::epsilon()//Apoplugin.h uses -20, but it's more mathematically correct to do it this way.
#define ISAAC_SIZE 4
#define MEMALIGN 32
#define DE_THRESH 100
#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 M_PI2 (T{M_PI_2})
#define M_PI4 (T{M_PI_4})
#define M_SQRT3 T(1.7320508075688772935274463415059)
#define M_SQRT3_2 T(0.86602540378443864676372317075294)
#define M_SQRT3_3 T(0.57735026918962576450914878050196)
#define M_SQRT5 T(2.2360679774997896964091736687313)
#define M_PHI T(1.61803398874989484820458683436563)
#define M_1_2PI T(0.15915494309189533576888376337251)
#define M_PI3 T(1.0471975511965977461542144610932)
#define M_PI6 T(0.52359877559829887307710723054658)
#define COLORMAP_LENGTH 256//These will need to change if 2D palette support is ever added, or variable sized palettes.
#define WHITE 255
#define DEFAULT_SBS (1024 * 10)
//#define XC(c) ((const xmlChar*)(c))
#define XC(c) (reinterpret_cast<const xmlChar*>(c))
#define CX(c) (reinterpret_cast<char*>(c))
#define CCX(c) (reinterpret_cast<const char*>(c))
#define BadVal(x) (std::isnan(x))
#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()
#define FLOAT_MAX_TAN 8388607.0f
#define FLOAT_MIN_TAN -FLOAT_MAX_TAN
#define CURVES_LENGTH 65536
#define CURVES_LENGTH_M1 65535.0f
#define ONE_OVER_CURVES_LENGTH_M1 1.525902189669e-5f
#define EMPTYFIELD -9999
typedef unsigned char et;
typedef std::lock_guard <std::recursive_mutex> rlg;
/// <summary>
/// Thin wrapper around getting the current time in milliseconds.
/// </summary>
typedef std::chrono::high_resolution_clock Clock;
typedef std::chrono::duration<double, std::ratio<1, 1000>> DoubleMs;
typedef std::chrono::time_point<Clock, DoubleMs> DoubleMsTimePoint;
static inline DoubleMsTimePoint NowMsD() noexcept { return time_point_cast<DoubleMs>(Clock::now()); }
static inline size_t NowMs() noexcept { return duration_cast<milliseconds>(Clock::now().time_since_epoch()).count(); }
//#ifndef byte
// typedef unsigned char byte;
//#endif
#define DO_DOUBLE 1//Comment this out for shorter build times during development. Always uncomment for release.
//#define ISAAC_FLAM3_DEBUG 1//This is almost never needed, but is very useful when troubleshooting difficult bugs. Enable it to do a side by side comparison with flam3.
//These two must always match.
#ifdef _WIN32
#define ALIGN __declspec(align(16))
#define STATIC static
#else
#define ALIGN __attribute__ ((aligned (16)))
#define STATIC
#endif
#define ALIGN_CL "((aligned (16)))"//The extra parens are necessary.
#if GLM_VERSION >= 96
#define v2T glm::tvec2<T, glm::defaultp>
#define v3T glm::tvec3<T, glm::defaultp>
#define v4T glm::tvec4<T, glm::defaultp>
#define v2F glm::tvec2<float, glm::defaultp>
#define v4F glm::tvec4<float, glm::defaultp>
#define v4D glm::tvec4<double, glm::defaultp>
#define v4bT glm::tvec4<bucketT, glm::defaultp>
#define m2T glm::tmat2x2<T, glm::defaultp>
#define m3T glm::tmat3x3<T, glm::defaultp>
#define m4T glm::tmat4x4<T, glm::defaultp>
#define m23T glm::tmat2x3<T, glm::defaultp>
typedef vector<glm::tvec4<float, glm::defaultp>> vv4F;
#else
#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 v2F glm::detail::tvec2<float, glm::defaultp>
#define v4F glm::detail::tvec4<float, glm::defaultp>
#define v4D glm::detail::tvec4<double, glm::defaultp>
#define v4bT glm::detail::tvec4<bucketT, 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>
typedef vector<glm::detail::tvec4<float, glm::defaultp>> vv4F;
#endif
enum class eInterp : et { EMBER_INTERP_LINEAR = 0, EMBER_INTERP_SMOOTH = 1 };
enum class eAffineInterp : et { AFFINE_INTERP_LINEAR = 0, AFFINE_INTERP_LOG = 1, AFFINE_INTERP_COMPAT = 2, AFFINE_INTERP_OLDER = 3 };
enum class ePaletteMode : et { PALETTE_STEP = 0, PALETTE_LINEAR = 1 };
enum class ePaletteInterp : et { INTERP_HSV = 0, INTERP_SWEEP = 1 };
enum class eMotion : et { MOTION_SIN = 1, MOTION_TRIANGLE = 2, MOTION_HILL = 3, MOTION_SAW = 4 };
enum class eProcessAction : et { NOTHING = 0, ACCUM_ONLY = 1, FILTER_AND_ACCUM = 2, KEEP_ITERATING = 3, FULL_RENDER = 4 };
enum class eProcessState : et { NONE = 0, ITER_STARTED = 1, ITER_DONE = 2, FILTER_DONE = 3, ACCUM_DONE = 4 };
enum class eInteractiveFilter : et { FILTER_LOG = 0, FILTER_DE = 1 };
enum class eScaleType : et { SCALE_NONE = 0, SCALE_WIDTH = 1, SCALE_HEIGHT = 2 };
enum class eRenderStatus : et { RENDER_OK = 0, RENDER_ERROR = 1, RENDER_ABORT = 2 };
enum class eEmberMotionParam : et//These must remain in this order forever.
{
FLAME_MOTION_NONE,
FLAME_MOTION_ZOOM,
FLAME_MOTION_ZPOS,
FLAME_MOTION_PERSPECTIVE,
FLAME_MOTION_YAW,
FLAME_MOTION_PITCH,
FLAME_MOTION_DEPTH_BLUR,
FLAME_MOTION_CENTER_X,
FLAME_MOTION_CENTER_Y,
FLAME_MOTION_ROTATE,
FLAME_MOTION_BRIGHTNESS,
FLAME_MOTION_GAMMA,
FLAME_MOTION_GAMMA_THRESH,
FLAME_MOTION_HIGHLIGHT_POWER,
FLAME_MOTION_K2,
FLAME_MOTION_RAND_RANGE,
FLAME_MOTION_BACKGROUND_R,
FLAME_MOTION_BACKGROUND_G,
FLAME_MOTION_BACKGROUND_B,
FLAME_MOTION_VIBRANCY,
FLAME_MOTION_BLUR_CURVE
};
/// <summary>
/// Thin wrapper to allow << operator on interp type.
/// </summary>
/// <param name="stream">The stream to insert into</param>
/// <param name="t">The type whose string representation will be inserted into the stream</param>
/// <returns></returns>
static std::ostream& operator<<(std::ostream& stream, const eInterp& t)
{
switch (t)
{
case EmberNs::eInterp::EMBER_INTERP_LINEAR:
stream << "linear";
break;
case EmberNs::eInterp::EMBER_INTERP_SMOOTH:
stream << "smooth";
break;
default:
stream << "error";
break;
}
return stream;
}
/// <summary>
/// Thin wrapper to allow << operator on affine interp type.
/// </summary>
/// <param name="stream">The stream to insert into</param>
/// <param name="t">The type whose string representation will be inserted into the stream</param>
/// <returns></returns>
static std::ostream& operator<<(std::ostream& stream, const eAffineInterp& t)
{
switch (t)
{
case EmberNs::eAffineInterp::AFFINE_INTERP_LINEAR:
stream << "linear";
break;
case EmberNs::eAffineInterp::AFFINE_INTERP_LOG:
stream << "log";
break;
case EmberNs::eAffineInterp::AFFINE_INTERP_COMPAT:
stream << "compat";
break;
case EmberNs::eAffineInterp::AFFINE_INTERP_OLDER:
stream << "older";
break;
default:
stream << "error";
break;
}
return stream;
}
/// <summary>
/// Thin wrapper to allow << operator on palette mode type.
/// </summary>
/// <param name="stream">The stream to insert into</param>
/// <param name="t">The type whose string representation will be inserted into the stream</param>
/// <returns></returns>
static std::ostream& operator<<(std::ostream& stream, const ePaletteMode& t)
{
switch (t)
{
case EmberNs::ePaletteMode::PALETTE_STEP:
stream << "step";
break;
case EmberNs::ePaletteMode::PALETTE_LINEAR:
stream << "linear";
break;
default:
stream << "error";
break;
}
return stream;
}
/// <summary>
/// Thin wrapper to allow << operator on palette interp type.
/// </summary>
/// <param name="stream">The stream to insert into</param>
/// <param name="t">The type whose string representation will be inserted into the stream</param>
/// <returns></returns>
static std::ostream& operator<<(std::ostream& stream, const ePaletteInterp& t)
{
switch (t)
{
case EmberNs::ePaletteInterp::INTERP_HSV:
stream << "hsv";
break;
case EmberNs::ePaletteInterp::INTERP_SWEEP:
stream << "sweep";
break;
default:
stream << "error";
break;
}
return stream;
}
/// <summary>
/// Thin wrapper to allow << operator on scale type.
/// </summary>
/// <param name="stream">The stream to insert into</param>
/// <param name="t">The type whose string representation will be inserted into the stream</param>
/// <returns></returns>
static std::ostream& operator<<(std::ostream& stream, const eScaleType& t)
{
switch (t)
{
case EmberNs::eScaleType::SCALE_NONE:
stream << "none";
break;
case EmberNs::eScaleType::SCALE_WIDTH:
stream << "width";
break;
case EmberNs::eScaleType::SCALE_HEIGHT:
stream << "height";
break;
default:
stream << "error";
break;
}
return stream;
}
/// <summary>
/// Thin wrapper to allow << operator on motion type.
/// </summary>
/// <param name="stream">The stream to insert into</param>
/// <param name="t">The type whose string representation will be inserted into the stream</param>
/// <returns></returns>
static std::ostream& operator<<(std::ostream& stream, const eMotion& t)
{
switch (t)
{
case EmberNs::eMotion::MOTION_SIN:
stream << "sin";
break;
case EmberNs::eMotion::MOTION_TRIANGLE:
stream << "triangle";
break;
case EmberNs::eMotion::MOTION_HILL:
stream << "hill";
break;
case EmberNs::eMotion::MOTION_SAW:
stream << "saw";
break;
default:
stream << "error";
break;
}
return stream;
}
}
+145 -145
View File
@@ -1,145 +1,145 @@
#pragma once
#include "EmberDefines.h"
namespace EmberNs
{
/// <summary>
/// Thin derivation of pair<eEmberMotionParam, T> for the element type
/// of EmberMotion<T>::m_MotionParams to allow for copying vectors
/// of different types of T.
/// Template argument expected to be float or double.
/// </summary>
template <typename T>
class EMBER_API MotionParam : public pair <eEmberMotionParam, T>
{
public:
/// <summary>
/// Default constructor, which calls the base, which sets first and second to their defaults.
/// </summary>
MotionParam() = default;
/// <summary>
/// Member-wise constructor.
/// </summary>
/// <param name="e">The eEmberMotionParam value to assign to first</param>
/// <param name="t">The T value to assign to second</param>
MotionParam(eEmberMotionParam e, T t)
: pair<eEmberMotionParam, T>(e, t)
{
}
/// <summary>
/// Default copy constructor.
/// </summary>
/// <param name="other">The MotionParam object to copy</param>
MotionParam(const MotionParam<T>& other)
: pair <eEmberMotionParam, T>()
{
operator=<T>(other);
}
/// <summary>
/// Copy constructor to copy a MotionParam object of type U.
/// </summary>
/// <param name="other">The MotionParam object to copy</param>
template <typename U>
MotionParam(const MotionParam<U>& other)
{
operator=<U>(other);
}
/// <summary>
/// Default assignment operator.
/// </summary>
/// <param name="other">The MotionParam object to copy</param>
MotionParam<T>& operator = (const MotionParam<T>& other)
{
if (this != &other)
MotionParam<T>::operator=<T>(other);
return *this;
}
/// <summary>
/// Assignment operator to assign a MotionParam object of type U.
/// </summary>
/// <param name="other">The MotionParam object to copy.</param>
/// <returns>Reference to updated self</returns>
template <typename U>
MotionParam& operator = (const MotionParam<U>& other)
{
this->first = other.first;
this->second = static_cast<T>(other.second);
return *this;
}
};
/// <summary>
/// EmberMotion elements allow for motion of the flame parameters such as zoom, yaw, pitch and friends
/// The values in these elements can be used to modify flame parameters during rotation in much the same
/// way as motion elements on xforms do.
/// Template argument expected to be float or double.
/// </summary>
template <typename T>
class EMBER_API EmberMotion
{
public:
/// <summary>
/// Default constructor to initialize motion freq and offset to 0 and the motion func to SIN.
/// </summary>
EmberMotion() = default;
~EmberMotion() = default;
/// <summary>
/// Default copy constructor.
/// </summary>
/// <param name="other">The EmberMotion object to copy</param>
EmberMotion(const EmberMotion<T>& other)
{
operator=<T>(other);
}
/// <summary>
/// Copy constructor to copy a EmberMotion object of type U.
/// </summary>
/// <param name="other">The EmberMotion object to copy</param>
template <typename U>
EmberMotion(const EmberMotion<U>& other)
{
operator=<U>(other);
}
/// <summary>
/// Default assignment operator.
/// </summary>
/// <param name="other">The EmberMotion object to copy</param>
EmberMotion<T>& operator = (const EmberMotion<T>& other)
{
if (this != &other)
EmberMotion<T>::operator=<T>(other);
return *this;
}
/// <summary>
/// Assignment operator to assign a EmberMotion object of type U.
/// </summary>
/// <param name="other">The EmberMotion object to copy.</param>
/// <returns>Reference to updated self</returns>
template <typename U>
EmberMotion& operator = (const EmberMotion<U>& other)
{
CopyCont(m_MotionParams, other.m_MotionParams);
m_MotionFunc = other.m_MotionFunc;
m_MotionFreq = static_cast<T>(other.m_MotionFreq);
m_MotionOffset = static_cast<T>(other.m_MotionOffset);
return *this;
}
T m_MotionFreq = 0;
T m_MotionOffset = 0;
eMotion m_MotionFunc = eMotion::MOTION_SIN;
vector<MotionParam<T>> m_MotionParams;
};
}
#pragma once
#include "EmberDefines.h"
namespace EmberNs
{
/// <summary>
/// Thin derivation of pair<eEmberMotionParam, T> for the element type
/// of EmberMotion<T>::m_MotionParams to allow for copying vectors
/// of different types of T.
/// Template argument expected to be float or double.
/// </summary>
template <typename T>
class EMBER_API MotionParam : public pair <eEmberMotionParam, T>
{
public:
/// <summary>
/// Default constructor, which calls the base, which sets first and second to their defaults.
/// </summary>
MotionParam() = default;
/// <summary>
/// Member-wise constructor.
/// </summary>
/// <param name="e">The eEmberMotionParam value to assign to first</param>
/// <param name="t">The T value to assign to second</param>
MotionParam(eEmberMotionParam e, T t)
: pair<eEmberMotionParam, T>(e, t)
{
}
/// <summary>
/// Default copy constructor.
/// </summary>
/// <param name="other">The MotionParam object to copy</param>
MotionParam(const MotionParam<T>& other)
: pair <eEmberMotionParam, T>()
{
operator=<T>(other);
}
/// <summary>
/// Copy constructor to copy a MotionParam object of type U.
/// </summary>
/// <param name="other">The MotionParam object to copy</param>
template <typename U>
MotionParam(const MotionParam<U>& other)
{
operator=<U>(other);
}
/// <summary>
/// Default assignment operator.
/// </summary>
/// <param name="other">The MotionParam object to copy</param>
MotionParam<T>& operator = (const MotionParam<T>& other)
{
if (this != &other)
MotionParam<T>::operator=<T>(other);
return *this;
}
/// <summary>
/// Assignment operator to assign a MotionParam object of type U.
/// </summary>
/// <param name="other">The MotionParam object to copy.</param>
/// <returns>Reference to updated self</returns>
template <typename U>
MotionParam& operator = (const MotionParam<U>& other)
{
this->first = other.first;
this->second = static_cast<T>(other.second);
return *this;
}
};
/// <summary>
/// EmberMotion elements allow for motion of the flame parameters such as zoom, yaw, pitch and friends
/// The values in these elements can be used to modify flame parameters during rotation in much the same
/// way as motion elements on xforms do.
/// Template argument expected to be float or double.
/// </summary>
template <typename T>
class EMBER_API EmberMotion
{
public:
/// <summary>
/// Default constructor to initialize motion freq and offset to 0 and the motion func to SIN.
/// </summary>
EmberMotion() = default;
~EmberMotion() = default;
/// <summary>
/// Default copy constructor.
/// </summary>
/// <param name="other">The EmberMotion object to copy</param>
EmberMotion(const EmberMotion<T>& other)
{
operator=<T>(other);
}
/// <summary>
/// Copy constructor to copy a EmberMotion object of type U.
/// </summary>
/// <param name="other">The EmberMotion object to copy</param>
template <typename U>
EmberMotion(const EmberMotion<U>& other)
{
operator=<U>(other);
}
/// <summary>
/// Default assignment operator.
/// </summary>
/// <param name="other">The EmberMotion object to copy</param>
EmberMotion<T>& operator = (const EmberMotion<T>& other)
{
if (this != &other)
EmberMotion<T>::operator=<T>(other);
return *this;
}
/// <summary>
/// Assignment operator to assign a EmberMotion object of type U.
/// </summary>
/// <param name="other">The EmberMotion object to copy.</param>
/// <returns>Reference to updated self</returns>
template <typename U>
EmberMotion& operator = (const EmberMotion<U>& other)
{
CopyCont(m_MotionParams, other.m_MotionParams);
m_MotionFunc = other.m_MotionFunc;
m_MotionFreq = static_cast<T>(other.m_MotionFreq);
m_MotionOffset = static_cast<T>(other.m_MotionOffset);
return *this;
}
T m_MotionFreq = 0;
T m_MotionOffset = 0;
eMotion m_MotionFunc = eMotion::MOTION_SIN;
vector<MotionParam<T>> m_MotionParams;
};
}
+1 -1
View File
@@ -1 +1 @@
#include "EmberPch.h"
#include "EmberPch.h"
+90 -90
View File
@@ -1,90 +1,90 @@
#ifdef _WIN32
#pragma once
#endif
/// <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
#define __TBB_NO_IMPLICIT_LINKAGE 1//Prevent tbb from automatically looking for tbb_debug.lib. We only care about the release tbb.lib/dll.
#ifdef _WIN32
#pragma warning(disable : 4251; disable : 4661; disable : 4100)
#define basename(x) _strdup(x)
#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 <array>
#include <chrono>
#include <complex>
#include <cstdint>
#include <fstream>
#include <functional>
#include <inttypes.h>
#include <iostream>
#include <iomanip>
#include <limits>
#include <list>
#ifdef __APPLE__
#include <malloc/malloc.h>
#else
#include <malloc.h>
#endif
#include <map>
#include <math.h>
#include <memory>
#include <mutex>
#include <numeric>
#include <ostream>
#include <sstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <thread>
#include <time.h>
#include <type_traits>
#include <vector>
#include <unordered_map>
//Third party headers.
#ifdef _WIN32
#include "libxml/parser.h"
#else
#include "libxml2/libxml/parser.h"
#endif
#define GLM_FORCE_RADIANS 1
#define GLM_ENABLE_EXPERIMENTAL 1
#ifndef __APPLE__
#define GLM_FORCE_INLINE 1
#endif
//glm is what's used for matrix math.
#include <glm/glm.hpp>
#if GLM_VERSION <= 990
#include <glm/detail/type_int.hpp>
#endif
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/string_cast.hpp>
using namespace std;
using namespace std::chrono;
using namespace glm;
using namespace glm::detail;
using glm::uint;
using glm::uint16;
#ifdef _WIN32
#pragma once
#endif
/// <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
#define __TBB_NO_IMPLICIT_LINKAGE 1//Prevent tbb from automatically looking for tbb_debug.lib. We only care about the release tbb.lib/dll.
#ifdef _WIN32
#pragma warning(disable : 4251; disable : 4661; disable : 4100)
#define basename(x) _strdup(x)
#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 <array>
#include <chrono>
#include <complex>
#include <cstdint>
#include <fstream>
#include <functional>
#include <inttypes.h>
#include <iostream>
#include <iomanip>
#include <limits>
#include <list>
#ifdef __APPLE__
#include <malloc/malloc.h>
#else
#include <malloc.h>
#endif
#include <map>
#include <math.h>
#include <memory>
#include <mutex>
#include <numeric>
#include <ostream>
#include <sstream>
#include <sys/types.h>
#include <sys/stat.h>
#include <thread>
#include <time.h>
#include <type_traits>
#include <vector>
#include <unordered_map>
//Third party headers.
#ifdef _WIN32
#include "libxml/parser.h"
#else
#include "libxml2/libxml/parser.h"
#endif
#define GLM_FORCE_RADIANS 1
#define GLM_ENABLE_EXPERIMENTAL 1
#ifndef __APPLE__
#define GLM_FORCE_INLINE 1
#endif
//glm is what's used for matrix math.
#include <glm/glm.hpp>
#if GLM_VERSION <= 990
#include <glm/detail/type_int.hpp>
#endif
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/string_cast.hpp>
using namespace std;
using namespace std::chrono;
using namespace glm;
using namespace glm::detail;
using glm::uint;
using glm::uint16;
File diff suppressed because it is too large Load Diff
+42 -42
View File
@@ -1,42 +1,42 @@
#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() = default;
~EmberToXml() = default;
EmberToXml(const EmberToXml<T>& e) = delete;
bool Save(const string& filename, Ember<T>& ember, size_t printEditDepth, bool doEdits, bool hexPalette, bool append = false, bool start = false, bool finish = false);
template <typename Alloc, template <typename, typename> class C>
bool Save(const string& filename, C<Ember<T>, Alloc>& embers, size_t printEditDepth, bool doEdits, bool hexPalette, bool append, bool start, bool finish);
string ToString(Ember<T>& ember, const string& extraAttributes, size_t printEditDepth, bool doEdits, bool hexPalette = true);
xmlDocPtr CreateNewEditdoc(Ember<T>* parent0, Ember<T>* parent1, const string& action, const string& nick, const string& url, const string& id, const string& comment, intmax_t sheepGen = 0, intmax_t sheepId = 0);
private:
string ToString(Xform<T>& xform, size_t xformCount, bool isFinal, bool doMotion);
string ToString(xmlNodePtr editNode, size_t tabs, bool formatting, size_t printEditDepth);
string ToString(const EmberMotion<T>& motion);
void AddFilenameWithoutAmpersand(xmlNodePtr node, string& filename);
};
}
#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() = default;
~EmberToXml() = default;
EmberToXml(const EmberToXml<T>& e) = delete;
bool Save(const string& filename, Ember<T>& ember, size_t printEditDepth, bool doEdits, bool hexPalette, bool append = false, bool start = false, bool finish = false);
template <typename Alloc, template <typename, typename> class C>
bool Save(const string& filename, C<Ember<T>, Alloc>& embers, size_t printEditDepth, bool doEdits, bool hexPalette, bool append, bool start, bool finish);
string ToString(Ember<T>& ember, const string& extraAttributes, size_t printEditDepth, bool doEdits, bool hexPalette = true);
xmlDocPtr CreateNewEditdoc(Ember<T>* parent0, Ember<T>* parent1, const string& action, const string& nick, const string& url, const string& id, const string& comment, intmax_t sheepGen = 0, intmax_t sheepId = 0);
private:
string ToString(Xform<T>& xform, size_t xformCount, bool isFinal, bool doMotion);
string ToString(xmlNodePtr editNode, size_t tabs, bool formatting, size_t printEditDepth);
string ToString(const EmberMotion<T>& motion);
void AddFilenameWithoutAmpersand(xmlNodePtr node, string& filename);
};
}
+1029 -1029
View File
File diff suppressed because it is too large Load Diff
+537 -537
View File
File diff suppressed because it is too large Load Diff
+611 -611
View File
File diff suppressed because it is too large Load Diff
+632 -632
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+60 -60
View File
@@ -1,60 +1,60 @@
#pragma once
#include "Palette.h"
#include "Point.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.
/// This can hold read only palettes, as well as user created and modifiable ones.
/// The key in the map is the fully qualified path and filename to each palette file.
/// Despite the keys being full paths, the same filename cannot be inserted twice, even if they reside
/// in different folders. Functions are provided to retrieve palette files via filename only, or full path.
/// Template argument should always be float (which makes the templating of this class pointless).
/// </summary>
template <typename T>
class EMBER_API PaletteList : public EmberReport, public Singleton<PaletteList<T>>
{
public:
const char* m_DefaultFilename = "flam3-palettes.xml";
bool AddPaletteFile(const string& filename, const vector<Palette<T>>& palettes);
bool AddEmptyPaletteFile(const string& filename);
bool AddPaletteToFile(const string& filename, const Palette<T>& palette);
bool Replace(const string& filename, const Palette<T>& palette);
bool Replace(const string& filename, const Palette<T>& palette, int index);
bool Delete(const string& filename, int index);
bool Add(const string& filename, bool force = false);
Palette<T>* GetRandomPalette();
Palette<T>* GetPaletteByFilename(const string& filename, size_t i);
Palette<T>* GetPaletteByFullPath(const string& filename, size_t i);
Palette<T>* GetPaletteByName(const string& filename, const string& name);
vector<Palette<T>>* GetPaletteListByFilename(const string& filename);
vector<Palette<T>>* GetPaletteListByFullPath(const string& filename);
vector<Palette<T>>* GetPaletteListByFullPathOrFilename(const string& filename);
string GetFullPathFromFilename(const string& filename);
bool GetHueAdjustedPalette(const string& filename, size_t i, T hue, Palette<T>& palette);
void Clear();
size_t Size();
size_t Size(size_t index);
size_t Size(const string& s);
const string& Name(size_t index);
bool IsModifiable(const string& filename);
const map<string, vector<Palette<T>>>& Palettes() const;
SINGLETON_DERIVED_DECL(PaletteList<T>);
private:
PaletteList();
bool Save(const string& filename);
void ParsePalettes(xmlNode* node, const shared_ptr<string>& filename, vector<Palette<T>>& palettes);
bool ParsePalettes(const string& buf, const shared_ptr<string>& filename, vector<Palette<T>>& palettes);
map<string, vector<Palette<T>>> s_Palettes;//The map of filenames to vectors that store the palettes.
};
}
#pragma once
#include "Palette.h"
#include "Point.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.
/// This can hold read only palettes, as well as user created and modifiable ones.
/// The key in the map is the fully qualified path and filename to each palette file.
/// Despite the keys being full paths, the same filename cannot be inserted twice, even if they reside
/// in different folders. Functions are provided to retrieve palette files via filename only, or full path.
/// Template argument should always be float (which makes the templating of this class pointless).
/// </summary>
template <typename T>
class EMBER_API PaletteList : public EmberReport, public Singleton<PaletteList<T>>
{
public:
const char* m_DefaultFilename = "flam3-palettes.xml";
bool AddPaletteFile(const string& filename, const vector<Palette<T>>& palettes);
bool AddEmptyPaletteFile(const string& filename);
bool AddPaletteToFile(const string& filename, const Palette<T>& palette);
bool Replace(const string& filename, const Palette<T>& palette);
bool Replace(const string& filename, const Palette<T>& palette, int index);
bool Delete(const string& filename, int index);
bool Add(const string& filename, bool force = false);
Palette<T>* GetRandomPalette();
Palette<T>* GetPaletteByFilename(const string& filename, size_t i);
Palette<T>* GetPaletteByFullPath(const string& filename, size_t i);
Palette<T>* GetPaletteByName(const string& filename, const string& name);
vector<Palette<T>>* GetPaletteListByFilename(const string& filename);
vector<Palette<T>>* GetPaletteListByFullPath(const string& filename);
vector<Palette<T>>* GetPaletteListByFullPathOrFilename(const string& filename);
string GetFullPathFromFilename(const string& filename);
bool GetHueAdjustedPalette(const string& filename, size_t i, T hue, Palette<T>& palette);
void Clear();
size_t Size();
size_t Size(size_t index);
size_t Size(const string& s);
const string& Name(size_t index);
bool IsModifiable(const string& filename);
const map<string, vector<Palette<T>>>& Palettes() const;
SINGLETON_DERIVED_DECL(PaletteList<T>);
private:
PaletteList();
bool Save(const string& filename);
void ParsePalettes(xmlNode* node, const shared_ptr<string>& filename, vector<Palette<T>>& palettes);
bool ParsePalettes(const string& buf, const shared_ptr<string>& filename, vector<Palette<T>>& palettes);
map<string, vector<Palette<T>>> s_Palettes;//The map of filenames to vectors that store the palettes.
};
}
+215 -217
View File
@@ -1,217 +1,215 @@
#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() = default;
~Point() = default;
/// <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_Opacity = point.m_Opacity;
return *this;
}
//Set spatial and color coordinates to zero, with full visibility.
T m_X = 0;
T m_Y = 0;
T m_Z = 0;
T m_ColorX = 0;
//T m_ColorY;
T m_Opacity = 1;
};
/// <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>
struct EMBER_API Color : public v4T
{
#ifndef _WIN32
using v4T::r;
using v4T::g;
using v4T::b;
using v4T::a;
#endif
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)
: v4T()
{
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)
{
#ifdef _WIN32
v4T::operator=<U>(color);
#else
v4T::template operator=<U>(color);
#endif
return *this;
}
/// <summary>
/// Member-wise constructor.
/// </summary>
/// <param name="rr">The red value, either 0-1 or 0-255.</param>
/// <param name="gg">The green value, either 0-1 or 0-255.</param>
/// <param name="bb">The blue value, either 0-1 or 0-255.</param>
/// <param name="aa">The alpha value, either 0-1 or 0-255.</param>
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 };
}
};
}
#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() = default;
~Point() = default;
/// <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_Opacity = point.m_Opacity;
return *this;
}
//Set spatial and color coordinates to zero, with full visibility.
T m_X = 0;
T m_Y = 0;
T m_Z = 0;
T m_ColorX = 0;
//T m_ColorY;
T m_Opacity = 1;
};
/// <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>
struct EMBER_API Color : public v4T
{
using v4T::r;
using v4T::g;
using v4T::b;
using v4T::a;
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)
: v4T()
{
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)
{
#ifdef _WIN32
v4T::operator=<U>(color);
#else
v4T::template operator=<U>(color);
#endif
return *this;
}
/// <summary>
/// Member-wise constructor.
/// </summary>
/// <param name="rr">The red value, either 0-1 or 0-255.</param>
/// <param name="gg">The green value, either 0-1 or 0-255.</param>
/// <param name="bb">The blue value, either 0-1 or 0-255.</param>
/// <param name="aa">The alpha value, either 0-1 or 0-255.</param>
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 };
}
};
}
+1777 -1778
View File
File diff suppressed because it is too large Load Diff
+206 -206
View File
@@ -1,206 +1,206 @@
#pragma once
#include "RendererBase.h"
#include "Iterator.h"
#include "SpatialFilter.h"
#include "TemporalFilter.h"
#include "Interpolate.h"
#include "CarToRas.h"
#include "EmberToXml.h"
#include "Spline.h"
/// <summary>
/// Renderer.
/// </summary>
namespace EmberNs
{
/// <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 overridden 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 must always be float.
/// </summary>
template <typename T, typename bucketT>
class EMBER_API Renderer : public RendererBase
{
public:
Renderer();
Renderer(const Renderer<T, bucketT>& renderer) = delete;
Renderer<T, bucketT>& operator = (const Renderer<T, bucketT>& renderer) = delete;
virtual ~Renderer() = default;
//Non-virtual processing functions.
void AddEmber(Ember<T>& ember);
bool AssignIterator();
//Virtual processing functions overriden from RendererBase.
void Prepare() override;
void ComputeBounds() override;
void ComputeQuality() override;
void ComputeCamera() override;
void SetEmber(const Ember<T>& ember, eProcessAction action = eProcessAction::FULL_RENDER, bool prep = false) override;
template <typename C>
void SetEmber(const C& embers);
void MoveEmbers(vector<Ember<T>>& embers);
void SetExternalEmbersPointer(vector<Ember<T>>* embers);
bool CreateDEFilter(bool& newAlloc) override;
bool CreateSpatialFilter(bool& newAlloc) override;
bool CreateTemporalFilter(bool& newAlloc) override;
size_t HistBucketSize() const override { return sizeof(tvec4<bucketT, glm::defaultp>); }
eRenderStatus Run(vector<v4F>& finalImage, double time = 0, size_t subBatchCountOverride = 0, bool forceOutput = false, size_t finalOffset = 0) override;
EmberImageComments ImageComments(const EmberStats& stats, size_t printEditDepth = 0, bool hexPalette = true) override;
protected:
//New virtual functions to be overridden in derived renderers that use the GPU, but not accessed outside.
virtual void MakeDmap(T colorScalar);
virtual bool Alloc(bool histOnly = false);
virtual bool ResetBuckets(bool resetHist = true, bool resetAccum = true);
virtual eRenderStatus LogScaleDensityFilter(bool forceOutput = false);
virtual eRenderStatus GaussianDensityFilter();
virtual eRenderStatus AccumulatorToFinalImage(vector<v4F>& pixels, size_t finalOffset);
virtual EmberStats Iterate(size_t iterCount, size_t temporalSample);
virtual void ComputeCurves();
public:
//Non-virtual render properties, getters and setters.
inline T PixelAspectRatio() const;
void PixelAspectRatio(T pixelAspectRatio);
//Non-virtual renderer properties, getters only.
inline T Scale() const;
inline T PixelsPerUnitX() const;
inline T PixelsPerUnitY() const;
inline bucketT K1() const;
inline bucketT K2() const;
inline const CarToRas<T>& CoordMap() const;
inline tvec4<bucketT, glm::defaultp>* HistBuckets();
inline tvec4<bucketT, glm::defaultp>* AccumulatorBuckets();
inline SpatialFilter<bucketT>* GetSpatialFilter();
inline TemporalFilter<T>* GetTemporalFilter();
//Virtual renderer properties overridden from RendererBase, getters only.
double ScaledQuality() const override;
double LowerLeftX(bool gutter = true) const override;
double LowerLeftY(bool gutter = true) const override;
double UpperRightX(bool gutter = true) const override;
double UpperRightY(bool gutter = true) const override;
DensityFilterBase* GetDensityFilter() override;
//Non-virtual ember wrappers, getters only.
inline bool XaosPresent() const;
inline size_t Supersample() const;
inline size_t 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 bucketT Brightness() const;
inline bucketT Gamma() const;
inline bucketT Vibrancy() const;
inline bucketT GammaThresh() const;
inline bucketT HighlightPower() const;
inline Color<T> Background() const;
inline const Xform<T>* Xforms() const;
inline Xform<T>* NonConstXforms();
inline size_t XformCount() const;
inline const Xform<T>* FinalXform() const;
inline Xform<T>* NonConstFinalXform();
inline bool UseFinalXform() const;
inline const Palette<float>* GetPalette() const;
inline ePaletteMode PaletteMode() const;
//Virtual ember wrappers overridden from RendererBase, getters only.
size_t TemporalSamples() const override;
size_t FinalRasW() const override;
size_t FinalRasH() const override;
size_t SubBatchSize() const override;
size_t FuseCount() const override;
//Non-virtual iterator wrappers.
const byte* XformDistributions() const;
size_t XformDistributionsSize() const;
Point<T>* Samples(size_t threadIndex) const;
protected:
//Non-virtual functions that might be needed by a derived class.
void PrepFinalAccumVals(Color<bucketT>& background, bucketT& g, bucketT& linRange, bucketT& vibrancy);
private:
//Miscellaneous non-virtual functions used only in this class.
void Accumulate(QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand, Point<T>* samples, size_t sampleCount, const Palette<bucketT>* palette);
void AddToAccum(const tvec4<bucketT, glm::defaultp>& bucket, intmax_t i, intmax_t ii, intmax_t j, intmax_t jj);
template <typename accumT> void GammaCorrection(tvec4<bucketT, glm::defaultp>& bucket, Color<bucketT>& background, bucketT g, bucketT linRange, bucketT vibrancy, bool scale, accumT* correctedChannels);
void CurveAdjust(bucketT& a, const glm::length_t& index);
void VectorizedLogScale(size_t row, size_t rowEnd);
protected:
//public:
T m_Scale;
T m_PixelsPerUnitX;
T m_PixelsPerUnitY;
T m_PixelAspectRatio = 1;
T m_LowerLeftX;
T m_LowerLeftY;
T m_UpperRightX;
T m_UpperRightY;
bucketT m_K1;
bucketT m_K2;
bucketT m_Vibrancy;//Accumulate these after each temporal sample.
bucketT m_Gamma;
T m_ScaledQuality;
Color<bucketT> m_Background;//This is a scaled copy of the m_Background member of m_Ember, but with a type of bucketT.
Affine2D<T> m_RotMat;
Ember<T> m_Ember;
Ember<T> m_TempEmber;
Ember<T> m_LastEmber;
private:
vector<Ember<T>> m_Embers;
protected:
vector<Ember<T>>* m_EmbersP = &m_Embers;
vector<Ember<T>> m_ThreadEmbers;
Interpolater<T> m_Interpolater;
CarToRas<T> m_CarToRas;
unique_ptr<StandardIterator<T>> m_StandardIterator = make_unique<StandardIterator<T>>();
unique_ptr<XaosIterator<T>> m_XaosIterator = make_unique<XaosIterator<T>>();
Iterator<T>* m_Iterator = m_StandardIterator.get();
Palette<bucketT> m_Dmap;
vector<tvec4<bucketT, glm::defaultp>> m_Csa;
vector<tvec4<bucketT, glm::defaultp>> m_HistBuckets;
vector<tvec4<bucketT, glm::defaultp>> m_AccumulatorBuckets;
unique_ptr<SpatialFilter<bucketT>> m_SpatialFilter;
unique_ptr<TemporalFilter<T>> m_TemporalFilter;
unique_ptr<DensityFilter<bucketT>> m_DensityFilter;
vector<vector<Point<T>>> m_Samples;
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.
}
#pragma once
#include "RendererBase.h"
#include "Iterator.h"
#include "SpatialFilter.h"
#include "TemporalFilter.h"
#include "Interpolate.h"
#include "CarToRas.h"
#include "EmberToXml.h"
#include "Spline.h"
/// <summary>
/// Renderer.
/// </summary>
namespace EmberNs
{
/// <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 overridden 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 must always be float.
/// </summary>
template <typename T, typename bucketT>
class EMBER_API Renderer : public RendererBase
{
public:
Renderer();
Renderer(const Renderer<T, bucketT>& renderer) = delete;
Renderer<T, bucketT>& operator = (const Renderer<T, bucketT>& renderer) = delete;
virtual ~Renderer() = default;
//Non-virtual processing functions.
void AddEmber(Ember<T>& ember);
bool AssignIterator();
//Virtual processing functions overriden from RendererBase.
void Prepare() override;
void ComputeBounds() override;
void ComputeQuality() override;
void ComputeCamera() override;
void SetEmber(const Ember<T>& ember, eProcessAction action = eProcessAction::FULL_RENDER, bool prep = false) override;
template <typename C>
void SetEmber(const C& embers);
void MoveEmbers(vector<Ember<T>>& embers);
void SetExternalEmbersPointer(vector<Ember<T>>* embers);
bool CreateDEFilter(bool& newAlloc) override;
bool CreateSpatialFilter(bool& newAlloc) override;
bool CreateTemporalFilter(bool& newAlloc) override;
size_t HistBucketSize() const override { return sizeof(tvec4<bucketT, glm::defaultp>); }
eRenderStatus Run(vector<v4F>& finalImage, double time = 0, size_t subBatchCountOverride = 0, bool forceOutput = false, size_t finalOffset = 0) override;
EmberImageComments ImageComments(const EmberStats& stats, size_t printEditDepth = 0, bool hexPalette = true) override;
protected:
//New virtual functions to be overridden in derived renderers that use the GPU, but not accessed outside.
virtual void MakeDmap(T colorScalar);
virtual bool Alloc(bool histOnly = false);
virtual bool ResetBuckets(bool resetHist = true, bool resetAccum = true);
virtual eRenderStatus LogScaleDensityFilter(bool forceOutput = false);
virtual eRenderStatus GaussianDensityFilter();
virtual eRenderStatus AccumulatorToFinalImage(vector<v4F>& pixels, size_t finalOffset);
virtual EmberStats Iterate(size_t iterCount, size_t temporalSample);
virtual void ComputeCurves();
public:
//Non-virtual render properties, getters and setters.
inline T PixelAspectRatio() const;
void PixelAspectRatio(T pixelAspectRatio);
//Non-virtual renderer properties, getters only.
inline T Scale() const;
inline T PixelsPerUnitX() const;
inline T PixelsPerUnitY() const;
inline bucketT K1() const;
inline bucketT K2() const;
inline const CarToRas<T>& CoordMap() const;
inline tvec4<bucketT, glm::defaultp>* HistBuckets();
inline tvec4<bucketT, glm::defaultp>* AccumulatorBuckets();
inline SpatialFilter<bucketT>* GetSpatialFilter();
inline TemporalFilter<T>* GetTemporalFilter();
//Virtual renderer properties overridden from RendererBase, getters only.
double ScaledQuality() const override;
double LowerLeftX(bool gutter = true) const override;
double LowerLeftY(bool gutter = true) const override;
double UpperRightX(bool gutter = true) const override;
double UpperRightY(bool gutter = true) const override;
DensityFilterBase* GetDensityFilter() override;
//Non-virtual ember wrappers, getters only.
inline bool XaosPresent() const;
inline size_t Supersample() const;
inline size_t 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 bucketT Brightness() const;
inline bucketT Gamma() const;
inline bucketT Vibrancy() const;
inline bucketT GammaThresh() const;
inline bucketT HighlightPower() const;
inline Color<T> Background() const;
inline const Xform<T>* Xforms() const;
inline Xform<T>* NonConstXforms();
inline size_t XformCount() const;
inline const Xform<T>* FinalXform() const;
inline Xform<T>* NonConstFinalXform();
inline bool UseFinalXform() const;
inline const Palette<float>* GetPalette() const;
inline ePaletteMode PaletteMode() const;
//Virtual ember wrappers overridden from RendererBase, getters only.
size_t TemporalSamples() const override;
size_t FinalRasW() const override;
size_t FinalRasH() const override;
size_t SubBatchSize() const override;
size_t FuseCount() const override;
//Non-virtual iterator wrappers.
const byte* XformDistributions() const;
size_t XformDistributionsSize() const;
Point<T>* Samples(size_t threadIndex) const;
protected:
//Non-virtual functions that might be needed by a derived class.
void PrepFinalAccumVals(Color<bucketT>& background, bucketT& g, bucketT& linRange, bucketT& vibrancy);
private:
//Miscellaneous non-virtual functions used only in this class.
void Accumulate(QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand, Point<T>* samples, size_t sampleCount, const Palette<bucketT>* palette);
void AddToAccum(const tvec4<bucketT, glm::defaultp>& bucket, intmax_t i, intmax_t ii, intmax_t j, intmax_t jj);
template <typename accumT> void GammaCorrection(tvec4<bucketT, glm::defaultp>& bucket, Color<bucketT>& background, bucketT g, bucketT linRange, bucketT vibrancy, bool scale, accumT* correctedChannels);
void CurveAdjust(bucketT& a, const glm::length_t& index);
void VectorizedLogScale(size_t row, size_t rowEnd);
protected:
//public:
T m_Scale;
T m_PixelsPerUnitX;
T m_PixelsPerUnitY;
T m_PixelAspectRatio = 1;
T m_LowerLeftX;
T m_LowerLeftY;
T m_UpperRightX;
T m_UpperRightY;
bucketT m_K1;
bucketT m_K2;
bucketT m_Vibrancy;//Accumulate these after each temporal sample.
bucketT m_Gamma;
T m_ScaledQuality;
Color<bucketT> m_Background;//This is a scaled copy of the m_Background member of m_Ember, but with a type of bucketT.
Affine2D<T> m_RotMat;
Ember<T> m_Ember;
Ember<T> m_TempEmber;
Ember<T> m_LastEmber;
private:
vector<Ember<T>> m_Embers;
protected:
vector<Ember<T>>* m_EmbersP = &m_Embers;
vector<Ember<T>> m_ThreadEmbers;
Interpolater<T> m_Interpolater;
CarToRas<T> m_CarToRas;
unique_ptr<StandardIterator<T>> m_StandardIterator = make_unique<StandardIterator<T>>();
unique_ptr<XaosIterator<T>> m_XaosIterator = make_unique<XaosIterator<T>>();
Iterator<T>* m_Iterator = m_StandardIterator.get();
Palette<bucketT> m_Dmap;
vector<tvec4<bucketT, glm::defaultp>> m_Csa;
vector<tvec4<bucketT, glm::defaultp>> m_HistBuckets;
vector<tvec4<bucketT, glm::defaultp>> m_AccumulatorBuckets;
unique_ptr<SpatialFilter<bucketT>> m_SpatialFilter;
unique_ptr<TemporalFilter<T>> m_TemporalFilter;
unique_ptr<DensityFilter<bucketT>> m_DensityFilter;
vector<vector<Point<T>>> m_Samples;
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.
}
File diff suppressed because it is too large Load Diff
+237 -237
View File
@@ -1,237 +1,237 @@
#pragma once
#include "Utils.h"
#include "Ember.h"
#include "DensityFilter.h"
/// <summary>
/// RendererBase, 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:
RenderCallback() = default;
RenderCallback(RenderCallback& callback) = delete;
/// <summary>
/// Virtual destructor to ensure anything declared in derived classes gets cleaned up.
/// </summary>
virtual ~RenderCallback() = default;
/// <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()
{
Clear();
}
void Clear()
{
m_Success = true;
m_Iters = 0;
m_Badvals = 0;
m_IterMs = 0;
m_RenderMs = 0;
}
EmberStats& operator += (const EmberStats& stats)
{
m_Success &= stats.m_Success;
m_Iters += stats.m_Iters;
m_Badvals += stats.m_Badvals;
m_IterMs += stats.m_IterMs;
m_RenderMs += stats.m_RenderMs;
return *this;
}
bool m_Success = true;
size_t m_Iters, m_Badvals;
double m_IterMs, m_RenderMs;
};
/// <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 class eRendererType : et { 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();
RendererBase(const RendererBase& renderer) = delete;
RendererBase& operator = (const RendererBase& renderer) = delete;
virtual ~RendererBase() = default;
//Non-virtual processing functions.
void ChangeVal(std::function<void(void)> func, eProcessAction action);
size_t HistMemoryRequired(size_t strips);
pair<size_t, size_t> MemoryRequired(size_t strips, bool includeFinal, bool threadedWrite);
vector<QTIsaac<ISAAC_SIZE, ISAAC_INT>> RandVec();
bool PrepFinalAccumVector(vector<v4F>& pixels);
//Virtual processing functions.
virtual bool Ok() const;
virtual size_t MemoryAvailable();
virtual void SetEmber(const Ember<float>& ember, eProcessAction action = eProcessAction::FULL_RENDER, bool prep = false) { }
virtual void SetEmber(const Ember<double>& ember, eProcessAction action = eProcessAction::FULL_RENDER, bool prep = false) { }
virtual bool RandVec(vector<QTIsaac<ISAAC_SIZE, ISAAC_INT>>& randVec);
//Abstract processing functions.
virtual bool CreateDEFilter(bool& newAlloc) = 0;
virtual bool CreateSpatialFilter(bool& newAlloc) = 0;
virtual bool CreateTemporalFilter(bool& newAlloc) = 0;
virtual void Prepare() = 0;
virtual void ComputeBounds() = 0;
virtual void ComputeQuality() = 0;
virtual void ComputeCamera() = 0;
virtual eRenderStatus Run(vector<v4F>& finalImage, double time = 0, size_t subBatchCountOverride = 0, bool forceOutput = false, size_t finalOffset = 0) = 0;
virtual EmberImageComments ImageComments(const EmberStats& stats, size_t printEditDepth = 0, bool hexPalette = true) = 0;
virtual DensityFilterBase* GetDensityFilter() = 0;
//Non-virtual renderer properties, getters only.
size_t SuperRasW() const;
size_t SuperRasH() const;
size_t SuperSize() const;
size_t FinalRowSize() const;
size_t FinalDimensions() const;
size_t FinalBufferSize() const;
size_t PixelSize() const;
size_t GutterWidth() const;
size_t DensityFilterOffset() const;
size_t TotalIterCount(size_t strips) const;
size_t ItersPerTemporalSample() const;
eProcessState ProcessState() const;
eProcessAction ProcessAction() const;
EmberStats Stats() const;
//Non-virtual render getters and setters.
bool LockAccum() const;
void LockAccum(bool lockAccum);
bool EarlyClip() const;
void EarlyClip(bool earlyClip);
bool YAxisUp() const;
void YAxisUp(bool yup);
bool InsertPalette() const;
void InsertPalette(bool insertPalette);
bool ReclaimOnResize() const;
void ReclaimOnResize(bool reclaimOnResize);
void Callback(RenderCallback* callback);
void ThreadCount(size_t threads, const char* seedString = nullptr);
size_t BytesPerChannel() const;
size_t NumChannels() const;
eThreadPriority Priority() const;
void Priority(eThreadPriority priority);
eInteractiveFilter InteractiveFilter() const;
void InteractiveFilter(eInteractiveFilter filter);
//Virtual render properties, getters and setters.
virtual size_t ThreadCount() const;
virtual eRendererType RendererType() const;
virtual bool Shared() const;
//Abstract render properties, getters only.
virtual size_t TemporalSamples() const = 0;
virtual size_t HistBucketSize() const = 0;
virtual size_t FinalRasW() const = 0;
virtual size_t FinalRasH() const = 0;
virtual size_t SubBatchSize() const = 0;
virtual size_t FuseCount() const = 0;
virtual double ScaledQuality() const = 0;
virtual double LowerLeftX(bool gutter = true) const = 0;
virtual double LowerLeftY(bool gutter = true) const = 0;
virtual double UpperRightX(bool gutter = true) const = 0;
virtual double UpperRightY(bool gutter = true) const = 0;
//Non-virtual threading control.
void Reset();
void EnterRender();
void LeaveRender();
void EnterFinalAccum();
void LeaveFinalAccum();
void EnterResize();
void LeaveResize();
void Abort();
bool Aborted();
void Pause(bool pause);
bool Paused();
bool InRender();
bool InFinalAccum();
void* m_ProgressParameter = nullptr;
protected:
bool m_EarlyClip = false;
bool m_YAxisUp = false;
bool m_LockAccum = false;
bool m_InRender = false;
bool m_InFinalAccum = false;
bool m_InsertPalette = false;
bool m_ReclaimOnResize = false;
bool m_CurvesSet = false;
volatile bool m_Abort = false;
volatile bool m_Pause = false;
size_t m_SuperRasW;
size_t m_SuperRasH;
size_t m_SuperSize = 0;
size_t m_GutterWidth;
size_t m_DensityFilterOffset;
size_t m_NumChannels = 4;
size_t m_BytesPerChannel = 4;
size_t m_ThreadsToUse;
size_t m_VibGamCount;
size_t m_LastTemporalSample = 0;
size_t m_LastIter = 0;
double m_LastIterPercent = 0;
eThreadPriority m_Priority = eThreadPriority::NORMAL;
eProcessAction m_ProcessAction = eProcessAction::FULL_RENDER;
eProcessState m_ProcessState = eProcessState::NONE;
eInteractiveFilter m_InteractiveFilter = eInteractiveFilter::FILTER_LOG;
EmberStats m_Stats;
RenderCallback* m_Callback = nullptr;
vector<size_t> m_SubBatch;
vector<size_t> m_BadVals;
vector<QTIsaac<ISAAC_SIZE, ISAAC_INT>> m_Rand;
std::recursive_mutex m_RenderingCs, m_AccumCs, m_FinalAccumCs, m_ResizeCs;
Timing m_RenderTimer, m_IterTimer, m_ProgressTimer;
};
}
#pragma once
#include "Utils.h"
#include "Ember.h"
#include "DensityFilter.h"
/// <summary>
/// RendererBase, 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:
RenderCallback() = default;
RenderCallback(RenderCallback& callback) = delete;
/// <summary>
/// Virtual destructor to ensure anything declared in derived classes gets cleaned up.
/// </summary>
virtual ~RenderCallback() = default;
/// <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() noexcept
{
Clear();
}
void Clear() noexcept
{
m_Success = true;
m_Iters = 0;
m_Badvals = 0;
m_IterMs = 0;
m_RenderMs = 0;
}
EmberStats& operator += (const EmberStats& stats) noexcept
{
m_Success &= stats.m_Success;
m_Iters += stats.m_Iters;
m_Badvals += stats.m_Badvals;
m_IterMs += stats.m_IterMs;
m_RenderMs += stats.m_RenderMs;
return *this;
}
bool m_Success = true;
size_t m_Iters, m_Badvals;
double m_IterMs, m_RenderMs;
};
/// <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 class eRendererType : et { 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();
RendererBase(const RendererBase& renderer) = delete;
RendererBase& operator = (const RendererBase& renderer) = delete;
virtual ~RendererBase() = default;
//Non-virtual processing functions.
void ChangeVal(std::function<void(void)> func, eProcessAction action);
size_t HistMemoryRequired(size_t strips);
pair<size_t, size_t> MemoryRequired(size_t strips, bool includeFinal, bool threadedWrite);
vector<QTIsaac<ISAAC_SIZE, ISAAC_INT>> RandVec();
bool PrepFinalAccumVector(vector<v4F>& pixels);
//Virtual processing functions.
virtual bool Ok() const;
virtual size_t MemoryAvailable();
virtual void SetEmber(const Ember<float>& ember, eProcessAction action = eProcessAction::FULL_RENDER, bool prep = false) { }
virtual void SetEmber(const Ember<double>& ember, eProcessAction action = eProcessAction::FULL_RENDER, bool prep = false) { }
virtual bool RandVec(vector<QTIsaac<ISAAC_SIZE, ISAAC_INT>>& randVec);
//Abstract processing functions.
virtual bool CreateDEFilter(bool& newAlloc) = 0;
virtual bool CreateSpatialFilter(bool& newAlloc) = 0;
virtual bool CreateTemporalFilter(bool& newAlloc) = 0;
virtual void Prepare() = 0;
virtual void ComputeBounds() = 0;
virtual void ComputeQuality() = 0;
virtual void ComputeCamera() = 0;
virtual eRenderStatus Run(vector<v4F>& finalImage, double time = 0, size_t subBatchCountOverride = 0, bool forceOutput = false, size_t finalOffset = 0) = 0;
virtual EmberImageComments ImageComments(const EmberStats& stats, size_t printEditDepth = 0, bool hexPalette = true) = 0;
virtual DensityFilterBase* GetDensityFilter() = 0;
//Non-virtual renderer properties, getters only.
size_t SuperRasW() const;
size_t SuperRasH() const;
size_t SuperSize() const;
size_t FinalRowSize() const;
size_t FinalDimensions() const;
size_t FinalBufferSize() const;
size_t PixelSize() const;
size_t GutterWidth() const;
size_t DensityFilterOffset() const;
size_t TotalIterCount(size_t strips) const;
size_t ItersPerTemporalSample() const;
eProcessState ProcessState() const;
eProcessAction ProcessAction() const;
EmberStats Stats() const;
//Non-virtual render getters and setters.
bool LockAccum() const;
void LockAccum(bool lockAccum);
bool EarlyClip() const;
void EarlyClip(bool earlyClip);
bool YAxisUp() const;
void YAxisUp(bool yup);
bool InsertPalette() const;
void InsertPalette(bool insertPalette);
bool ReclaimOnResize() const;
void ReclaimOnResize(bool reclaimOnResize);
void Callback(RenderCallback* callback);
void ThreadCount(size_t threads, const char* seedString = nullptr);
size_t BytesPerChannel() const;
size_t NumChannels() const;
eThreadPriority Priority() const;
void Priority(eThreadPriority priority);
eInteractiveFilter InteractiveFilter() const;
void InteractiveFilter(eInteractiveFilter filter);
//Virtual render properties, getters and setters.
virtual size_t ThreadCount() const;
virtual eRendererType RendererType() const;
virtual bool Shared() const;
//Abstract render properties, getters only.
virtual size_t TemporalSamples() const = 0;
virtual size_t HistBucketSize() const = 0;
virtual size_t FinalRasW() const = 0;
virtual size_t FinalRasH() const = 0;
virtual size_t SubBatchSize() const = 0;
virtual size_t FuseCount() const = 0;
virtual double ScaledQuality() const = 0;
virtual double LowerLeftX(bool gutter = true) const = 0;
virtual double LowerLeftY(bool gutter = true) const = 0;
virtual double UpperRightX(bool gutter = true) const = 0;
virtual double UpperRightY(bool gutter = true) const = 0;
//Non-virtual threading control.
void Reset();
void EnterRender();
void LeaveRender();
void EnterFinalAccum();
void LeaveFinalAccum();
void EnterResize();
void LeaveResize();
void Abort();
bool Aborted();
void Pause(bool pause);
bool Paused();
bool InRender();
bool InFinalAccum();
void* m_ProgressParameter = nullptr;
protected:
bool m_EarlyClip = false;
bool m_YAxisUp = false;
bool m_LockAccum = false;
bool m_InRender = false;
bool m_InFinalAccum = false;
bool m_InsertPalette = false;
bool m_ReclaimOnResize = false;
bool m_CurvesSet = false;
volatile bool m_Abort = false;
volatile bool m_Pause = false;
size_t m_SuperRasW;
size_t m_SuperRasH;
size_t m_SuperSize = 0;
size_t m_GutterWidth;
size_t m_DensityFilterOffset;
size_t m_NumChannels = 4;
size_t m_BytesPerChannel = 4;
size_t m_ThreadsToUse;
size_t m_VibGamCount;
size_t m_LastTemporalSample = 0;
size_t m_LastIter = 0;
double m_LastIterPercent = 0;
eThreadPriority m_Priority = eThreadPriority::NORMAL;
eProcessAction m_ProcessAction = eProcessAction::FULL_RENDER;
eProcessState m_ProcessState = eProcessState::NONE;
eInteractiveFilter m_InteractiveFilter = eInteractiveFilter::FILTER_LOG;
EmberStats m_Stats;
RenderCallback* m_Callback = nullptr;
vector<size_t> m_SubBatch;
vector<size_t> m_BadVals;
vector<QTIsaac<ISAAC_SIZE, ISAAC_INT>> m_Rand;
std::recursive_mutex m_RenderingCs, m_AccumCs, m_FinalAccumCs, m_ResizeCs;
Timing m_RenderTimer, m_IterTimer, m_ProgressTimer;
};
}
+1372 -1372
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+127 -127
View File
@@ -1,127 +1,127 @@
// This is a combination of this:
// https://stackoverflow.com/questions/25379422/b-spline-curves/25379851#25379851
// and this, but modified to operate on a spline with any number of points intead of just >= 4:
//
// Spline.cc
// CubicSplineLib/
//
// Source file for the "CubicSpline" class. This object facilitates natural
// cubic spline interpolation. Once instantiated the
// constructor builds the spline polynomials on the intervals of the (x, y)
// data provided and retains them for later invocation. Parallelized using
// OpenMP.
//
// Copyright (C) Geoffrey Lentner 2015. All rights reserved.
// See LICENCE file. (GPL v2.0)
//
// contact: Geoffrey Lentner, B.S.
// Graduate Student / Researcher
// 102 Natural Science Building
// Department of Physics & Astronomy
// University of Louisville
// Louisville, KY 40292 USA
//
// email: geoffrey.lentner@louisville.edu
//
// updated: 2015-1-19 13:10:30 EST
//
#include "EmberPch.h"
#include "Spline.h"
namespace EmberNs
{
/// <summary>
/// Constructor that takes a vector of x,y points, optionally sorts them
/// and builds the spline values.
/// </summary>
/// <param name="_vals">The vector of x,y points</param>
/// <param name="sorted">True to skip sorting, false to sort.</param>
template<class T>
Spline<T>::Spline(const std::vector<v2T>& _vals, bool sorted)
{
n = int(_vals.size() - 1);
vals = _vals;
// if not suppressed, ensure 'x' elements are in ascending order
if (!sorted)
std::sort(vals.begin(), vals.end(), [&](const v2T & lhs, const v2T & rhs) { return lhs.x < rhs.x; });
BuildSplines();
}
/// <summary>
/// Compute spline values for the passed in points.
/// This only needs to be done once.
/// </summary>
template<class T>
void Spline<T>::BuildSplines()
{
a.resize(n + 1);
b.resize(n + 1);
c.resize(n + 1);
d.resize(n + 1);
std::vector<T> w(n);
std::vector<T> h(n);
std::vector<T> ftt(n + 1);
for (int i = 0; i < n; i++)
{
w[i] = (vals[i + 1].x - vals[i].x);
h[i] = (vals[i + 1].y - vals[i].y) / w[i];
}
ftt[0] = 0;
for (int i = 0; i < n - 1; i++)
ftt[i + 1] = 3 * (h[i + 1] - h[i]) / (w[i + 1] + w[i]);
ftt[n] = 0;
for (int i = 0; i < n; i++)
{
a[i] = (ftt[i + 1] - ftt[i]) / (6 * w[i]);
b[i] = ftt[i] / 2;
c[i] = h[i] - w[i] * (ftt[i + 1] + 2 * ftt[i]) / 6;
d[i] = vals[i].y;
}
}
/// <summary>
/// Wrapper to generate y points on the spline for a vector of passed in points.
/// </summary>
/// <param name="newX">The vector of x points to generate spline points for</param>
/// <returns>The vector of computed spline y points.</returns>
template<class T>
std::vector<T> Spline<T>::Interpolate(const std::vector<T>& newX)
{
std::vector<T> output; output.resize(newX.size());
for (size_t i = 0; i < newX.size(); i++)
output[i] = Interpolate(newX[i]);
return output;
}
/// <summary>
/// Compute a y point on the spline for a the passed in value of x.
/// </summary>
/// <param name="newX">The x points to compute the spline point for</param>
/// <returns>The computed spline y points.</returns>
template<class T>
T Spline<T>::Interpolate(T newX)
{
ClampRef(newX, vals[0].x, vals[n].x);
int j = 0;
while (j < n && newX > vals[j + 1].x)
j++;
const auto xmxj = newX - vals[j].x;
const auto output = a[j] * (xmxj * xmxj * xmxj) +
b[j] * (xmxj * xmxj) +
c[j] * xmxj +
d[j];
return output;
}
template EMBER_API class Spline<float>;
}
// This is a combination of this:
// https://stackoverflow.com/questions/25379422/b-spline-curves/25379851#25379851
// and this, but modified to operate on a spline with any number of points intead of just >= 4:
//
// Spline.cc
// CubicSplineLib/
//
// Source file for the "CubicSpline" class. This object facilitates natural
// cubic spline interpolation. Once instantiated the
// constructor builds the spline polynomials on the intervals of the (x, y)
// data provided and retains them for later invocation. Parallelized using
// OpenMP.
//
// Copyright (C) Geoffrey Lentner 2015. All rights reserved.
// See LICENCE file. (GPL v2.0)
//
// contact: Geoffrey Lentner, B.S.
// Graduate Student / Researcher
// 102 Natural Science Building
// Department of Physics & Astronomy
// University of Louisville
// Louisville, KY 40292 USA
//
// email: geoffrey.lentner@louisville.edu
//
// updated: 2015-1-19 13:10:30 EST
//
#include "EmberPch.h"
#include "Spline.h"
namespace EmberNs
{
/// <summary>
/// Constructor that takes a vector of x,y points, optionally sorts them
/// and builds the spline values.
/// </summary>
/// <param name="_vals">The vector of x,y points</param>
/// <param name="sorted">True to skip sorting, false to sort.</param>
template<class T>
Spline<T>::Spline(const std::vector<v2T>& _vals, bool sorted)
{
n = int(_vals.size() - 1);
vals = _vals;
// if not suppressed, ensure 'x' elements are in ascending order
if (!sorted)
std::sort(vals.begin(), vals.end(), [&](const v2T & lhs, const v2T & rhs) { return lhs.x < rhs.x; });
BuildSplines();
}
/// <summary>
/// Compute spline values for the passed in points.
/// This only needs to be done once.
/// </summary>
template<class T>
void Spline<T>::BuildSplines()
{
a.resize(n + 1);
b.resize(n + 1);
c.resize(n + 1);
d.resize(n + 1);
std::vector<T> w(n);
std::vector<T> h(n);
std::vector<T> ftt(n + 1);
for (int i = 0; i < n; i++)
{
w[i] = (vals[i + 1].x - vals[i].x);
h[i] = (vals[i + 1].y - vals[i].y) / w[i];
}
ftt[0] = 0;
for (int i = 0; i < n - 1; i++)
ftt[i + 1] = 3 * (h[i + 1] - h[i]) / (w[i + 1] + w[i]);
ftt[n] = 0;
for (int i = 0; i < n; i++)
{
a[i] = (ftt[i + 1] - ftt[i]) / (6 * w[i]);
b[i] = ftt[i] / 2;
c[i] = h[i] - w[i] * (ftt[i + 1] + 2 * ftt[i]) / 6;
d[i] = vals[i].y;
}
}
/// <summary>
/// Wrapper to generate y points on the spline for a vector of passed in points.
/// </summary>
/// <param name="newX">The vector of x points to generate spline points for</param>
/// <returns>The vector of computed spline y points.</returns>
template<class T>
std::vector<T> Spline<T>::Interpolate(const std::vector<T>& newX)
{
std::vector<T> output; output.resize(newX.size());
for (size_t i = 0; i < newX.size(); i++)
output[i] = Interpolate(newX[i]);
return output;
}
/// <summary>
/// Compute a y point on the spline for a the passed in value of x.
/// </summary>
/// <param name="newX">The x points to compute the spline point for</param>
/// <returns>The computed spline y points.</returns>
template<class T>
T Spline<T>::Interpolate(T newX)
{
ClampRef(newX, vals[0].x, vals[n].x);
int j = 0;
while (j < n && newX > vals[j + 1].x)
j++;
const auto xmxj = newX - vals[j].x;
const auto output = a[j] * (xmxj * xmxj * xmxj) +
b[j] * (xmxj * xmxj) +
c[j] * xmxj +
d[j];
return output;
}
template EMBER_API class Spline<float>;
}
+55 -55
View File
@@ -1,55 +1,55 @@
// This is a combination of this:
// https://stackoverflow.com/questions/25379422/b-spline-curves/25379851#25379851
// and this, but modified to operate on a spline with any number of points intead of just >= 4:
//
// Spline.h
// CubicSplineLib/
//
// Header file for the "CubicSpline" class. This object facilitates natural
// cubic spline interpolation. Once instantiated the
// constructor builds the spline polynomials on the intervals of the (x, y)
// data provided and retains them for later invocation. Parallelized using
// OpenMP.
//
// Copyright (C) Geoffrey Lentner 2015. All rights reserved.
// See LICENCE file. (GPL v2.0)
//
// contact: Geoffrey Lentner, B.S.
// Graduate Student / Researcher
// 102 Natural Science Building
// Department of Physics & Astronomy
// University of Louisville
// Louisville, KY 40292 USA
//
// email: geoffrey.lentner@louisville.edu
//
// updated: 2015-1-19 13:10:30 EST
//
#pragma once
#include "Utils.h"
namespace EmberNs
{
/// <summary>
/// Class taking passed in x,y points, sorting them, and providing a function
/// to compute and return an interpolated spline curve for any value between the
/// first and last x.
/// Template argument expected to be float.
/// </summary>
template<class T = float>
class EMBER_API Spline
{
public:
Spline(const std::vector<v2T>& _vals, bool sorted = false);
std::vector<T> Interpolate(const std::vector<T>& newX);
T Interpolate(T newX);
private:
void BuildSplines();
std::vector<v2T> vals;
std::vector<T> a, b, c, d;
std::vector<T> c_prime, d_prime;
std::vector<T> k;
int n;
};
}
// This is a combination of this:
// https://stackoverflow.com/questions/25379422/b-spline-curves/25379851#25379851
// and this, but modified to operate on a spline with any number of points intead of just >= 4:
//
// Spline.h
// CubicSplineLib/
//
// Header file for the "CubicSpline" class. This object facilitates natural
// cubic spline interpolation. Once instantiated the
// constructor builds the spline polynomials on the intervals of the (x, y)
// data provided and retains them for later invocation. Parallelized using
// OpenMP.
//
// Copyright (C) Geoffrey Lentner 2015. All rights reserved.
// See LICENCE file. (GPL v2.0)
//
// contact: Geoffrey Lentner, B.S.
// Graduate Student / Researcher
// 102 Natural Science Building
// Department of Physics & Astronomy
// University of Louisville
// Louisville, KY 40292 USA
//
// email: geoffrey.lentner@louisville.edu
//
// updated: 2015-1-19 13:10:30 EST
//
#pragma once
#include "Utils.h"
namespace EmberNs
{
/// <summary>
/// Class taking passed in x,y points, sorting them, and providing a function
/// to compute and return an interpolated spline curve for any value between the
/// first and last x.
/// Template argument expected to be float.
/// </summary>
template<class T = float>
class EMBER_API Spline
{
public:
Spline(const std::vector<v2T>& _vals, bool sorted = false);
std::vector<T> Interpolate(const std::vector<T>& newX);
T Interpolate(T newX);
private:
void BuildSplines();
std::vector<v2T> vals;
std::vector<T> a, b, c, d;
std::vector<T> c_prime, d_prime;
std::vector<T> k;
int n;
};
}
+399 -399
View File
@@ -1,399 +1,399 @@
#pragma once
#include "EmberDefines.h"
/// <summary>
/// TemporalFilter base, derived and factory classes.
/// </summary>
namespace EmberNs
{
/// <summary>
/// The types of temporal filters available.
/// </summary>
enum class eTemporalFilterType : et
{
BOX_TEMPORAL_FILTER,
GAUSSIAN_TEMPORAL_FILTER,
EXP_TEMPORAL_FILTER
};
/// <summary>
/// g++ needs a forward declaration here.
/// </summary>
template <typename T> class TemporalFilterCreator;
#define TEMPORALFILTERUSINGS \
using TemporalFilter<T>::m_Filter; \
using TemporalFilter<T>::m_FilterExp; \
using TemporalFilter<T>::Size; \
using TemporalFilter<T>::FinishFilter;
/// <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="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 exponent. Unused except with ExpTemporalFilter, but needed to prevent equality tests from failing.</param>
TemporalFilter(eTemporalFilterType filterType, size_t temporalSamples, T filterWidth, T filterExp)
{
size_t i, steps = temporalSamples;
m_TemporalSamples = temporalSamples;
m_FilterWidth = filterWidth;
m_Deltas.resize(steps);
m_Filter.resize(steps);
m_FilterType = filterType;
m_FilterExp = filterExp;//Always assign this to prevent excessive recreates in Renderer::CreateTemporalFilter().
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] = (static_cast<T>(i) / static_cast<T>(steps - 1) - static_cast<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_TemporalSamples = filter.m_TemporalSamples;
m_FilterWidth = filter.m_FilterWidth;
m_FilterExp = filter.m_FilterExp;
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
{
size_t i;
stringstream ss;
ss << "Temporal Filter:\n"
<< "\n Size: " << Size()
<< "\n Type: " << TemporalFilterCreator<T>::ToString(m_FilterType)
<< "\n Sum Filt: " << SumFilt();
ss << "\nDeltas: \n";
for (i = 0; i < m_Deltas.size(); i++)
{
ss << "Deltas[" << i << "]: " << m_Deltas[i] << "\n";
}
ss << "Filter: \n";
for (i = 0; i < m_Filter.size(); i++)
{
ss << "Filter[" << i << "]: " << m_Filter[i] << "\n";
//ss << m_Filter[i] << "\n";
}
return ss.str();
}
/// <summary>
/// Accessors.
/// </summary>
size_t Size() const { return m_Filter.size(); }
size_t TemporalSamples() const { return m_TemporalSamples; }
T FilterWidth() const { return m_FilterWidth; }
T FilterExp() const { return m_FilterExp; }
T SumFilt() const { return m_SumFilt; }
T* Deltas() { return m_Deltas.data(); }
T* Filter() { return m_Filter.data(); }
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 (size_t i = 0; i < Size(); i++)
{
m_Filter[i] /= maxFilt;
m_SumFilt += m_Filter[i];
}
m_SumFilt /= Size();
}
T m_SumFilt = 1;//The sum of all filter values.
T m_FilterWidth;
T m_FilterExp;
size_t m_TemporalSamples;
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>
{
TEMPORALFILTERUSINGS
public:
/// <summary>
/// Constructor to create an Exp filter.
/// </summary>
/// <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 exponent.</param>
ExpTemporalFilter(size_t temporalSamples, T filterWidth, T filterExp = 1)
: TemporalFilter<T>(eTemporalFilterType::EXP_TEMPORAL_FILTER, temporalSamples, filterWidth, filterExp)
{
if (Size() > 1)
{
T slpx, maxFilt = 0;
for (size_t i = 0; i < Size(); i++)
{
if (filterExp >= 0)
slpx = (static_cast<T>(i) + 1) / Size();
else
slpx = static_cast<T>(Size() - i) / Size();
//Scale the color based on these values.
m_Filter[i] = std::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>
{
TEMPORALFILTERUSINGS
public:
/// <summary>
/// Constructor to create a Gaussian filter.
/// </summary>
/// <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">Unused, but needed to prevent equality tests from failing.</param>
GaussianTemporalFilter(size_t temporalSamples, T filterWidth, T filterExp)
: TemporalFilter<T>(eTemporalFilterType::GAUSSIAN_TEMPORAL_FILTER, temporalSamples, filterWidth, filterExp)
{
if (Size() > 1)
{
T maxFilt = 0, halfSteps = static_cast<T>(Size()) / static_cast<T>(2);
GaussianFilter<T> gaussian(1, 1);//Just pass dummy values, they are unused in this case.
for (size_t 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>
{
TEMPORALFILTERUSINGS
public:
/// <summary>
/// Constructor to create a Box filter.
/// </summary>
/// <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">Unused, but needed to prevent equality tests from failing.</param>
BoxTemporalFilter(size_t temporalSamples, T filterWidth, T filterExp)
: TemporalFilter<T>(eTemporalFilterType::BOX_TEMPORAL_FILTER, temporalSamples, filterWidth, filterExp)
{
if (Size() > 1)
{
for (size_t 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="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 unused but needed to prevent equality tests from failing.</param>
/// <returns>A pointer to the newly created filter object</returns>
static TemporalFilter<T>* Create(eTemporalFilterType filterType, size_t temporalSamples, T filterWidth, T filterExp)
{
TemporalFilter<T>* filter = nullptr;
switch (filterType)
{
case EmberNs::eTemporalFilterType::BOX_TEMPORAL_FILTER:
filter = new BoxTemporalFilter<T>(temporalSamples, filterWidth, filterExp);
break;
case EmberNs::eTemporalFilterType::GAUSSIAN_TEMPORAL_FILTER:
filter = new GaussianTemporalFilter<T>(temporalSamples, filterWidth, filterExp);
break;
case EmberNs::eTemporalFilterType::EXP_TEMPORAL_FILTER:
filter = new ExpTemporalFilter<T>(temporalSamples, filterWidth, filterExp);
break;
default:
filter = new BoxTemporalFilter<T>(temporalSamples, filterWidth, filterExp);//Default to box if bad enum passed in.
break;
}
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(const string& filterType)
{
if (!_stricmp(filterType.c_str(), "box"))
return eTemporalFilterType::BOX_TEMPORAL_FILTER;
else if (!_stricmp(filterType.c_str(), "gaussian"))
return eTemporalFilterType::GAUSSIAN_TEMPORAL_FILTER;
else if (!_stricmp(filterType.c_str(), "exp"))
return eTemporalFilterType::EXP_TEMPORAL_FILTER;
else
return eTemporalFilterType::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;
switch (filterType)
{
case EmberNs::eTemporalFilterType::BOX_TEMPORAL_FILTER:
filter = "Box";
break;
case EmberNs::eTemporalFilterType::GAUSSIAN_TEMPORAL_FILTER:
filter = "Gaussian";
break;
case EmberNs::eTemporalFilterType::EXP_TEMPORAL_FILTER:
filter = "Exp";
break;
default:
filter = "Box";
break;
}
return filter;
}
};
/// <summary>
/// Thin wrapper around TemporalFilterCreator::ToString() to allow << operator on temporal filter type.
/// </summary>
/// <param name="stream">The stream to insert into</param>
/// <param name="t">The type whose string representation will be inserted into the stream</param>
/// <returns></returns>
static std::ostream& operator<<(std::ostream& stream, const eTemporalFilterType& t)
{
stream << TemporalFilterCreator<float>::ToString(t);
return stream;
}
}
#pragma once
#include "EmberDefines.h"
/// <summary>
/// TemporalFilter base, derived and factory classes.
/// </summary>
namespace EmberNs
{
/// <summary>
/// The types of temporal filters available.
/// </summary>
enum class eTemporalFilterType : et
{
BOX_TEMPORAL_FILTER,
GAUSSIAN_TEMPORAL_FILTER,
EXP_TEMPORAL_FILTER
};
/// <summary>
/// g++ needs a forward declaration here.
/// </summary>
template <typename T> class TemporalFilterCreator;
#define TEMPORALFILTERUSINGS \
using TemporalFilter<T>::m_Filter; \
using TemporalFilter<T>::m_FilterExp; \
using TemporalFilter<T>::Size; \
using TemporalFilter<T>::FinishFilter;
/// <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="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 exponent. Unused except with ExpTemporalFilter, but needed to prevent equality tests from failing.</param>
TemporalFilter(eTemporalFilterType filterType, size_t temporalSamples, T filterWidth, T filterExp)
{
size_t i, steps = temporalSamples;
m_TemporalSamples = temporalSamples;
m_FilterWidth = filterWidth;
m_Deltas.resize(steps);
m_Filter.resize(steps);
m_FilterType = filterType;
m_FilterExp = filterExp;//Always assign this to prevent excessive recreates in Renderer::CreateTemporalFilter().
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] = (static_cast<T>(i) / static_cast<T>(steps - 1) - static_cast<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_TemporalSamples = filter.m_TemporalSamples;
m_FilterWidth = filter.m_FilterWidth;
m_FilterExp = filter.m_FilterExp;
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
{
size_t i;
stringstream ss;
ss << "Temporal Filter:\n"
<< "\n Size: " << Size()
<< "\n Type: " << TemporalFilterCreator<T>::ToString(m_FilterType)
<< "\n Sum Filt: " << SumFilt();
ss << "\nDeltas: \n";
for (i = 0; i < m_Deltas.size(); i++)
{
ss << "Deltas[" << i << "]: " << m_Deltas[i] << "\n";
}
ss << "Filter: \n";
for (i = 0; i < m_Filter.size(); i++)
{
ss << "Filter[" << i << "]: " << m_Filter[i] << "\n";
//ss << m_Filter[i] << "\n";
}
return ss.str();
}
/// <summary>
/// Accessors.
/// </summary>
size_t Size() const { return m_Filter.size(); }
size_t TemporalSamples() const { return m_TemporalSamples; }
T FilterWidth() const { return m_FilterWidth; }
T FilterExp() const { return m_FilterExp; }
T SumFilt() const { return m_SumFilt; }
T* Deltas() { return m_Deltas.data(); }
T* Filter() { return m_Filter.data(); }
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 (size_t i = 0; i < Size(); i++)
{
m_Filter[i] /= maxFilt;
m_SumFilt += m_Filter[i];
}
m_SumFilt /= Size();
}
T m_SumFilt = 1;//The sum of all filter values.
T m_FilterWidth;
T m_FilterExp;
size_t m_TemporalSamples;
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>
{
TEMPORALFILTERUSINGS
public:
/// <summary>
/// Constructor to create an Exp filter.
/// </summary>
/// <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 exponent.</param>
ExpTemporalFilter(size_t temporalSamples, T filterWidth, T filterExp = 1)
: TemporalFilter<T>(eTemporalFilterType::EXP_TEMPORAL_FILTER, temporalSamples, filterWidth, filterExp)
{
if (Size() > 1)
{
T slpx, maxFilt = 0;
for (size_t i = 0; i < Size(); i++)
{
if (filterExp >= 0)
slpx = (static_cast<T>(i) + 1) / Size();
else
slpx = static_cast<T>(Size() - i) / Size();
//Scale the color based on these values.
m_Filter[i] = std::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>
{
TEMPORALFILTERUSINGS
public:
/// <summary>
/// Constructor to create a Gaussian filter.
/// </summary>
/// <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">Unused, but needed to prevent equality tests from failing.</param>
GaussianTemporalFilter(size_t temporalSamples, T filterWidth, T filterExp)
: TemporalFilter<T>(eTemporalFilterType::GAUSSIAN_TEMPORAL_FILTER, temporalSamples, filterWidth, filterExp)
{
if (Size() > 1)
{
T maxFilt = 0, halfSteps = static_cast<T>(Size()) / static_cast<T>(2);
GaussianFilter<T> gaussian(1, 1);//Just pass dummy values, they are unused in this case.
for (size_t 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>
{
TEMPORALFILTERUSINGS
public:
/// <summary>
/// Constructor to create a Box filter.
/// </summary>
/// <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">Unused, but needed to prevent equality tests from failing.</param>
BoxTemporalFilter(size_t temporalSamples, T filterWidth, T filterExp)
: TemporalFilter<T>(eTemporalFilterType::BOX_TEMPORAL_FILTER, temporalSamples, filterWidth, filterExp)
{
if (Size() > 1)
{
for (size_t 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="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 unused but needed to prevent equality tests from failing.</param>
/// <returns>A pointer to the newly created filter object</returns>
static TemporalFilter<T>* Create(eTemporalFilterType filterType, size_t temporalSamples, T filterWidth, T filterExp)
{
TemporalFilter<T>* filter = nullptr;
switch (filterType)
{
case EmberNs::eTemporalFilterType::BOX_TEMPORAL_FILTER:
filter = new BoxTemporalFilter<T>(temporalSamples, filterWidth, filterExp);
break;
case EmberNs::eTemporalFilterType::GAUSSIAN_TEMPORAL_FILTER:
filter = new GaussianTemporalFilter<T>(temporalSamples, filterWidth, filterExp);
break;
case EmberNs::eTemporalFilterType::EXP_TEMPORAL_FILTER:
filter = new ExpTemporalFilter<T>(temporalSamples, filterWidth, filterExp);
break;
default:
filter = new BoxTemporalFilter<T>(temporalSamples, filterWidth, filterExp);//Default to box if bad enum passed in.
break;
}
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(const string& filterType)
{
if (!_stricmp(filterType.c_str(), "box"))
return eTemporalFilterType::BOX_TEMPORAL_FILTER;
else if (!_stricmp(filterType.c_str(), "gaussian"))
return eTemporalFilterType::GAUSSIAN_TEMPORAL_FILTER;
else if (!_stricmp(filterType.c_str(), "exp"))
return eTemporalFilterType::EXP_TEMPORAL_FILTER;
else
return eTemporalFilterType::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;
switch (filterType)
{
case EmberNs::eTemporalFilterType::BOX_TEMPORAL_FILTER:
filter = "Box";
break;
case EmberNs::eTemporalFilterType::GAUSSIAN_TEMPORAL_FILTER:
filter = "Gaussian";
break;
case EmberNs::eTemporalFilterType::EXP_TEMPORAL_FILTER:
filter = "Exp";
break;
default:
filter = "Box";
break;
}
return filter;
}
};
/// <summary>
/// Thin wrapper around TemporalFilterCreator::ToString() to allow << operator on temporal filter type.
/// </summary>
/// <param name="stream">The stream to insert into</param>
/// <param name="t">The type whose string representation will be inserted into the stream</param>
/// <returns></returns>
static std::ostream& operator<<(std::ostream& stream, const eTemporalFilterType& t)
{
stream << TemporalFilterCreator<float>::ToString(t);
return stream;
}
}
+146 -146
View File
@@ -1,146 +1,146 @@
#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 begin time cast to a double</returns>
double Tic()
{
m_BeginTime = NowMsD();
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: nullptr.</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 = nullptr, bool fullString = false)
{
m_EndTime = NowMsD();
const auto ms = ElapsedTime();
if (str)
{
cout << string(str) << (fullString ? "" : " processing time: ") << Format(ms) << "\n";
}
return ms;
}
/// <summary>
/// Return the begin time as a double.
/// </summary>
/// <returns></returns>
double BeginTime() const { return static_cast<double>(m_BeginTime.time_since_epoch().count()); }
/// <summary>
/// Return the end time as a double.
/// </summary>
/// <returns></returns>
double EndTime() const { return static_cast<double>(m_EndTime.time_since_epoch().count()); }
/// <summary>
/// Return the elapsed time in milliseconds.
/// </summary>
/// <returns>The elapsed time in milliseconds as a double</returns>
double ElapsedTime() const
{
return (m_EndTime - m_BeginTime).count();
}
/// <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 time in milliseconds to format</param>
/// <returns>The formatted string</returns>
string Format(double ms) const
{
stringstream ss;
double x = ms / 1000;
const auto secs = fmod(x, 60);
x /= 60;
const auto mins = fmod(x, 60);
x /= 60;
const auto hours = fmod(x, 24);
x /= 24;
const auto days = x;
if (days >= 1)
ss << static_cast<int>(days) << "d ";
if (hours >= 1)
ss << static_cast<int>(hours) << "h ";
if (mins >= 1)
ss << static_cast<int>(mins) << "m ";
ss << std::fixed << std::setprecision(m_Precision) << secs << "s";
return ss.str();
}
/// <summary>
/// Return the number of cores in the system.
/// </summary>
/// <returns>The number of cores in the system</returns>
static uint 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)
{
m_ProcessorCount = thread::hardware_concurrency();
m_TimingInit = true;
}
}
int m_Precision;//How many digits after the decimal place to print for seconds.
DoubleMsTimePoint m_BeginTime;//The start of the timing, set with Tic().
DoubleMsTimePoint m_EndTime;//The end of the timing, set with Toc().
static bool m_TimingInit;//Whether the performance info has bee queried.
static uint m_ProcessorCount;//The number of cores on the system, set in Init().
};
}
#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) noexcept
{
m_Precision = precision;
Init();
Tic();
}
/// <summary>
/// Set the begin time.
/// </summary>
/// <returns>The begin time cast to a double</returns>
double Tic() noexcept
{
m_BeginTime = NowMsD();
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: nullptr.</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 = nullptr, bool fullString = false)
{
m_EndTime = NowMsD();
const auto ms = ElapsedTime();
if (str)
{
cout << string(str) << (fullString ? "" : " processing time: ") << Format(ms) << "\n";
}
return ms;
}
/// <summary>
/// Return the begin time as a double.
/// </summary>
/// <returns></returns>
double BeginTime() const noexcept { return static_cast<double>(m_BeginTime.time_since_epoch().count()); }
/// <summary>
/// Return the end time as a double.
/// </summary>
/// <returns></returns>
double EndTime() const noexcept { return static_cast<double>(m_EndTime.time_since_epoch().count()); }
/// <summary>
/// Return the elapsed time in milliseconds.
/// </summary>
/// <returns>The elapsed time in milliseconds as a double</returns>
double ElapsedTime() const noexcept
{
return (m_EndTime - m_BeginTime).count();
}
/// <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 time in milliseconds to format</param>
/// <returns>The formatted string</returns>
string Format(double ms) const
{
stringstream ss;
double x = ms / 1000;
const auto secs = fmod(x, 60);
x /= 60;
const auto mins = fmod(x, 60);
x /= 60;
const auto hours = fmod(x, 24);
x /= 24;
const auto days = x;
if (days >= 1)
ss << static_cast<int>(days) << "d ";
if (hours >= 1)
ss << static_cast<int>(hours) << "h ";
if (mins >= 1)
ss << static_cast<int>(mins) << "m ";
ss << std::fixed << std::setprecision(m_Precision) << secs << "s";
return ss.str();
}
/// <summary>
/// Return the number of cores in the system.
/// </summary>
/// <returns>The number of cores in the system</returns>
static uint 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() noexcept
{
if (!m_TimingInit)
{
m_ProcessorCount = thread::hardware_concurrency();
m_TimingInit = true;
}
}
int m_Precision;//How many digits after the decimal place to print for seconds.
DoubleMsTimePoint m_BeginTime;//The start of the timing, set with Tic().
DoubleMsTimePoint m_EndTime;//The end of the timing, set with Toc().
static bool m_TimingInit;//Whether the performance info has bee queried.
static uint m_ProcessorCount;//The number of cores on the system, set in Init().
};
}
+1171 -1171
View File
File diff suppressed because it is too large Load Diff
+1342 -1342
View File
File diff suppressed because it is too large Load Diff
+2638 -2638
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+67 -67
View File
@@ -1,67 +1,67 @@
#pragma once
#include "Variation.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.
/// This class follows the singleton pattern.
/// All variations are deleted upon destruction.
/// Template argument expected to be float or double.
/// </summary>
template <typename T>
class EMBER_API VariationList: public Singleton<VariationList<T>>
{
public:
const Variation<T>* GetVariation(size_t index) const;
const Variation<T>* GetVariation(size_t index, eVariationType varType) const;
Variation<T>* GetVariationCopy(size_t index, T weight = 1) const;
Variation<T>* GetVariationCopy(size_t index, eVariationType varType, T weight = 1) const;
const Variation<T>* GetVariation(eVariationId id) const;
Variation<T>* GetVariationCopy(eVariationId id, T weight = 1) const;
const Variation<T>* GetVariation(const string& name) const;
Variation<T>* GetVariationCopy(const string& name, T weight = 1) const;
const ParametricVariation<T>* GetParametricVariation(size_t index) const;
const ParametricVariation<T>* GetParametricVariation(const string& name) const;
ParametricVariation<T>* GetParametricVariationCopy(eVariationId id, T weight = 1) const;
const Variation<T>* GetPreVariation(const string& name) const;
const Variation<T>* GetPostVariation(const string& name) const;
int GetVariationIndex(const string& name) const;
size_t Size() const;
size_t RegSize() const;
size_t PreSize() const;
size_t PostSize() const;
size_t ParametricSize() const;
size_t NonParametricSize() const;
const vector<const Variation<T>*>& AllVars() const;
const vector<const Variation<T>*>& RegVars() const;
const vector<const Variation<T>*>& PreVars() const;
const vector<const Variation<T>*>& PostVars() const;
const vector<const Variation<T>*>& NonParametricVariations() const;
const vector<const ParametricVariation<T>*>& ParametricVariations() const;
SINGLETON_DERIVED_DECL(VariationList<T>);
private:
VariationList();
Variation<T>* MakeCopyWithWeight(const Variation<T>* var, T weight) const;
template <template <typename> class U>
const U<T>* SearchVarName(const vector<const U<T>*>& vars, const string& name) const;
vector<const Variation<T>*> m_Variations;//A list of pointers to dynamically allocated variation objects.
vector<const Variation<T>*> m_RegVariations;
vector<const Variation<T>*> m_PreVariations;
vector<const Variation<T>*> m_PostVariations;
vector<const Variation<T>*> m_NonParametricVariations;
vector<const ParametricVariation<T>*> m_ParametricVariations;//A list of pointers to elements in m_Variations which are derived from ParametricVariation.
};
}
#pragma once
#include "Variation.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.
/// This class follows the singleton pattern.
/// All variations are deleted upon destruction.
/// Template argument expected to be float or double.
/// </summary>
template <typename T>
class EMBER_API VariationList: public Singleton<VariationList<T>>
{
public:
const Variation<T>* GetVariation(size_t index) const;
const Variation<T>* GetVariation(size_t index, eVariationType varType) const;
Variation<T>* GetVariationCopy(size_t index, T weight = 1) const;
Variation<T>* GetVariationCopy(size_t index, eVariationType varType, T weight = 1) const;
const Variation<T>* GetVariation(eVariationId id) const;
Variation<T>* GetVariationCopy(eVariationId id, T weight = 1) const;
const Variation<T>* GetVariation(const string& name) const;
Variation<T>* GetVariationCopy(const string& name, T weight = 1) const;
const ParametricVariation<T>* GetParametricVariation(size_t index) const;
const ParametricVariation<T>* GetParametricVariation(const string& name) const;
ParametricVariation<T>* GetParametricVariationCopy(eVariationId id, T weight = 1) const;
const Variation<T>* GetPreVariation(const string& name) const;
const Variation<T>* GetPostVariation(const string& name) const;
int GetVariationIndex(const string& name) const;
size_t Size() const;
size_t RegSize() const;
size_t PreSize() const;
size_t PostSize() const;
size_t ParametricSize() const;
size_t NonParametricSize() const;
const vector<const Variation<T>*>& AllVars() const;
const vector<const Variation<T>*>& RegVars() const;
const vector<const Variation<T>*>& PreVars() const;
const vector<const Variation<T>*>& PostVars() const;
const vector<const Variation<T>*>& NonParametricVariations() const;
const vector<const ParametricVariation<T>*>& ParametricVariations() const;
SINGLETON_DERIVED_DECL(VariationList<T>);
private:
VariationList();
Variation<T>* MakeCopyWithWeight(const Variation<T>* var, T weight) const;
template <template <typename> class U>
const U<T>* SearchVarName(const vector<const U<T>*>& vars, const string& name) const;
vector<const Variation<T>*> m_Variations;//A list of pointers to dynamically allocated variation objects.
vector<const Variation<T>*> m_RegVariations;
vector<const Variation<T>*> m_PreVariations;
vector<const Variation<T>*> m_PostVariations;
vector<const Variation<T>*> m_NonParametricVariations;
vector<const ParametricVariation<T>*> m_ParametricVariations;//A list of pointers to elements in m_Variations which are derived from ParametricVariation.
};
}
+7463 -7463
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+6148 -6148
View File
File diff suppressed because it is too large Load Diff
+5123 -5123
View File
File diff suppressed because it is too large Load Diff
+5756 -5756
View File
File diff suppressed because it is too large Load Diff
+4370 -4370
View File
File diff suppressed because it is too large Load Diff
+5522 -5522
View File
File diff suppressed because it is too large Load Diff
+7824 -7824
View File
File diff suppressed because it is too large Load Diff
+316 -316
View File
@@ -1,317 +1,317 @@
#pragma once
#include "Variation.h"
namespace EmberNs
{
/// <summary>
/// Gnarly.
/// </summary>
template <typename T>
class GnarlyVariation : public ParametricVariation<T>
{
public:
GnarlyVariation(T weight = 1.0) : ParametricVariation<T>("gnarly", eVariationId::VAR_GNARLY, weight)
{
Init();
}
PARVARCOPY(GnarlyVariation)
virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
{
T Vx, Vy;
T Cx, Cy;
T Lx, Ly;
T r, theta, s, c;
Vx = helper.In.x;
Vy = helper.In.y;
if (m_GnarlyCellSize != T(0))
{
Cx = (Floor<T>(Vx / m_GnarlyCellSize) + T(0.5)) * m_GnarlyCellSize;
Cy = (Floor<T>(Vy / m_GnarlyCellSize) + T(0.5)) * m_GnarlyCellSize;
Lx = Vx - Cx;
Ly = Vy - Cy;
if ((Lx * Lx + Ly * Ly) <= m_R2)
{
r = (Lx * Lx + Ly * Ly) / m_R2;
theta = m_GnarlyTwist * std::log(r);
sincos(theta, &s, &c);
Vx = Cx + c * Lx + s * Ly;
Vy = Cy - s * Lx + c * Ly;
}
}
helper.Out.x = m_Weight * Vx;
helper.Out.y = m_Weight * Vy;
helper.Out.z = DefaultZ(helper);
}
virtual string OpenCLString() const override
{
ostringstream ss, ss2;
intmax_t i = 0;
ss2 << "_" << XformIndexInEmber() << "]";
string index = ss2.str();
string weight = WeightDefineString();
string cellsize = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
string twist = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
string r2 = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
ss << "\t{\n"
<< "\t\treal_t Vx, Vy, Cx, Cy, Lx, Ly, Lxy;\n"
<< "\t\treal_t r, theta, s, c;\n"
<< "\n"
<< "\t\tVx = vIn.x;\n"
<< "\t\tVy = vIn.y;\n"
<< "\n"
<< "\t\tif (" << cellsize << " != (real_t)(0))\n"
<< "\t\t{\n"
<< "\t\t\tCx = (floor(Vx / " << cellsize << ") + (real_t)(0.5)) * " << cellsize << ";\n"
<< "\t\t\tCy = (floor(Vy / " << cellsize << ") + (real_t)(0.5)) * " << cellsize << ";\n"
<< "\n"
<< "\t\t\tLx = Vx - Cx;\n"
<< "\t\t\tLy = Vy - Cy;\n"
<< "\t\t\tLxy = fma(Lx, Lx, Ly * Ly);\n"
<< "\n"
<< "\t\t\tif (Lxy <= " << r2 << ")\n"
<< "\t\t\t{\n"
<< "\t\t\t\tr = Lxy / " << r2 << ";\n"
<< "\t\t\t\ttheta = " << twist << " * log(r);\n"
<< "\t\t\t\ts = sin(theta);\n"
<< "\t\t\t\tc = cos(theta);\n"
<< "\t\t\t\tVx = Cx + c * Lx + s * Ly;\n"
<< "\t\t\t\tVy = Cy - s * Lx + c * Ly;\n"
<< "\t\t\t}\n"
<< "\t\t}\n"
<< "\n"
<< "\t\tvOut.x = " << weight << " * Vx;\n"
<< "\t\tvOut.y = " << weight << " * Vy;\n"
<< "\t\tvOut.z = " << DefaultZCl()
<< "\t}\n";
return ss.str();
}
virtual void Precalc() override
{
T radius = T(0.5) * m_GnarlyCellSize;
m_R2 = Zeps(SQR(radius));
}
protected:
void Init()
{
string prefix = Prefix();
m_Params.clear();
m_Params.push_back(ParamWithName<T>(&m_GnarlyCellSize, prefix + "gnarly_cellsize", T(1)));
m_Params.push_back(ParamWithName<T>(&m_GnarlyTwist, prefix + "gnarly_twist", T(1)));
m_Params.push_back(ParamWithName<T>(true, &m_R2, prefix + "gnarly_r2"));//Precalc.
}
private:
T m_GnarlyCellSize;
T m_GnarlyTwist;
T m_R2;//Precalc.
};
/// <summary>
/// inkdrop by Jess.
/// </summary>
template <typename T>
class InkdropVariation : public ParametricVariation<T>
{
public:
InkdropVariation(T weight = 1.0) : ParametricVariation<T>("inkdrop", eVariationId::VAR_INKDROP, weight)
{
Init();
}
PARVARCOPY(InkdropVariation)
virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
{
T distx = helper.In.x - m_X;
T disty = helper.In.y - m_Y;
T dist2 = SQR(distx) + SQR(disty);
T adjust = std::sqrt(dist2 + m_Rad2) - std::sqrt(dist2);
T bearing = std::atan2(disty, distx);
T x = helper.In.x + (std::cos(bearing) * adjust);
T y = helper.In.y + (std::sin(bearing) * adjust);
helper.Out.x = m_Weight * x;
helper.Out.y = m_Weight * y;
helper.Out.z = DefaultZ(helper);
}
virtual string OpenCLString() const override
{
ostringstream ss, ss2;
intmax_t i = 0;
ss2 << "_" << XformIndexInEmber() << "]";
string index = ss2.str();
string weight = WeightDefineString();
string r = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
string x = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
string y = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
string rad2 = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
ss << "\t{\n"
<< "\t\treal_t distx = vIn.x - " << x << ";\n"
<< "\t\treal_t disty = vIn.y - " << y << ";\n"
<< "\t\treal_t dist2 = SQR(distx) + SQR(disty);\n"
<< "\t\treal_t adjust = sqrt(dist2 + " << rad2 << ") - sqrt(dist2);\n"
<< "\n"
<< "\t\treal_t bearing = atan2(disty, distx);\n"
<< "\t\treal_t x = fma(cos(bearing), adjust, vIn.x);\n"
<< "\t\treal_t y = fma(sin(bearing), adjust, vIn.y);\n"
<< "\n"
<< "\t\tvOut.x = " << weight << " * x;\n"
<< "\t\tvOut.y = " << weight << " * y;\n"
<< "\t\tvOut.z = " << DefaultZCl()
<< "\t}\n";
return ss.str();
}
virtual void Precalc() override
{
m_Rad2 = SQR(m_R);
}
protected:
void Init()
{
string prefix = Prefix();
m_Params.clear();
m_Params.push_back(ParamWithName<T>(&m_R, prefix + "inkdrop_r", T(0.5), eParamType::REAL, 0));
m_Params.push_back(ParamWithName<T>(&m_X, prefix + "inkdrop_x"));
m_Params.push_back(ParamWithName<T>(&m_Y, prefix + "inkdrop_y"));
m_Params.push_back(ParamWithName<T>(true, &m_Rad2, prefix + "inkdrop_rad2"));//Precalc.
}
private:
T m_R;
T m_X;
T m_Y;
T m_Rad2;//Precalc.
};
/// <summary>
/// hex_modulus.
/// By tatasz.
/// </summary>
template <typename T>
class HexModulusVariation : public ParametricVariation<T>
{
public:
HexModulusVariation(T weight = 1.0) : ParametricVariation<T>("hex_modulus", eVariationId::VAR_HEX_MODULUS, weight)
{
Init();
}
PARVARCOPY(HexModulusVariation)
virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
{
//get hex
T X = helper.In.x * m_HsizePrecalc;
T Y = helper.In.y * m_HsizePrecalc;
T yover3 = Y / 3;
T x = M_SQRT3_3 * X - yover3;
T z = T(2.0) * yover3;
T y = -x - z;
//round
T rx = std::round(x);
T ry = std::round(y);
T rz = std::round(z);
T x_diff = std::abs(rx - x);
T y_diff = std::abs(ry - y);
T z_diff = std::abs(rz - z);
if ((x_diff > y_diff) & (x_diff > z_diff))
rx = -ry - rz;
else if (y_diff > z_diff)
ry = -rx - rz;
else
rz = -rx - ry;
T FX_h = M_SQRT3 * rx + M_SQRT3_2 * rz;
T FY_h = T(1.5) * rz;
T FX = X - FX_h;
T FY = Y - FY_h;
helper.Out.x = FX * m_WeightPrecalc;
helper.Out.y = FY * m_WeightPrecalc;
helper.Out.z = DefaultZ(helper);
}
virtual string OpenCLString() const override
{
ostringstream ss, ss2;
intmax_t i = 0;
ss2 << "_" << XformIndexInEmber() << "]";
string index = ss2.str();
string weight = WeightDefineString();
string size = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
string hsizeprecalc = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
string weightprecalc = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
ss << "\t{\n"
<< "\t\t//get hex\n"
<< "\t\treal_t X = vIn.x * " << hsizeprecalc << ";\n"
<< "\t\treal_t Y = vIn.y * " << hsizeprecalc << ";\n"
<< "\t\treal_t yover3 = Y / (real_t)(3.0);\n"
<< "\t\treal_t x = fma(M_SQRT3_3, X, -yover3);\n"
<< "\t\treal_t z = (real_t)(2.0) * yover3;\n"
<< "\t\treal_t y = -x - z;\n"
<< "\t\t//round\n"
<< "\t\treal_t rx = round(x);\n"
<< "\t\treal_t ry = round(y);\n"
<< "\t\treal_t rz = round(z);\n"
<< "\n"
<< "\t\treal_t x_diff = fabs(rx - x);\n"
<< "\t\treal_t y_diff = fabs(ry - y);\n"
<< "\t\treal_t z_diff = fabs(rz - z);\n"
<< "\n"
<< "\t\tif ((x_diff > y_diff) & (x_diff > z_diff))\n"
<< "\t\trx = -ry - rz;\n"
<< "\t\telse if (y_diff > z_diff)\n"
<< "\t\try = -rx - rz;\n"
<< "\t\telse\n"
<< "\t\trz = -rx - ry;\n"
<< "\n"
<< "\t\treal_t FX_h = fma(M_SQRT3, rx, M_SQRT3_2 * rz);\n"
<< "\t\treal_t FY_h = (real_t)(1.5) * rz;\n"
<< "\n"
<< "\t\treal_t FX = X - FX_h;\n"
<< "\t\treal_t FY = Y - FY_h;\n"
<< "\n"
<< "\t\tvOut.x = FX * " << weightprecalc << ";\n"
<< "\t\tvOut.y = FY * " << weightprecalc << ";\n"
<< "\t\tvOut.z = " << DefaultZCl()
<< "\t}\n";
return ss.str();
}
virtual void Precalc() override
{
m_HsizePrecalc = M_SQRT3_2 / Zeps(m_Size);
m_WeightPrecalc = m_Weight / M_SQRT3_2;
}
protected:
void Init()
{
string prefix = Prefix();
m_Params.clear();
m_Params.push_back(ParamWithName<T>(&m_Size, prefix + "hex_modulus_size", T(1.0)));
m_Params.push_back(ParamWithName<T>(true, &m_HsizePrecalc, prefix + "hex_modulus_hsize_precalc"));//Precalc.
m_Params.push_back(ParamWithName<T>(true, &m_WeightPrecalc, prefix + "hex_modulus_weight_precalc"));
}
private:
T m_Size;
T m_HsizePrecalc;//Precalc.
T m_WeightPrecalc;
};
MAKEPREPOSTPARVAR(Gnarly, gnarly, GNARLY)
MAKEPREPOSTPARVAR(Inkdrop, inkdrop, INKDROP)
MAKEPREPOSTPARVAR(HexModulus, hex_modulus, HEX_MODULUS)
#pragma once
#include "Variation.h"
namespace EmberNs
{
/// <summary>
/// Gnarly.
/// </summary>
template <typename T>
class GnarlyVariation : public ParametricVariation<T>
{
public:
GnarlyVariation(T weight = 1.0) : ParametricVariation<T>("gnarly", eVariationId::VAR_GNARLY, weight)
{
Init();
}
PARVARCOPY(GnarlyVariation)
virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
{
T Vx, Vy;
T Cx, Cy;
T Lx, Ly;
T r, theta, s, c;
Vx = helper.In.x;
Vy = helper.In.y;
if (m_GnarlyCellSize != T(0))
{
Cx = (Floor<T>(Vx / m_GnarlyCellSize) + T(0.5)) * m_GnarlyCellSize;
Cy = (Floor<T>(Vy / m_GnarlyCellSize) + T(0.5)) * m_GnarlyCellSize;
Lx = Vx - Cx;
Ly = Vy - Cy;
if ((Lx * Lx + Ly * Ly) <= m_R2)
{
r = (Lx * Lx + Ly * Ly) / m_R2;
theta = m_GnarlyTwist * std::log(r);
sincos(theta, &s, &c);
Vx = Cx + c * Lx + s * Ly;
Vy = Cy - s * Lx + c * Ly;
}
}
helper.Out.x = m_Weight * Vx;
helper.Out.y = m_Weight * Vy;
helper.Out.z = DefaultZ(helper);
}
virtual string OpenCLString() const override
{
ostringstream ss, ss2;
intmax_t i = 0;
ss2 << "_" << XformIndexInEmber() << "]";
string index = ss2.str();
string weight = WeightDefineString();
string cellsize = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
string twist = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
string r2 = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
ss << "\t{\n"
<< "\t\treal_t Vx, Vy, Cx, Cy, Lx, Ly, Lxy;\n"
<< "\t\treal_t r, theta, s, c;\n"
<< "\n"
<< "\t\tVx = vIn.x;\n"
<< "\t\tVy = vIn.y;\n"
<< "\n"
<< "\t\tif (" << cellsize << " != (real_t)(0))\n"
<< "\t\t{\n"
<< "\t\t\tCx = (floor(Vx / " << cellsize << ") + (real_t)(0.5)) * " << cellsize << ";\n"
<< "\t\t\tCy = (floor(Vy / " << cellsize << ") + (real_t)(0.5)) * " << cellsize << ";\n"
<< "\n"
<< "\t\t\tLx = Vx - Cx;\n"
<< "\t\t\tLy = Vy - Cy;\n"
<< "\t\t\tLxy = fma(Lx, Lx, Ly * Ly);\n"
<< "\n"
<< "\t\t\tif (Lxy <= " << r2 << ")\n"
<< "\t\t\t{\n"
<< "\t\t\t\tr = Lxy / " << r2 << ";\n"
<< "\t\t\t\ttheta = " << twist << " * log(r);\n"
<< "\t\t\t\ts = sin(theta);\n"
<< "\t\t\t\tc = cos(theta);\n"
<< "\t\t\t\tVx = Cx + c * Lx + s * Ly;\n"
<< "\t\t\t\tVy = Cy - s * Lx + c * Ly;\n"
<< "\t\t\t}\n"
<< "\t\t}\n"
<< "\n"
<< "\t\tvOut.x = " << weight << " * Vx;\n"
<< "\t\tvOut.y = " << weight << " * Vy;\n"
<< "\t\tvOut.z = " << DefaultZCl()
<< "\t}\n";
return ss.str();
}
virtual void Precalc() override
{
T radius = T(0.5) * m_GnarlyCellSize;
m_R2 = Zeps(SQR(radius));
}
protected:
void Init()
{
string prefix = Prefix();
m_Params.clear();
m_Params.push_back(ParamWithName<T>(&m_GnarlyCellSize, prefix + "gnarly_cellsize", T(1)));
m_Params.push_back(ParamWithName<T>(&m_GnarlyTwist, prefix + "gnarly_twist", T(1)));
m_Params.push_back(ParamWithName<T>(true, &m_R2, prefix + "gnarly_r2"));//Precalc.
}
private:
T m_GnarlyCellSize;
T m_GnarlyTwist;
T m_R2;//Precalc.
};
/// <summary>
/// inkdrop by Jess.
/// </summary>
template <typename T>
class InkdropVariation : public ParametricVariation<T>
{
public:
InkdropVariation(T weight = 1.0) : ParametricVariation<T>("inkdrop", eVariationId::VAR_INKDROP, weight)
{
Init();
}
PARVARCOPY(InkdropVariation)
virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
{
T distx = helper.In.x - m_X;
T disty = helper.In.y - m_Y;
T dist2 = SQR(distx) + SQR(disty);
T adjust = std::sqrt(dist2 + m_Rad2) - std::sqrt(dist2);
T bearing = std::atan2(disty, distx);
T x = helper.In.x + (std::cos(bearing) * adjust);
T y = helper.In.y + (std::sin(bearing) * adjust);
helper.Out.x = m_Weight * x;
helper.Out.y = m_Weight * y;
helper.Out.z = DefaultZ(helper);
}
virtual string OpenCLString() const override
{
ostringstream ss, ss2;
intmax_t i = 0;
ss2 << "_" << XformIndexInEmber() << "]";
string index = ss2.str();
string weight = WeightDefineString();
string r = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
string x = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
string y = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
string rad2 = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
ss << "\t{\n"
<< "\t\treal_t distx = vIn.x - " << x << ";\n"
<< "\t\treal_t disty = vIn.y - " << y << ";\n"
<< "\t\treal_t dist2 = SQR(distx) + SQR(disty);\n"
<< "\t\treal_t adjust = sqrt(dist2 + " << rad2 << ") - sqrt(dist2);\n"
<< "\n"
<< "\t\treal_t bearing = atan2(disty, distx);\n"
<< "\t\treal_t x = fma(cos(bearing), adjust, vIn.x);\n"
<< "\t\treal_t y = fma(sin(bearing), adjust, vIn.y);\n"
<< "\n"
<< "\t\tvOut.x = " << weight << " * x;\n"
<< "\t\tvOut.y = " << weight << " * y;\n"
<< "\t\tvOut.z = " << DefaultZCl()
<< "\t}\n";
return ss.str();
}
virtual void Precalc() override
{
m_Rad2 = SQR(m_R);
}
protected:
void Init()
{
string prefix = Prefix();
m_Params.clear();
m_Params.push_back(ParamWithName<T>(&m_R, prefix + "inkdrop_r", T(0.5), eParamType::REAL, 0));
m_Params.push_back(ParamWithName<T>(&m_X, prefix + "inkdrop_x"));
m_Params.push_back(ParamWithName<T>(&m_Y, prefix + "inkdrop_y"));
m_Params.push_back(ParamWithName<T>(true, &m_Rad2, prefix + "inkdrop_rad2"));//Precalc.
}
private:
T m_R;
T m_X;
T m_Y;
T m_Rad2;//Precalc.
};
/// <summary>
/// hex_modulus.
/// By tatasz.
/// </summary>
template <typename T>
class HexModulusVariation : public ParametricVariation<T>
{
public:
HexModulusVariation(T weight = 1.0) : ParametricVariation<T>("hex_modulus", eVariationId::VAR_HEX_MODULUS, weight)
{
Init();
}
PARVARCOPY(HexModulusVariation)
virtual void Func(IteratorHelper<T>& helper, Point<T>& outPoint, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
{
//get hex
T X = helper.In.x * m_HsizePrecalc;
T Y = helper.In.y * m_HsizePrecalc;
T yover3 = Y / 3;
T x = M_SQRT3_3 * X - yover3;
T z = T(2.0) * yover3;
T y = -x - z;
//round
T rx = std::round(x);
T ry = std::round(y);
T rz = std::round(z);
T x_diff = std::abs(rx - x);
T y_diff = std::abs(ry - y);
T z_diff = std::abs(rz - z);
if ((x_diff > y_diff) & (x_diff > z_diff))
rx = -ry - rz;
else if (y_diff > z_diff)
ry = -rx - rz;
else
rz = -rx - ry;
T FX_h = M_SQRT3 * rx + M_SQRT3_2 * rz;
T FY_h = T(1.5) * rz;
T FX = X - FX_h;
T FY = Y - FY_h;
helper.Out.x = FX * m_WeightPrecalc;
helper.Out.y = FY * m_WeightPrecalc;
helper.Out.z = DefaultZ(helper);
}
virtual string OpenCLString() const override
{
ostringstream ss, ss2;
intmax_t i = 0;
ss2 << "_" << XformIndexInEmber() << "]";
string index = ss2.str();
string weight = WeightDefineString();
string size = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
string hsizeprecalc = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
string weightprecalc = "parVars[" + ToUpper(m_Params[i++].Name()) + index;
ss << "\t{\n"
<< "\t\t//get hex\n"
<< "\t\treal_t X = vIn.x * " << hsizeprecalc << ";\n"
<< "\t\treal_t Y = vIn.y * " << hsizeprecalc << ";\n"
<< "\t\treal_t yover3 = Y / (real_t)(3.0);\n"
<< "\t\treal_t x = fma(M_SQRT3_3, X, -yover3);\n"
<< "\t\treal_t z = (real_t)(2.0) * yover3;\n"
<< "\t\treal_t y = -x - z;\n"
<< "\t\t//round\n"
<< "\t\treal_t rx = round(x);\n"
<< "\t\treal_t ry = round(y);\n"
<< "\t\treal_t rz = round(z);\n"
<< "\n"
<< "\t\treal_t x_diff = fabs(rx - x);\n"
<< "\t\treal_t y_diff = fabs(ry - y);\n"
<< "\t\treal_t z_diff = fabs(rz - z);\n"
<< "\n"
<< "\t\tif ((x_diff > y_diff) & (x_diff > z_diff))\n"
<< "\t\trx = -ry - rz;\n"
<< "\t\telse if (y_diff > z_diff)\n"
<< "\t\try = -rx - rz;\n"
<< "\t\telse\n"
<< "\t\trz = -rx - ry;\n"
<< "\n"
<< "\t\treal_t FX_h = fma(M_SQRT3, rx, M_SQRT3_2 * rz);\n"
<< "\t\treal_t FY_h = (real_t)(1.5) * rz;\n"
<< "\n"
<< "\t\treal_t FX = X - FX_h;\n"
<< "\t\treal_t FY = Y - FY_h;\n"
<< "\n"
<< "\t\tvOut.x = FX * " << weightprecalc << ";\n"
<< "\t\tvOut.y = FY * " << weightprecalc << ";\n"
<< "\t\tvOut.z = " << DefaultZCl()
<< "\t}\n";
return ss.str();
}
virtual void Precalc() override
{
m_HsizePrecalc = M_SQRT3_2 / Zeps(m_Size);
m_WeightPrecalc = m_Weight / M_SQRT3_2;
}
protected:
void Init()
{
string prefix = Prefix();
m_Params.clear();
m_Params.push_back(ParamWithName<T>(&m_Size, prefix + "hex_modulus_size", T(1.0)));
m_Params.push_back(ParamWithName<T>(true, &m_HsizePrecalc, prefix + "hex_modulus_hsize_precalc"));//Precalc.
m_Params.push_back(ParamWithName<T>(true, &m_WeightPrecalc, prefix + "hex_modulus_weight_precalc"));
}
private:
T m_Size;
T m_HsizePrecalc;//Precalc.
T m_WeightPrecalc;
};
MAKEPREPOSTPARVAR(Gnarly, gnarly, GNARLY)
MAKEPREPOSTPARVAR(Inkdrop, inkdrop, INKDROP)
MAKEPREPOSTPARVAR(HexModulus, hex_modulus, HEX_MODULUS)
}
+1662 -1662
View File
File diff suppressed because it is too large Load Diff
+1329 -1329
View File
File diff suppressed because it is too large Load Diff
+2618 -2618
View File
File diff suppressed because it is too large Load Diff
+84 -84
View File
@@ -1,84 +1,84 @@
#pragma once
#include "Utils.h"
#include "PaletteList.h"
#include "VariationList.h"
#include "Ember.h"
#include "Spline.h"
#ifdef __APPLE__
#include <libgen.h>
#endif
/// <summary>
/// XmlToEmber and Locale classes.
/// </summary>
namespace EmberNs
{
/// <summary>
/// Convenience class for setting and resetting the locale.
/// It's set up in the constructor and restored in the destructor.
/// This relieves the caller of having to manually do it everywhere.
/// </summary>
class EMBER_API Locale
{
public:
Locale(int category = LC_NUMERIC, const char* loc = "C");
~Locale();
private:
int m_Category;
string m_NewLocale;
string m_OriginalLocale;
};
/// <summary>
/// Class for reading standard Xml flame files as well as Chaotica .chaos files into ember objects.
/// This class derives from EmberReport, so the caller is able
/// to retrieve a text dump of error information if any errors occur.
/// Since this class contains a VariationList object, it's important to declare one
/// instance and reuse it for the duration of the program instead of creating and deleting
/// them as local variables.
/// Template argument expected to be float or double.
/// </summary>
template <typename T>
class EMBER_API XmlToEmber : public EmberReport
{
public:
XmlToEmber();
template <typename Alloc, template <typename, typename> class C>
bool Parse(byte* buf, const char* filename, C<Ember<T>, Alloc>& embers, bool useDefaults);
template <typename Alloc, template <typename, typename> class C>
bool Parse(const char* filename, C<Ember<T>, Alloc>& embers, bool useDefaults);
template <typename valT>
bool Aton(const char* str, valT& val);
static vector<string> m_FlattenNames;
private:
template <typename Alloc, template <typename, typename> class C>
void ScanForEmberNodes(xmlNode* curNode, const char* parentFile, C<Ember<T>, Alloc>& embers, bool useDefaults);
template <typename Alloc, template <typename, typename> class C>
void ScanForChaosNodes(xmlNode* curNode, const char* parentFile, C<Ember<T>, Alloc>& embers, bool useDefaults);
bool ParseEmberElement(xmlNode* emberNode, Ember<T>& currentEmber);
bool ParseEmberElementFromChaos(xmlNode* emberNode, Ember<T>& currentEmber);
bool AttToEmberMotionFloat(xmlAttrPtr att, const char* attStr, eEmberMotionParam param, EmberMotion<T>& motion);
bool ParseXform(xmlNode* childNode, Xform<T>& xform, bool motion, bool fromEmber);
static string GetCorrectedParamName(const unordered_map<string, string>& names, const char* name);
static string GetCorrectedVariationName(vector<pair<pair<string, string>, vector<string>>>& vec, xmlAttrPtr att);
static string GetCorrectedVariationName(vector<pair<pair<string, string>, vector<string>>>& vec, const string& varname);
static bool XmlContainsTag(xmlAttrPtr att, const char* name);
bool ParseHexColors(const char* colstr, Ember<T>& ember, size_t numColors, intmax_t chan);
template <typename valT>
bool ParseAndAssign(const xmlChar* name, const char* attStr, const char* str, valT& val, bool& b);
template <typename valT>
bool ParseAndAssignContent(xmlNode* node, const char* fieldname, const char* fieldnameval, valT& val);
bool ParseAndAssignContent(xmlNode* node, const char* fieldname, const char* fieldnameval, std::string& val);
static bool m_Init;
static unordered_map<string, string> m_BadParamNames;
static vector<pair<pair<string, string>, vector<string>>> m_BadVariationNames;
shared_ptr<VariationList<T>> m_VariationList;//The variation list used to make copies of variations to populate the embers with.
shared_ptr<PaletteList<float>> m_PaletteList;
};
}
#pragma once
#include "Utils.h"
#include "PaletteList.h"
#include "VariationList.h"
#include "Ember.h"
#include "Spline.h"
#ifdef __APPLE__
#include <libgen.h>
#endif
/// <summary>
/// XmlToEmber and Locale classes.
/// </summary>
namespace EmberNs
{
/// <summary>
/// Convenience class for setting and resetting the locale.
/// It's set up in the constructor and restored in the destructor.
/// This relieves the caller of having to manually do it everywhere.
/// </summary>
class EMBER_API Locale
{
public:
Locale(int category = LC_NUMERIC, const char* loc = "C");
~Locale();
private:
int m_Category;
string m_NewLocale;
string m_OriginalLocale;
};
/// <summary>
/// Class for reading standard Xml flame files as well as Chaotica .chaos files into ember objects.
/// This class derives from EmberReport, so the caller is able
/// to retrieve a text dump of error information if any errors occur.
/// Since this class contains a VariationList object, it's important to declare one
/// instance and reuse it for the duration of the program instead of creating and deleting
/// them as local variables.
/// Template argument expected to be float or double.
/// </summary>
template <typename T>
class EMBER_API XmlToEmber : public EmberReport
{
public:
XmlToEmber();
template <typename Alloc, template <typename, typename> class C>
bool Parse(byte* buf, const char* filename, C<Ember<T>, Alloc>& embers, bool useDefaults);
template <typename Alloc, template <typename, typename> class C>
bool Parse(const char* filename, C<Ember<T>, Alloc>& embers, bool useDefaults);
template <typename valT>
bool Aton(const char* str, valT& val);
static vector<string> m_FlattenNames;
private:
template <typename Alloc, template <typename, typename> class C>
void ScanForEmberNodes(xmlNode* curNode, const char* parentFile, C<Ember<T>, Alloc>& embers, bool useDefaults);
template <typename Alloc, template <typename, typename> class C>
void ScanForChaosNodes(xmlNode* curNode, const char* parentFile, C<Ember<T>, Alloc>& embers, bool useDefaults);
bool ParseEmberElement(xmlNode* emberNode, Ember<T>& currentEmber);
bool ParseEmberElementFromChaos(xmlNode* emberNode, Ember<T>& currentEmber);
bool AttToEmberMotionFloat(xmlAttrPtr att, const char* attStr, eEmberMotionParam param, EmberMotion<T>& motion);
bool ParseXform(xmlNode* childNode, Xform<T>& xform, bool motion, bool fromEmber);
static string GetCorrectedParamName(const unordered_map<string, string>& names, const char* name);
static string GetCorrectedVariationName(vector<pair<pair<string, string>, vector<string>>>& vec, xmlAttrPtr att);
static string GetCorrectedVariationName(vector<pair<pair<string, string>, vector<string>>>& vec, const string& varname);
static bool XmlContainsTag(xmlAttrPtr att, const char* name);
bool ParseHexColors(const char* colstr, Ember<T>& ember, size_t numColors, intmax_t chan);
template <typename valT>
bool ParseAndAssign(const xmlChar* name, const char* attStr, const char* str, valT& val, bool& b);
template <typename valT>
bool ParseAndAssignContent(xmlNode* node, const char* fieldname, const char* fieldnameval, valT& val);
bool ParseAndAssignContent(xmlNode* node, const char* fieldname, const char* fieldnameval, std::string& val);
static bool m_Init;
static unordered_map<string, string> m_BadParamNames;
static vector<pair<pair<string, string>, vector<string>>> m_BadVariationNames;
shared_ptr<VariationList<T>> m_VariationList;//The variation list used to make copies of variations to populate the embers with.
shared_ptr<PaletteList<float>> m_PaletteList;
};
}