fractorium/Source/Fractorium/FractoriumXformsVariations.cpp
Person 1dfbd4eff2 --User changes
-Add new preset dimensions to the right click menu of the width and height fields in the editor.
-Change QSS stylesheets to properly handle tabs.
-Make tabs rectangular by default. For some reason, they had always been triangular.

--Bug fixes
 -Incremental rendering times in the editor were wrong.

--Code changes
 -Migrate to Qt6. There is probably more work to be done here.
-Migrate to VS2022.
-Migrate to Wix 4 installer.
-Change installer to install to program files for all users.
-Fix many VS2022 code analysis warnings.
-No longer use byte typedef, because std::byte is now a type. Revert all back to unsigned char.
-Upgrade OpenCL headers to version 3.0 and keep locally now rather than trying to look for system files.
-No longer link to Nvidia or AMD specific OpenCL libraries. Use the generic installer located at OCL_ROOT too.
-Add the ability to change OpenCL grid dimensions. This was attempted for investigating possible performance improvments, but made no difference.

This has not been verified on Linux or Mac yet.
2023-04-25 17:59:54 -06:00

434 lines
17 KiB
C++

#include "FractoriumPch.h"
#include "Fractorium.h"
/// <summary>
/// Initialize the xforms variations UI.
/// </summary>
void Fractorium::InitXformsVariationsUI()
{
const 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, 170);
tree->setColumnWidth(1, 80);
tree->setColumnWidth(2, 20);
//Set Default variation tree text and background colors for zero and non zero cases.
m_VariationTreeColorNonZero = Qt::black;
m_VariationTreeColorZero = Qt::black;
m_VariationTreeBgColorNonZero = Qt::lightGray;
m_VariationTreeBgColorZero = Qt::white;
}
/// <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)
{
const auto& ids = m_Fractorium->m_VarDialog->Map();
const auto tree = m_Fractorium->ui.VariationsTree;
const 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()
{
const 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 (const 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>::SetupVariationsTree()
{
T fMin = TLOW;
T fMax = TMAX;
const QSize hint0(170, 16);
const QSize hint1(80, 16);
const QSize hint2(20, 16);
static vector<string> dc{ "m_ColorX" };
static vector<string> assign{ "outPoint->m_X =", "outPoint->m_Y =", "outPoint->m_Z =",
"outPoint->m_X=", "outPoint->m_Y=", "outPoint->m_Z=" };
auto tree = m_Fractorium->ui.VariationsTree;
tree->clear();
tree->blockSignals(true);
int iconSize_ = 20;
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);
item->setSizeHint(2, hint2);
QPixmap pixmap(iconSize_ * 3, iconSize_);
auto mask = pixmap.createMaskFromColor(QColor("transparent"), Qt::MaskOutColor);
pixmap.setMask(mask);
QPainter paint(&pixmap);
paint.fillRect(QRect(0, 0, iconSize_ * 3, iconSize_), QColor(0, 0, 0, 0));
if (var->VarType() == eVariationType::VARTYPE_REG)
{
if (SearchVar(var, assign, false))
paint.fillRect(QRect(0, 0, iconSize_, iconSize_), QColor(255, 0, 0));
}
else if (var->VarType() == eVariationType::VARTYPE_PRE || var->VarType() == eVariationType::VARTYPE_POST)
{
if (var->AssignType() == eVariationAssignType::ASSIGNTYPE_SUM)
paint.fillRect(QRect(0, 0, iconSize_, iconSize_), QColor(255, 0, 0));
}
bool isDc = SearchVar(var, dc, false);
if (isDc)
paint.fillRect(QRect(iconSize_, 0, iconSize_, iconSize_), QColor(0, 255, 0));
if (!var->StateOpenCLString().empty())
paint.fillRect(QRect(iconSize_ * 2, 0, iconSize_, iconSize_), QColor(0, 0, 255));
QIcon qi(pixmap);
item->setIcon(2, qi);
spinBox->setRange(fMin, fMax);
spinBox->DoubleClick(true);
spinBox->DoubleClickZero(1);
spinBox->DoubleClickNonZero(0);
spinBox->SmallStep(0.001);
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() == eParamType::INTEGER || params[j].Type() == eParamType::INTEGER_NONZERO)
{
varSpinBox->setSingleStep(1);
varSpinBox->Step(1);
varSpinBox->SmallStep(1);
}
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()
{
const auto tree = m_Fractorium->ui.VariationsTree;
for (int i = 0; i < tree->topLevelItemCount(); i++)
{
const auto item = tree->topLevelItem(i);
if (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 (const auto varSpinBox = dynamic_cast<VariationTreeDoubleSpinBox*>(tree->itemWidget(item->child(j), 1)))//Cast the child widget to the VariationTreeDoubleSpinBox type.
varSpinBox->SetValueStealth(0);
}
}
}
}
/// <summary>
/// Copy the value of a variation or param spinner to its corresponding value
/// in the selected xforms.
/// 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
{
bool update = false;
const auto objSender = m_Fractorium->sender();
const auto tree = m_Fractorium->ui.VariationsTree;
const auto sender = dynamic_cast<VariationTreeDoubleSpinBox*>(objSender);
if (sender)
{
UpdateXform([&](Xform<T>* xform, size_t xfindex, size_t selIndex)
{
const auto var = m_VariationList->GetVariation(sender->GetVariationId());//The variation attached to the sender, for reference only.
const auto parVar = dynamic_cast<const ParametricVariation<T>*>(var);//The parametric cast of that variation.
const auto xformVar = xform->GetVariationById(var->VariationId());//The corresponding variation in the currently selected xform.
const auto widgetItem = sender->WidgetItem();
const auto 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 (const 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;
}
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->setForeground(0, m_Fractorium->m_VariationTreeColorZero);
widgetItem->setBackground(0, m_Fractorium->m_VariationTreeBgColorZero);
}
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.
const auto newVar = var->Copy();//Create a new one with default values.
newVar->m_Weight = d;
xform->AddVariation(newVar);
widgetItem->setForeground(0, m_Fractorium->m_VariationTreeColorNonZero);
widgetItem->setBackground(0, m_Fractorium->m_VariationTreeBgColorNonZero);
//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)
{
const 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.
{
const auto childItem = widgetItem->child(i);//Get the child.
const auto itemWidget = tree->itemWidget(childItem, 1);//Get the widget for the child.
if (const 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());
}
}
}
}
}
update = true;
}
}, eXformUpdate::UPDATE_CURRENT_AND_SELECTED, false);
if (update)
UpdateRender();
}
}
void Fractorium::OnVariationSpinBoxValueChanged(double d) { m_Controller->VariationSpinBoxValueChanged(d); }
/// <summary>
/// Fill in the variations tree with the values from the current xform.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::FillVariationTreeWithCurrentXform()
{
FillVariationTreeWithXform(CurrentXform());
}
/// <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)
{
const auto tree = m_Fractorium->ui.VariationsTree;
tree->blockSignals(true);
m_Fractorium->Filter();
for (int i = 0; i < tree->topLevelItemCount(); i++)
{
const auto item = dynamic_cast<VariationTreeWidgetItem*>(tree->topLevelItem(i));
const auto var = xform->GetVariationById(item->Id());//See if this variation in the tree was contained in the xform.
const auto parVar = dynamic_cast<ParametricVariation<T>*>(var);//Attempt cast to parametric variation for later.
const auto origParVar = dynamic_cast<const ParametricVariation<T>*>(m_VariationList->GetVariation(item->Id()));
if (const 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->setForeground(0, var ? m_Fractorium->m_VariationTreeColorNonZero : m_Fractorium->m_VariationTreeColorZero);
item->setBackground(0, var ? m_Fractorium->m_VariationTreeBgColorNonZero : m_Fractorium->m_VariationTreeBgColorZero);
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;
const auto childItem = item->child(j);//Get the child.
const auto childItemWidget = tree->itemWidget(childItem, 1);//Get the widget for the child.
if (const 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)
{
if (logicalIndex <= 1)
{
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