2014-07-08 03:11:14 -04:00
|
|
|
#include "FractoriumPch.h"
|
|
|
|
#include "Fractorium.h"
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Initialize the menus UI.
|
|
|
|
/// </summary>
|
|
|
|
void Fractorium::InitMenusUI()
|
|
|
|
{
|
|
|
|
//File menu.
|
|
|
|
connect(ui.ActionNewFlock, SIGNAL(triggered(bool)), this, SLOT(OnActionNewFlock(bool)), Qt::QueuedConnection);
|
|
|
|
connect(ui.ActionNewEmptyFlameInCurrentFile, SIGNAL(triggered(bool)), this, SLOT(OnActionNewEmptyFlameInCurrentFile(bool)), Qt::QueuedConnection);
|
|
|
|
connect(ui.ActionNewRandomFlameInCurrentFile, SIGNAL(triggered(bool)), this, SLOT(OnActionNewRandomFlameInCurrentFile(bool)), Qt::QueuedConnection);
|
|
|
|
connect(ui.ActionCopyFlameInCurrentFile, SIGNAL(triggered(bool)), this, SLOT(OnActionCopyFlameInCurrentFile(bool)), Qt::QueuedConnection);
|
|
|
|
connect(ui.ActionOpen, SIGNAL(triggered(bool)), this, SLOT(OnActionOpen(bool)), Qt::QueuedConnection);
|
|
|
|
connect(ui.ActionSaveCurrentAsXml, SIGNAL(triggered(bool)), this, SLOT(OnActionSaveCurrentAsXml(bool)), Qt::QueuedConnection);
|
|
|
|
connect(ui.ActionSaveEntireFileAsXml, SIGNAL(triggered(bool)), this, SLOT(OnActionSaveEntireFileAsXml(bool)), Qt::QueuedConnection);
|
|
|
|
connect(ui.ActionSaveCurrentToOpenedFile, SIGNAL(triggered(bool)), this, SLOT(OnActionSaveCurrentToOpenedFile(bool)), Qt::QueuedConnection);
|
|
|
|
connect(ui.ActionSaveCurrentScreen, SIGNAL(triggered(bool)), this, SLOT(OnActionSaveCurrentScreen(bool)), Qt::QueuedConnection);
|
|
|
|
connect(ui.ActionExit, SIGNAL(triggered(bool)), this, SLOT(OnActionExit(bool)), Qt::QueuedConnection);
|
|
|
|
|
|
|
|
//Edit menu.
|
|
|
|
connect(ui.ActionUndo, SIGNAL(triggered(bool)), this, SLOT(OnActionUndo(bool)), Qt::QueuedConnection);
|
|
|
|
connect(ui.ActionRedo, SIGNAL(triggered(bool)), this, SLOT(OnActionRedo(bool)), Qt::QueuedConnection);
|
|
|
|
connect(ui.ActionCopyXml, SIGNAL(triggered(bool)), this, SLOT(OnActionCopyXml(bool)), Qt::QueuedConnection);
|
|
|
|
connect(ui.ActionCopyAllXml, SIGNAL(triggered(bool)), this, SLOT(OnActionCopyAllXml(bool)), Qt::QueuedConnection);
|
|
|
|
connect(ui.ActionPasteXmlAppend, SIGNAL(triggered(bool)), this, SLOT(OnActionPasteXmlAppend(bool)), Qt::QueuedConnection);
|
|
|
|
connect(ui.ActionPasteXmlOver, SIGNAL(triggered(bool)), this, SLOT(OnActionPasteXmlOver(bool)), Qt::QueuedConnection);
|
2015-05-19 22:31:33 -04:00
|
|
|
connect(ui.ActionCopySelectedXforms, SIGNAL(triggered(bool)), this, SLOT(OnActionCopySelectedXforms(bool)), Qt::QueuedConnection);
|
|
|
|
connect(ui.ActionPasteSelectedXforms, SIGNAL(triggered(bool)), this, SLOT(OnActionPasteSelectedXforms(bool)), Qt::QueuedConnection);
|
|
|
|
ui.ActionPasteSelectedXforms->setEnabled(false);
|
2014-07-08 03:11:14 -04:00
|
|
|
|
2015-06-20 14:35:08 -04:00
|
|
|
//View menu.
|
|
|
|
connect(ui.ActionResetWorkspace, SIGNAL(triggered(bool)), this, SLOT(OnActionResetWorkspace(bool)), Qt::QueuedConnection);
|
|
|
|
|
2014-07-08 03:11:14 -04:00
|
|
|
//Tools menu.
|
|
|
|
connect(ui.ActionAddReflectiveSymmetry, SIGNAL(triggered(bool)), this, SLOT(OnActionAddReflectiveSymmetry(bool)), Qt::QueuedConnection);
|
|
|
|
connect(ui.ActionAddRotationalSymmetry, SIGNAL(triggered(bool)), this, SLOT(OnActionAddRotationalSymmetry(bool)), Qt::QueuedConnection);
|
|
|
|
connect(ui.ActionAddBothSymmetry, SIGNAL(triggered(bool)), this, SLOT(OnActionAddBothSymmetry(bool)), Qt::QueuedConnection);
|
|
|
|
connect(ui.ActionClearFlame, SIGNAL(triggered(bool)), this, SLOT(OnActionClearFlame(bool)), Qt::QueuedConnection);
|
Numerous fixes
0.4.0.5 Beta 07/18/2014
--User Changes
Allow for vibrancy values > 1.
Add flatten and unflatten menu items.
Automatically flatten like Apophysis does.
Add plugin and new_linear tags to Xml to be compatible with Apophysis.
--Bug Fixes
Fix blur, blur3d, bubble, cropn, cross, curl, curl3d, epispiral, ho,
julia3d, julia3dz, loonie, mirror_x, mirror_y, mirror_z, rotate_x,
sinusoidal, spherical, spherical3d, stripes.
Unique filename on final render was completely broken.
Two severe OpenCL bugs. Random seeds were biased and fusing was being
reset too often leading to results that differ from the CPU.
Subtle, but sometimes severe bug in the setup of the xaos weights.
Use properly defined epsilon by getting the value from
std::numeric_limits, rather than hard coding 1e-6 or 1e-10.
Omit incorrect usage of epsilon everywhere. It should not be
automatically added to denominators. Rather, it should only be used if
the denominator is zero.
Force final render progress bars to 100 on completion. Sometimes they
didn't seem to make it there.
Make variation name and params comparisons be case insensitive.
--Code Changes
Make ForEach and FindIf wrappers around std::for_each and std::find_if.
2014-07-19 02:33:18 -04:00
|
|
|
connect(ui.ActionFlatten, SIGNAL(triggered(bool)), this, SLOT(OnActionFlatten(bool)), Qt::QueuedConnection);
|
|
|
|
connect(ui.ActionUnflatten, SIGNAL(triggered(bool)), this, SLOT(OnActionUnflatten(bool)), Qt::QueuedConnection);
|
2014-07-08 03:11:14 -04:00
|
|
|
connect(ui.ActionStopRenderingPreviews, SIGNAL(triggered(bool)), this, SLOT(OnActionStopRenderingPreviews(bool)), Qt::QueuedConnection);
|
|
|
|
connect(ui.ActionRenderPreviews, SIGNAL(triggered(bool)), this, SLOT(OnActionRenderPreviews(bool)), Qt::QueuedConnection);
|
|
|
|
connect(ui.ActionFinalRender, SIGNAL(triggered(bool)), this, SLOT(OnActionFinalRender(bool)), Qt::QueuedConnection);
|
|
|
|
connect(m_FinalRenderDialog, SIGNAL(finished(int)), this, SLOT(OnFinalRenderClose(int)), Qt::QueuedConnection);
|
|
|
|
connect(ui.ActionOptions, SIGNAL(triggered(bool)), this, SLOT(OnActionOptions(bool)), Qt::QueuedConnection);
|
|
|
|
|
|
|
|
//Help menu.
|
|
|
|
connect(ui.ActionAbout, SIGNAL(triggered(bool)), this, SLOT(OnActionAbout(bool)), Qt::QueuedConnection);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Create a new flock of random embers, with the specified length.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="count">The number of embers to include in the flock</param>
|
|
|
|
template <typename T>
|
2014-12-06 00:05:09 -05:00
|
|
|
void FractoriumEmberController<T>::NewFlock(uint count)
|
2014-07-08 03:11:14 -04:00
|
|
|
{
|
|
|
|
Ember<T> ember;
|
|
|
|
|
|
|
|
StopPreviewRender();
|
|
|
|
m_EmberFile.Clear();
|
|
|
|
m_EmberFile.m_Embers.reserve(count);
|
|
|
|
m_EmberFile.m_Filename = EmberFile<T>::DefaultFilename();
|
|
|
|
|
2014-12-06 00:05:09 -05:00
|
|
|
for (uint i = 0; i < count; i++)
|
2014-07-08 03:11:14 -04:00
|
|
|
{
|
|
|
|
m_SheepTools->Random(ember);
|
|
|
|
ParamsToEmber(ember);
|
2014-10-14 11:53:15 -04:00
|
|
|
ember.m_Index = i;
|
|
|
|
ember.m_Name = m_EmberFile.m_Filename.toStdString() + "-" + ToString(i + 1).toStdString();
|
2014-07-08 03:11:14 -04:00
|
|
|
m_EmberFile.m_Embers.push_back(ember);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_LastSaveAll = "";
|
|
|
|
FillLibraryTree();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Create a new flock and assign the first ember as the current one.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="checked">Ignored</param>
|
|
|
|
void Fractorium::OnActionNewFlock(bool checked)
|
|
|
|
{
|
|
|
|
m_Controller->NewFlock(10);
|
|
|
|
m_Controller->SetEmber(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Create and add a new empty ember in the currently opened file
|
|
|
|
/// and set it as the current one.
|
|
|
|
/// It will have one empty xform in it.
|
|
|
|
/// Resets the rendering process.
|
|
|
|
/// </summary>
|
|
|
|
template <typename T>
|
|
|
|
void FractoriumEmberController<T>::NewEmptyFlameInCurrentFile()
|
|
|
|
{
|
|
|
|
Ember<T> ember;
|
|
|
|
Xform<T> xform;
|
|
|
|
QDateTime local(QDateTime::currentDateTime());
|
|
|
|
|
|
|
|
StopPreviewRender();
|
|
|
|
ParamsToEmber(ember);
|
|
|
|
xform.m_Weight = T(0.25);
|
|
|
|
xform.m_ColorX = m_Rand.Frand01<T>();
|
|
|
|
ember.AddXform(xform);
|
2015-04-08 21:23:29 -04:00
|
|
|
ember.m_Palette = *m_PaletteList.GetRandomPalette();
|
2014-10-14 11:53:15 -04:00
|
|
|
ember.m_Name = EmberFile<T>::DefaultEmberName(m_EmberFile.Size() + 1).toStdString();
|
|
|
|
ember.m_Index = m_EmberFile.Size();
|
2014-07-08 03:11:14 -04:00
|
|
|
m_EmberFile.m_Embers.push_back(ember);//Will invalidate the pointers contained in the EmberTreeWidgetItems, UpdateLibraryTree() will resync.
|
|
|
|
m_EmberFile.MakeNamesUnique();
|
|
|
|
UpdateLibraryTree();
|
2014-10-14 11:53:15 -04:00
|
|
|
SetEmber(m_EmberFile.Size() - 1);
|
2014-07-08 03:11:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Fractorium::OnActionNewEmptyFlameInCurrentFile(bool checked) { m_Controller->NewEmptyFlameInCurrentFile(); }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Create and add a new random ember in the currently opened file
|
|
|
|
/// and set it as the current one.
|
|
|
|
/// Resets the rendering process.
|
|
|
|
/// </summary>
|
|
|
|
template <typename T>
|
|
|
|
void FractoriumEmberController<T>::NewRandomFlameInCurrentFile()
|
|
|
|
{
|
|
|
|
Ember<T> ember;
|
|
|
|
|
|
|
|
StopPreviewRender();
|
|
|
|
m_SheepTools->Random(ember);
|
|
|
|
ParamsToEmber(ember);
|
2014-10-14 11:53:15 -04:00
|
|
|
ember.m_Name = EmberFile<T>::DefaultEmberName(m_EmberFile.Size() + 1).toStdString();
|
|
|
|
ember.m_Index = m_EmberFile.Size();
|
2014-07-08 03:11:14 -04:00
|
|
|
m_EmberFile.m_Embers.push_back(ember);//Will invalidate the pointers contained in the EmberTreeWidgetItems, UpdateLibraryTree() will resync.
|
|
|
|
m_EmberFile.MakeNamesUnique();
|
|
|
|
UpdateLibraryTree();
|
2014-10-14 11:53:15 -04:00
|
|
|
SetEmber(m_EmberFile.Size() - 1);
|
2014-07-08 03:11:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Fractorium::OnActionNewRandomFlameInCurrentFile(bool checked) { m_Controller->NewRandomFlameInCurrentFile(); }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Create and add a a copy of the current ember in the currently opened file
|
|
|
|
/// and set it as the current one.
|
|
|
|
/// Resets the rendering process.
|
|
|
|
/// </summary>
|
|
|
|
template <typename T>
|
|
|
|
void FractoriumEmberController<T>::CopyFlameInCurrentFile()
|
|
|
|
{
|
|
|
|
Ember<T> ember = m_Ember;
|
|
|
|
|
|
|
|
StopPreviewRender();
|
2014-10-14 11:53:15 -04:00
|
|
|
ember.m_Name = EmberFile<T>::DefaultEmberName(m_EmberFile.Size() + 1).toStdString();
|
|
|
|
ember.m_Index = m_EmberFile.Size();
|
2014-07-08 03:11:14 -04:00
|
|
|
m_EmberFile.m_Embers.push_back(ember);//Will invalidate the pointers contained in the EmberTreeWidgetItems, UpdateLibraryTree() will resync.
|
|
|
|
m_EmberFile.MakeNamesUnique();
|
|
|
|
UpdateLibraryTree();
|
2014-10-14 11:53:15 -04:00
|
|
|
SetEmber(m_EmberFile.Size() - 1);
|
2014-07-08 03:11:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Fractorium::OnActionCopyFlameInCurrentFile(bool checked) { m_Controller->CopyFlameInCurrentFile(); }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Open a list of ember Xml files, apply various values from the GUI widgets.
|
|
|
|
/// Either append these newly read embers to the existing open embers,
|
|
|
|
/// or clear the current ember file first.
|
|
|
|
/// When appending, add the new embers the the end of library tree.
|
|
|
|
/// When not appending, clear and populate the library tree with the new embers.
|
|
|
|
/// Set the current ember to the first one in the newly opened list.
|
|
|
|
/// Clears the undo state.
|
|
|
|
/// Resets the rendering process.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="filenames">A list of full paths and filenames</param>
|
|
|
|
/// <param name="append">True to append the embers in the new files to the end of the currently open embers, false to clear and replace them</param>
|
|
|
|
template <typename T>
|
2014-10-14 11:53:15 -04:00
|
|
|
void FractoriumEmberController<T>::OpenAndPrepFiles(const QStringList& filenames, bool append)
|
2014-07-08 03:11:14 -04:00
|
|
|
{
|
|
|
|
if (!filenames.empty())
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
EmberFile<T> emberFile;
|
|
|
|
XmlToEmber<T> parser;
|
|
|
|
vector<Ember<T>> embers;
|
2014-12-06 00:05:09 -05:00
|
|
|
uint previousSize = append ? m_EmberFile.Size() : 0;
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
|
|
StopPreviewRender();
|
|
|
|
emberFile.m_Filename = filenames[0];
|
|
|
|
|
2014-10-14 11:53:15 -04:00
|
|
|
foreach(const QString& filename, filenames)
|
2014-07-08 03:11:14 -04:00
|
|
|
{
|
|
|
|
embers.clear();
|
|
|
|
|
|
|
|
if (parser.Parse(filename.toStdString().c_str(), embers) && !embers.empty())
|
|
|
|
{
|
|
|
|
for (i = 0; i < embers.size(); i++)
|
|
|
|
{
|
2014-10-14 11:53:15 -04:00
|
|
|
ConstrainDimensions(embers[i]);//Do not exceed the max texture size.
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
|
|
//Also ensure it has a name.
|
|
|
|
if (embers[i].m_Name == "" || embers[i].m_Name == "No name")
|
2014-12-11 00:50:15 -05:00
|
|
|
embers[i].m_Name = ToString<qulonglong>(i).toStdString();
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
|
|
embers[i].m_Quality = m_Fractorium->m_QualitySpin->value();
|
|
|
|
embers[i].m_Supersample = m_Fractorium->m_SupersampleSpin->value();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_LastSaveAll = "";
|
|
|
|
emberFile.m_Embers.insert(emberFile.m_Embers.end(), embers.begin(), embers.end());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
vector<string> errors = parser.ErrorReport();
|
|
|
|
|
|
|
|
m_Fractorium->ErrorReportToQTextEdit(errors, m_Fractorium->ui.InfoFileOpeningTextEdit);
|
2014-10-14 11:53:15 -04:00
|
|
|
m_Fractorium->ShowCritical("Open Failed", "Could not open file, see info tab for details.");
|
2014-07-08 03:11:14 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (append)
|
|
|
|
{
|
|
|
|
if (m_EmberFile.m_Filename == "")
|
|
|
|
m_EmberFile.m_Filename = filenames[0];
|
|
|
|
|
|
|
|
m_EmberFile.m_Embers.insert(m_EmberFile.m_Embers.end(), emberFile.m_Embers.begin(), emberFile.m_Embers.end());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_EmberFile = emberFile;
|
|
|
|
|
|
|
|
//Resync indices and names.
|
2014-10-14 11:53:15 -04:00
|
|
|
for (i = 0; i < m_EmberFile.Size(); i++)
|
2014-07-08 03:11:14 -04:00
|
|
|
m_EmberFile.m_Embers[i].m_Index = i;
|
|
|
|
|
|
|
|
m_EmberFile.MakeNamesUnique();
|
|
|
|
|
|
|
|
if (append)
|
|
|
|
UpdateLibraryTree();
|
|
|
|
else
|
|
|
|
FillLibraryTree(append ? previousSize - 1 : 0);
|
|
|
|
|
|
|
|
ClearUndo();
|
|
|
|
SetEmber(previousSize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Show a file open dialog to open ember Xml files.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="checked">Ignored</param>
|
|
|
|
void Fractorium::OnActionOpen(bool checked) { m_Controller->OpenAndPrepFiles(SetupOpenXmlDialog(), false); }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Save current ember as Xml, using the Xml saving template values from the options.
|
|
|
|
/// This will first save the current ember back to the opened file in memory before
|
|
|
|
/// saving it to disk.
|
|
|
|
/// </summary>
|
|
|
|
template <typename T>
|
|
|
|
void FractoriumEmberController<T>::SaveCurrentAsXml()
|
|
|
|
{
|
|
|
|
QString filename;
|
|
|
|
FractoriumSettings* s = m_Fractorium->m_Settings;
|
|
|
|
|
2015-03-21 18:27:37 -04:00
|
|
|
if (s->SaveAutoUnique() && m_LastSaveCurrent != "")
|
|
|
|
{
|
|
|
|
filename = EmberFile<T>::UniqueFilename(m_LastSaveCurrent);
|
|
|
|
}
|
|
|
|
else if (QFile::exists(m_LastSaveCurrent))
|
2014-07-08 03:11:14 -04:00
|
|
|
{
|
|
|
|
filename = m_LastSaveCurrent;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-10-14 11:53:15 -04:00
|
|
|
if (m_EmberFile.Size() == 1)
|
2014-07-08 03:11:14 -04:00
|
|
|
filename = m_Fractorium->SetupSaveXmlDialog(m_EmberFile.m_Filename);//If only one ember present, just use parent filename.
|
|
|
|
else
|
|
|
|
filename = m_Fractorium->SetupSaveXmlDialog(QString::fromStdString(m_Ember.m_Name));//More than one ember present, use individual ember name.
|
|
|
|
}
|
|
|
|
|
|
|
|
if (filename != "")
|
|
|
|
{
|
|
|
|
Ember<T> ember = m_Ember;
|
|
|
|
EmberToXml<T> writer;
|
|
|
|
QFileInfo fileInfo(filename);
|
|
|
|
xmlDocPtr tempEdit = ember.m_Edits;
|
|
|
|
|
|
|
|
SaveCurrentToOpenedFile();//Save the current ember back to the opened file before writing to disk.
|
|
|
|
ApplyXmlSavingTemplate(ember);
|
2015-01-02 18:11:36 -05:00
|
|
|
ember.m_Edits = writer.CreateNewEditdoc(&ember, nullptr, "edit", s->Nick().toStdString(), s->Url().toStdString(), s->Id().toStdString(), "", 0, 0);
|
2014-07-08 03:11:14 -04:00
|
|
|
|
2015-01-02 18:11:36 -05:00
|
|
|
if (tempEdit != nullptr)
|
2014-07-08 03:11:14 -04:00
|
|
|
xmlFreeDoc(tempEdit);
|
|
|
|
|
|
|
|
if (writer.Save(filename.toStdString().c_str(), ember, 0, true, false, true))
|
|
|
|
{
|
|
|
|
s->SaveFolder(fileInfo.canonicalPath());
|
2015-03-21 18:27:37 -04:00
|
|
|
|
|
|
|
if (!s->SaveAutoUnique() || m_LastSaveCurrent == "")//Only save filename on first time through when doing auto unique names.
|
|
|
|
m_LastSaveCurrent = filename;
|
2014-07-08 03:11:14 -04:00
|
|
|
}
|
|
|
|
else
|
2014-10-14 11:53:15 -04:00
|
|
|
m_Fractorium->ShowCritical("Save Failed", "Could not save file, try saving to a different folder.");
|
2014-07-08 03:11:14 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fractorium::OnActionSaveCurrentAsXml(bool checked) { m_Controller->SaveCurrentAsXml(); }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Save entire opened file Xml, using the Xml saving template values from the options on each ember.
|
|
|
|
/// This will first save the current ember back to the opened file in memory before
|
|
|
|
/// saving all to disk.
|
|
|
|
/// </summary>
|
|
|
|
template <typename T>
|
|
|
|
void FractoriumEmberController<T>::SaveEntireFileAsXml()
|
|
|
|
{
|
|
|
|
QString filename;
|
|
|
|
FractoriumSettings* s = m_Fractorium->m_Settings;
|
|
|
|
|
2015-03-21 18:27:37 -04:00
|
|
|
if (s->SaveAutoUnique() && m_LastSaveAll != "")
|
|
|
|
filename = EmberFile<T>::UniqueFilename(m_LastSaveAll);
|
|
|
|
else if (QFile::exists(m_LastSaveAll))
|
2014-07-08 03:11:14 -04:00
|
|
|
filename = m_LastSaveAll;
|
|
|
|
else
|
|
|
|
filename = m_Fractorium->SetupSaveXmlDialog(m_EmberFile.m_Filename);
|
|
|
|
|
|
|
|
if (filename != "")
|
|
|
|
{
|
|
|
|
EmberFile<T> emberFile;
|
|
|
|
EmberToXml<T> writer;
|
|
|
|
QFileInfo fileInfo(filename);
|
|
|
|
|
|
|
|
SaveCurrentToOpenedFile();//Save the current ember back to the opened file before writing to disk.
|
|
|
|
emberFile = m_EmberFile;
|
|
|
|
|
2015-05-03 20:13:14 -04:00
|
|
|
for (auto& ember : emberFile.m_Embers)
|
|
|
|
ApplyXmlSavingTemplate(ember);
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
|
|
if (writer.Save(filename.toStdString().c_str(), emberFile.m_Embers, 0, true, false, true))
|
|
|
|
{
|
2015-03-21 18:27:37 -04:00
|
|
|
if (!s->SaveAutoUnique() || m_LastSaveAll == "")//Only save filename on first time through when doing auto unique names.
|
|
|
|
m_LastSaveAll = filename;
|
|
|
|
|
2014-07-08 03:11:14 -04:00
|
|
|
s->SaveFolder(fileInfo.canonicalPath());
|
|
|
|
}
|
|
|
|
else
|
2014-10-14 11:53:15 -04:00
|
|
|
m_Fractorium->ShowCritical("Save Failed", "Could not save file, try saving to a different folder.");
|
2014-07-08 03:11:14 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fractorium::OnActionSaveEntireFileAsXml(bool checked) { m_Controller->SaveEntireFileAsXml(); }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Show a file save dialog and save what is currently shown in the render window to disk as an image.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="checked">Ignored</param>
|
|
|
|
void Fractorium::OnActionSaveCurrentScreen(bool checked)
|
|
|
|
{
|
2014-10-14 11:53:15 -04:00
|
|
|
QString filename = SetupSaveImageDialog(m_Controller->Name());
|
2014-07-08 03:11:14 -04:00
|
|
|
|
2014-10-14 11:53:15 -04:00
|
|
|
m_Controller->SaveCurrentRender(filename, true);
|
2014-07-08 03:11:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Save the current ember back to its position in the opened file.
|
|
|
|
/// This does not save to disk.
|
|
|
|
/// </summary>
|
|
|
|
template <typename T>
|
|
|
|
void FractoriumEmberController<T>::SaveCurrentToOpenedFile()
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
bool fileFound = false;
|
|
|
|
|
2014-10-14 11:53:15 -04:00
|
|
|
for (i = 0; i < m_EmberFile.Size(); i++)
|
2014-07-08 03:11:14 -04:00
|
|
|
{
|
2014-10-14 11:53:15 -04:00
|
|
|
if ((m_Ember.m_Name == m_EmberFile.m_Embers[i].m_Name) &&//Check both to be extra sure.
|
|
|
|
(m_Ember.m_Index == m_EmberFile.m_Embers[i].m_Index))
|
2014-07-08 03:11:14 -04:00
|
|
|
{
|
|
|
|
m_EmberFile.m_Embers[i] = m_Ember;
|
|
|
|
fileFound = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!fileFound)
|
|
|
|
{
|
|
|
|
StopPreviewRender();
|
|
|
|
m_EmberFile.m_Embers.push_back(m_Ember);
|
|
|
|
m_EmberFile.MakeNamesUnique();
|
|
|
|
UpdateLibraryTree();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
RenderPreviews(i, i + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fractorium::OnActionSaveCurrentToOpenedFile(bool checked) { m_Controller->SaveCurrentToOpenedFile(); }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Exit the application.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="checked">Ignore.</param>
|
|
|
|
void Fractorium::OnActionExit(bool checked)
|
|
|
|
{
|
2015-01-02 18:11:36 -05:00
|
|
|
closeEvent(nullptr);
|
2014-07-08 03:11:14 -04:00
|
|
|
QApplication::exit();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Undoes this instance.
|
|
|
|
/// </summary>
|
|
|
|
template <typename T>
|
|
|
|
void FractoriumEmberController<T>::Undo()
|
|
|
|
{
|
|
|
|
if (m_UndoList.size() > 1 && m_UndoIndex > 0)
|
|
|
|
{
|
|
|
|
int index = m_Ember.GetTotalXformIndex(CurrentXform());
|
|
|
|
|
|
|
|
m_LastEditWasUndoRedo = true;
|
2015-03-21 18:27:37 -04:00
|
|
|
m_UndoIndex = std::max(0u, m_UndoIndex - 1u);
|
2014-07-08 03:11:14 -04:00
|
|
|
SetEmber(m_UndoList[m_UndoIndex], true);
|
|
|
|
m_EditState = UNDO_REDO;
|
|
|
|
|
|
|
|
if (index >= 0)
|
|
|
|
m_Fractorium->CurrentXform(index);
|
|
|
|
|
|
|
|
m_Fractorium->ui.ActionUndo->setEnabled(m_UndoList.size() > 1 && (m_UndoIndex > 0));
|
|
|
|
m_Fractorium->ui.ActionRedo->setEnabled(m_UndoList.size() > 1 && !(m_UndoIndex == m_UndoList.size() - 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fractorium::OnActionUndo(bool checked) { m_Controller->Undo(); }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Redoes this instance.
|
|
|
|
/// </summary>
|
|
|
|
template <typename T>
|
|
|
|
void FractoriumEmberController<T>::Redo()
|
|
|
|
{
|
|
|
|
if (m_UndoList.size() > 1 && m_UndoIndex < m_UndoList.size() - 1)
|
|
|
|
{
|
|
|
|
int index = m_Ember.GetTotalXformIndex(CurrentXform());
|
|
|
|
|
|
|
|
m_LastEditWasUndoRedo = true;
|
2015-03-21 18:27:37 -04:00
|
|
|
m_UndoIndex = std::min<uint>(m_UndoIndex + 1, m_UndoList.size() - 1);
|
2014-07-08 03:11:14 -04:00
|
|
|
SetEmber(m_UndoList[m_UndoIndex], true);
|
|
|
|
m_EditState = UNDO_REDO;
|
|
|
|
|
|
|
|
if (index >= 0)
|
|
|
|
m_Fractorium->CurrentXform(index);
|
|
|
|
|
|
|
|
m_Fractorium->ui.ActionUndo->setEnabled(m_UndoList.size() > 1 && (m_UndoIndex > 0));
|
|
|
|
m_Fractorium->ui.ActionRedo->setEnabled(m_UndoList.size() > 1 && !(m_UndoIndex == m_UndoList.size() - 1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fractorium::OnActionRedo(bool checked) { m_Controller->Redo(); }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Copy the current ember Xml to the clipboard.
|
|
|
|
/// Apply Xml saving settings from the options first.
|
|
|
|
/// </summary>
|
|
|
|
template <typename T>
|
|
|
|
void FractoriumEmberController<T>::CopyXml()
|
|
|
|
{
|
|
|
|
Ember<T> ember = m_Ember;
|
|
|
|
EmberToXml<T> emberToXml;
|
|
|
|
FractoriumSettings* settings = m_Fractorium->m_Settings;
|
|
|
|
|
|
|
|
ember.m_Quality = settings->XmlQuality();
|
|
|
|
ember.m_Supersample = settings->XmlSupersample();
|
|
|
|
ember.m_TemporalSamples = settings->XmlTemporalSamples();
|
|
|
|
QApplication::clipboard()->setText(QString::fromStdString(emberToXml.ToString(ember, "", 0, false, false, true)));
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fractorium::OnActionCopyXml(bool checked) { m_Controller->CopyXml(); }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Copy the Xmls for all open embers as a single string to the clipboard, enclosed with the <flames> tag.
|
|
|
|
/// Apply Xml saving settings from the options first for each ember.
|
|
|
|
/// </summary>
|
|
|
|
template <typename T>
|
|
|
|
void FractoriumEmberController<T>::CopyAllXml()
|
|
|
|
{
|
|
|
|
ostringstream os;
|
|
|
|
EmberToXml<T> emberToXml;
|
|
|
|
FractoriumSettings* settings = m_Fractorium->m_Settings;
|
|
|
|
|
|
|
|
os << "<flames>\n";
|
|
|
|
|
2015-05-03 20:13:14 -04:00
|
|
|
for (auto& e : m_EmberFile.m_Embers)
|
2014-07-08 03:11:14 -04:00
|
|
|
{
|
2015-05-03 20:13:14 -04:00
|
|
|
Ember<T> ember = e;
|
2014-07-08 03:11:14 -04:00
|
|
|
|
2014-10-14 11:53:15 -04:00
|
|
|
ApplyXmlSavingTemplate(ember);
|
2014-07-08 03:11:14 -04:00
|
|
|
os << emberToXml.ToString(ember, "", 0, false, false, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
os << "</flames>\n";
|
|
|
|
QApplication::clipboard()->setText(QString::fromStdString(os.str()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fractorium::OnActionCopyAllXml(bool checked) { m_Controller->CopyAllXml(); }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Convert the Xml text from the clipboard to an ember, add it to the end
|
|
|
|
/// of the current file and set it as the current ember. If multiple Xmls were
|
|
|
|
/// copied to the clipboard and were enclosed in <flames> tags, then all of them will be added.
|
|
|
|
/// Clears the undo state.
|
|
|
|
/// Resets the rendering process.
|
|
|
|
/// </summary>
|
|
|
|
template <typename T>
|
|
|
|
void FractoriumEmberController<T>::PasteXmlAppend()
|
|
|
|
{
|
2014-12-06 00:05:09 -05:00
|
|
|
uint i, previousSize = m_EmberFile.Size();
|
2014-07-08 03:11:14 -04:00
|
|
|
string s, errors;
|
|
|
|
XmlToEmber<T> parser;
|
|
|
|
vector<Ember<T>> embers;
|
|
|
|
QTextCodec* codec = QTextCodec::codecForName("UTF-8");
|
|
|
|
QByteArray b = codec->fromUnicode(QApplication::clipboard()->text());
|
|
|
|
|
|
|
|
s.reserve(b.size());
|
|
|
|
|
|
|
|
for (i = 0; i < b.size(); i++)
|
|
|
|
{
|
2014-12-11 00:50:15 -05:00
|
|
|
if (uint(b[i]) < 128u)
|
2014-07-08 03:11:14 -04:00
|
|
|
s.push_back(b[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
b.clear();
|
|
|
|
StopPreviewRender();
|
2014-12-11 00:50:15 -05:00
|
|
|
parser.Parse(reinterpret_cast<byte*>(const_cast<char*>(s.c_str())), "", embers);
|
2014-07-08 03:11:14 -04:00
|
|
|
errors = parser.ErrorReportString();
|
|
|
|
|
|
|
|
if (errors != "")
|
|
|
|
{
|
2014-10-14 11:53:15 -04:00
|
|
|
m_Fractorium->ShowCritical("Paste Error", QString::fromStdString(errors));
|
2014-07-08 03:11:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!embers.empty())
|
|
|
|
{
|
|
|
|
for (i = 0; i < embers.size(); i++)
|
|
|
|
{
|
2014-10-14 11:53:15 -04:00
|
|
|
embers[i].m_Index = m_EmberFile.Size();
|
|
|
|
ConstrainDimensions(embers[i]);//Do not exceed the max texture size.
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
|
|
//Also ensure it has a name.
|
|
|
|
if (embers[i].m_Name == "" || embers[i].m_Name == "No name")
|
2014-12-11 00:50:15 -05:00
|
|
|
embers[i].m_Name = ToString<qulonglong>(embers[i].m_Index).toStdString();
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
|
|
m_EmberFile.m_Embers.push_back(embers[i]);//Will invalidate the pointers contained in the EmberTreeWidgetItems, UpdateLibraryTree() will resync.
|
|
|
|
}
|
|
|
|
|
|
|
|
m_EmberFile.MakeNamesUnique();
|
|
|
|
UpdateLibraryTree();
|
|
|
|
SetEmber(previousSize);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fractorium::OnActionPasteXmlAppend(bool checked) { m_Controller->PasteXmlAppend(); }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Convert the Xml text from the clipboard to an ember, overwrite the
|
|
|
|
/// current file and set the first as the current ember. If multiple Xmls were
|
|
|
|
/// copied to the clipboard and were enclosed in <flames> tags, then the current file will contain all of them.
|
|
|
|
/// </summary>
|
|
|
|
template <typename T>
|
|
|
|
void FractoriumEmberController<T>::PasteXmlOver()
|
|
|
|
{
|
2014-12-06 00:05:09 -05:00
|
|
|
uint i;
|
2014-07-08 03:11:14 -04:00
|
|
|
string s, errors;
|
|
|
|
XmlToEmber<T> parser;
|
|
|
|
Ember<T> backupEmber = m_EmberFile.m_Embers[0];
|
|
|
|
QTextCodec* codec = QTextCodec::codecForName("UTF-8");
|
|
|
|
QByteArray b = codec->fromUnicode(QApplication::clipboard()->text());
|
|
|
|
|
|
|
|
s.reserve(b.size());
|
|
|
|
|
|
|
|
for (i = 0; i < b.size(); i++)
|
|
|
|
{
|
2014-12-11 00:50:15 -05:00
|
|
|
if (uint(b[i]) < 128u)
|
2014-07-08 03:11:14 -04:00
|
|
|
s.push_back(b[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
b.clear();
|
|
|
|
StopPreviewRender();
|
|
|
|
m_EmberFile.m_Embers.clear();//Will invalidate the pointers contained in the EmberTreeWidgetItems, UpdateLibraryTree() will resync.
|
2014-12-11 00:50:15 -05:00
|
|
|
parser.Parse(reinterpret_cast<byte*>(const_cast<char*>(s.c_str())), "", m_EmberFile.m_Embers);
|
2014-07-08 03:11:14 -04:00
|
|
|
errors = parser.ErrorReportString();
|
|
|
|
|
|
|
|
if (errors != "")
|
|
|
|
{
|
2014-10-14 11:53:15 -04:00
|
|
|
m_Fractorium->ShowCritical("Paste Error", QString::fromStdString(errors));
|
2014-07-08 03:11:14 -04:00
|
|
|
}
|
|
|
|
|
2014-10-14 11:53:15 -04:00
|
|
|
if (m_EmberFile.Size())
|
2014-07-08 03:11:14 -04:00
|
|
|
{
|
2014-10-14 11:53:15 -04:00
|
|
|
for (i = 0; i < m_EmberFile.Size(); i++)
|
2014-07-08 03:11:14 -04:00
|
|
|
{
|
|
|
|
m_EmberFile.m_Embers[i].m_Index = i;
|
2014-10-14 11:53:15 -04:00
|
|
|
ConstrainDimensions(m_EmberFile.m_Embers[i]);//Do not exceed the max texture size.
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
|
|
//Also ensure it has a name.
|
|
|
|
if (m_EmberFile.m_Embers[i].m_Name == "" || m_EmberFile.m_Embers[i].m_Name == "No name")
|
2014-12-11 00:50:15 -05:00
|
|
|
m_EmberFile.m_Embers[i].m_Name = ToString<qulonglong>(m_EmberFile.m_Embers[i].m_Index).toStdString();
|
2014-07-08 03:11:14 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
backupEmber.m_Index = 0;
|
|
|
|
m_EmberFile.m_Embers.push_back(backupEmber);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_EmberFile.MakeNamesUnique();
|
|
|
|
FillLibraryTree();
|
|
|
|
SetEmber(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fractorium::OnActionPasteXmlOver(bool checked) { m_Controller->PasteXmlOver(); }
|
|
|
|
|
2015-05-19 22:31:33 -04:00
|
|
|
/// <summary>
|
|
|
|
/// Copy the selected xforms.
|
|
|
|
/// Note this will also copy final if selected.
|
|
|
|
/// If none selected, just copy current.
|
|
|
|
/// </summary>
|
|
|
|
template <typename T>
|
|
|
|
void FractoriumEmberController<T>::CopySelectedXforms()
|
|
|
|
{
|
|
|
|
m_CopiedXforms.clear();
|
|
|
|
m_CopiedFinalXform.Clear();
|
|
|
|
|
|
|
|
UpdateXform([&](Xform<T>* xform)
|
|
|
|
{
|
|
|
|
if (m_Ember.IsFinalXform(xform))
|
|
|
|
m_CopiedFinalXform = *xform;
|
|
|
|
else
|
|
|
|
m_CopiedXforms.push_back(*xform);
|
|
|
|
}, UPDATE_SELECTED, false);
|
|
|
|
m_Fractorium->ui.ActionPasteSelectedXforms->setEnabled(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fractorium::OnActionCopySelectedXforms(bool checked)
|
|
|
|
{
|
|
|
|
m_Controller->CopySelectedXforms();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Paste the selected xforms.
|
|
|
|
/// Note this will also paste/overwrite final if previously copied.
|
|
|
|
/// Resets the rendering process.
|
|
|
|
/// </summary>
|
|
|
|
template <typename T>
|
|
|
|
void FractoriumEmberController<T>::PasteSelectedXforms()
|
|
|
|
{
|
|
|
|
Update([&]()
|
|
|
|
{
|
|
|
|
for (auto& it : m_CopiedXforms)
|
|
|
|
m_Ember.AddXform(it);
|
|
|
|
|
|
|
|
if (!m_CopiedFinalXform.Empty())
|
|
|
|
m_Ember.SetFinalXform(m_CopiedFinalXform);
|
|
|
|
|
|
|
|
FillXforms();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void Fractorium::OnActionPasteSelectedXforms(bool checked)
|
|
|
|
{
|
|
|
|
m_Controller->PasteSelectedXforms();
|
|
|
|
}
|
|
|
|
|
2015-06-20 14:35:08 -04:00
|
|
|
/// <summary>
|
|
|
|
/// Reset dock widgets and tabs to their default position.
|
|
|
|
/// Note that there is a bug in Qt, where it will only move them all to the left side if at least
|
|
|
|
/// one is on the left side, or they are all floating. If one or more are docked right, and none are docked
|
|
|
|
/// left, then it will put them all on the right side. Hopefully this isn't too much of a problem.
|
|
|
|
/// </summary>
|
|
|
|
void Fractorium::OnActionResetWorkspace(bool checked)
|
|
|
|
{
|
|
|
|
QDockWidget* firstDock = nullptr;
|
|
|
|
|
|
|
|
for (auto dock : m_Docks)
|
|
|
|
{
|
|
|
|
dock->setFloating(true);
|
|
|
|
dock->setGeometry(QRect(100, 100, dock->width(), dock->height()));//Doesn't seem to have an effect.
|
|
|
|
dock->setFloating(false);
|
|
|
|
dock->show();
|
|
|
|
|
|
|
|
if (firstDock)
|
|
|
|
tabifyDockWidget(firstDock, dock);
|
|
|
|
|
|
|
|
firstDock = dock;
|
|
|
|
}
|
|
|
|
|
|
|
|
ui.LibraryDockWidget->raise();
|
|
|
|
ui.LibraryDockWidget->show();
|
|
|
|
}
|
|
|
|
|
2014-07-08 03:11:14 -04:00
|
|
|
/// <summary>
|
|
|
|
/// Add reflective symmetry to the current ember.
|
|
|
|
/// Resets the rendering process.
|
|
|
|
/// </summary>
|
|
|
|
template <typename T>
|
|
|
|
void FractoriumEmberController<T>::AddReflectiveSymmetry()
|
|
|
|
{
|
|
|
|
QComboBox* combo = m_Fractorium->ui.CurrentXformCombo;
|
|
|
|
|
2015-04-27 01:11:56 -04:00
|
|
|
Update([&]()
|
|
|
|
{
|
|
|
|
m_Ember.AddSymmetry(-1, m_Rand);
|
2015-05-03 20:13:14 -04:00
|
|
|
int index = m_Ember.TotalXformCount() - (m_Ember.UseFinalXform() ? 2 : 1);//Set index to the last item before final.
|
|
|
|
FillXforms(index);
|
2015-04-27 01:11:56 -04:00
|
|
|
});
|
2014-07-08 03:11:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Fractorium::OnActionAddReflectiveSymmetry(bool checked) { m_Controller->AddReflectiveSymmetry(); }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Add rotational symmetry to the current ember.
|
|
|
|
/// Resets the rendering process.
|
|
|
|
/// </summary>
|
|
|
|
template <typename T>
|
|
|
|
void FractoriumEmberController<T>::AddRotationalSymmetry()
|
|
|
|
{
|
|
|
|
QComboBox* combo = m_Fractorium->ui.CurrentXformCombo;
|
|
|
|
|
2015-04-27 01:11:56 -04:00
|
|
|
Update([&]()
|
|
|
|
{
|
|
|
|
m_Ember.AddSymmetry(2, m_Rand);
|
2015-05-03 20:13:14 -04:00
|
|
|
int index = m_Ember.TotalXformCount() - (m_Ember.UseFinalXform() ? 2 : 1);//Set index to the last item before final.
|
|
|
|
FillXforms(index);
|
2015-04-27 01:11:56 -04:00
|
|
|
});
|
2014-07-08 03:11:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Fractorium::OnActionAddRotationalSymmetry(bool checked) { m_Controller->AddRotationalSymmetry(); }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Add both reflective and rotational symmetry to the current ember.
|
|
|
|
/// Resets the rendering process.
|
|
|
|
/// </summary>
|
|
|
|
template <typename T>
|
|
|
|
void FractoriumEmberController<T>::AddBothSymmetry()
|
|
|
|
{
|
|
|
|
QComboBox* combo = m_Fractorium->ui.CurrentXformCombo;
|
|
|
|
|
2015-04-27 01:11:56 -04:00
|
|
|
Update([&]()
|
|
|
|
{
|
|
|
|
m_Ember.AddSymmetry(-2, m_Rand);
|
2015-05-03 20:13:14 -04:00
|
|
|
int index = m_Ember.TotalXformCount() - (m_Ember.UseFinalXform() ? 2 : 1);//Set index to the last item before final.
|
|
|
|
FillXforms(index);
|
2015-04-27 01:11:56 -04:00
|
|
|
});
|
2014-07-08 03:11:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Fractorium::OnActionAddBothSymmetry(bool checked) { m_Controller->AddBothSymmetry(); }
|
|
|
|
|
Numerous fixes
0.4.0.5 Beta 07/18/2014
--User Changes
Allow for vibrancy values > 1.
Add flatten and unflatten menu items.
Automatically flatten like Apophysis does.
Add plugin and new_linear tags to Xml to be compatible with Apophysis.
--Bug Fixes
Fix blur, blur3d, bubble, cropn, cross, curl, curl3d, epispiral, ho,
julia3d, julia3dz, loonie, mirror_x, mirror_y, mirror_z, rotate_x,
sinusoidal, spherical, spherical3d, stripes.
Unique filename on final render was completely broken.
Two severe OpenCL bugs. Random seeds were biased and fusing was being
reset too often leading to results that differ from the CPU.
Subtle, but sometimes severe bug in the setup of the xaos weights.
Use properly defined epsilon by getting the value from
std::numeric_limits, rather than hard coding 1e-6 or 1e-10.
Omit incorrect usage of epsilon everywhere. It should not be
automatically added to denominators. Rather, it should only be used if
the denominator is zero.
Force final render progress bars to 100 on completion. Sometimes they
didn't seem to make it there.
Make variation name and params comparisons be case insensitive.
--Code Changes
Make ForEach and FindIf wrappers around std::for_each and std::find_if.
2014-07-19 02:33:18 -04:00
|
|
|
/// <summary>
|
|
|
|
/// Adds a FlattenVariation to every xform in the current ember.
|
|
|
|
/// Resets the rendering process.
|
|
|
|
/// </summary>
|
|
|
|
template <typename T>
|
2015-04-27 01:11:56 -04:00
|
|
|
void FractoriumEmberController<T>::Flatten() { UpdateXform([&] (Xform<T>* xform) { m_Ember.Flatten(XmlToEmber<T>::m_FlattenNames); FillVariationTreeWithXform(xform); }); }
|
Numerous fixes
0.4.0.5 Beta 07/18/2014
--User Changes
Allow for vibrancy values > 1.
Add flatten and unflatten menu items.
Automatically flatten like Apophysis does.
Add plugin and new_linear tags to Xml to be compatible with Apophysis.
--Bug Fixes
Fix blur, blur3d, bubble, cropn, cross, curl, curl3d, epispiral, ho,
julia3d, julia3dz, loonie, mirror_x, mirror_y, mirror_z, rotate_x,
sinusoidal, spherical, spherical3d, stripes.
Unique filename on final render was completely broken.
Two severe OpenCL bugs. Random seeds were biased and fusing was being
reset too often leading to results that differ from the CPU.
Subtle, but sometimes severe bug in the setup of the xaos weights.
Use properly defined epsilon by getting the value from
std::numeric_limits, rather than hard coding 1e-6 or 1e-10.
Omit incorrect usage of epsilon everywhere. It should not be
automatically added to denominators. Rather, it should only be used if
the denominator is zero.
Force final render progress bars to 100 on completion. Sometimes they
didn't seem to make it there.
Make variation name and params comparisons be case insensitive.
--Code Changes
Make ForEach and FindIf wrappers around std::for_each and std::find_if.
2014-07-19 02:33:18 -04:00
|
|
|
void Fractorium::OnActionFlatten(bool checked) { m_Controller->Flatten(); }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Removes pre/reg/post FlattenVariation from every xform in the current ember.
|
|
|
|
/// Resets the rendering process.
|
|
|
|
/// </summary>
|
|
|
|
template <typename T>
|
2015-04-27 01:11:56 -04:00
|
|
|
void FractoriumEmberController<T>::Unflatten() { UpdateXform([&] (Xform<T>* xform) { m_Ember.Unflatten(); FillVariationTreeWithXform(xform); }); }
|
Numerous fixes
0.4.0.5 Beta 07/18/2014
--User Changes
Allow for vibrancy values > 1.
Add flatten and unflatten menu items.
Automatically flatten like Apophysis does.
Add plugin and new_linear tags to Xml to be compatible with Apophysis.
--Bug Fixes
Fix blur, blur3d, bubble, cropn, cross, curl, curl3d, epispiral, ho,
julia3d, julia3dz, loonie, mirror_x, mirror_y, mirror_z, rotate_x,
sinusoidal, spherical, spherical3d, stripes.
Unique filename on final render was completely broken.
Two severe OpenCL bugs. Random seeds were biased and fusing was being
reset too often leading to results that differ from the CPU.
Subtle, but sometimes severe bug in the setup of the xaos weights.
Use properly defined epsilon by getting the value from
std::numeric_limits, rather than hard coding 1e-6 or 1e-10.
Omit incorrect usage of epsilon everywhere. It should not be
automatically added to denominators. Rather, it should only be used if
the denominator is zero.
Force final render progress bars to 100 on completion. Sometimes they
didn't seem to make it there.
Make variation name and params comparisons be case insensitive.
--Code Changes
Make ForEach and FindIf wrappers around std::for_each and std::find_if.
2014-07-19 02:33:18 -04:00
|
|
|
void Fractorium::OnActionUnflatten(bool checked) { m_Controller->Unflatten(); }
|
|
|
|
|
2014-07-08 03:11:14 -04:00
|
|
|
/// <summary>
|
|
|
|
/// Delete all but one xform in the current ember.
|
|
|
|
/// Clear that xform's variations.
|
|
|
|
/// Resets the rendering process.
|
|
|
|
/// </summary>
|
|
|
|
template <typename T>
|
|
|
|
void FractoriumEmberController<T>::ClearFlame()
|
|
|
|
{
|
2015-04-27 01:11:56 -04:00
|
|
|
Update([&]()
|
2014-07-08 03:11:14 -04:00
|
|
|
{
|
2015-04-27 01:11:56 -04:00
|
|
|
while (m_Ember.TotalXformCount() > 1)
|
|
|
|
m_Ember.DeleteTotalXform(m_Ember.TotalXformCount() - 1);
|
|
|
|
|
|
|
|
if (m_Ember.XformCount() == 1)
|
2014-07-08 03:11:14 -04:00
|
|
|
{
|
2015-04-27 01:11:56 -04:00
|
|
|
if (Xform<T>* xform = m_Ember.GetXform(0))
|
|
|
|
{
|
|
|
|
xform->Clear();
|
|
|
|
xform->ParentEmber(&m_Ember);
|
|
|
|
}
|
2014-07-08 03:11:14 -04:00
|
|
|
}
|
|
|
|
|
2015-04-27 01:11:56 -04:00
|
|
|
FillXforms();
|
|
|
|
});
|
2014-07-08 03:11:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void Fractorium::OnActionClearFlame(bool checked) { m_Controller->ClearFlame(); }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Re-render all previews.
|
|
|
|
/// </summary>
|
|
|
|
void Fractorium::OnActionRenderPreviews(bool checked)
|
|
|
|
{
|
|
|
|
m_Controller->RenderPreviews();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// 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.
|
|
|
|
/// </summary>
|
|
|
|
void Fractorium::OnActionStopRenderingPreviews(bool checked) { m_Controller->StopPreviewRender(); }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Show the final render dialog as a modeless dialog to allow
|
|
|
|
/// the user to minimize the main window while doing a lengthy final render.
|
|
|
|
/// Note: The user probably should not be otherwise interacting with the main GUI
|
|
|
|
/// while the final render is taking place.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="checked">Ignored</param>
|
|
|
|
void Fractorium::OnActionFinalRender(bool checked)
|
|
|
|
{
|
|
|
|
//First completely stop what the current rendering process is doing.
|
|
|
|
m_Controller->DeleteRenderer();//Delete the renderer, but not the controller.
|
2014-10-14 11:53:15 -04:00
|
|
|
OnActionSaveCurrentToOpenedFile(true);//Save whatever was edited back to the current open file.
|
2014-07-08 03:11:14 -04:00
|
|
|
m_RenderStatusLabel->setText("Renderer stopped.");
|
|
|
|
m_FinalRenderDialog->show();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Called when the final render dialog has been closed.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="result">Ignored</param>
|
|
|
|
void Fractorium::OnFinalRenderClose(int result)
|
|
|
|
{
|
|
|
|
m_RenderStatusLabel->setText("Renderer starting...");
|
|
|
|
StartRenderTimer();//Re-create the renderer and start rendering again.
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Show the final options dialog.
|
|
|
|
/// Restart rendering and sync options after the options dialog is dismissed with Ok.
|
|
|
|
/// Called when the options dialog is finished with ok.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="checked">Ignored</param>
|
|
|
|
void Fractorium::OnActionOptions(bool checked)
|
|
|
|
{
|
|
|
|
if (m_OptionsDialog->exec())
|
|
|
|
{
|
2015-07-29 20:25:02 -04:00
|
|
|
SyncOptionsToToolbar();//This won't trigger a recreate, the call below handles it.
|
|
|
|
ShutdownAndRecreateFromOptions();//This will recreate the controller and/or the renderer from the options if necessary, then start the render timer.
|
2014-07-08 03:11:14 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Show the about dialog.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="checked">Ignored</param>
|
|
|
|
void Fractorium::OnActionAbout(bool checked)
|
|
|
|
{
|
|
|
|
m_AboutDialog->exec();
|
|
|
|
}
|
2014-12-11 00:50:15 -05:00
|
|
|
|
|
|
|
template class FractoriumEmberController<float>;
|
|
|
|
|
|
|
|
#ifdef DO_DOUBLE
|
|
|
|
template class FractoriumEmberController<double>;
|
|
|
|
#endif
|