mirror of
https://bitbucket.org/mfeemster/fractorium.git
synced 2025-02-01 18:40:12 -05:00
018ba26b5f
-Add support for multiple GPU devices. --These options are present in the command line and in Fractorium. -Change scheme of specifying devices from platform,device to just total device index. --Single number on the command line. --Change from combo boxes for device selection to a table of all devices in Fractorium. -Temporal samples defaults to 100 instead of 1000 which was needless overkill. --Bug fixes -EmberAnimate, EmberRender, FractoriumSettings, FinalRenderDialog: Fix wrong order of arguments to Clamp() when assigning thread priority. -VariationsDC.h: Fix NVidia OpenCL compilation error in DCTriangleVariation. -FractoriumXformsColor.cpp: Checking for null pixmap pointer is not enough, must also check if the underlying buffer is null via call to QPixmap::isNull(). --Code changes -Ember.h: Add case for FLAME_MOTION_NONE and default in ApplyFlameMotion(). -EmberMotion.h: Call base constructor. -EmberPch.h: #pragma once only on Windows. -EmberToXml.h: --Handle different types of exceptions. --Add default cases to ToString(). -Isaac.h: Remove unused variable in constructor. -Point.h: Call base constructor in Color(). -Renderer.h/cpp: --Add bool to Alloc() to only allocate memory for the histogram. Needed for multi-GPU. --Make CoordMap() return a const ref, not a pointer. -SheepTools.h: --Use 64-bit types like the rest of the code already does. --Fix some comment misspellings. -Timing.h: Make BeginTime(), EndTime(), ElapsedTime() and Format() be const functions. -Utils.h: --Add new functions Equal() and Split(). --Handle more exception types in ReadFile(). --Get rid of most legacy blending of C and C++ argument parsing. -XmlToEmber.h: --Get rid of most legacy blending of C and C++ code from flam3. --Remove some unused variables. -EmberAnimate: --Support multi-GPU processing that alternates full frames between devices. --Use OpenCLInfo instead of OpenCLWrapper for --openclinfo option. --Remove bucketT template parameter, and hard code float in its place. --If a render fails, exit since there is no point in continuing an animation with a missing frame. --Pass variables to threaded save better, which most likely fixes a very subtle bug that existed before. --Remove some unused variables. -EmberGenome, EmberRender: --Support multi-GPU processing that alternates full frames between devices. --Use OpenCLInfo instead of OpenCLWrapper for --openclinfo option. --Remove bucketT template parameter, and hard code float in its place. -EmberRender: --Support multi-GPU processing that alternates full frames between devices. --Use OpenCLInfo instead of OpenCLWrapper for --openclinfo option. --Remove bucketT template parameter, and hard code float in its place. --Only print values when not rendering with OpenCL, since they're always 0 in that case. -EmberCLPch.h: --#pragma once only on Windows. --#include <atomic>. -IterOpenCLKernelCreator.h: Add new kernel for summing two histograms. This is needed for multi-GPU. -OpenCLWrapper.h: --Move all OpenCL info related code into its own class OpenCLInfo. --Add members to cache the values of global memory size and max allocation size. -RendererCL.h/cpp: --Redesign to accomodate multi-GPU. --Constructor now takes a vector of devices. --Remove DumpErrorReport() function, it's handled in the base. --ClearBuffer(), ReadPoints(), WritePoints(), ReadHist() and WriteHist() now optionally take a device index as a parameter. --MakeDmap() override and m_DmapCL member removed because it no longer applies since the histogram is always float since the last commit. --Add new function SumDeviceHist() to sum histograms from two devices by first copying to a temporary on the host, then a temporary on the device, then summing. --m_Calls member removed, as it's now per-device. --OpenCLWrapper removed. --m_Seeds member is now a vector of vector of seeds, to accomodate a separate and different array of seeds for each device. --Added member m_Devices, a vector of unique_ptr of RendererCLDevice. -EmberCommon.h --Added Devices() function to convert from a vector of device indices to a vector of platform,device indices. --Changed CreateRenderer() to accept a vector of devices to create a single RendererCL which will split work across multiple devices. --Added CreateRenderers() function to accept a vector of devices to create multiple RendererCL, each which will render on a single device. --Add more comments to some existing functions. -EmberCommonPch.h: #pragma once only on Windows. -EmberOptions.h: --Remove --platform option, it's just sequential device number now with the --device option. --Make --out be OPT_USE_RENDER instead of OPT_RENDER_ANIM since it's an error condition when animating. It makes no sense to write all frames to a single image. --Add Devices() function to parse comma separated --device option string and return a vector of device indices. --Make int and uint types be 64-bit, so intmax_t and size_t. --Make better use of macros. -JpegUtils.h: Make string parameters to WriteJpeg() and WritePng() be const ref. -All project files: Turn off buffer security check option in Visual Studio (/Gs-) -deployment.pri: Remove the line OTHER_FILES +=, it's pointless and was causing problems. -Ember.pro, EmberCL.pro: Add CONFIG += plugin, otherwise it wouldn't link. -EmberCL.pro: Add new files for multi-GPU support. -build_all.sh: use -j4 and QMAKE=${QMAKE:/usr/bin/qmake} -shared_settings.pri: -Add version string. -Remove old DESTDIR definitions. -Add the following lines or else nothing would build: CONFIG(release, debug|release) { CONFIG += warn_off DESTDIR = ../../../Bin/release } CONFIG(debug, debug|release) { DESTDIR = ../../../Bin/debug } QMAKE_POST_LINK += $$quote(cp --update ../../../Data/flam3-palettes.xml $${DESTDIR}$$escape_expand(\n\t)) LIBS += -L/usr/lib -lpthread -AboutDialog.ui: Another futile attempt to make it look correct on Linux. -FinalRenderDialog.h/cpp: --Add support for multi-GPU. --Change from combo boxes for device selection to a table of all devices. --Ensure device selection makes sense. --Remove "FinalRender" prefix of various function names, it's implied given the context. -FinalRenderEmberController.h/cpp: --Add support for multi-GPU. --Change m_FinishedImageCount to be atomic. --Move CancelRender() from the base to FinalRenderEmberController<T>. --Refactor RenderComplete() to omit any progress related functionality or image saving since it can be potentially ran in a thread. --Consolidate setting various renderer fields into SyncGuiToRenderer(). -Fractorium.cpp: Allow for resizing of the options dialog to show the entire device table. -FractoriumCommon.h: Add various functions to handle a table showing the available OpenCL devices on the system. -FractoriumEmberController.h/cpp: Remove m_FinalImageIndex, it's no longer needed. -FractoriumRender.cpp: Scale the interactive sub batch count and quality by the number of devices used. -FractoriumSettings.h/cpp: --Temporal samples defaults to 100 instead of 1000 which was needless overkill. --Add multi-GPU support, remove old device,platform pair. -FractoriumToolbar.cpp: Disable OpenCL toolbar button if there are no devices present on the system. -FractoriumOptionsDialog.h/cpp: --Add support for multi-GPU. --Consolidate more assignments in DataToGui(). --Enable/disable CPU/OpenCL items in response to OpenCL checkbox event. -Misc: Convert almost everything to size_t for unsigned, intmax_t for signed.
392 lines
14 KiB
C++
392 lines
14 KiB
C++
#include "FractoriumPch.h"
|
|
#include "Fractorium.h"
|
|
|
|
/// <summary>
|
|
/// Initialize the xforms variations UI.
|
|
/// </summary>
|
|
void Fractorium::InitXformsVariationsUI()
|
|
{
|
|
auto tree = ui.VariationsTree;
|
|
|
|
tree->clear();
|
|
tree->header()->setSectionsClickable(true);
|
|
connect(tree->header(), SIGNAL(sectionClicked(int)), this, SLOT(OnTreeHeaderSectionClicked(int)));
|
|
connect(ui.VariationsFilterLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(OnVariationsFilterLineEditTextChanged(const QString&)));
|
|
connect(ui.VariationsFilterClearButton, SIGNAL(clicked(bool)), this, SLOT(OnVariationsFilterClearButtonClicked(bool)));
|
|
connect(ui.ActionVariationsDialog, SIGNAL(triggered(bool)), this, SLOT(OnActionVariationsDialog(bool)), Qt::QueuedConnection);
|
|
|
|
//Setting dimensions in the designer with a layout is futile, so must hard code here.
|
|
tree->setColumnWidth(0, 160);
|
|
tree->setColumnWidth(1, 23);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Show the variations filter dialog.
|
|
/// </summary>
|
|
/// <param name="checked">Ignored</param>
|
|
void Fractorium::OnActionVariationsDialog(bool checked)
|
|
{
|
|
if (m_VarDialog->exec())
|
|
{
|
|
m_Controller->FilteredVariations();
|
|
Filter();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Apply the text passed in, in conjuction with the selections from
|
|
/// the variations filter dialog to only show variations whose names
|
|
/// contain the substring and are selected.
|
|
/// Called when the user types in the variation filter text box and
|
|
/// when the variations dialog exits.
|
|
/// </summary>
|
|
/// <param name="text">The text to filter on</param>
|
|
template <typename T>
|
|
void FractoriumEmberController<T>::Filter(const QString& text)
|
|
{
|
|
auto& ids = m_Fractorium->m_VarDialog->Map();
|
|
auto tree = m_Fractorium->ui.VariationsTree;
|
|
auto xform = CurrentXform();
|
|
|
|
tree->setUpdatesEnabled(false);
|
|
|
|
for (int i = 0; i < tree->topLevelItemCount(); i++)
|
|
{
|
|
if (auto item = dynamic_cast<VariationTreeWidgetItem*>(tree->topLevelItem(i)))
|
|
{
|
|
auto varName = item->text(0);
|
|
|
|
if (xform && xform->GetVariationById(item->Id()))//If it's present then show it no matter what the filter is.
|
|
{
|
|
item->setHidden(false);
|
|
}
|
|
else if (ids.contains(varName))//If the varation is the map of all variations, which is should always be, consider it as well as the filter text.
|
|
{
|
|
item->setHidden(!varName.contains(text, Qt::CaseInsensitive) || !ids[varName].toBool());
|
|
}
|
|
else//Wasn't present, which should never happen, so just consider filter text.
|
|
{
|
|
item->setHidden(!varName.contains(text, Qt::CaseInsensitive));
|
|
}
|
|
}
|
|
}
|
|
|
|
m_Fractorium->OnTreeHeaderSectionClicked(m_Fractorium->m_VarSortMode);//Must re-sort every time the filter changes.
|
|
tree->setUpdatesEnabled(true);
|
|
}
|
|
|
|
void Fractorium::Filter()
|
|
{
|
|
m_Controller->Filter(ui.VariationsFilterLineEdit->text());
|
|
}
|
|
|
|
template <typename T>
|
|
void FractoriumEmberController<T>::FilteredVariations()
|
|
{
|
|
auto& map = m_Fractorium->m_VarDialog->Map();
|
|
|
|
m_FilteredVariations.clear();
|
|
m_FilteredVariations.reserve(map.size());
|
|
|
|
for (auto i = 0; i < m_VariationList.Size(); i++)
|
|
if (auto var = m_VariationList.GetVariation(i))
|
|
if (map.contains(var->Name().c_str()) && map[var->Name().c_str()].toBool())
|
|
m_FilteredVariations.push_back(var->VariationId());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Dynamically populate the variation tree widget with VariationTreeWidgetItem and VariationTreeDoubleSpinBox
|
|
/// templated with the correct type.
|
|
/// This will clear any previous contents.
|
|
/// Called upon initialization, or controller type change.
|
|
/// </summary>
|
|
template <typename T>
|
|
void FractoriumEmberController<T>::SetupVariationTree()
|
|
{
|
|
T fMin = TLOW;
|
|
T fMax = TMAX;
|
|
QSize hint0(75, 16);
|
|
QSize hint1(30, 16);
|
|
auto tree = m_Fractorium->ui.VariationsTree;
|
|
|
|
tree->clear();
|
|
tree->blockSignals(true);
|
|
|
|
for (size_t i = 0; i < m_VariationList.Size(); i++)
|
|
{
|
|
auto var = m_VariationList.GetVariation(i);
|
|
auto parVar = dynamic_cast<const ParametricVariation<T>*>(var);
|
|
|
|
//First add the variation, with a spinner for its weight.
|
|
auto item = new VariationTreeWidgetItem(var->VariationId(), tree);
|
|
auto spinBox = new VariationTreeDoubleSpinBox(tree, item, var->VariationId(), "");
|
|
|
|
item->setText(0, QString::fromStdString(var->Name()));
|
|
item->setSizeHint(0, hint0);
|
|
item->setSizeHint(1, hint1);
|
|
spinBox->setRange(fMin, fMax);
|
|
spinBox->DoubleClick(true);
|
|
spinBox->DoubleClickZero(1);
|
|
spinBox->DoubleClickNonZero(0);
|
|
spinBox->SmallStep(0.001);
|
|
spinBox->setDecimals(4);
|
|
tree->setItemWidget(item, 1, spinBox);
|
|
m_Fractorium->connect(spinBox, SIGNAL(valueChanged(double)), SLOT(OnVariationSpinBoxValueChanged(double)), Qt::QueuedConnection);
|
|
|
|
//Check to see if the variation was parametric, and add a tree entry with a spinner for each parameter.
|
|
if (parVar)
|
|
{
|
|
auto params = parVar->Params();
|
|
|
|
for (size_t j = 0; j < parVar->ParamCount(); j++)
|
|
{
|
|
if (!params[j].IsPrecalc())
|
|
{
|
|
auto paramWidget = new VariationTreeWidgetItem(var->VariationId(), item);
|
|
auto varSpinBox = new VariationTreeDoubleSpinBox(tree, paramWidget, parVar->VariationId(), params[j].Name());
|
|
|
|
paramWidget->setText(0, params[j].Name().c_str());
|
|
paramWidget->setSizeHint(0, hint0);
|
|
paramWidget->setSizeHint(1, hint1);
|
|
varSpinBox->setRange(params[j].Min(), params[j].Max());
|
|
varSpinBox->setValue(params[j].ParamVal());
|
|
varSpinBox->DoubleClick(true);
|
|
varSpinBox->DoubleClickZero(1);
|
|
varSpinBox->DoubleClickNonZero(0);
|
|
|
|
if (params[j].Type() == INTEGER || params[j].Type() == INTEGER_NONZERO)
|
|
{
|
|
varSpinBox->setSingleStep(1);
|
|
varSpinBox->Step(1);
|
|
varSpinBox->SmallStep(1);
|
|
}
|
|
|
|
varSpinBox->setDecimals(4);
|
|
tree->setItemWidget(paramWidget, 1, varSpinBox);
|
|
m_Fractorium->connect(varSpinBox, SIGNAL(valueChanged(double)), SLOT(OnVariationSpinBoxValueChanged(double)), Qt::QueuedConnection);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Filter("");
|
|
tree->blockSignals(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set every spinner in the variation tree, including params, to zero.
|
|
/// </summary>
|
|
template <typename T>
|
|
void FractoriumEmberController<T>::ClearVariationsTree()
|
|
{
|
|
QTreeWidget* tree = m_Fractorium->ui.VariationsTree;
|
|
|
|
for (int i = 0; i < tree->topLevelItemCount(); i++)
|
|
{
|
|
QTreeWidgetItem* item = tree->topLevelItem(i);
|
|
auto* spinBox = dynamic_cast<VariationTreeDoubleSpinBox*>(tree->itemWidget(item, 1));
|
|
|
|
spinBox->SetValueStealth(0);
|
|
|
|
for (int j = 0; j < item->childCount(); j++)//Iterate through all of the children, which will be the params.
|
|
{
|
|
if ((spinBox = dynamic_cast<VariationTreeDoubleSpinBox*>(tree->itemWidget(item->child(j), 1))))//Cast the child widget to the VariationTreeDoubleSpinBox type.
|
|
spinBox->SetValueStealth(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copy the value of a variation or param spinner to its corresponding value
|
|
/// in the currently selected xform.
|
|
/// Called when any spinner in the variations tree is changed.
|
|
/// Resets the rendering process.
|
|
/// </summary>
|
|
/// <param name="d">The spinner value</param>
|
|
template <typename T>
|
|
void FractoriumEmberController<T>::VariationSpinBoxValueChanged(double d)//Would be awesome to make this work for all.//TODO
|
|
{
|
|
auto objSender = m_Fractorium->sender();
|
|
auto tree = m_Fractorium->ui.VariationsTree;
|
|
auto sender = dynamic_cast<VariationTreeDoubleSpinBox*>(objSender);
|
|
auto xform = m_Ember.GetTotalXform(m_Fractorium->ui.CurrentXformCombo->currentIndex());//Will retrieve normal xform or final if needed.
|
|
|
|
if (sender && xform)
|
|
{
|
|
auto var = m_VariationList.GetVariation(sender->GetVariationId());//The variation attached to the sender, for reference only.
|
|
auto parVar = dynamic_cast<const ParametricVariation<T>*>(var);//The parametric cast of that variation.
|
|
auto xformVar = xform->GetVariationById(var->VariationId());//The corresponding variation in the currently selected xform.
|
|
auto widgetItem = sender->WidgetItem();
|
|
bool isParam = parVar && sender->IsParam();
|
|
|
|
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 (xformParVar->SetParamVal(sender->ParamName().c_str(), d))
|
|
{
|
|
UpdateRender();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//If they spun down to zero, and it wasn't a parameter item,
|
|
//and the current xform contained the variation, then remove the variation.
|
|
if (IsNearZero(d))
|
|
{
|
|
if (xformVar)
|
|
xform->DeleteVariationById(var->VariationId());
|
|
|
|
//widgetItem->setBackgroundColor(0, Qt::darkGray);//Ensure background is always white if weight goes to zero.
|
|
widgetItem->setBackgroundColor(0, QColor(255, 255, 255));//Ensure background is always white if weight goes to zero.
|
|
}
|
|
else
|
|
{
|
|
if (xformVar)//The xform already contained this variation, which means they just went from a non-zero weight to another non-zero weight (the simple case).
|
|
{
|
|
xformVar->m_Weight = d;
|
|
}
|
|
else
|
|
{
|
|
//If the item wasn't a param and the xform did not contain this variation,
|
|
//it means they went from zero to a non-zero weight, so add a new copy of this xform.
|
|
auto newVar = var->Copy();//Create a new one with default values.
|
|
|
|
newVar->m_Weight = d;
|
|
xform->AddVariation(newVar);
|
|
//widgetItem->setBackgroundColor(0, Qt::darkGray);//Set background to gray when a variation has non-zero weight in this xform.
|
|
widgetItem->setBackgroundColor(0, QColor(200, 200, 200));//Set background to gray when a variation has non-zero weight in this xform.
|
|
|
|
//If they've added a new parametric variation, then grab the values currently in the spinners
|
|
//for the child parameters and assign them to the newly added variation.
|
|
if (parVar)
|
|
{
|
|
auto newParVar = dynamic_cast<ParametricVariation<T>*>(newVar);
|
|
|
|
for (int i = 0; i < widgetItem->childCount(); i++)//Iterate through all of the children, which will be the params.
|
|
{
|
|
auto childItem = widgetItem->child(i);//Get the child.
|
|
auto itemWidget = tree->itemWidget(childItem, 1);//Get the widget for the child.
|
|
|
|
if (auto spinBox = dynamic_cast<VariationTreeDoubleSpinBox*>(itemWidget))//Cast the widget to the VariationTreeDoubleSpinBox type.
|
|
{
|
|
string s = childItem->text(0).toStdString();//Use the name of the child, and the value of the spinner widget to assign the param.
|
|
|
|
newParVar->SetParamVal(s.c_str(), spinBox->value());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
UpdateRender();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Fractorium::OnVariationSpinBoxValueChanged(double d) { m_Controller->VariationSpinBoxValueChanged(d); }
|
|
|
|
/// <summary>
|
|
/// Fill the variation tree values from passed in xform and apply the current sorting mode.
|
|
/// Called when the currently selected xform changes.
|
|
/// </summary>
|
|
/// <param name="xform">The xform whose variation values will be used to fill the tree</param>
|
|
template <typename T>
|
|
void FractoriumEmberController<T>::FillVariationTreeWithXform(Xform<T>* xform)
|
|
{
|
|
auto tree = m_Fractorium->ui.VariationsTree;
|
|
|
|
tree->blockSignals(true);
|
|
m_Fractorium->Filter();
|
|
|
|
for (int i = 0; i < tree->topLevelItemCount(); i++)
|
|
{
|
|
auto item = dynamic_cast<VariationTreeWidgetItem*>(tree->topLevelItem(i));
|
|
auto var = xform->GetVariationById(item->Id());//See if this variation in the tree was contained in the xform.
|
|
auto parVar = dynamic_cast<ParametricVariation<T>*>(var);//Attempt cast to parametric variation for later.
|
|
auto origParVar = dynamic_cast<const ParametricVariation<T>*>(m_VariationList.GetVariation(item->Id()));
|
|
|
|
if (auto spinBox = dynamic_cast<VariationTreeDoubleSpinBox*>(tree->itemWidget(item, 1)))//Get the widget for the item, and cast the widget to the VariationTreeDoubleSpinBox type.
|
|
{
|
|
if (var)//Ensure it's visible, even if it's supposed to be filtered.
|
|
item->setHidden(false);
|
|
|
|
spinBox->SetValueStealth(var ? var->m_Weight : 0);//If the variation was present, set the spin box to its weight, else zero.
|
|
//item->setBackgroundColor(0, var ? Qt::darkGray : Qt::lightGray);//Ensure background is always white if the value goes to zero, else gray if var present.
|
|
item->setBackgroundColor(0, var ? QColor(200, 200, 200) : QColor(255, 255, 255));//Ensure background is always white if the value goes to zero, else gray if var present.
|
|
|
|
for (int j = 0; j < item->childCount(); j++)//Iterate through all of the children, which will be the params if it was a parametric variation.
|
|
{
|
|
T* param = nullptr;
|
|
auto childItem = item->child(j);//Get the child.
|
|
auto childItemWidget = tree->itemWidget(childItem, 1);//Get the widget for the child.
|
|
|
|
if (auto childSpinBox = dynamic_cast<VariationTreeDoubleSpinBox*>(childItemWidget))//Cast the widget to the VariationTreeDoubleSpinBox type.
|
|
{
|
|
string s = childItem->text(0).toStdString();//Get the name of the child.
|
|
|
|
if (parVar)
|
|
{
|
|
if ((param = parVar->GetParam(s.c_str())))//Retrieve pointer to the param.
|
|
childSpinBox->SetValueStealth(*param);
|
|
}
|
|
else if (origParVar)//Parametric variation was not present in this xform, so set child values to defaults.
|
|
{
|
|
if ((param = origParVar->GetParam(s.c_str())))
|
|
childSpinBox->SetValueStealth(*param);
|
|
else
|
|
childSpinBox->SetValueStealth(0);//Will most likely never happen, but just to be safe.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
tree->blockSignals(false);
|
|
m_Fractorium->OnTreeHeaderSectionClicked(m_Fractorium->m_VarSortMode);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Change the sorting to be either by variation ID, or by weight.
|
|
/// If sorting by variation ID, repeated clicks will alternate ascending or descending.
|
|
/// Called when user clicks the tree headers.
|
|
/// </summary>
|
|
/// <param name="logicalIndex">Column index of the header clicked. Sort by name if 0, sort by weight if 1.</param>
|
|
void Fractorium::OnTreeHeaderSectionClicked(int logicalIndex)
|
|
{
|
|
m_VarSortMode = logicalIndex;
|
|
ui.VariationsTree->sortItems(m_VarSortMode, m_VarSortMode == 0 ? Qt::AscendingOrder : Qt::DescendingOrder);
|
|
|
|
if (m_VarSortMode == 1)
|
|
ui.VariationsTree->scrollToTop();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Apply the text in the variation filter text box to only show variations whose names
|
|
/// contain the substring.
|
|
/// Called when the user types in the variation filter text box.
|
|
/// </summary>
|
|
/// <param name="text">The text to filter on</param>
|
|
void Fractorium::OnVariationsFilterLineEditTextChanged(const QString& text)
|
|
{
|
|
Filter();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clear the variation name filter, which will display all variations.
|
|
/// Called when clear variations filter button is clicked.
|
|
/// </summary>
|
|
/// <param name="checked">Ignored</param>
|
|
void Fractorium::OnVariationsFilterClearButtonClicked(bool checked)
|
|
{
|
|
ui.VariationsFilterLineEdit->clear();
|
|
}
|
|
|
|
template class FractoriumEmberController<float>;
|
|
|
|
#ifdef DO_DOUBLE
|
|
template class FractoriumEmberController<double>;
|
|
#endif
|