--Bug fixes

-Fix crash on palette editor when opening it with certain palette files in a particular order.
 -An xform with only a post variation in it might have showed up wrong.
 -The xforms combo box was obscuring the name of the xforms by not being wide enough.
 -Make variation state preservation be a little bit more correct in OpenCL.

--Code changes
 -Make all iterators on the CPU use a temporary point.
This commit is contained in:
Person
2020-02-17 18:45:02 -08:00
parent 8cd2340484
commit 8c66fa2c24
18 changed files with 341 additions and 119 deletions

View File

@ -37,7 +37,7 @@ static void sincos(float x, float* s, float* c)
namespace EmberNs
{
#define EMBER_VERSION "1.0.0.18"
#define EMBER_VERSION "1.0.0.19"
//#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.

View File

@ -300,7 +300,7 @@ public:
virtual size_t Iterate(Ember<T>& ember, const IterParams<T> params, const CarToRas<T>& ctr, Point<T>* samples, QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand) override
{
size_t i, badVals = 0;
Point<T> tempPoint, p1;
Point<T> tempPoint, p1, p2;
auto xforms = ember.NonConstXforms();
if (ember.ProjBits())//No xaos, 3D.
@ -311,8 +311,10 @@ public:
for (i = 0; i < params.m_Skip; i++)//Fuse.
{
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p1, rand))
DoBadVals(xforms, ember.m_RandPointRange, badVals, &p1, rand);
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p2, rand))
DoBadVals(xforms, ember.m_RandPointRange, badVals, &p2, rand);
p1 = p2;
}
DoFinalXform(ember, p1, samples, rand);//Apply to last fuse point and store as the first element in samples.
@ -320,10 +322,11 @@ public:
for (i = 1; i < params.m_Count; i++)//Real loop.
{
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p1, rand))
DoBadVals(xforms, ember.m_RandPointRange, badVals, &p1, rand);
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p2, rand))
DoBadVals(xforms, ember.m_RandPointRange, badVals, &p2, rand);
DoFinalXform(ember, p1, samples + i, rand);
p1 = p2;
DoFinalXform(ember, p2, samples + i, rand);
ember.Proj(samples[i], rand, ctr);
}
}
@ -333,8 +336,10 @@ public:
for (i = 0; i < params.m_Skip; i++)//Fuse.
{
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p1, rand))
DoBadVals(xforms, ember.m_RandPointRange, badVals, &p1, rand);
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p2, rand))
DoBadVals(xforms, ember.m_RandPointRange, badVals, &p2, rand);
p1 = p2;
}
samples[0] = p1;
@ -358,18 +363,21 @@ public:
for (i = 0; i < params.m_Skip; i++)//Fuse.
{
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p1, rand))
DoBadVals(xforms, ember.m_RandPointRange, badVals, &p1, rand);
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p2, rand))
DoBadVals(xforms, ember.m_RandPointRange, badVals, &p2, rand);
p1 = p2;
}
DoFinalXform(ember, p1, samples, rand);//Apply to last fuse point and store as the first element in samples.
for (i = 1; i < params.m_Count; i++)//Real loop.
{
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p1, rand))//Feed the resulting value of applying the randomly selected xform back into the next iter, and not the result of applying the final xform.
DoBadVals(xforms, ember.m_RandPointRange, badVals, &p1, rand);
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p2, rand))//Feed the resulting value of applying the randomly selected xform back into the next iter, and not the result of applying the final xform.
DoBadVals(xforms, ember.m_RandPointRange, badVals, &p2, rand);
DoFinalXform(ember, p1, samples + i, rand);
p1 = p2;
DoFinalXform(ember, p2, samples + i, rand);
}
}
else//No xaos, no 3D, no final.
@ -378,8 +386,10 @@ public:
for (i = 0; i < params.m_Skip; i++)//Fuse.
{
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p1, rand))
DoBadVals(xforms, ember.m_RandPointRange, badVals, &p1, rand);
if (xforms[NextXformFromIndex(rand.Rand())].Apply(&p1, &p2, rand))
DoBadVals(xforms, ember.m_RandPointRange, badVals, &p2, rand);
p1 = p2;
}
samples[0] = p1;
@ -468,7 +478,7 @@ public:
size_t i, xformIndex;
size_t lastXformUsed = 0;
size_t badVals = 0;
Point<T> tempPoint, p1;
Point<T> tempPoint, p1, p2;
auto xforms = ember.NonConstXforms();
if (ember.ProjBits())//Xaos, 3D.
@ -481,9 +491,10 @@ public:
{
xformIndex = NextXformFromIndex(rand.Rand(), lastXformUsed);
if (xforms[xformIndex].Apply(&p1, &p1, rand))
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p1, rand);
if (xforms[xformIndex].Apply(&p1, &p2, rand))
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p2, rand);
p1 = p2;
lastXformUsed = xformIndex + 1;//Store the last used transform.
}
@ -494,10 +505,11 @@ public:
{
xformIndex = NextXformFromIndex(rand.Rand(), lastXformUsed);
if (xforms[xformIndex].Apply(&p1, &p1, rand))//Feed the resulting value of applying the randomly selected xform back into the next iter, and not the result of applying the final xform.
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p1, rand);
if (xforms[xformIndex].Apply(&p1, &p2, rand))//Feed the resulting value of applying the randomly selected xform back into the next iter, and not the result of applying the final xform.
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p2, rand);
DoFinalXform(ember, p1, samples + i, rand);
p1 = p2;
DoFinalXform(ember, p2, samples + i, rand);
ember.Proj(samples[i], rand, ctr);
lastXformUsed = xformIndex + 1;//Store the last used transform.
}
@ -510,9 +522,10 @@ public:
{
xformIndex = NextXformFromIndex(rand.Rand(), lastXformUsed);
if (xforms[xformIndex].Apply(&p1, &p1, rand))
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p1, rand);
if (xforms[xformIndex].Apply(&p1, &p2, rand))
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p2, rand);
p1 = p2;
lastXformUsed = xformIndex + 1;//Store the last used transform.
}
@ -523,10 +536,10 @@ public:
{
xformIndex = NextXformFromIndex(rand.Rand(), lastXformUsed);
if (xforms[xformIndex].Apply(&p1, &p1, rand))
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p1, rand);
if (xforms[xformIndex].Apply(&p1, &p2, rand))
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p2, rand);
samples[i] = p1;
samples[i] = p1 = p2;
ember.Proj(samples[i], rand, ctr);
lastXformUsed = xformIndex + 1;//Store the last used transform.
}
@ -542,9 +555,10 @@ public:
{
xformIndex = NextXformFromIndex(rand.Rand(), lastXformUsed);
if (xforms[xformIndex].Apply(&p1, &p1, rand))
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p1, rand);
if (xforms[xformIndex].Apply(&p1, &p2, rand))
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p2, rand);
p1 = p2;
lastXformUsed = xformIndex + 1;//Store the last used transform.
}
@ -554,10 +568,11 @@ public:
{
xformIndex = NextXformFromIndex(rand.Rand(), lastXformUsed);
if (xforms[xformIndex].Apply(&p1, &p1, rand))//Feed the resulting value of applying the randomly selected xform back into the next iter, and not the result of applying the final xform.
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p1, rand);
if (xforms[xformIndex].Apply(&p1, &p2, rand))//Feed the resulting value of applying the randomly selected xform back into the next iter, and not the result of applying the final xform.
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p2, rand);
DoFinalXform(ember, p1, samples + i, rand);
p1 = p2;
DoFinalXform(ember, p2, samples + i, rand);
lastXformUsed = xformIndex + 1;//Store the last used transform.
}
}
@ -569,9 +584,10 @@ public:
{
xformIndex = NextXformFromIndex(rand.Rand(), lastXformUsed);
if (xforms[xformIndex].Apply(&p1, &p1, rand))
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p1, rand);
if (xforms[xformIndex].Apply(&p1, &p2, rand))
DoBadVals(xforms, xformIndex, ember.m_RandPointRange, lastXformUsed, badVals, &p2, rand);
p1 = p2;
lastXformUsed = xformIndex + 1;//Store the last used transform.
}

View File

@ -603,24 +603,24 @@ public:
outPoint->m_Opacity = m_Opacity;
iterHelper.m_Color.x = outPoint->m_ColorX = m_ColorSpeedCache + (m_OneMinusColorCache * inPoint->m_ColorX);
//Compute the pre affine portion of the transform.
//These x, y values are what get passed to the variations below.
//Note that they are not changed after this, except in the case of pre_ variations.
if (m_HasPre)
{
iterHelper.m_TransX = (m_Affine.A() * inPoint->m_X) + (m_Affine.B() * inPoint->m_Y) + m_Affine.C();
iterHelper.m_TransY = (m_Affine.D() * inPoint->m_X) + (m_Affine.E() * inPoint->m_Y) + m_Affine.F();
}
else
{
iterHelper.m_TransX = inPoint->m_X;
iterHelper.m_TransY = inPoint->m_Y;
}
iterHelper.m_TransZ = inPoint->m_Z;
if (m_HasPreOrRegularVars)
{
//Compute the pre affine portion of the transform.
//These x, y values are what get passed to the variations below.
//Note that they are not changed after this, except in the case of pre_ variations.
if (m_HasPre)
{
iterHelper.m_TransX = (m_Affine.A() * inPoint->m_X) + (m_Affine.B() * inPoint->m_Y) + m_Affine.C();
iterHelper.m_TransY = (m_Affine.D() * inPoint->m_X) + (m_Affine.E() * inPoint->m_Y) + m_Affine.F();
}
else
{
iterHelper.m_TransX = inPoint->m_X;
iterHelper.m_TransY = inPoint->m_Y;
}
iterHelper.m_TransZ = inPoint->m_Z;
//Apply pre_ variations, these don't affect outPoint, only iterHelper.m_TransX, Y, Z.
for (i = 0; i < PreVariationCount(); i++)
{

View File

@ -25,10 +25,87 @@ template <typename T> const string& IterOpenCLKernelCreator<T>::ZeroizeEntryPoin
template <typename T> const string& IterOpenCLKernelCreator<T>::SumHistKernel() const { return m_SumHistKernel; }
template <typename T> const string& IterOpenCLKernelCreator<T>::SumHistEntryPoint() const { return m_SumHistEntryPoint; }
template <typename T> const string& IterOpenCLKernelCreator<T>::IterEntryPoint() const { return m_IterEntryPoint; }
/// <summary>
/// Create the iteration kernel string using the Cuburn method.
/// Template argument expected to be float or double.
/// Pre Reg Post Formula
/// x trans = affine(inpoint)
/// foreach prevar
/// tempin = trans
/// tempout = prevar(i, tempin)
/// trans = tempout
/// outpoint = trans
///
/// x x trans = affine(inpoint)
/// foreach prevar
/// tempin = trans
/// tempout = prevar(i, tempin)
/// trans = tempout
/// tempin = trans
/// outpoint = 0
/// foreach regvar
/// tempout = regvar(i, tempin)
/// outpoint += tempout
//
/// x x x
/// trans = affine(inpoint)
/// foreach prevar
/// tempin = trans
/// tempout = prevar(i, tempin)
/// trans = tempout
/// tempin = trans
/// outpoint = 0
/// foreach regvar
/// tempout = regvar(i, tempin)
/// outpoint += tempout
/// foreach postvar
/// tempin = outpoint
/// tempout = postvar(i, tempin)
/// outpoint = tempout
///
/// x x
/// trans = affine(inpoint)
/// foreach prevar
/// tempin = trans
/// tempout = prevar(i, tempin)
/// trans = tempout
/// outpoint = trans
/// foreach postvar
/// tempin = outpoint
/// tempout = postvar(i, tempin)
/// outpoint = tempout
///
/// x
/// trans = affine(inpoint)
/// tempin = trans
/// outpoint = 0
/// foreach regvar
/// tempout = regvar(i, tempin)
/// outpoint += tempout
///
/// x x
/// trans = affine(inpoint)
/// tempin = trans
/// outpoint = 0
/// foreach regvar
/// tempout = regvar(i, tempin)
/// outpoint += tempout
/// foreach postvar
/// tempin = outpoint
/// tempout = postvar(i, tempin)
/// outpoint = tempout
///
/// x
/// trans = affine(inpoint)
/// outpoint = 0
/// foreach postvar
/// tempin = outpoint
/// tempout = postvar(i, tempin)
/// outpoint = tempout
///
/// none trans = affine(inpoint)
/// outpoint = 0
///
/// </summary>
/// <param name="ember">The ember to create the kernel string for</param>
/// <param name="params">The parametric variation #define string</param>
@ -59,6 +136,7 @@ string IterOpenCLKernelCreator<T>::CreateIterKernelString(const Ember<T>& ember,
bool needPrecalcAngles = false;
bool needPrecalcAtanXY = false;
bool needPrecalcAtanYX = false;
bool hasPreReg = (xform->PreVariationCount() + xform->VariationCount()) > 0;
v = varIndex = varCount = 0;
xformFuncs <<
"void Xform" << i << "(__constant XformCL* xform, __constant real_t* parVars, __global real_t* globalShared, Point* inPoint, Point* outPoint, uint2* mwc, VariationState* varState)\n" <<
@ -96,32 +174,24 @@ string IterOpenCLKernelCreator<T>::CreateIterKernelString(const Ember<T>& ember,
xformFuncs << "\treal_t tempColor = outPoint->m_ColorX = fma(xform->m_OneMinusColorCache, inPoint->m_ColorX, xform->m_ColorSpeedCache);\n\n";
if (xform->PreVariationCount() + xform->VariationCount() == 0)
if (optAffine && xform->m_Affine.IsID())
{
xformFuncs <<
" outPoint->m_X = 0;\n"
" outPoint->m_Y = 0;\n"
" outPoint->m_Z = 0;\n";
" transX = inPoint->m_X;\n" <<
" transY = inPoint->m_Y;\n";
}
else
{
if (optAffine && xform->m_Affine.IsID())
{
xformFuncs <<
" transX = inPoint->m_X;\n" <<
" transY = inPoint->m_Y;\n";
}
else
{
xformFuncs <<
" transX = fma(xform->m_A, inPoint->m_X, fma(xform->m_B, inPoint->m_Y, xform->m_C));\n" <<
" transY = fma(xform->m_D, inPoint->m_X, fma(xform->m_E, inPoint->m_Y, xform->m_F));\n";
}
xformFuncs <<
" transZ = inPoint->m_Z;\n";
varCount = xform->PreVariationCount();
" transX = fma(xform->m_A, inPoint->m_X, fma(xform->m_B, inPoint->m_Y, xform->m_C));\n" <<
" transY = fma(xform->m_D, inPoint->m_X, fma(xform->m_E, inPoint->m_Y, xform->m_F));\n";
}
xformFuncs << " transZ = inPoint->m_Z;\n";
varCount = xform->PreVariationCount();
if (hasPreReg)
{
if (varCount > 0)
{
xformFuncs << "\n\t//Apply each of the " << varCount << " pre variations in this xform.\n";
@ -186,6 +256,13 @@ string IterOpenCLKernelCreator<T>::CreateIterKernelString(const Ember<T>& ember,
" outPoint->m_Z = transZ;\n";
}
}
else
{
xformFuncs <<
" outPoint->m_X = 0;\n"
" outPoint->m_Y = 0;\n"
" outPoint->m_Z = 0;\n";
}
if (xform->PostVariationCount() > 0)
{
@ -450,13 +527,6 @@ string IterOpenCLKernelCreator<T>::CreateIterKernelString(const Ember<T>& ember,
"\n"
//Write to another thread's location.
" swap[sw] = secondPoint;\n";
if (hasVarState)
{
os <<
" varStates[blockStartIndex + sw] = varState;\n";
}
os <<
"\n"
//Populate randomized xform index buffer with new random values.
@ -466,13 +536,6 @@ string IterOpenCLKernelCreator<T>::CreateIterKernelString(const Ember<T>& ember,
" barrier(CLK_LOCAL_MEM_FENCE);\n"
//Another thread will have written to this thread's location, so read the new value and use it for accumulation below.
" firstPoint = swap[threadIndex];\n";
if (hasVarState)
{
os <<
" varState = varStates[blockStartThreadIndex];\n"
;
}
}
else
{
@ -488,14 +551,40 @@ string IterOpenCLKernelCreator<T>::CreateIterKernelString(const Ember<T>& ember,
os <<
"\n"
" if (fuse)\n"
" {\n"
" {\n";
if (hasVarState && ember.XformCount() > 1)
{
os <<
" varStates[blockStartIndex + sw] = varState;\n\n";
}
os <<
" if (i >= fuseCount - 1)\n"
" {\n"
" i = 0;\n"
" fuse = false;\n"
" itersToDo = iterCount;\n"
" barrier(CLK_LOCAL_MEM_FENCE);\n"//Sort of seems necessary, sort of doesn't. Makes no speed difference.
" itersToDo = iterCount;\n";
if (ember.XformCount() > 1)
os <<
" barrier(CLK_LOCAL_MEM_FENCE);\n"//Sort of seems necessary, sort of doesn't. Makes no speed difference.
;
os <<
" }\n"
;
if (hasVarState && ember.XformCount() > 1)
{
os <<
"\n"
" barrier(CLK_GLOBAL_MEM_FENCE);\n"//Sort of seems necessary, sort of doesn't. Makes no speed difference.
" varState = varStates[blockStartThreadIndex];"
;
}
os <<
"\n"
" continue;\n"
" }\n"
@ -516,6 +605,13 @@ string IterOpenCLKernelCreator<T>::CreateIterKernelString(const Ember<T>& ember,
"\n";
}
if (hasVarState && ember.XformCount() > 1)
{
os <<
" varStates[blockStartIndex + sw] = varState;\n"
;
}
os << CreateProjectionString(ember);
if (doAccum)
@ -591,8 +687,16 @@ string IterOpenCLKernelCreator<T>::CreateIterKernelString(const Ember<T>& ember,
os <<
" }\n"//histIndex < histSize.
" }\n"//CarToRasInBounds.
"\n"
"\n";
os <<
" barrier(CLK_GLOBAL_MEM_FENCE);\n";//Barrier every time, whether or not the point was in bounds, else artifacts will occur when doing strips.
if (hasVarState && ember.XformCount() > 1)
{
os <<
" varState = varStates[blockStartThreadIndex];\n"
;
}
}
os <<
@ -610,7 +714,7 @@ string IterOpenCLKernelCreator<T>::CreateIterKernelString(const Ember<T>& ember,
" seeds[pointsIndex] = mwc;\n"
" points[blockStartThreadIndex] = firstPoint;\n";
if (hasVarState)
if (hasVarState && ember.XformCount() == 1)
{
os <<
" varStates[blockStartThreadIndex] = varState;\n";

View File

@ -58,7 +58,7 @@
<enum>QFrame::NoFrame</enum>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Fractorium 1.0.0.18&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;A Qt-based fractal flame editor which uses a C++ re-write of the flam3 algorithm named Ember and a GPU capable version named EmberCL which implements a portion of the cuburn algorithm in OpenCL.&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;a href=&quot;http://fractorium.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;fractorium.com&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Fractorium 1.0.0.19&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;A Qt-based fractal flame editor which uses a C++ re-write of the flam3 algorithm named Ember and a GPU capable version named EmberCL which implements a portion of the cuburn algorithm in OpenCL.&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;a href=&quot;http://fractorium.com&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;fractorium.com&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>

View File

@ -139,6 +139,7 @@ public slots:
void OnActionNewEmptyFlameInCurrentFile(bool checked);
void OnActionNewRandomFlameInCurrentFile(bool checked);
void OnActionCopyFlameInCurrentFile(bool checked);
void OnActionCreateReferenceFile(bool checked);
void OnActionOpen(bool checked);
void OnActionSaveCurrentAsXml(bool checked);
void OnActionSaveEntireFileAsXml(bool checked);

View File

@ -96,6 +96,7 @@ public:
virtual void NewEmptyFlameInCurrentFile() { }
virtual void NewRandomFlameInCurrentFile() { }
virtual void CopyFlameInCurrentFile() { }
virtual void CreateReferenceFile() { }
virtual void OpenAndPrepFiles(const QStringList& filenames, bool append) { }
virtual void SaveCurrentAsXml() { }
virtual void SaveEntireFileAsXml() { }
@ -384,6 +385,7 @@ public:
virtual void NewEmptyFlameInCurrentFile() override;
virtual void NewRandomFlameInCurrentFile() override;
virtual void CopyFlameInCurrentFile() override;
virtual void CreateReferenceFile() override;
virtual void OpenAndPrepFiles(const QStringList& filenames, bool append) override;
virtual void SaveCurrentAsXml() override;
virtual void SaveEntireFileAsXml() override;

View File

@ -11,6 +11,7 @@ void Fractorium::InitMenusUI()
connect(ui.ActionNewEmptyFlameInCurrentFile, SIGNAL(triggered(bool)), this, SLOT(OnActionNewEmptyFlameInCurrentFile(bool)), Qt::QueuedConnection);
connect(ui.ActionNewRandomFlameInCurrentFile, SIGNAL(triggered(bool)), this, SLOT(OnActionNewRandomFlameInCurrentFile(bool)), Qt::QueuedConnection);
connect(ui.ActionCopyFlameInCurrentFile, SIGNAL(triggered(bool)), this, SLOT(OnActionCopyFlameInCurrentFile(bool)), Qt::QueuedConnection);
connect(ui.ActionCreateReferenceFile, SIGNAL(triggered(bool)), this, SLOT(OnActionCreateReferenceFile(bool)), Qt::QueuedConnection);
connect(ui.ActionOpen, SIGNAL(triggered(bool)), this, SLOT(OnActionOpen(bool)), Qt::QueuedConnection);
connect(ui.ActionSaveCurrentAsXml, SIGNAL(triggered(bool)), this, SLOT(OnActionSaveCurrentAsXml(bool)), Qt::QueuedConnection);
connect(ui.ActionSaveEntireFileAsXml, SIGNAL(triggered(bool)), this, SLOT(OnActionSaveEntireFileAsXml(bool)), Qt::QueuedConnection);
@ -172,6 +173,104 @@ void FractoriumEmberController<T>::CopyFlameInCurrentFile()
void Fractorium::OnActionCopyFlameInCurrentFile(bool checked) { m_Controller->CopyFlameInCurrentFile(); }
/// <summary>
/// Create a reference file containing one ember for every possible regular variation, plus one for post_smartcrop
/// since it only exists in post form.
/// This will replace whatever file the user has open.
/// Clears the undo state.
/// Resets the rendering process.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::CreateReferenceFile()
{
bool nv = false;
size_t i;
StopAllPreviewRenderers();
auto temppal = m_Ember.m_Palette;
m_EmberFile.Clear();
m_EmberFile.m_Filename = QString("Reference_") + EMBER_VERSION;
auto varList = VariationList<T>::Instance();
auto& regVars = varList->RegVars();
auto count = regVars.size();
auto addsquaresfunc = [&](size_t i, const Variation<T>* var)
{
Ember<T> ember;
Xform<T> xf0, xf1, xf2, xf3, xf4, xffinal;
//
xf0.AddVariation(varList->GetVariationCopy(eVariationId::VAR_SQUARE, T(0.5)));
//
xf1.m_Affine.C(T(0.5));
xf1.m_Affine.F(T(0.5));
xf1.AddVariation(varList->GetVariationCopy(eVariationId::VAR_LINEAR));
//
xf2.m_Affine.C(T(-0.5));
xf2.m_Affine.F(T(0.5));
xf2.AddVariation(varList->GetVariationCopy(eVariationId::VAR_LINEAR));
//
xf3.m_Affine.C(T(-0.5));
xf3.m_Affine.F(T(-0.5));
xf3.AddVariation(varList->GetVariationCopy(eVariationId::VAR_LINEAR));
//
xf4.m_Affine.C(T(0.5));
xf4.m_Affine.F(T(-0.5));
xf4.AddVariation(varList->GetVariationCopy(eVariationId::VAR_LINEAR));
//
xffinal.AddVariation(var->Copy());
//
ember.AddXform(xf0);
ember.AddXform(xf1);
ember.AddXform(xf2);
ember.AddXform(xf3);
ember.AddXform(xf4);
ember.SetFinalXform(xffinal);
ember.EqualizeWeights();
ember.m_Index = i;
ember.m_MaxRadDE = 0;
ember.m_Name = var->Name() + " Squares";
ember.m_Palette = temppal;
m_EmberFile.m_Embers.push_back(ember);
};
auto addsbarsfunc = [&](size_t i, const Variation<T>* var)
{
Ember<T> ember;
Xform<T> xf0, xf1, xf2, xffinal;
//
xf0.AddVariation(varList->GetVariationCopy(eVariationId::VAR_PRE_BLUR, T(100)));
xf0.AddVariation(varList->GetVariationCopy(eVariationId::VAR_CYLINDER, T(0.1)));
//
xf1.m_Affine.C(T(0.4));
xf1.AddVariation(varList->GetVariationCopy(eVariationId::VAR_LINEAR));
//
xf2.m_Affine.C(T(-0.4));
xf2.AddVariation(varList->GetVariationCopy(eVariationId::VAR_LINEAR));
//
xffinal.AddVariation(var->Copy());
ember.AddXform(xf0);
ember.AddXform(xf1);
ember.AddXform(xf2);
ember.SetFinalXform(xffinal);
ember.EqualizeWeights();
ember.m_Index = i;
ember.m_MaxRadDE = 0;
ember.m_Name = var->Name() + " Bars";
ember.m_Palette = temppal;
m_EmberFile.m_Embers.push_back(ember);
};
for (i = 0; i < count; i++)
{
addsquaresfunc(i, regVars[i]);
addsbarsfunc(i, regVars[i]);
}
addsquaresfunc(i, varList->GetVariation(eVariationId::VAR_POST_SMARTCROP));//post_smartcrop is the only variation that exists only in post form, so it must be done manually here.
addsbarsfunc(i, varList->GetVariation(eVariationId::VAR_POST_SMARTCROP));
m_LastSaveAll = "";
FillLibraryTree();
}
void Fractorium::OnActionCreateReferenceFile(bool checked) { m_Controller->CreateReferenceFile(); }
/// <summary>
/// Open a list of ember Xml files, apply various values from the GUI widgets.
/// Either append these newly read embers to the existing open embers,

View File

@ -36,7 +36,7 @@ void Fractorium::InitXformsUI()
ui.CurrentXformCombo->view()->setMinimumWidth(100);
ui.CurrentXformCombo->view()->setMaximumWidth(500);
//ui.CurrentXformCombo->view()->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
ui.CurrentXformCombo->view()->setSizeAdjustPolicy(QAbstractScrollArea::SizeAdjustPolicy::AdjustToContentsOnFirstShow);
ui.CurrentXformCombo->view()->setSizeAdjustPolicy(QAbstractScrollArea::SizeAdjustPolicy::AdjustToContents);
#ifndef _WIN32
//For some reason linux makes these 24x24, even though the designer explicitly says 16x16.
ui.AddXformButton->setIconSize(QSize(16, 16));
@ -518,9 +518,9 @@ void FractoriumEmberController<T>::FillWithXform(Xform<T>* xform)
if (auto item = m_Fractorium->ui.XformWeightNameTable->item(0, 1))
{
m_Fractorium->ui.XformWeightNameTable->blockSignals(true);
item->setText(QString::fromStdString(xform->m_Name));
m_Fractorium->ui.XformWeightNameTable->blockSignals(false);
m_Fractorium->m_XformNameEdit->blockSignals(true);
m_Fractorium->m_XformNameEdit->setText(QString::fromStdString(xform->m_Name));
m_Fractorium->m_XformNameEdit->blockSignals(false);
}
FillVariationTreeWithXform(xform);
@ -656,7 +656,7 @@ void FractoriumEmberController<T>::UpdateXformName(int index)
auto view = m_Fractorium->ui.CurrentXformCombo->view();
auto fontMetrics1 = view->fontMetrics();
auto textWidth = m_Fractorium->ui.CurrentXformCombo->width();
auto ww = fontMetrics1.width("WW");
auto ww = fontMetrics1.width("WW") * 3;
for (int i = 0; i < m_Fractorium->ui.CurrentXformCombo->count(); ++i)
textWidth = std::max(fontMetrics1.width(m_Fractorium->ui.CurrentXformCombo->itemText(i)) + ww, textWidth);

View File

@ -433,7 +433,7 @@ void PaletteEditor::OnCopyPaletteFileButtonClicked()
/// </summary>
void PaletteEditor::OnAppendPaletteButtonClicked()
{
auto& pal = m_GradientColorView->GetPalette(256);
auto& pal = GetPalette(256);
m_PaletteList->AddPaletteToFile(m_CurrentPaletteFilePath, pal);
::FillPaletteTable(m_CurrentPaletteFilePath, ui->PaletteListTable, m_PaletteList);
m_PaletteIndex = ui->PaletteListTable->rowCount() - 1;
@ -446,7 +446,7 @@ void PaletteEditor::OnAppendPaletteButtonClicked()
/// </summary>
void PaletteEditor::OnOverwritePaletteButtonClicked()
{
auto& pal = m_GradientColorView->GetPalette(256);
auto& pal = GetPalette(256);
m_PaletteList->Replace(m_CurrentPaletteFilePath, pal, m_PaletteIndex);
::FillPaletteTable(m_CurrentPaletteFilePath, ui->PaletteListTable, m_PaletteList);
emit PaletteFileChanged();
@ -632,10 +632,11 @@ void PaletteEditor::EnablePaletteFileControls()
void PaletteEditor::EnablePaletteControls()
{
bool b = IsCurrentPaletteAndFileEditable();//Both the file and the current palette must be editable.
auto& pal = GetPalette(256);
ui->DeletePaletteButton->setEnabled(b);
ui->CopyPaletteFileButton->setEnabled(b);
ui->AppendPaletteButton->setEnabled(b);
ui->OverwritePaletteButton->setEnabled(b && (GetFilename(m_CurrentPaletteFilePath) == GetFilename(*m_GradientColorView->GetPalette(256).m_Filename.get())));//Only allow overwrite if the palette is from the file it's overwriting a palette in.
ui->OverwritePaletteButton->setEnabled(b && pal.m_Filename.get() && (GetFilename(m_CurrentPaletteFilePath) == GetFilename(*pal.m_Filename.get())));//Only allow overwrite if the palette is from the file it's overwriting a palette in.
ui->AddColorButton->setEnabled(b);
ui->DistributeColorsButton->setEnabled(b);
ui->AutoDistributeCheckBox->setEnabled(b);
@ -654,5 +655,5 @@ void PaletteEditor::EnablePaletteControls()
/// <returns>True if both the currently selected palette is editable and if all palettes in the currently selected file are editable.</returns>
bool PaletteEditor::IsCurrentPaletteAndFileEditable()
{
return m_PaletteList->IsModifiable(m_CurrentPaletteFilePath) && !m_GradientColorView->GetPalette(256).m_SourceColors.empty();
return m_PaletteList->IsModifiable(m_CurrentPaletteFilePath) && !GetPalette(256).m_SourceColors.empty();
}

View File

@ -1,5 +1,4 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
@ -10,7 +9,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("apoconv")]
[assembly: AssemblyCopyright("Copyright © 2019")]
[assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]