mirror of
https://bitbucket.org/mfeemster/fractorium.git
synced 2025-01-21 21:20:07 -05:00
fcd060976c
-Support 4k monitors, and in general, properly scale any monitor that is not HD. -Allow for a spatial filter of radius zero, which means do not use a spatial filter. -Add new variations: concentric, cpow3, helicoid, helix, rand_cubes, sphereblur. -Use a new method for computing elliptic which is more precise. Developed by Discord user Claude. -Remove the 8 variation per xform limitation on the GPU. -Allow for loading the last flame file on startup, rather than randoms. -Use two different default quality values in the interactive renderer, one each for CPU and GPU. -Creating linked xforms was using non-standard behavior. Make it match Apo and also support creating multiple linked xforms at once. --Bug fixes -No variations in an xform used to have the same behavior as a single linear variation with weight 1. While sensible, this breaks backward compatibility. No variations now sets the output point to zeroes. -Prevent crashing the program when adjusting a value on the main window while a final render is in progress. -The xaos table was inverted. --Code changes -Convert projects to Visual Studio 2017. -Change bad vals from +- 1e10 to +-1e20. -Reintroduce the symmetry tag in xforms for legacy support in programs that do not use color_speed. -Compiler will not let us use default values in templated member functions anymore.
545 lines
21 KiB
C++
545 lines
21 KiB
C++
#include "FractoriumPch.h"
|
|
#include "Fractorium.h"
|
|
|
|
/// <summary>
|
|
/// Initialize the xforms UI.
|
|
/// </summary>
|
|
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);
|
|
connect(ui.ClearXformButton, SIGNAL(clicked(bool)), this, SLOT(OnClearXformButtonClicked(bool)), Qt::QueuedConnection);
|
|
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);
|
|
m_XformWeightSpin->setDecimals(3);
|
|
m_XformWeightSpin->SmallStep(0.001);
|
|
m_XformWeightSpinnerButtonWidget = new SpinnerButtonWidget(m_XformWeightSpin, "=", 20, 19, ui.XformWeightNameTable);
|
|
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.
|
|
ui.AddXformButton->setIconSize(QSize(16, 16));
|
|
ui.DuplicateXformButton->setIconSize(QSize(16, 16));
|
|
ui.ClearXformButton->setIconSize(QSize(16, 16));
|
|
ui.DeleteXformButton->setIconSize(QSize(16, 16));
|
|
ui.AddFinalXformButton->setIconSize(QSize(16, 16));
|
|
ui.CurrentXformCombo->setIconSize(QSize(16, 16));
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the current xform.
|
|
/// </summary>
|
|
/// <returns>The current xform as specified by the current xform combo box index. nullptr if out of range (should never happen).</returns>
|
|
template <typename T>
|
|
Xform<T>* FractoriumEmberController<T>::CurrentXform()
|
|
{
|
|
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>
|
|
/// Set the current xform to the index passed in.
|
|
/// </summary>
|
|
/// <param name="i">The index to set the current xform to</param>
|
|
void Fractorium::CurrentXform(uint i)
|
|
{
|
|
if (i < uint(ui.CurrentXformCombo->count()))
|
|
ui.CurrentXformCombo->setCurrentIndex(i);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the current xform and populate all GUI widgets.
|
|
/// Called when the current xform combo box index changes.
|
|
/// </summary>
|
|
/// <param name="index">The selected combo box index</param>
|
|
template <typename T>
|
|
void FractoriumEmberController<T>::CurrentXformComboChanged(int index)
|
|
{
|
|
bool forceFinal = m_Fractorium->HaveFinal();
|
|
|
|
if (auto xform = m_Ember.GetTotalXform(index, forceFinal))
|
|
{
|
|
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);
|
|
m_Fractorium->ui.AddLinkedXformButton->setEnabled(enable);
|
|
m_Fractorium->ui.AddFinalXformButton->setEnabled(!m_Ember.UseFinalXform());
|
|
}
|
|
}
|
|
|
|
void Fractorium::OnCurrentXformComboChanged(int index) { m_Controller->CurrentXformComboChanged(index); }
|
|
|
|
/// <summary>
|
|
/// Add an empty xform in the current ember and set it as the current xform.
|
|
/// Called when the add xform button is clicked.
|
|
/// Resets the rendering process.
|
|
/// </summary>
|
|
/// <param name="checked">Ignored</param>
|
|
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>();
|
|
newXform.AddVariation(m_VariationList->GetVariationCopy(eVariationId::VAR_LINEAR));
|
|
m_Ember.AddXform(newXform);
|
|
int index = int(m_Ember.TotalXformCount(forceFinal) - (forceFinal ? 2 : 1));//Set index to the last item before final.
|
|
FillXforms(index);
|
|
});
|
|
}
|
|
|
|
void Fractorium::OnAddXformButtonClicked(bool checked) { m_Controller->AddXform(); }
|
|
|
|
/// <summary>
|
|
/// Add a new linked xform in the current ember and set it as the current xform.
|
|
/// Linked means:
|
|
/// Add an xform whose xaos values are:
|
|
/// From: All xaos values from the current xform are zero when going to any xform but the new one added, which is 1.
|
|
/// To: The xaos value coming from the current xform is 1 and the xaos values from all other xforms are 0, when going to the newly added xform.
|
|
/// Take different action when a single xform is selected vs. multiple.
|
|
/// Single: Copy current xform's xaos values to the new one.
|
|
/// Multiple: Set new xform's xaos values to 1, and except the last entry which is 0.
|
|
/// Called when the add xform button is clicked.
|
|
/// Resets the rendering process.
|
|
/// </summary>
|
|
/// <param name="checked">Ignored</param>
|
|
template <typename T>
|
|
void FractoriumEmberController<T>::AddLinkedXform()
|
|
{
|
|
bool hasAdded = false;
|
|
bool forceFinal = m_Fractorium->HaveFinal();
|
|
auto selCount = m_Fractorium->SelectedXformCount(false);
|
|
|
|
if (!selCount)//If none explicitly selected, use current.
|
|
selCount = 1;
|
|
|
|
Ember<T> ember = m_Ember;
|
|
m_Ember.Reserve(m_Ember.XformCount() + 1);//Doing this ahead of time ensures pointers remain valid.
|
|
auto iterCount = 0;
|
|
UpdateXform([&](Xform<T>* xform)
|
|
{
|
|
//Covers very strange case where final is selected, but initially not considered because final is excluded,
|
|
//but after adding the new xform, it thinks its selected index is non-final.
|
|
if (iterCount < selCount)
|
|
{
|
|
size_t i, count = m_Ember.XformCount();
|
|
|
|
if (!hasAdded)
|
|
{
|
|
Xform<T> newXform;
|
|
newXform.m_Weight = 0.5;
|
|
newXform.m_Opacity = xform->m_Opacity;
|
|
newXform.m_ColorSpeed = 0;
|
|
newXform.m_ColorX = 0;
|
|
//newXform.m_ColorY = xform->m_ColorY;
|
|
newXform.AddVariation(m_VariationList->GetVariationCopy(eVariationId::VAR_LINEAR));
|
|
|
|
//Set all of the new xform's xaos values to the selected xform's xaos values,
|
|
//then set the selected xform's xaos values to 0.
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
if (selCount == 1)
|
|
newXform.SetXaos(i, xform->Xaos(i));
|
|
else
|
|
newXform.SetXaos(i, 1);
|
|
}
|
|
|
|
//Add the new xform and update the total count.
|
|
m_Ember.AddXform(newXform);
|
|
count = m_Ember.XformCount();
|
|
|
|
//Set the xaos for all xforms pointing to the new one to zero.
|
|
//Will set the last element of all linking and non-linking xforms, including the one we just added.
|
|
//Linking xforms will have their last xaos element set to 1 below.
|
|
for (i = 0; i < count; i++)
|
|
if (auto xf = m_Ember.GetXform(i))
|
|
xf->SetXaos(count - 1, 0);
|
|
|
|
hasAdded = true;
|
|
}
|
|
|
|
//Linking xform, so set all xaos elements to 0, except the last.
|
|
for (i = 0; i < count - 1; i++)
|
|
xform->SetXaos(i, 0);
|
|
|
|
xform->SetXaos(count - 1, 1);//Set the xaos value for the linking xform pointing to the new one to one.
|
|
xform->m_Opacity = 0;//Clear the opacity of the all linking xform.
|
|
iterCount++;
|
|
}
|
|
}, eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL);
|
|
//Now update the GUI.
|
|
int index = int(m_Ember.TotalXformCount(forceFinal) - (forceFinal ? 2 : 1));//Set index to the last item before final.
|
|
FillXforms(index);
|
|
FillXaos();
|
|
}
|
|
|
|
void Fractorium::OnAddLinkedXformButtonClicked(bool checked) { m_Controller->AddLinkedXform(); }
|
|
|
|
/// <summary>
|
|
/// Duplicate the specified xforms in the current ember, and set the last one as the current xform.
|
|
/// Called when the duplicate xform button is clicked.
|
|
/// Resets the rendering process.
|
|
/// </summary>
|
|
/// <param name="checked">Ignored</param>
|
|
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)
|
|
{
|
|
vec.push_back(*xform);
|
|
}, eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL, false);
|
|
Update([&]()
|
|
{
|
|
for (auto& it : vec)
|
|
m_Ember.AddXform(it);
|
|
|
|
int index = int(m_Ember.TotalXformCount(forceFinal) - (forceFinal ? 2 : 1));//Set index to the last item before final.
|
|
FillXforms(index);//Handles xaos.
|
|
});
|
|
}
|
|
|
|
void Fractorium::OnDuplicateXformButtonClicked(bool checked) { m_Controller->DuplicateXform(); }
|
|
|
|
/// <summary>
|
|
/// Clear all variations from the selected xforms. Affine, palette and xaos are left untouched.
|
|
/// Called when the clear xform button is clicked.
|
|
/// Resets the rendering process.
|
|
/// </summary>
|
|
/// <param name="checked">Ignored</param>
|
|
template <typename T>
|
|
void FractoriumEmberController<T>::ClearXform()
|
|
{
|
|
UpdateXform([&] (Xform<T>* xform)
|
|
{
|
|
xform->ClearAndDeleteVariations();//Note xaos is left alone.
|
|
}, eXformUpdate::UPDATE_SELECTED);
|
|
FillVariationTreeWithXform(CurrentXform());
|
|
}
|
|
|
|
void Fractorium::OnClearXformButtonClicked(bool checked) { m_Controller->ClearXform(); }
|
|
|
|
/// <summary>
|
|
/// Delete the selected xforms.
|
|
/// Will not delete the last remaining non-final xform.
|
|
/// Called when the delete xform button is clicked.
|
|
/// Resets the rendering process.
|
|
/// </summary>
|
|
/// <param name="checked">Ignored</param>
|
|
template <typename T>
|
|
void FractoriumEmberController<T>::DeleteXforms()
|
|
{
|
|
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.
|
|
//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)
|
|
{
|
|
if (!w->isChecked())//Keep if not checked.
|
|
{
|
|
if (isFinal)
|
|
finalXform = m_Ember.NonConstFinalXform();
|
|
else if (auto xform = m_Ember.GetXform(i))
|
|
xforms.push_back(*xform);
|
|
}
|
|
});
|
|
//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 (!anySelected && !finalSelected)
|
|
{
|
|
//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 (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);
|
|
UpdateRender();
|
|
}
|
|
}
|
|
|
|
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.
|
|
/// Called when the add final xform button is clicked.
|
|
/// Resets the rendering process.
|
|
/// </summary>
|
|
/// <param name="checked">Ignored</param>
|
|
template <typename T>
|
|
void FractoriumEmberController<T>::AddFinalXform()
|
|
{
|
|
//Check to see if a final xform is already present.
|
|
if (!m_Fractorium->HaveFinal())
|
|
{
|
|
Update([&]()
|
|
{
|
|
Xform<T> final;
|
|
final.AddVariation(m_VariationList->GetVariationCopy(eVariationId::VAR_LINEAR));//Just a placeholder so other parts of the code don't see it as being empty.
|
|
m_Ember.SetFinalXform(final);
|
|
int index = int(m_Ember.TotalXformCount() - 1);//Set index to the last item.
|
|
FillXforms(index);
|
|
});
|
|
}
|
|
}
|
|
void Fractorium::OnAddFinalXformButtonClicked(bool checked) { m_Controller->AddFinalXform(); }
|
|
/// <summary>
|
|
/// Set the weight of the selected xforms.
|
|
/// Called when weight spinner changes.
|
|
/// Resets the rendering process.
|
|
/// </summary>
|
|
/// <param name="d">The weight</param>
|
|
template <typename T>
|
|
void FractoriumEmberController<T>::XformWeightChanged(double d)
|
|
{
|
|
UpdateXform([&] (Xform<T>* xform)
|
|
{
|
|
xform->m_Weight = 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>
|
|
template <typename T>
|
|
void FractoriumEmberController<T>::EqualizeWeights()
|
|
{
|
|
UpdateXform([&] (Xform<T>* xform)
|
|
{
|
|
m_Ember.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.
|
|
/// Called when the user types in the name cell of the table.
|
|
/// </summary>
|
|
/// <param name="row">The row of the cell</param>
|
|
/// <param name="col">The col of the cell</param>
|
|
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, 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
|
|
/// 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)
|
|
{
|
|
bool final = IsFinal(CurrentXform());
|
|
auto index = m_Fractorium->ui.CurrentXformCombo->currentIndex();
|
|
UpdateAll([&](Ember<T>& ember)
|
|
{
|
|
if (final)//If the current xform was final, only apply to other embers which also have a final xform.
|
|
{
|
|
if (ember.UseFinalXform())
|
|
{
|
|
auto xform = ember.NonConstFinalXform();
|
|
xform->m_Animate = state > 0 ? 1 : 0;
|
|
}
|
|
}
|
|
else//Current was not final, so apply to other embers which have a non-final xform at this index.
|
|
{
|
|
if (auto xform = ember.GetXform(index))
|
|
xform->m_Animate = state > 0 ? 1 : 0;
|
|
}
|
|
}, 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>
|
|
/// <param name="xform">The xform whose values will be used to populate the widgets</param>
|
|
template <typename T>
|
|
void FractoriumEmberController<T>::FillWithXform(Xform<T>* xform)//Need to see where all this is called from and sync with FillXform(). Maybe rename the latter.
|
|
{
|
|
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 (auto item = m_Fractorium->ui.XformWeightNameTable->item(0, 1))
|
|
{
|
|
m_Fractorium->ui.XformWeightNameTable->blockSignals(true);
|
|
item->setText(QString::fromStdString(xform->m_Name));
|
|
m_Fractorium->ui.XformWeightNameTable->blockSignals(false);
|
|
}
|
|
|
|
FillVariationTreeWithXform(xform);
|
|
FillColorWithXform(xform);
|
|
FillAffineWithXform(xform, true);
|
|
FillAffineWithXform(xform, false);
|
|
}
|
|
/// <summary>
|
|
/// Set the normalized weight of the current xform as the suffix text of the weight spinner.
|
|
/// </summary>
|
|
/// <param name="xform">The current xform whose normalized weight will be shown</param>
|
|
template <typename T>
|
|
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())
|
|
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>
|
|
/// <param name="xform">The xform to examine</param>
|
|
/// <returns>True if final, else false.</returns>
|
|
template <typename T>
|
|
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.
|
|
/// Also dynamically generate a checkbox for each xform which will allow the user
|
|
/// to select which xforms to apply operations to.
|
|
/// </summary>
|
|
/// <param name="index">The index to select after populating, default 0.</param>
|
|
template <typename T>
|
|
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);
|
|
|
|
//Fill combo box and create new checkboxes.
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
combo->addItem(ToString(i + 1));
|
|
combo->setItemIcon(i, m_Fractorium->m_XformComboIcons[i % XFORM_COLOR_COUNT]);
|
|
}
|
|
|
|
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);
|
|
QObject::connect(cb1, &QCheckBox::stateChanged, [&](int state) { m_Fractorium->ui.GLDisplay->update(); });//Ensure circles are drawn immediately after toggle.
|
|
QObject::connect(cb2, &QCheckBox::stateChanged, [&](int state) { m_Fractorium->ui.GLDisplay->update(); });
|
|
m_Fractorium->m_XformSelections.push_back(cb1);
|
|
m_Fractorium->m_XformSelections.push_back(cb2);
|
|
m_Fractorium->m_XformsSelectionLayout->addRow(cb1, cb2);
|
|
i += 2;
|
|
}
|
|
else if (i < count)
|
|
{
|
|
auto cb = new QCheckBox(MakeXformCaption(i), m_Fractorium);
|
|
QObject::connect(cb, &QCheckBox::stateChanged, [&](int state) { m_Fractorium->ui.GLDisplay->update(); });
|
|
m_Fractorium->m_XformSelections.push_back(cb);
|
|
m_Fractorium->m_XformsSelectionLayout->addRow(cb, new QWidget(m_Fractorium));
|
|
i++;
|
|
}
|
|
}
|
|
|
|
//Special case for final xform.
|
|
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);
|
|
}
|
|
|
|
m_Fractorium->m_XformsSelectionLayout->blockSignals(false);
|
|
combo->blockSignals(false);
|
|
|
|
if (index < combo->count())
|
|
combo->setCurrentIndex(index);
|
|
|
|
m_Fractorium->FillXaosTable();
|
|
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 |