--User changes

-Support 4k monitors, and in general, properly scale any monitor that is not HD.
 -Allow for a spatial filter of radius zero, which means do not use a spatial filter.
 -Add new variations: concentric, cpow3, helicoid, helix, rand_cubes, sphereblur.
 -Use a new method for computing elliptic which is more precise. Developed by Discord user Claude.
 -Remove the 8 variation per xform limitation on the GPU.
 -Allow for loading the last flame file on startup, rather than randoms.
 -Use two different default quality values in the interactive renderer, one each for CPU and GPU.
 -Creating linked xforms was using non-standard behavior. Make it match Apo and also support creating multiple linked xforms at once.

--Bug fixes
 -No variations in an xform used to have the same behavior as a single linear variation with weight 1. While sensible, this breaks backward compatibility. No variations now sets the output point to zeroes.
 -Prevent crashing the program when adjusting a value on the main window while a final render is in progress.
 -The xaos table was inverted.

--Code changes
 -Convert projects to Visual Studio 2017.
 -Change bad vals from +- 1e10 to +-1e20.
 -Reintroduce the symmetry tag in xforms for legacy support in programs that do not use color_speed.
 -Compiler will not let us use default values in templated member functions anymore.
This commit is contained in:
Person
2017-11-26 17:27:00 -08:00
parent be1bfd9ab6
commit fcd060976c
74 changed files with 7559 additions and 3188 deletions

View File

@ -58,7 +58,7 @@
<enum>QFrame::NoFrame</enum>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;Fractorium 1.0.0.6&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;A Qt-based fractal flame editor which uses a C++ re-write of the flam3 algorithm named Ember and a GPU capable version named EmberCL which implements a portion of the cuburn algorithm in OpenCL.&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;a href=&quot;http://fractorium.com&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;fractorium.com&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&lt;br/&gt;Lead: Matt Feemster&lt;br/&gt;Contributors: Simon Detheridge, Michel Mastriani&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;Fractorium 1.0.0.7&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;A Qt-based fractal flame editor which uses a C++ re-write of the flam3 algorithm named Ember and a GPU capable version named EmberCL which implements a portion of the cuburn algorithm in OpenCL.&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;a href=&quot;http://fractorium.com&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;fractorium.com&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&lt;br/&gt;Lead: Matt Feemster&lt;br/&gt;Contributors: Simon Detheridge, Michel Mastriani&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>

View File

@ -118,9 +118,9 @@ FinalRenderEmberController<T>::FinalRenderEmberController(FractoriumFinalRenderD
//Save backup Xml.
if (doAll)
m_XmlWriter.Save(backup.toStdString().c_str(), m_EmberFile.m_Embers, 0, true, false, true);
m_XmlWriter.Save(backup.toStdString().c_str(), m_EmberFile.m_Embers, 0, true, false, true, false, false);
else
m_XmlWriter.Save(backup.toStdString().c_str(), *m_Ember, 0, true, false, true);
m_XmlWriter.Save(backup.toStdString().c_str(), *m_Ember, 0, true, false, true, false, false);
m_FinishedImageCount.store(0);
SyncGuiToRenderer();

View File

@ -112,10 +112,10 @@ Fractorium::Fractorium(QWidget* p)
m_Controller->SetupVariationsTree();
m_Controller->FilteredVariations();
if (m_Info->Ok() && m_Settings->OpenCL() && m_QualitySpin->value() < (30 * m_Settings->Devices().size()))
m_QualitySpin->setValue(30 * m_Settings->Devices().size());
if (m_Info->Ok() && m_Settings->OpenCL() && m_QualitySpin->value() < (m_Settings->OpenClQuality() * m_Settings->Devices().size()))
m_QualitySpin->setValue(m_Settings->OpenClQuality() * m_Settings->Devices().size());
int statusBarHeight = 20 * devicePixelRatio();
int statusBarHeight = 20;// *devicePixelRatio();
ui.StatusBar->setMinimumHeight(statusBarHeight);
ui.StatusBar->setMaximumHeight(statusBarHeight);
m_RenderStatusLabel = new QLabel(this);
@ -222,6 +222,9 @@ Fractorium::~Fractorium()
m_Settings->ShowGrid(ui.ActionDrawGrid->isChecked());
m_Settings->setValue("windowState", saveState());
m_Settings->sync();
if (m_Settings->LoadLast())
m_Controller->SaveCurrentFileOnShutdown();
}
/// <summary>
@ -330,8 +333,8 @@ bool Fractorium::eventFilter(QObject* o, QEvent* e)
{
if (o == ui.GLParentScrollArea && e->type() == QEvent::Resize)
{
m_WidthSpin->DoubleClickNonZero(ui.GLParentScrollArea->width());
m_HeightSpin->DoubleClickNonZero(ui.GLParentScrollArea->height());
m_WidthSpin->DoubleClickNonZero(ui.GLParentScrollArea->width() * ui.GLDisplay->devicePixelRatioF());
m_HeightSpin->DoubleClickNonZero(ui.GLParentScrollArea->height() * ui.GLDisplay->devicePixelRatioF());
}
else if (auto ke = dynamic_cast<QKeyEvent*>(e))
{
@ -368,8 +371,8 @@ bool Fractorium::eventFilter(QObject* o, QEvent* e)
/// <param name="e">The event</param>
void Fractorium::resizeEvent(QResizeEvent* e)
{
m_WidthSpin->DoubleClickNonZero(ui.GLParentScrollArea->width());
m_HeightSpin->DoubleClickNonZero(ui.GLParentScrollArea->height());
m_WidthSpin->DoubleClickNonZero(ui.GLParentScrollArea->width() * ui.GLDisplay->devicePixelRatioF());
m_HeightSpin->DoubleClickNonZero(ui.GLParentScrollArea->height() * ui.GLDisplay->devicePixelRatioF());
QMainWindow::resizeEvent(e);
}

View File

@ -310,6 +310,7 @@ public slots:
void OnXformsSelectAllButtonClicked(bool checked);
void OnXformsSelectNoneButtonClicked(bool checked);
bool IsXformSelected(size_t i);
int SelectedXformCount(bool includeFinal);
//Xaos.
void OnXaosChanged(double d);

View File

@ -74,8 +74,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>1269</width>
<height>987</height>
<width>1259</width>
<height>980</height>
</rect>
</property>
<property name="sizePolicy">
@ -2007,14 +2007,14 @@
<rect>
<x>770</x>
<y>0</y>
<width>294</width>
<width>295</width>
<height>881</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>294</width>
<height>617</height>
<width>295</width>
<height>624</height>
</size>
</property>
<property name="features">
@ -2689,13 +2689,13 @@
</property>
<property name="minimumSize">
<size>
<width>60</width>
<width>65</width>
<height>22</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>60</width>
<width>65</width>
<height>22</height>
</size>
</property>
@ -3700,8 +3700,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>263</width>
<height>722</height>
<width>128</width>
<height>686</height>
</rect>
</property>
<property name="autoFillBackground">
@ -5786,8 +5786,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>259</width>
<height>652</height>
<width>136</width>
<height>58</height>
</rect>
</property>
<property name="sizePolicy">
@ -5920,7 +5920,7 @@
<x>0</x>
<y>0</y>
<width>291</width>
<height>851</height>
<height>854</height>
</rect>
</property>
<property name="sizePolicy">
@ -7027,8 +7027,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>432</width>
<height>589</height>
<width>442</width>
<height>587</height>
</rect>
</property>
<property name="sizePolicy">

View File

@ -344,7 +344,7 @@ void FractoriumEmberController<T>::SetEmberPrivate(const Ember<U>& ember, bool v
dir.mkpath(".");
string filename = path.toStdString() + "/last.flame";
writer.Save(filename.c_str(), m_Ember, 0, true, true, false, true, true);
writer.Save(filename, m_Ember, 0, true, true, false, true, true);
m_GLController->ResetMouseState();
FillXforms();//Must do this first because the palette setup in FillParamTablesAndPalette() uses the xforms combo.
FillParamTablesAndPalette();

View File

@ -100,6 +100,7 @@ public:
virtual void SaveCurrentAsXml() { }
virtual void SaveEntireFileAsXml() { }
virtual uint SaveCurrentToOpenedFile(bool render = true) { return 0; }
virtual void SaveCurrentFileOnShutdown() { }
virtual void Undo() { }//Edit.
virtual void Redo() { }
virtual void CopyXml() { }
@ -362,6 +363,7 @@ public:
virtual void SaveCurrentAsXml() override;
virtual void SaveEntireFileAsXml() override;
virtual uint SaveCurrentToOpenedFile(bool render = true) override;
virtual void SaveCurrentFileOnShutdown() override;
virtual void Undo() override;
virtual void Redo() override;
virtual void CopyXml() override;

View File

@ -768,7 +768,7 @@ void FractoriumEmberController<T>::SequenceSaveButtonClicked()
for (auto& ember : m_SequenceFile.m_Embers)
ApplyXmlSavingTemplate(ember);
if (writer.Save(filename.toStdString().c_str(), m_SequenceFile.m_Embers, 0, true, true))
if (writer.Save(filename.toStdString().c_str(), m_SequenceFile.m_Embers, 0, true, true, false, false, false))
s->SaveFolder(fileInfo.canonicalPath());
else
m_Fractorium->ShowCritical("Save Failed", "Could not save sequence file, try saving to a different folder.");
@ -800,7 +800,7 @@ void FractoriumEmberController<T>::SequenceOpenButtonClicked()
{
embers.clear();
if (parser.Parse(filename.toStdString().c_str(), embers) && !embers.empty())
if (parser.Parse(filename.toStdString().c_str(), embers, true) && !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.

View File

@ -77,7 +77,7 @@ void FractoriumEmberController<T>::NewFlock(size_t count)
for (size_t i = 0; i < count; i++)
{
m_SheepTools->Random(ember, filteredVariationsRef, static_cast<intmax_t>(QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedFrand<T>(-2, 2)), 0, MAX_CL_VARS);
m_SheepTools->Random(ember, filteredVariationsRef, static_cast<intmax_t>(QTIsaac<ISAAC_SIZE, ISAAC_INT>::LockedFrand<T>(-2, 2)), 0, 8);
ParamsToEmber(ember);
ember.m_Index = i;
ember.m_Name = m_EmberFile.m_Filename.toStdString() + "_" + ToString(i + 1ULL).toStdString();
@ -114,6 +114,7 @@ void FractoriumEmberController<T>::NewEmptyFlameInCurrentFile()
ParamsToEmber(ember);
xform.m_Weight = T(0.25);
xform.m_ColorX = m_Rand.Frand01<T>();
xform.AddVariation(m_VariationList->GetVariationCopy(eVariationId::VAR_LINEAR));
ember.AddXform(xform);
ember.m_Palette = *m_PaletteList->GetRandomPalette();
ember.m_Name = EmberFile<T>::DefaultEmberName(m_EmberFile.Size() + 1).toStdString();
@ -136,7 +137,7 @@ void FractoriumEmberController<T>::NewRandomFlameInCurrentFile()
{
Ember<T> ember;
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, 8);
ParamsToEmber(ember);
ember.m_Name = EmberFile<T>::DefaultEmberName(m_EmberFile.Size() + 1).toStdString();
ember.m_Index = m_EmberFile.Size();
@ -198,7 +199,7 @@ void FractoriumEmberController<T>::OpenAndPrepFiles(const QStringList& filenames
{
embers.clear();
if (parser.Parse(filename.toStdString().c_str(), embers) && !embers.empty())
if (parser.Parse(filename.toStdString().c_str(), embers, true) && !embers.empty())
{
for (i = 0; i < embers.size(); i++)
{
@ -235,6 +236,12 @@ void FractoriumEmberController<T>::OpenAndPrepFiles(const QStringList& filenames
}
else if (emberFile.Size() > 0)//Ensure at least something was read.
m_EmberFile = std::move(emberFile);//Move the temp to avoid creating dupes because we no longer need it.
else if (!m_EmberFile.Size())
{
//Loading failed, so fill it with a dummy.
Ember<T> ember;
m_EmberFile.m_Embers.push_back(ember);
}
//Resync indices and names.
i = 0;
@ -343,7 +350,7 @@ void FractoriumEmberController<T>::SaveEntireFileAsXml()
for (auto& ember : emberFile.m_Embers)
ApplyXmlSavingTemplate(ember);
if (writer.Save(filename.toStdString().c_str(), emberFile.m_Embers, 0, true, true))
if (writer.Save(filename.toStdString().c_str(), emberFile.m_Embers, 0, true, true, false, false, false))
{
if (!s->SaveAutoUnique() || m_LastSaveAll == "")//Only save filename on first time through when doing auto unique names.
m_LastSaveAll = filename;
@ -357,6 +364,20 @@ void FractoriumEmberController<T>::SaveEntireFileAsXml()
void Fractorium::OnActionSaveEntireFileAsXml(bool checked) { m_Controller->SaveEntireFileAsXml(); }
template <typename T>
void FractoriumEmberController<T>::SaveCurrentFileOnShutdown()
{
EmberToXml<T> writer;
auto path = GetDefaultUserPath();
QDir dir(path);
if (!dir.exists())
dir.mkpath(".");
string filename = path.toStdString() + "/lastonshutdown.flame";
writer.Save(filename, m_EmberFile.m_Embers, 0, true, true, false, false, false);
}
/// <summary>
/// Show a file save dialog and save what is currently shown in the render window to disk as an image.
/// </summary>
@ -557,7 +578,7 @@ void FractoriumEmberController<T>::PasteXmlAppend()
b.clear();
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, true);
errors = parser.ErrorReportString();
if (errors != "")
@ -598,7 +619,8 @@ void FractoriumEmberController<T>::PasteXmlOver()
size_t i = 0;
string s, errors;
XmlToEmber<T> parser;
auto backupEmber = m_EmberFile.m_Embers.begin();
list<Ember<T>> embers;
auto backupEmber = *m_EmberFile.m_Embers.begin();
auto codec = QTextCodec::codecForName("UTF-8");
auto b = codec->fromUnicode(QApplication::clipboard()->text());
s.reserve(b.size());
@ -611,8 +633,7 @@ void FractoriumEmberController<T>::PasteXmlOver()
b.clear();
StopAllPreviewRenderers();
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())), "", embers, true);
errors = parser.ErrorReportString();
if (errors != "")
@ -620,8 +641,10 @@ void FractoriumEmberController<T>::PasteXmlOver()
m_Fractorium->ShowCritical("Paste Error", QString::fromStdString(errors));
}
if (m_EmberFile.Size())
if (embers.size())
{
m_EmberFile.m_Embers = std::move(embers);//Will invalidate the pointers contained in the EmberTreeWidgetItems, UpdateLibraryTree() will resync.
for (auto it : m_EmberFile.m_Embers)
{
it.m_Index = i++;
@ -631,16 +654,11 @@ void FractoriumEmberController<T>::PasteXmlOver()
if (it.m_Name == "" || it.m_Name == "No name")
it.m_Name = ToString<qulonglong>(it.m_Index).toStdString();
}
}
else
{
backupEmber->m_Index = 0;
m_EmberFile.m_Embers.push_back(*backupEmber);
}
m_EmberFile.MakeNamesUnique();
FillLibraryTree();
SetEmber(0, false);
m_EmberFile.MakeNamesUnique();
FillLibraryTree();
SetEmber(0, false);
}
}
void Fractorium::OnActionPasteXmlOver(bool checked) { m_Controller->PasteXmlOver(); }
@ -856,6 +874,7 @@ void FractoriumEmberController<T>::ClearFlame()
if (auto xform = m_Ember.GetXform(0))
{
xform->Clear();
xform->AddVariation(m_VariationList->GetVariationCopy(eVariationId::VAR_LINEAR));
xform->ParentEmber(&m_Ember);
}
}

View File

@ -64,7 +64,7 @@ void Fractorium::InitParamsUI()
//Filter.
row = 0;
table = ui.FilterTable;
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_SpatialFilterWidthSpin, spinHeight, 0.1, 2, 0.1, SIGNAL(valueChanged(double)), SLOT(OnSpatialFilterWidthChanged(double)), true, 1.0, 1.0, 0.1);
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_SpatialFilterWidthSpin, spinHeight, 0, 2, 0.1, SIGNAL(valueChanged(double)), SLOT(OnSpatialFilterWidthChanged(double)), true, 1.0, 1.0, 0);
m_SpatialFilterWidthSpin->DoubleClickLowVal(0.1);
comboVals = SpatialFilterCreator<float>::FilterTypes();
SetupCombo(table, this, row, 1, m_SpatialFilterTypeCombo, comboVals, SIGNAL(currentIndexChanged(const QString&)), SLOT(OnSpatialFilterTypeComboCurrentIndexChanged(const QString&)));
@ -74,10 +74,11 @@ void Fractorium::InitParamsUI()
//Iteration.
row = 0;
table = ui.IterationTable;
auto quality = m_Settings->OpenCL() ? m_Settings->OpenClQuality() : m_Settings->CpuQuality();
SetupSpinner<SpinBox, int>( table, this, row, 1, m_SbsSpin, spinHeight, 1000, 100000, 100, SIGNAL(valueChanged(int)), SLOT(OnSbsChanged(int)), true, DEFAULT_SBS, DEFAULT_SBS, DEFAULT_SBS);
SetupSpinner<SpinBox, int>( table, this, row, 1, m_FuseSpin, spinHeight, 1, 1000, 5, SIGNAL(valueChanged(int)), SLOT(OnFuseChanged(int)), true, 15, 15, 15);
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_QualitySpin, spinHeight, 1, dmax, 50, SIGNAL(valueChanged(double)), SLOT(OnQualityChanged(double)), true, 10, 10, 10);
SetupSpinner<SpinBox, int>( table, this, row, 1, m_SupersampleSpin, spinHeight, 1, 4, 1, SIGNAL(valueChanged(int)), SLOT(OnSupersampleChanged(int)), true, 1, 1, 1);
SetupSpinner<SpinBox, int>( table, this, row, 1, m_FuseSpin, spinHeight, 1, 1000, 5, SIGNAL(valueChanged(int)), SLOT(OnFuseChanged(int)), true, 15, 15, 15);
SetupSpinner<DoubleSpinBox, double>(table, this, row, 1, m_QualitySpin, spinHeight, 1, dmax, 50, SIGNAL(valueChanged(double)), SLOT(OnQualityChanged(double)), true, quality, 10, 10);
SetupSpinner<SpinBox, int>( table, this, row, 1, m_SupersampleSpin, spinHeight, 1, 4, 1, SIGNAL(valueChanged(int)), SLOT(OnSupersampleChanged(int)), true, 1, 1, 1);
//Animation.
row = 0;
table = ui.AnimationTable;

View File

@ -97,7 +97,7 @@ void FractoriumEmberControllerBase::UpdateRender(eProcessAction action)
void FractoriumEmberControllerBase::DeleteRenderer()
{
Shutdown();
m_Renderer.reset();
m_Renderer = make_unique<EmberNs::Renderer<float, float>>();
if (GLController())
GLController()->ClearWindow();
@ -299,6 +299,9 @@ bool FractoriumEmberController<T>::SyncSizes()
template <typename T>
bool FractoriumEmberController<T>::Render()
{
if (!m_Renderer.get())
return false;
m_Rendering = true;
bool success = true;
auto gl = m_Fractorium->ui.GLDisplay;
@ -567,7 +570,7 @@ bool FractoriumEmberController<T>::CreateRenderer(eRendererType renderType, cons
if (m_RenderType == eRendererType::OPENCL_RENDERER)
{
auto val = 30 * m_Fractorium->m_Settings->Devices().size();
auto val = m_Fractorium->m_Settings->OpenClQuality() * m_Fractorium->m_Settings->Devices().size();
m_Fractorium->m_QualitySpin->DoubleClickZero(val);
m_Fractorium->m_QualitySpin->DoubleClickNonZero(val);
@ -576,11 +579,12 @@ bool FractoriumEmberController<T>::CreateRenderer(eRendererType renderType, cons
}
else
{
m_Fractorium->m_QualitySpin->DoubleClickZero(10);
m_Fractorium->m_QualitySpin->DoubleClickNonZero(10);
auto quality = m_Fractorium->m_Settings->CpuQuality();
m_Fractorium->m_QualitySpin->DoubleClickZero(quality);
m_Fractorium->m_QualitySpin->DoubleClickNonZero(quality);
if (m_Fractorium->m_QualitySpin->value() > 10)
m_Fractorium->m_QualitySpin->setValue(10);
if (m_Fractorium->m_QualitySpin->value() > quality)
m_Fractorium->m_QualitySpin->setValue(quality);
}
m_Renderer->Callback(this);

View File

@ -57,6 +57,12 @@ void FractoriumSettings::EnsureDefaults()
OpenCLSubBatch(std::max(1u, OpenCLSubBatch()));
RandomCount(std::max(1u, RandomCount()));
if (CpuQuality() == 0)
CpuQuality(10);
if (OpenClQuality() == 0)
OpenClQuality(30);
if (FinalScale() > int(eScaleType::SCALE_HEIGHT))
FinalScale(0);
@ -72,7 +78,11 @@ void FractoriumSettings::EnsureDefaults()
if (SaveImageExt() == "")
SaveImageExt(".png");
if (FinalExt() != "jpg" && FinalExt() != "png")
if (FinalExt() != "jpg" && FinalExt() != "png" && FinalExt() != "exr"
#ifdef _WIN32
&& FinalExt() != "bmp"
#endif
)
FinalExt("png");
auto s = SaveFolder();
@ -148,6 +158,15 @@ void FractoriumSettings::OpenCLSubBatch(uint i) { setValue(OPENCLSUBBATCH,
uint FractoriumSettings::RandomCount() { return value(RANDOMCOUNT).toUInt(); }
void FractoriumSettings::RandomCount(uint i) { setValue(RANDOMCOUNT, i); }
uint FractoriumSettings::CpuQuality() { return value(CPU_QUALITY).toUInt(); }
void FractoriumSettings::CpuQuality(uint i) { setValue(CPU_QUALITY, i); }
uint FractoriumSettings::OpenClQuality() { return value(OPENCL_QUALITY).toUInt(); }
void FractoriumSettings::OpenClQuality(uint i) { setValue(OPENCL_QUALITY, i); }
bool FractoriumSettings::LoadLast() { return value(LOAD_LAST).toBool(); }
void FractoriumSettings::LoadLast(bool b) { setValue(LOAD_LAST, b); }
/// <summary>
/// Sequence generation settings.
/// </summary>

View File

@ -24,6 +24,9 @@
#define CPUSUBBATCH "render/cpusubbatch"
#define OPENCLSUBBATCH "render/openclsubbatch"
#define RANDOMCOUNT "render/randomcount"
#define CPU_QUALITY "render/cpuquality"
#define OPENCL_QUALITY "render/openclquality"
#define LOAD_LAST "render/loadlastonstart"
#define STAGGER "sequence/stagger"
#define STAGGERMAX "sequence/staggermax"
@ -153,6 +156,15 @@ public:
uint RandomCount();
void RandomCount(uint i);
uint CpuQuality();
void CpuQuality(uint i);
uint OpenClQuality();
void OpenClQuality(uint i);
bool LoadLast();
void LoadLast(bool b);
double Stagger();
void Stagger(double i);

View File

@ -32,13 +32,13 @@ void Fractorium::InitXaosUI()
template <typename T>
void FractoriumEmberController<T>::FillXaos()
{
for (int i = 0, count = int(XformCount()); i < count; i++)
for (int i = 0, count = int(XformCount()); i < count; i++)//Column.
{
if (auto xform = m_Ember.GetXform(i))
{
for (int j = 0; j < count; j++)
for (int j = 0; j < count; j++)//Row.
{
QModelIndex index = m_Fractorium->m_XaosTableModel->index(i, j, QModelIndex());
QModelIndex index = m_Fractorium->m_XaosTableModel->index(j, i, QModelIndex());//j and i are intentionally swapped here.
m_Fractorium->m_XaosTableModel->setData(index, xform->Xaos(j));
}
}
@ -57,7 +57,9 @@ void FractoriumEmberController<T>::FillXaos()
/// xaos value.
/// Resets the rendering process.
/// </summary>
/// <param name="sender">The DoubleSpinBox that triggered this event</param>
/// <param name="x">The index of the xform whose xaos value was changed (column)</param>
/// <param name="y">The index of the to value that was changed for the xform (row)</param>
/// <param name="val">The changed value of the xaos element</param>
template <typename T>
void FractoriumEmberController<T>::XaosChanged(int x, int y, double val)
{
@ -73,13 +75,13 @@ void Fractorium::OnXaosChanged(double d)
if (auto senderSpinBox = qobject_cast<DoubleSpinBox*>(sender()))
{
auto p = senderSpinBox->property("tableindex").toPoint();
m_Controller->XaosChanged(p.x(), p.y(), d);
m_Controller->XaosChanged(p.y(), p.x(), d);//Intentionally switched, column is the from xform, row is the to xform.
}
}
void Fractorium::OnXaosTableModelDataChanged(const QModelIndex& indexA, const QModelIndex& indexB)
{
m_Controller->XaosChanged(indexA.row(), indexA.column(), indexA.data().toDouble());
m_Controller->XaosChanged(indexA.column(), indexA.row(), indexA.data().toDouble());//Intentionally switched, column is the from xform, row is the to xform.
}
/// <summary>
@ -190,5 +192,5 @@ void Fractorium::OnXaosColDoubleClicked(int logicalIndex)
template class FractoriumEmberController<float>;
#ifdef DO_DOUBLE
template class FractoriumEmberController<double>;
template class FractoriumEmberController<double>;
#endif

View File

@ -104,6 +104,7 @@ void FractoriumEmberController<T>::AddXform()
Xform<T> newXform;
newXform.m_Weight = 0.25;
newXform.m_ColorX = m_Rand.Frand01<T>();
newXform.AddVariation(m_VariationList->GetVariationCopy(eVariationId::VAR_LINEAR));
m_Ember.AddXform(newXform);
int index = int(m_Ember.TotalXformCount(forceFinal) - (forceFinal ? 2 : 1));//Set index to the last item before final.
FillXforms(index);
@ -115,7 +116,12 @@ void Fractorium::OnAddXformButtonClicked(bool checked) { m_Controller->AddXform(
/// <summary>
/// Add a new linked xform in the current ember and set it as the current xform.
/// Linked means:
///
/// Add an xform whose xaos values are:
/// From: All xaos values from the current xform are zero when going to any xform but the new one added, which is 1.
/// To: The xaos value coming from the current xform is 1 and the xaos values from all other xforms are 0, when going to the newly added xform.
/// Take different action when a single xform is selected vs. multiple.
/// Single: Copy current xform's xaos values to the new one.
/// Multiple: Set new xform's xaos values to 1, and except the last entry which is 0.
/// Called when the add xform button is clicked.
/// Resets the rendering process.
/// </summary>
@ -123,40 +129,71 @@ void Fractorium::OnAddXformButtonClicked(bool checked) { m_Controller->AddXform(
template <typename T>
void FractoriumEmberController<T>::AddLinkedXform()
{
bool hasAdded = false;
bool forceFinal = m_Fractorium->HaveFinal();
auto selCount = m_Fractorium->SelectedXformCount(false);
if (!selCount)//If none explicitly selected, use current.
selCount = 1;
Ember<T> ember = m_Ember;
m_Ember.Reserve(m_Ember.XformCount() + 1);//Doing this ahead of time ensures pointers remain valid.
auto iterCount = 0;
UpdateXform([&](Xform<T>* xform)
{
size_t i, count = m_Ember.XformCount();
Xform<T> newXform;
newXform.m_Weight = 0.5;
newXform.m_Opacity = 1;
newXform.m_ColorSpeed = 1;
newXform.m_ColorX = xform->m_ColorX;
//newXform.m_ColorY = xform->m_ColorY;
//Set all of the new xform's xaos values to the selected xform's xaos values,
//then set the selected xform's xaos values to 0.
for (i = 0; i < count; i++)
//Covers very strange case where final is selected, but initially not considered because final is excluded,
//but after adding the new xform, it thinks its selected index is non-final.
if (iterCount < selCount)
{
newXform.SetXaos(i, xform->Xaos(i));
xform->SetXaos(i, 0);
size_t i, count = m_Ember.XformCount();
if (!hasAdded)
{
Xform<T> newXform;
newXform.m_Weight = 0.5;
newXform.m_Opacity = xform->m_Opacity;
newXform.m_ColorSpeed = 0;
newXform.m_ColorX = 0;
//newXform.m_ColorY = xform->m_ColorY;
newXform.AddVariation(m_VariationList->GetVariationCopy(eVariationId::VAR_LINEAR));
//Set all of the new xform's xaos values to the selected xform's xaos values,
//then set the selected xform's xaos values to 0.
for (i = 0; i < count; i++)
{
if (selCount == 1)
newXform.SetXaos(i, xform->Xaos(i));
else
newXform.SetXaos(i, 1);
}
//Add the new xform and update the total count.
m_Ember.AddXform(newXform);
count = m_Ember.XformCount();
//Set the xaos for all xforms pointing to the new one to zero.
//Will set the last element of all linking and non-linking xforms, including the one we just added.
//Linking xforms will have their last xaos element set to 1 below.
for (i = 0; i < count; i++)
if (auto xf = m_Ember.GetXform(i))
xf->SetXaos(count - 1, 0);
hasAdded = true;
}
//Linking xform, so set all xaos elements to 0, except the last.
for (i = 0; i < count - 1; i++)
xform->SetXaos(i, 0);
xform->SetXaos(count - 1, 1);//Set the xaos value for the linking xform pointing to the new one to one.
xform->m_Opacity = 0;//Clear the opacity of the all linking xform.
iterCount++;
}
//Add the new xform and update the total count.
m_Ember.AddXform(newXform);
count = m_Ember.XformCount();
//Set the xaos for all xforms pointing to the new one to zero.
for (i = 0; i < count; i++)
if (auto xf = m_Ember.GetXform(i))
xf->SetXaos(count - 1, 0);
xform->SetXaos(count - 1, 1);//Set the xaos value for the previous xform pointing to the new one to one.
xform->m_Opacity = 0;//Clear the opacity of the previous xform.
int index = int(m_Ember.TotalXformCount(forceFinal) - (forceFinal ? 2 : 1));//Set index to the last item before final.
FillXforms(index);
FillXaos();
}, eXformUpdate::UPDATE_CURRENT);
}, eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL);
//Now update the GUI.
int index = int(m_Ember.TotalXformCount(forceFinal) - (forceFinal ? 2 : 1));//Set index to the last item before final.
FillXforms(index);
FillXaos();
}
void Fractorium::OnAddLinkedXformButtonClicked(bool checked) { m_Controller->AddLinkedXform(); }

View File

@ -38,6 +38,25 @@ bool Fractorium::IsXformSelected(size_t i)
return false;
}
/// <summary>
/// Return the number of xforms selected, optionally counting the final xform.
/// </summary>
/// <param name="includeFinal">Whether to include the final in the count if selected.</param>
/// <returns>The caption string</returns>
int Fractorium::SelectedXformCount(bool includeFinal)
{
int selCount = 0;
ForEachXformCheckbox([&](int index, QCheckBox * cb, bool isFinal)
{
if (cb->isChecked())
{
if (!isFinal || includeFinal)
selCount++;
}
});
return selCount;
}
/// <summary>
/// Clear all of the dynamically created xform checkboxes.
/// </summary>
@ -81,7 +100,7 @@ QString FractoriumEmberController<T>::MakeXformCaption(size_t i)
/// <summary>
/// Function to perform the specified operation on every dynamically created xform selection checkbox.
/// </summary>
/// <param name="func">The operation to perform</param>
/// <param name="func">The operation to perform which is a function taking the index of the xform, its checkbox, and a bool specifying whether it's final.</param>
void Fractorium::ForEachXformCheckbox(std::function<void(int, QCheckBox*, bool)> func)
{
int i = 0;

View File

@ -32,13 +32,36 @@ void GLWidget::InitGL()
{
if (!m_Init)
{
int w = m_Fractorium->ui.GLParentScrollArea->width();
int h = m_Fractorium->ui.GLParentScrollArea->height();
int w = std::ceil(m_Fractorium->ui.GLParentScrollArea->width() * devicePixelRatioF());
int h = std::ceil(m_Fractorium->ui.GLParentScrollArea->height() * devicePixelRatioF());
SetDimensions(w, h);
m_Fractorium->m_WidthSpin->setValue(w);
m_Fractorium->m_HeightSpin->setValue(h);
//Start with a flock of random embers. Can't do this until now because the window wasn't maximized yet, so the sizes would have been off.
m_Fractorium->OnActionNewFlock(false);
//Start with either a flock of random embers, or the last flame from the previous run.
//Can't do this until now because the window wasn't maximized yet, so the sizes would have been off.
bool b = m_Fractorium->m_Settings->LoadLast();
if (b)
{
auto path = GetDefaultUserPath();
QDir dir(path);
QString filename = path + "/lastonshutdown.flame";
if (dir.exists(filename))
{
QStringList ql;
ql << filename;
m_Fractorium->m_Controller->OpenAndPrepFiles(ql, false);
}
else
b = false;
}
if (!b)
{
m_Fractorium->OnActionNewFlock(false);
m_Fractorium->m_WidthSpin->setValue(w);
m_Fractorium->m_HeightSpin->setValue(h);
}
m_Fractorium->m_Controller->DelayedStartRenderTimer();
m_Init = true;
}
@ -61,9 +84,14 @@ void GLWidget::DrawQuad()
if (m_OutputTexID != 0 && finalImage && !finalImage->empty())
{
glBindTexture(GL_TEXTURE_2D, m_OutputTexID);//The texture to draw to.
auto scaledW = std::ceil(width() * devicePixelRatioF());
auto scaledH = std::ceil(height() * devicePixelRatioF());
//Only draw if the dimensions match exactly.
if (m_TexWidth == width() && m_TexHeight == height() && ((m_TexWidth * m_TexHeight) == GLint(finalImage->size())))
if (m_TexWidth == m_Fractorium->m_Controller->FinalRasW() &&
m_TexHeight == m_Fractorium->m_Controller->FinalRasH() &&
((m_TexWidth * m_TexHeight) == GLint(finalImage->size())))
//if (m_TexWidth == scaledW && m_TexHeight == scaledH && ((m_TexWidth * m_TexHeight) == GLint(finalImage->size())))
{
glMatrixMode(GL_PROJECTION);
glPushMatrix();
@ -337,16 +365,16 @@ void GLEmberController<T>::DrawAffines(bool pre, bool post)
if (dragging)//Draw large yellow dot on select or drag.
{
m_GL->glPointSize(6.0f);
m_GL->glPointSize(6.0f * m_GL->devicePixelRatioF());
m_GL->glBegin(GL_POINTS);
m_GL->glColor4f(1.0f, 1.0f, 0.5f, 1.0f);
m_GL->glVertex2f(m_DragHandlePos.x, m_DragHandlePos.y);
m_GL->glEnd();
m_GL->glPointSize(1.0f);//Restore point size.
m_GL->glPointSize(1.0f * m_GL->devicePixelRatioF());//Restore point size.
}
else if (m_DragState == eDragState::DragSelect)
{
m_GL->glLineWidth(2.0f);
m_GL->glLineWidth(2.0f * m_GL->devicePixelRatioF());
m_GL->glBegin(GL_LINES);
m_GL->glColor4f(0.0f, 0.0f, 1.0f, 1.0f);
m_GL->glVertex2f(m_MouseDownWorldPos.x, m_MouseDownWorldPos.y);//UL->UR
@ -358,16 +386,16 @@ void GLEmberController<T>::DrawAffines(bool pre, bool post)
m_GL->glVertex2f(m_MouseWorldPos.x, m_MouseDownWorldPos.y);//UR->LR
m_GL->glVertex2f(m_MouseWorldPos.x, m_MouseWorldPos.y);
m_GL->glEnd();
m_GL->glLineWidth(1.0f);
m_GL->glLineWidth(1.0f * m_GL->devicePixelRatioF());
}
else if (m_HoverType != eHoverType::HoverNone && m_HoverXform == m_SelectedXform)//Draw large turquoise dot on hover if they are hovering over the selected xform.
{
m_GL->glPointSize(6.0f);
m_GL->glPointSize(6.0f * m_GL->devicePixelRatioF());
m_GL->glBegin(GL_POINTS);
m_GL->glColor4f(0.5f, 1.0f, 1.0f, 1.0f);
m_GL->glVertex2f(m_HoverHandlePos.x, m_HoverHandlePos.y);
m_GL->glEnd();
m_GL->glPointSize(1.0f);
m_GL->glPointSize(1.0f * m_GL->devicePixelRatioF());
}
}
@ -435,7 +463,7 @@ void GLWidget::keyReleaseEvent(QKeyEvent* e)
template <typename T>
void GLEmberController<T>::MousePress(QMouseEvent* e)
{
v3T mouseFlipped(e->x() * m_GL->devicePixelRatio(), m_Viewport[3] - e->y() * m_GL->devicePixelRatio(), 0);//Must flip y because in OpenGL, 0,0 is bottom left, but in windows, it's top left.
v3T mouseFlipped(e->x() * m_GL->devicePixelRatioF(), m_Viewport[3] - e->y() * m_GL->devicePixelRatioF(), 0);//Must flip y because in OpenGL, 0,0 is bottom left, but in windows, it's top left.
auto ember = m_FractoriumEmberController->CurrentEmber();
auto renderer = m_FractoriumEmberController->Renderer();
@ -443,7 +471,7 @@ void GLEmberController<T>::MousePress(QMouseEvent* e)
if (!renderer)
return;
m_MouseDownPos = glm::ivec2(e->x() * m_GL->devicePixelRatio(), e->y() * m_GL->devicePixelRatio());//Capture the raster coordinates of where the mouse was clicked.
m_MouseDownPos = glm::ivec2(e->x() * m_GL->devicePixelRatioF(), e->y() * m_GL->devicePixelRatioF());//Capture the raster coordinates of where the mouse was clicked.
m_MouseWorldPos = WindowToWorld(mouseFlipped, false);//Capture the world cartesian coordinates of where the mouse is.
m_BoundsDown.w = renderer->LowerLeftX(false);//Need to capture these because they'll be changing if scaling.
m_BoundsDown.x = renderer->LowerLeftY(false);
@ -530,7 +558,7 @@ void GLWidget::mousePressEvent(QMouseEvent* e)
template <typename T>
void GLEmberController<T>::MouseRelease(QMouseEvent* e)
{
v3T mouseFlipped(e->x() * m_GL->devicePixelRatio(), m_Viewport[3] - e->y() * m_GL->devicePixelRatio(), 0);//Must flip y because in OpenGL, 0,0 is bottom left, but in windows, it's top left.
v3T mouseFlipped(e->x() * m_GL->devicePixelRatioF(), m_Viewport[3] - e->y() * m_GL->devicePixelRatioF(), 0);//Must flip y because in OpenGL, 0,0 is bottom left, but in windows, it's top left.
m_MouseWorldPos = WindowToWorld(mouseFlipped, false);
if (m_DragState == eDragState::DragDragging && (e->button() & Qt::LeftButton))
@ -567,8 +595,8 @@ template <typename T>
void GLEmberController<T>::MouseMove(QMouseEvent* e)
{
bool draw = true;
glm::ivec2 mouse(e->x() * m_GL->devicePixelRatio(), e->y() * m_GL->devicePixelRatio());
v3T mouseFlipped(e->x() * m_GL->devicePixelRatio(), m_Viewport[3] - e->y() * m_GL->devicePixelRatio(), 0);//Must flip y because in OpenGL, 0,0 is bottom left, but in windows, it's top left.
glm::ivec2 mouse(e->x() * m_GL->devicePixelRatioF(), e->y() * m_GL->devicePixelRatioF());
v3T mouseFlipped(e->x() * m_GL->devicePixelRatioF(), m_Viewport[3] - e->y() * m_GL->devicePixelRatioF(), 0);//Must flip y because in OpenGL, 0,0 is bottom left, but in windows, it's top left.
auto ember = m_FractoriumEmberController->CurrentEmber();
//First check to see if the mouse actually moved.
@ -580,7 +608,7 @@ void GLEmberController<T>::MouseMove(QMouseEvent* e)
//Update status bar on main window, regardless of whether anything is being dragged.
if (m_Fractorium->m_Controller->RenderTimerRunning())
m_Fractorium->SetCoordinateStatus(e->x() * m_GL->devicePixelRatio(), e->y() * m_GL->devicePixelRatio(), m_MouseWorldPos.x, m_MouseWorldPos.y);
m_Fractorium->SetCoordinateStatus(e->x() * m_GL->devicePixelRatioF(), e->y() * m_GL->devicePixelRatioF(), m_MouseWorldPos.x, m_MouseWorldPos.y);
if (m_SelectedXform && m_DragState == eDragState::DragDragging)//Dragging and affine.
{
@ -723,7 +751,9 @@ void GLWidget::wheelEvent(QWheelEvent* e)
/// <param name="h">Height in pixels</param>
void GLWidget::SetDimensions(int w, int h)
{
setFixedSize(w, h);
auto downscaledW = std::ceil(w / devicePixelRatioF());
auto downscaledH = std::ceil(h / devicePixelRatioF());
setFixedSize(downscaledW, downscaledH);
}
/// <summary>
@ -736,13 +766,18 @@ void GLWidget::SetDimensions(int w, int h)
bool GLWidget::Allocate(bool force)
{
bool alloc = false;
bool doResize = force || m_TexWidth != width() || m_TexHeight != height();
//auto scaledW = std::ceil(width() * devicePixelRatioF());
auto w = m_Fractorium->m_Controller->FinalRasW();
auto h = m_Fractorium->m_Controller->FinalRasH();
bool doResize = force || m_TexWidth != w || m_TexHeight != h;
bool doIt = doResize || m_OutputTexID == 0;
if (doIt)
{
m_TexWidth = width();
m_TexHeight = height();
//m_TexWidth = scaledW;
//m_TexHeight = scaledH;
m_TexWidth = GLint(w);
m_TexHeight = GLint(h);
glEnable(GL_TEXTURE_2D);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
@ -811,12 +846,12 @@ void GLWidget::SetViewport()
template <typename T>
bool GLEmberController<T>::SizesMatch()
{
//auto scaledW = std::ceil(m_GL->width() * m_GL->devicePixelRatioF());
//auto scaledH = std::ceil(m_GL->height() * m_GL->devicePixelRatioF());
auto ember = m_FractoriumEmberController->CurrentEmber();
return (ember &&
ember->m_FinalRasW == m_GL->width() &&
ember->m_FinalRasH == m_GL->height() &&
m_GL->width() == m_GL->m_TexWidth &&
m_GL->height() == m_GL->m_TexHeight &&
ember->m_FinalRasW == m_GL->m_TexWidth &&
ember->m_FinalRasH == m_GL->m_TexHeight &&
m_GL->m_TexWidth == m_GL->m_ViewWidth &&
m_GL->m_TexHeight == m_GL->m_ViewHeight);
}
@ -826,7 +861,7 @@ bool GLEmberController<T>::SizesMatch()
/// </summary>
void GLWidget::DrawUnitSquare()
{
glLineWidth(1.0f);
glLineWidth(1.0f * devicePixelRatioF());
glBegin(GL_LINES);
glColor4f(1.0f, 1.0f, 1.0f, 0.25f);
glVertex2f(-1, -1);
@ -868,7 +903,7 @@ void GLEmberController<T>::DrawGrid()
m_GL->glPushMatrix();
m_GL->glLoadIdentity();
MultMatrix(mat);
m_GL->glLineWidth(1.0f);
m_GL->glLineWidth(1.0f * m_GL->devicePixelRatioF());
m_GL->glBegin(GL_LINES);
m_GL->glColor4f(0.5f, 0.5f, 0.5f, alpha);
@ -924,24 +959,24 @@ void GLEmberController<T>::DrawAffine(Xform<T>* xform, bool pre, bool selected)
m_GL->glPushMatrix();
m_GL->glLoadIdentity();
MultMatrix(mat);
m_GL->glLineWidth(3.0f);//One 3px wide, colored black, except green on x axis for post affine.
m_GL->glLineWidth(3.0f * m_GL->devicePixelRatioF());//One 3px wide, colored black, except green on x axis for post affine.
m_GL->DrawAffineHelper(index, selected, pre, final, true);
m_GL->glLineWidth(1.0f);//Again 1px wide, colored white, to give a white middle with black outline effect.
m_GL->glLineWidth(1.0f * m_GL->devicePixelRatioF());//Again 1px wide, colored white, to give a white middle with black outline effect.
m_GL->DrawAffineHelper(index, selected, pre, final, false);
m_GL->glPointSize(5.0f);//Three black points, one in the center and two on the circle. Drawn big 5px first to give a black outline.
m_GL->glPointSize(5.0f * m_GL->devicePixelRatioF());//Three black points, one in the center and two on the circle. Drawn big 5px first to give a black outline.
m_GL->glBegin(GL_POINTS);
m_GL->glColor4f(0.0f, 0.0f, 0.0f, selected ? 1.0f : 0.5f);
m_GL->glVertex2f(0.0f, 0.0f);
m_GL->glVertex2f(1.0f, 0.0f);
m_GL->glVertex2f(0.0f, 1.0f);
m_GL->glEnd();
m_GL->glLineWidth(2.0f);//Draw lines again for y axis only, without drawing the circle, using the color of the selected xform.
m_GL->glLineWidth(2.0f * m_GL->devicePixelRatioF());//Draw lines again for y axis only, without drawing the circle, using the color of the selected xform.
m_GL->glBegin(GL_LINES);
m_GL->glColor4f(color.r, color.g, color.b, 1.0f);
m_GL->glVertex2f(0.0f, 0.0f);
m_GL->glVertex2f(0.0f, 1.0f);
m_GL->glEnd();
m_GL->glPointSize(3.0f);//Draw smaller white points, to give a black outline effect.
m_GL->glPointSize(3.0f * m_GL->devicePixelRatioF());//Draw smaller white points, to give a black outline effect.
m_GL->glBegin(GL_POINTS);
m_GL->glColor4f(1.0f, 1.0f, 1.0f, selected ? 1.0f : 0.5f);
m_GL->glVertex2f(0.0f, 0.0f);

View File

@ -11,6 +11,7 @@
int main(int argc, char* argv[])
{
int rv = -1;
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication a(argc, argv);
#ifdef TEST_CL
QMessageBox::critical(QApplication::desktop(), "Error", "Fractorium cannot be run in test mode, undefine TEST_CL first.");

View File

@ -46,6 +46,7 @@ FractoriumOptionsDialog::FractoriumOptionsDialog(QWidget* p, Qt::WindowFlags f)
ui.OpenCLCheckBox->setChecked(false);
ui.OpenCLCheckBox->setEnabled(false);
ui.OpenCLSubBatchSpin->setEnabled(false);
ui.OpenCLQualitySpin->setEnabled(false);
ui.OpenCLFilteringDERadioButton->setEnabled(false);
ui.OpenCLFilteringLogRadioButton->setEnabled(false);
ui.InteraciveGpuFilteringGroupBox->setEnabled(false);
@ -69,8 +70,11 @@ bool FractoriumOptionsDialog::ShowAllXforms() { return ui.ShowAllXformsCheckBox-
bool FractoriumOptionsDialog::ToggleType() { return ui.ToggleTypeCheckBox->isChecked(); }
bool FractoriumOptionsDialog::Png16Bit() { return ui.Png16BitCheckBox->isChecked(); }
bool FractoriumOptionsDialog::AutoUnique() { return ui.AutoUniqueCheckBox->isChecked(); }
bool FractoriumOptionsDialog::LoadLast() { return ui.LoadLastOnStartCheckBox->isChecked(); }
uint FractoriumOptionsDialog::ThreadCount() { return ui.ThreadCountSpin->value(); }
uint FractoriumOptionsDialog::RandomCount() { return ui.RandomCountSpin->value(); }
uint FractoriumOptionsDialog::CpuQuality() { return ui.CpuQualitySpin->value(); }
uint FractoriumOptionsDialog::OpenClQuality() { return ui.OpenCLQualitySpin->value(); }
/// <summary>
/// The check state of one of the OpenCL devices was changed.
@ -122,6 +126,8 @@ void FractoriumOptionsDialog::OnOpenCLCheckBoxStateChanged(int state)
ui.ThreadCountSpin->setEnabled(!checked);
ui.CpuSubBatchSpin->setEnabled(!checked);
ui.OpenCLSubBatchSpin->setEnabled(checked);
ui.OpenCLQualitySpin->setEnabled(checked);
ui.CpuQualitySpin->setEnabled(!checked);
ui.CpuFilteringDERadioButton->setEnabled(!checked);
ui.CpuFilteringLogRadioButton->setEnabled(!checked);
ui.OpenCLFilteringDERadioButton->setEnabled(checked);
@ -178,6 +184,9 @@ void FractoriumOptionsDialog::GuiToData()
m_Settings->Png16Bit(Png16Bit());
m_Settings->ThreadCount(ThreadCount());
m_Settings->RandomCount(RandomCount());
m_Settings->LoadLast(LoadLast());
m_Settings->CpuQuality(CpuQuality());
m_Settings->OpenClQuality(OpenClQuality());
m_Settings->CpuSubBatch(ui.CpuSubBatchSpin->value());
m_Settings->OpenCLSubBatch(ui.OpenCLSubBatchSpin->value());
m_Settings->CpuDEFilter(ui.CpuFilteringDERadioButton->isChecked());
@ -212,6 +221,9 @@ void FractoriumOptionsDialog::DataToGui()
ui.Png16BitCheckBox->setChecked(m_Settings->Png16Bit());
ui.ThreadCountSpin->setValue(m_Settings->ThreadCount());
ui.RandomCountSpin->setValue(m_Settings->RandomCount());
ui.LoadLastOnStartCheckBox->setChecked(m_Settings->LoadLast());
ui.CpuQualitySpin->setValue(m_Settings->CpuQuality());
ui.OpenCLQualitySpin->setValue(m_Settings->OpenClQuality());
ui.CpuSubBatchSpin->setValue(m_Settings->CpuSubBatch());
ui.OpenCLSubBatchSpin->setValue(m_Settings->OpenCLSubBatch());
SettingsToDeviceTable(ui.DeviceTable, devices);

View File

@ -47,8 +47,11 @@ private:
bool ToggleType();
bool Png16Bit();
bool AutoUnique();
bool LoadLast();
uint ThreadCount();
uint RandomCount();
uint CpuQuality();
uint OpenClQuality();
void DataToGui();
void GuiToData();

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>427</width>
<height>487</height>
<height>475</height>
</rect>
</property>
<property name="sizePolicy">
@ -169,6 +169,26 @@
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="ToggleTypeCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Checked: right clicking toggles spin boxes, right button dragging disabled.&lt;/p&gt;&lt;p&gt;Unchecked: double clicking toggles spin boxes, right button dragging enabled.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Right Click Toggles Spinboxes</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="Png16BitCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Save each RGBA component as 16-bits when saving Png files.&lt;/p&gt;&lt;p&gt;This leads to greater color precision for use in high end rendering and display on HDR monitors, however it makes the file size larger.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Save 16-bit Png</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="QTableWidget" name="DeviceTable">
<property name="sizePolicy">
@ -257,180 +277,212 @@
</column>
</widget>
</item>
<item row="8" column="0">
<widget class="QSpinBox" name="ThreadCountSpin">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The number of threads to use with CPU rendering.&lt;/p&gt;&lt;p&gt;Decrease for more responsive editing, increase for better performance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="prefix">
<string>Threads </string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>64</number>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QSpinBox" name="CpuSubBatchSpin">
<property name="toolTip">
<string>The number of 10,000 iteration chunks ran per thread on the CPU
<item row="15" column="0" colspan="2">
<layout class="QGridLayout" name="InteractiveRenderingTabGridLayout">
<item row="3" column="1">
<widget class="QGroupBox" name="InteraciveGpuFilteringGroupBox">
<property name="title">
<string>OpenCL Filtering</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QRadioButton" name="OpenCLFilteringLogRadioButton">
<property name="toolTip">
<string>Use log scale filtering for interactive editing using OpenCL.</string>
</property>
<property name="text">
<string>Log Scale</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="OpenCLFilteringDERadioButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use full density estimation filtering for interactive editing using OpenCL.&lt;/p&gt;&lt;p&gt;This is slower, but gives better feedback.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Full DE</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<widget class="QGroupBox" name="InteraciveCpuFilteringGroupBox">
<property name="title">
<string>CPU Filtering</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QRadioButton" name="CpuFilteringLogRadioButton">
<property name="toolTip">
<string>Use log scale filtering for interactive editing on the CPU.</string>
</property>
<property name="text">
<string>Log Scale</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="CpuFilteringDERadioButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use full density estimation filtering for interactive editing on the CPU.&lt;/p&gt;&lt;p&gt;This is slower, but gives better feedback.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Full DE</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QSpinBox" name="OpenCLSubBatchSpin">
<property name="toolTip">
<string>The number of ~8M iteration chunks ran using OpenCL
in interactive mode for each mouse movement</string>
</property>
<property name="prefix">
<string>CPU Sub Batch </string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100</number>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QSpinBox" name="OpenCLSubBatchSpin">
<property name="toolTip">
<string>The number of ~8M iteration chunks ran using OpenCL
</property>
<property name="prefix">
<string>OpenCL Sub Batch </string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100</number>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="OpenCLQualitySpin">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The default quality to use for the interactive renderer when using OpenCL.&lt;/p&gt;&lt;p&gt;30 is a good number for this, but you can use a larger value if you have a faster GPU.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="prefix">
<string>OpenCL Quality </string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>99999</number>
</property>
<property name="value">
<number>30</number>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="CpuQualitySpin">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The default quality to use for the interactive renderer when using the CPU.&lt;/p&gt;&lt;p&gt;10 is a good number for this, but you can use a larger value if you have a faster system.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="prefix">
<string>CPU Quality </string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>99999</number>
</property>
<property name="value">
<number>10</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="CpuSubBatchSpin">
<property name="toolTip">
<string>The number of 10,000 iteration chunks ran per thread on the CPU
in interactive mode for each mouse movement</string>
</property>
<property name="prefix">
<string>OpenCL Sub Batch </string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100</number>
</property>
</widget>
</property>
<property name="prefix">
<string>CPU Sub Batch </string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QSpinBox" name="RandomCountSpin">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The number of random flames to generate on startup.&lt;/p&gt;&lt;p&gt;These are usually of low quality, so leave the count as 1 unless you are searching for good randoms.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="prefix">
<string>Randoms </string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>99999</number>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QSpinBox" name="ThreadCountSpin">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The number of threads to use with CPU rendering.&lt;/p&gt;&lt;p&gt;Decrease for more responsive editing, increase for better performance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="prefix">
<string>Threads </string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>64</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="11" column="0">
<widget class="QGroupBox" name="InteraciveCpuFilteringGroupBox">
<property name="title">
<string>CPU Filtering</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QRadioButton" name="CpuFilteringLogRadioButton">
<property name="toolTip">
<string>Use log scale filtering for interactive editing on the CPU.</string>
</property>
<property name="text">
<string>Log Scale</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="CpuFilteringDERadioButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use full density estimation filtering for interactive editing on the CPU.&lt;/p&gt;&lt;p&gt;This is slower, but gives better feedback.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Full DE</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="12" column="0">
<widget class="QGroupBox" name="InteraciveGpuFilteringGroupBox">
<property name="title">
<string>OpenCL Filtering</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>6</number>
</property>
<property name="topMargin">
<number>3</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>5</number>
</property>
<item>
<widget class="QRadioButton" name="OpenCLFilteringLogRadioButton">
<property name="toolTip">
<string>Use log scale filtering for interactive editing using OpenCL.</string>
</property>
<property name="text">
<string>Log Scale</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="OpenCLFilteringDERadioButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use full density estimation filtering for interactive editing using OpenCL.&lt;/p&gt;&lt;p&gt;This is slower, but gives better feedback.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Full DE</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="7" column="0">
<widget class="QSpinBox" name="RandomCountSpin">
<item row="4" column="1">
<widget class="QCheckBox" name="LoadLastOnStartCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The number of random flames to generate on startup.&lt;/p&gt;&lt;p&gt;These are usually of low quality, so leave the count as 1 unless you are searching for good randoms.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="prefix">
<string>Randoms </string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>99999</number>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="ToggleTypeCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Checked: right clicking toggles spin boxes, right button dragging disabled.&lt;/p&gt;&lt;p&gt;Unchecked: double clicking toggles spin boxes, right button dragging enabled.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Checked: load the flame from the previous run on startup.&lt;/p&gt;&lt;p&gt;Unchecked: create randoms on startup.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Right Click Toggles Spinboxes</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="Png16BitCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Save each RGBA component as 16-bits when saving Png files.&lt;/p&gt;&lt;p&gt;This leads to greater color precision for use in high end rendering and display on HDR monitors, however it makes the file size larger.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Save 16-bit Png</string>
<string>Load Last on Start</string>
</property>
</widget>
</item>
@ -858,10 +910,13 @@ in interactive mode for each mouse movement</string>
<tabstop>ContinuousUpdateCheckBox</tabstop>
<tabstop>ToggleTypeCheckBox</tabstop>
<tabstop>Png16BitCheckBox</tabstop>
<tabstop>RandomCountSpin</tabstop>
<tabstop>LoadLastOnStartCheckBox</tabstop>
<tabstop>ThreadCountSpin</tabstop>
<tabstop>CpuSubBatchSpin</tabstop>
<tabstop>RandomCountSpin</tabstop>
<tabstop>OpenCLQualitySpin</tabstop>
<tabstop>OpenCLSubBatchSpin</tabstop>
<tabstop>CpuQualitySpin</tabstop>
<tabstop>CpuFilteringLogRadioButton</tabstop>
<tabstop>CpuFilteringDERadioButton</tabstop>
<tabstop>OpenCLFilteringLogRadioButton</tabstop>