Merged mfeemster/fractorium into master

This commit is contained in:
Michel Mastriani 2020-03-05 21:40:04 -03:00
commit becdb64d40
22 changed files with 1034 additions and 310 deletions

View File

@ -1,28 +1,25 @@
#include "EmberPch.h" #include "EmberPch.h"
#include "EmberDefines.h" #include "EmberDefines.h"
#include "Isaac.h" #include "Isaac.h"
#include "Utils.h"
namespace EmberNs namespace EmberNs
{ {
template<> unique_ptr<QTIsaac<ISAAC_SIZE, ISAAC_INT>> QTIsaac<ISAAC_SIZE, ISAAC_INT>::GlobalRand = unique_ptr<QTIsaac<ISAAC_SIZE, ISAAC_INT>>(new QTIsaac<ISAAC_SIZE, ISAAC_INT>()); template<> unique_ptr<QTIsaac<ISAAC_SIZE, ISAAC_INT>> QTIsaac<ISAAC_SIZE, ISAAC_INT>::GlobalRand = unique_ptr<QTIsaac<ISAAC_SIZE, ISAAC_INT>>(new QTIsaac<ISAAC_SIZE, ISAAC_INT>());
template<> unique_ptr<recursive_mutex> QTIsaac<ISAAC_SIZE, ISAAC_INT>::s_CS = unique_ptr<recursive_mutex>(new recursive_mutex()); template<> unique_ptr<recursive_mutex> QTIsaac<ISAAC_SIZE, ISAAC_INT>::s_CS = unique_ptr<recursive_mutex>(new recursive_mutex());
template EMBER_API class QTIsaac<ISAAC_SIZE, ISAAC_INT>; template EMBER_API class QTIsaac<ISAAC_SIZE, ISAAC_INT>;
bool Compat::m_Compat = false;
} }
#include "Curves.h" #include "Curves.h"
#include "Ember.h" #include "Ember.h"
#include "Utils.h"
#include "Iterator.h" #include "Iterator.h"
#include "Palette.h" #include "Palette.h"
#include "PaletteList.h" #include "PaletteList.h"
#include "Point.h" #include "Point.h"
#include "VarFuncs.h" #include "VarFuncs.h"
#include "Variation.h" #include "Variation.h"
#ifdef FLAM3_COMPAT #include "Variations01.h"
#include "Variations01_flam3_compat.h"//Do this instead if you want full compatibility with flam3.
#else
#include "Variations01.h"
#endif
#include "Variations02.h" #include "Variations02.h"
#include "Variations03.h" #include "Variations03.h"
#include "Variations04.h" #include "Variations04.h"

View File

@ -44,7 +44,6 @@ namespace EmberNs
#define ISAAC_SIZE 4 #define ISAAC_SIZE 4
#define MEMALIGN 32 #define MEMALIGN 32
#define DE_THRESH 100 #define DE_THRESH 100
#define MAX_VARS_PER_XFORM 8
#define DEG_2_RAD (M_PI / 180) #define DEG_2_RAD (M_PI / 180)
#define RAD_2_DEG (180 / M_PI) #define RAD_2_DEG (180 / M_PI)
#define DEG_2_RAD_T (T(M_PI) / T(180)) #define DEG_2_RAD_T (T(M_PI) / T(180))

View File

@ -285,6 +285,16 @@ protected:
x(const x& other) = delete; \ x(const x& other) = delete; \
const x& operator=(const x& other) = delete const x& operator=(const x& other) = delete
/// <summary>
/// The calculations in some variations were changed from what they were in flam3/Apophysis to match Chaotica.
/// Some users prefer the old functionality, so provide an option to retain it.
/// </summary>
class EMBER_API Compat
{
public:
static bool m_Compat;
};
/// <summary> /// <summary>
/// Open a file in binary mode and read its entire contents into a vector of bytes. Optionally null terminate. /// Open a file in binary mode and read its entire contents into a vector of bytes. Optionally null terminate.
/// </summary> /// </summary>

File diff suppressed because it is too large Load Diff

View File

@ -268,16 +268,15 @@ public:
m_HasPost = false; m_HasPost = false;
m_HasPreOrRegularVars = false; m_HasPreOrRegularVars = false;
m_ParentEmber = nullptr; m_ParentEmber = nullptr;
m_PreVariations.reserve(MAX_VARS_PER_XFORM); m_PreVariations.reserve(8);
m_Variations.reserve(MAX_VARS_PER_XFORM); m_Variations.reserve(8);
m_PostVariations.reserve(MAX_VARS_PER_XFORM); m_PostVariations.reserve(8);
CacheColorVals(); CacheColorVals();
count++; count++;
} }
/// <summary> /// <summary>
/// Add a pointer to a variation which will be deleted on destruction so the caller should not delete. /// Add a pointer to a variation which will be deleted on destruction so the caller should not delete.
/// This checks if the total number of variations is less than or equal to MAX_VARS_PER_XFORM.
/// It also checks if the variation is already present, in which case it doesn't add. /// It also checks if the variation is already present, in which case it doesn't add.
/// If add, set all precalcs. /// If add, set all precalcs.
/// </summary> /// </summary>
@ -299,23 +298,63 @@ public:
else else
vec = &m_Variations; vec = &m_Variations;
if (vec->size() < MAX_VARS_PER_XFORM) vec->push_back(variation);
//Flatten must always be last.
for (size_t i = 0; i < vec->size(); i++)
{ {
vec->push_back(variation); if ((i != vec->size() - 1) && ((*vec)[i]->Name().find("flatten") != string::npos))
//Flatten must always be last.
for (size_t i = 0; i < vec->size(); i++)
{ {
if ((i != vec->size() - 1) && ((*vec)[i]->Name().find("flatten") != string::npos)) std::swap((*vec)[i], (*vec)[vec->size() - 1]);
{ break;
std::swap((*vec)[i], (*vec)[vec->size() - 1]);
break;
}
} }
SetPrecalcFlags();
return true;
} }
SetPrecalcFlags();
return true;
}
return false;
}
/// <summary>
/// Insert a pointer to a variation, at the specified location, which will be deleted on destruction so the caller should not delete.
/// It also checks if the variation is already present, in which case it doesn't add.
/// If add, set all precalcs.
/// </summary>
/// <param name="variation">Pointer to a varation to add</param>
/// <param name="index">The index to insert at</param>
/// <returns>True if the successful, else false.</returns>
bool InsertVariation(Variation<T>* variation, size_t index)
{
if (variation && (GetVariationById(variation->VariationId()) == nullptr))
{
string name = variation->Name();
bool pre = name.find("pre_") == 0;
bool post = name.find("post_") == 0;
vector<Variation<T>*>* vec;
if (pre)
vec = &m_PreVariations;
else if (post)
vec = &m_PostVariations;
else
vec = &m_Variations;
vec->insert(vec->begin() + index, variation);
//Flatten must always be last.
for (size_t i = 0; i < vec->size(); i++)
{
if ((i != vec->size() - 1) && ((*vec)[i]->Name().find("flatten") != string::npos))
{
std::swap((*vec)[i], (*vec)[vec->size() - 1]);
break;
}
}
SetPrecalcFlags();
return true;
} }
return false; return false;
@ -443,6 +482,36 @@ public:
return found; return found;
} }
/// <summary>
/// Remove the variation with the matching ID, but instead of deleting it, return it.
/// Update precalcs if deletion successful.
/// </summary>
/// <param name="id">The ID to search for</param>
/// <returns>The variation if found, else nullptr.</returns>
Variation<T>* RemoveVariationById(eVariationId id)
{
bool found = false;
Variation<T>* var = nullptr;
AllVarsFunc([&](vector<Variation<T>*>& variations, bool & keepGoing)
{
for (size_t i = 0; i < variations.size(); i++)
{
if (variations[i] && variations[i]->VariationId() == id)
{
var = variations[i];
variations.erase(variations.begin() + i);
keepGoing = false;
break;
}
}
});
if (var)
SetPrecalcFlags();
return var;
}
/// <summary> /// <summary>
/// Delete the motion elements. /// Delete the motion elements.
/// </summary> /// </summary>

View File

@ -46,6 +46,7 @@ bool EmberAnimate(int argc, _TCHAR* argv[], EmberOptions& opt)
vector<string> errorReport; vector<string> errorReport;
std::recursive_mutex verboseCs; std::recursive_mutex verboseCs;
auto fullpath = GetExePath(argv[0]); auto fullpath = GetExePath(argv[0]);
Compat::m_Compat = opt.Flam3Compat();
if (opt.EmberCL()) if (opt.EmberCL())
{ {

View File

@ -994,6 +994,13 @@ template <typename T>
bool IterOpenCLKernelCreator<T>::IsBuildRequired(const Ember<T>& ember1, const Ember<T>& ember2, bool optAffine) bool IterOpenCLKernelCreator<T>::IsBuildRequired(const Ember<T>& ember1, const Ember<T>& ember2, bool optAffine)
{ {
size_t i, j, xformCount = ember1.TotalXformCount(); size_t i, j, xformCount = ember1.TotalXformCount();
static bool lastCompat = Compat::m_Compat;
if (lastCompat != Compat::m_Compat)
{
lastCompat = Compat::m_Compat;
return true;
}
if (xformCount != ember2.TotalXformCount()) if (xformCount != ember2.TotalXformCount())
return true; return true;

View File

@ -68,6 +68,7 @@ enum class eOptionIDs : et
OPT_CW_INTERP_LOOPS, OPT_CW_INTERP_LOOPS,
OPT_LOCK_ACCUM, OPT_LOCK_ACCUM,
OPT_DUMP_KERNEL, OPT_DUMP_KERNEL,
OPT_FLAM3_COMPAT,
//Value args. //Value args.
OPT_NTHREADS,//Int value args. OPT_NTHREADS,//Int value args.
@ -372,6 +373,7 @@ public:
INITBOOLOPTION(CwInterpLoops, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_CW_INTERP_LOOPS, _T("--cwinterploops"), false, SO_NONE, " --cwinterploops Rotate clockwise during interpolation, ignored if --interploops is 0 [default: false].\n")); INITBOOLOPTION(CwInterpLoops, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_CW_INTERP_LOOPS, _T("--cwinterploops"), false, SO_NONE, " --cwinterploops Rotate clockwise during interpolation, ignored if --interploops is 0 [default: false].\n"));
INITBOOLOPTION(LockAccum, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_LOCK_ACCUM, _T("--lock_accum"), false, SO_NONE, " --lock_accum Lock threads when accumulating to the histogram using the CPU. This will drop performance to that of single threading [default: false].\n")); INITBOOLOPTION(LockAccum, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_LOCK_ACCUM, _T("--lock_accum"), false, SO_NONE, " --lock_accum Lock threads when accumulating to the histogram using the CPU. This will drop performance to that of single threading [default: false].\n"));
INITBOOLOPTION(DumpKernel, Eob(eOptionUse::OPT_USE_RENDER, eOptionIDs::OPT_DUMP_KERNEL, _T("--dump_kernel"), false, SO_NONE, " --dump_kernel Print the iteration kernel string when using OpenCL (ignored for CPU) [default: false].\n")); INITBOOLOPTION(DumpKernel, Eob(eOptionUse::OPT_USE_RENDER, eOptionIDs::OPT_DUMP_KERNEL, _T("--dump_kernel"), false, SO_NONE, " --dump_kernel Print the iteration kernel string when using OpenCL (ignored for CPU) [default: false].\n"));
INITBOOLOPTION(Flam3Compat, Eob(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_FLAM3_COMPAT, _T("--flam3_compat"), false, SO_NONE, " --flam3_compat The behavior of the cos, cosh, cot, coth, csc, csch, sec, sech, sin, sinh, tan and tanh variations are different in flam3/Apophysis versus Chaotica. True for flam3/Apophysis behavior, false for Chaotica behavior [default: false].\n"));
//Int. //Int.
INITINTOPTION(Symmetry, Eoi(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_SYMMETRY, _T("--symmetry"), 0, SO_REQ_SEP, " --symmetry=<val> Set symmetry of result [default: 0].\n")); INITINTOPTION(Symmetry, Eoi(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_SYMMETRY, _T("--symmetry"), 0, SO_REQ_SEP, " --symmetry=<val> Set symmetry of result [default: 0].\n"));
INITINTOPTION(SheepGen, Eoi(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_SHEEP_GEN, _T("--sheep_gen"), -1, SO_REQ_SEP, " --sheep_gen=<val> Sheep generation of this flame [default: -1].\n")); INITINTOPTION(SheepGen, Eoi(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_SHEEP_GEN, _T("--sheep_gen"), -1, SO_REQ_SEP, " --sheep_gen=<val> Sheep generation of this flame [default: -1].\n"));
@ -531,6 +533,7 @@ public:
PARSEBOOLOPTION(eOptionIDs::OPT_CW_INTERP_LOOPS, CwInterpLoops); PARSEBOOLOPTION(eOptionIDs::OPT_CW_INTERP_LOOPS, CwInterpLoops);
PARSEBOOLOPTION(eOptionIDs::OPT_LOCK_ACCUM, LockAccum); PARSEBOOLOPTION(eOptionIDs::OPT_LOCK_ACCUM, LockAccum);
PARSEBOOLOPTION(eOptionIDs::OPT_DUMP_KERNEL, DumpKernel); PARSEBOOLOPTION(eOptionIDs::OPT_DUMP_KERNEL, DumpKernel);
PARSEBOOLOPTION(eOptionIDs::OPT_FLAM3_COMPAT, Flam3Compat);
PARSEOPTION(eOptionIDs::OPT_SYMMETRY, Symmetry);//Int args PARSEOPTION(eOptionIDs::OPT_SYMMETRY, Symmetry);//Int args
PARSEOPTION(eOptionIDs::OPT_SHEEP_GEN, SheepGen); PARSEOPTION(eOptionIDs::OPT_SHEEP_GEN, SheepGen);
PARSEOPTION(eOptionIDs::OPT_SHEEP_ID, SheepId); PARSEOPTION(eOptionIDs::OPT_SHEEP_ID, SheepId);
@ -819,6 +822,7 @@ public:
Eob CwInterpLoops; Eob CwInterpLoops;
Eob LockAccum; Eob LockAccum;
Eob DumpKernel; Eob DumpKernel;
Eob Flam3Compat;
Eoi Symmetry;//Value int. Eoi Symmetry;//Value int.
Eoi SheepGen; Eoi SheepGen;

View File

@ -58,6 +58,7 @@ bool EmberGenome(int argc, _TCHAR* argv[], EmberOptions& opt)
return true; return true;
} }
Compat::m_Compat = opt.Flam3Compat();
auto varList = VariationList<T>::Instance(); auto varList = VariationList<T>::Instance();
if (opt.AllVars() || opt.SumVars() || opt.AssignVars() || opt.PpSumVars() || opt.PpAssignVars() || if (opt.AllVars() || opt.SumVars() || opt.AssignVars() || opt.PpSumVars() || opt.PpAssignVars() ||

View File

@ -48,6 +48,7 @@ bool EmberRender(int argc, _TCHAR* argv[], EmberOptions& opt)
unique_ptr<Renderer<T, float>> renderer(CreateRenderer<T>(opt.EmberCL() ? eRendererType::OPENCL_RENDERER : eRendererType::CPU_RENDERER, devices, false, 0, emberReport)); unique_ptr<Renderer<T, float>> renderer(CreateRenderer<T>(opt.EmberCL() ? eRendererType::OPENCL_RENDERER : eRendererType::CPU_RENDERER, devices, false, 0, emberReport));
vector<string> errorReport = emberReport.ErrorReport(); vector<string> errorReport = emberReport.ErrorReport();
auto fullpath = GetExePath(argv[0]); auto fullpath = GetExePath(argv[0]);
Compat::m_Compat = opt.Flam3Compat();
if (!errorReport.empty()) if (!errorReport.empty())
emberReport.DumpErrorReport(); emberReport.DumpErrorReport();

View File

@ -129,6 +129,9 @@ public:
bool DrawAllPost(); bool DrawAllPost();
bool LocalPivot(); bool LocalPivot();
//Info.
void ReorderVariations(QTreeWidgetItem* item);
public slots: public slots:
//Dock. //Dock.
void OnDockTopLevelChanged(bool topLevel); void OnDockTopLevelChanged(bool topLevel);

View File

@ -6540,7 +6540,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QTreeWidget" name="SummaryTree"> <widget class="InfoTreeWidget" name="SummaryTree">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding"> <sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -6562,6 +6562,12 @@
<property name="showDropIndicator" stdset="0"> <property name="showDropIndicator" stdset="0">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::MoveAction</enum>
</property>
<property name="selectionMode"> <property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum> <enum>QAbstractItemView::SingleSelection</enum>
</property> </property>
@ -8727,6 +8733,11 @@
<extends>QTreeWidget</extends> <extends>QTreeWidget</extends>
<header>LibraryTreeWidget.h</header> <header>LibraryTreeWidget.h</header>
</customwidget> </customwidget>
<customwidget>
<class>InfoTreeWidget</class>
<extends>QTreeWidget</extends>
<header>LibraryTreeWidget.h</header>
</customwidget>
</customwidgets> </customwidgets>
<tabstops> <tabstops>
<tabstop>LibraryDockWidget</tabstop> <tabstop>LibraryDockWidget</tabstop>

View File

@ -262,6 +262,7 @@ public:
//Info. //Info.
virtual void FillSummary() { } virtual void FillSummary() { }
virtual void ReorderVariations(QTreeWidgetItem* item) { }
//Rendering/progress. //Rendering/progress.
virtual bool Render() { return false; } virtual bool Render() { return false; }
@ -556,6 +557,7 @@ public:
//Info. //Info.
virtual void FillSummary() override; virtual void FillSummary() override;
virtual void ReorderVariations(QTreeWidgetItem* item) override;
//Rendering/progress. //Rendering/progress.
virtual bool Render() override; virtual bool Render() override;

View File

@ -21,6 +21,7 @@ void Fractorium::InitInfoUI()
ui.SummaryTable->setItem(4, 0, m_InfoXformCountItem = new QTableWidgetItem("")); ui.SummaryTable->setItem(4, 0, m_InfoXformCountItem = new QTableWidgetItem(""));
ui.SummaryTable->setItem(5, 0, m_InfoFinalXformItem = new QTableWidgetItem("")); ui.SummaryTable->setItem(5, 0, m_InfoFinalXformItem = new QTableWidgetItem(""));
ui.InfoTabWidget->setCurrentIndex(0);//Make summary tab focused by default. ui.InfoTabWidget->setCurrentIndex(0);//Make summary tab focused by default.
ui.SummaryTree->SetMainWindow(this);
} }
/// <summary> /// <summary>
@ -75,6 +76,8 @@ void FractoriumEmberController<T>::FillSummary()
QColor color; QColor color;
auto table = m_Fractorium->ui.SummaryTable; auto table = m_Fractorium->ui.SummaryTable;
auto tree = m_Fractorium->ui.SummaryTree; auto tree = m_Fractorium->ui.SummaryTree;
auto nondraggable = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
auto draggable = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
tree->blockSignals(true); tree->blockSignals(true);
tree->clear(); tree->clear();
m_Fractorium->m_InfoNameItem->setText(m_Ember.m_Name.c_str()); m_Fractorium->m_InfoNameItem->setText(m_Ember.m_Name.c_str());
@ -109,8 +112,10 @@ void FractoriumEmberController<T>::FillSummary()
item1->setText(0, "Final xform"); item1->setText(0, "Final xform");
item1->setText(1, xform->m_Name.c_str()); item1->setText(1, xform->m_Name.c_str());
item1->setFlags(nondraggable);
auto affineItem = new QTreeWidgetItem(item1); auto affineItem = new QTreeWidgetItem(item1);
affineItem->setText(0, "Affine"); affineItem->setText(0, "Affine");
affineItem->setFlags(nondraggable);
if (xform->m_Affine.IsZero()) if (xform->m_Affine.IsZero())
as += " Empty"; as += " Empty";
@ -129,6 +134,7 @@ void FractoriumEmberController<T>::FillSummary()
auto colorIndexItem = new QTreeWidgetItem(item1); auto colorIndexItem = new QTreeWidgetItem(item1);
colorIndexItem->setText(0, "Color index"); colorIndexItem->setText(0, "Color index");
colorIndexItem->setText(1, QLocale::system().toString(xform->m_ColorX, pc, p)); colorIndexItem->setText(1, QLocale::system().toString(xform->m_ColorX, pc, p));
colorIndexItem->setFlags(nondraggable | Qt::ItemNeverHasChildren);
color = ColorIndexToQColor(xform->m_ColorX); color = ColorIndexToQColor(xform->m_ColorX);
color.setAlphaF(xform->m_Opacity); color.setAlphaF(xform->m_Opacity);
colorIndexItem->setBackgroundColor(1, color); colorIndexItem->setBackgroundColor(1, color);
@ -136,18 +142,25 @@ void FractoriumEmberController<T>::FillSummary()
auto colorSpeedItem = new QTreeWidgetItem(item1); auto colorSpeedItem = new QTreeWidgetItem(item1);
colorSpeedItem->setText(0, "Color speed"); colorSpeedItem->setText(0, "Color speed");
colorSpeedItem->setText(1, QLocale::system().toString(xform->m_ColorSpeed, pc, p)); colorSpeedItem->setText(1, QLocale::system().toString(xform->m_ColorSpeed, pc, p));
colorSpeedItem->setFlags(nondraggable | Qt::ItemNeverHasChildren);
auto opacityItem = new QTreeWidgetItem(item1); auto opacityItem = new QTreeWidgetItem(item1);
opacityItem->setText(0, "Opacity"); opacityItem->setText(0, "Opacity");
opacityItem->setText(1, QLocale::system().toString(xform->m_Opacity, pc, p)); opacityItem->setText(1, QLocale::system().toString(xform->m_Opacity, pc, p));
opacityItem->setFlags(nondraggable | Qt::ItemNeverHasChildren);
auto dcItem = new QTreeWidgetItem(item1); auto dcItem = new QTreeWidgetItem(item1);
dcItem->setText(0, "Direct color"); dcItem->setText(0, "Direct color");
dcItem->setText(1, QLocale::system().toString(xform->m_DirectColor, pc, p)); dcItem->setText(1, QLocale::system().toString(xform->m_DirectColor, pc, p));
dcItem->setFlags(nondraggable | Qt::ItemNeverHasChildren);
if (dcItem->text(0) != tree->LastNonVarField())
throw "Last info tree non-variation index did not match expected value";
while (auto var = xform->GetVariation(i++)) while (auto var = xform->GetVariation(i++))
{ {
auto vitem = new QTreeWidgetItem(item1); auto vitem = new VariationTreeWidgetItem(var->VariationId(), item1);
vitem->setText(0, QString::fromStdString(var->Name())); vitem->setText(0, QString::fromStdString(var->Name()));
vitem->setText(1, QLocale::system().toString(var->m_Weight, pc, vp).rightJustified(vlen, ' ')); vitem->setText(1, QLocale::system().toString(var->m_Weight, pc, vp).rightJustified(vlen, ' '));
vitem->setFlags(draggable);
if (auto parVar = dynamic_cast<ParametricVariation<T>*>(var)) if (auto parVar = dynamic_cast<ParametricVariation<T>*>(var))
{ {
@ -160,6 +173,7 @@ void FractoriumEmberController<T>::FillSummary()
auto pitem = new QTreeWidgetItem(vitem); auto pitem = new QTreeWidgetItem(vitem);
pitem->setText(0, params[j].Name().c_str()); pitem->setText(0, params[j].Name().c_str());
pitem->setText(1, QLocale::system().toString(params[j].ParamVal(), pc, vp).rightJustified(vlen, ' ')); pitem->setText(1, QLocale::system().toString(params[j].ParamVal(), pc, vp).rightJustified(vlen, ' '));
pitem->setFlags(nondraggable);
} }
} }
} }
@ -177,6 +191,57 @@ void Fractorium::FillSummary()
m_Controller->FillSummary(); m_Controller->FillSummary();
} }
/// <summary>
/// Reorder the variations of the xform for the passed in tree widget item.
/// Read the newly reordered variation items in order, removing each from the xform
/// corresponding to the passed in item, and storing them in a vector. Then re-add those variation
/// pointers back to the xform in the same order they were removed.
/// This will be called after the user performs a drag and drop operation on the variations in the
/// info tree. So the variations will be in the newly desired order.
/// </summary>
/// <param name="dme">Pointer to the parent (xform level) tree widget item which contains the variation item being dragged</param>
template <typename T>
void FractoriumEmberController<T>::ReorderVariations(QTreeWidgetItem* item)
{
auto tree = m_Fractorium->ui.SummaryTree;
auto xfindex = tree->indexOfTopLevelItem(item) / 2;//Blank lines each count as one.
if (auto xform = m_Ember.GetTotalXform(xfindex))
{
vector<Variation<T>*> vars;
vars.reserve(xform->TotalVariationCount());
Update([&]
{
int i = 0;
while (auto ch = item->child(i))
{
if (ch->text(0) == tree->LastNonVarField())
{
i++;
while (auto varch = dynamic_cast<VariationTreeWidgetItem*>(item->child(i++)))
if (auto var = xform->RemoveVariationById(varch->Id()))
vars.push_back(var);
for (auto& var : vars)
xform->AddVariation(var);
break;
}
i++;
}
}, true, eProcessAction::FULL_RENDER);
}
}
void Fractorium::ReorderVariations(QTreeWidgetItem* item)
{
m_Controller->ReorderVariations(item);
}
/// <summary> /// <summary>
/// Update the histogram bounds display labels. /// Update the histogram bounds display labels.
/// This shows the user the actual bounds of what's /// This shows the user the actual bounds of what's

View File

@ -1076,12 +1076,15 @@ void Fractorium::OnActionOptions(bool checked)
bool ec = m_Settings->EarlyClip(); bool ec = m_Settings->EarlyClip();
bool yup = m_Settings->YAxisUp(); bool yup = m_Settings->YAxisUp();
bool trans = m_Settings->Transparency(); bool trans = m_Settings->Transparency();
bool compat = m_Settings->Flam3Compat();
if (m_OptionsDialog->exec()) if (m_OptionsDialog->exec())
{ {
bool updatePreviews = ec != m_Settings->EarlyClip() || bool updatePreviews = ec != m_Settings->EarlyClip() ||
yup != m_Settings->YAxisUp() || yup != m_Settings->YAxisUp() ||
trans != m_Settings->Transparency(); trans != m_Settings->Transparency() ||
compat != m_Settings->Flam3Compat();
Compat::m_Compat = m_Settings->Flam3Compat();
SyncOptionsToToolbar();//This won't trigger a recreate, the call below handles it. SyncOptionsToToolbar();//This won't trigger a recreate, the call below handles it.
ShutdownAndRecreateFromOptions(updatePreviews);//This will recreate the controller and/or the renderer from the options if necessary, then start the render timer. ShutdownAndRecreateFromOptions(updatePreviews);//This will recreate the controller and/or the renderer from the options if necessary, then start the render timer.
} }

View File

@ -122,6 +122,8 @@ void FractoriumSettings::EnsureDefaults()
if (value(SHAREDTEXTURE).toString() == "")//Set this to true if the setting is missing because it only needs to be false for the rare system that has problems with shared textures. if (value(SHAREDTEXTURE).toString() == "")//Set this to true if the setting is missing because it only needs to be false for the rare system that has problems with shared textures.
SharedTexture(true); SharedTexture(true);
Compat::m_Compat = Flam3Compat();
} }
/// <summary> /// <summary>
@ -200,6 +202,9 @@ void FractoriumSettings::LoadLast(bool b) { setValue(LOAD
bool FractoriumSettings::RotateAndScale() { return value(ROTSCALE).toBool(); } bool FractoriumSettings::RotateAndScale() { return value(ROTSCALE).toBool(); }
void FractoriumSettings::RotateAndScale(bool b) { setValue(ROTSCALE, b); } void FractoriumSettings::RotateAndScale(bool b) { setValue(ROTSCALE, b); }
bool FractoriumSettings::Flam3Compat() { return value(FLAM3COMPAT).toBool(); }
void FractoriumSettings::Flam3Compat(bool b) { setValue(FLAM3COMPAT, b); }
/// <summary> /// <summary>
/// Sequence generation settings. /// Sequence generation settings.
/// </summary> /// </summary>

View File

@ -30,6 +30,7 @@
#define OPENCLQUALITY "render/openclquality" #define OPENCLQUALITY "render/openclquality"
#define LOADLAST "render/loadlastonstart" #define LOADLAST "render/loadlastonstart"
#define ROTSCALE "render/rotateandscale" #define ROTSCALE "render/rotateandscale"
#define FLAM3COMPAT "render/flam3compat"
#define STAGGER "sequence/stagger" #define STAGGER "sequence/stagger"
#define STAGGERMAX "sequence/staggermax" #define STAGGERMAX "sequence/staggermax"
@ -180,6 +181,9 @@ public:
bool RotateAndScale(); bool RotateAndScale();
void RotateAndScale(bool b); void RotateAndScale(bool b);
bool Flam3Compat();
void Flam3Compat(bool b);
double Stagger(); double Stagger();
void Stagger(double i); void Stagger(double i);

View File

@ -93,4 +93,112 @@ void LibraryTreeWidget::dropEvent(QDropEvent* de)
m_Fractorium->m_Controller->MoveLibraryItems(items, row); m_Fractorium->m_Controller->MoveLibraryItems(items, row);
} }
} }
/// <summary>
/// Set a pointer to the main window.
/// </summary>
/// <param name="f">Pointer to the main Fractorium object</param>
void InfoTreeWidget::SetMainWindow(Fractorium* f)
{
m_Fractorium = f;
}
/// <summary>
/// Called on each mouse movement while dragging, validate whether the area
/// being dragged over can be dropped on.
/// Can only drop on like (pre/reg/post) variation section of the same xform
/// of the variation being dragged.
/// </summary>
/// <param name="dme">Pointer to the drag move event</param>
void InfoTreeWidget::dragMoveEvent(QDragMoveEvent* dme)
{
QModelIndex index = indexAt(dme->pos());
if (!index.isValid())//Don't process drop because it's outside of the droppable area.
{
dme->ignore();
return;
}
QList<QTreeWidgetItem*> dragItems = selectedItems();
if (dragItems.size())
{
auto drag0 = dragItems[0];
if (auto itemat = itemFromIndex(index))
{
auto dragpre = drag0->text(0).startsWith("pre_", Qt::CaseInsensitive);
auto droppre = itemat->text(0).startsWith("pre_", Qt::CaseInsensitive);
auto dragpost = drag0->text(0).startsWith("post_", Qt::CaseInsensitive);
auto droppost = itemat->text(0).startsWith("post_", Qt::CaseInsensitive);
if (auto par = itemat->parent())
{
if (drag0->parent() == par &&
(par->text(0).startsWith("xform ", Qt::CaseInsensitive) ||
par->text(0).startsWith("final", Qt::CaseInsensitive)))
{
if (auto vitemat = dynamic_cast<VariationTreeWidgetItem*>(itemat))
{
bool dopre = dragpre && droppre;
bool dopost = dragpost && droppost;
bool doreg = !dragpre && !droppre && !dragpost && !droppost;
if (dopre || doreg || dopost)
{
QTreeWidget::dragMoveEvent(dme);
return;
}
}
}
}
}
}
dme->ignore();
}
/// <summary>
/// Process the drop event to allow for moving items around inside of the tree.
/// This will only allow variations to be moved around within the variation section of the tree
/// and nowhere else.
/// </summary>
/// <param name="de">Pointer to the QDropEvent object</param>
void InfoTreeWidget::dropEvent(QDropEvent* de)
{
QModelIndex droppedIndex = indexAt(de->pos());
auto items = selectionModel()->selectedRows();
if (!droppedIndex.isValid())//Don't process drop because it's outside of the droppable area.
{
de->ignore();
return;
}
else if (!items.empty())//Actually do the drop and move the item to a new location.
{
QList<QTreeWidgetItem*> dragItems = selectedItems();
if (dragItems.size())
{
auto drag0 = dragItems[0];
auto itemat = itemFromIndex(droppedIndex);
if (auto par = itemat->parent())
{
if (auto vdropitem = dynamic_cast<VariationTreeWidgetItem*>(itemat))
{
if (auto vdragitem = dynamic_cast<VariationTreeWidgetItem*>(drag0))
{
QTreeWidget::dropEvent(de);//This internally changes the order of the items.
m_Fractorium->ReorderVariations(par);
return;
}
}
}
}
de->ignore();
}
}

View File

@ -26,4 +26,29 @@ protected:
virtual void dropEvent(QDropEvent* de) override; virtual void dropEvent(QDropEvent* de) override;
Fractorium* m_Fractorium = nullptr; Fractorium* m_Fractorium = nullptr;
}; };
class InfoTreeWidget : public QTreeWidget
{
Q_OBJECT
public:
/// <summary>
/// Constructor that passes p to the parent.
/// </summary>
/// <param name="p">The parent widget</param>
explicit InfoTreeWidget(QWidget* p = nullptr)
: QTreeWidget(p)
{
}
void SetMainWindow(Fractorium* f);
const QString& LastNonVarField() const { return m_LastNonVarField; }
protected:
virtual void dropEvent(QDropEvent* de) override;
virtual void dragMoveEvent(QDragMoveEvent* dme) override;
Fractorium* m_Fractorium = nullptr;
QString m_LastNonVarField = "Direct color";//It is critical to update this if any more fields are ever added before the variations start.
};

View File

@ -78,6 +78,7 @@ bool FractoriumOptionsDialog::Png16Bit() { return ui.Png16BitCheckBox->isChecked
bool FractoriumOptionsDialog::AutoUnique() { return ui.AutoUniqueCheckBox->isChecked(); } bool FractoriumOptionsDialog::AutoUnique() { return ui.AutoUniqueCheckBox->isChecked(); }
bool FractoriumOptionsDialog::LoadLast() { return ui.LoadLastOnStartCheckBox->isChecked(); } bool FractoriumOptionsDialog::LoadLast() { return ui.LoadLastOnStartCheckBox->isChecked(); }
bool FractoriumOptionsDialog::RotateAndScale() { return ui.RotateAndScaleCheckBox->isChecked(); } bool FractoriumOptionsDialog::RotateAndScale() { return ui.RotateAndScaleCheckBox->isChecked(); }
bool FractoriumOptionsDialog::Flam3Compat() { return ui.Flam3CompatCheckBox->isChecked(); }
uint FractoriumOptionsDialog::ThreadCount() { return ui.ThreadCountSpin->value(); } uint FractoriumOptionsDialog::ThreadCount() { return ui.ThreadCountSpin->value(); }
uint FractoriumOptionsDialog::RandomCount() { return ui.RandomCountSpin->value(); } uint FractoriumOptionsDialog::RandomCount() { return ui.RandomCountSpin->value(); }
uint FractoriumOptionsDialog::CpuQuality() { return ui.CpuQualitySpin->value(); } uint FractoriumOptionsDialog::CpuQuality() { return ui.CpuQualitySpin->value(); }
@ -196,6 +197,7 @@ void FractoriumOptionsDialog::GuiToData()
m_Settings->RandomCount(RandomCount()); m_Settings->RandomCount(RandomCount());
m_Settings->LoadLast(LoadLast()); m_Settings->LoadLast(LoadLast());
m_Settings->RotateAndScale(RotateAndScale()); m_Settings->RotateAndScale(RotateAndScale());
m_Settings->Flam3Compat(Flam3Compat());
m_Settings->CpuQuality(CpuQuality()); m_Settings->CpuQuality(CpuQuality());
m_Settings->OpenClQuality(OpenClQuality()); m_Settings->OpenClQuality(OpenClQuality());
m_Settings->CpuSubBatch(ui.CpuSubBatchSpin->value()); m_Settings->CpuSubBatch(ui.CpuSubBatchSpin->value());
@ -236,6 +238,7 @@ void FractoriumOptionsDialog::DataToGui()
ui.RandomCountSpin->setValue(m_Settings->RandomCount()); ui.RandomCountSpin->setValue(m_Settings->RandomCount());
ui.LoadLastOnStartCheckBox->setChecked(m_Settings->LoadLast()); ui.LoadLastOnStartCheckBox->setChecked(m_Settings->LoadLast());
ui.RotateAndScaleCheckBox->setChecked(m_Settings->RotateAndScale()); ui.RotateAndScaleCheckBox->setChecked(m_Settings->RotateAndScale());
ui.Flam3CompatCheckBox->setChecked(m_Settings->Flam3Compat());
ui.CpuQualitySpin->setValue(m_Settings->CpuQuality()); ui.CpuQualitySpin->setValue(m_Settings->CpuQuality());
ui.OpenCLQualitySpin->setValue(m_Settings->OpenClQuality()); ui.OpenCLQualitySpin->setValue(m_Settings->OpenClQuality());
ui.CpuSubBatchSpin->setValue(m_Settings->CpuSubBatch()); ui.CpuSubBatchSpin->setValue(m_Settings->CpuSubBatch());

View File

@ -38,6 +38,7 @@ public:
bool AutoUnique(); bool AutoUnique();
bool LoadLast(); bool LoadLast();
bool RotateAndScale(); bool RotateAndScale();
bool Flam3Compat();
uint ThreadCount(); uint ThreadCount();
uint RandomCount(); uint RandomCount();
uint CpuQuality(); uint CpuQuality();

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>546</width> <width>546</width>
<height>490</height> <height>512</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -538,6 +538,16 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="0">
<widget class="QCheckBox" name="Flam3CompatCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The behavior of the cos, cosh, cot, coth, csc, csch, sec, sech, sin, sinh, tan and tanh variations are different in flam3/Apophysis versus Chaotica.&lt;/p&gt;&lt;p&gt;Checked: use the Apophysis behavior.&lt;/p&gt;&lt;p&gt;Unchecked: use the Chaotica behavior.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Flam3 Compatibility</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="OptionsXmlSavingTab"> <widget class="QWidget" name="OptionsXmlSavingTab">