--User changes

-Allow locking of the scale at which affine circles are displayed.
 -Allow user to toggle whether xform will be animated when generating sequences. Also show the animate value when loading.

--Code changes
 -More conversion to C++11 style code.
 -Add another value to the eXformUpdate enum called UPDATE_CURRENT_AND_SELECTED in anticipation of future work.
 -Remove some old #defines.
This commit is contained in:
mfeemster 2016-02-02 17:51:58 -08:00
parent 96d53113a0
commit f10e4e610a
24 changed files with 424 additions and 358 deletions

View File

@ -112,6 +112,22 @@ typename v2T Affine2D<T>::operator * (const v2T& v)
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 * (const T& t)
{
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.
@ -172,6 +188,21 @@ bool Affine2D<T>::IsEmpty() const
(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>
/// Rotate this affine transform around its origin by the specified angle in degrees.
/// </summary>
@ -181,7 +212,6 @@ void Affine2D<T>::Rotate(T angle)
{
m4T origMat4 = ToMat4ColMajor(true);//Must center and use column major for glm to work.
m4T newMat4 = glm::rotate(origMat4, angle * DEG_2_RAD_T, v3T(0, 0, 1));//Assuming only rotating around z.
A(newMat4[0][0]);//Use direct assignments instead of constructor to skip assigning C and F.
B(newMat4[0][1]);
D(newMat4[1][0]);
@ -206,7 +236,6 @@ template <typename T>
void Affine2D<T>::RotateScaleXTo(const v2T& v)
{
Affine2D<T> rs = CalcRotateScale(X(), v);
X(rs.TransformNormal(X()));
Y(rs.TransformNormal(Y()));
}
@ -219,7 +248,6 @@ template <typename T>
void Affine2D<T>::RotateScaleYTo(const v2T& v)
{
Affine2D<T> rs = CalcRotateScale(Y(), v);
X(rs.TransformNormal(X()));
Y(rs.TransformNormal(Y()));
}
@ -232,10 +260,9 @@ template <typename T>
Affine2D<T> Affine2D<T>::Inverse() const
{
T det = A() * E() - D() * B();
return Affine2D<T>(E() / det, -D() / det,
-B() / det, A() / det,
(F() * B() - C() * E()) / det, (C() * D() - F() * A()) / det);
-B() / det, A() / det,
(F() * B() - C() * E()) / det, (C() * D() - F() * A()) / det);
}
/// <summary>
@ -294,9 +321,8 @@ typename m4T Affine2D<T>::ToMat4ColMajor(bool center) const
{
m4T mat(A(), B(), 0, center ? 0 : C(), //Col0...
D(), E(), 0, center ? 0 : F(), //1
0, 0, 1, 0, //2
0, 0, 0, 1);//3
0, 0, 1, 0, //2
0, 0, 0, 1);//3
return mat;
}
@ -310,9 +336,8 @@ typename m4T Affine2D<T>::ToMat4RowMajor(bool center) const
{
m4T mat(A(), D(), 0, 0,
B(), E(), 0, 0,
0, 0, 1, 0,
0, 0, 1, 0,
center ? 0 : C(), center ? 0 : F(), 0, 1);
return mat;
}
@ -351,7 +376,6 @@ 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);
}
@ -368,7 +392,6 @@ template <typename T>
void Affine2D<T>::CalcRSAC(const v2T& from, const v2T& to, T& a, T& c)
{
T lsq = from.x * from.x + from.y * from.y;
a = (from.y * to.y + from.x * to.x) / lsq;
c = (from.x * to.y - from.y * to.x) / lsq;
}

View File

@ -64,17 +64,19 @@ public:
D(T(affine.D()));
E(T(affine.E()));
F(T(affine.F()));
return *this;
}
bool operator == (const Affine2D<T>& affine);
v2T operator * (const v2T& v);
Affine2D<T> operator * (const T& t);
void MakeID();
bool IsID() const;
bool IsZero() const;
bool IsEmpty() const;
void Scale(T amount);
Affine2D<T> ScaleCopy(T amount);
void Rotate(T angle);
void Translate(const v2T& v);
void RotateScaleXTo(const v2T& v);

View File

@ -314,8 +314,6 @@ public:
/// <returns>True if success, else false.</returns>
bool DeleteXform(size_t i)
{
Xform<T>* xform;
if (i < XformCount())
{
m_Xforms.erase(m_Xforms.begin() + i);
@ -323,7 +321,7 @@ public:
//Now shuffle xaos values from i on back by 1 for every xform.
for (size_t x1 = 0; x1 < XformCount(); x1++)
{
if ((xform = GetXform(x1)))
if (auto xform = GetXform(x1))
{
for (size_t x2 = i + 1; x2 <= XformCount(); x2++)//Iterate from the position after the deletion index up to the old count.
xform->SetXaos(x2 - 1, xform->Xaos(x2));
@ -463,7 +461,7 @@ public:
{
for (size_t i = 0; i < TotalXformCount(); i++)
{
Xform<T>* xform = GetTotalXform(i);
auto xform = GetTotalXform(i);
xform->CacheColorVals();
xform->SetPrecalcFlags();
}
@ -824,7 +822,7 @@ public:
//This includes all xforms plus final.
for (size_t i = 0; i < totalXformCount; i++)
{
Xform<T>* thisXform = GetTotalXform(i);
auto thisXform = GetTotalXform(i);
if (size == 2 && stagger > 0 && thisXform != &m_FinalXform)
{
@ -845,7 +843,7 @@ public:
for (size_t k = 0; k < size; k++)//For each ember in the list.
{
Xform<T>* tempXform = embers[k].GetTotalXform(i);//Xform in this position in this ember, including final.
auto tempXform = embers[k].GetTotalXform(i);//Xform in this position in this ember, including final.
if (tempXform)
{
@ -904,7 +902,7 @@ public:
{
if (i < embers[k].TotalXformCount())//Xform in this position in this ember.
{
Xform<T>* tempXform = embers[k].GetTotalXform(i);
auto tempXform = embers[k].GetTotalXform(i);
allID &= tempXform->m_Post.IsID();
}
}
@ -931,7 +929,7 @@ public:
for (size_t k = 0; k < size; k++)
{
Xform<T>* tempXform = embers[k].GetTotalXform(i);//Xform in this position in this ember.
auto tempXform = embers[k].GetTotalXform(i);//Xform in this position in this ember.
if (tempXform)
{
@ -971,7 +969,7 @@ public:
//Now fill them with interpolated values.
for (size_t j = 0; j < size; j++)//For each ember in the list.
{
Xform<T>* tempXform = embers[j].GetXform(i);
auto tempXform = embers[j].GetXform(i);
for (size_t k = 0; k < XformCount(); k++)//For each xaos entry in this xform's xaos array, sum it with the same entry in all of the embers multiplied by the coef for that ember.
{

View File

@ -41,7 +41,6 @@ public:
bool aligned = true;
bool currentFinal, final = sourceEmbers[0].UseFinalXform();
size_t i, xf, currentCount, maxCount = sourceEmbers[0].XformCount();
Xform<T>* destXform;
Xform<T>* destOtherXform;
//Determine the max number of xforms present in sourceEmbers.
@ -84,7 +83,7 @@ public:
for (xf = 0; xf < maxCount; xf++)//This will include both normal xforms and the final.
{
destXform = destEmbers[i].GetTotalXform(xf, final);
auto destXform = destEmbers[i].GetTotalXform(xf, final);
//Ensure every parametric variation contained in every xform at either position i - 1 or i + 1 is also contained in the dest xform.
if (i > 0)
@ -607,7 +606,7 @@ public:
//Keep translation linear.
zlm[0] = zlm[1] = 0;
if (Xform<T>* xform = embers[k].GetTotalXform(xfi))
if (auto xform = embers[k].GetTotalXform(xfi))
{
for (col = 0; col < 2; col++)
{
@ -650,7 +649,7 @@ public:
{
for (k = 1; k < size; k++)
{
if (Xform<T>* xform = embers[k].GetTotalXform(xfi))
if (auto xform = embers[k].GetTotalXform(xfi))
{
//Adjust angles differently if an asymmetric case.
if (xform->m_Wind[col] > 0 && cflag == 0)

View File

@ -98,7 +98,7 @@ public:
{
size_t i;
size_t distribCount = ember.XaosPresent() ? ember.XformCount() + 1 : 1;
const Xform<T>* xforms = ember.Xforms();
auto xforms = ember.Xforms();
if (m_XformDistributions.size() < CHOOSE_XFORM_GRAIN * distribCount)
m_XformDistributions.resize(CHOOSE_XFORM_GRAIN * distribCount);
@ -306,7 +306,7 @@ public:
{
size_t i, badVals = 0;
Point<T> tempPoint, p1;
Xform<T>* xforms = ember.NonConstXforms();
auto xforms = ember.NonConstXforms();
if (ember.ProjBits())
{
@ -473,7 +473,7 @@ public:
size_t lastXformUsed = 0;
size_t badVals = 0;
Point<T> tempPoint, p1;
Xform<T>* xforms = ember.NonConstXforms();
auto xforms = ember.NonConstXforms();
if (ember.ProjBits())
{

View File

@ -118,7 +118,7 @@ public:
//First clear out any xforms that are not the final, and have a density of less than 0.001.
for (i = 0; i < ember.XformCount(); i++)
{
Xform<T>* xform = ember.GetXform(i);
auto xform = ember.GetXform(i);
if (xform->m_Weight < T(0.001))
{
@ -131,7 +131,7 @@ public:
//Now consider all xforms, including final.
for (i = 0; i < ember.TotalXformCount(); i++)
{
Xform<T>* xform = ember.GetTotalXform(i);
auto xform = ember.GetTotalXform(i);
do
{
@ -223,8 +223,8 @@ public:
for (size_t i = 0; i < ember.TotalXformCount(); i++)
{
Xform<T>* xform1 = ember.GetTotalXform(i);
Xform<T>* xform2 = mutation.GetTotalXform(i);
auto xform1 = ember.GetTotalXform(i);
auto xform2 = mutation.GetTotalXform(i);
if (xform1 && xform2)
{
@ -254,8 +254,8 @@ public:
Random(mutation, useVars, sym, 2, maxVars);
//Which xform to mutate?
modXform = m_Rand.Rand() % ember.TotalXformCount();
Xform<T>* xform1 = ember.GetTotalXform(modXform);
Xform<T>* xform2 = mutation.GetTotalXform(0);
auto xform1 = ember.GetTotalXform(modXform);
auto xform2 = mutation.GetTotalXform(0);
os << "mutate xform " << modXform << " coefs";
//If less than 3 xforms, then change only the translation part.
@ -285,7 +285,7 @@ public:
for (size_t i = 0; i < ember.TotalXformCount(); i++)
{
bool copy = (i > 0) && same;
Xform<T>* xform = ember.GetTotalXform(i);
auto xform = ember.GetTotalXform(i);
if (copy)//Copy the post from the first xform to the rest of them.
{
@ -400,8 +400,8 @@ public:
//Change all the coefs by a fraction of the random.
for (size_t x = 0; x < ember.TotalXformCount(); x++)
{
Xform<T>* xform1 = ember.GetTotalXform(x);
Xform<T>* xform2 = mutation.GetTotalXform(x);
auto xform1 = ember.GetTotalXform(x);
auto xform2 = mutation.GetTotalXform(x);
for (glm::length_t i = 0; i < 2; i++)
for (glm::length_t j = 0; j < 3; j++)
@ -498,7 +498,7 @@ public:
{
if (i < ember1.XformCount() && ember1.GetXform(i)->m_Weight > 0)
{
Xform<T>* xform = emberOut.GetXform(i);
auto xform = emberOut.GetXform(i);
*xform = *ember1.GetXform(i);
os << " 1";
got1 = 1;
@ -513,7 +513,7 @@ public:
{
if (i < ember0.XformCount() && ember0.GetXform(i)->m_Weight > 0)
{
Xform<T>* xform = emberOut.GetXform(i);
auto xform = emberOut.GetXform(i);
*xform = *ember0.GetXform(i);
os << " 0";
got0 = 1;
@ -654,7 +654,7 @@ public:
//Loop over xforms.
for (i = 0; i < ember.TotalXformCount(); i++)
{
Xform<T>* xform = ember.GetTotalXform(i);
auto xform = ember.GetTotalXform(i);
xform->m_Weight = T(1) / ember.TotalXformCount();
xform->m_ColorX = m_Rand.Frand01<T>();//Original pingponged between 0 and 1, which gives bad coloring. Ember does random instead.
xform->m_ColorY = m_Rand.Frand01<T>();//Will need to update this if 2D coordinates are ever supported.
@ -699,7 +699,7 @@ public:
if (samed && i > 0)
{
//Copy the same variations from the previous xform.
Xform<T>* prevXform = ember.GetXform(i - 1);
auto prevXform = ember.GetXform(i - 1);
for (j = 0; j < prevXform->TotalVariationCount(); j++)
if (xform->TotalVariationCount() < maxVars)
@ -893,10 +893,6 @@ public:
/// <param name="changePalette">Change palette if true, else don't</param>
void ChangeColors(Ember<T>& ember, bool changePalette)
{
size_t i;
Xform<T>* xform0;
Xform<T>* xform1;
if (changePalette)
{
if (m_PaletteList.Size())
@ -910,14 +906,14 @@ public:
}
}
for (i = 0; i < ember.TotalXformCount(); i++)
for (size_t i = 0; i < ember.TotalXformCount(); i++)
{
ember.GetTotalXform(i)->m_ColorX = m_Rand.Frand01<T>();
ember.GetTotalXform(i)->m_ColorY = m_Rand.Frand01<T>();
}
xform0 = RandomXform(ember, -1);
xform1 = RandomXform(ember, ember.GetXformIndex(xform0));
auto xform0 = RandomXform(ember, -1);
auto xform1 = RandomXform(ember, ember.GetXformIndex(xform0));
if (xform0 && (m_Rand.Rand() & 1))
{
@ -949,7 +945,7 @@ public:
if (i != excluded)
{
Xform<T>* xform = ember.GetTotalXform(i);
auto xform = ember.GetTotalXform(i);
if (xform->m_Weight > 0)
return xform;
@ -975,8 +971,8 @@ public:
//the result xforms before rotate is called.
for (size_t i = 0; i < ember.TotalXformCount(); i++)
{
Xform<T>* xform1 = ember.GetTotalXform(i);
Xform<T>* xform2 = rotated.GetTotalXform(i);
auto xform1 = ember.GetTotalXform(i);
auto xform2 = rotated.GetTotalXform(i);
if (!xform1->m_Motion.empty())
xform2->ApplyMotion(*xform1, blend);
@ -1009,7 +1005,7 @@ public:
for (i = 0; i < embers[si].TotalXformCount(); i++)
{
Xform<T>* xform = embers[si].GetTotalXform(i);
auto xform = embers[si].GetTotalXform(i);
if (!xform->m_Motion.empty())
xform->ApplyMotion(*(prealign[si].GetTotalXform(i)), blend);//Apply motion parameters to result.xform[i] using blend parameter.

View File

@ -54,7 +54,7 @@ string IterOpenCLKernelCreator<T>::CreateIterKernelString(const Ember<T>& ember,
for (i = 0; i < totalXformCount; i++)
{
Xform<T>* xform = ember.GetTotalXform(i);
auto xform = ember.GetTotalXform(i);
bool needPrecalcSumSquares = false;
bool needPrecalcSqrtSumSquares = false;
bool needPrecalcAngles = false;
@ -831,9 +831,9 @@ bool IterOpenCLKernelCreator<T>::IsBuildRequired(const Ember<T>& ember1, const E
for (i = 0; i < xformCount; i++)
{
Xform<T>* xform1 = ember1.GetTotalXform(i);
Xform<T>* xform2 = ember2.GetTotalXform(i);
size_t varCount = xform1->TotalVariationCount();
auto xform1 = ember1.GetTotalXform(i);
auto xform2 = ember2.GetTotalXform(i);
auto varCount = xform1->TotalVariationCount();
if (xform1->HasPost() != xform2->HasPost())
return true;

View File

@ -1750,7 +1750,7 @@ void RendererCL<T, bucketT>::ConvertEmber(Ember<T>& ember, EmberCL<T>& emberCL,
for (size_t i = 0; i < ember.TotalXformCount() && i < xformsCL.size(); i++)
{
Xform<T>* xform = ember.GetTotalXform(i);
auto xform = ember.GetTotalXform(i);
xformsCL[i].m_A = xform->m_Affine.A();
xformsCL[i].m_B = xform->m_Affine.B();
xformsCL[i].m_C = xform->m_Affine.C();

View File

@ -1880,7 +1880,6 @@ void DistribTester()
size_t distribCount = 1;
size_t xformCount = 3;
vector<byte> m_XformDistributions;
//const Xform<T>* xforms = 3;
size_t j = 0;
vector<T> weights { T(0.333333), T(1.0), T(0.25) };
double tempDensity = 0, currentDensityLimit = 0, densityPerElement;

View File

@ -247,7 +247,7 @@ void FractoriumEmberController<T>::ApplyXmlSavingTemplate(Ember<T>& ember)
/// <returns>True if the current ember contains a final xform, else false.</returns>
bool Fractorium::HaveFinal()
{
QComboBox* combo = ui.CurrentXformCombo;
auto combo = ui.CurrentXformCombo;
return (combo->count() > 0 && combo->itemText(combo->count() - 1) == "Final");
}
@ -389,7 +389,9 @@ void Fractorium::dragEnterEvent(QDragEnterEvent* e)
{
if (e->mimeData()->hasUrls())
{
foreach (QUrl url, e->mimeData()->urls())
auto urls = e->mimeData()->urls();
for (auto& url : urls)
{
QString localFile = url.toLocalFile();
QFileInfo fileInfo(localFile);
@ -426,7 +428,9 @@ void Fractorium::dropEvent(QDropEvent* e)
if (e->mimeData()->hasUrls())
{
foreach (QUrl url, e->mimeData()->urls())
auto urls = e->mimeData()->urls();
for (auto& url : urls)
{
QString localFile = url.toLocalFile();
QFileInfo fileInfo(localFile);

View File

@ -203,8 +203,10 @@ public slots:
void OnXformWeightChanged(double d);
void OnEqualWeightButtonClicked(bool checked);
void OnXformNameChanged(int row, int col);
void OnXformAnimateCheckBoxStateChanged(int state);
//Xforms Affine.
void OnLockAffineScaleCheckBoxStateChanged(int state);
void OnPreAffineRowDoubleClicked(int logicalIndex);
void OnPreAffineColDoubleClicked(int logicalIndex);
void OnPostAffineRowDoubleClicked(int logicalIndex);

View File

@ -2350,7 +2350,7 @@
<property name="minimumSize">
<size>
<width>291</width>
<height>613</height>
<height>632</height>
</size>
</property>
<property name="features">
@ -2756,7 +2756,7 @@
</item>
</widget>
</item>
<item row="2" column="0">
<item row="3" column="0">
<widget class="QTabWidget" name="XformsTabWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="MinimumExpanding">
@ -2780,7 +2780,7 @@
<enum>QTabWidget::Triangular</enum>
</property>
<property name="currentIndex">
<number>2</number>
<number>1</number>
</property>
<widget class="QWidget" name="XformColorTab">
<property name="sizePolicy">
@ -3444,6 +3444,13 @@
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QCheckBox" name="LockAffineCheckBox">
<property name="text">
<string>Lock Affine Scale</string>
</property>
</widget>
</item>
<item>
<widget class="QScrollArea" name="AffineTabScrollArea">
<property name="autoFillBackground">
@ -3464,7 +3471,7 @@
<x>0</x>
<y>0</y>
<width>263</width>
<height>743</height>
<height>700</height>
</rect>
</property>
<property name="autoFillBackground">
@ -5479,7 +5486,7 @@
<x>0</x>
<y>0</y>
<width>259</width>
<height>673</height>
<height>652</height>
</rect>
</property>
<property name="sizePolicy">
@ -5529,6 +5536,16 @@
</widget>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="AnimateXformCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Rotate this xform when creating a sequence for animation&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Animate</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>

View File

@ -15,6 +15,7 @@ FractoriumEmberControllerBase::FractoriumEmberControllerBase(Fractorium* fractor
m_Shared = true;
m_FailedRenders = 0;
m_UndoIndex = 0;
m_LockedScale = 1;
m_RenderType = eRendererType::CPU_RENDERER;
m_OutputTexID = 0;
m_SubBatchCount = 1;//Will be ovewritten by the options on first render.
@ -224,6 +225,7 @@ void FractoriumEmberController<T>::Update(std::function<void (void)> func, bool
/// <summary>
/// Wrapper to call a function on the specified xforms, then optionally add the requested action to the rendering queue.
/// If no xforms are selected via the checkboxes, and the update type is UPDATE_SELECTED, then the function will be called only on the currently selected xform.
/// If the update type is UPDATE_CURRENT_AND_SELECTED, and the current is not among those selected, then the function will be called on the currently selected xform as well.
/// </summary>
/// <param name="func">The function to call</param>
/// <param name="updateType">Whether to apply this update operation on the current, all or selected xforms. Default: eXformUpdate::UPDATE_CURRENT.</param>
@ -240,19 +242,48 @@ void FractoriumEmberController<T>::UpdateXform(std::function<void(Xform<T>*)> fu
{
case eXformUpdate::UPDATE_CURRENT:
{
if (Xform<T>* xform = CurrentXform())
if (auto xform = CurrentXform())
func(xform);
}
break;
case eXformUpdate::UPDATE_CURRENT_AND_SELECTED:
{
bool currentDone = false;
auto current = CurrentXform();
while (auto xform = m_Ember.GetTotalXform(i))
{
if (auto child = m_Fractorium->m_XformsSelectionLayout->itemAt(i))
{
if (auto* w = qobject_cast<QCheckBox*>(child->widget()))
{
if (w->isChecked())
{
func(xform);
if (xform == current)
currentDone = true;
}
}
}
i++;
}
if (!currentDone)//Current was not among those selected, so apply to it.
func(current);
}
break;
case eXformUpdate::UPDATE_SELECTED:
case eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL:
{
bool anyUpdated = false;
while (Xform<T>* xform = (doFinal ? m_Ember.GetTotalXform(i) : m_Ember.GetXform(i)))
while (auto xform = (doFinal ? m_Ember.GetTotalXform(i) : m_Ember.GetXform(i)))
{
if (QLayoutItem* child = m_Fractorium->m_XformsSelectionLayout->itemAt(i))
if (auto child = m_Fractorium->m_XformsSelectionLayout->itemAt(i))
{
if (auto* w = qobject_cast<QCheckBox*>(child->widget()))
{
@ -269,14 +300,14 @@ void FractoriumEmberController<T>::UpdateXform(std::function<void(Xform<T>*)> fu
if (!anyUpdated)//None were selected, so just apply to the current.
if (doFinal || !isCurrentFinal)//If do final, call func regardless. If not, only call if current is not final.
if (Xform<T>* xform = CurrentXform())
if (auto xform = CurrentXform())
func(xform);
}
break;
case eXformUpdate::UPDATE_ALL:
{
while (Xform<T>* xform = m_Ember.GetTotalXform(i++))
while (auto xform = m_Ember.GetTotalXform(i++))
func(xform);
}
break;
@ -284,7 +315,7 @@ void FractoriumEmberController<T>::UpdateXform(std::function<void(Xform<T>*)> fu
case eXformUpdate::UPDATE_ALL_EXCEPT_FINAL:
default:
{
while (Xform<T>* xform = m_Ember.GetXform(i++))
while (auto xform = m_Ember.GetXform(i++))
func(xform);
}
break;
@ -325,6 +356,7 @@ void FractoriumEmberController<T>::SetEmberPrivate(const Ember<U>& ember, bool v
string filename = "last.flame";
writer.Save(filename.c_str(), m_Ember, 0, true, false, true);
m_GLController->ResetMouseState();
m_Fractorium->ui.LockAffineCheckBox->setChecked(false);
FillXforms();//Must do this first because the palette setup in FillParamTablesAndPalette() uses the xforms combo.
FillParamTablesAndPalette();
FillSummary();

View File

@ -16,7 +16,7 @@ enum class eEditUndoState : et { REGULAR_EDIT, UNDO_REDO, EDIT_UNDO };
/// <summary>
/// An enum representing which xforms an update should be applied to.
/// </summary>
enum class eXformUpdate : et { UPDATE_CURRENT, UPDATE_SELECTED, UPDATE_SELECTED_EXCEPT_FINAL, UPDATE_ALL, UPDATE_ALL_EXCEPT_FINAL };
enum class eXformUpdate : et { UPDATE_CURRENT, UPDATE_SELECTED, UPDATE_CURRENT_AND_SELECTED, UPDATE_SELECTED_EXCEPT_FINAL, UPDATE_ALL, UPDATE_ALL_EXCEPT_FINAL };
/// <summary>
/// FractoriumEmberController and Fractorium need each other, but each can't include the other.
@ -161,6 +161,7 @@ public:
virtual void XformWeightChanged(double d) { }
virtual void EqualizeWeights() { }
virtual void XformNameChanged(int row, int col) { }
virtual void XformAnimateChanged(int state) { }
virtual void FillXforms(int index = 0) { }
//Xforms Affine.
@ -171,6 +172,8 @@ public:
virtual void ScaleXforms(double scale, bool pre) { }
virtual void ResetXformsAffine(bool pre) { }
virtual void FillBothAffines() { }
double LockedScale() { return m_LockedScale; }
virtual void LockAffineScaleCheckBoxStateChanged(int state) { }
//Xforms Color.
virtual void XformColorIndexChanged(double d, bool updateRender) { }
@ -241,6 +244,7 @@ protected:
uint m_SubBatchCount;
uint m_FailedRenders;
uint m_UndoIndex;
double m_LockedScale;
eRendererType m_RenderType;
eEditUndoState m_EditState;
GLuint m_OutputTexID;
@ -397,9 +401,11 @@ public:
virtual void XformWeightChanged(double d) override;
virtual void EqualizeWeights() override;
virtual void XformNameChanged(int row, int col) override;
virtual void XformAnimateChanged(int state) override;
virtual void FillXforms(int index = 0) override;
void FillWithXform(Xform<T>* xform);
Xform<T>* CurrentXform();
void UpdateXform(std::function<void(Xform<T>*)> func, eXformUpdate updateType = eXformUpdate::UPDATE_CURRENT, bool updateRender = true, eProcessAction action = eProcessAction::FULL_RENDER);
//Xforms Affine.
virtual void AffineSetHelper(double d, int index, bool pre) override;
@ -409,7 +415,10 @@ public:
virtual void ScaleXforms(double scale, bool pre) override;
virtual void ResetXformsAffine(bool pre) override;
virtual void FillBothAffines() override;
virtual void LockAffineScaleCheckBoxStateChanged(int state) override;
void FillAffineWithXform(Xform<T>* xform, bool pre);
T AffineScaleCurrentToLocked();
T AffineScaleLockedToCurrent();
//Xforms Color.
virtual void XformColorIndexChanged(double d, bool updateRender) override;
@ -473,7 +482,6 @@ private:
QString MakeXformCaption(size_t i);
bool XformCheckboxAt(int i, std::function<void(QCheckBox*)> func);
bool XformCheckboxAt(Xform<T>* xform, std::function<void(QCheckBox*)> func);
void UpdateXform(std::function<void(Xform<T>*)> func, eXformUpdate updateType = eXformUpdate::UPDATE_CURRENT, bool updateRender = true, eProcessAction action = eProcessAction::FULL_RENDER);
//Palette.
void UpdateAdjustedPaletteGUI(Palette<T>& palette);

View File

@ -176,7 +176,7 @@ void FractoriumEmberController<T>::OpenAndPrepFiles(const QStringList& filenames
StopPreviewRender();
emberFile.m_Filename = filenames[0];
foreach (const QString& filename, filenames)
for (auto& filename : filenames)
{
embers.clear();
@ -694,7 +694,7 @@ void Fractorium::OnActionResetWorkspace(bool checked)
template <typename T>
void FractoriumEmberController<T>::AddReflectiveSymmetry()
{
QComboBox* combo = m_Fractorium->ui.CurrentXformCombo;
auto combo = m_Fractorium->ui.CurrentXformCombo;
Update([&]()
{
m_Ember.AddSymmetry(-1, m_Rand);
@ -712,7 +712,7 @@ void Fractorium::OnActionAddReflectiveSymmetry(bool checked) { m_Controller->Add
template <typename T>
void FractoriumEmberController<T>::AddRotationalSymmetry()
{
QComboBox* combo = m_Fractorium->ui.CurrentXformCombo;
auto combo = m_Fractorium->ui.CurrentXformCombo;
Update([&]()
{
m_Ember.AddSymmetry(2, m_Rand);
@ -730,7 +730,7 @@ void Fractorium::OnActionAddRotationalSymmetry(bool checked) { m_Controller->Add
template <typename T>
void FractoriumEmberController<T>::AddBothSymmetry()
{
QComboBox* combo = m_Fractorium->ui.CurrentXformCombo;
auto combo = m_Fractorium->ui.CurrentXformCombo;
Update([&]()
{
m_Ember.AddSymmetry(-2, m_Rand);
@ -772,7 +772,7 @@ void FractoriumEmberController<T>::ClearFlame()
if (m_Ember.XformCount() == 1)
{
if (Xform<T>* xform = m_Ember.GetXform(0))
if (auto xform = m_Ember.GetXform(0))
{
xform->Clear();
xform->ParentEmber(&m_Ember);

View File

@ -10,38 +10,30 @@
void Fractorium::InitPaletteUI()
{
int spinHeight = 20, row = 0;
QTableWidget* paletteTable = ui.PaletteListTable;
QTableWidget* palettePreviewTable = ui.PalettePreviewTable;
auto paletteTable = ui.PaletteListTable;
auto palettePreviewTable = ui.PalettePreviewTable;
connect(ui.PaletteFilenameCombo, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(OnPaletteFilenameComboChanged(const QString&)), Qt::QueuedConnection);
connect(paletteTable, SIGNAL(cellClicked(int, int)), this, SLOT(OnPaletteCellClicked(int, int)), Qt::QueuedConnection);
connect(paletteTable, SIGNAL(cellDoubleClicked(int, int)), this, SLOT(OnPaletteCellDoubleClicked(int, int)), Qt::QueuedConnection);
//Palette adjustment table.
QTableWidget* table = ui.PaletteAdjustTable;
auto table = ui.PaletteAdjustTable;
table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);//Split width over all columns evenly.
SetupSpinner<SpinBox, int>(table, this, row, 1, m_PaletteHueSpin, spinHeight, -180, 180, 1, SIGNAL(valueChanged(int)), SLOT(OnPaletteAdjust(int)), true, 0, 0, 0);
SetupSpinner<SpinBox, int>(table, this, row, 1, m_PaletteSaturationSpin, spinHeight, -100, 100, 1, SIGNAL(valueChanged(int)), SLOT(OnPaletteAdjust(int)), true, 0, 0, 0);
SetupSpinner<SpinBox, int>(table, this, row, 1, m_PaletteBrightnessSpin, spinHeight, -255, 255, 1, SIGNAL(valueChanged(int)), SLOT(OnPaletteAdjust(int)), true, 0, 0, 0);
row = 0;
SetupSpinner<SpinBox, int>(table, this, row, 3, m_PaletteContrastSpin, spinHeight, -100, 100, 1, SIGNAL(valueChanged(int)), SLOT(OnPaletteAdjust(int)), true, 0, 0, 0);
SetupSpinner<SpinBox, int>(table, this, row, 3, m_PaletteBlurSpin, spinHeight, 0, 127, 1, SIGNAL(valueChanged(int)), SLOT(OnPaletteAdjust(int)), true, 0, 0, 0);
SetupSpinner<SpinBox, int>(table, this, row, 3, m_PaletteFrequencySpin, spinHeight, 1, 10, 1, SIGNAL(valueChanged(int)), SLOT(OnPaletteAdjust(int)), true, 1, 1, 1);
connect(ui.PaletteRandomSelect, SIGNAL(clicked(bool)), this, SLOT(OnPaletteRandomSelectButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PaletteRandomAdjust, SIGNAL(clicked(bool)), this, SLOT(OnPaletteRandomAdjustButtonClicked(bool)), Qt::QueuedConnection);
//Preview table.
palettePreviewTable->setRowCount(1);
palettePreviewTable->setColumnWidth(1, 260);//256 plus small margin on each side.
QTableWidgetItem* previewNameCol = new QTableWidgetItem("");
auto previewNameCol = new QTableWidgetItem("");
palettePreviewTable->setItem(0, 0, previewNameCol);
QTableWidgetItem* previewPaletteItem = new QTableWidgetItem();
auto previewPaletteItem = new QTableWidgetItem();
palettePreviewTable->setItem(0, 1, previewPaletteItem);
connect(ui.PaletteFilterLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(OnPaletteFilterLineEditTextChanged(const QString&)));
connect(ui.PaletteFilterClearButton, SIGNAL(clicked(bool)), this, SLOT(OnPaletteFilterClearButtonClicked(bool)));
paletteTable->setColumnWidth(1, 260);//256 plus small margin on each side.
@ -60,7 +52,6 @@ template <typename T>
int FractoriumEmberController<T>::InitPaletteList(const string& s)
{
QDirIterator it(s.c_str(), QStringList() << "*.xml", QDir::Files, QDirIterator::FollowSymlinks);
m_PaletteList.Clear();
m_Fractorium->ui.PaletteFilenameCombo->clear();
m_Fractorium->ui.PaletteFilenameCombo->setProperty("path", QString::fromStdString(s));
@ -89,9 +80,8 @@ bool FractoriumEmberController<T>::FillPaletteTable(const string& s)
{
if (!s.empty())//This occasionally seems to get called with an empty string for reasons unknown.
{
QTableWidget* paletteTable = m_Fractorium->ui.PaletteListTable;
QTableWidget* palettePreviewTable = m_Fractorium->ui.PalettePreviewTable;
auto paletteTable = m_Fractorium->ui.PaletteListTable;
auto palettePreviewTable = m_Fractorium->ui.PalettePreviewTable;
m_CurrentPaletteFilePath = m_Fractorium->ui.PaletteFilenameCombo->property("path").toString().toStdString() + "/" + s;
if (size_t paletteSize = m_PaletteList.Size(m_CurrentPaletteFilePath))
@ -99,14 +89,11 @@ bool FractoriumEmberController<T>::FillPaletteTable(const string& s)
paletteTable->clear();
paletteTable->blockSignals(true);
paletteTable->setRowCount(paletteSize);
//Headers get removed when clearing, so must re-create here.
QTableWidgetItem* nameHeader = new QTableWidgetItem("Name");
QTableWidgetItem* paletteHeader = new QTableWidgetItem("Palette");
auto nameHeader = new QTableWidgetItem("Name");
auto paletteHeader = new QTableWidgetItem("Palette");
nameHeader->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
paletteHeader->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
paletteTable->setHorizontalHeaderItem(0, nameHeader);
paletteTable->setHorizontalHeaderItem(1, paletteHeader);
@ -117,13 +104,10 @@ bool FractoriumEmberController<T>::FillPaletteTable(const string& s)
{
auto v = p->MakeRgbPaletteBlock(PALETTE_CELL_HEIGHT);
auto nameCol = new QTableWidgetItem(p->m_Name.c_str());
nameCol->setToolTip(p->m_Name.c_str());
paletteTable->setItem(i, 0, nameCol);
QImage image(v.data(), p->Size(), PALETTE_CELL_HEIGHT, QImage::Format_RGB888);
auto paletteItem = new PaletteTableWidgetItem<T>(p);
paletteItem->setData(Qt::DecorationRole, QPixmap::fromImage(image));
paletteTable->setItem(i, 1, paletteItem);
}
@ -135,7 +119,6 @@ bool FractoriumEmberController<T>::FillPaletteTable(const string& s)
else
{
vector<string> errors = m_PaletteList.ErrorReport();
m_Fractorium->ErrorReportToQTextEdit(errors, m_Fractorium->ui.InfoFileOpeningTextEdit);
m_Fractorium->ShowCritical("Palette Read Error", "Could not load palette file, all images will be black. See info tab for details.");
}
@ -163,7 +146,6 @@ void FractoriumEmberController<T>::ApplyPaletteToEmber()
double brightness = double(m_Fractorium->m_PaletteBrightnessSpin->value() / 255.0);
double contrast = double(m_Fractorium->m_PaletteContrastSpin->value() > 0 ? (m_Fractorium->m_PaletteContrastSpin->value() * 2) : m_Fractorium->m_PaletteContrastSpin->value()) / 100.0;
double hue = double(m_Fractorium->m_PaletteHueSpin->value()) / 360.0;
//Use the temp palette as the base and apply the adjustments gotten from the GUI and save the result in the ember palette.
m_TempPalette.MakeAdjustedPalette(m_Ember.m_Palette, 0, hue, sat, brightness, contrast, blur, freq);
}
@ -177,23 +159,21 @@ void FractoriumEmberController<T>::ApplyPaletteToEmber()
template <typename T>
void FractoriumEmberController<T>::UpdateAdjustedPaletteGUI(Palette<T>& palette)
{
Xform<T>* xform = CurrentXform();
QTableWidget* palettePreviewTable = m_Fractorium->ui.PalettePreviewTable;
QTableWidgetItem* previewPaletteItem = palettePreviewTable->item(0, 1);
QString paletteName = QString::fromStdString(m_Ember.m_Palette.m_Name);
auto xform = CurrentXform();
auto palettePreviewTable = m_Fractorium->ui.PalettePreviewTable;
auto previewPaletteItem = palettePreviewTable->item(0, 1);
auto paletteName = QString::fromStdString(m_Ember.m_Palette.m_Name);
if (previewPaletteItem)//This can be null if the palette file was moved or corrupted.
{
//Use the adjusted palette to fill the preview palette control so the user can see the effects of applying the adjustements.
vector<byte> v = palette.MakeRgbPaletteBlock(PALETTE_CELL_HEIGHT);//Make the palette repeat for PALETTE_CELL_HEIGHT rows.
m_FinalPaletteImage = QImage(palette.Size(), PALETTE_CELL_HEIGHT, QImage::Format_RGB888);//Create a QImage out of it.
memcpy(m_FinalPaletteImage.scanLine(0), v.data(), v.size() * sizeof(v[0]));//Memcpy the data in.
QPixmap pixmap = QPixmap::fromImage(m_FinalPaletteImage);//Create a QPixmap out of the QImage.
auto pixmap = QPixmap::fromImage(m_FinalPaletteImage);//Create a QPixmap out of the QImage.
previewPaletteItem->setData(Qt::DecorationRole, pixmap.scaled(QSize(pixmap.width(), palettePreviewTable->rowHeight(0) + 2), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));//Set the pixmap on the palette tab.
m_Fractorium->SetPaletteTableItem(&pixmap, m_Fractorium->ui.XformPaletteRefTable, m_Fractorium->m_PaletteRefItem, 0, 0);//Set the palette ref table on the xforms | color tab.
QTableWidgetItem* previewNameItem = palettePreviewTable->item(0, 0);
auto previewNameItem = palettePreviewTable->item(0, 0);
previewNameItem->setText(paletteName);//Finally, set the name of the palette to be both the text and the tooltip.
previewNameItem->setToolTip(paletteName);
}
@ -308,7 +288,6 @@ void Fractorium::OnPaletteRandomSelectButtonClicked(bool checked)
void Fractorium::OnPaletteRandomAdjustButtonClicked(bool checked)
{
QTIsaac<ISAAC_SIZE, ISAAC_INT>* gRand = QTIsaac<ISAAC_SIZE, ISAAC_INT>::GlobalRand.get();
m_PaletteHueSpin->setValue(-180 + gRand->Rand(361));
m_PaletteSaturationSpin->setValue(-50 + gRand->Rand(101));//Full range of these leads to bad palettes, so clamp range.
m_PaletteBrightnessSpin->setValue(-50 + gRand->Rand(101));
@ -338,7 +317,6 @@ void Fractorium::OnPaletteRandomAdjustButtonClicked(bool checked)
void Fractorium::OnPaletteFilterLineEditTextChanged(const QString& text)
{
auto table = ui.PaletteListTable;
table->setUpdatesEnabled(false);
for (int i = 0; i < table->rowCount(); i++)
@ -407,5 +385,5 @@ void Fractorium::SetPaletteFileComboIndex(const string& filename)
template class FractoriumEmberController<float>;
#ifdef DO_DOUBLE
template class FractoriumEmberController<double>;
template class FractoriumEmberController<double>;
#endif

View File

@ -167,12 +167,12 @@ void FractoriumEmberController<T>::BackgroundChanged(const QColor& color)
Update([&]
{
int itemRow = 5;
QTableWidget* colorTable = m_Fractorium->ui.ColorTable;
auto colorTable = m_Fractorium->ui.ColorTable;
colorTable->item(itemRow, 1)->setBackgroundColor(color);
QString r = ToString(color.red());
QString g = ToString(color.green());
QString b = ToString(color.blue());
auto r = ToString(color.red());
auto g = ToString(color.green());
auto b = ToString(color.blue());
colorTable->item(itemRow, 1)->setTextColor(VisibleColor(color));
colorTable->item(itemRow, 1)->setText("rgb(" + r + ", " + g + ", " + b + ")");

View File

@ -56,7 +56,7 @@ void FractoriumEmberController<T>::FillXaos()
template <typename T>
QString FractoriumEmberController<T>::MakeXaosNameString(uint i)
{
Xform<T>* xform = m_Ember.GetXform(i);
auto xform = m_Ember.GetXform(i);
QString name;
//if (xform)
//{

View File

@ -7,7 +7,6 @@
void Fractorium::InitXformsUI()
{
int spinHeight = 20, row = 0;
connect(ui.AddXformButton, SIGNAL(clicked(bool)), this, SLOT(OnAddXformButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.AddLinkedXformButton, SIGNAL(clicked(bool)), this, SLOT(OnAddLinkedXformButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.DuplicateXformButton, SIGNAL(clicked(bool)), this, SLOT(OnDuplicateXformButtonClicked(bool)), Qt::QueuedConnection);
@ -15,7 +14,7 @@ void Fractorium::InitXformsUI()
connect(ui.DeleteXformButton, SIGNAL(clicked(bool)), this, SLOT(OnDeleteXformButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.AddFinalXformButton, SIGNAL(clicked(bool)), this, SLOT(OnAddFinalXformButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.CurrentXformCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(OnCurrentXformComboChanged(int)), Qt::QueuedConnection);
connect(ui.AnimateXformCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnXformAnimateCheckBoxStateChanged(int)), Qt::QueuedConnection);
SetFixedTableHeader(ui.XformWeightNameTable->horizontalHeader());
//Use SetupSpinner() just to create the spinner, but use col of -1 to prevent it from being added to the table.
SetupSpinner<DoubleSpinBox, double>(ui.XformWeightNameTable, this, row, -1, m_XformWeightSpin, spinHeight, 0, 1000, 0.05, SIGNAL(valueChanged(double)), SLOT(OnXformWeightChanged(double)), false, 0, 1, 0);
@ -25,11 +24,9 @@ void Fractorium::InitXformsUI()
m_XformWeightSpinnerButtonWidget->m_Button->setToolTip("Equalize weights");
m_XformWeightSpinnerButtonWidget->m_Button->setStyleSheet("text-align: center center");
connect(m_XformWeightSpinnerButtonWidget->m_Button, SIGNAL(clicked(bool)), this, SLOT(OnEqualWeightButtonClicked(bool)), Qt::QueuedConnection);
ui.XformWeightNameTable->setCellWidget(0, 0, m_XformWeightSpinnerButtonWidget);
ui.XformWeightNameTable->setItem(0, 1, new QTableWidgetItem());
connect(ui.XformWeightNameTable, SIGNAL(cellChanged(int, int)), this, SLOT(OnXformNameChanged(int, int)), Qt::QueuedConnection);
ui.CurrentXformCombo->setProperty("soloxform", -1);
#ifndef WIN32
//For some reason linux makes these 24x24, even though the designer explicitly says 16x16.
@ -70,19 +67,15 @@ void Fractorium::CurrentXform(uint i)
template <typename T>
void FractoriumEmberController<T>::CurrentXformComboChanged(int index)
{
if (Xform<T>* xform = m_Ember.GetTotalXform(index))
if (auto xform = m_Ember.GetTotalXform(index))
{
FillWithXform(xform);
m_GLController->SetSelectedXform(xform);
int solo = m_Fractorium->ui.CurrentXformCombo->property("soloxform").toInt();
m_Fractorium->ui.SoloXformCheckBox->blockSignals(true);
m_Fractorium->ui.SoloXformCheckBox->setChecked(solo == index);
m_Fractorium->ui.SoloXformCheckBox->blockSignals(false);
bool enable = !IsFinal(CurrentXform());
m_Fractorium->ui.DuplicateXformButton->setEnabled(enable);
m_Fractorium->m_XformWeightSpin->setEnabled(enable);
m_Fractorium->ui.SoloXformCheckBox->setEnabled(enable);
@ -105,7 +98,6 @@ void FractoriumEmberController<T>::AddXform()
Update([&]()
{
Xform<T> newXform;
newXform.m_Weight = 0.25;
newXform.m_ColorX = m_Rand.Frand01<T>();
m_Ember.AddXform(newXform);
@ -131,7 +123,6 @@ void FractoriumEmberController<T>::AddLinkedXform()
{
size_t i, count = m_Ember.XformCount();
Xform<T> newXform;
newXform.m_Weight = 0.5;
newXform.m_Opacity = 1;
newXform.m_ColorSpeed = 1;
@ -175,17 +166,14 @@ template <typename T>
void FractoriumEmberController<T>::DuplicateXform()
{
vector<Xform<T>> vec;
vec.reserve(m_Ember.XformCount());
UpdateXform([&] (Xform<T>* xform)
{
vec.push_back(*xform);
}, eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL, false);
Update([&]()
{
QComboBox* combo = m_Fractorium->ui.CurrentXformCombo;
auto combo = m_Fractorium->ui.CurrentXformCombo;
for (auto& it : vec)
m_Ember.AddXform(it);
@ -210,7 +198,6 @@ void FractoriumEmberController<T>::ClearXform()
{
xform->ClearAndDeleteVariations();//Note xaos is left alone.
}, eXformUpdate::UPDATE_SELECTED);
FillVariationTreeWithXform(CurrentXform());
}
@ -229,13 +216,12 @@ void FractoriumEmberController<T>::DeleteXforms()
int i = 0, offset = 0, current = 0, checked = 0;
bool haveFinal = false;
size_t count;
QComboBox* combo = m_Fractorium->ui.CurrentXformCombo;
auto combo = m_Fractorium->ui.CurrentXformCombo;
//Iterating over the checkboxes must be done instead of using UpdateXform() to iterate over xforms
//because xforms are being deleted inside the loop.
//Also manually calling UpdateRender() rather than using the usual Update() call because
//it should only be called if an xform has actually been deleted.
m_Fractorium->ForEachXformCheckbox([&](int i, QCheckBox* w)
m_Fractorium->ForEachXformCheckbox([&](int i, QCheckBox * w)
{
count = m_Ember.TotalXformCount();
haveFinal = m_Ember.UseFinalXform();//Requery every time.
@ -252,20 +238,18 @@ void FractoriumEmberController<T>::DeleteXforms()
if (w->isChecked())
{
//qDebug() << "Deleting " << w->text();
m_Ember.DeleteTotalXform(i - offset);//Subtract offset to account for previously deleted xforms.
offset++;
}
});
current = combo->currentIndex();
count = m_Ember.TotalXformCount();
haveFinal = m_Ember.UseFinalXform();//Requery again.
//Nothing was selected, so just delete current.
if (!checked &&
!(haveFinal && count <= 2 && current == 0) &&//Again disallow deleting the only remaining non-final xform.
!(!haveFinal && count == 1))
!(haveFinal && count <= 2 && current == 0) &&//Again disallow deleting the only remaining non-final xform.
!(!haveFinal && count == 1))
{
m_Ember.DeleteTotalXform(current);
offset++;
@ -298,7 +282,6 @@ void FractoriumEmberController<T>::AddFinalXform()
{
Xform<T> final;
auto combo = m_Fractorium->ui.CurrentXformCombo;
final.AddVariation(new LinearVariation<T>());//Just a placeholder so other parts of the code don't see it as being empty.
m_Ember.SetFinalXform(final);
int index = m_Ember.TotalXformCount() - 1;//Set index to the last item.
@ -322,7 +305,6 @@ void FractoriumEmberController<T>::XformWeightChanged(double d)
{
xform->m_Weight = d;
}, eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL);
SetNormalizedWeightText(CurrentXform());
}
@ -356,19 +338,32 @@ void FractoriumEmberController<T>::XformNameChanged(int row, int col)
UpdateXform([&] (Xform<T>* xform)
{
int index = m_Ember.GetTotalXformIndex(xform);
xform->m_Name = m_Fractorium->ui.XformWeightNameTable->item(row, col)->text().toStdString();
XformCheckboxAt(index, [&](QCheckBox* checkbox) { checkbox->setText(MakeXformCaption(index)); });
//if (index != -1)
//{
// if (QTableWidgetItem* xformNameItem = m_Fractorium->ui.XaosTable->item(index, 0))
// xformNameItem->setText(MakeXaosNameString(index));
//}
XformCheckboxAt(index, [&](QCheckBox * checkbox) { checkbox->setText(MakeXformCaption(index)); });
}, eXformUpdate::UPDATE_CURRENT, false);
}
void Fractorium::OnXformNameChanged(int row, int col) { m_Controller->XformNameChanged(row, col); }
/// <summary>
/// Set the animate field of the selected xforms.
/// This has no effect on interactive rendering, it only sets a value
/// that will later be saved to Xml when the user saves.
/// This value is observed when creating sequences for animation.
/// Called when the user toggles the animate xform checkbox.
/// </summary>
/// <param name="state">1 for checked, else false</param>
template <typename T>
void FractoriumEmberController<T>::XformAnimateChanged(int state)
{
UpdateXform([&](Xform<T>* xform)
{
xform->m_Animate = state > 0 ? 1 : 0;
}, eXformUpdate::UPDATE_SELECTED, false);
}
void Fractorium::OnXformAnimateCheckBoxStateChanged(int state) { m_Controller->XformAnimateChanged(state); }
/// <summary>
/// Fill all GUI widgets with values from the passed in xform.
/// </summary>
@ -378,8 +373,9 @@ void FractoriumEmberController<T>::FillWithXform(Xform<T>* xform)//Need to see w
{
m_Fractorium->m_XformWeightSpin->SetValueStealth(xform->m_Weight);
SetNormalizedWeightText(xform);
m_Fractorium->ui.AnimateXformCheckBox->setChecked(xform->m_Animate > 0 ? true : false);//Make a signal/slot to handle checking this.
if (QTableWidgetItem* item = m_Fractorium->ui.XformWeightNameTable->item(0, 1))
if (auto item = m_Fractorium->ui.XformWeightNameTable->item(0, 1))
{
m_Fractorium->ui.XformWeightNameTable->blockSignals(true);
item->setText(QString::fromStdString(xform->m_Name));
@ -402,7 +398,6 @@ void FractoriumEmberController<T>::SetNormalizedWeightText(Xform<T>* xform)
if (xform)
{
int index = m_Ember.GetXformIndex(xform);
m_Ember.CalcNormalizedWeights(m_NormalizedWeights);
if (index != -1 && index < m_NormalizedWeights.size())
@ -433,10 +428,8 @@ void FractoriumEmberController<T>::FillXforms(int index)
{
int i = 0, count = int(XformCount());
auto combo = m_Fractorium->ui.CurrentXformCombo;
combo->blockSignals(true);
combo->clear();
//First clear all dynamically created checkboxes.
m_Fractorium->ClearXformsSelections();
m_Fractorium->m_XformsSelectionLayout->blockSignals(true);
@ -449,13 +442,13 @@ void FractoriumEmberController<T>::FillXforms(int index)
}
i = 0;
while (i < count)
{
if (i < count - 1)
{
auto cb1 = new QCheckBox(MakeXformCaption(i), m_Fractorium);
auto cb2 = new QCheckBox(MakeXformCaption(i + 1), m_Fractorium);
m_Fractorium->m_XformSelections.push_back(cb1);
m_Fractorium->m_XformSelections.push_back(cb2);
m_Fractorium->m_XformsSelectionLayout->addRow(cb1, cb2);
@ -464,7 +457,6 @@ void FractoriumEmberController<T>::FillXforms(int index)
else if (i < count)
{
auto cb = new QCheckBox(MakeXformCaption(i), m_Fractorium);
m_Fractorium->m_XformSelections.push_back(cb);
m_Fractorium->m_XformsSelectionLayout->addRow(cb, new QWidget(m_Fractorium));
i++;
@ -475,10 +467,8 @@ void FractoriumEmberController<T>::FillXforms(int index)
if (UseFinalXform())
{
auto cb = new QCheckBox(MakeXformCaption(i), m_Fractorium);
m_Fractorium->m_XformSelections.push_back(cb);
m_Fractorium->m_XformsSelectionLayout->addRow(cb, new QWidget(m_Fractorium));
combo->addItem("Final");
combo->setItemIcon(i, m_Fractorium->m_FinalXformComboIcon);
}
@ -497,5 +487,5 @@ void FractoriumEmberController<T>::FillXforms(int index)
template class FractoriumEmberController<float>;
#ifdef DO_DOUBLE
template class FractoriumEmberController<double>;
template class FractoriumEmberController<double>;
#endif

View File

@ -8,8 +8,8 @@ void Fractorium::InitXformsAffineUI()
{
int row = 0, affinePrec = 6, spinHeight = 20;
double affineStep = 0.01, affineMin = std::numeric_limits<double>::lowest(), affineMax = std::numeric_limits<double>::max();
QTableWidget* table = ui.PreAffineTable;
auto table = ui.PreAffineTable;
connect(ui.LockAffineCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnLockAffineScaleCheckBoxStateChanged(int)), Qt::QueuedConnection);
table->verticalHeader()->setVisible(true);//The designer continually clobbers these values, so must manually set them here.
table->horizontalHeader()->setVisible(true);
table->verticalHeader()->setSectionsClickable(true);
@ -18,7 +18,6 @@ void Fractorium::InitXformsAffineUI()
table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
connect(table->verticalHeader(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(OnPreAffineRowDoubleClicked(int)), Qt::QueuedConnection);
connect(table->horizontalHeader(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(OnPreAffineColDoubleClicked(int)), Qt::QueuedConnection);
//Pre affine spinners.
SetupAffineSpinner(table, this, 0, 0, m_PreX1Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnX1Changed(double)));
SetupAffineSpinner(table, this, 0, 1, m_PreX2Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnX2Changed(double)));
@ -26,7 +25,6 @@ void Fractorium::InitXformsAffineUI()
SetupAffineSpinner(table, this, 1, 1, m_PreY2Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnY2Changed(double)));
SetupAffineSpinner(table, this, 2, 0, m_PreO1Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnO1Changed(double)));
SetupAffineSpinner(table, this, 2, 1, m_PreO2Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnO2Changed(double)));
table = ui.PostAffineTable;
table->verticalHeader()->setVisible(true);//The designer continually clobbers these values, so must manually set them here.
table->horizontalHeader()->setVisible(true);
@ -34,10 +32,8 @@ void Fractorium::InitXformsAffineUI()
table->horizontalHeader()->setSectionsClickable(true);
table->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
connect(table->verticalHeader(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(OnPostAffineRowDoubleClicked(int)), Qt::QueuedConnection);
connect(table->horizontalHeader(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(OnPostAffineColDoubleClicked(int)), Qt::QueuedConnection);
//Post affine spinners.
SetupAffineSpinner(table, this, 0, 0, m_PostX1Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnX1Changed(double)));
SetupAffineSpinner(table, this, 0, 1, m_PostX2Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnX2Changed(double)));
@ -45,73 +41,60 @@ void Fractorium::InitXformsAffineUI()
SetupAffineSpinner(table, this, 1, 1, m_PostY2Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnY2Changed(double)));
SetupAffineSpinner(table, this, 2, 0, m_PostO1Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnO1Changed(double)));
SetupAffineSpinner(table, this, 2, 1, m_PostO2Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnO2Changed(double)));
QDoubleValidator* preRotateVal = new QDoubleValidator(ui.PreRotateCombo); preRotateVal->setLocale(QLocale::system());
QDoubleValidator* preMoveVal = new QDoubleValidator(ui.PreMoveCombo); preMoveVal->setLocale(QLocale::system());
QDoubleValidator* preScaleVal = new QDoubleValidator(ui.PreScaleCombo); preScaleVal->setLocale(QLocale::system());
QDoubleValidator* postRotateVal = new QDoubleValidator(ui.PostRotateCombo); postRotateVal->setLocale(QLocale::system());
QDoubleValidator* postMoveVal = new QDoubleValidator(ui.PostMoveCombo); postMoveVal->setLocale(QLocale::system());
QDoubleValidator* postScaleVal = new QDoubleValidator(ui.PostScaleCombo); postScaleVal->setLocale(QLocale::system());
auto preRotateVal = new QDoubleValidator(ui.PreRotateCombo); preRotateVal->setLocale(QLocale::system());
auto preMoveVal = new QDoubleValidator(ui.PreMoveCombo); preMoveVal->setLocale(QLocale::system());
auto preScaleVal = new QDoubleValidator(ui.PreScaleCombo); preScaleVal->setLocale(QLocale::system());
auto postRotateVal = new QDoubleValidator(ui.PostRotateCombo); postRotateVal->setLocale(QLocale::system());
auto postMoveVal = new QDoubleValidator(ui.PostMoveCombo); postMoveVal->setLocale(QLocale::system());
auto postScaleVal = new QDoubleValidator(ui.PostScaleCombo); postScaleVal->setLocale(QLocale::system());
ui.PreRotateCombo->setValidator(preRotateVal);
ui.PreMoveCombo->setValidator(preMoveVal);
ui.PreScaleCombo->setValidator(preScaleVal);
ui.PostRotateCombo->setValidator(postRotateVal);
ui.PostMoveCombo->setValidator(postMoveVal);
ui.PostScaleCombo->setValidator(postScaleVal);
QStringList moveList;
moveList.append(ToString(0.5));
moveList.append(ToString(0.25));
moveList.append(ToString(0.1));
moveList.append(ToString(0.05));
moveList.append(ToString(0.025));
moveList.append(ToString(0.01));
ui.PreMoveCombo->addItems(moveList);
ui.PostMoveCombo->addItems(moveList);
connect(ui.PreFlipHorizontalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipHorizontalButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreFlipVerticalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipVerticalButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreRotate90CButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreRotate90CcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CcButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreRotateCButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreRotateCcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCcButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreMoveUpButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveUpButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreMoveDownButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveDownButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreMoveLeftButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveLeftButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreMoveRightButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveRightButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreScaleDownButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleDownButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreScaleUpButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleUpButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreResetButton, SIGNAL(clicked(bool)), this, SLOT(OnResetAffineButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostFlipHorizontalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipHorizontalButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostFlipVerticalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipVerticalButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostRotate90CcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CcButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostRotateCcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCcButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostRotateCButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostRotate90CButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostMoveUpButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveUpButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostMoveDownButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveDownButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostMoveLeftButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveLeftButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostMoveRightButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveRightButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostScaleDownButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleDownButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostScaleUpButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleUpButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostResetButton, SIGNAL(clicked(bool)), this, SLOT(OnResetAffineButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreAffineGroupBox, SIGNAL(toggled(bool)), this, SLOT(OnAffineGroupBoxToggled(bool)), Qt::QueuedConnection);
connect(ui.PostAffineGroupBox, SIGNAL(toggled(bool)), this, SLOT(OnAffineGroupBoxToggled(bool)), Qt::QueuedConnection);
connect(ui.ShowPreAffineAllRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection);
connect(ui.ShowPreAffineCurrentRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection);
connect(ui.ShowPostAffineAllRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection);
connect(ui.ShowPostAffineCurrentRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection);
connect(ui.PolarAffineCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnPolarAffineCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.PreFlipHorizontalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipHorizontalButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreFlipVerticalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipVerticalButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreRotate90CButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreRotate90CcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CcButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreRotateCButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreRotateCcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCcButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreMoveUpButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveUpButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreMoveDownButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveDownButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreMoveLeftButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveLeftButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreMoveRightButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveRightButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreScaleDownButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleDownButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreScaleUpButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleUpButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreResetButton, SIGNAL(clicked(bool)), this, SLOT(OnResetAffineButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostFlipHorizontalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipHorizontalButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostFlipVerticalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipVerticalButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostRotate90CcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CcButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostRotateCcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCcButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostRotateCButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostRotate90CButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostMoveUpButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveUpButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostMoveDownButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveDownButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostMoveLeftButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveLeftButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostMoveRightButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveRightButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostScaleDownButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleDownButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostScaleUpButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleUpButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PostResetButton, SIGNAL(clicked(bool)), this, SLOT(OnResetAffineButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.PreAffineGroupBox, SIGNAL(toggled(bool)), this, SLOT(OnAffineGroupBoxToggled(bool)), Qt::QueuedConnection);
connect(ui.PostAffineGroupBox, SIGNAL(toggled(bool)), this, SLOT(OnAffineGroupBoxToggled(bool)), Qt::QueuedConnection);
connect(ui.ShowPreAffineAllRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection);
connect(ui.ShowPreAffineCurrentRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection);
connect(ui.ShowPostAffineAllRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection);
connect(ui.ShowPostAffineCurrentRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection);
connect(ui.PolarAffineCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnPolarAffineCheckBoxStateChanged(int)), Qt::QueuedConnection);
#ifndef WIN32
//For some reason linux makes these 24x24, even though the designer explicitly says 16x16.
//Also, in order to get 4 pixels of spacing between elements in the grid layout, 0 must be specified.
@ -130,7 +113,6 @@ void Fractorium::InitXformsAffineUI()
ui.PreResetButton->setIconSize(QSize(16, 16));
ui.PreAffineGridLayout->setHorizontalSpacing(0);
ui.PreAffineGridLayout->setVerticalSpacing(0);
ui.PostFlipHorizontalButton->setIconSize(QSize(16, 16));
ui.PostFlipVerticalButton->setIconSize(QSize(16, 16));
ui.PostRotate90CButton->setIconSize(QSize(16, 16));
@ -146,13 +128,11 @@ void Fractorium::InitXformsAffineUI()
ui.PostResetButton->setIconSize(QSize(16, 16));
ui.PostAffineGridLayout->setHorizontalSpacing(0);
ui.PostAffineGridLayout->setVerticalSpacing(0);
//Further, the size of the dock widget won't be properly adjusted until the xforms tab is shown.
//So show it here and it will be switched back in Fractorium's constructor.
//ui.ParamsTabWidget->setCurrentIndex(2);
//ui.DockWidget->update();
#endif
//Placing pointers to the spin boxes in arrays makes them easier to access in various places.
m_PreSpins[0] = m_PreX1Spin;//A
m_PreSpins[1] = m_PreY1Spin;//B
@ -160,20 +140,58 @@ void Fractorium::InitXformsAffineUI()
m_PreSpins[3] = m_PreX2Spin;//D
m_PreSpins[4] = m_PreY2Spin;//E
m_PreSpins[5] = m_PreO2Spin;//F
m_PostSpins[0] = m_PostX1Spin;
m_PostSpins[1] = m_PostY1Spin;
m_PostSpins[2] = m_PostO1Spin;
m_PostSpins[3] = m_PostX2Spin;
m_PostSpins[4] = m_PostY2Spin;
m_PostSpins[5] = m_PostO2Spin;
ui.PreAffineGroupBox->setChecked(false);//Flip both once to force enabling/disabling the disabling of the group boxes and the corner buttons.
ui.PreAffineGroupBox->setChecked(true);//Pre affine enabled.
ui.PostAffineGroupBox->setChecked(true);
ui.PostAffineGroupBox->setChecked(false);//Post affine disabled.
}
/// <summary>
/// Toggle whether to lock the visual scale of the affine spinners.
/// Called when the user checks LockAffineCheckBox.
/// </summary>
/// <param name="state">True if checked, else false.</param>
template <typename T>
void FractoriumEmberController<T>::LockAffineScaleCheckBoxStateChanged(int state)
{
m_LockedScale = m_Ember.m_PixelsPerUnit;
m_Fractorium->ui.GLDisplay->update();
}
void Fractorium::OnLockAffineScaleCheckBoxStateChanged(int state) { m_Controller->LockAffineScaleCheckBoxStateChanged(state); }
/// <summary>
/// Return the value needed to multiply the current scale by to get back to the locked scale.
/// </summary>
/// <returns>The scale value</returns>
template <typename T>
T FractoriumEmberController<T>::AffineScaleCurrentToLocked()
{
if (m_Fractorium->ui.LockAffineCheckBox->isChecked())
return LockedScale() / m_Ember.m_PixelsPerUnit;
else
return 1;
}
/// <summary>
/// Return the value needed to multiply the locked scale by to get to the current scale.
/// </summary>
/// <returns>The scale value</returns>
template <typename T>
T FractoriumEmberController<T>::AffineScaleLockedToCurrent()
{
if (m_Fractorium->ui.LockAffineCheckBox->isChecked())
return m_Ember.m_PixelsPerUnit / LockedScale();
else
return 1;
}
/// <summary>
/// Toggle all pre affine values in one row for the selected xforms.
/// Resets the rendering process.
@ -226,7 +244,7 @@ void FractoriumEmberController<T>::AffineSetHelper(double d, int index, bool pre
{
UpdateXform([&] (Xform<T>* xform)
{
Affine2D<T>* affine = pre ? &xform->m_Affine : &xform->m_Post;
auto affine = pre ? &xform->m_Affine : &xform->m_Post;
DoubleSpinBox** spinners = pre ? m_Fractorium->m_PreSpins : m_Fractorium->m_PostSpins;
if (m_Fractorium->ui.PolarAffineCheckBox->isChecked())
@ -238,11 +256,13 @@ void FractoriumEmberController<T>::AffineSetHelper(double d, int index, bool pre
affine->A(cos(spinners[0]->value() * DEG_2_RAD) * spinners[3]->value());
affine->D(sin(spinners[0]->value() * DEG_2_RAD) * spinners[3]->value());
break;
case 1:
case 4:
affine->B(cos(spinners[1]->value() * DEG_2_RAD) * spinners[4]->value());
affine->E(sin(spinners[1]->value() * DEG_2_RAD) * spinners[4]->value());
break;
case 2:
case 5:
default:
@ -258,18 +278,23 @@ void FractoriumEmberController<T>::AffineSetHelper(double d, int index, bool pre
case 0:
affine->A(d);
break;
case 1:
affine->B(d);
break;
case 2:
affine->C(d);
break;
case 3:
affine->D(d);
break;
case 4:
affine->E(d);
break;
case 5:
affine->F(d);
break;
@ -301,7 +326,7 @@ void FractoriumEmberController<T>::FlipXforms(bool horizontal, bool vertical, bo
{
UpdateXform([&] (Xform<T>* xform)
{
Affine2D<T>* affine = pre ? &xform->m_Affine : &xform->m_Post;
auto affine = pre ? &xform->m_Affine : &xform->m_Post;
if (horizontal)
{
@ -320,9 +345,7 @@ void FractoriumEmberController<T>::FlipXforms(bool horizontal, bool vertical, bo
if (!m_Fractorium->LocalPivot())
affine->F(-affine->F());
}
}, eXformUpdate::UPDATE_SELECTED);
FillAffineWithXform(CurrentXform(), pre);
}
@ -340,11 +363,9 @@ void FractoriumEmberController<T>::RotateXformsByAngle(double angle, bool pre)
{
UpdateXform([&] (Xform<T>* xform)
{
Affine2D<T>* affine = pre ? &xform->m_Affine : &xform->m_Post;
auto affine = pre ? &xform->m_Affine : &xform->m_Post;
affine->Rotate(angle);
}, eXformUpdate::UPDATE_SELECTED);
FillAffineWithXform(CurrentXform(), pre);
}
@ -367,7 +388,7 @@ void Fractorium::OnRotateCButtonClicked(bool checked)
{
bool ok;
bool pre = sender() == ui.PreRotateCButton;
QComboBox* combo = pre ? ui.PreRotateCombo : ui.PostRotateCombo;
auto combo = pre ? ui.PreRotateCombo : ui.PostRotateCombo;
double d = ToDouble(combo->currentText(), &ok);
if (ok)
@ -384,7 +405,7 @@ void Fractorium::OnRotateCcButtonClicked(bool checked)
{
bool ok;
bool pre = sender() == ui.PreRotateCcButton;
QComboBox* combo = pre ? ui.PreRotateCombo : ui.PostRotateCombo;
auto combo = pre ? ui.PreRotateCombo : ui.PostRotateCombo;
double d = ToDouble(combo->currentText(), &ok);
if (ok)
@ -403,12 +424,10 @@ void FractoriumEmberController<T>::MoveXforms(double x, double y, bool pre)
{
UpdateXform([&] (Xform<T>* xform)
{
Affine2D<T>* affine = pre ? &xform->m_Affine : &xform->m_Post;
auto* affine = pre ? &xform->m_Affine : &xform->m_Post;
affine->C(affine->C() + x);
affine->F(affine->F() + y);
}, eXformUpdate::UPDATE_SELECTED);
FillAffineWithXform(CurrentXform(), pre);
}
@ -422,7 +441,7 @@ void Fractorium::OnMoveUpButtonClicked(bool checked)
{
bool ok;
bool pre = sender() == ui.PreMoveUpButton;
QComboBox* combo = pre ? ui.PreMoveCombo : ui.PostMoveCombo;
auto combo = pre ? ui.PreMoveCombo : ui.PostMoveCombo;
double d = ToDouble(combo->currentText(), &ok);
if (ok)
@ -439,7 +458,7 @@ void Fractorium::OnMoveDownButtonClicked(bool checked)
{
bool ok;
bool pre = sender() == ui.PreMoveDownButton;
QComboBox* combo = pre ? ui.PreMoveCombo : ui.PostMoveCombo;
auto combo = pre ? ui.PreMoveCombo : ui.PostMoveCombo;
double d = ToDouble(combo->currentText(), &ok);
if (ok)
@ -456,7 +475,7 @@ void Fractorium::OnMoveLeftButtonClicked(bool checked)
{
bool ok;
bool pre = sender() == ui.PreMoveLeftButton;
QComboBox* combo = pre ? ui.PreMoveCombo : ui.PostMoveCombo;
auto combo = pre ? ui.PreMoveCombo : ui.PostMoveCombo;
double d = ToDouble(combo->currentText(), &ok);
if (ok)
@ -473,7 +492,7 @@ void Fractorium::OnMoveRightButtonClicked(bool checked)
{
bool ok;
bool pre = sender() == ui.PreMoveRightButton;
QComboBox* combo = pre ? ui.PreMoveCombo : ui.PostMoveCombo;
auto combo = pre ? ui.PreMoveCombo : ui.PostMoveCombo;
double d = ToDouble(combo->currentText(), &ok);
if (ok)
@ -491,14 +510,12 @@ void FractoriumEmberController<T>::ScaleXforms(double scale, bool pre)
{
UpdateXform([&] (Xform<T>* xform)
{
Affine2D<T>* affine = pre ? &xform->m_Affine : &xform->m_Post;
auto affine = pre ? &xform->m_Affine : &xform->m_Post;
affine->A(affine->A() * scale);
affine->B(affine->B() * scale);
affine->D(affine->D() * scale);
affine->E(affine->E() * scale);
}, eXformUpdate::UPDATE_SELECTED);
FillAffineWithXform(CurrentXform(), pre);
}
@ -512,7 +529,7 @@ void Fractorium::OnScaleDownButtonClicked(bool checked)
{
bool ok;
bool pre = sender() == ui.PreScaleDownButton;
QComboBox* combo = pre ? ui.PreScaleCombo : ui.PostScaleCombo;
auto combo = pre ? ui.PreScaleCombo : ui.PostScaleCombo;
double d = ToDouble(combo->currentText(), &ok);
if (ok)
@ -529,7 +546,7 @@ void Fractorium::OnScaleUpButtonClicked(bool checked)
{
bool ok;
bool pre = sender() == ui.PreScaleUpButton;
QComboBox* combo = pre ? ui.PreScaleCombo : ui.PostScaleCombo;
auto combo = pre ? ui.PreScaleCombo : ui.PostScaleCombo;
double d = ToDouble(combo->currentText(), &ok);
if (ok)
@ -546,11 +563,9 @@ void FractoriumEmberController<T>::ResetXformsAffine(bool pre)
{
UpdateXform([&] (Xform<T>* xform)
{
Affine2D<T>* affine = pre ? &xform->m_Affine : &xform->m_Post;
auto affine = pre ? &xform->m_Affine : &xform->m_Post;
affine->MakeID();
}, eXformUpdate::UPDATE_SELECTED);
FillAffineWithXform(CurrentXform(), pre);
}
@ -699,5 +714,5 @@ bool Fractorium::LocalPivot() { return ui.LocalPivotRadio->isChecked();
template class FractoriumEmberController<float>;
#ifdef DO_DOUBLE
template class FractoriumEmberController<double>;
template class FractoriumEmberController<double>;
#endif

View File

@ -142,6 +142,7 @@ private:
m4T m_Projection;
Affine2D<T> m_DragSrcTransform;
vector<Affine2D<T>> m_DragSrcTransforms;
Xform<T>* m_HoverXform;
Xform<T>* m_SelectedXform;

View File

@ -2,8 +2,6 @@
#include "GLWidget.h"
#include "Fractorium.h"
//#define OLDDRAG 1
/// <summary>
/// Constructor which passes parent widget to the base and initializes OpenGL profile.
/// This will need to change in the future to implement all drawing as shader programs.
@ -142,7 +140,7 @@ void GLEmberControllerBase::ClearControl() { m_DragModifier &= ~et(eDragModifier
template <typename T>
void GLEmberController<T>::ClearWindow()
{
Ember<T>* ember = m_FractoriumEmberController->CurrentEmber();
auto ember = m_FractoriumEmberController->CurrentEmber();
m_GL->makeCurrent();
m_GL->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
m_GL->glClearColor(ember->m_Background.r, ember->m_Background.g, ember->m_Background.b, 1.0);
@ -211,10 +209,10 @@ void GLWidget::initializeGL()
/// </summary>
void GLWidget::paintGL()
{
FractoriumEmberControllerBase* controller = m_Fractorium->m_Controller.get();
auto controller = m_Fractorium->m_Controller.get();
//Ensure there is a renderer and that it's supposed to be drawing, signified by the running timer.
if (controller && controller->Renderer() && controller->RenderTimerRunning())
if (controller && controller->Renderer())
{
RendererBase* renderer = controller->Renderer();
m_Drawing = true;
@ -256,8 +254,8 @@ void GLWidget::paintGL()
template <typename T>
void GLEmberController<T>::DrawImage()
{
RendererBase* renderer = m_FractoriumEmberController->Renderer();
Ember<T>* ember = m_FractoriumEmberController->CurrentEmber();
auto renderer = m_FractoriumEmberController->Renderer();
auto ember = m_FractoriumEmberController->CurrentEmber();
m_GL->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
m_GL->glClearColor(ember->m_Background.r, ember->m_Background.g, ember->m_Background.b, 1.0);
m_GL->glDisable(GL_DEPTH_TEST);
@ -286,14 +284,14 @@ template <typename T>
void GLEmberController<T>::DrawAffines(bool pre, bool post)
{
QueryVMP();//Resolves to float or double specialization function depending on T.
Ember<T>* ember = m_FractoriumEmberController->CurrentEmber();
auto ember = m_FractoriumEmberController->CurrentEmber();
bool dragging = m_DragState == eDragState::DragDragging;
//Draw grid if control key is pressed.
if (m_GL->hasFocus() && GetControl())
{
m_GL->glLineWidth(1.0f);
m_GL->DrawGrid();
m_GL->DrawGrid(m_FractoriumEmberController->AffineScaleLockedToCurrent());
}
//When dragging, only draw the selected xform's affine and hide all others.
@ -308,7 +306,7 @@ void GLEmberController<T>::DrawAffines(bool pre, bool post)
{
for (size_t i = 0; i < ember->TotalXformCount(); i++)
{
Xform<T>* xform = ember->GetTotalXform(i);
auto xform = ember->GetTotalXform(i);
bool selected = dragging ? (m_SelectedXform == xform) : (m_HoverXform == xform);
DrawAffine(xform, true, selected);
}
@ -322,7 +320,7 @@ void GLEmberController<T>::DrawAffines(bool pre, bool post)
{
for (size_t i = 0; i < ember->TotalXformCount(); i++)
{
Xform<T>* xform = ember->GetTotalXform(i);
auto xform = ember->GetTotalXform(i);
bool selected = dragging ? (m_SelectedXform == xform) : (m_HoverXform == xform);
DrawAffine(xform, false, selected);
}
@ -359,27 +357,12 @@ void GLEmberController<T>::DrawAffines(bool pre, bool post)
/// <param name="e">The event</param>
bool GLEmberControllerBase::KeyPress_(QKeyEvent* e)
{
#ifdef OLDDRAG
if (e->key() == Qt::Key_Shift)
SetShift();
else if (e->key() == Qt::Key_Control || e->key() == Qt::Key_C)
SetControl();
else if (e->key() == Qt::Key_Alt || e->key() == Qt::Key_A)
SetAlt();
else
return false;
return true;
#else
if (e->key() == Qt::Key_Control)
{
SetControl();
return true;
}
#endif
return false;
}
@ -401,27 +384,12 @@ void GLWidget::keyPressEvent(QKeyEvent* e)
/// <param name="e">The event</param>
bool GLEmberControllerBase::KeyRelease_(QKeyEvent* e)
{
#ifdef OLDDRAG
if (e->key() == Qt::Key_Shift)
ClearShift();
else if (e->key() == Qt::Key_Control || e->key() == Qt::Key_C)
ClearControl();
else if (e->key() == Qt::Key_Alt || e->key() == Qt::Key_A)
ClearAlt();
else
return false;
return true;
#else
if (e->key() == Qt::Key_Control)
{
ClearControl();
return true;
}
#endif
return false;
}
@ -448,8 +416,10 @@ template <typename T>
void GLEmberController<T>::MousePress(QMouseEvent* e)
{
v3T mouseFlipped(e->x() * m_GL->devicePixelRatio(), m_Viewport[3] - e->y() * m_GL->devicePixelRatio(), 0);//Must flip y because in OpenGL, 0,0 is bottom left, but in windows, it's top left.
Ember<T>* ember = m_FractoriumEmberController->CurrentEmber();
RendererBase* renderer = m_FractoriumEmberController->Renderer();
auto ember = m_FractoriumEmberController->CurrentEmber();
auto xforms = ember->TotalXformCount();
auto renderer = m_FractoriumEmberController->Renderer();
size_t i = 0;
//Ensure everything has been initialized.
if (!renderer)
@ -461,7 +431,6 @@ void GLEmberController<T>::MousePress(QMouseEvent* e)
m_BoundsDown.x = renderer->LowerLeftY(false);
m_BoundsDown.y = renderer->UpperRightX(false);
m_BoundsDown.z = renderer->UpperRightY(false);
#ifndef OLDDRAG
Qt::KeyboardModifiers mod = e->modifiers();
if (mod.testFlag(Qt::ShiftModifier))
@ -472,8 +441,6 @@ void GLEmberController<T>::MousePress(QMouseEvent* e)
if (mod.testFlag(Qt::AltModifier))// || mod.testFlag(Qt::Key_A))
SetAlt();
#endif
if (m_DragState == eDragState::DragNone)//Only take action if the user wasn't already dragging.
{
m_MouseDownWorldPos = m_MouseWorldPos;//Set the mouse down position to the current position.
@ -486,11 +453,16 @@ void GLEmberController<T>::MousePress(QMouseEvent* e)
{
m_SelectedXform = m_HoverXform;
m_DragSrcTransform = Affine2D<T>(m_AffineType == eAffineType::AffinePre ? m_SelectedXform->m_Affine : m_SelectedXform->m_Post);//Copy the affine of the xform that was selected.
m_DragHandlePos = m_HoverHandlePos;
m_DragHandleOffset = m_DragHandlePos - m_MouseWorldPos;
m_DragState = eDragState::DragDragging;
//The user has selected an xform by clicking on it, so update the main GUI by selecting this xform in the combo box.
m_Fractorium->CurrentXform(xformIndex);
m_Fractorium->CurrentXform(xformIndex);//Must do this first so UpdateXform() below properly grabs the current plus any selected.
//m_DragSrcTransforms.clear();
//m_FractoriumEmberController->UpdateXform([&](Xform<T>* xform)
//{
// m_DragSrcTransforms.push_back(m_AffineType == eAffineType::AffinePre ? xform->m_Affine : xform->m_Post);
//}, eXformUpdate::UPDATE_CURRENT_AND_SELECTED, false);//Don't update renderer here.
m_DragHandlePos = m_HoverHandlePos;//The location in local coordinates of the point selected on the spinner, x, y or center.
m_DragHandleOffset = m_DragHandlePos - m_MouseWorldPos;//The distance in world coordinates from the point selected to the center of the spinner.
m_DragState = eDragState::DragDragging;
//Draw large yellow dot on select or drag.
m_GL->glPointSize(6.0f);
m_GL->glBegin(GL_POINTS);
@ -554,9 +526,7 @@ void GLEmberController<T>::MouseRelease(QMouseEvent* e)
UpdateHover(mouseFlipped);
m_DragState = eDragState::DragNone;
#ifndef OLDDRAG
m_DragModifier = 0;
#endif
m_GL->repaint();//Force immediate redraw.
}
@ -585,7 +555,7 @@ void GLEmberController<T>::MouseMove(QMouseEvent* e)
bool draw = true;
glm::ivec2 mouse(e->x() * m_GL->devicePixelRatio(), e->y() * m_GL->devicePixelRatio());
v3T mouseFlipped(e->x() * m_GL->devicePixelRatio(), m_Viewport[3] - e->y() * m_GL->devicePixelRatio(), 0);//Must flip y because in OpenGL, 0,0 is bottom left, but in windows, it's top left.
Ember<T>* ember = m_FractoriumEmberController->CurrentEmber();
auto ember = m_FractoriumEmberController->CurrentEmber();
//First check to see if the mouse actually moved.
if (mouse == m_MousePos)
@ -593,7 +563,6 @@ void GLEmberController<T>::MouseMove(QMouseEvent* e)
m_MousePos = mouse;
m_MouseWorldPos = WindowToWorld(mouseFlipped, false);
//v3T mouseDelta = m_MouseWorldPos - m_MouseDownWorldPos;//Determine how far the mouse has moved in world cartesian coordinates.
//Update status bar on main window, regardless of whether anything is being dragged.
if (m_Fractorium->m_Controller->RenderTimerRunning())
@ -605,7 +574,15 @@ void GLEmberController<T>::MouseMove(QMouseEvent* e)
Affine2D<T>* affine = pre ? &m_SelectedXform->m_Affine : &m_SelectedXform->m_Post;//Determine pre or post affine.
if (m_HoverType == eHoverType::HoverTranslation)
{
//m_FractoriumEmberController->UpdateXform([&](Xform<T>* xform)
//{
// affine = pre ? &xform->m_Affine : &xform->m_Post;//Determine pre or post affine.
// *affine = CalcDragTranslation();
//}, eXformUpdate::UPDATE_ALL, false);//Don't need to update render for every xform, just do it once below.
*affine = CalcDragTranslation();
//CalcDragTranslation();
}
else if (m_HoverType == eHoverType::HoverXAxis)
*affine = CalcDragXAxis();
else if (m_HoverType == eHoverType::HoverYAxis)
@ -649,7 +626,7 @@ void GLEmberController<T>::MouseMove(QMouseEvent* e)
if (UpdateHover(mouseFlipped) == -1)
draw = false;
//Xform<T>* previousHover = m_HoverXform;
//auto previousHover = m_HoverXform;
//
//if (UpdateHover(mouseFlipped) == -1)
// m_HoverXform = m_SelectedXform;
@ -690,7 +667,7 @@ void GLWidget::mouseMoveEvent(QMouseEvent* e)
template <typename T>
void GLEmberController<T>::Wheel(QWheelEvent* e)
{
Ember<T>* ember = m_FractoriumEmberController->CurrentEmber();
auto ember = m_FractoriumEmberController->CurrentEmber();
if (m_Fractorium && !(e->buttons() & Qt::MiddleButton))//Middle button does whole image translation, so ignore the mouse wheel while panning to avoid inadvertent zooming.
m_Fractorium->SetScale(ember->m_PixelsPerUnit + (e->angleDelta().y() >= 0 ? 50 : -50));
@ -822,7 +799,7 @@ void GLWidget::SetViewport()
template <typename T>
bool GLEmberController<T>::SizesMatch()
{
Ember<T>* ember = m_FractoriumEmberController->CurrentEmber();
auto ember = m_FractoriumEmberController->CurrentEmber();
return (ember &&
ember->m_FinalRasW == m_GL->width() &&
ember->m_FinalRasH == m_GL->height() &&
@ -837,12 +814,13 @@ bool GLEmberController<T>::SizesMatch()
/// The frequency of the grid lines will change depending on the zoom.
/// Calculated with the frame always centered, the renderer just moves the camera.
/// </summary>
void GLWidget::DrawGrid()
/// <param name="scale">A value to scale by, used when locking the affine scale</param>
void GLWidget::DrawGrid(double scale)
{
RendererBase* renderer = m_Fractorium->m_Controller->Renderer();
float unitX = std::abs(renderer->UpperRightX(false) - renderer->LowerLeftX(false)) / 2.0f;
float unitY = std::abs(renderer->UpperRightY(false) - renderer->LowerLeftY(false)) / 2.0f;
float rad = std::max(unitX, unitY);
float rad = std::max(unitX * scale, unitY * scale);
float xLow = floor(-unitX);
float xHigh = ceil(unitX);
float yLow = floor(-unitY);
@ -866,6 +844,9 @@ void GLWidget::DrawGrid()
}
}
unitX *= scale;
unitY *= scale;
if (unitX <= 64.0f)
{
glColor4f(0.5f, 0.5f, 0.5f, 1.0f);
@ -934,16 +915,16 @@ void GLWidget::DrawUnitSquare()
template <typename T>
void GLEmberController<T>::DrawAffine(Xform<T>* xform, bool pre, bool selected)
{
Ember<T>* ember = m_FractoriumEmberController->CurrentEmber();
auto ember = m_FractoriumEmberController->CurrentEmber();
bool final = ember->IsFinalXform(xform);
int index = ember->GetXformIndex(xform);
size_t size = ember->m_Palette.m_Entries.size();
v4T color = ember->m_Palette.m_Entries[Clamp<T>(xform->m_ColorX * size, 0, size - 1)];
Affine2D<T>* affine = pre ? &xform->m_Affine : &xform->m_Post;
auto affine = pre ? &xform->m_Affine : &xform->m_Post;
//For some incredibly strange reason, even though glm and OpenGL use matrices with a column-major
//data layout, nothing will work here unless they are flipped to row major order. This is how it was
//done in Fractron.
m4T mat = affine->ToMat4RowMajor();
m4T mat = (*affine * m_FractoriumEmberController->AffineScaleCurrentToLocked()).ToMat4RowMajor();
m_GL->glPushMatrix();
m_GL->glLoadIdentity();
MultMatrix(mat);
@ -1054,7 +1035,7 @@ int GLEmberController<T>::UpdateHover(v3T& glCoords)
bool postAll = post && m_Fractorium->DrawAllPost();
uint bestIndex = -1;
T bestDist = 10;
Ember<T>* ember = m_FractoriumEmberController->CurrentEmber();
auto ember = m_FractoriumEmberController->CurrentEmber();
m_HoverType = eHoverType::HoverNone;
//If there's a selected/current xform, check it first so it gets precedence over the others.
@ -1076,7 +1057,7 @@ int GLEmberController<T>::UpdateHover(v3T& glCoords)
//Check all xforms.
for (size_t i = 0; i < ember->TotalXformCount(); i++)
{
Xform<T>* xform = ember->GetTotalXform(i);
auto xform = ember->GetTotalXform(i);
if (preAll || (pre && m_HoverXform == xform))//Only check pre affine if they are shown.
{
@ -1116,17 +1097,17 @@ template <typename T>
bool GLEmberController<T>::CheckXformHover(Xform<T>* xform, v3T& glCoords, T& bestDist, bool pre, bool post)
{
bool preFound = false, postFound = false;
float dist = 0.0f;
T dist = 0, scale = m_FractoriumEmberController->AffineScaleCurrentToLocked();
v3T pos;
Ember<T>* ember = m_FractoriumEmberController->CurrentEmber();
if (pre)
{
v3T translation(xform->m_Affine.C()/* - ember->m_CenterX*/, /*ember->m_CenterY + */xform->m_Affine.F(), 0);
auto affineScaled = xform->m_Affine * scale;
v3T translation(affineScaled.C(), affineScaled.F(), 0);
v3T transScreen = glm::project(translation, m_Modelview, m_Projection, m_Viewport);
v3T xAxis(xform->m_Affine.A(), xform->m_Affine.D(), 0);
v3T xAxis(affineScaled.A(), affineScaled.D(), 0);
v3T xAxisScreen = glm::project(translation + xAxis, m_Modelview, m_Projection, m_Viewport);
v3T yAxis(xform->m_Affine.B(), xform->m_Affine.E(), 0);
v3T yAxis(affineScaled.B(), affineScaled.E(), 0);
v3T yAxisScreen = glm::project(translation + yAxis, m_Modelview, m_Projection, m_Viewport);
pos = translation;
dist = glm::distance(glCoords, transScreen);
@ -1167,11 +1148,12 @@ bool GLEmberController<T>::CheckXformHover(Xform<T>* xform, v3T& glCoords, T& be
if (post)
{
v3T translation(xform->m_Post.C()/* - ember->m_CenterX*/, /*ember->m_CenterY + */xform->m_Post.F(), 0);
auto affineScaled = xform->m_Post * scale;
v3T translation(affineScaled.C(), affineScaled.F(), 0);
v3T transScreen = glm::project(translation, m_Modelview, m_Projection, m_Viewport);
v3T xAxis(xform->m_Post.A(), xform->m_Post.D(), 0);
v3T xAxis(affineScaled.A(), affineScaled.D(), 0);
v3T xAxisScreen = glm::project(translation + xAxis, m_Modelview, m_Projection, m_Viewport);
v3T yAxis(xform->m_Post.B(), xform->m_Post.E(), 0);
v3T yAxis(affineScaled.B(), affineScaled.E(), 0);
v3T yAxisScreen = glm::project(translation + yAxis, m_Modelview, m_Projection, m_Viewport);
pos = translation;
dist = glm::distance(glCoords, transScreen);
@ -1235,7 +1217,8 @@ template <typename T>
Affine2D<T> GLEmberController<T>::CalcDragXAxis()
{
v3T t3, newAxis, newPos;
Affine2D<T> result = m_DragSrcTransform;
auto scale = m_FractoriumEmberController->AffineScaleLockedToCurrent();
auto result = m_DragSrcTransform;
bool worldPivotShiftAlt = !m_Fractorium->LocalPivot() && GetShift() && GetAlt();
if (worldPivotShiftAlt)
@ -1245,7 +1228,7 @@ Affine2D<T> GLEmberController<T>::CalcDragXAxis()
if (GetShift())
{
v3T targetAxis = m_MouseWorldPos - t3;
v3T targetAxis = (m_MouseWorldPos * scale) - t3;
v3T norm = glm::normalize(targetAxis);
if (GetControl())
@ -1263,7 +1246,7 @@ Affine2D<T> GLEmberController<T>::CalcDragXAxis()
else
newPos = m_MouseWorldPos + m_DragHandleOffset;
newAxis = newPos - t3;
newAxis = (newPos * scale) - t3;
}
if (GetAlt())
@ -1278,7 +1261,8 @@ Affine2D<T> GLEmberController<T>::CalcDragXAxis()
result.RotateScaleXTo(v2T(newAxis));
}
m_DragHandlePos = v3T(result.O() + result.X(), 0);
T scaleBack = m_FractoriumEmberController->AffineScaleCurrentToLocked();
m_DragHandlePos = v3T((result.O() + result.X()) * scaleBack, 0);
return result;
}
@ -1304,7 +1288,8 @@ template <typename T>
Affine2D<T> GLEmberController<T>::CalcDragYAxis()
{
v3T t3, newAxis, newPos;
Affine2D<T> result = m_DragSrcTransform;
auto scale = m_FractoriumEmberController->AffineScaleLockedToCurrent();
auto result = m_DragSrcTransform;
bool worldPivotShiftAlt = !m_Fractorium->LocalPivot() && GetShift() && GetAlt();
if (worldPivotShiftAlt)
@ -1314,7 +1299,7 @@ Affine2D<T> GLEmberController<T>::CalcDragYAxis()
if (GetShift())
{
v3T targetAxis = m_MouseWorldPos - t3;
v3T targetAxis = (m_MouseWorldPos * scale) - t3;
v3T norm = glm::normalize(targetAxis);
if (GetControl())
@ -1332,7 +1317,7 @@ Affine2D<T> GLEmberController<T>::CalcDragYAxis()
else
newPos = m_MouseWorldPos + m_DragHandleOffset;
newAxis = newPos - t3;
newAxis = (newPos * scale) - t3;
}
if (GetAlt())
@ -1347,7 +1332,8 @@ Affine2D<T> GLEmberController<T>::CalcDragYAxis()
result.RotateScaleYTo(v2T(newAxis));
}
m_DragHandlePos = v3T(result.O() + result.Y(), 0);
T scaleBack = m_FractoriumEmberController->AffineScaleCurrentToLocked();
m_DragHandlePos = v3T((result.O() + result.Y()) * scaleBack, 0);
return result;
}
@ -1368,8 +1354,9 @@ Affine2D<T> GLEmberController<T>::CalcDragYAxis()
template <typename T>
Affine2D<T> GLEmberController<T>::CalcDragTranslation()
{
v3T newPos, newX, newY;
Affine2D<T> result = m_DragSrcTransform;
v3T newPos;
auto scale = m_FractoriumEmberController->AffineScaleLockedToCurrent();
auto result = m_DragSrcTransform;
bool worldPivotShift = !m_Fractorium->LocalPivot() && GetShift();
if (GetShift())
@ -1387,17 +1374,32 @@ Affine2D<T> GLEmberController<T>::CalcDragTranslation()
T endAngle = atan2(newPos.y, newPos.x);
T angle = startAngle - endAngle;
result.Rotate(angle * RAD_2_DEG);
//RotateXformsByAngle
}
}
else
{
if (GetControl())
{
newPos = SnapToGrid(m_MouseWorldPos);
}
else
{
newPos = m_MouseWorldPos + m_DragHandleOffset;
//bool pre = m_AffineType == eAffineType::AffinePre;
//size_t index = 0;
//newPos = m_MouseWorldPos;
//auto diff = m_MouseWorldPos - m_MouseDownWorldPos;
//m_FractoriumEmberController->UpdateXform([&](Xform<T>* xform)
//{
// auto affine = pre ? &xform->m_Affine : &xform->m_Post;//Determine pre or post affine.
// affine->O(m_DragSrcTransforms[index++].O() + v2T(diff));
//}, eXformUpdate::UPDATE_CURRENT_AND_SELECTED, false);//Don't need to update render for every xform, just do it once below.
}
}
result.O(v2T(newPos));
T scaleBack = m_FractoriumEmberController->AffineScaleCurrentToLocked();
result.O(v2T(newPos * scale));
m_DragHandlePos = newPos;
return result;
}

View File

@ -69,7 +69,7 @@ private:
bool Allocate(bool force = false);
bool Deallocate();
void SetViewport();
void DrawGrid();
void DrawGrid(double scale);
void DrawUnitSquare();
void DrawAffineHelper(int index, bool selected, bool pre, bool final, bool background);
GLEmberControllerBase* GLController();

View File

@ -56,7 +56,7 @@ void FractoriumVariationsDialog::ForEachSelectedCell(std::function<void(QTableWi
QList<QTableWidgetItem*> selectedItems = table->selectedItems();
table->model()->blockSignals(true);
foreach (QTableWidgetItem* item, selectedItems)
for (auto item : selectedItems)
if (item)
func(item);