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());
}
/// <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>
/// Copy this ember with optional padding xforms added.
/// </summary>
@ -280,12 +301,13 @@ public:
/// Delete the xform at the specified index, including the final one.
/// </summary>
/// <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>
bool DeleteTotalXform(size_t i)
bool DeleteTotalXform(size_t i, bool forceFinal = false)
{
if (DeleteXform(i))
{ }
else if (i == XformCount() && UseFinalXform())
else if (i == XformCount() && (forceFinal || UseFinalXform()))
m_FinalXform.Clear();
else
return false;
@ -310,13 +332,13 @@ public:
/// Get a pointer to the xform at the specified index, including the final one.
/// </summary>
/// <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>
Xform<T>* GetTotalXform(size_t i, bool forceFinal = false) const
{
if (i < XformCount())
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);
else
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.
/// </summary>
/// <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>
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++)
if (GetTotalXform(i) == xform)
if (GetTotalXform(i, forceFinal) == xform)
return intmax_t(i);
return -1;
@ -1468,7 +1491,7 @@ public:
inline const Xform<T>* FinalXform() const { return &m_FinalXform; }
inline Xform<T>* NonConstFinalXform() { return &m_FinalXform; }
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 T BlurCoef() { return m_BlurCoef; }
inline eScaleType ScaleType() const { return m_ScaleType; }

View File

@ -264,6 +264,8 @@ void FractoriumEmberController<T>::ApplyXmlSavingTemplate(Ember<T>& ember)
/// <summary>
/// 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>
/// <returns>True if the current ember contains a final xform, else false.</returns>
bool Fractorium::HaveFinal()

View File

@ -405,7 +405,7 @@ private:
//Xforms Selection.
void ClearXformsSelections();
void ForEachXformCheckbox(std::function<void(int, QCheckBox*)> func);
void ForEachXformCheckbox(std::function<void(int, QCheckBox*, bool)> func);
//Xaos.
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>
</property>
<property name="text">
<string>Random Color Speed</string>
<string>Random Color Speeds</string>
</property>
</widget>
</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)
{
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;
switch (updateType)
{
case eXformUpdate::UPDATE_SPECIFIC:
{
if (auto xform = m_Ember.GetTotalXform(index))
if (auto xform = m_Ember.GetTotalXform(index, forceFinal))
func(xform);
}
break;
case eXformUpdate::UPDATE_CURRENT:
{
if (auto xform = CurrentXform())
func(xform);
if (current)
func(current);
}
break;
case eXformUpdate::UPDATE_CURRENT_AND_SELECTED:
{
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())
{
@ -261,7 +262,7 @@ void FractoriumEmberController<T>::UpdateXform(std::function<void(Xform<T>*)> fu
{
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())
{
@ -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 (doFinal || !isCurrentFinal)//If do final, call func regardless. If not, only call if current is not final.
if (auto xform = CurrentXform())
func(xform);
if (current)
func(current);
}
break;
case eXformUpdate::UPDATE_ALL:
{
while (auto xform = m_Ember.GetTotalXform(i++))
while (auto xform = m_Ember.GetTotalXform(i++, forceFinal))
func(xform);
}
break;

View File

@ -69,7 +69,8 @@ void FractoriumEmberController<T>::FillSummary()
int vp = 4;
int vlen = 7;
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;
QColor color;
auto table = m_Fractorium->ui.SummaryTable;
@ -85,7 +86,7 @@ void FractoriumEmberController<T>::FillSummary()
QSize size(table->columnWidth(0), table->rowHeight(1) + 1);
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;
QString as = "Pre";
@ -233,5 +234,5 @@ void Fractorium::ErrorReportToQTextEdit(const vector<string>& errors, QTextEdit*
template class FractoriumEmberController<float>;
#ifdef DO_DOUBLE
template class FractoriumEmberController<double>;
template class FractoriumEmberController<double>;
#endif

View File

@ -428,7 +428,9 @@ void FractoriumEmberController<T>::Undo()
{
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_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...
@ -452,7 +454,9 @@ void FractoriumEmberController<T>::Redo()
{
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_UndoIndex = std::min<size_t>(m_UndoIndex + 1, m_UndoList.size() - 1);
SetEmber(m_UndoList[m_UndoIndex], true, false);
@ -740,10 +744,11 @@ void Fractorium::OnActionResetScale(bool checked)
template <typename T>
void FractoriumEmberController<T>::AddReflectiveSymmetry()
{
bool forceFinal = m_Fractorium->HaveFinal();
Update([&]()
{
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));
});
}
@ -757,10 +762,11 @@ void Fractorium::OnActionAddReflectiveSymmetry(bool checked) { m_Controller->Add
template <typename T>
void FractoriumEmberController<T>::AddRotationalSymmetry()
{
bool forceFinal = m_Fractorium->HaveFinal();
Update([&]()
{
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));
});
}
@ -774,10 +780,11 @@ void Fractorium::OnActionAddRotationalSymmetry(bool checked) { m_Controller->Add
template <typename T>
void FractoriumEmberController<T>::AddBothSymmetry()
{
bool forceFinal = m_Fractorium->HaveFinal();
Update([&]()
{
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));
});
}

View File

@ -323,8 +323,9 @@ void FractoriumEmberController<T>::PaletteEditorButtonClicked()
Palette<float> prevPal = m_TempPalette;
ed->SetPalette(m_TempPalette);
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;
ed->SetColorIndices(colorIndices);

View File

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

View File

@ -46,7 +46,8 @@ void Fractorium::InitXformsUI()
template <typename T>
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>
@ -67,7 +68,9 @@ void Fractorium::CurrentXform(uint i)
template <typename T>
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);
m_GLController->SetSelectedXform(xform);
@ -95,13 +98,14 @@ void Fractorium::OnCurrentXformComboChanged(int index) { m_Controller->CurrentXf
template <typename T>
void FractoriumEmberController<T>::AddXform()
{
bool forceFinal = m_Fractorium->HaveFinal();
Update([&]()
{
Xform<T> newXform;
newXform.m_Weight = 0.25;
newXform.m_ColorX = m_Rand.Frand01<T>();
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);
});
}
@ -119,6 +123,7 @@ void Fractorium::OnAddXformButtonClicked(bool checked) { m_Controller->AddXform(
template <typename T>
void FractoriumEmberController<T>::AddLinkedXform()
{
bool forceFinal = m_Fractorium->HaveFinal();
UpdateXform([&](Xform<T>* xform)
{
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->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);
FillXaos();
}, eXformUpdate::UPDATE_CURRENT);
@ -165,6 +170,7 @@ void Fractorium::OnAddLinkedXformButtonClicked(bool checked) { m_Controller->Add
template <typename T>
void FractoriumEmberController<T>::DuplicateXform()
{
bool forceFinal = m_Fractorium->HaveFinal();
vector<Xform<T>> vec;
vec.reserve(m_Ember.XformCount());
UpdateXform([&] (Xform<T>* xform)
@ -176,7 +182,7 @@ void FractoriumEmberController<T>::DuplicateXform()
for (auto& it : vec)
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.
});
}
@ -211,49 +217,60 @@ void Fractorium::OnClearXformButtonClicked(bool checked) { m_Controller->ClearXf
template <typename T>
void FractoriumEmberController<T>::DeleteXforms()
{
int offset = 0, current = 0, checked = 0;
bool haveFinal = false;
size_t count;
bool removed = false;
bool haveFinal = m_Fractorium->HaveFinal();
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
//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)
//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();
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())
if (!w->isChecked())//Keep if not checked.
{
m_Ember.DeleteTotalXform(i - offset);//Subtract offset to account for previously deleted xforms.
offset++;
if (isFinal)
finalXform = m_Ember.NonConstFinalXform();
else if (auto xform = m_Ember.GetXform(i))
xforms.push_back(*xform);
}
});
current = combo->currentIndex();
count = m_Ember.TotalXformCount();
haveFinal = m_Ember.UseFinalXform();//Requery again.
//They might not have selected any checkboxes, in which case just delete the current.
auto current = combo->currentIndex();
auto count = m_Ember.TotalXformCount();
bool anySelected = xforms.size() < m_Ember.XformCount();
bool finalSelected = !finalXform && haveFinal;
//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))
if (!anySelected && !finalSelected)
{
m_Ember.DeleteTotalXform(current);
offset++;
//Disallow deleting the only remaining non-final xform.
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.
FillXforms(index);
@ -262,7 +279,6 @@ void FractoriumEmberController<T>::DeleteXforms()
}
void Fractorium::OnDeleteXformButtonClicked(bool checked) { m_Controller->DeleteXforms(); }
/// <summary>
/// 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.
@ -286,9 +302,7 @@ void FractoriumEmberController<T>::AddFinalXform()
});
}
}
void Fractorium::OnAddFinalXformButtonClicked(bool checked) { m_Controller->AddFinalXform(); }
/// <summary>
/// Set the weight of the selected xforms.
/// Called when weight spinner changes.
@ -304,9 +318,7 @@ void FractoriumEmberController<T>::XformWeightChanged(double d)
}, eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL);
SetNormalizedWeightText(CurrentXform());
}
void Fractorium::OnXformWeightChanged(double d) { m_Controller->XformWeightChanged(d); }
/// <summary>
/// Equalize the weights of all xforms in the ember.
/// </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.
}, eXformUpdate::UPDATE_CURRENT, false);
}
void Fractorium::OnEqualWeightButtonClicked(bool checked) { m_Controller->EqualizeWeights(); }
/// <summary>
/// Set the name of the current xform.
/// Update the corresponding xform checkbox text with the name.
@ -332,17 +342,16 @@ void Fractorium::OnEqualWeightButtonClicked(bool checked) { m_Controller->Equali
template <typename T>
void FractoriumEmberController<T>::XformNameChanged(int row, int col)
{
bool forceFinal = m_Fractorium->HaveFinal();
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();
XformCheckboxAt(index, [&](QCheckBox * checkbox) { checkbox->setText(MakeXformCaption(index)); });
}, eXformUpdate::UPDATE_CURRENT, false);
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); }
/// <summary>
/// Set the animate field of the selected xforms.
/// 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());
}
void Fractorium::OnXformAnimateCheckBoxStateChanged(int state) { m_Controller->XformAnimateChanged(state); }
/// <summary>
/// Fill all GUI widgets with values from the passed in xform.
/// </summary>
@ -399,7 +406,6 @@ void FractoriumEmberController<T>::FillWithXform(Xform<T>* xform)//Need to see w
FillAffineWithXform(xform, true);
FillAffineWithXform(xform, false);
}
/// <summary>
/// Set the normalized weight of the current xform as the suffix text of the weight spinner.
/// </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) + ")");
}
}
/// <summary>
/// Determine whether the specified xform is the final xform in the ember.
/// </summary>
@ -427,7 +432,6 @@ bool FractoriumEmberController<T>::IsFinal(Xform<T>* xform)
{
return (m_Fractorium->HaveFinal() && (xform == m_Ember.FinalXform()));
}
/// <summary>
/// Fill the xforms combo box with the xforms in the current ember.
/// 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->OnCurrentXformComboChanged(index);//Make sure the event gets called, because it won't if the zero index is already selected.
}
template class FractoriumEmberController<float>;
#ifdef DO_DOUBLE
template class FractoriumEmberController<double>;
#endif
template class FractoriumEmberController<double>;
#endif

View File

@ -16,13 +16,13 @@ void Fractorium::InitXformsSelectUI()
/// Check all of the xform selection checkboxes.
/// </summary>
/// <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>
/// Uncheck all of the xform selection checkboxes.
/// </summary>
/// <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>
/// Return whether the checkbox at the specified index is checked.
@ -67,10 +67,11 @@ void Fractorium::ClearXformsSelections()
template <typename T>
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);
if (auto xform = m_Ember.GetTotalXform(i))
if (auto xform = m_Ember.GetTotalXform(i, forceFinal))
if (!xform->m_Name.empty())
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.
/// </summary>
/// <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;
bool haveFinal = HaveFinal();
for (auto& cb : m_XformSelections)
func(i++, cb);
func(i++, cb, haveFinal && cb == m_XformSelections.back());
}
/// <summary>
@ -120,7 +122,8 @@ bool FractoriumEmberController<T>::XformCheckboxAt(int i, std::function<void(QCh
template <typename T>
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>;

View File

@ -215,7 +215,7 @@ void FractoriumEmberController<T>::VariationSpinBoxValueChanged(double d)//Would
if (isParam)
{
//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))
update = true;
}

View File

@ -290,6 +290,7 @@ void GLEmberController<T>::DrawAffines(bool pre, bool post)
auto ember = m_FractoriumEmberController->CurrentEmber();
bool dragging = m_DragState == eDragState::DragDragging;
bool forceFinal = m_Fractorium->HaveFinal();
//Draw grid if control key is pressed.
if ((m_GL->hasFocus() && GetControl()) || m_Fractorium->DrawGrid())
@ -307,7 +308,7 @@ void GLEmberController<T>::DrawAffines(bool pre, bool post)
{
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));
DrawAffine(xform, true, selected);
@ -322,7 +323,7 @@ void GLEmberController<T>::DrawAffines(bool pre, bool post)
{
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));
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.
{
bool forceFinal = m_Fractorium->HaveFinal();
//If there's a selected/current xform, check it first so it gets precedence over the others.
if (m_SelectedXform)
{
@ -1047,12 +1050,13 @@ int GLEmberController<T>::UpdateHover(v3T& glCoords)
if (CheckXformHover(m_SelectedXform, glCoords, bestDist, checkSelPre, checkSelPost))
{
m_HoverXform = m_SelectedXform;
bestIndex = int(ember->GetTotalXformIndex(m_SelectedXform));
bestIndex = int(ember->GetTotalXformIndex(m_SelectedXform, forceFinal));
}
}
//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.
{