Bug fixes:

--Fix bug where clearing the final xform, but leaving it present, then editing it would crash the program.
This commit is contained in:
Person 2017-07-26 21:25:44 -07:00
parent 706c0b60ad
commit a81b93d94a
13 changed files with 141 additions and 96 deletions

View File

@ -215,6 +215,27 @@ public:
AddXforms(xformPad - XformCount()); AddXforms(xformPad - XformCount());
} }
/// <summary>
/// Replace the xforms vector with the one passed in.
/// </summary>
/// <param name="xforms">The xforms to replace with</param>
/// <param name="xformPad">True to move, false to copy. Default: true.</param>
/// <returns>True if replaced, else false.</returns>
bool ReplaceXforms(vector<Xform<T>>& xforms, bool move = true)
{
if (!xforms.empty())
{
if (move)
m_Xforms = std::move(xforms);
else
m_Xforms = xforms;
return true;
}
else
return false;
}
/// <summary> /// <summary>
/// Copy this ember with optional padding xforms added. /// Copy this ember with optional padding xforms added.
/// </summary> /// </summary>
@ -280,12 +301,13 @@ public:
/// Delete the xform at the specified index, including the final one. /// Delete the xform at the specified index, including the final one.
/// </summary> /// </summary>
/// <param name="i">The index to delete</param> /// <param name="i">The index to delete</param>
/// <param name="forceFinal">If true, delete the final xform when its index is passed in even if one is not present. Default: false.</param>
/// <returns>True if success, else false.</returns> /// <returns>True if success, else false.</returns>
bool DeleteTotalXform(size_t i) bool DeleteTotalXform(size_t i, bool forceFinal = false)
{ {
if (DeleteXform(i)) if (DeleteXform(i))
{ } { }
else if (i == XformCount() && UseFinalXform()) else if (i == XformCount() && (forceFinal || UseFinalXform()))
m_FinalXform.Clear(); m_FinalXform.Clear();
else else
return false; return false;
@ -310,13 +332,13 @@ public:
/// Get a pointer to the xform at the specified index, including the final one. /// Get a pointer to the xform at the specified index, including the final one.
/// </summary> /// </summary>
/// <param name="i">The index to get</param> /// <param name="i">The index to get</param>
/// <param name="forceFinal">If true, return the final xform when its index is requested even if one is not present</param> /// <param name="forceFinal">If true, return the final xform when its index is requested even if one is not present. Default: false.</param>
/// <returns>A pointer to the xform at the index if successful, else nullptr.</returns> /// <returns>A pointer to the xform at the index if successful, else nullptr.</returns>
Xform<T>* GetTotalXform(size_t i, bool forceFinal = false) const Xform<T>* GetTotalXform(size_t i, bool forceFinal = false) const
{ {
if (i < XformCount()) if (i < XformCount())
return const_cast<Xform<T>*>(&m_Xforms[i]); return const_cast<Xform<T>*>(&m_Xforms[i]);
else if (forceFinal || (i == XformCount() && UseFinalXform())) else if (i == XformCount() && (forceFinal || UseFinalXform()))
return const_cast<Xform<T>*>(&m_FinalXform); return const_cast<Xform<T>*>(&m_FinalXform);
else else
return nullptr; return nullptr;
@ -342,13 +364,14 @@ public:
/// Search the xforms, including final, to find which one's address matches the address of the specified xform. /// Search the xforms, including final, to find which one's address matches the address of the specified xform.
/// </summary> /// </summary>
/// <param name="xform">A pointer to the xform to find</param> /// <param name="xform">A pointer to the xform to find</param>
/// <param name="forceFinal">If true, return the index of the final xform when its pointer is passed, even if a final is not present. Default: false.</param>
/// <returns>The index of the matched xform if found, else -1.</returns> /// <returns>The index of the matched xform if found, else -1.</returns>
intmax_t GetTotalXformIndex(Xform<T>* xform) const intmax_t GetTotalXformIndex(Xform<T>* xform, bool forceFinal = false) const
{ {
size_t totalXformCount = TotalXformCount(); size_t totalXformCount = TotalXformCount(forceFinal);
for (size_t i = 0; i < totalXformCount; i++) for (size_t i = 0; i < totalXformCount; i++)
if (GetTotalXform(i) == xform) if (GetTotalXform(i, forceFinal) == xform)
return intmax_t(i); return intmax_t(i);
return -1; return -1;
@ -1468,7 +1491,7 @@ public:
inline const Xform<T>* FinalXform() const { return &m_FinalXform; } inline const Xform<T>* FinalXform() const { return &m_FinalXform; }
inline Xform<T>* NonConstFinalXform() { return &m_FinalXform; } inline Xform<T>* NonConstFinalXform() { return &m_FinalXform; }
inline bool UseFinalXform() const { return !m_FinalXform.Empty(); } inline bool UseFinalXform() const { return !m_FinalXform.Empty(); }
inline size_t TotalXformCount() const { return XformCount() + (UseFinalXform() ? 1 : 0); } inline size_t TotalXformCount(bool forceFinal = false) const { return XformCount() + ((forceFinal || UseFinalXform()) ? 1 : 0); }
inline int PaletteIndex() const { return m_Palette.m_Index; } inline int PaletteIndex() const { return m_Palette.m_Index; }
inline T BlurCoef() { return m_BlurCoef; } inline T BlurCoef() { return m_BlurCoef; }
inline eScaleType ScaleType() const { return m_ScaleType; } inline eScaleType ScaleType() const { return m_ScaleType; }

View File

@ -264,6 +264,8 @@ void FractoriumEmberController<T>::ApplyXmlSavingTemplate(Ember<T>& ember)
/// <summary> /// <summary>
/// Return whether the current ember contains a final xform and the GUI is aware of it. /// Return whether the current ember contains a final xform and the GUI is aware of it.
/// Note this can be true even if the final is empty, as long as they've added one and have
/// not explicitly deleted it.
/// </summary> /// </summary>
/// <returns>True if the current ember contains a final xform, else false.</returns> /// <returns>True if the current ember contains a final xform, else false.</returns>
bool Fractorium::HaveFinal() bool Fractorium::HaveFinal()

View File

@ -405,7 +405,7 @@ private:
//Xforms Selection. //Xforms Selection.
void ClearXformsSelections(); void ClearXformsSelections();
void ForEachXformCheckbox(std::function<void(int, QCheckBox*)> func); void ForEachXformCheckbox(std::function<void(int, QCheckBox*, bool)> func);
//Xaos. //Xaos.
void FillXaosTable(); void FillXaosTable();

View File

@ -3423,7 +3423,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Set all xform color speed values to random numbers between 0 and 1, inclusive&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Set all xform color speed values to random numbers between 0 and 1, inclusive&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="text"> <property name="text">
<string>Random Color Speed</string> <string>Random Color Speeds</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -208,31 +208,32 @@ template <typename T>
void FractoriumEmberController<T>::UpdateXform(std::function<void(Xform<T>*)> func, eXformUpdate updateType, bool updateRender, eProcessAction action, size_t index) void FractoriumEmberController<T>::UpdateXform(std::function<void(Xform<T>*)> func, eXformUpdate updateType, bool updateRender, eProcessAction action, size_t index)
{ {
int i = 0; int i = 0;
bool isCurrentFinal = m_Ember.IsFinalXform(CurrentXform()); auto current = CurrentXform();
bool forceFinal = m_Fractorium->HaveFinal();
bool isCurrentFinal = m_Ember.IsFinalXform(current);
bool doFinal = updateType != eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL && updateType != eXformUpdate::UPDATE_ALL_EXCEPT_FINAL; bool doFinal = updateType != eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL && updateType != eXformUpdate::UPDATE_ALL_EXCEPT_FINAL;
switch (updateType) switch (updateType)
{ {
case eXformUpdate::UPDATE_SPECIFIC: case eXformUpdate::UPDATE_SPECIFIC:
{ {
if (auto xform = m_Ember.GetTotalXform(index)) if (auto xform = m_Ember.GetTotalXform(index, forceFinal))
func(xform); func(xform);
} }
break; break;
case eXformUpdate::UPDATE_CURRENT: case eXformUpdate::UPDATE_CURRENT:
{ {
if (auto xform = CurrentXform()) if (current)
func(xform); func(current);
} }
break; break;
case eXformUpdate::UPDATE_CURRENT_AND_SELECTED: case eXformUpdate::UPDATE_CURRENT_AND_SELECTED:
{ {
bool currentDone = false; bool currentDone = false;
auto current = CurrentXform();
while (auto xform = m_Ember.GetTotalXform(i)) while (auto xform = m_Ember.GetTotalXform(i, forceFinal))
{ {
if (i < m_Fractorium->m_XformSelections.size()) if (i < m_Fractorium->m_XformSelections.size())
{ {
@ -261,7 +262,7 @@ void FractoriumEmberController<T>::UpdateXform(std::function<void(Xform<T>*)> fu
{ {
bool anyUpdated = false; bool anyUpdated = false;
while (auto xform = (doFinal ? m_Ember.GetTotalXform(i) : m_Ember.GetXform(i))) while (auto xform = (doFinal ? m_Ember.GetTotalXform(i, forceFinal) : m_Ember.GetXform(i)))
{ {
if (i < m_Fractorium->m_XformSelections.size()) if (i < m_Fractorium->m_XformSelections.size())
{ {
@ -280,14 +281,14 @@ void FractoriumEmberController<T>::UpdateXform(std::function<void(Xform<T>*)> fu
if (!anyUpdated)//None were selected, so just apply to the current. 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 (doFinal || !isCurrentFinal)//If do final, call func regardless. If not, only call if current is not final.
if (auto xform = CurrentXform()) if (current)
func(xform); func(current);
} }
break; break;
case eXformUpdate::UPDATE_ALL: case eXformUpdate::UPDATE_ALL:
{ {
while (auto xform = m_Ember.GetTotalXform(i++)) while (auto xform = m_Ember.GetTotalXform(i++, forceFinal))
func(xform); func(xform);
} }
break; break;

View File

@ -69,7 +69,8 @@ void FractoriumEmberController<T>::FillSummary()
int vp = 4; int vp = 4;
int vlen = 7; int vlen = 7;
char pc = 'f'; char pc = 'f';
size_t x = 0, total = m_Ember.TotalXformCount(); bool forceFinal = m_Fractorium->HaveFinal();
size_t x = 0, total = m_Ember.TotalXformCount(forceFinal);
Xform<T>* xform = nullptr; Xform<T>* xform = nullptr;
QColor color; QColor color;
auto table = m_Fractorium->ui.SummaryTable; auto table = m_Fractorium->ui.SummaryTable;
@ -85,7 +86,7 @@ void FractoriumEmberController<T>::FillSummary()
QSize size(table->columnWidth(0), table->rowHeight(1) + 1); QSize size(table->columnWidth(0), table->rowHeight(1) + 1);
m_Fractorium->m_InfoPaletteItem->setData(Qt::DecorationRole, pixmap.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); m_Fractorium->m_InfoPaletteItem->setData(Qt::DecorationRole, pixmap.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
for (x = 0; x < total && (xform = m_Ember.GetTotalXform(x)); x++) for (x = 0; x < total && (xform = m_Ember.GetTotalXform(x, forceFinal)); x++)
{ {
size_t i = 0; size_t i = 0;
QString as = "Pre"; QString as = "Pre";
@ -233,5 +234,5 @@ void Fractorium::ErrorReportToQTextEdit(const vector<string>& errors, QTextEdit*
template class FractoriumEmberController<float>; template class FractoriumEmberController<float>;
#ifdef DO_DOUBLE #ifdef DO_DOUBLE
template class FractoriumEmberController<double>; template class FractoriumEmberController<double>;
#endif #endif

View File

@ -428,7 +428,9 @@ void FractoriumEmberController<T>::Undo()
{ {
if (m_UndoList.size() > 1 && m_UndoIndex > 0) if (m_UndoList.size() > 1 && m_UndoIndex > 0)
{ {
int index = m_Ember.GetTotalXformIndex(CurrentXform()); bool forceFinal = m_Fractorium->HaveFinal();
auto current = CurrentXform();
int index = m_Ember.GetTotalXformIndex(current, forceFinal);
m_LastEditWasUndoRedo = true; m_LastEditWasUndoRedo = true;
m_UndoIndex = std::max<size_t>(0u, m_UndoIndex - 1u); m_UndoIndex = std::max<size_t>(0u, m_UndoIndex - 1u);
SetEmber(m_UndoList[m_UndoIndex], true, false);//Don't update pointer because it's coming from the undo list... SetEmber(m_UndoList[m_UndoIndex], true, false);//Don't update pointer because it's coming from the undo list...
@ -452,7 +454,9 @@ void FractoriumEmberController<T>::Redo()
{ {
if (m_UndoList.size() > 1 && m_UndoIndex < m_UndoList.size() - 1) if (m_UndoList.size() > 1 && m_UndoIndex < m_UndoList.size() - 1)
{ {
int index = m_Ember.GetTotalXformIndex(CurrentXform()); bool forceFinal = m_Fractorium->HaveFinal();
auto current = CurrentXform();
int index = m_Ember.GetTotalXformIndex(current, forceFinal);
m_LastEditWasUndoRedo = true; m_LastEditWasUndoRedo = true;
m_UndoIndex = std::min<size_t>(m_UndoIndex + 1, m_UndoList.size() - 1); m_UndoIndex = std::min<size_t>(m_UndoIndex + 1, m_UndoList.size() - 1);
SetEmber(m_UndoList[m_UndoIndex], true, false); SetEmber(m_UndoList[m_UndoIndex], true, false);
@ -740,10 +744,11 @@ void Fractorium::OnActionResetScale(bool checked)
template <typename T> template <typename T>
void FractoriumEmberController<T>::AddReflectiveSymmetry() void FractoriumEmberController<T>::AddReflectiveSymmetry()
{ {
bool forceFinal = m_Fractorium->HaveFinal();
Update([&]() Update([&]()
{ {
m_Ember.AddSymmetry(-1, m_Rand); m_Ember.AddSymmetry(-1, m_Rand);
auto index = m_Ember.TotalXformCount() - (m_Ember.UseFinalXform() ? 2 : 1);//Set index to the last item before final. auto index = m_Ember.TotalXformCount(forceFinal) - (forceFinal ? 2 : 1);//Set index to the last item before final.
FillXforms(int(index)); FillXforms(int(index));
}); });
} }
@ -757,10 +762,11 @@ void Fractorium::OnActionAddReflectiveSymmetry(bool checked) { m_Controller->Add
template <typename T> template <typename T>
void FractoriumEmberController<T>::AddRotationalSymmetry() void FractoriumEmberController<T>::AddRotationalSymmetry()
{ {
bool forceFinal = m_Fractorium->HaveFinal();
Update([&]() Update([&]()
{ {
m_Ember.AddSymmetry(2, m_Rand); m_Ember.AddSymmetry(2, m_Rand);
auto index = m_Ember.TotalXformCount() - (m_Ember.UseFinalXform() ? 2 : 1);//Set index to the last item before final. auto index = m_Ember.TotalXformCount(forceFinal) - (forceFinal ? 2 : 1);//Set index to the last item before final.
FillXforms(int(index)); FillXforms(int(index));
}); });
} }
@ -774,10 +780,11 @@ void Fractorium::OnActionAddRotationalSymmetry(bool checked) { m_Controller->Add
template <typename T> template <typename T>
void FractoriumEmberController<T>::AddBothSymmetry() void FractoriumEmberController<T>::AddBothSymmetry()
{ {
bool forceFinal = m_Fractorium->HaveFinal();
Update([&]() Update([&]()
{ {
m_Ember.AddSymmetry(-2, m_Rand); m_Ember.AddSymmetry(-2, m_Rand);
auto index = m_Ember.TotalXformCount() - (m_Ember.UseFinalXform() ? 2 : 1);//Set index to the last item before final. auto index = m_Ember.TotalXformCount(forceFinal) - (forceFinal ? 2 : 1);//Set index to the last item before final.
FillXforms(int(index)); FillXforms(int(index));
}); });
} }

View File

@ -323,8 +323,9 @@ void FractoriumEmberController<T>::PaletteEditorButtonClicked()
Palette<float> prevPal = m_TempPalette; Palette<float> prevPal = m_TempPalette;
ed->SetPalette(m_TempPalette); ed->SetPalette(m_TempPalette);
map<size_t, float> colorIndices; map<size_t, float> colorIndices;
bool forceFinal = m_Fractorium->HaveFinal();
while (auto xform = m_Ember.GetTotalXform(i)) while (auto xform = m_Ember.GetTotalXform(i, forceFinal))
colorIndices[i++] = xform->m_ColorX; colorIndices[i++] = xform->m_ColorX;
ed->SetColorIndices(colorIndices); ed->SetColorIndices(colorIndices);

View File

@ -341,10 +341,11 @@ bool FractoriumEmberController<T>::Render()
{ {
size_t i = 0; size_t i = 0;
int solo = m_Fractorium->ui.CurrentXformCombo->property("soloxform").toInt(); int solo = m_Fractorium->ui.CurrentXformCombo->property("soloxform").toInt();
bool forceFinal = m_Fractorium->HaveFinal();
if (solo != -1) if (solo != -1)
{ {
m_TempOpacities.resize(m_Ember.TotalXformCount()); m_TempOpacities.resize(m_Ember.TotalXformCount(forceFinal));
while (auto xform = m_Ember.GetTotalXform(i)) while (auto xform = m_Ember.GetTotalXform(i))
{ {
@ -357,7 +358,7 @@ bool FractoriumEmberController<T>::Render()
m_Renderer->SetEmber(m_Ember, action); m_Renderer->SetEmber(m_Ember, action);
if (solo != -1) if (solo != -1)
while (auto xform = m_Ember.GetTotalXform(i)) while (auto xform = m_Ember.GetTotalXform(i, forceFinal))
xform->m_Opacity = m_TempOpacities[i++]; xform->m_Opacity = m_TempOpacities[i++];
} }

View File

@ -46,7 +46,8 @@ void Fractorium::InitXformsUI()
template <typename T> template <typename T>
Xform<T>* FractoriumEmberController<T>::CurrentXform() Xform<T>* FractoriumEmberController<T>::CurrentXform()
{ {
return m_Ember.GetTotalXform(m_Fractorium->ui.CurrentXformCombo->currentIndex()); bool hasFinal = m_Fractorium->HaveFinal();
return m_Ember.GetTotalXform(m_Fractorium->ui.CurrentXformCombo->currentIndex(), hasFinal);//Need to force final for the special case they created a final, then cleared it, but did not delete it.
} }
/// <summary> /// <summary>
@ -67,7 +68,9 @@ void Fractorium::CurrentXform(uint i)
template <typename T> template <typename T>
void FractoriumEmberController<T>::CurrentXformComboChanged(int index) void FractoriumEmberController<T>::CurrentXformComboChanged(int index)
{ {
if (auto xform = m_Ember.GetTotalXform(index)) bool forceFinal = m_Fractorium->HaveFinal();
if (auto xform = m_Ember.GetTotalXform(index, forceFinal))
{ {
FillWithXform(xform); FillWithXform(xform);
m_GLController->SetSelectedXform(xform); m_GLController->SetSelectedXform(xform);
@ -95,13 +98,14 @@ void Fractorium::OnCurrentXformComboChanged(int index) { m_Controller->CurrentXf
template <typename T> template <typename T>
void FractoriumEmberController<T>::AddXform() void FractoriumEmberController<T>::AddXform()
{ {
bool forceFinal = m_Fractorium->HaveFinal();
Update([&]() Update([&]()
{ {
Xform<T> newXform; Xform<T> newXform;
newXform.m_Weight = 0.25; newXform.m_Weight = 0.25;
newXform.m_ColorX = m_Rand.Frand01<T>(); newXform.m_ColorX = m_Rand.Frand01<T>();
m_Ember.AddXform(newXform); m_Ember.AddXform(newXform);
int index = int(m_Ember.TotalXformCount() - (m_Ember.UseFinalXform() ? 2 : 1));//Set index to the last item before final. int index = int(m_Ember.TotalXformCount(forceFinal) - (forceFinal ? 2 : 1));//Set index to the last item before final.
FillXforms(index); FillXforms(index);
}); });
} }
@ -119,6 +123,7 @@ void Fractorium::OnAddXformButtonClicked(bool checked) { m_Controller->AddXform(
template <typename T> template <typename T>
void FractoriumEmberController<T>::AddLinkedXform() void FractoriumEmberController<T>::AddLinkedXform()
{ {
bool forceFinal = m_Fractorium->HaveFinal();
UpdateXform([&](Xform<T>* xform) UpdateXform([&](Xform<T>* xform)
{ {
size_t i, count = m_Ember.XformCount(); size_t i, count = m_Ember.XformCount();
@ -148,7 +153,7 @@ void FractoriumEmberController<T>::AddLinkedXform()
xform->SetXaos(count - 1, 1);//Set the xaos value for the previous xform pointing to the new one to one. xform->SetXaos(count - 1, 1);//Set the xaos value for the previous xform pointing to the new one to one.
xform->m_Opacity = 0;//Clear the opacity of the previous xform. xform->m_Opacity = 0;//Clear the opacity of the previous xform.
int index = int(m_Ember.TotalXformCount() - (m_Ember.UseFinalXform() ? 2 : 1));//Set index to the last item before final. int index = int(m_Ember.TotalXformCount(forceFinal) - (forceFinal ? 2 : 1));//Set index to the last item before final.
FillXforms(index); FillXforms(index);
FillXaos(); FillXaos();
}, eXformUpdate::UPDATE_CURRENT); }, eXformUpdate::UPDATE_CURRENT);
@ -165,6 +170,7 @@ void Fractorium::OnAddLinkedXformButtonClicked(bool checked) { m_Controller->Add
template <typename T> template <typename T>
void FractoriumEmberController<T>::DuplicateXform() void FractoriumEmberController<T>::DuplicateXform()
{ {
bool forceFinal = m_Fractorium->HaveFinal();
vector<Xform<T>> vec; vector<Xform<T>> vec;
vec.reserve(m_Ember.XformCount()); vec.reserve(m_Ember.XformCount());
UpdateXform([&] (Xform<T>* xform) UpdateXform([&] (Xform<T>* xform)
@ -176,7 +182,7 @@ void FractoriumEmberController<T>::DuplicateXform()
for (auto& it : vec) for (auto& it : vec)
m_Ember.AddXform(it); m_Ember.AddXform(it);
int index = int(m_Ember.TotalXformCount() - (m_Ember.UseFinalXform() ? 2 : 1));//Set index to the last item before final. int index = int(m_Ember.TotalXformCount(forceFinal) - (forceFinal ? 2 : 1));//Set index to the last item before final.
FillXforms(index);//Handles xaos. FillXforms(index);//Handles xaos.
}); });
} }
@ -211,49 +217,60 @@ void Fractorium::OnClearXformButtonClicked(bool checked) { m_Controller->ClearXf
template <typename T> template <typename T>
void FractoriumEmberController<T>::DeleteXforms() void FractoriumEmberController<T>::DeleteXforms()
{ {
int offset = 0, current = 0, checked = 0; bool removed = false;
bool haveFinal = false; bool haveFinal = m_Fractorium->HaveFinal();
size_t count;
auto combo = m_Fractorium->ui.CurrentXformCombo; auto combo = m_Fractorium->ui.CurrentXformCombo;
Xform<T>* finalXform = nullptr;
vector<Xform<T>> xforms;
xforms.reserve(m_Ember.TotalXformCount());
//Iterating over the checkboxes must be done instead of using UpdateXform() to iterate over xforms //Iterating over the checkboxes must be done instead of using UpdateXform() to iterate over xforms
//because xforms are being deleted inside the loop. //because xforms are being deleted inside the loop.
//Also manually calling UpdateRender() rather than using the usual Update() call because //Also manually calling UpdateRender() rather than using the usual Update() call because
//it should only be called if an xform has actually been deleted. //it should only be called if an xform has actually been deleted.
m_Fractorium->ForEachXformCheckbox([&](int i, QCheckBox * w) //Rather than go through and delete, it's easier to just make a list of what we want to keep.
m_Fractorium->ForEachXformCheckbox([&](int i, QCheckBox * w, bool isFinal)
{ {
count = m_Ember.TotalXformCount(); if (!w->isChecked())//Keep if not checked.
haveFinal = m_Ember.UseFinalXform();//Requery every time.
if (w->isChecked())
checked++;
//Do not allow deleting the only remaining non-final xform.
if (haveFinal && count <= 2 && !(i - offset))
return;
if (!haveFinal && count == 1)
return;
if (w->isChecked())
{ {
m_Ember.DeleteTotalXform(i - offset);//Subtract offset to account for previously deleted xforms. if (isFinal)
offset++; finalXform = m_Ember.NonConstFinalXform();
else if (auto xform = m_Ember.GetXform(i))
xforms.push_back(*xform);
} }
}); });
current = combo->currentIndex(); //They might not have selected any checkboxes, in which case just delete the current.
count = m_Ember.TotalXformCount(); auto current = combo->currentIndex();
haveFinal = m_Ember.UseFinalXform();//Requery again. auto count = m_Ember.TotalXformCount();
bool anySelected = xforms.size() < m_Ember.XformCount();
bool finalSelected = !finalXform && haveFinal;
//Nothing was selected, so just delete current. //Nothing was selected, so just delete current.
if (!checked && if (!anySelected && !finalSelected)
!(haveFinal && count <= 2 && current == 0) &&//Again disallow deleting the only remaining non-final xform.
!(!haveFinal && count == 1))
{ {
m_Ember.DeleteTotalXform(current); //Disallow deleting the only remaining non-final xform.
offset++; if (!(haveFinal && count <= 2 && current == 0) &&//One non-final, one final, disallow deleting non-final.
!(!haveFinal && count == 1))//One non-final, no final, disallow deleting.
{
m_Ember.DeleteTotalXform(current, haveFinal);
removed = true;
}
}
else
{
if (!xforms.empty() && (xforms.size() != m_Ember.XformCount()))//Remove if they requested to do so, but ensure it's not removing all.
{
removed = true;
m_Ember.ReplaceXforms(xforms);
}
if (finalSelected)
{
removed = true;
m_Ember.NonConstFinalXform()->Clear();
}
} }
if (offset) if (removed)
{ {
int index = int(m_Ember.TotalXformCount() - (m_Ember.UseFinalXform() ? 2 : 1));//Set index to the last item before final. Note final is requeried one last time. int index = int(m_Ember.TotalXformCount() - (m_Ember.UseFinalXform() ? 2 : 1));//Set index to the last item before final. Note final is requeried one last time.
FillXforms(index); FillXforms(index);
@ -262,7 +279,6 @@ void FractoriumEmberController<T>::DeleteXforms()
} }
void Fractorium::OnDeleteXformButtonClicked(bool checked) { m_Controller->DeleteXforms(); } void Fractorium::OnDeleteXformButtonClicked(bool checked) { m_Controller->DeleteXforms(); }
/// <summary> /// <summary>
/// Add a final xform to the ember and set it as the current xform. /// Add a final xform to the ember and set it as the current xform.
/// Will only take action if a final xform is not already present. /// Will only take action if a final xform is not already present.
@ -286,9 +302,7 @@ void FractoriumEmberController<T>::AddFinalXform()
}); });
} }
} }
void Fractorium::OnAddFinalXformButtonClicked(bool checked) { m_Controller->AddFinalXform(); } void Fractorium::OnAddFinalXformButtonClicked(bool checked) { m_Controller->AddFinalXform(); }
/// <summary> /// <summary>
/// Set the weight of the selected xforms. /// Set the weight of the selected xforms.
/// Called when weight spinner changes. /// Called when weight spinner changes.
@ -304,9 +318,7 @@ void FractoriumEmberController<T>::XformWeightChanged(double d)
}, eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL); }, eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL);
SetNormalizedWeightText(CurrentXform()); SetNormalizedWeightText(CurrentXform());
} }
void Fractorium::OnXformWeightChanged(double d) { m_Controller->XformWeightChanged(d); } void Fractorium::OnXformWeightChanged(double d) { m_Controller->XformWeightChanged(d); }
/// <summary> /// <summary>
/// Equalize the weights of all xforms in the ember. /// Equalize the weights of all xforms in the ember.
/// </summary> /// </summary>
@ -319,9 +331,7 @@ void FractoriumEmberController<T>::EqualizeWeights()
m_Fractorium->m_XformWeightSpin->setValue(xform->m_Weight);//Will trigger an update, so pass false to updateRender below. m_Fractorium->m_XformWeightSpin->setValue(xform->m_Weight);//Will trigger an update, so pass false to updateRender below.
}, eXformUpdate::UPDATE_CURRENT, false); }, eXformUpdate::UPDATE_CURRENT, false);
} }
void Fractorium::OnEqualWeightButtonClicked(bool checked) { m_Controller->EqualizeWeights(); } void Fractorium::OnEqualWeightButtonClicked(bool checked) { m_Controller->EqualizeWeights(); }
/// <summary> /// <summary>
/// Set the name of the current xform. /// Set the name of the current xform.
/// Update the corresponding xform checkbox text with the name. /// Update the corresponding xform checkbox text with the name.
@ -332,17 +342,16 @@ void Fractorium::OnEqualWeightButtonClicked(bool checked) { m_Controller->Equali
template <typename T> template <typename T>
void FractoriumEmberController<T>::XformNameChanged(int row, int col) void FractoriumEmberController<T>::XformNameChanged(int row, int col)
{ {
bool forceFinal = m_Fractorium->HaveFinal();
UpdateXform([&] (Xform<T>* xform) UpdateXform([&] (Xform<T>* xform)
{ {
int index = m_Ember.GetTotalXformIndex(xform); int index = m_Ember.GetTotalXformIndex(xform, forceFinal);
xform->m_Name = m_Fractorium->ui.XformWeightNameTable->item(row, col)->text().toStdString(); xform->m_Name = m_Fractorium->ui.XformWeightNameTable->item(row, col)->text().toStdString();
XformCheckboxAt(index, [&](QCheckBox * checkbox) { checkbox->setText(MakeXformCaption(index)); }); XformCheckboxAt(index, [&](QCheckBox * checkbox) { checkbox->setText(MakeXformCaption(index)); });
}, eXformUpdate::UPDATE_CURRENT, false); }, eXformUpdate::UPDATE_CURRENT, false);
FillSummary();//Manually update because this does not trigger a render, which is where this would normally be called. FillSummary();//Manually update because this does not trigger a render, which is where this would normally be called.
} }
void Fractorium::OnXformNameChanged(int row, int col) { m_Controller->XformNameChanged(row, col); } void Fractorium::OnXformNameChanged(int row, int col) { m_Controller->XformNameChanged(row, col); }
/// <summary> /// <summary>
/// Set the animate field of the selected xforms. /// Set the animate field of the selected xforms.
/// This has no effect on interactive rendering, it only sets a value /// This has no effect on interactive rendering, it only sets a value
@ -373,9 +382,7 @@ void FractoriumEmberController<T>::XformAnimateChanged(int state)
} }
}, false, eProcessAction::NOTHING, m_Fractorium->ApplyAll()); }, false, eProcessAction::NOTHING, m_Fractorium->ApplyAll());
} }
void Fractorium::OnXformAnimateCheckBoxStateChanged(int state) { m_Controller->XformAnimateChanged(state); } void Fractorium::OnXformAnimateCheckBoxStateChanged(int state) { m_Controller->XformAnimateChanged(state); }
/// <summary> /// <summary>
/// Fill all GUI widgets with values from the passed in xform. /// Fill all GUI widgets with values from the passed in xform.
/// </summary> /// </summary>
@ -399,7 +406,6 @@ void FractoriumEmberController<T>::FillWithXform(Xform<T>* xform)//Need to see w
FillAffineWithXform(xform, true); FillAffineWithXform(xform, true);
FillAffineWithXform(xform, false); FillAffineWithXform(xform, false);
} }
/// <summary> /// <summary>
/// Set the normalized weight of the current xform as the suffix text of the weight spinner. /// Set the normalized weight of the current xform as the suffix text of the weight spinner.
/// </summary> /// </summary>
@ -416,7 +422,6 @@ void FractoriumEmberController<T>::SetNormalizedWeightText(Xform<T>* xform)
m_Fractorium->m_XformWeightSpin->setSuffix(QString(" (") + QLocale::system().toString(double(m_NormalizedWeights[index]), 'g', 3) + ")"); m_Fractorium->m_XformWeightSpin->setSuffix(QString(" (") + QLocale::system().toString(double(m_NormalizedWeights[index]), 'g', 3) + ")");
} }
} }
/// <summary> /// <summary>
/// Determine whether the specified xform is the final xform in the ember. /// Determine whether the specified xform is the final xform in the ember.
/// </summary> /// </summary>
@ -427,7 +432,6 @@ bool FractoriumEmberController<T>::IsFinal(Xform<T>* xform)
{ {
return (m_Fractorium->HaveFinal() && (xform == m_Ember.FinalXform())); return (m_Fractorium->HaveFinal() && (xform == m_Ember.FinalXform()));
} }
/// <summary> /// <summary>
/// Fill the xforms combo box with the xforms in the current ember. /// Fill the xforms combo box with the xforms in the current ember.
/// Select the index passed in and fill all widgets with its values. /// Select the index passed in and fill all widgets with its values.
@ -498,9 +502,7 @@ void FractoriumEmberController<T>::FillXforms(int index)
m_Fractorium->OnSoloXformCheckBoxStateChanged(Qt::Unchecked); m_Fractorium->OnSoloXformCheckBoxStateChanged(Qt::Unchecked);
m_Fractorium->OnCurrentXformComboChanged(index);//Make sure the event gets called, because it won't if the zero index is already selected. m_Fractorium->OnCurrentXformComboChanged(index);//Make sure the event gets called, because it won't if the zero index is already selected.
} }
template class FractoriumEmberController<float>; template class FractoriumEmberController<float>;
#ifdef DO_DOUBLE #ifdef DO_DOUBLE
template class FractoriumEmberController<double>; template class FractoriumEmberController<double>;
#endif #endif

View File

@ -16,13 +16,13 @@ void Fractorium::InitXformsSelectUI()
/// Check all of the xform selection checkboxes. /// Check all of the xform selection checkboxes.
/// </summary> /// </summary>
/// <param name="checked">Ignored</param> /// <param name="checked">Ignored</param>
void Fractorium::OnXformsSelectAllButtonClicked(bool checked) { ForEachXformCheckbox([&](int i, QCheckBox * w) { w->setChecked(true); }); } void Fractorium::OnXformsSelectAllButtonClicked(bool checked) { ForEachXformCheckbox([&](int i, QCheckBox * w, bool isFinal) { w->setChecked(true); }); }
/// <summary> /// <summary>
/// Uncheck all of the xform selection checkboxes. /// Uncheck all of the xform selection checkboxes.
/// </summary> /// </summary>
/// <param name="checked">Ignored</param> /// <param name="checked">Ignored</param>
void Fractorium::OnXformsSelectNoneButtonClicked(bool checked) { ForEachXformCheckbox([&](int i, QCheckBox * w) { w->setChecked(false); }); } void Fractorium::OnXformsSelectNoneButtonClicked(bool checked) { ForEachXformCheckbox([&](int i, QCheckBox * w, bool isFinal) { w->setChecked(false); }); }
/// <summary> /// <summary>
/// Return whether the checkbox at the specified index is checked. /// Return whether the checkbox at the specified index is checked.
@ -67,10 +67,11 @@ void Fractorium::ClearXformsSelections()
template <typename T> template <typename T>
QString FractoriumEmberController<T>::MakeXformCaption(size_t i) QString FractoriumEmberController<T>::MakeXformCaption(size_t i)
{ {
bool isFinal = m_Ember.FinalXform() == m_Ember.GetTotalXform(i); bool forceFinal = m_Fractorium->HaveFinal();
bool isFinal = m_Ember.FinalXform() == m_Ember.GetTotalXform(i, forceFinal);
QString caption = isFinal ? "Final" : QString::number(i + 1); QString caption = isFinal ? "Final" : QString::number(i + 1);
if (auto xform = m_Ember.GetTotalXform(i)) if (auto xform = m_Ember.GetTotalXform(i, forceFinal))
if (!xform->m_Name.empty()) if (!xform->m_Name.empty())
caption += " (" + QString::fromStdString(xform->m_Name) + ")"; caption += " (" + QString::fromStdString(xform->m_Name) + ")";
@ -81,12 +82,13 @@ QString FractoriumEmberController<T>::MakeXformCaption(size_t i)
/// Function to perform the specified operation on every dynamically created xform selection checkbox. /// Function to perform the specified operation on every dynamically created xform selection checkbox.
/// </summary> /// </summary>
/// <param name="func">The operation to perform</param> /// <param name="func">The operation to perform</param>
void Fractorium::ForEachXformCheckbox(std::function<void(int, QCheckBox*)> func) void Fractorium::ForEachXformCheckbox(std::function<void(int, QCheckBox*, bool)> func)
{ {
int i = 0; int i = 0;
bool haveFinal = HaveFinal();
for (auto& cb : m_XformSelections) for (auto& cb : m_XformSelections)
func(i++, cb); func(i++, cb, haveFinal && cb == m_XformSelections.back());
} }
/// <summary> /// <summary>
@ -120,7 +122,8 @@ bool FractoriumEmberController<T>::XformCheckboxAt(int i, std::function<void(QCh
template <typename T> template <typename T>
bool FractoriumEmberController<T>::XformCheckboxAt(Xform<T>* xform, std::function<void(QCheckBox*)> func) bool FractoriumEmberController<T>::XformCheckboxAt(Xform<T>* xform, std::function<void(QCheckBox*)> func)
{ {
return XformCheckboxAt(m_Ember.GetTotalXformIndex(xform), func); bool forceFinal = m_Fractorium->HaveFinal();
return XformCheckboxAt(m_Ember.GetTotalXformIndex(xform, forceFinal), func);
} }
template class FractoriumEmberController<float>; template class FractoriumEmberController<float>;

View File

@ -215,7 +215,7 @@ void FractoriumEmberController<T>::VariationSpinBoxValueChanged(double d)//Would
if (isParam) if (isParam)
{ {
//Do not take action if the xform doesn't contain the variation which this param is part of. //Do not take action if the xform doesn't contain the variation which this param is part of.
if (ParametricVariation<T>* xformParVar = dynamic_cast<ParametricVariation<T>*>(xformVar))//The parametric cast of the xform's variation. if (auto xformParVar = dynamic_cast<ParametricVariation<T>*>(xformVar))//The parametric cast of the xform's variation.
if (xformParVar->SetParamVal(sender->ParamName().c_str(), d)) if (xformParVar->SetParamVal(sender->ParamName().c_str(), d))
update = true; update = true;
} }

View File

@ -290,6 +290,7 @@ void GLEmberController<T>::DrawAffines(bool pre, bool post)
auto ember = m_FractoriumEmberController->CurrentEmber(); auto ember = m_FractoriumEmberController->CurrentEmber();
bool dragging = m_DragState == eDragState::DragDragging; bool dragging = m_DragState == eDragState::DragDragging;
bool forceFinal = m_Fractorium->HaveFinal();
//Draw grid if control key is pressed. //Draw grid if control key is pressed.
if ((m_GL->hasFocus() && GetControl()) || m_Fractorium->DrawGrid()) if ((m_GL->hasFocus() && GetControl()) || m_Fractorium->DrawGrid())
@ -307,7 +308,7 @@ void GLEmberController<T>::DrawAffines(bool pre, bool post)
{ {
size_t i = 0; size_t i = 0;
while (auto xform = ember->GetTotalXform(i)) while (auto xform = ember->GetTotalXform(i, forceFinal))
{ {
bool selected = m_Fractorium->IsXformSelected(i++) || (dragging ? (m_SelectedXform == xform) : (m_HoverXform == xform)); bool selected = m_Fractorium->IsXformSelected(i++) || (dragging ? (m_SelectedXform == xform) : (m_HoverXform == xform));
DrawAffine(xform, true, selected); DrawAffine(xform, true, selected);
@ -322,7 +323,7 @@ void GLEmberController<T>::DrawAffines(bool pre, bool post)
{ {
size_t i = 0; size_t i = 0;
while (auto xform = ember->GetTotalXform(i)) while (auto xform = ember->GetTotalXform(i, forceFinal))
{ {
bool selected = m_Fractorium->IsXformSelected(i++) || (dragging ? (m_SelectedXform == xform) : (m_HoverXform == xform)); bool selected = m_Fractorium->IsXformSelected(i++) || (dragging ? (m_SelectedXform == xform) : (m_HoverXform == xform));
DrawAffine(xform, false, selected); DrawAffine(xform, false, selected);
@ -1035,6 +1036,8 @@ int GLEmberController<T>::UpdateHover(v3T& glCoords)
if (m_Fractorium->DrawXforms())//Don't bother checking anything if the user wants to see no xforms. if (m_Fractorium->DrawXforms())//Don't bother checking anything if the user wants to see no xforms.
{ {
bool forceFinal = m_Fractorium->HaveFinal();
//If there's a selected/current xform, check it first so it gets precedence over the others. //If there's a selected/current xform, check it first so it gets precedence over the others.
if (m_SelectedXform) if (m_SelectedXform)
{ {
@ -1047,12 +1050,13 @@ int GLEmberController<T>::UpdateHover(v3T& glCoords)
if (CheckXformHover(m_SelectedXform, glCoords, bestDist, checkSelPre, checkSelPost)) if (CheckXformHover(m_SelectedXform, glCoords, bestDist, checkSelPre, checkSelPost))
{ {
m_HoverXform = m_SelectedXform; m_HoverXform = m_SelectedXform;
bestIndex = int(ember->GetTotalXformIndex(m_SelectedXform)); bestIndex = int(ember->GetTotalXformIndex(m_SelectedXform, forceFinal));
} }
} }
//Check all xforms. //Check all xforms.
while (auto xform = ember->GetTotalXform(i))
while (auto xform = ember->GetTotalXform(i, forceFinal))
{ {
if (preAll || (pre && m_HoverXform == xform))//Only check pre affine if they are shown. if (preAll || (pre && m_HoverXform == xform))//Only check pre affine if they are shown.
{ {