--User changes

-Add animation sequence creation to Fractorium.
 -Add two new options to EmberGenome which are used when generating an animation sequence.:
  --startcount: Add this number to the filename of each flame.
  --padding: Override the automatically calculated amount of padding zeroes added to each filename.

--Bug fixes
 -Prevent filenames in command line programs from using scientific notation when rendering a large number of frames.
 -Fix tab orders to match newer GUI items which were overlooked in previous releases.
 -Re-render previews if transparency value in the options dialog was changed. Re-rendering was previously only done if early clip or y axis up was changed.
 -Use transparency when rendering thumbnail previews.

--Code changes
 -Wrap EmberCommon.h in a namespace called EmberCommon.
 -Move FormatName() from EmberGenome to EmberCommon.h/cpp
 -Add a prefix parameter to EmberFile::DefaultFilename() to allow for creating a default filename for sequences.
 -When showing the final render dialog, allow specifying where it came from: the toolbar or the render sequence button.
 -Refactor all preview rendering code out into its own class hierarchy with overrides for the main window and the final render dialog.
 -Remove all preview render cancelling functions, they are now built into the new class hierarchy and a new render will not start until the previous one is stopped.
 -Add two new function ConstrainLow() and ConstrainHigh() which wrap constraining two min/max spinboxes to each others' values.
 -Add a bool to FractoriumEmberControllerBase::CopyEmberFile() to specify whether to copy the main file or the sequence file. This is somewhat of a hack and was done in a rush.
 -Add a bool to FractoriumEmberControllerBase::SetEmberFile() to specify whether to move the file rather than copy. This is used in FinalRenderEmberController and improves efficiency.
 -Add wrapper functions for variations filter dialog settings.
This commit is contained in:
mfeemster 2016-06-11 17:47:03 -07:00
parent 51c1cc7a83
commit c91171d392
27 changed files with 1734 additions and 450 deletions

View File

@ -1323,6 +1323,16 @@ public:
m_Comment = comment; m_Comment = comment;
} }
/// <summary>
/// Set stagger value.
/// Greater than 0 means interpolate xforms one at a time, else interpolate all at once.
/// </summary>
/// <param name="stagger">The stagger value to set.</param>
void Stagger(T stagger)
{
m_Stagger = stagger;
}
private: private:
bool m_Smooth = true; bool m_Smooth = true;
intmax_t m_SheepGen = -1; intmax_t m_SheepGen = -1;

View File

@ -2,6 +2,8 @@
#include "EmberAnimate.h" #include "EmberAnimate.h"
#include "JpegUtils.h" #include "JpegUtils.h"
using namespace EmberCommon;
/// <summary> /// <summary>
/// The core of the EmberAnimate.exe program. /// The core of the EmberAnimate.exe program.
/// Template argument expected to be float or double. /// Template argument expected to be float or double.
@ -368,7 +370,7 @@ bool EmberAnimate(EmberOptions& opt)
break; break;
} }
fnstream << inputPath << opt.Prefix() << setfill('0') << setw(padding) << ftime << opt.Suffix() << "." << opt.Format(); fnstream << inputPath << opt.Prefix() << setfill('0') << setprecision(0) << fixed << setw(padding) << ftime << opt.Suffix() << "." << opt.Format();
filename = fnstream.str(); filename = fnstream.str();
fnstream.str(""); fnstream.str("");

View File

@ -7,6 +7,8 @@
/// Ember and its derivatives. /// Ember and its derivatives.
/// </summary> /// </summary>
namespace EmberCommon
{
/// <summary> /// <summary>
/// Derivation of the RenderCallback class to do custom printing action /// Derivation of the RenderCallback class to do custom printing action
/// whenever the progress function is internally called inside of Ember /// whenever the progress function is internally called inside of Ember
@ -147,6 +149,20 @@ static bool InitPaletteList(const string& filename)
return true; return true;
} }
/// <summary>
/// Formats a filename with digits using the passed in amount of 0 padding.
/// </summary>
/// <param name="result">The ember whose name will be set</param>
/// <param name="os">The ostringstream which will be used to format</param>
/// <param name="padding">The amount of padding to use</param>
template <typename T>
void FormatName(Ember<T>& result, ostringstream& os, streamsize padding)
{
os << std::setw(padding) << result.m_Time;
result.m_Name = os.str();
os.str("");
}
/// <summary> /// <summary>
/// Convert an RGBA buffer to an RGB buffer. /// Convert an RGBA buffer to an RGB buffer.
/// The two buffers can point to the same memory location if needed. /// The two buffers can point to the same memory location if needed.
@ -371,7 +387,7 @@ static vector<unique_ptr<Renderer<T, float>>> CreateRenderers(eRendererType rend
else else
{ {
s = "CPU"; s = "CPU";
v.push_back(std::move(unique_ptr<Renderer<T, float>>(::CreateRenderer<T>(eRendererType::CPU_RENDERER, devices, shared, texId, errorReport)))); v.push_back(std::move(unique_ptr<Renderer<T, float>>(EmberCommon::CreateRenderer<T>(eRendererType::CPU_RENDERER, devices, shared, texId, errorReport))));
} }
} }
catch (const std::exception& e) catch (const std::exception& e)
@ -388,7 +404,7 @@ static vector<unique_ptr<Renderer<T, float>>> CreateRenderers(eRendererType rend
try try
{ {
s = "CPU"; s = "CPU";
v.push_back(std::move(unique_ptr<Renderer<T, float>>(::CreateRenderer<T>(eRendererType::CPU_RENDERER, devices, shared, texId, errorReport)))); v.push_back(std::move(unique_ptr<Renderer<T, float>>(EmberCommon::CreateRenderer<T>(eRendererType::CPU_RENDERER, devices, shared, texId, errorReport))));
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
@ -628,6 +644,7 @@ static vector<const Variation<T>*> FindVarsWithout(const vector<const Variation<
return vec; return vec;
} }
}
/// <summary> /// <summary>
/// Simple macro to print a string if the --verbose options has been specified. /// Simple macro to print a string if the --verbose options has been specified.

View File

@ -87,6 +87,8 @@ enum class eOptionIDs : et
OPT_REPEAT, OPT_REPEAT,
OPT_TRIES, OPT_TRIES,
OPT_MAX_XFORMS, OPT_MAX_XFORMS,
OPT_START_COUNT,
OPT_PADDING,
OPT_PRIORITY, OPT_PRIORITY,
OPT_SS,//Float value args. OPT_SS,//Float value args.
@ -370,6 +372,8 @@ public:
INITUINTOPTION(Repeat, Eou(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_REPEAT, _T("--repeat"), 1, SO_REQ_SEP, "\t--repeat=<val> Number of new flames to create. Ignored if sequence, inter or rotate were specified [default: 1].\n")); INITUINTOPTION(Repeat, Eou(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_REPEAT, _T("--repeat"), 1, SO_REQ_SEP, "\t--repeat=<val> Number of new flames to create. Ignored if sequence, inter or rotate were specified [default: 1].\n"));
INITUINTOPTION(Tries, Eou(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_TRIES, _T("--tries"), 10, SO_REQ_SEP, "\t--tries=<val> Number times to try creating a flame that meets the specified constraints. Ignored if sequence, inter or rotate were specified [default: 10].\n")); INITUINTOPTION(Tries, Eou(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_TRIES, _T("--tries"), 10, SO_REQ_SEP, "\t--tries=<val> Number times to try creating a flame that meets the specified constraints. Ignored if sequence, inter or rotate were specified [default: 10].\n"));
INITUINTOPTION(MaxXforms, Eou(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_MAX_XFORMS, _T("--maxxforms"), UINT_MAX, SO_REQ_SEP, "\t--maxxforms=<val> The maximum number of xforms allowed in the final output.\n")); INITUINTOPTION(MaxXforms, Eou(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_MAX_XFORMS, _T("--maxxforms"), UINT_MAX, SO_REQ_SEP, "\t--maxxforms=<val> The maximum number of xforms allowed in the final output.\n"));
INITUINTOPTION(StartCount, Eou(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_START_COUNT, _T("--startcount"), 0, SO_REQ_SEP, "\t--startcount=<val> The number to add to each flame name when generating a sequence. Useful for programs like ffmpeg which require numerically increasing filenames [default: 0].\n"));
INITUINTOPTION(Padding, Eou(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_PADDING, _T("--padding"), 0, SO_REQ_SEP, "\t--padding=<val> Override the amount of zero padding added to each flame name when generating a sequence. Useful for programs like ffmpeg which require fixed width filenames [default: 0 (auto calculate padding)].\n"));
//Double. //Double.
INITDOUBLEOPTION(SizeScale, Eod(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_SS, _T("--ss"), 1, SO_REQ_SEP, "\t--ss=<val> Size scale. All dimensions are scaled by this amount [default: 1.0].\n")); INITDOUBLEOPTION(SizeScale, Eod(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_SS, _T("--ss"), 1, SO_REQ_SEP, "\t--ss=<val> Size scale. All dimensions are scaled by this amount [default: 1.0].\n"));
INITDOUBLEOPTION(QualityScale, Eod(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_QS, _T("--qs"), 1, SO_REQ_SEP, "\t--qs=<val> Quality scale. All quality values are scaled by this amount [default: 1.0].\n")); INITDOUBLEOPTION(QualityScale, Eod(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_QS, _T("--qs"), 1, SO_REQ_SEP, "\t--qs=<val> Quality scale. All quality values are scaled by this amount [default: 1.0].\n"));
@ -509,6 +513,8 @@ public:
PARSEUINTOPTION(eOptionIDs::OPT_REPEAT, Repeat); PARSEUINTOPTION(eOptionIDs::OPT_REPEAT, Repeat);
PARSEUINTOPTION(eOptionIDs::OPT_TRIES, Tries); PARSEUINTOPTION(eOptionIDs::OPT_TRIES, Tries);
PARSEUINTOPTION(eOptionIDs::OPT_MAX_XFORMS, MaxXforms); PARSEUINTOPTION(eOptionIDs::OPT_MAX_XFORMS, MaxXforms);
PARSEUINTOPTION(eOptionIDs::OPT_START_COUNT, StartCount);
PARSEUINTOPTION(eOptionIDs::OPT_PADDING, Padding);
PARSEDOUBLEOPTION(eOptionIDs::OPT_SS, SizeScale);//Float args. PARSEDOUBLEOPTION(eOptionIDs::OPT_SS, SizeScale);//Float args.
PARSEDOUBLEOPTION(eOptionIDs::OPT_QS, QualityScale); PARSEDOUBLEOPTION(eOptionIDs::OPT_QS, QualityScale);
PARSEDOUBLEOPTION(eOptionIDs::OPT_QUALITY, Quality); PARSEDOUBLEOPTION(eOptionIDs::OPT_QUALITY, Quality);
@ -792,6 +798,8 @@ public:
Eou Repeat; Eou Repeat;
Eou Tries; Eou Tries;
Eou MaxXforms; Eou MaxXforms;
Eou StartCount;
Eou Padding;
Eod SizeScale;//Value double. Eod SizeScale;//Value double.
Eod QualityScale; Eod QualityScale;

View File

@ -3,6 +3,8 @@
#include "EmberGenome.h" #include "EmberGenome.h"
#include "JpegUtils.h" #include "JpegUtils.h"
using namespace EmberCommon;
/// <summary> /// <summary>
/// Set various default test values on the passed in ember. /// Set various default test values on the passed in ember.
/// </summary> /// </summary>
@ -34,14 +36,6 @@ void SetDefaultTestValues(Ember<T>& ember)
ember.m_CurveDE = T(0.6); ember.m_CurveDE = T(0.6);
} }
template <typename T>
void FormatName(Ember<T>& result, ostringstream& os, streamsize padding)
{
os << std::setw(padding) << result.m_Time;
result.m_Name = os.str();
os.str("");
}
/// <summary> /// <summary>
/// The core of the EmberGenome.exe program. /// The core of the EmberGenome.exe program.
/// Template argument expected to be float or double. /// Template argument expected to be float or double.
@ -470,18 +464,20 @@ bool EmberGenome(EmberOptions& opt)
frameCount = 0; frameCount = 0;
os.str(""); os.str("");
os << setfill('0'); os << setfill('0') << setprecision(0) << fixed;
auto padding = streamsize(std::log10(((opt.Frames() * opt.Loops()) + opt.Frames()) * embers.size())) + 1; auto padding = opt.Padding() ? streamsize(opt.Padding()) : (streamsize(std::log10(opt.StartCount() + (((opt.Frames() * opt.Loops()) + opt.Frames()) * embers.size()))) + 1);
t.Tic(); t.Tic();
for (i = 0; i < embers.size(); i++) for (i = 0; i < embers.size(); i++)
{ {
if (opt.Loops() > 0) if (opt.Loops() > 0)
{ {
for (frame = 0; frame < std::round(opt.Frames() * opt.Loops()); frame++) size_t roundFrames = size_t(std::round(opt.Frames() * opt.Loops()));
for (frame = 0; frame < roundFrames; frame++)
{ {
blend = T(frame) / T(opt.Frames()); blend = T(frame) / T(opt.Frames());
tools.Spin(embers[i], pTemplate, result, frameCount++, blend);//Result is cleared and reassigned each time inside of Spin(). tools.Spin(embers[i], pTemplate, result, opt.StartCount() + frameCount++, blend);//Result is cleared and reassigned each time inside of Spin().
FormatName(result, os, padding); FormatName(result, os, padding);
cout << emberToXml.ToString(result, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette()); cout << emberToXml.ToString(result, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
} }
@ -489,9 +485,9 @@ bool EmberGenome(EmberOptions& opt)
//The loop above will have rotated just shy of a complete rotation. //The loop above will have rotated just shy of a complete rotation.
//Rotate the next step and save in result, but do not print. //Rotate the next step and save in result, but do not print.
//result will be the starting point for the interp phase below. //result will be the starting point for the interp phase below.
frame = size_t(std::round(opt.Frames() * opt.Loops())); frame = roundFrames;
blend = T(frame) / T(opt.Frames()); blend = T(frame) / T(opt.Frames());
tools.Spin(embers[i], pTemplate, result, frameCount, blend);//Do not increment frameCount here. tools.Spin(embers[i], pTemplate, result, opt.StartCount() + frameCount, blend);//Do not increment frameCount here.
FormatName(result, os, padding); FormatName(result, os, padding);
} }
@ -502,18 +498,17 @@ bool EmberGenome(EmberOptions& opt)
for (frame = 0; frame < opt.Frames(); frame++) for (frame = 0; frame < opt.Frames(); frame++)
{ {
seqFlag = (frame == 0 || (frame == opt.Frames() - 1)); seqFlag = frame == 0 || (frame == opt.Frames() - 1);
blend = frame / T(opt.Frames()); blend = frame / T(opt.Frames());
result.Clear(); result.Clear();
tools.SpinInter(&embers[i], pTemplate, result, frameCount++, seqFlag, blend); tools.SpinInter(&embers[i], pTemplate, result, opt.StartCount() + frameCount++, seqFlag, blend);
FormatName(result, os, padding); FormatName(result, os, padding);
cout << emberToXml.ToString(result, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette()); cout << emberToXml.ToString(result, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
} }
} }
} }
result = embers.back(); tools.Spin(embers.back(), pTemplate, result, opt.StartCount() + frameCount, 0);
tools.Spin(embers.back(), pTemplate, result, frameCount, 0);
FormatName(result, os, padding); FormatName(result, os, padding);
cout << emberToXml.ToString(result, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette()); cout << emberToXml.ToString(result, opt.Extras(), opt.PrintEditDepth(), !opt.NoEdits(), opt.HexPalette());
t.Toc("Sequencing"); t.Toc("Sequencing");

View File

@ -2,7 +2,7 @@
#include "EmberRender.h" #include "EmberRender.h"
#include "JpegUtils.h" #include "JpegUtils.h"
//template <class OpenCLInfo> weak_ptr<OpenCLInfo> Singleton<OpenCLInfo>::m_Instance = weak_ptr<OpenCLInfo>(); using namespace EmberCommon;
/// <summary> /// <summary>
/// The core of the EmberRender.exe program. /// The core of the EmberRender.exe program.
@ -273,7 +273,7 @@ bool EmberRender(EmberOptions& opt)
else else
{ {
ostringstream fnstream; ostringstream fnstream;
fnstream << inputPath << opt.Prefix() << setfill('0') << setw(padding) << i << opt.Suffix() << "." << opt.Format(); fnstream << inputPath << opt.Prefix() << setfill('0') << setprecision(0) << fixed << setw(padding) << i << opt.Suffix() << "." << opt.Format();
filename = fnstream.str(); filename = fnstream.str();
} }

View File

@ -13,6 +13,7 @@
/// </summary> /// </summary>
using namespace EmberNs; using namespace EmberNs;
using namespace EmberCommon;
template <typename T> template <typename T>
void SaveFinalImage(Renderer<T, T>& renderer, vector<byte>& pixels, char* suffix) void SaveFinalImage(Renderer<T, T>& renderer, vector<byte>& pixels, char* suffix)

View File

@ -159,9 +159,9 @@ public:
/// Return the default filename based on the current date/time. /// Return the default filename based on the current date/time.
/// </summary> /// </summary>
/// <returns>The default filename</returns> /// <returns>The default filename</returns>
static QString DefaultFilename() static QString DefaultFilename(QString prefix = "Flame_")
{ {
return "Flame_" + QDateTime(QDateTime::currentDateTime()).toString("yyyy-MM-dd-hhmmss"); return prefix + QDateTime(QDateTime::currentDateTime()).toString("yyyy-MM-dd-hhmmss");
} }
/// <summary> /// <summary>

View File

@ -189,6 +189,16 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(FractoriumSettings* set
w = SetTabOrder(this, w, ui.FinalRenderCloseButton); w = SetTabOrder(this, w, ui.FinalRenderCloseButton);
} }
/// <summary>
/// Show the final render dialog and specify whether it was called from the toolbar or the sequence render button.
/// </summary>
/// <param name="fromSequence">True if this is called from the sequence render button, else false.</param>
void FractoriumFinalRenderDialog::Show(bool fromSequence)
{
m_FromSequence = fromSequence;
show();
}
/// <summary> /// <summary>
/// GUI settings wrapper functions, getters only. /// GUI settings wrapper functions, getters only.
/// </summary> /// </summary>
@ -636,7 +646,7 @@ void FractoriumFinalRenderDialog::OnCancelRenderClicked(bool checked)
/// <param name="e">The event</param> /// <param name="e">The event</param>
void FractoriumFinalRenderDialog::showEvent(QShowEvent* e) void FractoriumFinalRenderDialog::showEvent(QShowEvent* e)
{ {
if (m_Controller.get() && m_Controller->m_Run) if (m_Controller.get() && m_Controller->m_Run)//On Linux, this event will be called when the main window minimized/maximized while rendering, so filter it out.
return; return;
if (CreateControllerFromGUI(true))//Create controller if it does not exist, or if it does and the renderer is not running. if (CreateControllerFromGUI(true))//Create controller if it does not exist, or if it does and the renderer is not running.
@ -645,16 +655,17 @@ void FractoriumFinalRenderDialog::showEvent(QShowEvent* e)
#ifdef DO_DOUBLE #ifdef DO_DOUBLE
Ember<double> ed; Ember<double> ed;
EmberFile<double> efi; EmberFile<double> efi;
m_Fractorium->m_Controller->CopyEmberFile(efi, [&](Ember<double>& ember) m_Fractorium->m_Controller->CopyEmberFile(efi, m_FromSequence, [&](Ember<double>& ember)
{ {
ember.SyncSize(); ember.SyncSize();
ember.m_Quality = m_Settings->FinalQuality(); ember.m_Quality = m_Settings->FinalQuality();
ember.m_Supersample = m_Settings->FinalSupersample(); ember.m_Supersample = m_Settings->FinalSupersample();
ember.m_TemporalSamples = m_Settings->FinalTemporalSamples();
});//Copy the whole file, will take about 0.2ms per ember in the file. });//Copy the whole file, will take about 0.2ms per ember in the file.
#else #else
Ember<float> ed; Ember<float> ed;
EmberFile<float> efi; EmberFile<float> efi;
m_Fractorium->m_Controller->CopyEmberFile(efi, [&](Ember<float>& ember) m_Fractorium->m_Controller->CopyEmberFile(efi, m_FromSequence, [&](Ember<float>& ember)
{ {
ember.SyncSize(); ember.SyncSize();
ember.m_Quality = m_Settings->FinalQuality(); ember.m_Quality = m_Settings->FinalQuality();
@ -662,7 +673,7 @@ void FractoriumFinalRenderDialog::showEvent(QShowEvent* e)
ember.m_TemporalSamples = m_Settings->FinalTemporalSamples(); ember.m_TemporalSamples = m_Settings->FinalTemporalSamples();
});//Copy the whole file, will take about 0.2ms per ember in the file. });//Copy the whole file, will take about 0.2ms per ember in the file.
#endif #endif
m_Controller->SetEmberFile(efi);//Copy the temp file into the final render controller. m_Controller->SetEmberFile(efi, true);//Move the temp file into the final render controller.
ui.FinalRenderCurrentSpin->setMaximum(int(efi.Size())); ui.FinalRenderCurrentSpin->setMaximum(int(efi.Size()));
ui.FinalRenderCurrentSpin->blockSignals(true); ui.FinalRenderCurrentSpin->blockSignals(true);
ui.FinalRenderCurrentSpin->setValue(index);//Set the currently selected ember to the one that was being edited. ui.FinalRenderCurrentSpin->setValue(index);//Set the currently selected ember to the one that was being edited.
@ -677,6 +688,12 @@ void FractoriumFinalRenderDialog::showEvent(QShowEvent* e)
Path(m_Controller->ComposePath(m_Controller->Name()));//Update the GUI. Path(m_Controller->ComposePath(m_Controller->Name()));//Update the GUI.
} }
if (m_FromSequence)
{
ui.FinalRenderDoAllCheckBox->setChecked(true);
ui.FinalRenderDoSequenceCheckBox->setChecked(true);
}
ui.FinalRenderTextOutput->clear(); ui.FinalRenderTextOutput->clear();
QDialog::showEvent(e); QDialog::showEvent(e);
} }
@ -726,9 +743,9 @@ bool FractoriumFinalRenderDialog::CreateControllerFromGUI(bool createRenderer)
if (m_Controller.get()) if (m_Controller.get())
{ {
#ifdef DO_DOUBLE #ifdef DO_DOUBLE
m_Controller->CopyEmberFile(efd, [&](Ember<double>& ember) { });//Convert float to double or save double verbatim; m_Controller->CopyEmberFile(efd, false, [&](Ember<double>& ember) { });//Convert float to double or save double verbatim;
#else #else
m_Controller->CopyEmberFile(efd, [&](Ember<float>& ember) { });//Convert float to double or save double verbatim; m_Controller->CopyEmberFile(efd, false, [&](Ember<float>& ember) { });//Convert float to double or save double verbatim;
#endif #endif
m_Controller->Shutdown(); m_Controller->Shutdown();
} }
@ -745,7 +762,7 @@ bool FractoriumFinalRenderDialog::CreateControllerFromGUI(bool createRenderer)
//Restore the ember and ember file. //Restore the ember and ember file.
if (m_Controller.get()) if (m_Controller.get())
{ {
m_Controller->SetEmberFile(efd);//Convert float to double or set double verbatim; m_Controller->SetEmberFile(efd, true);//Convert float to double and move or move double verbatim.
m_Controller->SetEmber(index, false); m_Controller->SetEmber(index, false);
} }
} }

View File

@ -40,13 +40,18 @@ class FractoriumFinalRenderDialog : public QDialog
friend Fractorium; friend Fractorium;
friend FinalRenderEmberControllerBase; friend FinalRenderEmberControllerBase;
friend FinalRenderEmberController<float>; friend FinalRenderEmberController<float>;
friend PreviewRenderer<float>;
friend FinalRenderPreviewRenderer<float>;
#ifdef DO_DOUBLE #ifdef DO_DOUBLE
friend FinalRenderEmberController<double>; friend FinalRenderEmberController<double>;
friend PreviewRenderer<double>;
friend FinalRenderPreviewRenderer<double>;
#endif #endif
public: public:
FractoriumFinalRenderDialog(FractoriumSettings* settings, QWidget* p, Qt::WindowFlags f = 0); FractoriumFinalRenderDialog(FractoriumSettings* settings, QWidget* p, Qt::WindowFlags f = 0);
void Show(bool fromSequence);
bool EarlyClip(); bool EarlyClip();
bool YAxisUp(); bool YAxisUp();
bool Transparency(); bool Transparency();
@ -113,6 +118,7 @@ private:
bool CreateControllerFromGUI(bool createRenderer); bool CreateControllerFromGUI(bool createRenderer);
bool SetMemory(); bool SetMemory();
bool m_FromSequence;
int m_MemoryCellIndex; int m_MemoryCellIndex;
int m_ItersCellIndex; int m_ItersCellIndex;
int m_PathCellIndex; int m_PathCellIndex;

View File

@ -101,50 +101,7 @@ template<typename T>
FinalRenderEmberController<T>::FinalRenderEmberController(FractoriumFinalRenderDialog* finalRender) FinalRenderEmberController<T>::FinalRenderEmberController(FractoriumFinalRenderDialog* finalRender)
: FinalRenderEmberControllerBase(finalRender) : FinalRenderEmberControllerBase(finalRender)
{ {
m_FinalPreviewRenderer = make_unique<EmberNs::Renderer<T, float>>(); m_FinalPreviewRenderer = make_unique<FinalRenderPreviewRenderer<T>>(this);
m_FinalPreviewRenderer->Callback(nullptr);
m_FinalPreviewRenderer->NumChannels(4);
m_FinalPreviewRenderFunc = [&]()
{
rlg l(m_PreviewCs);//Thread prep.
m_PreviewRun = true;
m_FinalPreviewRenderer->Abort();
T scalePercentage;
size_t maxDim = 100;
QLabel* widget = m_FinalRenderDialog->ui.FinalRenderPreviewLabel;
//Determine how to scale the scaled ember to fit in the label with a max of 100x100.
if (m_Ember->m_FinalRasW >= m_Ember->m_FinalRasH)
scalePercentage = T(maxDim) / m_Ember->m_FinalRasW;
else
scalePercentage = T(maxDim) / m_Ember->m_FinalRasH;
m_PreviewEmber = *m_Ember;
m_PreviewEmber.m_Quality = 100;
m_PreviewEmber.m_TemporalSamples = 1;
m_PreviewEmber.m_FinalRasW = std::max<size_t>(1, std::min<size_t>(maxDim, size_t(scalePercentage * m_Ember->m_FinalRasW)));//Ensure neither is zero.
m_PreviewEmber.m_FinalRasH = std::max<size_t>(1, std::min<size_t>(maxDim, size_t(scalePercentage * m_Ember->m_FinalRasH)));
m_PreviewEmber.m_PixelsPerUnit = scalePercentage * m_Ember->m_PixelsPerUnit;
m_FinalPreviewRenderer->EarlyClip(m_FinalRenderDialog->EarlyClip());
m_FinalPreviewRenderer->YAxisUp(m_FinalRenderDialog->YAxisUp());
m_FinalPreviewRenderer->Transparency(m_FinalRenderDialog->Transparency());
m_FinalPreviewRenderer->SetEmber(m_PreviewEmber);
m_FinalPreviewRenderer->PrepFinalAccumVector(m_PreviewFinalImage);//Must manually call this first because it could be erroneously made smaller due to strips if called inside Renderer::Run().
auto strips = VerifyStrips(m_PreviewEmber.m_FinalRasH, m_FinalRenderDialog->Strips(),
[&](const string & s) { }, [&](const string & s) { }, [&](const string & s) { });
StripsRender<T>(m_FinalPreviewRenderer.get(), m_PreviewEmber, m_PreviewFinalImage, 0, strips, m_FinalRenderDialog->YAxisUp(),
[&](size_t strip) { },//Pre strip.
[&](size_t strip) { },//Post strip.
[&](size_t strip) { },//Error.
[&](Ember<T>& finalEmber)//Final strip.
{
QImage image(int(finalEmber.m_FinalRasW), int(finalEmber.m_FinalRasH), QImage::Format_RGBA8888);//The label wants RGBA.
memcpy(image.scanLine(0), m_PreviewFinalImage.data(), finalEmber.m_FinalRasW * finalEmber.m_FinalRasH * 4);//Memcpy the data in.
QPixmap pixmap(QPixmap::fromImage(image));
QMetaObject::invokeMethod(widget, "setPixmap", Qt::QueuedConnection, Q_ARG(QPixmap, pixmap));
});
m_PreviewRun = false;
};
//The main rendering function which will be called in a Qt thread. //The main rendering function which will be called in a Qt thread.
//A backup Xml is made before the rendering process starts just in case it crashes before finishing. //A backup Xml is made before the rendering process starts just in case it crashes before finishing.
//If it finishes successfully, delete the backup file. //If it finishes successfully, delete the backup file.
@ -380,24 +337,24 @@ FinalRenderEmberController<T>::FinalRenderEmberController(FractoriumFinalRenderD
/// These are used to preserve the current ember/file when switching between renderers. /// These are used to preserve the current ember/file when switching between renderers.
/// Note that some precision will be lost when going from double to float. /// Note that some precision will be lost when going from double to float.
/// </summary> /// </summary>
template <typename T> void FinalRenderEmberController<T>::SetEmberFile(const EmberFile<float>& emberFile) template <typename T> void FinalRenderEmberController<T>::SetEmberFile(const EmberFile<float>& emberFile, bool move)
{ {
m_EmberFile = emberFile; move ? m_EmberFile = std::move(emberFile) : m_EmberFile = emberFile;
m_Ember = m_EmberFile.Get(0); m_Ember = m_EmberFile.Get(0);
} }
template <typename T> void FinalRenderEmberController<T>::CopyEmberFile(EmberFile<float>& emberFile, std::function<void(Ember<float>& ember)> perEmberOperation) template <typename T> void FinalRenderEmberController<T>::CopyEmberFile(EmberFile<float>& emberFile, bool sequence, std::function<void(Ember<float>& ember)> perEmberOperation)
{ {
emberFile.m_Filename = m_EmberFile.m_Filename; emberFile.m_Filename = m_EmberFile.m_Filename;
CopyCont(emberFile.m_Embers, m_EmberFile.m_Embers, perEmberOperation); CopyCont(emberFile.m_Embers, m_EmberFile.m_Embers, perEmberOperation);
} }
#ifdef DO_DOUBLE #ifdef DO_DOUBLE
template <typename T> void FinalRenderEmberController<T>::SetEmberFile(const EmberFile<double>& emberFile) template <typename T> void FinalRenderEmberController<T>::SetEmberFile(const EmberFile<double>& emberFile, bool move)
{ {
m_EmberFile = emberFile; move ? m_EmberFile = std::move(emberFile) : m_EmberFile = emberFile;
m_Ember = m_EmberFile.Get(0); m_Ember = m_EmberFile.Get(0);
} }
template <typename T> void FinalRenderEmberController<T>::CopyEmberFile(EmberFile<double>& emberFile, std::function<void(Ember<double>& ember)> perEmberOperation) template <typename T> void FinalRenderEmberController<T>::CopyEmberFile(EmberFile<double>& emberFile, bool sequence, std::function<void(Ember<double>& ember)> perEmberOperation)
{ {
emberFile.m_Filename = m_EmberFile.m_Filename; emberFile.m_Filename = m_EmberFile.m_Filename;
CopyCont(emberFile.m_Embers, m_EmberFile.m_Embers, perEmberOperation); CopyCont(emberFile.m_Embers, m_EmberFile.m_Embers, perEmberOperation);
@ -692,8 +649,7 @@ tuple<size_t, size_t, size_t> FinalRenderEmberController<T>::SyncAndComputeMemor
m_Renderer->ComputeBounds(); m_Renderer->ComputeBounds();
m_Renderer->ComputeQuality(); m_Renderer->ComputeQuality();
m_Renderer->ComputeCamera(); m_Renderer->ComputeCamera();
CancelPreviewRender(); m_FinalPreviewRenderer->Render(UINT_MAX, UINT_MAX);
m_FinalPreviewRenderFunc();
p = m_Renderer->MemoryRequired(strips, true, m_FinalRenderDialog->DoSequence()); p = m_Renderer->MemoryRequired(strips, true, m_FinalRenderDialog->DoSequence());
iterCount = m_Renderer->TotalIterCount(strips); iterCount = m_Renderer->TotalIterCount(strips);
} }
@ -710,8 +666,7 @@ tuple<size_t, size_t, size_t> FinalRenderEmberController<T>::SyncAndComputeMemor
renderer->ComputeCamera(); renderer->ComputeCamera();
} }
CancelPreviewRender(); m_FinalPreviewRenderer->Render(UINT_MAX, UINT_MAX);
m_FinalPreviewRenderFunc();
strips = 1; strips = 1;
p = m_Renderers[0]->MemoryRequired(1, true, m_FinalRenderDialog->DoSequence()); p = m_Renderers[0]->MemoryRequired(1, true, m_FinalRenderDialog->DoSequence());
iterCount = m_Renderers[0]->TotalIterCount(strips); iterCount = m_Renderers[0]->TotalIterCount(strips);
@ -759,22 +714,6 @@ EmberNs::Renderer<T, float>* FinalRenderEmberController<T>::FirstOrDefaultRender
} }
} }
/// <summary>
/// Stop the preview renderer.
/// This is meant to only be called programatically and never by the user.
/// </summary>
template <typename T>
void FinalRenderEmberController<T>::CancelPreviewRender()
{
m_FinalPreviewRenderer->Abort();
while (m_FinalPreviewRenderer->InRender()) { QApplication::processEvents(); }
while (m_PreviewRun) { QApplication::processEvents(); }
while (m_FinalPreviewResult.isRunning()) { QApplication::processEvents(); }
}
/// <summary> /// <summary>
/// Save the output of the render. /// Save the output of the render.
/// </summary> /// </summary>
@ -1017,6 +956,56 @@ QString FinalRenderEmberController<T>::CheckMemory(const tuple<size_t, size_t, s
return s; return s;
} }
/// <summary>
/// Thin derivation to handle preview rendering that is specific to the final render dialog.
/// This differs from the preview renderers on the main window because they render multiple embers
/// to a tree, whereas this renders a single preview.
/// </summary>
/// <param name="start">Ignored</param>
/// <param name="end">Ignored</param>
template <typename T>
void FinalRenderPreviewRenderer<T>::PreviewRenderFunc(uint start, uint end)
{
T scalePercentage;
size_t maxDim = 100;
auto d = m_Controller->m_FinalRenderDialog;
QLabel* widget = d->ui.FinalRenderPreviewLabel;
//Determine how to scale the scaled ember to fit in the label with a max of 100x100.
auto e = m_Controller->m_Ember;
if (e->m_FinalRasW >= e->m_FinalRasH)
scalePercentage = T(maxDim) / e->m_FinalRasW;
else
scalePercentage = T(maxDim) / e->m_FinalRasH;
m_PreviewEmber = *e;
m_PreviewEmber.m_Quality = 100;
m_PreviewEmber.m_TemporalSamples = 1;
m_PreviewEmber.m_FinalRasW = std::max<size_t>(1, std::min<size_t>(maxDim, size_t(scalePercentage * e->m_FinalRasW)));//Ensure neither is zero.
m_PreviewEmber.m_FinalRasH = std::max<size_t>(1, std::min<size_t>(maxDim, size_t(scalePercentage * e->m_FinalRasH)));
m_PreviewEmber.m_PixelsPerUnit = scalePercentage * e->m_PixelsPerUnit;
m_PreviewRenderer.EarlyClip(d->EarlyClip());
m_PreviewRenderer.YAxisUp(d->YAxisUp());
m_PreviewRenderer.Transparency(d->Transparency());
m_PreviewRenderer.Callback(nullptr);
m_PreviewRenderer.NumChannels(4);
m_PreviewRenderer.SetEmber(m_PreviewEmber);
m_PreviewRenderer.PrepFinalAccumVector(m_PreviewFinalImage);//Must manually call this first because it could be erroneously made smaller due to strips if called inside Renderer::Run().
auto strips = VerifyStrips(m_PreviewEmber.m_FinalRasH, d->Strips(),
[&](const string & s) {}, [&](const string & s) {}, [&](const string & s) {});
StripsRender<T>(&m_PreviewRenderer, m_PreviewEmber, m_PreviewFinalImage, 0, strips, d->YAxisUp(),
[&](size_t strip) {},//Pre strip.
[&](size_t strip) {},//Post strip.
[&](size_t strip) {},//Error.
[&](Ember<T>& finalEmber)//Final strip.
{
QImage image(int(finalEmber.m_FinalRasW), int(finalEmber.m_FinalRasH), QImage::Format_RGBA8888);//The label wants RGBA.
memcpy(image.scanLine(0), m_PreviewFinalImage.data(), finalEmber.m_FinalRasW * finalEmber.m_FinalRasH * 4);//Memcpy the data in.
QPixmap pixmap(QPixmap::fromImage(image));
QMetaObject::invokeMethod(widget, "setPixmap", Qt::QueuedConnection, Q_ARG(QPixmap, pixmap));
});
}
template class FinalRenderEmberController<float>; template class FinalRenderEmberController<float>;
#ifdef DO_DOUBLE #ifdef DO_DOUBLE

View File

@ -13,6 +13,7 @@
/// </summary> /// </summary>
class Fractorium; class Fractorium;
class FractoriumFinalRenderDialog; class FractoriumFinalRenderDialog;
template <typename T> class FinalRenderPreviewRenderer;
/// <summary> /// <summary>
/// Used to hold the options specified in the current state of the Gui for performing the final render. /// Used to hold the options specified in the current state of the Gui for performing the final render.
@ -74,19 +75,16 @@ public:
protected: protected:
bool m_Run = false; bool m_Run = false;
bool m_PreviewRun = false;
size_t m_ImageCount = 0; size_t m_ImageCount = 0;
std::atomic<size_t> m_FinishedImageCount; std::atomic<size_t> m_FinishedImageCount;
QFuture<void> m_Result; QFuture<void> m_Result;
QFuture<void> m_FinalPreviewResult;
std::function<void (void)> m_FinalRenderFunc; std::function<void (void)> m_FinalRenderFunc;
std::function<void (void)> m_FinalPreviewRenderFunc;
FractoriumSettings* m_Settings; FractoriumSettings* m_Settings;
FractoriumFinalRenderDialog* m_FinalRenderDialog; FractoriumFinalRenderDialog* m_FinalRenderDialog;
FinalRenderGuiState m_GuiState; FinalRenderGuiState m_GuiState;
std::recursive_mutex m_PreviewCs, m_ProgressCs; std::recursive_mutex m_ProgressCs;
Timing m_RenderTimer; Timing m_RenderTimer;
Timing m_TotalTimer; Timing m_TotalTimer;
}; };
@ -98,16 +96,18 @@ protected:
template<typename T> template<typename T>
class FinalRenderEmberController : public FinalRenderEmberControllerBase class FinalRenderEmberController : public FinalRenderEmberControllerBase
{ {
friend FinalRenderPreviewRenderer<T>;
public: public:
FinalRenderEmberController(FractoriumFinalRenderDialog* finalRender); FinalRenderEmberController(FractoriumFinalRenderDialog* finalRender);
virtual ~FinalRenderEmberController() { } virtual ~FinalRenderEmberController() { }
//Virtual functions overridden from FractoriumEmberControllerBase. //Virtual functions overridden from FractoriumEmberControllerBase.
virtual void SetEmberFile(const EmberFile<float>& emberFile) override; virtual void SetEmberFile(const EmberFile<float>& emberFile, bool move) override;
virtual void CopyEmberFile(EmberFile<float>& emberFile, std::function<void(Ember<float>& ember)> perEmberOperation/* = [&](Ember<float>& ember) { }*/) override; virtual void CopyEmberFile(EmberFile<float>& emberFile, bool sequence, std::function<void(Ember<float>& ember)> perEmberOperation/* = [&](Ember<float>& ember) { }*/) override;
#ifdef DO_DOUBLE #ifdef DO_DOUBLE
virtual void SetEmberFile(const EmberFile<double>& emberFile) override; virtual void SetEmberFile(const EmberFile<double>& emberFile, bool move) override;
virtual void CopyEmberFile(EmberFile<double>& emberFile, std::function<void(Ember<double>& ember)> perEmberOperation/* = [&](Ember<double>& ember) { }*/) override; virtual void CopyEmberFile(EmberFile<double>& emberFile, bool sequence, std::function<void(Ember<double>& ember)> perEmberOperation/* = [&](Ember<double>& ember) { }*/) override;
#endif #endif
virtual void SetEmber(size_t index, bool verbatim) override; virtual void SetEmber(size_t index, bool verbatim) override;
virtual bool Render() override; virtual bool Render() override;
@ -132,7 +132,6 @@ public:
EmberNs::Renderer<T, float>* FirstOrDefaultRenderer(); EmberNs::Renderer<T, float>* FirstOrDefaultRenderer();
protected: protected:
void CancelPreviewRender();
void HandleFinishedProgress(); void HandleFinishedProgress();
void SaveCurrentRender(Ember<T>& ember); void SaveCurrentRender(Ember<T>& ember);
void SaveCurrentRender(Ember<T>& ember, const EmberImageComments& comments, vector<byte>& pixels, size_t width, size_t height, size_t channels, size_t bpc); void SaveCurrentRender(Ember<T>& ember, const EmberImageComments& comments, vector<byte>& pixels, size_t width, size_t height, size_t channels, size_t bpc);
@ -143,10 +142,32 @@ protected:
void SetProgressComplete(int val); void SetProgressComplete(int val);
Ember<T>* m_Ember; Ember<T>* m_Ember;
Ember<T> m_PreviewEmber;
EmberFile<T> m_EmberFile; EmberFile<T> m_EmberFile;
EmberToXml<T> m_XmlWriter; EmberToXml<T> m_XmlWriter;
unique_ptr<EmberNs::Renderer<T, float>> m_FinalPreviewRenderer; unique_ptr<FinalRenderPreviewRenderer<T>> m_FinalPreviewRenderer;
vector<unique_ptr<EmberNs::Renderer<T, float>>> m_Renderers; vector<unique_ptr<EmberNs::Renderer<T, float>>> m_Renderers;
}; };
/// <summary>
/// Thin derivation to handle preview rendering that is specific to the final render dialog.
/// This differs from the preview renderers on the main window because they render multiple embers
/// to a tree, whereas this renders a single preview.
/// </summary>
template <typename T>
class FinalRenderPreviewRenderer : public PreviewRenderer<T>
{
public:
using PreviewRenderer<T>::m_PreviewRun;
using PreviewRenderer<T>::m_PreviewEmber;
using PreviewRenderer<T>::m_PreviewRenderer;
using PreviewRenderer<T>::m_PreviewFinalImage;
FinalRenderPreviewRenderer(FinalRenderEmberController<T>* controller) : m_Controller(controller)
{
}
virtual void PreviewRenderFunc(uint start, uint end) override;
private:
FinalRenderEmberController<T>* m_Controller;
};

View File

@ -198,6 +198,7 @@ Fractorium::Fractorium(QWidget* p)
/// </summary> /// </summary>
Fractorium::~Fractorium() Fractorium::~Fractorium()
{ {
SyncSequenceSettings();
m_VarDialog->SyncSettings(); m_VarDialog->SyncSettings();
m_Settings->setValue("windowState", saveState()); m_Settings->setValue("windowState", saveState());
m_Settings->sync(); m_Settings->sync();
@ -373,7 +374,7 @@ void Fractorium::closeEvent(QCloseEvent* e)
if (m_Controller.get()) if (m_Controller.get())
{ {
m_Controller->StopRenderTimer(true);//Will wait until fully exited and stopped. m_Controller->StopRenderTimer(true);//Will wait until fully exited and stopped.
m_Controller->StopPreviewRender(); m_Controller->StopAllPreviewRenderers();
} }
if (e) if (e)
@ -665,13 +666,15 @@ void Fractorium::ShowCritical(const QString& title, const QString& text, bool in
/// </summary> /// </summary>
void Fractorium::SetTabOrders() void Fractorium::SetTabOrders()
{ {
QWidget* w = SetTabOrder(this, ui.ColorTable, m_BrightnessSpin);//Flame. QWidget* w = SetTabOrder(this, ui.ColorTable, m_BrightnessSpin);//Flame color.
w = SetTabOrder(this, w, m_GammaSpin); w = SetTabOrder(this, w, m_GammaSpin);
w = SetTabOrder(this, w, m_GammaThresholdSpin); w = SetTabOrder(this, w, m_GammaThresholdSpin);
w = SetTabOrder(this, w, m_VibrancySpin); w = SetTabOrder(this, w, m_VibrancySpin);
w = SetTabOrder(this, w, m_HighlightSpin); w = SetTabOrder(this, w, m_HighlightSpin);
w = SetTabOrder(this, w, m_BackgroundColorButton); w = SetTabOrder(this, w, m_BackgroundColorButton);
w = SetTabOrder(this, w, m_PaletteModeCombo); w = SetTabOrder(this, w, m_PaletteModeCombo);
w = SetTabOrder(this, w, m_WidthSpin);//Flame geometry.
w = SetTabOrder(this, w, m_HeightSpin);
w = SetTabOrder(this, w, m_CenterXSpin); w = SetTabOrder(this, w, m_CenterXSpin);
w = SetTabOrder(this, w, m_CenterYSpin); w = SetTabOrder(this, w, m_CenterYSpin);
w = SetTabOrder(this, w, m_ScaleSpin); w = SetTabOrder(this, w, m_ScaleSpin);
@ -682,18 +685,44 @@ void Fractorium::SetTabOrders()
w = SetTabOrder(this, w, m_PitchSpin); w = SetTabOrder(this, w, m_PitchSpin);
w = SetTabOrder(this, w, m_YawSpin); w = SetTabOrder(this, w, m_YawSpin);
w = SetTabOrder(this, w, m_DepthBlurSpin); w = SetTabOrder(this, w, m_DepthBlurSpin);
w = SetTabOrder(this, w, m_SpatialFilterWidthSpin); w = SetTabOrder(this, w, m_SpatialFilterWidthSpin);//Flame filter.
w = SetTabOrder(this, w, m_SpatialFilterTypeCombo); w = SetTabOrder(this, w, m_SpatialFilterTypeCombo);
w = SetTabOrder(this, w, m_TemporalFilterTypeCombo);
w = SetTabOrder(this, w, m_DEFilterMinRadiusSpin); w = SetTabOrder(this, w, m_DEFilterMinRadiusSpin);
w = SetTabOrder(this, w, m_DEFilterMaxRadiusSpin); w = SetTabOrder(this, w, m_DEFilterMaxRadiusSpin);
w = SetTabOrder(this, w, m_DECurveSpin); w = SetTabOrder(this, w, m_DECurveSpin);
w = SetTabOrder(this, w, m_TemporalSamplesSpin); w = SetTabOrder(this, w, m_SbsSpin);//Flame iteration.
w = SetTabOrder(this, w, m_FuseSpin);
w = SetTabOrder(this, w, m_QualitySpin); w = SetTabOrder(this, w, m_QualitySpin);
w = SetTabOrder(this, w, m_SupersampleSpin); w = SetTabOrder(this, w, m_SupersampleSpin);
w = SetTabOrder(this, w, m_InterpTypeCombo);//Flame animation.
w = SetTabOrder(this, w, m_AffineInterpTypeCombo); w = SetTabOrder(this, w, m_AffineInterpTypeCombo);
w = SetTabOrder(this, w, m_InterpTypeCombo); w = SetTabOrder(this, w, m_TemporalSamplesSpin);
w = SetTabOrder(this, w, m_TemporalFilterWidthSpin);
w = SetTabOrder(this, w, m_TemporalFilterTypeCombo);
w = SetTabOrder(this, ui.LibraryTree, ui.SequenceStartCountSpinBox);//Library.
w = SetTabOrder(this, w, ui.SequenceStartPreviewsButton);
w = SetTabOrder(this, w, ui.SequenceStopPreviewsButton);
w = SetTabOrder(this, w, ui.SequenceStartFlameSpinBox);
w = SetTabOrder(this, w, ui.SequenceStopFlameSpinBox);
w = SetTabOrder(this, w, ui.SequenceAllButton);
w = SetTabOrder(this, w, ui.SequenceRandomizeStaggerCheckBox);
w = SetTabOrder(this, w, ui.SequenceStaggerCheckBox);
w = SetTabOrder(this, w, ui.SequenceRandomizeFramesPerRotCheckBox);
w = SetTabOrder(this, w, ui.SequenceFramesPerRotSpinBox);
w = SetTabOrder(this, w, ui.SequenceRandomFramesPerRotMaxSpinBox);
w = SetTabOrder(this, w, ui.SequenceRandomizeRotationsCheckBox);
w = SetTabOrder(this, w, ui.SequenceRotationsSpinBox);
w = SetTabOrder(this, w, ui.SequenceRandomRotationsMaxSpinBox);
w = SetTabOrder(this, w, ui.SequenceRandomizeBlendFramesCheckBox);
w = SetTabOrder(this, w, ui.SequenceBlendFramesSpinBox);
w = SetTabOrder(this, w, ui.SequenceRandomBlendMaxFramesSpinBox);
w = SetTabOrder(this, w, ui.SequenceGenerateButton);
w = SetTabOrder(this, w, ui.SequenceRenderButton);
w = SetTabOrder(this, w, ui.SequenceSaveButton);
w = SetTabOrder(this, w, ui.SequenceOpenButton);
w = SetTabOrder(this, w, ui.SequenceTree);
w = SetTabOrder(this, ui.CurrentXformCombo, ui.AddXformButton);//Xforms. w = SetTabOrder(this, ui.CurrentXformCombo, ui.AddXformButton);//Xforms.
w = SetTabOrder(this, w, ui.AddLinkedXformButton);
w = SetTabOrder(this, w, ui.DuplicateXformButton); w = SetTabOrder(this, w, ui.DuplicateXformButton);
w = SetTabOrder(this, w, ui.ClearXformButton); w = SetTabOrder(this, w, ui.ClearXformButton);
w = SetTabOrder(this, w, ui.DeleteXformButton); w = SetTabOrder(this, w, ui.DeleteXformButton);
@ -701,11 +730,14 @@ void Fractorium::SetTabOrders()
w = SetTabOrder(this, w, m_XformWeightSpin); w = SetTabOrder(this, w, m_XformWeightSpin);
w = SetTabOrder(this, w, m_XformWeightSpinnerButtonWidget->m_Button); w = SetTabOrder(this, w, m_XformWeightSpinnerButtonWidget->m_Button);
w = SetTabOrder(this, m_XformColorIndexSpin, ui.XformColorScroll);//Xforms color. w = SetTabOrder(this, m_XformColorIndexSpin, ui.XformColorScroll);//Xforms color.
w = SetTabOrder(this, w, ui.RandomColorIndicesButton);
w = SetTabOrder(this, w, ui.ToggleColorIndicesButton);
w = SetTabOrder(this, w, m_XformColorSpeedSpin); w = SetTabOrder(this, w, m_XformColorSpeedSpin);
w = SetTabOrder(this, w, m_XformOpacitySpin); w = SetTabOrder(this, w, m_XformOpacitySpin);
w = SetTabOrder(this, w, m_XformDirectColorSpin); w = SetTabOrder(this, w, m_XformDirectColorSpin);
w = SetTabOrder(this, w, ui.SoloXformCheckBox); w = SetTabOrder(this, w, ui.SoloXformCheckBox);
w = SetTabOrder(this, ui.PreAffineGroupBox, m_PreX1Spin);//Xforms affine. w = SetTabOrder(this, ui.LockAffineCheckBox, ui.PreAffineGroupBox);//Xforms affine.
w = SetTabOrder(this, w, m_PreX1Spin);
w = SetTabOrder(this, w, m_PreX2Spin); w = SetTabOrder(this, w, m_PreX2Spin);
w = SetTabOrder(this, w, m_PreY1Spin); w = SetTabOrder(this, w, m_PreY1Spin);
w = SetTabOrder(this, w, m_PreY2Spin); w = SetTabOrder(this, w, m_PreY2Spin);
@ -727,6 +759,7 @@ void Fractorium::SetTabOrders()
w = SetTabOrder(this, w, ui.PreScaleDownButton); w = SetTabOrder(this, w, ui.PreScaleDownButton);
w = SetTabOrder(this, w, ui.PreScaleCombo); w = SetTabOrder(this, w, ui.PreScaleCombo);
w = SetTabOrder(this, w, ui.PreScaleUpButton); w = SetTabOrder(this, w, ui.PreScaleUpButton);
w = SetTabOrder(this, w, ui.PreRandomButton);
w = SetTabOrder(this, w, ui.ShowPreAffineCurrentRadio); w = SetTabOrder(this, w, ui.ShowPreAffineCurrentRadio);
w = SetTabOrder(this, w, ui.ShowPreAffineAllRadio); w = SetTabOrder(this, w, ui.ShowPreAffineAllRadio);
w = SetTabOrder(this, w, ui.PostAffineGroupBox); w = SetTabOrder(this, w, ui.PostAffineGroupBox);
@ -752,21 +785,33 @@ void Fractorium::SetTabOrders()
w = SetTabOrder(this, w, ui.PostScaleDownButton); w = SetTabOrder(this, w, ui.PostScaleDownButton);
w = SetTabOrder(this, w, ui.PostScaleCombo); w = SetTabOrder(this, w, ui.PostScaleCombo);
w = SetTabOrder(this, w, ui.PostScaleUpButton); w = SetTabOrder(this, w, ui.PostScaleUpButton);
w = SetTabOrder(this, w, ui.PostRandomButton);
w = SetTabOrder(this, w, ui.ShowPostAffineCurrentRadio); w = SetTabOrder(this, w, ui.ShowPostAffineCurrentRadio);
w = SetTabOrder(this, w, ui.ShowPostAffineAllRadio); w = SetTabOrder(this, w, ui.ShowPostAffineAllRadio);
w = SetTabOrder(this, w, ui.PolarAffineCheckBox);
w = SetTabOrder(this, w, ui.LocalPivotRadio); w = SetTabOrder(this, w, ui.LocalPivotRadio);
w = SetTabOrder(this, w, ui.WorldPivotRadio); w = SetTabOrder(this, w, ui.WorldPivotRadio);
w = SetTabOrder(this, ui.VariationsFilterLineEdit, ui.VariationsFilterClearButton);//Xforms variation. w = SetTabOrder(this, ui.VariationsFilterLineEdit, ui.VariationsFilterClearButton);//Xforms variation.
w = SetTabOrder(this, w, ui.VariationsTree); w = SetTabOrder(this, w, ui.VariationsTree);
//Xforms xaos is done dynamically every time. //Xforms xaos is done dynamically every time.
w = SetTabOrder(this, m_PaletteHueSpin, m_PaletteContrastSpin);//Palette. w = SetTabOrder(this, ui.PaletteFilenameCombo, m_PaletteHueSpin);//Palette.
w = SetTabOrder(this, w, m_PaletteContrastSpin);
w = SetTabOrder(this, w, m_PaletteSaturationSpin); w = SetTabOrder(this, w, m_PaletteSaturationSpin);
w = SetTabOrder(this, w, m_PaletteBlurSpin); w = SetTabOrder(this, w, m_PaletteBlurSpin);
w = SetTabOrder(this, w, m_PaletteBrightnessSpin); w = SetTabOrder(this, w, m_PaletteBrightnessSpin);
w = SetTabOrder(this, w, m_PaletteFrequencySpin); w = SetTabOrder(this, w, m_PaletteFrequencySpin);
w = SetTabOrder(this, w, ui.PaletteRandomSelect);
w = SetTabOrder(this, w, ui.PaletteRandomAdjust);
w = SetTabOrder(this, w, ui.PaletteFilterLineEdit); w = SetTabOrder(this, w, ui.PaletteFilterLineEdit);
w = SetTabOrder(this, w, ui.PaletteFilterClearButton); w = SetTabOrder(this, w, ui.PaletteFilterClearButton);
w = SetTabOrder(this, w, ui.PaletteListTable); w = SetTabOrder(this, w, ui.PaletteListTable);
w = SetTabOrder(this, w, ui.ResetCurvesButton);//Palette curves.
w = SetTabOrder(this, w, ui.CurvesView);
w = SetTabOrder(this, w, ui.CurvesGroupBox);
w = SetTabOrder(this, w, ui.CurvesAllRadio);
w = SetTabOrder(this, w, ui.CurvesRedRadio);
w = SetTabOrder(this, w, ui.CurvesGreenRadio);
w = SetTabOrder(this, w, ui.CurvesBlueRadio);
w = SetTabOrder(this, ui.SummaryTable, ui.SummaryTree);//Info summary. w = SetTabOrder(this, ui.SummaryTable, ui.SummaryTree);//Info summary.
w = SetTabOrder(this, ui.InfoBoundsGroupBox, ui.InfoBoundsFrame);//Info bounds. w = SetTabOrder(this, ui.InfoBoundsGroupBox, ui.InfoBoundsFrame);//Info bounds.
w = SetTabOrder(this, w, ui.InfoBoundsTable); w = SetTabOrder(this, w, ui.InfoBoundsTable);

View File

@ -75,17 +75,24 @@ class Fractorium : public QMainWindow
friend FractoriumEmberController<float>; friend FractoriumEmberController<float>;
friend FinalRenderEmberControllerBase; friend FinalRenderEmberControllerBase;
friend FinalRenderEmberController<float>; friend FinalRenderEmberController<float>;
friend PreviewRenderer<float>;
friend TreePreviewRenderer<float>;
#ifdef DO_DOUBLE #ifdef DO_DOUBLE
friend GLEmberController<double>; friend GLEmberController<double>;
friend FractoriumEmberController<double>; friend FractoriumEmberController<double>;
friend FinalRenderEmberController<double>; friend FinalRenderEmberController<double>;
friend PreviewRenderer<double>;
friend TreePreviewRenderer<double>;
#endif #endif
public: public:
Fractorium(QWidget* p = nullptr); Fractorium(QWidget* p = nullptr);
~Fractorium(); ~Fractorium();
//Library.
void SyncFileCountToSequenceCount();
//Geometry. //Geometry.
bool ApplyAll(); bool ApplyAll();
void SetCenter(float x, float y); void SetCenter(float x, float y);
@ -156,6 +163,25 @@ public slots:
void OnEmberTreeItemChanged(QTreeWidgetItem* item, int col); void OnEmberTreeItemChanged(QTreeWidgetItem* item, int col);
void OnEmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col); void OnEmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col);
void OnDelete(const pair<size_t, QTreeWidgetItem*>& p); void OnDelete(const pair<size_t, QTreeWidgetItem*>& p);
void OnSequenceTreeItemChanged(QTreeWidgetItem* item, int col);
void OnSequenceStartPreviewsButtonClicked(bool checked);
void OnSequenceStopPreviewsButtonClicked(bool checked);
void OnSequenceAllButtonClicked(bool checked);
void OnSequenceGenerateButtonClicked(bool checked);
void OnSequenceRenderButtonClicked(bool checked);
void OnSequenceSaveButtonClicked(bool checked);
void OnSequenceOpenButtonClicked(bool checked);
void OnSequenceRandomizeFramesPerRotCheckBoxStateChanged(int state);
void OnSequenceRandomizeRotationsCheckBoxStateChanged(int state);
void OnSequenceRandomizeBlendFramesCheckBoxStateChanged(int state);
void OnSequenceStartFlameSpinBoxChanged(int d);
void OnSequenceStopFlameSpinBoxChanged(int d);
void OnSequenceFramesPerRotSpinBoxChanged(int d);
void OnSequenceRandomFramesPerRotMaxSpinBoxChanged(int d);
void OnSequenceRotationsSpinBoxChanged(double d);
void OnSequenceRandomRotationsMaxSpinBoxChanged(double d);
void OnSequenceBlendFramesSpinBoxChanged(int d);
void OnSequenceRandomBlendMaxFramesSpinBoxChanged(int d);
//Params. //Params.
void OnBrightnessChanged(double d);//Color. void OnBrightnessChanged(double d);//Color.
@ -342,6 +368,7 @@ private:
//Library. //Library.
pair<size_t, QTreeWidgetItem*> GetCurrentEmberIndex(); pair<size_t, QTreeWidgetItem*> GetCurrentEmberIndex();
void SyncSequenceSettings();
//Params. //Params.

View File

@ -74,7 +74,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1184</width> <width>1016</width>
<height>987</height> <height>987</height>
</rect> </rect>
</property> </property>
@ -6683,7 +6683,7 @@
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>150</width> <width>150</width>
<height>200</height> <height>251</height>
</size> </size>
</property> </property>
<property name="maximumSize"> <property name="maximumSize">
@ -6711,6 +6711,12 @@
<number>1</number> <number>1</number>
</attribute> </attribute>
<widget class="QWidget" name="DockWidgetContents"> <widget class="QWidget" name="DockWidgetContents">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="autoFillBackground"> <property name="autoFillBackground">
<bool>false</bool> <bool>false</bool>
</property> </property>
@ -6731,45 +6737,16 @@
<number>4</number> <number>4</number>
</property> </property>
<item> <item>
<widget class="QScrollArea" name="LibraryTabScrollArea"> <widget class="QSplitter" name="LibrarySequenceSplitter">
<property name="frameShape"> <property name="sizePolicy">
<enum>QFrame::NoFrame</enum> <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
<property name="frameShadow"> <property name="orientation">
<enum>QFrame::Raised</enum> <enum>Qt::Vertical</enum>
</property> </property>
<property name="lineWidth">
<number>1</number>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="LibraryTabScrollWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>256</width>
<height>956</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_11">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="LibraryTreeWidget" name="LibraryTree"> <widget class="LibraryTreeWidget" name="LibraryTree">
<property name="focusPolicy"> <property name="focusPolicy">
<enum>Qt::WheelFocus</enum> <enum>Qt::WheelFocus</enum>
@ -6819,8 +6796,498 @@
</property> </property>
</column> </column>
</widget> </widget>
<widget class="QScrollArea" name="SequenceScrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="SequenceScrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>424</width>
<height>589</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_10" stretch="3,0,8">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QGridLayout" name="SequenceParamsControlsGridLayout" columnstretch="0,0,0">
<property name="spacing">
<number>4</number>
</property>
<item row="4" column="1">
<widget class="QSpinBox" name="SequenceFramesPerRotSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="prefix">
<string>Frames per rot: </string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>10000</number>
</property>
<property name="value">
<number>30</number>
</property>
</widget>
</item>
<item row="3" column="1" alignment="Qt::AlignLeft">
<widget class="QCheckBox" name="SequenceStaggerCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Stagger</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QDoubleSpinBox" name="SequenceRotationsSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="prefix">
<string>Rotations: </string>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
<property name="value">
<double>3.000000000000000</double>
</property>
</widget>
</item>
<item row="4" column="0" alignment="Qt::AlignLeft">
<widget class="QCheckBox" name="SequenceRandomizeFramesPerRotCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QSpinBox" name="SequenceRandomFramesPerRotMaxSpinBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="prefix">
<string>Frames per rot max: </string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>10000</number>
</property>
<property name="value">
<number>30</number>
</property>
</widget>
</item>
<item row="3" column="0" alignment="Qt::AlignLeft">
<widget class="QCheckBox" name="SequenceRandomizeStaggerCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>Random</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QSpinBox" name="SequenceBlendFramesSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="suffix">
<string/>
</property>
<property name="prefix">
<string>Blend frames: </string>
</property>
<property name="maximum">
<number>10000</number>
</property>
<property name="value">
<number>120</number>
</property>
</widget>
</item>
<item row="6" column="2">
<widget class="QSpinBox" name="SequenceRandomBlendMaxFramesSpinBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="prefix">
<string>Blend max frames: </string>
</property>
<property name="maximum">
<number>10000</number>
</property>
<property name="value">
<number>120</number>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QDoubleSpinBox" name="SequenceRandomRotationsMaxSpinBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="prefix">
<string>Rotations max: </string>
</property>
<property name="maximum">
<double>10000.000000000000000</double>
</property>
<property name="value">
<double>3.000000000000000</double>
</property>
</widget>
</item>
<item row="5" column="0" alignment="Qt::AlignLeft">
<widget class="QCheckBox" name="SequenceRandomizeRotationsCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="6" column="0" alignment="Qt::AlignLeft">
<widget class="QCheckBox" name="SequenceRandomizeBlendFramesCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QSpinBox" name="SequenceStartFlameSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>125</width>
<height>16777215</height>
</size>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="prefix">
<string>Start flame: </string>
</property>
<property name="maximum">
<number>999999999</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="SequenceStopFlameSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="prefix">
<string>Stop flame: </string>
</property>
<property name="maximum">
<number>999999999</number>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QPushButton" name="SequenceAllButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>33</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string>All</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QSpinBox" name="SequenceStartCountSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="frame">
<bool>true</bool>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="prefix">
<string>Start count: </string>
</property>
<property name="maximum">
<number>999999999</number>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="SequenceStartPreviewsButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Start Previews</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="SequenceStopPreviewsButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Stop Previews</string>
</property>
</widget>
</item> </item>
</layout> </layout>
</item>
<item>
<layout class="QHBoxLayout" name="SequenceSaveButtonsHLayout" stretch="0,0,0,0">
<property name="spacing">
<number>2</number>
</property>
<item>
<widget class="QPushButton" name="SequenceGenerateButton">
<property name="text">
<string>Generate</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="SequenceRenderButton">
<property name="text">
<string>Render</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="SequenceSaveButton">
<property name="text">
<string>Save</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="SequenceOpenButton">
<property name="text">
<string>Open</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="LibraryTreeWidget" name="SequenceTree">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::WheelFocus</enum>
</property>
<property name="contextMenuPolicy">
<enum>Qt::DefaultContextMenu</enum>
</property>
<property name="acceptDrops">
<bool>false</bool>
</property>
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::SelectedClicked</set>
</property>
<property name="showDropIndicator" stdset="0">
<bool>true</bool>
</property>
<property name="dragEnabled">
<bool>false</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::InternalMove</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::MoveAction</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="indentation">
<number>10</number>
</property>
<property name="sortingEnabled">
<bool>false</bool>
</property>
<attribute name="headerMinimumSectionSize">
<number>27</number>
</attribute>
<column>
<property name="text">
<string>Sequence</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</widget> </widget>
</widget> </widget>
</item> </item>

View File

@ -406,3 +406,47 @@ static QList<T> GetAllParents(QWidget* widget)
return parents; return parents;
} }
/// <summary>
/// Constrain the value in low to be less than or equal to the value in high.
/// Template expected to be any control which has member functions value() and setValue().
/// Most likely QSpinBox or QDoubleSpinbox.
/// </summary>
/// <param name="low">The control which must contain the lower value</param>
/// <param name="high">The control which must contain the higher value</param>
/// <returns>True if the value of low had to be changed, else false.</returns>
template <typename T>
bool ConstrainLow(T* low, T* high)
{
if (low->value() > high->value())
{
low->blockSignals(true);
low->setValue(high->value());
low->blockSignals(false);
return true;
}
return false;
}
/// <summary>
/// Constrain the value in high to be greater than or equal to the value in low.
/// Template expected to be any control which has member functions value() and setValue().
/// Most likely QSpinBox or QDoubleSpinbox.
/// </summary>
/// <param name="low">The control which must contain the lower value</param>
/// <param name="high">The control which must contain the higher value</param>
/// <returns>True if the value of high had to be changed, else false.</returns>
template <typename T>
bool ConstrainHigh(T* low, T* high)
{
if (high->value() < low->value())
{
high->blockSignals(true);
high->setValue(low->value());
high->blockSignals(false);
return true;
}
return false;
}

View File

@ -44,6 +44,8 @@ FractoriumEmberController<T>::FractoriumEmberController(Fractorium* fractorium)
{ {
bool b = false; bool b = false;
m_GLController = make_unique<GLEmberController<T>>(fractorium, fractorium->ui.GLDisplay, this); m_GLController = make_unique<GLEmberController<T>>(fractorium, fractorium->ui.GLDisplay, this);
m_LibraryPreviewRenderer = make_unique<TreePreviewRenderer<T>>(this, m_Fractorium->ui.LibraryTree, m_EmberFile);
m_SequencePreviewRenderer = make_unique<TreePreviewRenderer<T>>(this, m_Fractorium->ui.SequenceTree, m_SequenceFile);
//Initial combo change event to fill the palette table will be called automatically later. //Initial combo change event to fill the palette table will be called automatically later.
//Look hard for a palette. //Look hard for a palette.
static vector<string> paths = static vector<string> paths =
@ -70,57 +72,6 @@ FractoriumEmberController<T>::FractoriumEmberController(Fractorium* fractorium)
BackgroundChanged(QColor(0, 0, 0));//Default to black. BackgroundChanged(QColor(0, 0, 0));//Default to black.
ClearUndo(); ClearUndo();
m_PreviewRenderer->Callback(nullptr);
m_PreviewRenderer->NumChannels(4);
m_PreviewRenderer->EarlyClip(m_Fractorium->m_Settings->EarlyClip());
m_PreviewRenderer->YAxisUp(m_Fractorium->m_Settings->YAxisUp());
m_PreviewRenderer->SetEmber(m_Ember);//Give it an initial ember, will be updated many times later.
//m_PreviewRenderer->ThreadCount(1);//For debugging.
m_PreviewRenderFunc = [&](uint start, uint end)
{
while (m_PreviewRun || m_PreviewRunning)
{
}
m_PreviewRun = true;
m_PreviewRunning = true;
m_PreviewRenderer->ThreadCount(std::max(1u, Timing::ProcessorCount() - 1));//Leave one processor free so the GUI can breathe.
auto tree = m_Fractorium->ui.LibraryTree;
if (auto top = tree->topLevelItem(0))
{
size_t i = start;
for (auto b = Advance(m_EmberFile.m_Embers.begin(), start); m_PreviewRun && i < end && b != m_EmberFile.m_Embers.end(); ++b, ++i)
{
Ember<T> ember = *b;
ember.SyncSize();
ember.SetSizeAndAdjustScale(PREVIEW_SIZE, PREVIEW_SIZE, false, eScaleType::SCALE_WIDTH);
ember.m_TemporalSamples = 1;
ember.m_Quality = 25;
ember.m_Supersample = 1;
m_PreviewRenderer->SetEmber(ember);
if (m_PreviewRenderer->Run(m_PreviewFinalImage) == eRenderStatus::RENDER_OK)
{
if (auto treeItem = dynamic_cast<EmberTreeWidgetItem<T>*>(top->child(int(i))))
{
//It is critical that Qt::BlockingQueuedConnection is passed because this is running on a different thread than the UI.
//This ensures the events are processed in order as each preview is updated, and that control does not return here
//until the update is complete.
QMetaObject::invokeMethod(m_Fractorium, "SetLibraryTreeItemData", Qt::BlockingQueuedConnection,
Q_ARG(EmberTreeWidgetItemBase*, dynamic_cast<EmberTreeWidgetItemBase*>(treeItem)),
Q_ARG(vector<byte>&, m_PreviewFinalImage),
Q_ARG(uint, PREVIEW_SIZE),
Q_ARG(uint, PREVIEW_SIZE));
}
}
}
}
m_PreviewRun = false;
m_PreviewRunning = false;
};
} }
/// <summary> /// <summary>
@ -136,11 +87,19 @@ FractoriumEmberController<T>::~FractoriumEmberController() { }
/// </summary> /// </summary>
template <typename T> void FractoriumEmberController<T>::SetEmber(const Ember<float>& ember, bool verbatim, bool updatePointer) { SetEmberPrivate<float>(ember, verbatim, updatePointer); } template <typename T> void FractoriumEmberController<T>::SetEmber(const Ember<float>& ember, bool verbatim, bool updatePointer) { SetEmberPrivate<float>(ember, verbatim, updatePointer); }
template <typename T> void FractoriumEmberController<T>::CopyEmber(Ember<float>& ember, std::function<void(Ember<float>& ember)> perEmberOperation) { ember = m_Ember; perEmberOperation(ember); } template <typename T> void FractoriumEmberController<T>::CopyEmber(Ember<float>& ember, std::function<void(Ember<float>& ember)> perEmberOperation) { ember = m_Ember; perEmberOperation(ember); }
template <typename T> void FractoriumEmberController<T>::SetEmberFile(const EmberFile<float>& emberFile) { m_EmberFile = emberFile; } template <typename T> void FractoriumEmberController<T>::SetEmberFile(const EmberFile<float>& emberFile, bool move) { move ? m_EmberFile = std::move(emberFile) : m_EmberFile = emberFile; }
template <typename T> void FractoriumEmberController<T>::CopyEmberFile(EmberFile<float>& emberFile, std::function<void(Ember<float>& ember)> perEmberOperation) template <typename T> void FractoriumEmberController<T>::CopyEmberFile(EmberFile<float>& emberFile, bool sequence, std::function<void(Ember<float>& ember)> perEmberOperation)
{ {
if (sequence)
{
emberFile.m_Filename = m_SequenceFile.m_Filename;
CopyCont(emberFile.m_Embers, m_SequenceFile.m_Embers, perEmberOperation);
}
else
{
emberFile.m_Filename = m_EmberFile.m_Filename; emberFile.m_Filename = m_EmberFile.m_Filename;
CopyCont(emberFile.m_Embers, m_EmberFile.m_Embers, perEmberOperation); CopyCont(emberFile.m_Embers, m_EmberFile.m_Embers, perEmberOperation);
}
} }
template <typename T> void FractoriumEmberController<T>::SetTempPalette(const Palette<float>& palette) { m_TempPalette = palette; } template <typename T> void FractoriumEmberController<T>::SetTempPalette(const Palette<float>& palette) { m_TempPalette = palette; }
@ -148,11 +107,19 @@ template <typename T> void FractoriumEmberController<T>::CopyTempPalette(Palette
#ifdef DO_DOUBLE #ifdef DO_DOUBLE
template <typename T> void FractoriumEmberController<T>::SetEmber(const Ember<double>& ember, bool verbatim, bool updatePointer) { SetEmberPrivate<double>(ember, verbatim, updatePointer); } template <typename T> void FractoriumEmberController<T>::SetEmber(const Ember<double>& ember, bool verbatim, bool updatePointer) { SetEmberPrivate<double>(ember, verbatim, updatePointer); }
template <typename T> void FractoriumEmberController<T>::CopyEmber(Ember<double>& ember, std::function<void(Ember<double>& ember)> perEmberOperation) { ember = m_Ember; perEmberOperation(ember); } template <typename T> void FractoriumEmberController<T>::CopyEmber(Ember<double>& ember, std::function<void(Ember<double>& ember)> perEmberOperation) { ember = m_Ember; perEmberOperation(ember); }
template <typename T> void FractoriumEmberController<T>::SetEmberFile(const EmberFile<double>& emberFile) { m_EmberFile = emberFile; } template <typename T> void FractoriumEmberController<T>::SetEmberFile(const EmberFile<double>& emberFile, bool move) { move ? m_EmberFile = std::move(emberFile) : m_EmberFile = emberFile; }
template <typename T> void FractoriumEmberController<T>::CopyEmberFile(EmberFile<double>& emberFile, std::function<void(Ember<double>& ember)> perEmberOperation) template <typename T> void FractoriumEmberController<T>::CopyEmberFile(EmberFile<double>& emberFile, bool sequence, std::function<void(Ember<double>& ember)> perEmberOperation)
{ {
if (sequence)
{
emberFile.m_Filename = m_SequenceFile.m_Filename;
CopyCont(emberFile.m_Embers, m_SequenceFile.m_Embers, perEmberOperation);
}
else
{
emberFile.m_Filename = m_EmberFile.m_Filename; emberFile.m_Filename = m_EmberFile.m_Filename;
CopyCont(emberFile.m_Embers, m_EmberFile.m_Embers, perEmberOperation); CopyCont(emberFile.m_Embers, m_EmberFile.m_Embers, perEmberOperation);
}
} }
template <typename T> void FractoriumEmberController<T>::SetTempPalette(const Palette<double>& palette) { m_TempPalette = palette; } template <typename T> void FractoriumEmberController<T>::SetTempPalette(const Palette<double>& palette) { m_TempPalette = palette; }
@ -387,8 +354,58 @@ void FractoriumEmberController<T>::SetEmberPrivate(const Ember<U>& ember, bool v
m_Fractorium->CenterScrollbars(); m_Fractorium->CenterScrollbars();
} }
/// <summary>
/// Thin derivation to handle preview rendering multiple embers previews to a tree.
/// </summary>
/// <param name="start">The 0-based index to start rendering previews for</param>
/// <param name="end">The 0-based index which is one beyond the last ember to render a preview for</param>
template <typename T>
void TreePreviewRenderer<T>::PreviewRenderFunc(uint start, uint end)
{
auto f = m_Controller->m_Fractorium;
m_PreviewRenderer.EarlyClip(f->m_Settings->EarlyClip());
m_PreviewRenderer.YAxisUp(f->m_Settings->YAxisUp());
m_PreviewRenderer.Transparency(f->m_Settings->Transparency());
m_PreviewRenderer.ThreadCount(std::max(1u, Timing::ProcessorCount() - 1));//Leave one processor free so the GUI can breathe.
if (auto top = m_Tree->topLevelItem(0))
{
size_t i = start;
for (auto b = Advance(m_EmberFile.m_Embers.begin(), start); m_PreviewRun && i < end && b != m_EmberFile.m_Embers.end(); ++b, ++i)
{
m_PreviewEmber = *b;
m_PreviewEmber.SyncSize();
m_PreviewEmber.SetSizeAndAdjustScale(PREVIEW_SIZE, PREVIEW_SIZE, false, eScaleType::SCALE_WIDTH);
m_PreviewEmber.m_TemporalSamples = 1;
m_PreviewEmber.m_Quality = 25;
m_PreviewEmber.m_Supersample = 1;
m_PreviewRenderer.SetEmber(m_PreviewEmber);
if (m_PreviewRenderer.Run(m_PreviewFinalImage) == eRenderStatus::RENDER_OK)
{
if (auto treeItem = dynamic_cast<EmberTreeWidgetItemBase*>(top->child(int(i))))
{
//It is critical that Qt::BlockingQueuedConnection is passed because this is running on a different thread than the UI.
//This ensures the events are processed in order as each preview is updated, and that control does not return here
//until the update is complete.
QMetaObject::invokeMethod(f, "SetLibraryTreeItemData", Qt::BlockingQueuedConnection,
Q_ARG(EmberTreeWidgetItemBase*, treeItem),
Q_ARG(vector<byte>&, m_PreviewFinalImage),
Q_ARG(uint, PREVIEW_SIZE),
Q_ARG(uint, PREVIEW_SIZE));
}
}
}
}
}
template class FractoriumEmberController<float>; template class FractoriumEmberController<float>;
template class PreviewRenderer<float>;
template class TreePreviewRenderer<float>;
#ifdef DO_DOUBLE #ifdef DO_DOUBLE
template class FractoriumEmberController<double>; template class FractoriumEmberController<double>;
template class PreviewRenderer<double>;
template class TreePreviewRenderer<double>;
#endif #endif

View File

@ -29,6 +29,9 @@ enum eLibraryUpdate { INDEX = 1, NAME = 2, POINTER = 4 };
/// So Fractorium includes this file, and Fractorium is declared as a forward declaration here. /// So Fractorium includes this file, and Fractorium is declared as a forward declaration here.
/// </summary> /// </summary>
class Fractorium; class Fractorium;
template <typename T> class PreviewRenderer;
template <typename T> class TreePreviewRenderer;
#define PREVIEW_SIZE 256 #define PREVIEW_SIZE 256
#define UNDO_SIZE 128 #define UNDO_SIZE 128
@ -54,20 +57,19 @@ public:
//Embers. //Embers.
virtual void SetEmber(const Ember<float>& ember, bool verbatim, bool updatePointer) { } virtual void SetEmber(const Ember<float>& ember, bool verbatim, bool updatePointer) { }
virtual void CopyEmber(Ember<float>& ember, std::function<void(Ember<float>& ember)> perEmberOperation/* = [&](Ember<float>& ember) { }*/) { }//Uncomment default lambdas once LLVM fixes a crash in their compiler with default lambda parameters.//TODO virtual void CopyEmber(Ember<float>& ember, std::function<void(Ember<float>& ember)> perEmberOperation/* = [&](Ember<float>& ember) { }*/) { }//Uncomment default lambdas once LLVM fixes a crash in their compiler with default lambda parameters.//TODO
virtual void SetEmberFile(const EmberFile<float>& emberFile) { } virtual void SetEmberFile(const EmberFile<float>& emberFile, bool move) { }
virtual void CopyEmberFile(EmberFile<float>& emberFile, std::function<void(Ember<float>& ember)> perEmberOperation/* = [&](Ember<float>& ember) { }*/) { } virtual void CopyEmberFile(EmberFile<float>& emberFile, bool sequence, std::function<void(Ember<float>& ember)> perEmberOperation/* = [&](Ember<float>& ember) { }*/) { }
virtual void SetTempPalette(const Palette<float>& palette) { } virtual void SetTempPalette(const Palette<float>& palette) { }
virtual void CopyTempPalette(Palette<float>& palette) { } virtual void CopyTempPalette(Palette<float>& palette) { }
#ifdef DO_DOUBLE #ifdef DO_DOUBLE
virtual void SetEmber(const Ember<double>& ember, bool verbatim, bool updatePointer) { } virtual void SetEmber(const Ember<double>& ember, bool verbatim, bool updatePointer) { }
virtual void CopyEmber(Ember<double>& ember, std::function<void(Ember<double>& ember)> perEmberOperation/* = [&](Ember<double>& ember) { }*/) { } virtual void CopyEmber(Ember<double>& ember, std::function<void(Ember<double>& ember)> perEmberOperation/* = [&](Ember<double>& ember) { }*/) { }
virtual void SetEmberFile(const EmberFile<double>& emberFile) { } virtual void SetEmberFile(const EmberFile<double>& emberFile, bool move) { }
virtual void CopyEmberFile(EmberFile<double>& emberFile, std::function<void(Ember<double>& ember)> perEmberOperation/* = [&](Ember<double>& ember) { }*/) { } virtual void CopyEmberFile(EmberFile<double>& emberFile, bool sequence, std::function<void(Ember<double>& ember)> perEmberOperation/* = [&](Ember<double>& ember) { }*/) { }
virtual void SetTempPalette(const Palette<double>& palette) { } virtual void SetTempPalette(const Palette<double>& palette) { }
virtual void CopyTempPalette(Palette<double>& palette) { } virtual void CopyTempPalette(Palette<double>& palette) { }
#endif #endif
virtual void SetEmber(size_t index, bool verbatim) { } virtual void SetEmber(size_t index, bool verbatim) { }
//virtual void Clear() { }
virtual void AddXform() { } virtual void AddXform() { }
virtual void AddLinkedXform() { } virtual void AddLinkedXform() { }
virtual void DuplicateXform() { } virtual void DuplicateXform() { }
@ -119,10 +121,18 @@ public:
virtual void UpdateLibraryTree() { } virtual void UpdateLibraryTree() { }
virtual void EmberTreeItemChanged(QTreeWidgetItem* item, int col) { } virtual void EmberTreeItemChanged(QTreeWidgetItem* item, int col) { }
virtual void EmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col) { } virtual void EmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col) { }
virtual void RenderPreviews(uint start = UINT_MAX, uint end = UINT_MAX) { } virtual void RenderLibraryPreviews(uint start = UINT_MAX, uint end = UINT_MAX) { }
virtual void StopPreviewRender() { } virtual void RenderSequencePreviews(uint start = UINT_MAX, uint end = UINT_MAX) { }
virtual void SequenceTreeItemChanged(QTreeWidgetItem* item, int col) { }
virtual void StopLibraryPreviewRender() { }
virtual void StopSequencePreviewRender() { }
virtual void StopAllPreviewRenderers() { }
virtual void MoveLibraryItems(int startRow, int destRow) { } virtual void MoveLibraryItems(int startRow, int destRow) { }
virtual void Delete(const pair<size_t, QTreeWidgetItem*>& p) { } virtual void Delete(const pair<size_t, QTreeWidgetItem*>& p) { }
virtual void FillSequenceTree() { }
virtual void SequenceGenerateButtonClicked() { }
virtual void SequenceSaveButtonClicked() { }
virtual void SequenceOpenButtonClicked() { }
//Params. //Params.
virtual void SetCenter(double x, double y) { } virtual void SetCenter(double x, double y) { }
@ -287,6 +297,9 @@ protected:
template<typename T> template<typename T>
class FractoriumEmberController : public FractoriumEmberControllerBase class FractoriumEmberController : public FractoriumEmberControllerBase
{ {
friend PreviewRenderer<T>;
friend TreePreviewRenderer<T>;
public: public:
FractoriumEmberController(Fractorium* fractorium); FractoriumEmberController(Fractorium* fractorium);
FractoriumEmberController(const FractoriumEmberController<T>& controller) = delete; FractoriumEmberController(const FractoriumEmberController<T>& controller) = delete;
@ -295,20 +308,19 @@ public:
//Embers. //Embers.
virtual void SetEmber(const Ember<float>& ember, bool verbatim, bool updatePointer) override; virtual void SetEmber(const Ember<float>& ember, bool verbatim, bool updatePointer) override;
virtual void CopyEmber(Ember<float>& ember, std::function<void(Ember<float>& ember)> perEmberOperation/* = [&](Ember<float>& ember) { }*/) override; virtual void CopyEmber(Ember<float>& ember, std::function<void(Ember<float>& ember)> perEmberOperation/* = [&](Ember<float>& ember) { }*/) override;
virtual void SetEmberFile(const EmberFile<float>& emberFile) override; virtual void SetEmberFile(const EmberFile<float>& emberFile, bool move) override;
virtual void CopyEmberFile(EmberFile<float>& emberFile, std::function<void(Ember<float>& ember)> perEmberOperation/* = [&](Ember<float>& ember) { }*/) override; virtual void CopyEmberFile(EmberFile<float>& emberFile, bool sequence, std::function<void(Ember<float>& ember)> perEmberOperation/* = [&](Ember<float>& ember) { }*/) override;
virtual void SetTempPalette(const Palette<float>& palette) override; virtual void SetTempPalette(const Palette<float>& palette) override;
virtual void CopyTempPalette(Palette<float>& palette) override; virtual void CopyTempPalette(Palette<float>& palette) override;
#ifdef DO_DOUBLE #ifdef DO_DOUBLE
virtual void SetEmber(const Ember<double>& ember, bool verbatim, bool updatePointer) override; virtual void SetEmber(const Ember<double>& ember, bool verbatim, bool updatePointer) override;
virtual void CopyEmber(Ember<double>& ember, std::function<void(Ember<double>& ember)> perEmberOperation/* = [&](Ember<double>& ember) { }*/) override; virtual void CopyEmber(Ember<double>& ember, std::function<void(Ember<double>& ember)> perEmberOperation/* = [&](Ember<double>& ember) { }*/) override;
virtual void SetEmberFile(const EmberFile<double>& emberFile) override; virtual void SetEmberFile(const EmberFile<double>& emberFile, bool move) override;
virtual void CopyEmberFile(EmberFile<double>& emberFile, std::function<void(Ember<double>& ember)> perEmberOperation/* = [&](Ember<double>& ember) { }*/) override; virtual void CopyEmberFile(EmberFile<double>& emberFile, bool sequence, std::function<void(Ember<double>& ember)> perEmberOperation/* = [&](Ember<double>& ember) { }*/) override;
virtual void SetTempPalette(const Palette<double>& palette) override; virtual void SetTempPalette(const Palette<double>& palette) override;
virtual void CopyTempPalette(Palette<double>& palette) override; virtual void CopyTempPalette(Palette<double>& palette) override;
#endif #endif
virtual void SetEmber(size_t index, bool verbatim) override; virtual void SetEmber(size_t index, bool verbatim) override;
//virtual void Clear() override { }
virtual void AddXform() override; virtual void AddXform() override;
virtual void AddLinkedXform() override; virtual void AddLinkedXform() override;
virtual void DuplicateXform() override; virtual void DuplicateXform() override;
@ -316,7 +328,6 @@ public:
virtual void DeleteXforms() override; virtual void DeleteXforms() override;
virtual void AddFinalXform() override; virtual void AddFinalXform() override;
virtual bool UseFinalXform() override { return m_Ember.UseFinalXform(); } virtual bool UseFinalXform() override { return m_Ember.UseFinalXform(); }
//virtual bool IsFinal(uint i) { return false; }
virtual size_t XformCount() const override { return m_Ember.XformCount(); } virtual size_t XformCount() const override { return m_Ember.XformCount(); }
virtual size_t TotalXformCount() const override { return m_Ember.TotalXformCount(); } virtual size_t TotalXformCount() const override { return m_Ember.TotalXformCount(); }
virtual QString Name() const override { return QString::fromStdString(m_Ember.m_Name); } virtual QString Name() const override { return QString::fromStdString(m_Ember.m_Name); }
@ -365,8 +376,17 @@ public:
virtual void Delete(const pair<size_t, QTreeWidgetItem*>& p) override; virtual void Delete(const pair<size_t, QTreeWidgetItem*>& p) override;
virtual void EmberTreeItemChanged(QTreeWidgetItem* item, int col) override; virtual void EmberTreeItemChanged(QTreeWidgetItem* item, int col) override;
virtual void EmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col) override; virtual void EmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col) override;
virtual void RenderPreviews(uint start = UINT_MAX, uint end = UINT_MAX) override; void RenderPreviews(QTreeWidget* tree, TreePreviewRenderer<T>* renderer, EmberFile<T>& file, uint start = UINT_MAX, uint end = UINT_MAX);
virtual void StopPreviewRender() override; virtual void RenderLibraryPreviews(uint start = UINT_MAX, uint end = UINT_MAX) override;
virtual void RenderSequencePreviews(uint start = UINT_MAX, uint end = UINT_MAX) override;
virtual void SequenceTreeItemChanged(QTreeWidgetItem* item, int col) override;
virtual void StopLibraryPreviewRender() override;
virtual void StopSequencePreviewRender() override;
virtual void StopAllPreviewRenderers() override;
virtual void FillSequenceTree() override;
virtual void SequenceGenerateButtonClicked() override;
virtual void SequenceSaveButtonClicked() override;
virtual void SequenceOpenButtonClicked() override;
//Params. //Params.
virtual void SetCenter(double x, double y) override; virtual void SetCenter(double x, double y) override;
@ -504,13 +524,13 @@ private:
bool SyncSizes(); bool SyncSizes();
//Templated members. //Templated members.
bool m_PreviewRun = false;
bool m_PreviewRunning = false; bool m_PreviewRunning = false;
vector<T> m_TempOpacities; vector<T> m_TempOpacities;
vector<T> m_NormalizedWeights; vector<T> m_NormalizedWeights;
Ember<T> m_Ember; Ember<T> m_Ember;
const void* m_EmberFilePointer = nullptr; const void* m_EmberFilePointer = nullptr;
EmberFile<T> m_EmberFile; EmberFile<T> m_EmberFile;
EmberFile<T> m_SequenceFile;
deque<Ember<T>> m_UndoList; deque<Ember<T>> m_UndoList;
vector<Xform<T>> m_CopiedXforms; vector<Xform<T>> m_CopiedXforms;
Xform<T> m_CopiedFinalXform; Xform<T> m_CopiedFinalXform;
@ -519,8 +539,115 @@ private:
shared_ptr<VariationList<T>> m_VariationList; shared_ptr<VariationList<T>> m_VariationList;
unique_ptr<SheepTools<T, float>> m_SheepTools; unique_ptr<SheepTools<T, float>> m_SheepTools;
unique_ptr<GLEmberController<T>> m_GLController; unique_ptr<GLEmberController<T>> m_GLController;
unique_ptr<EmberNs::Renderer<T, float>> m_PreviewRenderer = make_unique<EmberNs::Renderer<T, float>>(); unique_ptr<TreePreviewRenderer<T>> m_LibraryPreviewRenderer;
QFuture<void> m_PreviewResult; unique_ptr<TreePreviewRenderer<T>> m_SequencePreviewRenderer;
std::function<void (uint, uint)> m_PreviewRenderFunc;
}; };
/// <summary>
/// Base class for encapsulating a preview renderer which will be used
/// in such places as the main library tree, the sequence tree and the
/// single preview thumbnail shown in the final render dialog.
/// Derived classes will implement PreviewRenderFunc() to handle the rendering
/// functionality specific to their previews.
/// </summary>
template <typename T>
class PreviewRenderer
{
public:
PreviewRenderer()
{
}
void Render(uint start, uint end)
{
Stop();
m_PreviewResult = QtConcurrent::run([&](uint s, uint e)
{
rlg l(m_PreviewCs);
m_PreviewRun = true;
PreviewRenderFunc(s, e);
m_PreviewRun = false;
}, start, end);
}
void Stop()
{
m_PreviewRun = false;
m_PreviewRenderer.Abort();
m_PreviewResult.cancel();
while (m_PreviewResult.isRunning())
QApplication::processEvents();
}
bool EarlyClip()
{
return m_PreviewRenderer.EarlyClip();
}
bool YAxisUp()
{
return m_PreviewRenderer.YAxisUp();
}
bool Transparency()
{
return m_PreviewRenderer.Transparency();
}
bool Running()
{
return m_PreviewRun || m_PreviewResult.isRunning();
}
virtual void PreviewRenderFunc(uint start, uint end) {}
protected:
volatile bool m_PreviewRun = false;
Ember<T> m_PreviewEmber;
vector<byte> m_PreviewFinalImage;
EmberNs::Renderer<T, float> m_PreviewRenderer;
private:
QFuture<void> m_PreviewResult;
std::recursive_mutex m_PreviewCs;
};
/// <summary>
/// Thin derivation to handle preview rendering multiple embers previews to a tree.
/// </summary>
template <typename T>
class TreePreviewRenderer : public PreviewRenderer<T>
{
public:
using PreviewRenderer<T>::m_PreviewRun;
using PreviewRenderer<T>::m_PreviewEmber;
using PreviewRenderer<T>::m_PreviewRenderer;
using PreviewRenderer<T>::m_PreviewFinalImage;
/// <summary>
/// Initializes a new instance of the <see cref="TreePreviewRenderer{T}"/> class.
/// </summary>
/// <param name="controller">A pointer to the controller this instance is a member of</param>
/// <param name="tree">A pointer to the tree to render to</param>
/// <param name="emberFile">A reference to the ember file to render</param>
TreePreviewRenderer(FractoriumEmberController<T>* controller, QTreeWidget* tree, EmberFile<T>& emberFile) :
m_Controller(controller),
m_Tree(tree),
m_EmberFile(emberFile)
{
auto f = m_Controller->m_Fractorium;
m_PreviewRenderer.Callback(nullptr);
m_PreviewRenderer.NumChannels(4);
m_PreviewRenderer.EarlyClip(f->m_Settings->EarlyClip());
m_PreviewRenderer.YAxisUp(f->m_Settings->YAxisUp());
m_PreviewRenderer.Transparency(f->m_Settings->Transparency());
}
virtual void PreviewRenderFunc(uint start, uint end) override;
protected:
FractoriumEmberController<T>* m_Controller;
QTreeWidget* m_Tree;
EmberFile<T>& m_EmberFile;
};

View File

@ -7,9 +7,34 @@
void Fractorium::InitLibraryUI() void Fractorium::InitLibraryUI()
{ {
ui.LibraryTree->SetMainWindow(this); ui.LibraryTree->SetMainWindow(this);
ui.SequenceFramesPerRotSpinBox->setValue(m_Settings->FramesPerRot());
ui.SequenceRandomFramesPerRotMaxSpinBox->setValue(m_Settings->FramesPerRotMax());
ui.SequenceRotationsSpinBox->setValue(m_Settings->Rotations());
ui.SequenceRandomRotationsMaxSpinBox->setValue(m_Settings->RotationsMax());
ui.SequenceBlendFramesSpinBox->setValue(m_Settings->BlendFrames());
ui.SequenceRandomBlendMaxFramesSpinBox->setValue(m_Settings->BlendFramesMax());
connect(ui.LibraryTree, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(OnEmberTreeItemChanged(QTreeWidgetItem*, int)), Qt::QueuedConnection); connect(ui.LibraryTree, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(OnEmberTreeItemChanged(QTreeWidgetItem*, int)), Qt::QueuedConnection);
connect(ui.LibraryTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(OnEmberTreeItemDoubleClicked(QTreeWidgetItem*, int)), Qt::QueuedConnection); connect(ui.LibraryTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(OnEmberTreeItemDoubleClicked(QTreeWidgetItem*, int)), Qt::QueuedConnection);
connect(ui.LibraryTree, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(OnEmberTreeItemDoubleClicked(QTreeWidgetItem*, int)), Qt::QueuedConnection); connect(ui.LibraryTree, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(OnEmberTreeItemDoubleClicked(QTreeWidgetItem*, int)), Qt::QueuedConnection);
connect(ui.SequenceTree, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(OnSequenceTreeItemChanged(QTreeWidgetItem*, int)), Qt::QueuedConnection);
connect(ui.SequenceStartPreviewsButton, SIGNAL(clicked(bool)), this, SLOT(OnSequenceStartPreviewsButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.SequenceStopPreviewsButton, SIGNAL(clicked(bool)), this, SLOT(OnSequenceStopPreviewsButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.SequenceAllButton, SIGNAL(clicked(bool)), this, SLOT(OnSequenceAllButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.SequenceGenerateButton, SIGNAL(clicked(bool)), this, SLOT(OnSequenceGenerateButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.SequenceRenderButton, SIGNAL(clicked(bool)), this, SLOT(OnSequenceRenderButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.SequenceSaveButton, SIGNAL(clicked(bool)), this, SLOT(OnSequenceSaveButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.SequenceOpenButton, SIGNAL(clicked(bool)), this, SLOT(OnSequenceOpenButtonClicked(bool)), Qt::QueuedConnection);
connect(ui.SequenceRandomizeFramesPerRotCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSequenceRandomizeFramesPerRotCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.SequenceRandomizeRotationsCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSequenceRandomizeRotationsCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.SequenceRandomizeBlendFramesCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSequenceRandomizeBlendFramesCheckBoxStateChanged(int)), Qt::QueuedConnection);
connect(ui.SequenceStartFlameSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnSequenceStartFlameSpinBoxChanged(int)), Qt::QueuedConnection);
connect(ui.SequenceStopFlameSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnSequenceStopFlameSpinBoxChanged(int)), Qt::QueuedConnection);
connect(ui.SequenceFramesPerRotSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnSequenceFramesPerRotSpinBoxChanged(int)), Qt::QueuedConnection);
connect(ui.SequenceRandomFramesPerRotMaxSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnSequenceRandomFramesPerRotMaxSpinBoxChanged(int)), Qt::QueuedConnection);
connect(ui.SequenceRotationsSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnSequenceRotationsSpinBoxChanged(double)), Qt::QueuedConnection);
connect(ui.SequenceRandomRotationsMaxSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnSequenceRandomRotationsMaxSpinBoxChanged(double)), Qt::QueuedConnection);
connect(ui.SequenceBlendFramesSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnSequenceBlendFramesSpinBoxChanged(int)), Qt::QueuedConnection);
connect(ui.SequenceRandomBlendMaxFramesSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnSequenceRandomBlendMaxFramesSpinBoxChanged(int)), Qt::QueuedConnection);
} }
/// <summary> /// <summary>
@ -95,7 +120,7 @@ void FractoriumEmberController<T>::FillLibraryTree(int selectIndex)
uint i = 0; uint i = 0;
auto tree = m_Fractorium->ui.LibraryTree; auto tree = m_Fractorium->ui.LibraryTree;
vector<byte> v(size * size * 4); vector<byte> v(size * size * 4);
StopPreviewRender(); StopAllPreviewRenderers();
tree->clear(); tree->clear();
QCoreApplication::flush(); QCoreApplication::flush();
tree->blockSignals(true); tree->blockSignals(true);
@ -125,8 +150,9 @@ void FractoriumEmberController<T>::FillLibraryTree(int selectIndex)
if (auto emberItem = dynamic_cast<EmberTreeWidgetItem<T>*>(top->child(selectIndex))) if (auto emberItem = dynamic_cast<EmberTreeWidgetItem<T>*>(top->child(selectIndex)))
emberItem->setSelected(true); emberItem->setSelected(true);
m_Fractorium->SyncFileCountToSequenceCount();
QCoreApplication::flush(); QCoreApplication::flush();
RenderPreviews(0, uint(m_EmberFile.Size())); RenderLibraryPreviews(0, uint(m_EmberFile.Size()));
tree->expandAll(); tree->expandAll();
} }
@ -162,8 +188,9 @@ void FractoriumEmberController<T>::UpdateLibraryTree()
//When adding elements, ensure all indices are sequential. //When adding elements, ensure all indices are sequential.
SyncLibrary(eLibraryUpdate::INDEX); SyncLibrary(eLibraryUpdate::INDEX);
m_Fractorium->SyncFileCountToSequenceCount();
tree->blockSignals(false); tree->blockSignals(false);
RenderPreviews(origChildCount, uint(m_EmberFile.Size())); RenderLibraryPreviews(origChildCount, uint(m_EmberFile.Size()));
} }
} }
@ -280,6 +307,7 @@ void FractoriumEmberController<T>::Delete(const pair<size_t, QTreeWidgetItem*>&
{ {
delete p.second; delete p.second;
SyncLibrary(eLibraryUpdate::INDEX); SyncLibrary(eLibraryUpdate::INDEX);
m_Fractorium->SyncFileCountToSequenceCount();
} }
//If there is now only one item left and it wasn't selected, select it. //If there is now only one item left and it wasn't selected, select it.
@ -308,13 +336,12 @@ void Fractorium::OnDelete(const pair<size_t, QTreeWidgetItem*>& p)
/// <param name="start">The 0-based index to start rendering previews for</param> /// <param name="start">The 0-based index to start rendering previews for</param>
/// <param name="end">The 0-based index which is one beyond the last ember to render a preview for</param> /// <param name="end">The 0-based index which is one beyond the last ember to render a preview for</param>
template <typename T> template <typename T>
void FractoriumEmberController<T>::RenderPreviews(uint start, uint end) void FractoriumEmberController<T>::RenderPreviews(QTreeWidget* tree, TreePreviewRenderer<T>* renderer, EmberFile<T>& file, uint start, uint end)
{ {
StopPreviewRender(); renderer->Stop();
if (start == UINT_MAX && end == UINT_MAX) if (start == UINT_MAX && end == UINT_MAX)
{ {
auto tree = m_Fractorium->ui.LibraryTree;
tree->blockSignals(true); tree->blockSignals(true);
if (auto top = tree->topLevelItem(0)) if (auto top = tree->topLevelItem(0))
@ -323,36 +350,401 @@ void FractoriumEmberController<T>::RenderPreviews(uint start, uint end)
vector<byte> emptyPreview(PREVIEW_SIZE * PREVIEW_SIZE * 4); vector<byte> emptyPreview(PREVIEW_SIZE * PREVIEW_SIZE * 4);
for (int i = 0; i < childCount; i++) for (int i = 0; i < childCount; i++)
if (auto treeItem = dynamic_cast<EmberTreeWidgetItem<T>*>(top->child(i))) if (auto treeItem = dynamic_cast<EmberTreeWidgetItemBase*>(top->child(i)))
treeItem->SetImage(emptyPreview, PREVIEW_SIZE, PREVIEW_SIZE); treeItem->SetImage(emptyPreview, PREVIEW_SIZE, PREVIEW_SIZE);
} }
tree->blockSignals(false); tree->blockSignals(false);
m_PreviewResult = QtConcurrent::run(m_PreviewRenderFunc, 0, uint(m_EmberFile.Size())); renderer->Render(0, uint(file.Size()));
} }
else else
m_PreviewResult = QtConcurrent::run(m_PreviewRenderFunc, start, end); renderer->Render(start, end);
} }
/// <summary> /// <summary>
/// Stop the preview rendering thread. /// Wrapper around calling RenderPreviews with the appropriate values passed in for the previews in the main library tree.
/// </summary>
/// <param name="start">The 0-based index to start rendering previews for</param>
/// <param name="end">The 0-based index which is one beyond the last ember to render a preview for</param>
template <typename T>
void FractoriumEmberController<T>::RenderLibraryPreviews(uint start, uint end)
{
RenderPreviews(m_Fractorium->ui.LibraryTree, m_LibraryPreviewRenderer.get(), m_EmberFile, start, end);
}
template <typename T>
void FractoriumEmberController<T>::StopLibraryPreviewRender() { m_LibraryPreviewRenderer->Stop(); }
/// <summary>
/// Thing wrapper around StopLibraryPreviewRender() and StopSequencePreviewRender() to stop both preview renderers.
/// </summary> /// </summary>
template <typename T> template <typename T>
void FractoriumEmberController<T>::StopPreviewRender() void FractoriumEmberController<T>::StopAllPreviewRenderers()
{ {
m_PreviewRun = false; StopLibraryPreviewRender();
m_PreviewRenderer->Abort(); StopSequencePreviewRender();
}
while (m_PreviewRunning) /// <summary>
QApplication::processEvents(); /// Fill the sequence tree with the names of the embers in the
/// currently generated sequence.
m_PreviewResult.cancel(); /// Start the sequence preview render thread.
/// </summary>
while (m_PreviewResult.isRunning()) template <typename T>
QApplication::processEvents(); void FractoriumEmberController<T>::FillSequenceTree()
{
QCoreApplication::sendPostedEvents(m_Fractorium->ui.LibraryTree); uint size = 64;
uint i = 0;
auto tree = m_Fractorium->ui.SequenceTree;
vector<byte> v(size * size * 4);
m_SequencePreviewRenderer->Stop();
tree->clear();
QCoreApplication::flush(); QCoreApplication::flush();
tree->blockSignals(true);
auto fileItem = new QTreeWidgetItem(tree);
QFileInfo info(m_SequenceFile.m_Filename);
fileItem->setText(0, info.fileName());
fileItem->setToolTip(0, m_SequenceFile.m_Filename);
fileItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);
for (auto& it : m_SequenceFile.m_Embers)
{
auto emberItem = new EmberTreeWidgetItemBase(fileItem);
if (it.m_Name.empty())
emberItem->setText(0, ToString(i++));
else
emberItem->setText(0, it.m_Name.c_str());
emberItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
emberItem->setToolTip(0, emberItem->text(0));
emberItem->SetImage(v, size, size);
}
tree->blockSignals(false);
QCoreApplication::flush();
RenderSequencePreviews(0, uint(m_SequenceFile.Size()));
tree->expandAll();
}
/// <summary>
/// Copy the text of the root item to the name of the sequence file.
/// Called whenever the text of the root item is changed.
/// </summary>
/// <param name="item">The root sequence tree item which changed</param>
/// <param name="col">The column clicked, ignored.</param>
template <typename T>
void FractoriumEmberController<T>::SequenceTreeItemChanged(QTreeWidgetItem* item, int col)
{
if (auto parentItem = dynamic_cast<QTreeWidgetItem*>(item))
{
QString text = parentItem->text(0);
if (text != "")
m_SequenceFile.m_Filename = text;
}
}
void Fractorium::OnSequenceTreeItemChanged(QTreeWidgetItem* item, int col) { m_Controller->SequenceTreeItemChanged(item, col); }
/// <summary>
/// Wrapper around calling RenderPreviews with the appropriate values passed in for the previews in the sequence tree.
/// Called when Render Previews is clicked.
/// </summary>
/// <param name="start">Ignored, render all.</param>
/// <param name="end">Ignored, render all.</param>
template <typename T>
void FractoriumEmberController<T>::RenderSequencePreviews(uint start, uint end) { RenderPreviews(m_Fractorium->ui.SequenceTree, m_SequencePreviewRenderer.get(), m_SequenceFile, start, end); }
void Fractorium::OnSequenceStartPreviewsButtonClicked(bool checked) { m_Controller->RenderSequencePreviews(); }
/// <summary>
/// Stop rendering the sequence previews.
/// Called when Stop Previews is clicked.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::StopSequencePreviewRender() { m_SequencePreviewRenderer->Stop(); }
void Fractorium::OnSequenceStopPreviewsButtonClicked(bool checked) { m_Controller->StopSequencePreviewRender(); }
/// <summary>
/// Set the start and stop spin boxes to 0 and the length of the ember file minus 1, respectively.
/// Called whenever the count of the current file changes or when All is clicked.
/// </summary>
void Fractorium::SyncFileCountToSequenceCount()
{
if (auto top = ui.LibraryTree->topLevelItem(0))
{
int count = top->childCount() - 1;
ui.SequenceStartFlameSpinBox->setMinimum(0);
ui.SequenceStartFlameSpinBox->setMaximum(count);
ui.SequenceStartFlameSpinBox->setValue(0);
ui.SequenceStopFlameSpinBox->setMinimum(0);
ui.SequenceStopFlameSpinBox->setMaximum(count);
ui.SequenceStopFlameSpinBox->setValue(count);
}
}
void Fractorium::OnSequenceAllButtonClicked(bool checked) { SyncFileCountToSequenceCount(); }
/// <summary>
/// Generate an animation sequence and place it in the sequence tree. This code is
/// mostly similar to that of EmberGenome.
/// It differs in a few ways:
/// The number of frames used in a rotation and in blending can differ. In EmberGenome, they are the same.
/// The number of rotations, frames used in rotations and frames used in blending can all be randomized.
/// Called when the Generate button is clicked.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::SequenceGenerateButtonClicked()
{
StopAllPreviewRenderers();
SaveCurrentToOpenedFile(false);
Ember<T> result;
auto& ui = m_Fractorium->ui;
auto s = m_Fractorium->m_Settings;
bool randStagger = ui.SequenceRandomizeStaggerCheckBox->isChecked();
bool randFramesRot = ui.SequenceRandomizeFramesPerRotCheckBox->isChecked();
bool randRot = ui.SequenceRandomizeRotationsCheckBox->isChecked();
bool randBlend = ui.SequenceRandomizeBlendFramesCheckBox->isChecked();
bool stagger = ui.SequenceStaggerCheckBox->isChecked();
double rots = ui.SequenceRotationsSpinBox->value();
double rotsMax = ui.SequenceRandomRotationsMaxSpinBox->value();
int framesPerRot = ui.SequenceFramesPerRotSpinBox->value();
int framesPerRotMax = ui.SequenceRandomFramesPerRotMaxSpinBox->value();
int framesBlend = ui.SequenceBlendFramesSpinBox->value();
int framesBlendMax = ui.SequenceRandomBlendMaxFramesSpinBox->value();
size_t start = ui.SequenceStartFlameSpinBox->value();
size_t stop = ui.SequenceStopFlameSpinBox->value();
size_t startCount = ui.SequenceStartCountSpinBox->value();
size_t keyFrames = (stop - start) + 1;
size_t frameCount = 0;
double frames = 0;
vector<pair<size_t, size_t>> devices;//Dummy.
EmberReport emberReport;
ostringstream os;
string palettePath =
#ifdef _WIN32
"./flam3-palettes.xml";
#else
"~/.config/fractorium";
#endif
SheepTools<T, float> tools(palettePath, EmberCommon::CreateRenderer<T>(eRendererType::CPU_RENDERER, devices, false, 0, emberReport));
tools.SetSpinParams(true,
randStagger ? m_Rand.RandBit() : stagger,
0,
0,
s->Nick().toStdString(),
s->Url().toStdString(),
s->Id().toStdString(),
"",
0,
0);
if (randFramesRot)
frames = ui.SequenceRandomFramesPerRotMaxSpinBox->value();
else
frames = ui.SequenceFramesPerRotSpinBox->value();
if (randRot)
frames *= ui.SequenceRandomRotationsMaxSpinBox->value();
else
frames *= ui.SequenceRotationsSpinBox->value();
if (randBlend)
frames += ui.SequenceRandomBlendMaxFramesSpinBox->value();
else
frames += ui.SequenceBlendFramesSpinBox->value();
frames *= keyFrames;
frames += startCount;
os << setfill('0') << setprecision(0) << fixed;
m_SequenceFile.Clear();
m_SequenceFile.m_Filename = EmberFile<T>::DefaultFilename("Sequence_");
double blend;
size_t frame;
Ember<T> embers[2];//Spin needs contiguous array below, and this will also get modified, so a copy is needed to avoid modifying the embers in the original file.
auto padding = streamsize(std::log10(frames)) + 1;
auto it = Advance(m_EmberFile.m_Embers.begin(), start);
for (size_t i = start; i <= stop && it != m_EmberFile.m_Embers.end(); i++, ++it)
{
double rotations = randRot ? m_Rand.Frand<double>(rots, rotsMax) : rots;
embers[0] = *it;
if (rotations > 0)
{
double rotFrames = randFramesRot ? m_Rand.Frand<double>(framesPerRot, framesPerRotMax) : framesPerRot;
size_t roundFrames = size_t(std::round(rotFrames * rotations));
for (frame = 0; frame < roundFrames; frame++)
{
blend = frame / rotFrames;
tools.Spin(embers[0], nullptr, result, startCount + frameCount++, blend);//Result is cleared and reassigned each time inside of Spin().
FormatName(result, os, padding);
m_SequenceFile.m_Embers.push_back(result);
}
//The loop above will have rotated just shy of a complete rotation.
//Rotate the next step and save in result, but do not print.
//result will be the starting point for the interp phase below.
frame = roundFrames;
blend = frame / rotFrames;
tools.Spin(embers[0], nullptr, result, startCount + frameCount, blend);//Do not increment frameCount here.
FormatName(result, os, padding);
}
if (i < stop)
{
if (rotations > 0)//Store the last result as the flame to interpolate from. This applies for whole or fractional values of opt.Loops().
embers[0] = result;
auto it2 = it;//Need a quick temporary to avoid modifying it which is used in the loop.
embers[1] = *(++it2);//Get the next ember to be used with blending below.
size_t blendFrames = randBlend ? m_Rand.Frand<double>(framesBlend, framesBlendMax) : framesBlend;
for (frame = 0; frame < blendFrames; frame++)
{
bool seqFlag = frame == 0 || (frame == blendFrames - 1);
blend = frame / double(blendFrames);
result.Clear();
if (randStagger)
tools.Stagger(m_Rand.RandBit());
tools.SpinInter(&embers[0], nullptr, result, startCount + frameCount++, seqFlag, blend);
FormatName(result, os, padding);
m_SequenceFile.m_Embers.push_back(result);
}
}
}
it = Advance(m_EmberFile.m_Embers.begin(), stop);
tools.Spin(*it, nullptr, result, startCount + frameCount, 0);
FormatName(result, os, padding);
m_SequenceFile.m_Embers.push_back(result);
FillSequenceTree();//The sequence has been generated, now create preview thumbnails.
}
void Fractorium::OnSequenceGenerateButtonClicked(bool checked) { m_Controller->SequenceGenerateButtonClicked(); }
/// <summary>
/// Show the final render dialog and load the sequence into it.
/// This will automatically check the Render All and Render as Animation sequence checkboxes.
/// Called when the Render Sequence button is clicked.
/// </summary>
/// <param name="checked">Ignored.</param>
void Fractorium::OnSequenceRenderButtonClicked(bool checked)
{
//First completely stop what the current rendering process is doing.
m_Controller->DeleteRenderer();//Delete the renderer, but not the controller.
m_Controller->StopAllPreviewRenderers();
m_Controller->SaveCurrentToOpenedFile(false);//Save whatever was edited back to the current open file.
m_RenderStatusLabel->setText("Renderer stopped.");
m_FinalRenderDialog->Show(true);//Show with a bool specifying that it came from the sequence generator.
}
/// <summary>
/// Save the sequence to a file.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::SequenceSaveButtonClicked()
{
auto s = m_Fractorium->m_Settings;
QString filename = m_Fractorium->SetupSaveXmlDialog(m_SequenceFile.m_Filename);
if (filename != "")
{
EmberToXml<T> writer;
QFileInfo fileInfo(filename);
if (writer.Save(filename.toStdString().c_str(), m_SequenceFile.m_Embers, 0, true, true))
s->SaveFolder(fileInfo.canonicalPath());
else
m_Fractorium->ShowCritical("Save Failed", "Could not save sequence file, try saving to a different folder.");
}
}
void Fractorium::OnSequenceSaveButtonClicked(bool checked) { m_Controller->SequenceSaveButtonClicked(); }
/// <summary>
/// Open one or more sequence file, concatenate them all, place them in the sequence
/// tree and begin rendering the sequence previews.
/// </summary>
template <typename T>
void FractoriumEmberController<T>::SequenceOpenButtonClicked()
{
m_SequencePreviewRenderer->Stop();
auto filenames = m_Fractorium->SetupOpenXmlDialog();
if (!filenames.empty())
{
size_t i;
EmberFile<T> emberFile;
XmlToEmber<T> parser;
vector<Ember<T>> embers;
vector<string> errors;
emberFile.m_Filename = filenames[0];
for (auto& filename : filenames)
{
embers.clear();
if (parser.Parse(filename.toStdString().c_str(), embers) && !embers.empty())
{
for (i = 0; i < embers.size(); i++)
if (embers[i].m_Name == "" || embers[i].m_Name == "No name")//Ensure it has a name.
embers[i].m_Name = ToString<qulonglong>(i).toStdString();
emberFile.m_Embers.insert(emberFile.m_Embers.end(), embers.begin(), embers.end());
errors = parser.ErrorReport();
}
else
{
errors = parser.ErrorReport();
m_Fractorium->ShowCritical("Open Failed", "Could not open sequence file, see info tab for details.");
}
if (!errors.empty())
m_Fractorium->ErrorReportToQTextEdit(errors, m_Fractorium->ui.InfoFileOpeningTextEdit, false);//Concat errors from all files.
}
if (emberFile.Size() > 0)//Ensure at least something was read.
{
emberFile.MakeNamesUnique();
m_SequenceFile = std::move(emberFile);//Move the temp to avoid creating dupes because we no longer need it.
FillSequenceTree();
}
}
}
void Fractorium::OnSequenceOpenButtonClicked(bool checked) { m_Controller->SequenceOpenButtonClicked(); }
void Fractorium::OnSequenceRandomizeFramesPerRotCheckBoxStateChanged(int state) { ui.SequenceRandomFramesPerRotMaxSpinBox->setEnabled(state); }
void Fractorium::OnSequenceRandomizeRotationsCheckBoxStateChanged(int state) { ui.SequenceRandomRotationsMaxSpinBox->setEnabled(state); }
void Fractorium::OnSequenceRandomizeBlendFramesCheckBoxStateChanged(int state) { ui.SequenceRandomBlendMaxFramesSpinBox->setEnabled(state); }
/// <summary>
/// Constrain all min/max spinboxes.
/// </summary>
void Fractorium::OnSequenceStartFlameSpinBoxChanged(int d) { ConstrainLow(ui.SequenceStartFlameSpinBox, ui.SequenceStopFlameSpinBox); }
void Fractorium::OnSequenceStopFlameSpinBoxChanged(int d) { ConstrainHigh(ui.SequenceStartFlameSpinBox, ui.SequenceStopFlameSpinBox); }
void Fractorium::OnSequenceFramesPerRotSpinBoxChanged(int d) { if (ui.SequenceRandomizeFramesPerRotCheckBox->isChecked()) ConstrainLow(ui.SequenceFramesPerRotSpinBox, ui.SequenceRandomFramesPerRotMaxSpinBox); }
void Fractorium::OnSequenceRandomFramesPerRotMaxSpinBoxChanged(int d) { ConstrainHigh(ui.SequenceFramesPerRotSpinBox, ui.SequenceRandomFramesPerRotMaxSpinBox); }
void Fractorium::OnSequenceRotationsSpinBoxChanged(double d) { if (ui.SequenceRandomizeRotationsCheckBox->isChecked()) ConstrainLow(ui.SequenceRotationsSpinBox, ui.SequenceRandomRotationsMaxSpinBox); }
void Fractorium::OnSequenceRandomRotationsMaxSpinBoxChanged(double d) { ConstrainHigh(ui.SequenceRotationsSpinBox, ui.SequenceRandomRotationsMaxSpinBox); }
void Fractorium::OnSequenceBlendFramesSpinBoxChanged(int d) { if (ui.SequenceRandomizeBlendFramesCheckBox->isChecked()) ConstrainLow(ui.SequenceBlendFramesSpinBox, ui.SequenceRandomBlendMaxFramesSpinBox); }
void Fractorium::OnSequenceRandomBlendMaxFramesSpinBoxChanged(int d) { ConstrainHigh(ui.SequenceBlendFramesSpinBox, ui.SequenceRandomBlendMaxFramesSpinBox); }
/// <summary>
/// Save all sequence settings to match the values in the controls.
/// </summary>
void Fractorium::SyncSequenceSettings()
{
m_Settings->FramesPerRot(ui.SequenceFramesPerRotSpinBox->value());
m_Settings->FramesPerRotMax(ui.SequenceRandomFramesPerRotMaxSpinBox->value());
m_Settings->Rotations(ui.SequenceRotationsSpinBox->value());
m_Settings->RotationsMax(ui.SequenceRandomRotationsMaxSpinBox->value());
m_Settings->BlendFrames(ui.SequenceBlendFramesSpinBox->value());
m_Settings->BlendFramesMax(ui.SequenceRandomBlendMaxFramesSpinBox->value());
} }
template class FractoriumEmberController<float>; template class FractoriumEmberController<float>;

View File

@ -52,7 +52,7 @@ template <typename T>
void FractoriumEmberController<T>::NewFlock(size_t count) void FractoriumEmberController<T>::NewFlock(size_t count)
{ {
Ember<T> ember; Ember<T> ember;
StopPreviewRender(); StopAllPreviewRenderers();
m_EmberFile.Clear(); m_EmberFile.Clear();
m_EmberFile.m_Filename = EmberFile<T>::DefaultFilename(); m_EmberFile.m_Filename = EmberFile<T>::DefaultFilename();
@ -91,7 +91,7 @@ void FractoriumEmberController<T>::NewEmptyFlameInCurrentFile()
Ember<T> ember; Ember<T> ember;
Xform<T> xform; Xform<T> xform;
QDateTime local(QDateTime::currentDateTime()); QDateTime local(QDateTime::currentDateTime());
StopPreviewRender(); StopAllPreviewRenderers();
ParamsToEmber(ember); ParamsToEmber(ember);
xform.m_Weight = T(0.25); xform.m_Weight = T(0.25);
xform.m_ColorX = m_Rand.Frand01<T>(); xform.m_ColorX = m_Rand.Frand01<T>();
@ -116,7 +116,7 @@ template <typename T>
void FractoriumEmberController<T>::NewRandomFlameInCurrentFile() void FractoriumEmberController<T>::NewRandomFlameInCurrentFile()
{ {
Ember<T> ember; Ember<T> ember;
StopPreviewRender(); StopAllPreviewRenderers();
m_SheepTools->Random(ember, m_FilteredVariations, static_cast<int>(QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedFrand<T>(-2, 2)), 0, MAX_CL_VARS); m_SheepTools->Random(ember, m_FilteredVariations, static_cast<int>(QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedFrand<T>(-2, 2)), 0, MAX_CL_VARS);
ParamsToEmber(ember); ParamsToEmber(ember);
ember.m_Name = EmberFile<T>::DefaultEmberName(m_EmberFile.Size() + 1).toStdString(); ember.m_Name = EmberFile<T>::DefaultEmberName(m_EmberFile.Size() + 1).toStdString();
@ -138,7 +138,7 @@ template <typename T>
void FractoriumEmberController<T>::CopyFlameInCurrentFile() void FractoriumEmberController<T>::CopyFlameInCurrentFile()
{ {
auto ember = m_Ember; auto ember = m_Ember;
StopPreviewRender(); StopAllPreviewRenderers();
ember.m_Name = EmberFile<T>::DefaultEmberName(m_EmberFile.Size() + 1).toStdString(); ember.m_Name = EmberFile<T>::DefaultEmberName(m_EmberFile.Size() + 1).toStdString();
ember.m_Index = m_EmberFile.Size(); ember.m_Index = m_EmberFile.Size();
m_EmberFile.m_Embers.push_back(ember);//Will invalidate the pointers contained in the EmberTreeWidgetItems, UpdateLibraryTree() will resync. m_EmberFile.m_Embers.push_back(ember);//Will invalidate the pointers contained in the EmberTreeWidgetItems, UpdateLibraryTree() will resync.
@ -172,7 +172,7 @@ void FractoriumEmberController<T>::OpenAndPrepFiles(const QStringList& filenames
vector<Ember<T>> embers; vector<Ember<T>> embers;
vector<string> errors; vector<string> errors;
uint previousSize = append ? uint(m_EmberFile.Size()) : 0u; uint previousSize = append ? uint(m_EmberFile.Size()) : 0u;
StopPreviewRender(); StopAllPreviewRenderers();
emberFile.m_Filename = filenames[0]; emberFile.m_Filename = filenames[0];
for (auto& filename : filenames) for (auto& filename : filenames)
@ -377,7 +377,7 @@ uint FractoriumEmberController<T>::SaveCurrentToOpenedFile(bool render)
uint i = 0; uint i = 0;
bool fileFound = false; bool fileFound = false;
if (!m_PreviewRunning) if (!m_LibraryPreviewRenderer->Running())
{ {
for (auto& it : m_EmberFile.m_Embers) for (auto& it : m_EmberFile.m_Embers)
{ {
@ -393,7 +393,7 @@ uint FractoriumEmberController<T>::SaveCurrentToOpenedFile(bool render)
if (!fileFound) if (!fileFound)
{ {
StopPreviewRender(); StopAllPreviewRenderers();
m_EmberFile.m_Embers.push_back(m_Ember); m_EmberFile.m_Embers.push_back(m_Ember);
m_EmberFile.MakeNamesUnique(); m_EmberFile.MakeNamesUnique();
@ -401,7 +401,7 @@ uint FractoriumEmberController<T>::SaveCurrentToOpenedFile(bool render)
UpdateLibraryTree(); UpdateLibraryTree();
} }
else if (render) else if (render)
RenderPreviews(i, i + 1); RenderLibraryPreviews(i, i + 1);
} }
return i; return i;
@ -532,7 +532,7 @@ void FractoriumEmberController<T>::PasteXmlAppend()
} }
b.clear(); b.clear();
StopPreviewRender(); StopAllPreviewRenderers();
parser.Parse(reinterpret_cast<byte*>(const_cast<char*>(s.c_str())), "", embers); parser.Parse(reinterpret_cast<byte*>(const_cast<char*>(s.c_str())), "", embers);
errors = parser.ErrorReportString(); errors = parser.ErrorReportString();
@ -586,7 +586,7 @@ void FractoriumEmberController<T>::PasteXmlOver()
} }
b.clear(); b.clear();
StopPreviewRender(); StopAllPreviewRenderers();
m_EmberFile.m_Embers.clear();//Will invalidate the pointers contained in the EmberTreeWidgetItems, UpdateLibraryTree() will resync. m_EmberFile.m_Embers.clear();//Will invalidate the pointers contained in the EmberTreeWidgetItems, UpdateLibraryTree() will resync.
parser.Parse(reinterpret_cast<byte*>(const_cast<char*>(s.c_str())), "", m_EmberFile.m_Embers); parser.Parse(reinterpret_cast<byte*>(const_cast<char*>(s.c_str())), "", m_EmberFile.m_Embers);
errors = parser.ErrorReportString(); errors = parser.ErrorReportString();
@ -812,14 +812,14 @@ void Fractorium::OnActionClearFlame(bool checked) { m_Controller->ClearFlame();
/// </summary> /// </summary>
void Fractorium::OnActionRenderPreviews(bool checked) void Fractorium::OnActionRenderPreviews(bool checked)
{ {
m_Controller->RenderPreviews(); m_Controller->RenderLibraryPreviews();
} }
/// <summary> /// <summary>
/// Stop all previews from being rendered. This is handy if the user /// Stop all previews from being rendered. This is handy if the user
/// opens a large file with many embers in it, such as an animation sequence. /// opens a large file with many embers in it, such as an animation sequence.
/// </summary> /// </summary>
void Fractorium::OnActionStopRenderingPreviews(bool checked) { m_Controller->StopPreviewRender(); } void Fractorium::OnActionStopRenderingPreviews(bool checked) { m_Controller->StopLibraryPreviewRender(); }
/// <summary> /// <summary>
/// Show the final render dialog as a modeless dialog to allow /// Show the final render dialog as a modeless dialog to allow
@ -832,10 +832,10 @@ void Fractorium::OnActionFinalRender(bool checked)
{ {
//First completely stop what the current rendering process is doing. //First completely stop what the current rendering process is doing.
m_Controller->DeleteRenderer();//Delete the renderer, but not the controller. m_Controller->DeleteRenderer();//Delete the renderer, but not the controller.
m_Controller->StopPreviewRender(); m_Controller->StopAllPreviewRenderers();
m_Controller->SaveCurrentToOpenedFile(false);//Save whatever was edited back to the current open file. m_Controller->SaveCurrentToOpenedFile(false);//Save whatever was edited back to the current open file.
m_RenderStatusLabel->setText("Renderer stopped."); m_RenderStatusLabel->setText("Renderer stopped.");
m_FinalRenderDialog->show(); m_FinalRenderDialog->Show(false);
} }
/// <summary> /// <summary>

View File

@ -469,19 +469,17 @@ void Fractorium::OnTemporalFilterTypeComboCurrentIndexChanged(const QString& tex
template <typename T> template <typename T>
void FractoriumEmberController<T>::DEFilterMinRadiusWidthChanged(double d) void FractoriumEmberController<T>::DEFilterMinRadiusWidthChanged(double d)
{ {
if (m_Fractorium->m_DEFilterMinRadiusSpin->value() > m_Fractorium->m_DEFilterMaxRadiusSpin->value())
{
m_Fractorium->m_DEFilterMinRadiusSpin->SetValueStealth(m_Fractorium->m_DEFilterMaxRadiusSpin->value());
return;
}
UpdateAll([&](Ember<T>& ember) UpdateAll([&](Ember<T>& ember)
{ {
ember.m_MinRadDE = d; ember.m_MinRadDE = d;
}, true, eProcessAction::FILTER_AND_ACCUM, m_Fractorium->ApplyAll()); }, true, eProcessAction::FILTER_AND_ACCUM, m_Fractorium->ApplyAll());
} }
void Fractorium::OnDEFilterMinRadiusWidthChanged(double d) { m_Controller->DEFilterMinRadiusWidthChanged(d); } void Fractorium::OnDEFilterMinRadiusWidthChanged(double d)
{
if (!ConstrainLow(m_DEFilterMinRadiusSpin, m_DEFilterMaxRadiusSpin))
m_Controller->DEFilterMinRadiusWidthChanged(d);
}
/// <summary> /// <summary>
/// Set the density estimation filter max radius value. /// Set the density estimation filter max radius value.
@ -491,19 +489,17 @@ void Fractorium::OnDEFilterMinRadiusWidthChanged(double d) { m_Controller->DEFil
template <typename T> template <typename T>
void FractoriumEmberController<T>::DEFilterMaxRadiusWidthChanged(double d) void FractoriumEmberController<T>::DEFilterMaxRadiusWidthChanged(double d)
{ {
if (m_Fractorium->m_DEFilterMaxRadiusSpin->value() < m_Fractorium->m_DEFilterMinRadiusSpin->value())
{
m_Fractorium->m_DEFilterMaxRadiusSpin->SetValueStealth(m_Fractorium->m_DEFilterMinRadiusSpin->value());
return;
}
UpdateAll([&](Ember<T>& ember) UpdateAll([&](Ember<T>& ember)
{ {
ember.m_MaxRadDE = d; ember.m_MaxRadDE = d;
}, true, eProcessAction::FILTER_AND_ACCUM, m_Fractorium->ApplyAll()); }, true, eProcessAction::FILTER_AND_ACCUM, m_Fractorium->ApplyAll());
} }
void Fractorium::OnDEFilterMaxRadiusWidthChanged(double d) { m_Controller->DEFilterMaxRadiusWidthChanged(d); } void Fractorium::OnDEFilterMaxRadiusWidthChanged(double d)
{
if (!ConstrainHigh(m_DEFilterMinRadiusSpin, m_DEFilterMaxRadiusSpin))
m_Controller->DEFilterMaxRadiusWidthChanged(d);
}
/// <summary> /// <summary>
/// Set the density estimation filter curve value. /// Set the density estimation filter curve value.

View File

@ -85,4 +85,6 @@
using namespace std; using namespace std;
using namespace EmberNs; using namespace EmberNs;
using namespace EmberCLns; using namespace EmberCLns;
using namespace EmberCommon;
#endif #endif

View File

@ -580,13 +580,11 @@ bool FractoriumEmberController<T>::CreateRenderer(eRendererType renderType, cons
else else
m_Renderer->InteractiveFilter(s->OpenCLDEFilter() ? eInteractiveFilter::FILTER_DE : eInteractiveFilter::FILTER_LOG); m_Renderer->InteractiveFilter(s->OpenCLDEFilter() ? eInteractiveFilter::FILTER_DE : eInteractiveFilter::FILTER_LOG);
if ((m_Renderer->EarlyClip() != m_PreviewRenderer->EarlyClip()) || if ((m_Renderer->EarlyClip() != m_LibraryPreviewRenderer->EarlyClip()) ||
(m_Renderer->YAxisUp() != m_PreviewRenderer->YAxisUp())) (m_Renderer->YAxisUp() != m_LibraryPreviewRenderer->YAxisUp()) ||
(m_Renderer->Transparency() != m_LibraryPreviewRenderer->Transparency()))
{ {
StopPreviewRender(); RenderLibraryPreviews();
m_PreviewRenderer->EarlyClip(m_Renderer->EarlyClip());
m_PreviewRenderer->YAxisUp(m_Renderer->YAxisUp());
RenderPreviews();
} }
m_FailedRenders = 0; m_FailedRenders = 0;
@ -690,7 +688,7 @@ bool Fractorium::CreateControllerFromOptions()
if (m_Controller.get()) if (m_Controller.get())
{ {
scale = m_Controller->LockedScale(); scale = m_Controller->LockedScale();
m_Controller->StopPreviewRender();//Must stop any previews first, else changing controllers will crash the program and SaveCurrentToOpenedFile() will return 0. m_Controller->StopAllPreviewRenderers();//Must stop any previews first, else changing controllers will crash the program and SaveCurrentToOpenedFile() will return 0.
current = m_Controller->SaveCurrentToOpenedFile(false); current = m_Controller->SaveCurrentToOpenedFile(false);
m_Controller->CopyTempPalette(tempPalette);//Convert float to double or save double verbatim; m_Controller->CopyTempPalette(tempPalette);//Convert float to double or save double verbatim;
//Replace below with this once LLVM fixes a crash in their compiler with default lambda parameters.//TODO //Replace below with this once LLVM fixes a crash in their compiler with default lambda parameters.//TODO
@ -698,10 +696,10 @@ bool Fractorium::CreateControllerFromOptions()
//m_Controller->CopyEmberFile(efd); //m_Controller->CopyEmberFile(efd);
#ifdef DO_DOUBLE #ifdef DO_DOUBLE
m_Controller->CopyEmber(ed, [&](Ember<double>& ember) { }); m_Controller->CopyEmber(ed, [&](Ember<double>& ember) { });
m_Controller->CopyEmberFile(efd, [&](Ember<double>& ember) { }); m_Controller->CopyEmberFile(efd, false, [&](Ember<double>& ember) { });
#else #else
m_Controller->CopyEmber(ed, [&](Ember<float>& ember) { }); m_Controller->CopyEmber(ed, [&](Ember<float>& ember) { });
m_Controller->CopyEmberFile(efd, [&](Ember<float>& ember) { }); m_Controller->CopyEmberFile(efd, false, [&](Ember<float>& ember) { });
#endif #endif
m_Controller->Shutdown(); m_Controller->Shutdown();
} }
@ -718,7 +716,7 @@ bool Fractorium::CreateControllerFromOptions()
if (m_Controller.get()) if (m_Controller.get())
{ {
ed.m_Palette = tempPalette;//Restore base temp palette. Adjustments will be then be applied and stored back in in m_Ember.m_Palette below. ed.m_Palette = tempPalette;//Restore base temp palette. Adjustments will be then be applied and stored back in in m_Ember.m_Palette below.
m_Controller->SetEmberFile(efd); m_Controller->SetEmberFile(efd, true);
m_Controller->SetEmber(current, true); m_Controller->SetEmber(current, true);
m_Controller->LockedScale(scale); m_Controller->LockedScale(scale);
//Setting these and updating the GUI overwrites the work of clearing them done in SetEmber() above. //Setting these and updating the GUI overwrites the work of clearing them done in SetEmber() above.

View File

@ -134,6 +134,51 @@ void FractoriumSettings::OpenCLSubBatch(uint i) { setValue(OPENCLSUBBATCH,
uint FractoriumSettings::RandomCount() { return value(RANDOMCOUNT).toUInt(); } uint FractoriumSettings::RandomCount() { return value(RANDOMCOUNT).toUInt(); }
void FractoriumSettings::RandomCount(uint i) { setValue(RANDOMCOUNT, i); } void FractoriumSettings::RandomCount(uint i) { setValue(RANDOMCOUNT, i); }
/// <summary>
/// Sequence generation settings.
/// </summary>
uint FractoriumSettings::FramesPerRot() { return value(FRAMESPERROT).toUInt(); }
void FractoriumSettings::FramesPerRot(uint i) { setValue(FRAMESPERROT, i); }
uint FractoriumSettings::FramesPerRotMax() { return value(FRAMESPERROTMAX).toUInt(); }
void FractoriumSettings::FramesPerRotMax(uint i) { setValue(FRAMESPERROTMAX, i); }
uint FractoriumSettings::Rotations() { return value(ROTATIONS).toUInt(); }
void FractoriumSettings::Rotations(uint i) { setValue(ROTATIONS, i); }
uint FractoriumSettings::RotationsMax() { return value(ROTATIONSMAX).toUInt(); }
void FractoriumSettings::RotationsMax(uint i) { setValue(ROTATIONSMAX, i); }
uint FractoriumSettings::BlendFrames() { return value(BLENDFRAMES).toUInt(); }
void FractoriumSettings::BlendFrames(uint i) { setValue(BLENDFRAMES, i); }
uint FractoriumSettings::BlendFramesMax() { return value(BLENDFRAMESMAX).toUInt(); }
void FractoriumSettings::BlendFramesMax(uint i) { setValue(BLENDFRAMESMAX, i); }
/// <summary>
/// Variations filter settings.
/// </summary>
int FractoriumSettings::VarFilterSum() { return value(VARFILTERSUM).toInt(); }
void FractoriumSettings::VarFilterSum(int i) { setValue(VARFILTERSUM, i); }
int FractoriumSettings::VarFilterAssign() { return value(VARFILTERASSIGN).toInt(); }
void FractoriumSettings::VarFilterAssign(int i) { setValue(VARFILTERASSIGN, i); }
int FractoriumSettings::VarFilterPpsum() { return value(VARFILTERPPSUM).toInt(); }
void FractoriumSettings::VarFilterPpsum(int i) { setValue(VARFILTERPPSUM, i); }
int FractoriumSettings::VarFilterPpassign() { return value(VARFILTERPPASSIGN).toInt(); }
void FractoriumSettings::VarFilterPpassign(int i) { setValue(VARFILTERPPASSIGN, i); }
int FractoriumSettings::VarFilterSdc() { return value(VARFILTERSDC).toInt(); }
void FractoriumSettings::VarFilterSdc(int i) { setValue(VARFILTERSDC, i); }
int FractoriumSettings::VarFilterState() { return value(VARFILTERSTATE).toInt(); }
void FractoriumSettings::VarFilterState(int i) { setValue(VARFILTERSTATE, i); }
int FractoriumSettings::VarFilterParam() { return value(VARFILTERPARAM).toInt(); }
void FractoriumSettings::VarFilterParam(int i) { setValue(VARFILTERPARAM, i); }
int FractoriumSettings::VarFilterNonparam() { return value(VARFILTERNONPARAM).toInt(); }
void FractoriumSettings::VarFilterNonparam(int i) { setValue(VARFILTERNONPARAM, i); }
/// <summary> /// <summary>
/// Final render settings. /// Final render settings.
/// </summary> /// </summary>

View File

@ -21,12 +21,19 @@
#define OPENCLSUBBATCH "render/openclsubbatch" #define OPENCLSUBBATCH "render/openclsubbatch"
#define RANDOMCOUNT "render/randomcount" #define RANDOMCOUNT "render/randomcount"
#define FRAMESPERROT "sequence/framesperrot"
#define FRAMESPERROTMAX "sequence/framesperrotmax"
#define ROTATIONS "sequence/rotations"
#define ROTATIONSMAX "sequence/rotationsmax"
#define BLENDFRAMES "sequence/blendframes"
#define BLENDFRAMESMAX "sequence/blendframesmax"
#define VARFILTERSUM "varfilter/sumcheckbox" #define VARFILTERSUM "varfilter/sumcheckbox"
#define VARFILTERASSIGN "varfilter/assigncheckbox" #define VARFILTERASSIGN "varfilter/assigncheckbox"
#define VARFILTERPPSUM "varfilter/ppsumcheckbox" #define VARFILTERPPSUM "varfilter/ppsumcheckbox"
#define VARFILTERPPASSIGN "varfilter/ppassigncheckbox" #define VARFILTERPPASSIGN "varfilter/ppassigncheckbox"
#define VARFILTERSDC "varfilter/dccheckbox" #define VARFILTERSDC "varfilter/dccheckbox"
#define VARFILTERSSTATE "varfilter/statecheckbox" #define VARFILTERSTATE "varfilter/statecheckbox"
#define VARFILTERPARAM "varfilter/paramcheckbox" #define VARFILTERPARAM "varfilter/paramcheckbox"
#define VARFILTERNONPARAM "varfilter/nonparamcheckbox" #define VARFILTERNONPARAM "varfilter/nonparamcheckbox"
@ -126,6 +133,43 @@ public:
uint RandomCount(); uint RandomCount();
void RandomCount(uint i); void RandomCount(uint i);
uint FramesPerRot();
void FramesPerRot(uint i);
uint FramesPerRotMax();
void FramesPerRotMax(uint i);
uint Rotations();
void Rotations(uint i);
uint RotationsMax();
void RotationsMax(uint i);
uint BlendFrames();
void BlendFrames(uint i);
uint BlendFramesMax();
void BlendFramesMax(uint i);
int VarFilterSum();
void VarFilterSum(int i);
int VarFilterAssign();
void VarFilterAssign(int i);
int VarFilterPpsum();
void VarFilterPpsum(int i);
int VarFilterPpassign();
void VarFilterPpassign(int i);
int VarFilterSdc();
void VarFilterSdc(int i);
int VarFilterState();
void VarFilterState(int i);
int VarFilterParam();
void VarFilterParam(int i);
int VarFilterNonparam();
void VarFilterNonparam(int i);
bool FinalEarlyClip(); bool FinalEarlyClip();
void FinalEarlyClip(bool b); void FinalEarlyClip(bool b);

View File

@ -26,14 +26,14 @@ FractoriumVariationsDialog::FractoriumVariationsDialog(FractoriumSettings* setti
m_CheckBoxes.push_back(ui.StateCheckBox); m_CheckBoxes.push_back(ui.StateCheckBox);
m_CheckBoxes.push_back(ui.ParamCheckBox); m_CheckBoxes.push_back(ui.ParamCheckBox);
m_CheckBoxes.push_back(ui.NonParamCheckBox); m_CheckBoxes.push_back(ui.NonParamCheckBox);
ui.SumCheckBox->setCheckState(Qt::CheckState(m_Settings->value(VARFILTERSUM).toInt())); ui.SumCheckBox->setCheckState (Qt::CheckState(m_Settings->VarFilterSum ()));
ui.AssignCheckBox->setCheckState(Qt::CheckState(m_Settings->value(VARFILTERASSIGN).toInt())); ui.AssignCheckBox->setCheckState (Qt::CheckState(m_Settings->VarFilterAssign ()));
ui.PpSumCheckBox->setCheckState(Qt::CheckState(m_Settings->value(VARFILTERPPSUM).toInt())); ui.PpSumCheckBox->setCheckState (Qt::CheckState(m_Settings->VarFilterPpsum ()));
ui.PpAssignCheckBox->setCheckState(Qt::CheckState(m_Settings->value(VARFILTERPPASSIGN).toInt())); ui.PpAssignCheckBox->setCheckState(Qt::CheckState(m_Settings->VarFilterPpassign()));
ui.DcCheckBox->setCheckState(Qt::CheckState(m_Settings->value(VARFILTERSDC).toInt())); ui.DcCheckBox->setCheckState (Qt::CheckState(m_Settings->VarFilterSdc ()));
ui.StateCheckBox->setCheckState(Qt::CheckState(m_Settings->value(VARFILTERSSTATE).toInt())); ui.StateCheckBox->setCheckState (Qt::CheckState(m_Settings->VarFilterState ()));
ui.ParamCheckBox->setCheckState(Qt::CheckState(m_Settings->value(VARFILTERPARAM).toInt())); ui.ParamCheckBox->setCheckState (Qt::CheckState(m_Settings->VarFilterParam ()));
ui.NonParamCheckBox->setCheckState(Qt::CheckState(m_Settings->value(VARFILTERNONPARAM).toInt())); ui.NonParamCheckBox->setCheckState(Qt::CheckState(m_Settings->VarFilterNonparam()));
for (auto& cb : m_CheckBoxes) for (auto& cb : m_CheckBoxes)
{ {
@ -112,14 +112,14 @@ void FractoriumVariationsDialog::SyncSettings()
m[cb->text()] = cb->checkState() == Qt::CheckState::Checked; m[cb->text()] = cb->checkState() == Qt::CheckState::Checked;
}); });
m_Settings->Variations(m); m_Settings->Variations(m);
m_Settings->setValue(VARFILTERSUM , int(ui.SumCheckBox->checkState())); m_Settings->VarFilterSum (int(ui.SumCheckBox->checkState()));
m_Settings->setValue(VARFILTERASSIGN , int(ui.AssignCheckBox->checkState())); m_Settings->VarFilterAssign (int(ui.AssignCheckBox->checkState()));
m_Settings->setValue(VARFILTERPPSUM , int(ui.PpSumCheckBox->checkState())); m_Settings->VarFilterPpsum (int(ui.PpSumCheckBox->checkState()));
m_Settings->setValue(VARFILTERPPASSIGN, int(ui.PpAssignCheckBox->checkState())); m_Settings->VarFilterPpassign(int(ui.PpAssignCheckBox->checkState()));
m_Settings->setValue(VARFILTERSDC , int(ui.DcCheckBox->checkState())); m_Settings->VarFilterSdc (int(ui.DcCheckBox->checkState()));
m_Settings->setValue(VARFILTERSSTATE , int(ui.StateCheckBox->checkState())); m_Settings->VarFilterState (int(ui.StateCheckBox->checkState()));
m_Settings->setValue(VARFILTERPARAM , int(ui.ParamCheckBox->checkState())); m_Settings->VarFilterParam (int(ui.ParamCheckBox->checkState()));
m_Settings->setValue(VARFILTERNONPARAM, int(ui.NonParamCheckBox->checkState())); m_Settings->VarFilterNonparam(int(ui.NonParamCheckBox->checkState()));
} }
/// <summary> /// <summary>

View File

@ -243,6 +243,20 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<tabstops>
<tabstop>SelectAllButton</tabstop>
<tabstop>InvertSelectionButton</tabstop>
<tabstop>SelectNoneButton</tabstop>
<tabstop>SumCheckBox</tabstop>
<tabstop>AssignCheckBox</tabstop>
<tabstop>PpSumCheckBox</tabstop>
<tabstop>PpAssignCheckBox</tabstop>
<tabstop>DcCheckBox</tabstop>
<tabstop>StateCheckBox</tabstop>
<tabstop>ParamCheckBox</tabstop>
<tabstop>NonParamCheckBox</tabstop>
<tabstop>VariationsTable</tabstop>
</tabstops>
<resources/> <resources/>
<connections> <connections>
<connection> <connection>