2018-07-08 11:46:23 -04:00
|
|
|
|
#include "FractoriumPch.h"
|
2014-07-08 03:11:14 -04:00
|
|
|
|
#include "Fractorium.h"
|
2015-10-27 00:31:35 -04:00
|
|
|
|
#include "QssDialog.h"
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
2015-05-30 00:08:44 -04:00
|
|
|
|
// X11 headers on Linux define this, causing build errors.
|
|
|
|
|
#ifdef KeyRelease
|
|
|
|
|
#undef KeyRelease
|
|
|
|
|
#endif
|
|
|
|
|
|
2014-07-08 03:11:14 -04:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Constructor that initializes the entire program.
|
|
|
|
|
/// The setup process is very lengthy because it requires many custom modifications
|
|
|
|
|
/// to the GUI widgets that are not possible to do through the designer. So if something
|
|
|
|
|
/// is present here, it's safe to assume it can't be done in the designer.
|
|
|
|
|
/// </summary>
|
2014-12-11 00:50:15 -05:00
|
|
|
|
/// <param name="p">The parent widget of this item</param>
|
2015-12-31 16:41:59 -05:00
|
|
|
|
Fractorium::Fractorium(QWidget* p)
|
|
|
|
|
: QMainWindow(p)
|
2014-07-08 03:11:14 -04:00
|
|
|
|
{
|
2016-03-01 20:26:45 -05:00
|
|
|
|
int iconSize_ = 9;
|
2014-07-26 15:03:51 -04:00
|
|
|
|
size_t i = 0;
|
2015-10-27 00:31:35 -04:00
|
|
|
|
string s;
|
2014-07-08 03:11:14 -04:00
|
|
|
|
Timing t;
|
|
|
|
|
ui.setupUi(this);
|
2015-12-31 16:41:59 -05:00
|
|
|
|
m_Info = OpenCLInfo::Instance();
|
2017-05-31 22:50:05 -04:00
|
|
|
|
qRegisterMetaType<size_t>("size_t");
|
2014-07-08 03:11:14 -04:00
|
|
|
|
qRegisterMetaType<QVector<int>>("QVector<int>");//For previews.
|
2014-12-06 00:05:09 -05:00
|
|
|
|
qRegisterMetaType<vector<byte>>("vector<byte>");
|
2017-07-22 16:43:35 -04:00
|
|
|
|
qRegisterMetaType<vv4F>("vv4F");
|
2014-07-08 03:11:14 -04:00
|
|
|
|
qRegisterMetaType<EmberTreeWidgetItemBase*>("EmberTreeWidgetItemBase*");
|
2015-06-17 23:05:53 -04:00
|
|
|
|
tabifyDockWidget(ui.LibraryDockWidget, ui.FlameDockWidget);
|
|
|
|
|
tabifyDockWidget(ui.FlameDockWidget, ui.XformsDockWidget);
|
|
|
|
|
tabifyDockWidget(ui.XformsDockWidget, ui.XaosDockWidget);
|
|
|
|
|
tabifyDockWidget(ui.XaosDockWidget, ui.PaletteDockWidget);
|
|
|
|
|
tabifyDockWidget(ui.PaletteDockWidget, ui.InfoDockWidget);
|
2017-12-16 00:05:58 -05:00
|
|
|
|
setTabPosition(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea, QTabWidget::TabPosition::North);
|
|
|
|
|
setTabShape(QTabWidget::TabShape::Triangular);
|
2015-06-20 14:35:08 -04:00
|
|
|
|
m_Docks.reserve(8);
|
|
|
|
|
m_Docks.push_back(ui.LibraryDockWidget);
|
|
|
|
|
m_Docks.push_back(ui.FlameDockWidget);
|
|
|
|
|
m_Docks.push_back(ui.XformsDockWidget);
|
|
|
|
|
m_Docks.push_back(ui.XaosDockWidget);
|
|
|
|
|
m_Docks.push_back(ui.PaletteDockWidget);
|
|
|
|
|
m_Docks.push_back(ui.InfoDockWidget);
|
2017-12-16 00:05:58 -05:00
|
|
|
|
|
|
|
|
|
for (auto dock : m_Docks)//Prevents a dock from ever getting accidentally hidden.
|
|
|
|
|
{
|
|
|
|
|
dock->setWindowFlags(dock->windowFlags() & Qt::WindowStaysOnTopHint);
|
2017-12-22 01:11:18 -05:00
|
|
|
|
dock->setAllowedAreas(Qt::DockWidgetArea::LeftDockWidgetArea
|
|
|
|
|
//#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
|
|
|
|
| Qt::DockWidgetArea::RightDockWidgetArea
|
|
|
|
|
//#endif
|
|
|
|
|
);
|
2017-12-16 00:05:58 -05:00
|
|
|
|
}
|
|
|
|
|
|
2014-07-08 03:11:14 -04:00
|
|
|
|
m_FontSize = 9;
|
|
|
|
|
m_VarSortMode = 1;//Sort by weight by default.
|
2015-05-31 01:14:34 -04:00
|
|
|
|
m_PaletteSortMode = 0;//Sort by palette ascending by default.
|
2014-07-08 03:11:14 -04:00
|
|
|
|
m_ColorDialog = new QColorDialog(this);
|
2016-12-05 22:04:33 -05:00
|
|
|
|
m_Settings = FractoriumSettings::Instance();
|
2015-10-27 00:31:35 -04:00
|
|
|
|
m_QssDialog = new QssDialog(this);
|
2016-12-05 22:04:33 -05:00
|
|
|
|
m_FinalRenderDialog = new FractoriumFinalRenderDialog(this);
|
|
|
|
|
m_OptionsDialog = new FractoriumOptionsDialog(this);
|
|
|
|
|
m_VarDialog = new FractoriumVariationsDialog(this);
|
2014-07-08 03:11:14 -04:00
|
|
|
|
m_AboutDialog = new FractoriumAboutDialog(this);
|
2014-09-08 00:05:27 -04:00
|
|
|
|
//Put the about dialog in the screen center.
|
|
|
|
|
const QRect screen = QApplication::desktop()->screenGeometry();
|
|
|
|
|
m_AboutDialog->move(screen.center() - m_AboutDialog->rect().center());
|
2014-07-08 03:11:14 -04:00
|
|
|
|
connect(m_ColorDialog, SIGNAL(colorSelected(const QColor&)), this, SLOT(OnColorSelected(const QColor&)), Qt::QueuedConnection);
|
2014-07-26 15:03:51 -04:00
|
|
|
|
m_XformComboColors[i++] = QColor(0XFF, 0X00, 0X00);
|
|
|
|
|
m_XformComboColors[i++] = QColor(0XCC, 0XCC, 0X00);
|
|
|
|
|
m_XformComboColors[i++] = QColor(0X00, 0XCC, 0X00);
|
|
|
|
|
m_XformComboColors[i++] = QColor(0X00, 0XCC, 0XCC);
|
|
|
|
|
m_XformComboColors[i++] = QColor(0X40, 0X40, 0XFF);
|
|
|
|
|
m_XformComboColors[i++] = QColor(0XCC, 0X00, 0XCC);
|
|
|
|
|
m_XformComboColors[i++] = QColor(0XCC, 0X80, 0X00);
|
|
|
|
|
m_XformComboColors[i++] = QColor(0X80, 0X00, 0X4F);
|
|
|
|
|
m_XformComboColors[i++] = QColor(0X80, 0X80, 0X22);
|
|
|
|
|
m_XformComboColors[i++] = QColor(0X60, 0X80, 0X60);
|
|
|
|
|
m_XformComboColors[i++] = QColor(0X50, 0X80, 0X80);
|
|
|
|
|
m_XformComboColors[i++] = QColor(0X4F, 0X4F, 0X80);
|
|
|
|
|
m_XformComboColors[i++] = QColor(0X80, 0X50, 0X80);
|
|
|
|
|
m_XformComboColors[i++] = QColor(0X80, 0X60, 0X22);
|
|
|
|
|
m_FinalXformComboColor = QColor(0x7F, 0x7F, 0x7F);
|
2015-12-31 16:41:59 -05:00
|
|
|
|
|
2014-07-26 15:03:51 -04:00
|
|
|
|
for (i = 0; i < XFORM_COLOR_COUNT; i++)
|
|
|
|
|
{
|
2014-12-11 00:50:15 -05:00
|
|
|
|
QPixmap pixmap(iconSize_, iconSize_);
|
2014-07-26 15:03:51 -04:00
|
|
|
|
pixmap.fill(m_XformComboColors[i]);
|
|
|
|
|
m_XformComboIcons[i] = QIcon(pixmap);
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-11 00:50:15 -05:00
|
|
|
|
QPixmap pixmap(iconSize_, iconSize_);
|
2014-07-26 15:03:51 -04:00
|
|
|
|
pixmap.fill(m_FinalXformComboColor);
|
|
|
|
|
m_FinalXformComboIcon = QIcon(pixmap);
|
2014-07-08 03:11:14 -04:00
|
|
|
|
InitToolbarUI();
|
|
|
|
|
InitParamsUI();
|
|
|
|
|
InitXformsUI();
|
|
|
|
|
InitXformsColorUI();
|
|
|
|
|
InitXformsAffineUI();
|
|
|
|
|
InitXformsVariationsUI();
|
2015-04-27 01:11:56 -04:00
|
|
|
|
InitXformsSelectUI();
|
|
|
|
|
InitXaosUI();
|
2014-07-08 03:11:14 -04:00
|
|
|
|
InitPaletteUI();
|
|
|
|
|
InitLibraryUI();
|
2015-07-15 23:27:32 -04:00
|
|
|
|
InitInfoUI();
|
2014-07-08 03:11:14 -04:00
|
|
|
|
InitMenusUI();
|
|
|
|
|
//This will init the controller and fill in the variations and palette tables with template specific instances
|
|
|
|
|
//of their respective objects.
|
|
|
|
|
#ifdef DO_DOUBLE
|
2015-12-31 16:41:59 -05:00
|
|
|
|
|
2014-07-08 03:11:14 -04:00
|
|
|
|
if (m_Settings->Double())
|
2014-10-18 17:07:07 -04:00
|
|
|
|
m_Controller = unique_ptr<FractoriumEmberControllerBase>(new FractoriumEmberController<double>(this));
|
2014-07-08 03:11:14 -04:00
|
|
|
|
else
|
|
|
|
|
#endif
|
2014-10-18 17:07:07 -04:00
|
|
|
|
m_Controller = unique_ptr<FractoriumEmberControllerBase>(new FractoriumEmberController<float>(this));
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
2016-03-28 21:49:10 -04:00
|
|
|
|
m_Controller->SetupVariationsTree();
|
2015-08-10 23:10:23 -04:00
|
|
|
|
m_Controller->FilteredVariations();
|
2015-06-28 17:04:30 -04:00
|
|
|
|
|
--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.
2017-11-26 20:27:00 -05:00
|
|
|
|
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());
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
--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.
2017-11-26 20:27:00 -05:00
|
|
|
|
int statusBarHeight = 20;// *devicePixelRatio();
|
2015-10-27 00:31:35 -04:00
|
|
|
|
ui.StatusBar->setMinimumHeight(statusBarHeight);
|
|
|
|
|
ui.StatusBar->setMaximumHeight(statusBarHeight);
|
2014-07-08 03:11:14 -04:00
|
|
|
|
m_RenderStatusLabel = new QLabel(this);
|
|
|
|
|
m_RenderStatusLabel->setMinimumWidth(200);
|
|
|
|
|
m_RenderStatusLabel->setAlignment(Qt::AlignRight);
|
2015-10-27 00:31:35 -04:00
|
|
|
|
ui.StatusBar->addPermanentWidget(m_RenderStatusLabel);
|
2014-07-08 03:11:14 -04:00
|
|
|
|
m_CoordinateStatusLabel = new QLabel(this);
|
|
|
|
|
m_CoordinateStatusLabel->setMinimumWidth(300);
|
|
|
|
|
m_CoordinateStatusLabel->setMaximumWidth(300);
|
|
|
|
|
m_CoordinateStatusLabel->setAlignment(Qt::AlignLeft);
|
2015-10-27 00:31:35 -04:00
|
|
|
|
ui.StatusBar->addWidget(m_CoordinateStatusLabel);
|
2014-07-08 03:11:14 -04:00
|
|
|
|
int progressBarHeight = 15;
|
|
|
|
|
int progressBarWidth = 300;
|
|
|
|
|
m_ProgressBar = new QProgressBar(this);
|
|
|
|
|
m_ProgressBar->setRange(0, 100);
|
|
|
|
|
m_ProgressBar->setValue(0);
|
|
|
|
|
m_ProgressBar->setMinimumHeight(progressBarHeight);
|
|
|
|
|
m_ProgressBar->setMaximumHeight(progressBarHeight);
|
|
|
|
|
m_ProgressBar->setMinimumWidth(progressBarWidth);
|
|
|
|
|
m_ProgressBar->setMaximumWidth(progressBarWidth);
|
2015-10-27 00:31:35 -04:00
|
|
|
|
ui.StatusBar->addPermanentWidget(m_ProgressBar);
|
2015-01-01 10:17:05 -05:00
|
|
|
|
//Setup pointer in the GL window to point back to here.
|
|
|
|
|
ui.GLDisplay->SetMainWindow(this);
|
2015-06-20 14:35:08 -04:00
|
|
|
|
bool restored = restoreState(m_Settings->value("windowState").toByteArray());
|
2015-01-02 18:11:36 -05:00
|
|
|
|
showMaximized();//This won't fully set things up and show them until after this constructor exits.
|
2015-06-17 23:05:53 -04:00
|
|
|
|
connect(ui.LibraryDockWidget, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(dockLocationChanged(Qt::DockWidgetArea)));
|
|
|
|
|
connect(ui.LibraryDockWidget, SIGNAL(topLevelChanged(bool)), this, SLOT(OnDockTopLevelChanged(bool)));
|
2015-12-31 16:41:59 -05:00
|
|
|
|
|
2016-04-03 21:55:12 -04:00
|
|
|
|
//Always ensure the library tab is selected if not restoring, which will show preview renders.
|
2015-06-20 14:35:08 -04:00
|
|
|
|
if (!restored)
|
|
|
|
|
{
|
|
|
|
|
ui.LibraryDockWidget->raise();
|
|
|
|
|
ui.LibraryDockWidget->show();
|
|
|
|
|
ui.XformsTabWidget->setCurrentIndex(2);//Make variations tab the currently selected one under the Xforms tab.
|
|
|
|
|
}
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
|
|
|
|
m_PreviousPaletteRow = -1;//Force click handler the first time through.
|
|
|
|
|
SetCoordinateStatus(0, 0, 0, 0);
|
2014-07-28 01:25:38 -04:00
|
|
|
|
SetTabOrders();
|
2015-10-27 00:31:35 -04:00
|
|
|
|
m_SettingsPath = QFileInfo(m_Settings->fileName()).absoluteDir().absolutePath();
|
|
|
|
|
ifstream ifs((m_SettingsPath + "/default.qss").toStdString().c_str(), ifstream::in);
|
|
|
|
|
|
|
|
|
|
if (ifs.is_open())
|
|
|
|
|
{
|
|
|
|
|
string total, qs;
|
2016-12-05 22:04:33 -05:00
|
|
|
|
total.reserve(20 * 1024);
|
2015-10-27 00:31:35 -04:00
|
|
|
|
|
|
|
|
|
while (std::getline(ifs, qs))
|
|
|
|
|
total += qs + "\n";
|
|
|
|
|
|
|
|
|
|
m_Style = QString::fromStdString(total);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
m_Style = BaseStyle();
|
|
|
|
|
|
|
|
|
|
setStyleSheet(m_Style);
|
|
|
|
|
|
|
|
|
|
if (!m_Settings->Theme().isEmpty())
|
|
|
|
|
{
|
|
|
|
|
if (auto theme = QStyleFactory::create(m_Settings->Theme()))
|
|
|
|
|
{
|
|
|
|
|
m_Theme = theme;
|
|
|
|
|
setStyle(m_Theme);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (!QStyleFactory::keys().empty())
|
|
|
|
|
{
|
|
|
|
|
m_Theme = QStyleFactory::create(qApp->style()->objectName());
|
|
|
|
|
setStyle(m_Theme);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
--User changes
-Add a palette editor.
-Add support for reading .ugr/.gradient/.gradients palette files.
-Allow toggling on spinners whose minimum value is not zero.
-Allow toggling display of image, affines and grid.
-Add new variations: cylinder2, circlesplit, tile_log, truchet_fill, waves2_radial.
--Bug fixes
-cpow2 was wrong.
-Palettes with rapid changes in color would produce slightly different outputs from Apo/Chaotica. This was due to a long standing bug from flam3.
-Use exec() on Apple and show() on all other OSes for dialog boxes.
-Trying to render a sequence with no frames would crash.
-Selecting multiple xforms and rotating them would produce the wrong rotation.
-Better handling when parsing flames using different encoding, such as unicode and UTF-8.
-Switching between SP/DP didn't reselect the selected flame in the Library tab.
--Code changes
-Make all types concerning palettes be floats, including PaletteTableWidgetItem.
-PaletteTableWidgetItem is no longer templated because all palettes are float.
-Include the source colors for user created gradients.
-Change parallel_for() calls to work with very old versions of TBB which are lingering on some systems.
-Split conditional out of accumulation loop on the CPU for better performance.
-Vectorize summing when doing density filter for better performance.
-Make all usage of palettes be of type float, double is pointless.
-Allow palettes to reside in multiple folders, while ensuring only one of each name is added.
-Refactor some palette path searching code.
-Make ReadFile() throw and catch an exception if the file operation fails.
-A little extra safety in foci and foci3D with a call to Zeps().
-Cast to (real_t) in the OpenCL string for the w variation, which was having trouble compiling on Mac.
-Fixing missing comma between paths in InitPaletteList().
-Move Xml and PaletteList classes into cpp to shorten build times when working on them.
-Remove default param values for IterOpenCLKernelCreator<T>::SharedDataIndexDefines in cpp file.
-Change more NULL to nullptr.
2017-02-26 03:02:21 -05:00
|
|
|
|
#ifdef __APPLE__
|
|
|
|
|
|
|
|
|
|
for (auto dock : m_Docks)//Fixes focus problem on OSX.
|
|
|
|
|
{
|
|
|
|
|
if (!dock->isHidden())
|
|
|
|
|
{
|
|
|
|
|
dock->setFloating(!dock->isFloating());
|
|
|
|
|
dock->setFloating(!dock->isFloating());
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
2014-07-08 03:11:14 -04:00
|
|
|
|
//At this point, everything has been setup except the renderer. Shortly after
|
2015-01-02 18:11:36 -05:00
|
|
|
|
//this constructor exits, GLWidget::InitGL() will create the initial flock and start the rendering timer
|
2014-07-08 03:11:14 -04:00
|
|
|
|
//which executes whenever the program is idle. Upon starting the timer, the renderer
|
|
|
|
|
//will be initialized.
|
2018-04-29 01:28:05 -04:00
|
|
|
|
//auto cdc = wglGetCurrentDC();
|
|
|
|
|
//auto cc = wglGetCurrentContext();
|
|
|
|
|
//qDebug() << "Fractorium::Fractorium():";
|
|
|
|
|
//qDebug() << "Current DC: " << cdc;
|
|
|
|
|
//qDebug() << "Current Context: " << cc;
|
2018-01-28 20:51:19 -05:00
|
|
|
|
QTimer::singleShot(1000, [&]() { ui.GLDisplay->InitGL(); });
|
2014-07-08 03:11:14 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Destructor which saves out the settings file.
|
|
|
|
|
/// All other memory is cleared automatically through the use of STL.
|
|
|
|
|
/// </summary>
|
|
|
|
|
Fractorium::~Fractorium()
|
|
|
|
|
{
|
2016-06-11 20:47:03 -04:00
|
|
|
|
SyncSequenceSettings();
|
2015-07-23 21:16:36 -04:00
|
|
|
|
m_VarDialog->SyncSettings();
|
--User changes
-Add a palette editor.
-Add support for reading .ugr/.gradient/.gradients palette files.
-Allow toggling on spinners whose minimum value is not zero.
-Allow toggling display of image, affines and grid.
-Add new variations: cylinder2, circlesplit, tile_log, truchet_fill, waves2_radial.
--Bug fixes
-cpow2 was wrong.
-Palettes with rapid changes in color would produce slightly different outputs from Apo/Chaotica. This was due to a long standing bug from flam3.
-Use exec() on Apple and show() on all other OSes for dialog boxes.
-Trying to render a sequence with no frames would crash.
-Selecting multiple xforms and rotating them would produce the wrong rotation.
-Better handling when parsing flames using different encoding, such as unicode and UTF-8.
-Switching between SP/DP didn't reselect the selected flame in the Library tab.
--Code changes
-Make all types concerning palettes be floats, including PaletteTableWidgetItem.
-PaletteTableWidgetItem is no longer templated because all palettes are float.
-Include the source colors for user created gradients.
-Change parallel_for() calls to work with very old versions of TBB which are lingering on some systems.
-Split conditional out of accumulation loop on the CPU for better performance.
-Vectorize summing when doing density filter for better performance.
-Make all usage of palettes be of type float, double is pointless.
-Allow palettes to reside in multiple folders, while ensuring only one of each name is added.
-Refactor some palette path searching code.
-Make ReadFile() throw and catch an exception if the file operation fails.
-A little extra safety in foci and foci3D with a call to Zeps().
-Cast to (real_t) in the OpenCL string for the w variation, which was having trouble compiling on Mac.
-Fixing missing comma between paths in InitPaletteList().
-Move Xml and PaletteList classes into cpp to shorten build times when working on them.
-Remove default param values for IterOpenCLKernelCreator<T>::SharedDataIndexDefines in cpp file.
-Change more NULL to nullptr.
2017-02-26 03:02:21 -05:00
|
|
|
|
m_Settings->ShowXforms(ui.ActionDrawXforms->isChecked());
|
|
|
|
|
m_Settings->ShowGrid(ui.ActionDrawGrid->isChecked());
|
2015-06-17 23:05:53 -04:00
|
|
|
|
m_Settings->setValue("windowState", saveState());
|
2014-07-08 03:11:14 -04:00
|
|
|
|
m_Settings->sync();
|
--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.
2017-11-26 20:27:00 -05:00
|
|
|
|
|
|
|
|
|
if (m_Settings->LoadLast())
|
|
|
|
|
m_Controller->SaveCurrentFileOnShutdown();
|
2014-07-08 03:11:14 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Set the coordinate text in the status bar.
|
|
|
|
|
/// </summary>
|
2014-12-11 00:50:15 -05:00
|
|
|
|
/// <param name="rasX">The raster x coordinate</param>
|
|
|
|
|
/// <param name="rasY">The raster y coordinate</param>
|
2014-07-08 03:11:14 -04:00
|
|
|
|
/// <param name="worldX">The cartesian world x coordinate</param>
|
|
|
|
|
/// <param name="worldY">The cartesian world y coordinate</param>
|
2014-12-11 00:50:15 -05:00
|
|
|
|
void Fractorium::SetCoordinateStatus(int rasX, int rasY, float worldX, float worldY)
|
2014-07-08 03:11:14 -04:00
|
|
|
|
{
|
2016-12-05 22:04:33 -05:00
|
|
|
|
static QString coords;
|
|
|
|
|
coords.sprintf("Window: %4d, %4d World: %2.2f, %2.2f", rasX, rasY, worldX, worldY);
|
|
|
|
|
m_CoordinateStatusLabel->setText(coords);
|
2014-07-08 03:11:14 -04:00
|
|
|
|
}
|
|
|
|
|
|
2015-01-02 18:11:36 -05:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Center the scroll area.
|
|
|
|
|
/// Called in response to a resizing, or setting of new ember.
|
|
|
|
|
/// </summary>
|
|
|
|
|
void Fractorium::CenterScrollbars()
|
|
|
|
|
{
|
|
|
|
|
QScrollBar* w = ui.GLParentScrollArea->horizontalScrollBar();
|
|
|
|
|
QScrollBar* h = ui.GLParentScrollArea->verticalScrollBar();
|
|
|
|
|
w->setValue(w->maximum() / 2);
|
|
|
|
|
h->setValue(h->maximum() / 2);
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-08 03:11:14 -04:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Apply the settings for saving an ember to an Xml file to an ember (presumably about to be saved).
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="ember">The ember to apply the settings to</param>
|
|
|
|
|
template <typename T>
|
|
|
|
|
void FractoriumEmberController<T>::ApplyXmlSavingTemplate(Ember<T>& ember)
|
|
|
|
|
{
|
|
|
|
|
ember.m_Quality = m_Fractorium->m_Settings->XmlQuality();
|
|
|
|
|
ember.m_Supersample = m_Fractorium->m_Settings->XmlSupersample();
|
2014-10-14 11:53:15 -04:00
|
|
|
|
ember.m_TemporalSamples = m_Fractorium->m_Settings->XmlTemporalSamples();
|
2014-07-08 03:11:14 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Return whether the current ember contains a final xform and the GUI is aware of it.
|
2017-07-27 00:25:44 -04:00
|
|
|
|
/// Note this can be true even if the final is empty, as long as they've added one and have
|
|
|
|
|
/// not explicitly deleted it.
|
2014-07-08 03:11:14 -04:00
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>True if the current ember contains a final xform, else false.</returns>
|
|
|
|
|
bool Fractorium::HaveFinal()
|
|
|
|
|
{
|
2016-02-02 20:51:58 -05:00
|
|
|
|
auto combo = ui.CurrentXformCombo;
|
2014-07-08 03:11:14 -04:00
|
|
|
|
return (combo->count() > 0 && combo->itemText(combo->count() - 1) == "Final");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Slots.
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Empty placeholder for now.
|
|
|
|
|
/// Qt has a severe bug where the dock gets hidden behind the window.
|
|
|
|
|
/// Perhaps this will be used in the future if Qt ever fixes that bug.
|
|
|
|
|
/// Called when the top level dock is changed.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="topLevel">True if top level, else false.</param>
|
|
|
|
|
void Fractorium::OnDockTopLevelChanged(bool topLevel)
|
|
|
|
|
{
|
2017-12-16 00:05:58 -05:00
|
|
|
|
//setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::TabPosition::North);
|
2014-07-08 03:11:14 -04:00
|
|
|
|
//if (topLevel)
|
|
|
|
|
//{
|
|
|
|
|
// if (ui.DockWidget->y() <= 0)
|
|
|
|
|
// ui.DockWidget->setGeometry(ui.DockWidget->x(), ui.DockWidget->y() + 100, ui.DockWidget->width(), ui.DockWidget->height());
|
|
|
|
|
//
|
|
|
|
|
// ui.DockWidget->setFloating(true);
|
|
|
|
|
//}
|
|
|
|
|
//else
|
|
|
|
|
// ui.DockWidget->setFloating(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Empty placeholder for now.
|
|
|
|
|
/// Qt has a severe bug where the dock gets hidden behind the window.
|
|
|
|
|
/// Perhaps this will be used in the future if Qt ever fixes that bug.
|
|
|
|
|
/// Called when the dock location is changed.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="area">The dock widget area</param>
|
|
|
|
|
void Fractorium::dockLocationChanged(Qt::DockWidgetArea area)
|
|
|
|
|
{
|
2017-12-16 00:05:58 -05:00
|
|
|
|
//setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::TabPosition::North);
|
2014-07-08 03:11:14 -04:00
|
|
|
|
//ui.DockWidget->resize(500, ui.DockWidget->height());
|
|
|
|
|
//ui.DockWidget->update();
|
|
|
|
|
//ui.dockWidget->setFloating(true);
|
|
|
|
|
//ui.dockWidget->setFloating(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Virtual event overrides.
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2015-03-21 18:27:37 -04:00
|
|
|
|
/// Event filter for taking special action on:
|
|
|
|
|
/// Dock widget resize events, which in turn trigger GLParentScrollArea events.
|
|
|
|
|
/// Library tree key events, specifically delete.
|
2016-04-03 21:55:12 -04:00
|
|
|
|
/// Library tree drag n drop events.
|
2014-07-08 03:11:14 -04:00
|
|
|
|
/// </summary>
|
2014-12-12 03:54:03 -05:00
|
|
|
|
/// <param name="o">The object</param>
|
|
|
|
|
/// <param name="e">The eevent</param>
|
|
|
|
|
/// <returns>false</returns>
|
|
|
|
|
bool Fractorium::eventFilter(QObject* o, QEvent* e)
|
|
|
|
|
{
|
2018-07-31 00:39:41 -04:00
|
|
|
|
static int fcount = 0;//Qt seems to deliver three events for every key press. So a count must be kept to only respond to the third event.
|
|
|
|
|
static int libdelcount = 0;//Note that if anything ever changes under the hood with Qt, this will likely stop working, so adjust as accordingly.
|
|
|
|
|
static int xfupcount = 0;
|
|
|
|
|
static int xfdncount = 0;
|
|
|
|
|
|
2014-12-12 03:54:03 -05:00
|
|
|
|
if (o == ui.GLParentScrollArea && e->type() == QEvent::Resize)
|
|
|
|
|
{
|
--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.
2017-11-26 20:27:00 -05:00
|
|
|
|
m_WidthSpin->DoubleClickNonZero(ui.GLParentScrollArea->width() * ui.GLDisplay->devicePixelRatioF());
|
|
|
|
|
m_HeightSpin->DoubleClickNonZero(ui.GLParentScrollArea->height() * ui.GLDisplay->devicePixelRatioF());
|
2014-12-12 03:54:03 -05:00
|
|
|
|
}
|
2016-04-03 21:55:12 -04:00
|
|
|
|
else if (auto ke = dynamic_cast<QKeyEvent*>(e))
|
2015-03-21 18:27:37 -04:00
|
|
|
|
{
|
2018-07-31 00:39:41 -04:00
|
|
|
|
auto combo = ui.CurrentXformCombo;
|
--User changes
-Add new variations: bubbleT3D, crob, hexaplay3D, hexcrop, hexes, hexnix3D, loonie2, loonie3, nBlur, octapol and synth.
-Allow for pre/post versions of dc_bubble, dc_cylinder and dc_linear whereas before they were omitted.
-When saving a file with multiple embers in it, detect if time values are all the same and if so, start them at zero and increment by 1 for each ember.
-Allow for numerous quality increases to be coalesced into one. It will pick up at the end of the current render.
-Show selection highlight on variations tree in response to mouse hover. This makes it easier to see for which variation or param the current mouse wheel action will apply.
-Make default temporal samples be 100, whereas before it was 1000 which was overkill.
-Require the shift key to be held with delete for deleting an ember to prevent it from triggering when the user enters delete in the edit box.
-This wasn't otherwise fixable without writing a lot more code.
--Bug fixes
-EmberGenome was crashing when generating a sequence from a source file with more than 2 embers in it.
-EmberGenome was improperly handling the first frame of a merge after the last frame of the loop.
-These bugs were due to a previous commit. Revert parts of that commit.
-Prevent a zoom value of less than 0 when reading from xml.
-Slight optimization of the crescents, and mask variations, if the compiler wasn't doing it already.
-Unique file naming was broken because it was looking for _# and the default names ended with -#.
-Disallow renaming of an ember in the library tree to an empty string.
-Severe bug that prevented some variations from being read correctly from params generated outside this program.
-Severe OpenCL randomization bug. The first x coordinates of the first points in the first kernel call of the first ember of a render since the OpenCL renderer object was created were not random and were mostly -1.
-Severe bug when populating xform selection distributions that could sometimes cause a crash due to roundoff error. Fix by using double.
-Limit the max number of variations in a random ember to MAX_CL_VARS, which is 8. This ensures they'll look the same on CPU and GPU.
-Prevent user from saving stylesheet to default.qss, it's a special reserved filename.
--Code changes
-Generalize using the running sum output point inside of a variation for all cases: pre, reg and post.
-Allow for array variables in variations where the address of each element is stored in m_Params.
-Qualify all math functions with std::
-No longer use our own Clamp() in OpenCL, instead use the standard clamp().
-Redesign how functions are used in the variations OpenCL code.
-Add tests to EmberTester to verify some of the new functionality.
-Place more const and override qualifiers on functions where appropriate.
-Add a global rand with a lock to be used very sparingly.
-Use a map instead of a vector for bad param names in Xml parsing.
-Prefix affine interpolation mode defines with "AFFINE_" to make their purpose more clear.
-Allow for variations that change state during iteration by sending a separate copy of the ember to each rendering thread.
-Implement this same functionality with a local struct in OpenCL. It's members are the total of all variables that need to change state within an ember.
-Add Contains() function to Utils.h.
-EmberRender: print names of kernels being printed with --dump_kernel option.
-Clean up EmberTester to handle some of the recent changes.
-Fix various casts.
-Replace % 2 with & 1, even though the compiler was likely doing this already.
-Add new file Variations06.h to accommodate new variations.
-General cleanup.
2015-11-22 17:15:07 -05:00
|
|
|
|
bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
|
|
|
|
|
|
2015-05-03 20:13:14 -04:00
|
|
|
|
if (ke->key() >= Qt::Key_F1 && ke->key() <= Qt::Key_F32)
|
|
|
|
|
{
|
2018-07-31 00:39:41 -04:00
|
|
|
|
fcount++;
|
|
|
|
|
|
|
|
|
|
if (fcount >= 3)
|
|
|
|
|
{
|
|
|
|
|
int val = ke->key() - (int)Qt::Key_F1;
|
|
|
|
|
|
|
|
|
|
if (val < combo->count())
|
|
|
|
|
combo->setCurrentIndex(val);
|
|
|
|
|
|
|
|
|
|
fcount = 0;
|
2018-09-22 01:42:18 -04:00
|
|
|
|
//qDebug() << "global function key press: " << ke->key() << " " << o->metaObject()->className() << " " << o->objectName();
|
2018-07-31 00:39:41 -04:00
|
|
|
|
}
|
2015-05-03 20:13:14 -04:00
|
|
|
|
|
2018-07-31 00:39:41 -04:00
|
|
|
|
return true;
|
2015-05-03 20:13:14 -04:00
|
|
|
|
}
|
|
|
|
|
else if (o == ui.LibraryTree)
|
2015-03-21 18:27:37 -04:00
|
|
|
|
{
|
--User changes
-Add new variations: bubbleT3D, crob, hexaplay3D, hexcrop, hexes, hexnix3D, loonie2, loonie3, nBlur, octapol and synth.
-Allow for pre/post versions of dc_bubble, dc_cylinder and dc_linear whereas before they were omitted.
-When saving a file with multiple embers in it, detect if time values are all the same and if so, start them at zero and increment by 1 for each ember.
-Allow for numerous quality increases to be coalesced into one. It will pick up at the end of the current render.
-Show selection highlight on variations tree in response to mouse hover. This makes it easier to see for which variation or param the current mouse wheel action will apply.
-Make default temporal samples be 100, whereas before it was 1000 which was overkill.
-Require the shift key to be held with delete for deleting an ember to prevent it from triggering when the user enters delete in the edit box.
-This wasn't otherwise fixable without writing a lot more code.
--Bug fixes
-EmberGenome was crashing when generating a sequence from a source file with more than 2 embers in it.
-EmberGenome was improperly handling the first frame of a merge after the last frame of the loop.
-These bugs were due to a previous commit. Revert parts of that commit.
-Prevent a zoom value of less than 0 when reading from xml.
-Slight optimization of the crescents, and mask variations, if the compiler wasn't doing it already.
-Unique file naming was broken because it was looking for _# and the default names ended with -#.
-Disallow renaming of an ember in the library tree to an empty string.
-Severe bug that prevented some variations from being read correctly from params generated outside this program.
-Severe OpenCL randomization bug. The first x coordinates of the first points in the first kernel call of the first ember of a render since the OpenCL renderer object was created were not random and were mostly -1.
-Severe bug when populating xform selection distributions that could sometimes cause a crash due to roundoff error. Fix by using double.
-Limit the max number of variations in a random ember to MAX_CL_VARS, which is 8. This ensures they'll look the same on CPU and GPU.
-Prevent user from saving stylesheet to default.qss, it's a special reserved filename.
--Code changes
-Generalize using the running sum output point inside of a variation for all cases: pre, reg and post.
-Allow for array variables in variations where the address of each element is stored in m_Params.
-Qualify all math functions with std::
-No longer use our own Clamp() in OpenCL, instead use the standard clamp().
-Redesign how functions are used in the variations OpenCL code.
-Add tests to EmberTester to verify some of the new functionality.
-Place more const and override qualifiers on functions where appropriate.
-Add a global rand with a lock to be used very sparingly.
-Use a map instead of a vector for bad param names in Xml parsing.
-Prefix affine interpolation mode defines with "AFFINE_" to make their purpose more clear.
-Allow for variations that change state during iteration by sending a separate copy of the ember to each rendering thread.
-Implement this same functionality with a local struct in OpenCL. It's members are the total of all variables that need to change state within an ember.
-Add Contains() function to Utils.h.
-EmberRender: print names of kernels being printed with --dump_kernel option.
-Clean up EmberTester to handle some of the recent changes.
-Fix various casts.
-Replace % 2 with & 1, even though the compiler was likely doing this already.
-Add new file Variations06.h to accommodate new variations.
-General cleanup.
2015-11-22 17:15:07 -05:00
|
|
|
|
//Require shift for deleting to prevent it from triggering when the user enters delete in the edit box.
|
|
|
|
|
if (ke->key() == Qt::Key_Delete && e->type() == QEvent::KeyRelease && shift)
|
2015-03-21 18:27:37 -04:00
|
|
|
|
{
|
2018-07-31 00:39:41 -04:00
|
|
|
|
libdelcount++;
|
|
|
|
|
|
|
|
|
|
if (libdelcount >= 3)
|
|
|
|
|
{
|
|
|
|
|
auto v = GetCurrentEmberIndex();
|
|
|
|
|
|
|
|
|
|
if (ui.LibraryTree->topLevelItem(0)->childCount() > 1)
|
|
|
|
|
OnDelete(v);
|
|
|
|
|
|
|
|
|
|
libdelcount = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (o == this)
|
|
|
|
|
{
|
2018-09-30 17:20:02 -04:00
|
|
|
|
auto focusedctrl = dynamic_cast<QSpinBox*>(this->focusWidget());
|
2018-07-31 00:39:41 -04:00
|
|
|
|
|
2018-09-30 17:20:02 -04:00
|
|
|
|
if (!focusedctrl)//Doesn't seem to matter, but just to be safe.
|
2018-07-31 00:39:41 -04:00
|
|
|
|
{
|
2018-09-30 17:20:02 -04:00
|
|
|
|
unsigned int index = combo->currentIndex();
|
2018-07-31 00:39:41 -04:00
|
|
|
|
|
2018-09-30 17:20:02 -04:00
|
|
|
|
if (ke->key() == Qt::Key_Plus || ke->key() == Qt::Key_Equal)
|
2018-07-31 00:39:41 -04:00
|
|
|
|
{
|
2018-09-30 17:20:02 -04:00
|
|
|
|
xfupcount++;
|
2018-07-31 00:39:41 -04:00
|
|
|
|
|
2018-09-30 17:20:02 -04:00
|
|
|
|
if (xfupcount >= 3)
|
|
|
|
|
{
|
|
|
|
|
xfupcount = 0;
|
|
|
|
|
combo->setCurrentIndex((index + 1) % combo->count());
|
|
|
|
|
//qDebug() << "global arrow plus key press: " << ke->key() << " " << o->metaObject()->className() << " " << o->objectName();
|
|
|
|
|
}
|
2018-07-31 00:39:41 -04:00
|
|
|
|
|
2018-09-30 17:20:02 -04:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else if (ke->key() == Qt::Key_Minus || ke->key() == Qt::Key_Underscore)
|
2018-07-31 00:39:41 -04:00
|
|
|
|
{
|
2018-09-30 17:20:02 -04:00
|
|
|
|
xfdncount++;
|
2018-07-31 00:39:41 -04:00
|
|
|
|
|
2018-09-30 17:20:02 -04:00
|
|
|
|
if (xfdncount >= 3)
|
|
|
|
|
{
|
|
|
|
|
xfdncount = 0;
|
2018-07-31 00:39:41 -04:00
|
|
|
|
|
2018-09-30 17:20:02 -04:00
|
|
|
|
if (index == 0)
|
|
|
|
|
index = combo->count();
|
2015-03-21 18:27:37 -04:00
|
|
|
|
|
2018-09-30 17:20:02 -04:00
|
|
|
|
combo->setCurrentIndex((index - 1) % combo->count());
|
|
|
|
|
//qDebug() << "global arrow minus key press: " << ke->key() << " " << o->metaObject()->className() << " " << o->objectName();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2015-03-21 18:27:37 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-12-12 03:54:03 -05:00
|
|
|
|
|
|
|
|
|
return QMainWindow::eventFilter(o, e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Respond to a resize event which will set the double click default value
|
|
|
|
|
/// in the width and height spinners.
|
|
|
|
|
/// Note, this does not change the size of the ember being rendered or
|
|
|
|
|
/// the OpenGL texture it's being drawn on.
|
2014-07-08 03:11:14 -04:00
|
|
|
|
/// <param name="e">The event</param>
|
|
|
|
|
void Fractorium::resizeEvent(QResizeEvent* e)
|
|
|
|
|
{
|
--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.
2017-11-26 20:27:00 -05:00
|
|
|
|
m_WidthSpin->DoubleClickNonZero(ui.GLParentScrollArea->width() * ui.GLDisplay->devicePixelRatioF());
|
|
|
|
|
m_HeightSpin->DoubleClickNonZero(ui.GLParentScrollArea->height() * ui.GLDisplay->devicePixelRatioF());
|
2014-07-08 03:11:14 -04:00
|
|
|
|
QMainWindow::resizeEvent(e);
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-29 09:55:55 -04:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Respond to a show event to ensure Qt updates the native menubar.
|
|
|
|
|
/// On first create, Qt can fail to create the native menu bar properly,
|
|
|
|
|
/// but telling it that this window has become the focus window forces
|
|
|
|
|
/// it to refresh this.
|
|
|
|
|
/// <param name="e">The event</param>
|
2015-12-31 16:41:59 -05:00
|
|
|
|
void Fractorium::showEvent(QShowEvent* e)
|
2015-07-29 09:55:55 -04:00
|
|
|
|
{
|
2015-07-31 22:46:53 -04:00
|
|
|
|
//Tell Qt to refresh the native menubar from this widget.
|
2015-07-29 09:55:55 -04:00
|
|
|
|
emit qGuiApp->focusWindowChanged(windowHandle());
|
|
|
|
|
QMainWindow::showEvent(e);
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-08 03:11:14 -04:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Stop rendering and block before exiting.
|
|
|
|
|
/// Called on program exit.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="e">The event</param>
|
|
|
|
|
void Fractorium::closeEvent(QCloseEvent* e)
|
|
|
|
|
{
|
|
|
|
|
if (m_Controller.get())
|
|
|
|
|
{
|
|
|
|
|
m_Controller->StopRenderTimer(true);//Will wait until fully exited and stopped.
|
2016-06-11 20:47:03 -04:00
|
|
|
|
m_Controller->StopAllPreviewRenderers();
|
2014-07-08 03:11:14 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (e)
|
|
|
|
|
e->accept();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Examine the files dragged when it first enters the window area.
|
|
|
|
|
/// Ok if at least one file is .flam3, .flam3 or .xml, else not ok.
|
|
|
|
|
/// Called when the user first drags files in.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="e">The event</param>
|
|
|
|
|
void Fractorium::dragEnterEvent(QDragEnterEvent* e)
|
|
|
|
|
{
|
|
|
|
|
if (e->mimeData()->hasUrls())
|
|
|
|
|
{
|
2016-02-02 20:51:58 -05:00
|
|
|
|
auto urls = e->mimeData()->urls();
|
|
|
|
|
|
|
|
|
|
for (auto& url : urls)
|
2014-07-08 03:11:14 -04:00
|
|
|
|
{
|
|
|
|
|
QString localFile = url.toLocalFile();
|
|
|
|
|
QFileInfo fileInfo(localFile);
|
|
|
|
|
QString suf = fileInfo.suffix();
|
|
|
|
|
|
|
|
|
|
if (suf == "flam3" || suf == "flame" || suf == "xml")
|
|
|
|
|
{
|
|
|
|
|
e->accept();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Always accept drag when moving, so that the drop event will correctly be called.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="e">The event</param>
|
|
|
|
|
void Fractorium::dragMoveEvent(QDragMoveEvent* e)
|
|
|
|
|
{
|
|
|
|
|
e->accept();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Examine and open the dropped files.
|
|
|
|
|
/// Called when the user drops a file in.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="e">The event</param>
|
|
|
|
|
void Fractorium::dropEvent(QDropEvent* e)
|
|
|
|
|
{
|
|
|
|
|
QStringList filenames;
|
|
|
|
|
Qt::KeyboardModifiers mod = e->keyboardModifiers();
|
2016-03-28 21:49:10 -04:00
|
|
|
|
bool append = mod.testFlag(Qt::ControlModifier) ? false : true;
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
|
|
|
|
if (e->mimeData()->hasUrls())
|
|
|
|
|
{
|
2016-02-02 20:51:58 -05:00
|
|
|
|
auto urls = e->mimeData()->urls();
|
|
|
|
|
|
|
|
|
|
for (auto& url : urls)
|
2014-07-08 03:11:14 -04:00
|
|
|
|
{
|
|
|
|
|
QString localFile = url.toLocalFile();
|
|
|
|
|
QFileInfo fileInfo(localFile);
|
|
|
|
|
QString suf = fileInfo.suffix();
|
|
|
|
|
|
|
|
|
|
if (suf == "flam3" || suf == "flame" || suf == "xml")
|
|
|
|
|
filenames << localFile;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!filenames.empty())
|
|
|
|
|
m_Controller->OpenAndPrepFiles(filenames, append);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Setup a combo box to be placed in a table cell.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="table">The table the combo box belongs to</param>
|
|
|
|
|
/// <param name="receiver">The receiver object</param>
|
|
|
|
|
/// <param name="row">The row in the table where this combo box resides</param>
|
|
|
|
|
/// <param name="col">The col in the table where this combo box resides</param>
|
|
|
|
|
/// <param name="comboBox">Double pointer to combo box which will hold the spinner upon exit</param>
|
|
|
|
|
/// <param name="vals">The string values to populate the combo box with</param>
|
|
|
|
|
/// <param name="signal">The signal the combo box emits</param>
|
|
|
|
|
/// <param name="slot">The slot to receive the signal</param>
|
|
|
|
|
/// <param name="connectionType">Type of the connection. Default: Qt::QueuedConnection.</param>
|
2015-05-03 20:13:14 -04:00
|
|
|
|
void Fractorium::SetupCombo(QTableWidget* table, const QObject* receiver, int& row, int col, StealthComboBox*& comboBox, const vector<string>& vals, const char* signal, const char* slot, Qt::ConnectionType connectionType)
|
2014-07-08 03:11:14 -04:00
|
|
|
|
{
|
|
|
|
|
comboBox = new StealthComboBox(table);
|
2015-12-31 16:41:59 -05:00
|
|
|
|
|
2015-05-03 20:13:14 -04:00
|
|
|
|
for (auto& s : vals) comboBox->addItem(s.c_str());
|
2015-12-31 16:41:59 -05:00
|
|
|
|
|
2014-07-08 03:11:14 -04:00
|
|
|
|
table->setCellWidget(row, col, comboBox);
|
|
|
|
|
connect(comboBox, signal, receiver, slot, connectionType);
|
|
|
|
|
row++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Set the header of a table to be fixed.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="header">The header to set</param>
|
|
|
|
|
/// <param name="mode">The resizing mode to use. Default: QHeaderView::Fixed.</param>
|
|
|
|
|
void Fractorium::SetFixedTableHeader(QHeaderView* header, QHeaderView::ResizeMode mode)
|
|
|
|
|
{
|
|
|
|
|
header->setVisible(true);//For some reason, the designer keeps clobbering this value, so force it here.
|
|
|
|
|
header->setSectionsClickable(false);
|
|
|
|
|
header->setSectionResizeMode(mode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Setup and show the open XML dialog.
|
|
|
|
|
/// This will perform lazy instantiation.
|
|
|
|
|
/// </summary>
|
2017-07-22 16:43:35 -04:00
|
|
|
|
/// <returns>The list of filenames selected</returns>
|
2014-07-08 03:11:14 -04:00
|
|
|
|
QStringList Fractorium::SetupOpenXmlDialog()
|
|
|
|
|
{
|
2017-04-04 19:21:58 -04:00
|
|
|
|
#ifndef __APPLE__
|
|
|
|
|
|
2014-07-08 03:11:14 -04:00
|
|
|
|
//Lazy instantiate since it takes a long time.
|
|
|
|
|
if (!m_FileDialog)
|
|
|
|
|
{
|
|
|
|
|
m_FileDialog = new QFileDialog(this);
|
|
|
|
|
m_FileDialog->setViewMode(QFileDialog::List);
|
2018-09-30 17:20:02 -04:00
|
|
|
|
m_FileDialog->setOption(QFileDialog::DontUseNativeDialog, true);
|
2014-07-08 03:11:14 -04:00
|
|
|
|
}
|
2015-12-31 16:41:59 -05:00
|
|
|
|
|
2014-07-08 03:11:14 -04:00
|
|
|
|
QStringList filenames;
|
|
|
|
|
m_FileDialog->disconnect(SIGNAL(filterSelected(const QString&)));
|
2016-04-03 21:55:12 -04:00
|
|
|
|
connect(m_FileDialog, &QFileDialog::filterSelected, [&](const QString & filter) { m_Settings->OpenXmlExt(filter); });
|
2014-07-08 03:11:14 -04:00
|
|
|
|
m_FileDialog->setFileMode(QFileDialog::ExistingFiles);
|
|
|
|
|
m_FileDialog->setAcceptMode(QFileDialog::AcceptOpen);
|
2018-08-10 21:06:04 -04:00
|
|
|
|
m_FileDialog->setNameFilter("Flam3 (*.flam3);;Flame (*.flame);;Xml (*.xml)");
|
--User changes
-Add a palette editor.
-Add support for reading .ugr/.gradient/.gradients palette files.
-Allow toggling on spinners whose minimum value is not zero.
-Allow toggling display of image, affines and grid.
-Add new variations: cylinder2, circlesplit, tile_log, truchet_fill, waves2_radial.
--Bug fixes
-cpow2 was wrong.
-Palettes with rapid changes in color would produce slightly different outputs from Apo/Chaotica. This was due to a long standing bug from flam3.
-Use exec() on Apple and show() on all other OSes for dialog boxes.
-Trying to render a sequence with no frames would crash.
-Selecting multiple xforms and rotating them would produce the wrong rotation.
-Better handling when parsing flames using different encoding, such as unicode and UTF-8.
-Switching between SP/DP didn't reselect the selected flame in the Library tab.
--Code changes
-Make all types concerning palettes be floats, including PaletteTableWidgetItem.
-PaletteTableWidgetItem is no longer templated because all palettes are float.
-Include the source colors for user created gradients.
-Change parallel_for() calls to work with very old versions of TBB which are lingering on some systems.
-Split conditional out of accumulation loop on the CPU for better performance.
-Vectorize summing when doing density filter for better performance.
-Make all usage of palettes be of type float, double is pointless.
-Allow palettes to reside in multiple folders, while ensuring only one of each name is added.
-Refactor some palette path searching code.
-Make ReadFile() throw and catch an exception if the file operation fails.
-A little extra safety in foci and foci3D with a call to Zeps().
-Cast to (real_t) in the OpenCL string for the w variation, which was having trouble compiling on Mac.
-Fixing missing comma between paths in InitPaletteList().
-Move Xml and PaletteList classes into cpp to shorten build times when working on them.
-Remove default param values for IterOpenCLKernelCreator<T>::SharedDataIndexDefines in cpp file.
-Change more NULL to nullptr.
2017-02-26 03:02:21 -05:00
|
|
|
|
m_FileDialog->setWindowTitle("Open Flame");
|
2014-07-08 03:11:14 -04:00
|
|
|
|
m_FileDialog->setDirectory(m_Settings->OpenFolder());
|
|
|
|
|
m_FileDialog->selectNameFilter(m_Settings->OpenXmlExt());
|
|
|
|
|
|
|
|
|
|
if (m_FileDialog->exec() == QDialog::Accepted)
|
|
|
|
|
{
|
|
|
|
|
filenames = m_FileDialog->selectedFiles();
|
|
|
|
|
|
|
|
|
|
if (!filenames.empty())
|
|
|
|
|
m_Settings->OpenFolder(QFileInfo(filenames[0]).canonicalPath());
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-04 19:21:58 -04:00
|
|
|
|
#else
|
|
|
|
|
auto defaultFilter(m_Settings->OpenXmlExt());
|
2018-08-10 21:06:04 -04:00
|
|
|
|
auto filenames = QFileDialog::getOpenFileNames(this, tr("Open Flame"), m_Settings->OpenFolder(), tr("Flam3(*.flam3);; Flame(*.flame);; Xml(*.xml)"), &defaultFilter);
|
2017-04-04 19:21:58 -04:00
|
|
|
|
m_Settings->OpenXmlExt(defaultFilter);
|
|
|
|
|
|
|
|
|
|
if (!filenames.empty())
|
|
|
|
|
m_Settings->OpenFolder(QFileInfo(filenames[0]).canonicalPath());
|
|
|
|
|
|
|
|
|
|
#endif
|
2014-07-08 03:11:14 -04:00
|
|
|
|
return filenames;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Setup and show the save XML dialog.
|
|
|
|
|
/// This will perform lazy instantiation.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="defaultFilename">The default filename to populate the text box with</param>
|
|
|
|
|
/// <returns>The filename selected</returns>
|
2014-10-14 11:53:15 -04:00
|
|
|
|
QString Fractorium::SetupSaveXmlDialog(const QString& defaultFilename)
|
2014-07-08 03:11:14 -04:00
|
|
|
|
{
|
2017-04-04 19:21:58 -04:00
|
|
|
|
#ifndef __APPLE__
|
|
|
|
|
|
2014-07-08 03:11:14 -04:00
|
|
|
|
//Lazy instantiate since it takes a long time.
|
2016-04-03 21:55:12 -04:00
|
|
|
|
//QS
|
2014-07-08 03:11:14 -04:00
|
|
|
|
if (!m_FileDialog)
|
|
|
|
|
{
|
|
|
|
|
m_FileDialog = new QFileDialog(this);
|
|
|
|
|
m_FileDialog->setViewMode(QFileDialog::List);
|
2018-09-30 17:20:02 -04:00
|
|
|
|
m_FileDialog->setOption(QFileDialog::DontUseNativeDialog, true);
|
2014-07-08 03:11:14 -04:00
|
|
|
|
}
|
2015-12-31 16:41:59 -05:00
|
|
|
|
|
2014-07-08 03:11:14 -04:00
|
|
|
|
QString filename;
|
|
|
|
|
m_FileDialog->disconnect(SIGNAL(filterSelected(const QString&)));
|
2017-07-22 16:43:35 -04:00
|
|
|
|
connect(m_FileDialog, &QFileDialog::filterSelected, [&](const QString & filter)
|
|
|
|
|
{
|
|
|
|
|
m_Settings->SaveXmlExt(filter);
|
|
|
|
|
m_FileDialog->setDefaultSuffix(filter);
|
|
|
|
|
});
|
2014-07-08 03:11:14 -04:00
|
|
|
|
//This must come first because it clears various internal states which allow the file text to be properly set.
|
|
|
|
|
//This is most likely a bug in QFileDialog.
|
|
|
|
|
m_FileDialog->setAcceptMode(QFileDialog::AcceptSave);
|
|
|
|
|
m_FileDialog->selectFile(defaultFilename);
|
2018-08-10 21:06:04 -04:00
|
|
|
|
m_FileDialog->setNameFilter("Flam3 (*.flam3);;Flame (*.flame);;Xml (*.xml)");
|
2014-07-08 03:11:14 -04:00
|
|
|
|
m_FileDialog->setWindowTitle("Save flame as xml");
|
|
|
|
|
m_FileDialog->setDirectory(m_Settings->SaveFolder());
|
|
|
|
|
m_FileDialog->selectNameFilter(m_Settings->SaveXmlExt());
|
2017-07-22 16:43:35 -04:00
|
|
|
|
m_FileDialog->setDefaultSuffix(m_Settings->SaveXmlExt());
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
|
|
|
|
if (m_FileDialog->exec() == QDialog::Accepted)
|
|
|
|
|
filename = m_FileDialog->selectedFiles().value(0);
|
|
|
|
|
|
2017-04-04 19:21:58 -04:00
|
|
|
|
#else
|
|
|
|
|
auto defaultFilter(m_Settings->SaveXmlExt());
|
|
|
|
|
auto filename = QFileDialog::getSaveFileName(this, tr("Save flame as xml"), m_Settings->SaveFolder() + "/" + defaultFilename, tr("Flam3 (*.flam3);;Flame (*.flame);;Xml (*.xml)"), &defaultFilter);
|
|
|
|
|
m_Settings->SaveXmlExt(defaultFilter);
|
|
|
|
|
#endif
|
2014-07-08 03:11:14 -04:00
|
|
|
|
return filename;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Setup and show the save image dialog.
|
|
|
|
|
/// This will perform lazy instantiation.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="defaultFilename">The default filename to populate the text box with</param>
|
|
|
|
|
/// <returns>The filename selected</returns>
|
2014-10-14 11:53:15 -04:00
|
|
|
|
QString Fractorium::SetupSaveImageDialog(const QString& defaultFilename)
|
2014-07-08 03:11:14 -04:00
|
|
|
|
{
|
2017-04-04 19:21:58 -04:00
|
|
|
|
#ifndef __APPLE__
|
|
|
|
|
|
2014-07-08 03:11:14 -04:00
|
|
|
|
//Lazy instantiate since it takes a long time.
|
|
|
|
|
if (!m_FileDialog)
|
|
|
|
|
{
|
|
|
|
|
m_FileDialog = new QFileDialog(this);
|
|
|
|
|
m_FileDialog->setViewMode(QFileDialog::List);
|
2018-09-30 17:20:02 -04:00
|
|
|
|
m_FileDialog->setOption(QFileDialog::DontUseNativeDialog, true);
|
2014-07-08 03:11:14 -04:00
|
|
|
|
}
|
2015-12-31 16:41:59 -05:00
|
|
|
|
|
2014-07-08 03:11:14 -04:00
|
|
|
|
QString filename;
|
|
|
|
|
m_FileDialog->disconnect(SIGNAL(filterSelected(const QString&)));
|
2017-07-22 16:43:35 -04:00
|
|
|
|
connect(m_FileDialog, &QFileDialog::filterSelected, [&](const QString & filter)
|
|
|
|
|
{
|
|
|
|
|
m_Settings->SaveImageExt(filter);
|
|
|
|
|
m_FileDialog->setDefaultSuffix(filter);
|
|
|
|
|
});
|
2014-07-08 03:11:14 -04:00
|
|
|
|
//This must come first because it clears various internal states which allow the file text to be properly set.
|
|
|
|
|
//This is most likely a bug in QFileDialog.
|
|
|
|
|
m_FileDialog->setAcceptMode(QFileDialog::AcceptSave);
|
|
|
|
|
m_FileDialog->selectFile(defaultFilename);
|
|
|
|
|
m_FileDialog->setFileMode(QFileDialog::AnyFile);
|
|
|
|
|
m_FileDialog->setOption(QFileDialog::ShowDirsOnly, false);
|
2017-07-22 16:43:35 -04:00
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
m_FileDialog->setNameFilter(".bmp;;.jpg;;.png;;.exr");
|
|
|
|
|
#else
|
|
|
|
|
m_FileDialog->setNameFilter(".jpg;;.png;;.exr");
|
|
|
|
|
#endif
|
2014-07-08 03:11:14 -04:00
|
|
|
|
m_FileDialog->setWindowTitle("Save image");
|
|
|
|
|
m_FileDialog->setDirectory(m_Settings->SaveFolder());
|
|
|
|
|
m_FileDialog->selectNameFilter(m_Settings->SaveImageExt());
|
2017-07-22 16:43:35 -04:00
|
|
|
|
m_FileDialog->setDefaultSuffix(m_Settings->SaveImageExt());
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
|
|
|
|
if (m_FileDialog->exec() == QDialog::Accepted)
|
|
|
|
|
filename = m_FileDialog->selectedFiles().value(0);
|
|
|
|
|
|
2017-04-04 19:21:58 -04:00
|
|
|
|
#else
|
|
|
|
|
auto defaultFilter(m_Settings->SaveImageExt());
|
2017-07-22 16:43:35 -04:00
|
|
|
|
auto filename = QFileDialog::getSaveFileName(this, tr("Save image"), m_Settings->SaveFolder() + "/" + defaultFilename, tr("Jpg (*.jpg);;Png (*.png);;Exr (*.exr)"), &defaultFilter);
|
2017-04-04 19:21:58 -04:00
|
|
|
|
m_Settings->SaveImageExt(defaultFilter);
|
|
|
|
|
#endif
|
2014-07-08 03:11:14 -04:00
|
|
|
|
return filename;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Setup and show the save folder dialog.
|
|
|
|
|
/// This will perform lazy instantiation.
|
|
|
|
|
/// </summary>
|
2014-10-14 11:53:15 -04:00
|
|
|
|
/// <returns>The folder selected, with '/' appended to the end</returns>
|
2014-07-08 03:11:14 -04:00
|
|
|
|
QString Fractorium::SetupSaveFolderDialog()
|
|
|
|
|
{
|
2017-04-04 19:21:58 -04:00
|
|
|
|
#ifndef __APPLE__
|
|
|
|
|
|
2014-07-08 03:11:14 -04:00
|
|
|
|
//Lazy instantiate since it takes a long time.
|
|
|
|
|
if (!m_FolderDialog)
|
|
|
|
|
{
|
|
|
|
|
m_FolderDialog = new QFileDialog(this);
|
|
|
|
|
m_FolderDialog->setViewMode(QFileDialog::List);
|
2018-09-30 17:20:02 -04:00
|
|
|
|
m_FolderDialog->setOption(QFileDialog::DontUseNativeDialog, true);
|
2014-07-08 03:11:14 -04:00
|
|
|
|
}
|
2015-12-31 16:41:59 -05:00
|
|
|
|
|
2014-07-08 03:11:14 -04:00
|
|
|
|
QString filename;
|
|
|
|
|
//This must come first because it clears various internal states which allow the file text to be properly set.
|
|
|
|
|
//This is most likely a bug in QFileDialog.
|
|
|
|
|
m_FolderDialog->setAcceptMode(QFileDialog::AcceptSave);
|
|
|
|
|
m_FolderDialog->setFileMode(QFileDialog::Directory);
|
|
|
|
|
m_FolderDialog->setOption(QFileDialog::ShowDirsOnly, true);
|
|
|
|
|
m_FolderDialog->setOption(QFileDialog::DontUseNativeDialog, true);
|
|
|
|
|
m_FolderDialog->selectFile("");
|
|
|
|
|
m_FolderDialog->setWindowTitle("Save to folder");
|
|
|
|
|
m_FolderDialog->setDirectory(m_Settings->SaveFolder());
|
|
|
|
|
|
|
|
|
|
if (m_FolderDialog->exec() == QDialog::Accepted)
|
2014-10-14 11:53:15 -04:00
|
|
|
|
{
|
|
|
|
|
filename = MakeEnd(m_FolderDialog->selectedFiles().value(0), '/');
|
|
|
|
|
}
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
2017-04-04 19:21:58 -04:00
|
|
|
|
#else
|
|
|
|
|
auto filename = QFileDialog::getExistingDirectory(this, tr("Save to folder"),
|
|
|
|
|
m_Settings->SaveFolder(),
|
|
|
|
|
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
|
|
|
|
|
|
|
|
|
|
if (filename.size() > 0)
|
|
|
|
|
filename = MakeEnd(filename, '/');
|
|
|
|
|
|
|
|
|
|
#endif
|
2014-07-08 03:11:14 -04:00
|
|
|
|
return filename;
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-14 11:53:15 -04:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Thin wrapper around QMessageBox::critical() to allow it to be invoked from another thread.
|
|
|
|
|
/// </summary>
|
2015-05-30 00:08:44 -04:00
|
|
|
|
/// <param name="title">The title of the message box</param>
|
|
|
|
|
/// <param name="text">The text displayed on the message box</param>
|
|
|
|
|
/// <param name="invokeRequired">True if running on another thread, else false. Default: false.</param>
|
2014-10-14 11:53:15 -04:00
|
|
|
|
void Fractorium::ShowCritical(const QString& title, const QString& text, bool invokeRequired)
|
|
|
|
|
{
|
|
|
|
|
if (!invokeRequired)
|
|
|
|
|
QMessageBox::critical(this, title, text);
|
|
|
|
|
else
|
|
|
|
|
QMetaObject::invokeMethod(this, "ShowCritical", Qt::QueuedConnection, Q_ARG(const QString&, title), Q_ARG(const QString&, text), Q_ARG(bool, false));
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-28 01:25:38 -04:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Explicitly set the tab orders for the entire program.
|
|
|
|
|
/// Qt has a facility to do this, but it fails when using custom widgets in
|
|
|
|
|
/// tables, so it must be done manually here.
|
|
|
|
|
/// This list must be kept in sync with any UI changes.
|
|
|
|
|
/// </summary>
|
|
|
|
|
void Fractorium::SetTabOrders()
|
|
|
|
|
{
|
2016-06-11 20:47:03 -04:00
|
|
|
|
QWidget* w = SetTabOrder(this, ui.ColorTable, m_BrightnessSpin);//Flame color.
|
2014-07-28 01:25:38 -04:00
|
|
|
|
w = SetTabOrder(this, w, m_GammaSpin);
|
|
|
|
|
w = SetTabOrder(this, w, m_GammaThresholdSpin);
|
|
|
|
|
w = SetTabOrder(this, w, m_VibrancySpin);
|
|
|
|
|
w = SetTabOrder(this, w, m_HighlightSpin);
|
|
|
|
|
w = SetTabOrder(this, w, m_BackgroundColorButton);
|
|
|
|
|
w = SetTabOrder(this, w, m_PaletteModeCombo);
|
2016-06-11 20:47:03 -04:00
|
|
|
|
w = SetTabOrder(this, w, m_WidthSpin);//Flame geometry.
|
|
|
|
|
w = SetTabOrder(this, w, m_HeightSpin);
|
2014-07-28 01:25:38 -04:00
|
|
|
|
w = SetTabOrder(this, w, m_CenterXSpin);
|
|
|
|
|
w = SetTabOrder(this, w, m_CenterYSpin);
|
|
|
|
|
w = SetTabOrder(this, w, m_ScaleSpin);
|
|
|
|
|
w = SetTabOrder(this, w, m_ZoomSpin);
|
|
|
|
|
w = SetTabOrder(this, w, m_RotateSpin);
|
|
|
|
|
w = SetTabOrder(this, w, m_ZPosSpin);
|
|
|
|
|
w = SetTabOrder(this, w, m_PerspectiveSpin);
|
|
|
|
|
w = SetTabOrder(this, w, m_PitchSpin);
|
|
|
|
|
w = SetTabOrder(this, w, m_YawSpin);
|
|
|
|
|
w = SetTabOrder(this, w, m_DepthBlurSpin);
|
2016-06-11 20:47:03 -04:00
|
|
|
|
w = SetTabOrder(this, w, m_SpatialFilterWidthSpin);//Flame filter.
|
2014-07-28 01:25:38 -04:00
|
|
|
|
w = SetTabOrder(this, w, m_SpatialFilterTypeCombo);
|
|
|
|
|
w = SetTabOrder(this, w, m_DEFilterMinRadiusSpin);
|
|
|
|
|
w = SetTabOrder(this, w, m_DEFilterMaxRadiusSpin);
|
|
|
|
|
w = SetTabOrder(this, w, m_DECurveSpin);
|
2016-06-11 20:47:03 -04:00
|
|
|
|
w = SetTabOrder(this, w, m_SbsSpin);//Flame iteration.
|
|
|
|
|
w = SetTabOrder(this, w, m_FuseSpin);
|
2014-07-28 01:25:38 -04:00
|
|
|
|
w = SetTabOrder(this, w, m_QualitySpin);
|
|
|
|
|
w = SetTabOrder(this, w, m_SupersampleSpin);
|
2016-06-11 20:47:03 -04:00
|
|
|
|
w = SetTabOrder(this, w, m_InterpTypeCombo);//Flame animation.
|
2014-07-28 01:25:38 -04:00
|
|
|
|
w = SetTabOrder(this, w, m_AffineInterpTypeCombo);
|
2016-06-11 20:47:03 -04:00
|
|
|
|
w = SetTabOrder(this, w, m_TemporalSamplesSpin);
|
|
|
|
|
w = SetTabOrder(this, w, m_TemporalFilterWidthSpin);
|
|
|
|
|
w = SetTabOrder(this, w, m_TemporalFilterTypeCombo);
|
|
|
|
|
w = SetTabOrder(this, ui.LibraryTree, ui.SequenceStartCountSpinBox);//Library.
|
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceStartPreviewsButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceStopPreviewsButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceStartFlameSpinBox);
|
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceStopFlameSpinBox);
|
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceAllButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceRandomizeStaggerCheckBox);
|
2016-06-11 22:01:19 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceStaggerSpinBox);
|
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceRandomStaggerMaxSpinBox);
|
2016-06-11 20:47:03 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceRandomizeRotationsCheckBox);
|
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceRotationsSpinBox);
|
--User changes
-Add a palette editor.
-Add support for reading .ugr/.gradient/.gradients palette files.
-Allow toggling on spinners whose minimum value is not zero.
-Allow toggling display of image, affines and grid.
-Add new variations: cylinder2, circlesplit, tile_log, truchet_fill, waves2_radial.
--Bug fixes
-cpow2 was wrong.
-Palettes with rapid changes in color would produce slightly different outputs from Apo/Chaotica. This was due to a long standing bug from flam3.
-Use exec() on Apple and show() on all other OSes for dialog boxes.
-Trying to render a sequence with no frames would crash.
-Selecting multiple xforms and rotating them would produce the wrong rotation.
-Better handling when parsing flames using different encoding, such as unicode and UTF-8.
-Switching between SP/DP didn't reselect the selected flame in the Library tab.
--Code changes
-Make all types concerning palettes be floats, including PaletteTableWidgetItem.
-PaletteTableWidgetItem is no longer templated because all palettes are float.
-Include the source colors for user created gradients.
-Change parallel_for() calls to work with very old versions of TBB which are lingering on some systems.
-Split conditional out of accumulation loop on the CPU for better performance.
-Vectorize summing when doing density filter for better performance.
-Make all usage of palettes be of type float, double is pointless.
-Allow palettes to reside in multiple folders, while ensuring only one of each name is added.
-Refactor some palette path searching code.
-Make ReadFile() throw and catch an exception if the file operation fails.
-A little extra safety in foci and foci3D with a call to Zeps().
-Cast to (real_t) in the OpenCL string for the w variation, which was having trouble compiling on Mac.
-Fixing missing comma between paths in InitPaletteList().
-Move Xml and PaletteList classes into cpp to shorten build times when working on them.
-Remove default param values for IterOpenCLKernelCreator<T>::SharedDataIndexDefines in cpp file.
-Change more NULL to nullptr.
2017-02-26 03:02:21 -05:00
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceRotationsCWCheckBox);
|
2016-06-11 20:47:03 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceRandomRotationsMaxSpinBox);
|
--User changes
-Add a palette editor.
-Add support for reading .ugr/.gradient/.gradients palette files.
-Allow toggling on spinners whose minimum value is not zero.
-Allow toggling display of image, affines and grid.
-Add new variations: cylinder2, circlesplit, tile_log, truchet_fill, waves2_radial.
--Bug fixes
-cpow2 was wrong.
-Palettes with rapid changes in color would produce slightly different outputs from Apo/Chaotica. This was due to a long standing bug from flam3.
-Use exec() on Apple and show() on all other OSes for dialog boxes.
-Trying to render a sequence with no frames would crash.
-Selecting multiple xforms and rotating them would produce the wrong rotation.
-Better handling when parsing flames using different encoding, such as unicode and UTF-8.
-Switching between SP/DP didn't reselect the selected flame in the Library tab.
--Code changes
-Make all types concerning palettes be floats, including PaletteTableWidgetItem.
-PaletteTableWidgetItem is no longer templated because all palettes are float.
-Include the source colors for user created gradients.
-Change parallel_for() calls to work with very old versions of TBB which are lingering on some systems.
-Split conditional out of accumulation loop on the CPU for better performance.
-Vectorize summing when doing density filter for better performance.
-Make all usage of palettes be of type float, double is pointless.
-Allow palettes to reside in multiple folders, while ensuring only one of each name is added.
-Refactor some palette path searching code.
-Make ReadFile() throw and catch an exception if the file operation fails.
-A little extra safety in foci and foci3D with a call to Zeps().
-Cast to (real_t) in the OpenCL string for the w variation, which was having trouble compiling on Mac.
-Fixing missing comma between paths in InitPaletteList().
-Move Xml and PaletteList classes into cpp to shorten build times when working on them.
-Remove default param values for IterOpenCLKernelCreator<T>::SharedDataIndexDefines in cpp file.
-Change more NULL to nullptr.
2017-02-26 03:02:21 -05:00
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceRandomizeFramesPerRotCheckBox);
|
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceFramesPerRotSpinBox);
|
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceRandomFramesPerRotMaxSpinBox);
|
2016-06-11 20:47:03 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceRandomizeBlendFramesCheckBox);
|
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceBlendFramesSpinBox);
|
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceRandomBlendMaxFramesSpinBox);
|
--User changes
-Add a palette editor.
-Add support for reading .ugr/.gradient/.gradients palette files.
-Allow toggling on spinners whose minimum value is not zero.
-Allow toggling display of image, affines and grid.
-Add new variations: cylinder2, circlesplit, tile_log, truchet_fill, waves2_radial.
--Bug fixes
-cpow2 was wrong.
-Palettes with rapid changes in color would produce slightly different outputs from Apo/Chaotica. This was due to a long standing bug from flam3.
-Use exec() on Apple and show() on all other OSes for dialog boxes.
-Trying to render a sequence with no frames would crash.
-Selecting multiple xforms and rotating them would produce the wrong rotation.
-Better handling when parsing flames using different encoding, such as unicode and UTF-8.
-Switching between SP/DP didn't reselect the selected flame in the Library tab.
--Code changes
-Make all types concerning palettes be floats, including PaletteTableWidgetItem.
-PaletteTableWidgetItem is no longer templated because all palettes are float.
-Include the source colors for user created gradients.
-Change parallel_for() calls to work with very old versions of TBB which are lingering on some systems.
-Split conditional out of accumulation loop on the CPU for better performance.
-Vectorize summing when doing density filter for better performance.
-Make all usage of palettes be of type float, double is pointless.
-Allow palettes to reside in multiple folders, while ensuring only one of each name is added.
-Refactor some palette path searching code.
-Make ReadFile() throw and catch an exception if the file operation fails.
-A little extra safety in foci and foci3D with a call to Zeps().
-Cast to (real_t) in the OpenCL string for the w variation, which was having trouble compiling on Mac.
-Fixing missing comma between paths in InitPaletteList().
-Move Xml and PaletteList classes into cpp to shorten build times when working on them.
-Remove default param values for IterOpenCLKernelCreator<T>::SharedDataIndexDefines in cpp file.
-Change more NULL to nullptr.
2017-02-26 03:02:21 -05:00
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceRandomizeRotationsPerBlendCheckBox);
|
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceRotationsPerBlendSpinBox);
|
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceRotationsPerBlendCWCheckBox);
|
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceRotationsPerBlendMaxSpinBox);
|
2016-06-11 20:47:03 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceGenerateButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceRenderButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceSaveButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceOpenButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.SequenceTree);
|
2014-07-28 01:25:38 -04:00
|
|
|
|
w = SetTabOrder(this, ui.CurrentXformCombo, ui.AddXformButton);//Xforms.
|
2016-06-11 20:47:03 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.AddLinkedXformButton);
|
2014-07-28 01:25:38 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.DuplicateXformButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.ClearXformButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.DeleteXformButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.AddFinalXformButton);
|
|
|
|
|
w = SetTabOrder(this, w, m_XformWeightSpin);
|
|
|
|
|
w = SetTabOrder(this, w, m_XformWeightSpinnerButtonWidget->m_Button);
|
|
|
|
|
w = SetTabOrder(this, m_XformColorIndexSpin, ui.XformColorScroll);//Xforms color.
|
2016-06-11 20:47:03 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.RandomColorIndicesButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.ToggleColorIndicesButton);
|
2014-07-28 01:25:38 -04:00
|
|
|
|
w = SetTabOrder(this, w, m_XformColorSpeedSpin);
|
|
|
|
|
w = SetTabOrder(this, w, m_XformOpacitySpin);
|
|
|
|
|
w = SetTabOrder(this, w, m_XformDirectColorSpin);
|
|
|
|
|
w = SetTabOrder(this, w, ui.SoloXformCheckBox);
|
2016-06-11 20:47:03 -04:00
|
|
|
|
w = SetTabOrder(this, w, m_PreX1Spin);
|
2014-07-28 01:25:38 -04:00
|
|
|
|
w = SetTabOrder(this, w, m_PreX2Spin);
|
|
|
|
|
w = SetTabOrder(this, w, m_PreY1Spin);
|
|
|
|
|
w = SetTabOrder(this, w, m_PreY2Spin);
|
|
|
|
|
w = SetTabOrder(this, w, m_PreO1Spin);
|
|
|
|
|
w = SetTabOrder(this, w, m_PreO2Spin);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PreFlipVerticalButton);
|
2018-09-15 06:11:12 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.PreCopyButton);
|
2014-07-28 01:25:38 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.PreResetButton);
|
2018-09-15 06:11:12 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.PrePasteButton);
|
2014-07-28 01:25:38 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.PreFlipHorizontalButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PreRotate90CcButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PreRotateCcButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PreRotateCombo);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PreRotateCButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PreRotate90CButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PreMoveUpButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PreMoveDownButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PreMoveCombo);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PreMoveLeftButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PreMoveRightButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PreScaleDownButton);
|
2015-06-20 14:35:08 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.PreScaleCombo);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PreScaleUpButton);
|
2016-06-11 20:47:03 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.PreRandomButton);
|
2014-07-28 01:25:38 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.ShowPreAffineCurrentRadio);
|
|
|
|
|
w = SetTabOrder(this, w, ui.ShowPreAffineAllRadio);
|
2018-09-30 17:20:02 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.SwapAffinesButton);
|
2014-07-28 01:25:38 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.PostAffineGroupBox);
|
|
|
|
|
w = SetTabOrder(this, w, m_PostX1Spin);
|
|
|
|
|
w = SetTabOrder(this, w, m_PostX2Spin);
|
|
|
|
|
w = SetTabOrder(this, w, m_PostY1Spin);
|
|
|
|
|
w = SetTabOrder(this, w, m_PostY2Spin);
|
|
|
|
|
w = SetTabOrder(this, w, m_PostO1Spin);
|
|
|
|
|
w = SetTabOrder(this, w, m_PostO2Spin);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PostFlipVerticalButton);
|
2018-09-15 06:11:12 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.PostCopyButton);
|
2014-07-28 01:25:38 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.PostResetButton);
|
2018-09-15 06:11:12 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.PostPasteButton);
|
2014-07-28 01:25:38 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.PostFlipHorizontalButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PostRotate90CcButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PostRotateCcButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PostRotateCombo);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PostRotateCButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PostRotate90CButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PostMoveUpButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PostMoveDownButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PostMoveCombo);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PostMoveLeftButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PostMoveRightButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PostScaleDownButton);
|
2015-06-20 14:35:08 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.PostScaleCombo);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PostScaleUpButton);
|
2016-06-11 20:47:03 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.PostRandomButton);
|
2014-07-28 01:25:38 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.ShowPostAffineCurrentRadio);
|
|
|
|
|
w = SetTabOrder(this, w, ui.ShowPostAffineAllRadio);
|
2016-06-11 20:47:03 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.PolarAffineCheckBox);
|
2014-07-28 01:25:38 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.LocalPivotRadio);
|
|
|
|
|
w = SetTabOrder(this, w, ui.WorldPivotRadio);
|
|
|
|
|
w = SetTabOrder(this, ui.VariationsFilterLineEdit, ui.VariationsFilterClearButton);//Xforms variation.
|
|
|
|
|
w = SetTabOrder(this, w, ui.VariationsTree);
|
2018-07-31 00:39:41 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.ClearXaosButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.RandomXaosButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.AddLayerButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.AddLayerSpinBox);
|
2014-07-28 01:25:38 -04:00
|
|
|
|
//Xforms xaos is done dynamically every time.
|
2016-06-11 20:47:03 -04:00
|
|
|
|
w = SetTabOrder(this, ui.PaletteFilenameCombo, m_PaletteHueSpin);//Palette.
|
|
|
|
|
w = SetTabOrder(this, w, m_PaletteContrastSpin);
|
2014-07-28 01:25:38 -04:00
|
|
|
|
w = SetTabOrder(this, w, m_PaletteSaturationSpin);
|
|
|
|
|
w = SetTabOrder(this, w, m_PaletteBlurSpin);
|
|
|
|
|
w = SetTabOrder(this, w, m_PaletteBrightnessSpin);
|
|
|
|
|
w = SetTabOrder(this, w, m_PaletteFrequencySpin);
|
--User changes
-Add a palette editor.
-Add support for reading .ugr/.gradient/.gradients palette files.
-Allow toggling on spinners whose minimum value is not zero.
-Allow toggling display of image, affines and grid.
-Add new variations: cylinder2, circlesplit, tile_log, truchet_fill, waves2_radial.
--Bug fixes
-cpow2 was wrong.
-Palettes with rapid changes in color would produce slightly different outputs from Apo/Chaotica. This was due to a long standing bug from flam3.
-Use exec() on Apple and show() on all other OSes for dialog boxes.
-Trying to render a sequence with no frames would crash.
-Selecting multiple xforms and rotating them would produce the wrong rotation.
-Better handling when parsing flames using different encoding, such as unicode and UTF-8.
-Switching between SP/DP didn't reselect the selected flame in the Library tab.
--Code changes
-Make all types concerning palettes be floats, including PaletteTableWidgetItem.
-PaletteTableWidgetItem is no longer templated because all palettes are float.
-Include the source colors for user created gradients.
-Change parallel_for() calls to work with very old versions of TBB which are lingering on some systems.
-Split conditional out of accumulation loop on the CPU for better performance.
-Vectorize summing when doing density filter for better performance.
-Make all usage of palettes be of type float, double is pointless.
-Allow palettes to reside in multiple folders, while ensuring only one of each name is added.
-Refactor some palette path searching code.
-Make ReadFile() throw and catch an exception if the file operation fails.
-A little extra safety in foci and foci3D with a call to Zeps().
-Cast to (real_t) in the OpenCL string for the w variation, which was having trouble compiling on Mac.
-Fixing missing comma between paths in InitPaletteList().
-Move Xml and PaletteList classes into cpp to shorten build times when working on them.
-Remove default param values for IterOpenCLKernelCreator<T>::SharedDataIndexDefines in cpp file.
-Change more NULL to nullptr.
2017-02-26 03:02:21 -05:00
|
|
|
|
w = SetTabOrder(this, w, ui.PaletteRandomSelectButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PaletteRandomAdjustButton);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PaletteEditorButton);
|
2015-06-17 23:05:53 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.PaletteFilterLineEdit);
|
|
|
|
|
w = SetTabOrder(this, w, ui.PaletteFilterClearButton);
|
2014-07-28 01:25:38 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.PaletteListTable);
|
2016-06-11 20:47:03 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.ResetCurvesButton);//Palette curves.
|
|
|
|
|
w = SetTabOrder(this, w, ui.CurvesView);
|
|
|
|
|
w = SetTabOrder(this, w, ui.CurvesGroupBox);
|
|
|
|
|
w = SetTabOrder(this, w, ui.CurvesAllRadio);
|
|
|
|
|
w = SetTabOrder(this, w, ui.CurvesRedRadio);
|
|
|
|
|
w = SetTabOrder(this, w, ui.CurvesGreenRadio);
|
|
|
|
|
w = SetTabOrder(this, w, ui.CurvesBlueRadio);
|
2015-10-27 00:31:35 -04:00
|
|
|
|
w = SetTabOrder(this, ui.SummaryTable, ui.SummaryTree);//Info summary.
|
2015-07-15 23:27:32 -04:00
|
|
|
|
w = SetTabOrder(this, ui.InfoBoundsGroupBox, ui.InfoBoundsFrame);//Info bounds.
|
2014-07-28 01:25:38 -04:00
|
|
|
|
w = SetTabOrder(this, w, ui.InfoBoundsTable);
|
|
|
|
|
w = SetTabOrder(this, w, ui.InfoFileOpeningGroupBox);
|
|
|
|
|
w = SetTabOrder(this, w, ui.InfoFileOpeningTextEdit);
|
|
|
|
|
w = SetTabOrder(this, w, ui.InfoRenderingGroupBox);
|
|
|
|
|
w = SetTabOrder(this, w, ui.InfoRenderingTextEdit);
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-15 21:45:15 -04:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Toggle all table spinner values in one row.
|
|
|
|
|
/// The logic is:
|
|
|
|
|
/// If any cell in the row is non zero, set all cells to zero, else 1.
|
|
|
|
|
/// If shift is held down, reverse the logic.
|
|
|
|
|
/// Resets the rendering process.
|
|
|
|
|
/// </summary>
|
2015-05-30 00:08:44 -04:00
|
|
|
|
/// <param name="table">The QTableWidget or QTableView whose row will be toggled</param>
|
2015-05-15 21:45:15 -04:00
|
|
|
|
/// <param name="logicalIndex">The index of the row that was double clicked</param>
|
2015-05-30 00:08:44 -04:00
|
|
|
|
void Fractorium::ToggleTableRow(QTableView* table, int logicalIndex)
|
2015-05-15 21:45:15 -04:00
|
|
|
|
{
|
|
|
|
|
bool allZero = true;
|
2015-05-30 00:08:44 -04:00
|
|
|
|
auto model = table->model();
|
|
|
|
|
int cols = model->columnCount();
|
2015-05-15 21:45:15 -04:00
|
|
|
|
bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
|
2015-07-23 21:16:36 -04:00
|
|
|
|
auto tableWidget = qobject_cast<QTableWidget*>(table);
|
2015-05-15 21:45:15 -04:00
|
|
|
|
|
2015-05-30 00:08:44 -04:00
|
|
|
|
if (tableWidget)
|
2015-05-15 21:45:15 -04:00
|
|
|
|
{
|
2015-05-30 00:08:44 -04:00
|
|
|
|
for (int i = 0; i < cols; i++)
|
2015-05-15 21:45:15 -04:00
|
|
|
|
{
|
2016-02-20 21:44:52 -05:00
|
|
|
|
if (auto spinBox = qobject_cast<DoubleSpinBox*>(tableWidget->cellWidget(logicalIndex, i)))
|
2015-05-30 00:08:44 -04:00
|
|
|
|
{
|
|
|
|
|
if (!IsNearZero(spinBox->value()))
|
|
|
|
|
{
|
|
|
|
|
allZero = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (shift)
|
|
|
|
|
allZero = !allZero;
|
|
|
|
|
|
|
|
|
|
double val = allZero ? 1.0 : 0.0;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < cols; i++)
|
2016-02-20 21:44:52 -05:00
|
|
|
|
if (auto spinBox = qobject_cast<DoubleSpinBox*>(tableWidget->cellWidget(logicalIndex, i)))
|
2015-05-30 00:08:44 -04:00
|
|
|
|
spinBox->setValue(val);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < cols; i++)
|
|
|
|
|
{
|
|
|
|
|
if (!IsNearZero(model->data(model->index(logicalIndex, i, QModelIndex())).toDouble()))
|
2015-05-15 21:45:15 -04:00
|
|
|
|
{
|
|
|
|
|
allZero = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-30 00:08:44 -04:00
|
|
|
|
if (shift)
|
|
|
|
|
allZero = !allZero;
|
2015-05-15 21:45:15 -04:00
|
|
|
|
|
2015-05-30 00:08:44 -04:00
|
|
|
|
double val = allZero ? 1.0 : 0.0;
|
2015-05-15 21:45:15 -04:00
|
|
|
|
|
2015-05-30 00:08:44 -04:00
|
|
|
|
for (int i = 0; i < cols; i++)
|
|
|
|
|
model->setData(model->index(logicalIndex, i), val, Qt::EditRole);
|
|
|
|
|
}
|
2015-05-15 21:45:15 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Toggle all table spinner values in one column.
|
|
|
|
|
/// The logic is:
|
|
|
|
|
/// If any cell in the column is non zero, set all cells to zero, else 1.
|
|
|
|
|
/// If shift is held down, reverse the logic.
|
|
|
|
|
/// Resets the rendering process.
|
|
|
|
|
/// </summary>
|
2015-05-30 00:08:44 -04:00
|
|
|
|
/// <param name="table">The QTableWidget or QTableView whose column will be toggled</param>
|
2015-05-15 21:45:15 -04:00
|
|
|
|
/// <param name="logicalIndex">The index of the column that was double clicked</param>
|
2015-05-30 00:08:44 -04:00
|
|
|
|
void Fractorium::ToggleTableCol(QTableView* table, int logicalIndex)
|
2015-05-15 21:45:15 -04:00
|
|
|
|
{
|
|
|
|
|
bool allZero = true;
|
2015-05-30 00:08:44 -04:00
|
|
|
|
auto model = table->model();
|
|
|
|
|
int rows = model->rowCount();
|
2015-05-15 21:45:15 -04:00
|
|
|
|
bool shift = QGuiApplication::keyboardModifiers().testFlag(Qt::ShiftModifier);
|
2015-07-23 21:16:36 -04:00
|
|
|
|
auto tableWidget = qobject_cast<QTableWidget*>(table);
|
2015-05-15 21:45:15 -04:00
|
|
|
|
|
2015-05-30 00:08:44 -04:00
|
|
|
|
if (tableWidget)
|
2015-05-15 21:45:15 -04:00
|
|
|
|
{
|
2015-05-30 00:08:44 -04:00
|
|
|
|
for (int i = 0; i < rows; i++)
|
2015-05-15 21:45:15 -04:00
|
|
|
|
{
|
2016-02-20 21:44:52 -05:00
|
|
|
|
if (auto spinBox = qobject_cast<DoubleSpinBox*>(tableWidget->cellWidget(i, logicalIndex)))
|
2015-05-15 21:45:15 -04:00
|
|
|
|
{
|
2015-05-30 00:08:44 -04:00
|
|
|
|
if (!IsNearZero(spinBox->value()))
|
|
|
|
|
{
|
|
|
|
|
allZero = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2015-05-15 21:45:15 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-30 00:08:44 -04:00
|
|
|
|
if (shift)
|
|
|
|
|
allZero = !allZero;
|
2015-05-15 21:45:15 -04:00
|
|
|
|
|
2015-05-30 00:08:44 -04:00
|
|
|
|
double val = allZero ? 1.0 : 0.0;
|
2015-05-15 21:45:15 -04:00
|
|
|
|
|
2015-05-30 00:08:44 -04:00
|
|
|
|
for (int i = 0; i < rows; i++)
|
2016-02-20 21:44:52 -05:00
|
|
|
|
if (auto spinBox = qobject_cast<DoubleSpinBox*>(tableWidget->cellWidget(i, logicalIndex)))
|
2015-05-30 00:08:44 -04:00
|
|
|
|
spinBox->setValue(val);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < rows; i++)
|
|
|
|
|
{
|
|
|
|
|
if (!IsNearZero(model->data(model->index(i, logicalIndex, QModelIndex())).toDouble()))
|
|
|
|
|
{
|
|
|
|
|
allZero = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-05-15 21:45:15 -04:00
|
|
|
|
|
2015-05-30 00:08:44 -04:00
|
|
|
|
if (shift)
|
|
|
|
|
allZero = !allZero;
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
2015-05-30 00:08:44 -04:00
|
|
|
|
double val = allZero ? 1.0 : 0.0;
|
2014-07-08 03:11:14 -04:00
|
|
|
|
|
2015-05-30 00:08:44 -04:00
|
|
|
|
for (int i = 0; i < rows; i++)
|
|
|
|
|
model->setData(model->index(i, logicalIndex), val, Qt::EditRole);
|
|
|
|
|
}
|
2014-07-08 03:11:14 -04:00
|
|
|
|
}
|
|
|
|
|
|
2014-12-11 00:50:15 -05:00
|
|
|
|
template class FractoriumEmberController<float>;
|
|
|
|
|
|
|
|
|
|
#ifdef DO_DOUBLE
|
|
|
|
|
template class FractoriumEmberController<double>;
|
|
|
|
|
#endif
|