#include "FractoriumPch.h" #include "Fractorium.h" /// /// 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. /// /// The parent widget of this item Fractorium::Fractorium(QWidget* p) : QMainWindow(p) { int spinHeight = 20, iconSize_ = 9; size_t i = 0; Timing t; ui.setupUi(this); qRegisterMetaType>("QVector");//For previews. qRegisterMetaType>("vector"); qRegisterMetaType("EmberTreeWidgetItemBase*"); m_FontSize = 9; m_VarSortMode = 1;//Sort by weight by default. m_ColorDialog = new QColorDialog(this); m_Settings = new FractoriumSettings(this); m_FileDialog = nullptr;//Use lazy instantiation upon first use. m_FolderDialog = nullptr; m_FinalRenderDialog = new FractoriumFinalRenderDialog(m_Settings, this); m_OptionsDialog = new FractoriumOptionsDialog(m_Settings, this); m_AboutDialog = new FractoriumAboutDialog(this); //Put the about dialog in the screen center. const QRect screen = QApplication::desktop()->screenGeometry(); m_AboutDialog->move(screen.center() - m_AboutDialog->rect().center()); //The options dialog should be a fixed size without a size grip, however even if it's here, it still shows up. Perhaps Qt will fix it some day. m_OptionsDialog->layout()->setSizeConstraint(QLayout::SetFixedSize); m_OptionsDialog->setSizeGripEnabled(false); connect(m_ColorDialog, SIGNAL(colorSelected(const QColor&)), this, SLOT(OnColorSelected(const QColor&)), Qt::QueuedConnection); 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); for (i = 0; i < XFORM_COLOR_COUNT; i++) { QPixmap pixmap(iconSize_, iconSize_); pixmap.fill(m_XformComboColors[i]); m_XformComboIcons[i] = QIcon(pixmap); } QPixmap pixmap(iconSize_, iconSize_); pixmap.fill(m_FinalXformComboColor); m_FinalXformComboIcon = QIcon(pixmap); InitToolbarUI(); InitParamsUI(); InitXformsUI(); InitXformsColorUI(); InitXformsAffineUI(); InitXformsVariationsUI(); InitXformsSelectUI(); InitXaosUI(); InitPaletteUI(); InitLibraryUI(); 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 if (m_Settings->Double()) m_Controller = unique_ptr(new FractoriumEmberController(this)); else #endif m_Controller = unique_ptr(new FractoriumEmberController(this)); if (m_Wrapper.CheckOpenCL() && m_Settings->OpenCL() && m_QualitySpin->value() < 30) m_QualitySpin->setValue(30); int statusBarHeight = 20 * devicePixelRatio(); ui.statusBar->setMinimumHeight(statusBarHeight); ui.statusBar->setMaximumHeight(statusBarHeight); m_RenderStatusLabel = new QLabel(this); m_RenderStatusLabel->setMinimumWidth(200); m_RenderStatusLabel->setAlignment(Qt::AlignRight); ui.statusBar->addPermanentWidget(m_RenderStatusLabel); m_CoordinateStatusLabel = new QLabel(this); m_CoordinateStatusLabel->setMinimumWidth(300); m_CoordinateStatusLabel->setMaximumWidth(300); m_CoordinateStatusLabel->setAlignment(Qt::AlignLeft); ui.statusBar->addWidget(m_CoordinateStatusLabel); 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); ui.statusBar->addPermanentWidget(m_ProgressBar); //Setup pointer in the GL window to point back to here. ui.GLDisplay->SetMainWindow(this); showMaximized();//This won't fully set things up and show them until after this constructor exits. connect(ui.DockWidget, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(dockLocationChanged(Qt::DockWidgetArea))); connect(ui.DockWidget, SIGNAL(topLevelChanged(bool)), this, SLOT(OnDockTopLevelChanged(bool))); //Always ensure the library tab is selected, which will show preview renders. ui.ParamsTabWidget->setCurrentIndex(0); ui.XformsTabWidget->setCurrentIndex(2);//Make variations tab the currently selected one under the Xforms tab. //Setting certain values will completely throw off the GUI, doing everything //from setting strange margins, to arbitrarily changing the fonts used. //For these cases, the only way to fix the problem is to use style sheets. ui.ColorTable->setStyleSheet("QTableWidget::item { padding: 1px; }"); ui.GeometryTable->setStyleSheet("QTableWidget::item { padding: 1px; }"); ui.FilterTable->setStyleSheet("QTableWidget::item { padding: 1px; }"); ui.IterationTable->setStyleSheet("QTableWidget::item { padding: 1px; }"); ui.XformAffineTab->setStyleSheet("QTableWidget::item { padding: 1px; }"); ui.XformWeightNameTable->setStyleSheet("QTableWidget::item { padding: 0px; }"); ui.XformColorIndexTable->setStyleSheet("QTableWidget::item { padding: 1px; }"); ui.XformColorValuesTable->setStyleSheet("QTableWidget::item { padding: 1px; }"); ui.XformPaletteRefTable->setStyleSheet("QTableWidget::item { padding: 0px; border: none; margin: 0px; }"); ui.PaletteAdjustTable->setStyleSheet("QTableWidget::item { padding: 1px; }");//Need this to avoid covering the top border pixel with the spinners. ui.statusBar->setStyleSheet("QStatusBar QLabel { padding-left: 2px; padding-right: 2px; }"); //setStyleSheet("QGroupBox { border: 2px solid gray; border-radius: 3px; } "); m_PreviousPaletteRow = -1;//Force click handler the first time through. SetCoordinateStatus(0, 0, 0, 0); SetTabOrders(); //At this point, everything has been setup except the renderer. Shortly after //this constructor exits, GLWidget::InitGL() will create the initial flock and start the rendering timer //which executes whenever the program is idle. Upon starting the timer, the renderer //will be initialized. QTimer::singleShot(500, [&]() { ui.GLDisplay->InitGL(); }); } /// /// Destructor which saves out the settings file. /// All other memory is cleared automatically through the use of STL. /// Fractorium::~Fractorium() { m_Settings->sync(); } /// /// Set the coordinate text in the status bar. /// /// The raster x coordinate /// The raster y coordinate /// The cartesian world x coordinate /// The cartesian world y coordinate void Fractorium::SetCoordinateStatus(int rasX, int rasY, float worldX, float worldY) { //Use sprintf rather than allocating and concatenating 6 QStrings for efficiency since this is called on every mouse move. sprintf_s(m_CoordinateString, 128, "Window: %4d, %4d World: %2.2f, %2.2f", rasX, rasY, worldX, worldY); m_CoordinateStatusLabel->setText(QString(m_CoordinateString)); } /// /// Center the scroll area. /// Called in response to a resizing, or setting of new ember. /// void Fractorium::CenterScrollbars() { QScrollBar* w = ui.GLParentScrollArea->horizontalScrollBar(); QScrollBar* h = ui.GLParentScrollArea->verticalScrollBar(); w->setValue(w->maximum() / 2); h->setValue(h->maximum() / 2); } /// /// Apply the settings for saving an ember to an Xml file to an ember (presumably about to be saved). /// /// The ember to apply the settings to template void FractoriumEmberController::ApplyXmlSavingTemplate(Ember& ember) { ember.m_Quality = m_Fractorium->m_Settings->XmlQuality(); ember.m_Supersample = m_Fractorium->m_Settings->XmlSupersample(); ember.m_TemporalSamples = m_Fractorium->m_Settings->XmlTemporalSamples(); } /// /// Return whether the current ember contains a final xform and the GUI is aware of it. /// /// True if the current ember contains a final xform, else false. bool Fractorium::HaveFinal() { QComboBox* combo = ui.CurrentXformCombo; return (combo->count() > 0 && combo->itemText(combo->count() - 1) == "Final"); } /// /// Slots. /// /// /// 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. /// /// True if top level, else false. void Fractorium::OnDockTopLevelChanged(bool topLevel) { //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); } /// /// 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. /// /// The dock widget area void Fractorium::dockLocationChanged(Qt::DockWidgetArea area) { //ui.DockWidget->resize(500, ui.DockWidget->height()); //ui.DockWidget->update(); //ui.dockWidget->setFloating(true); //ui.dockWidget->setFloating(false); } /// /// Virtual event overrides. /// /// /// Event filter for taking special action on: /// Dock widget resize events, which in turn trigger GLParentScrollArea events. /// Library tree key events, specifically delete. /// /// The object /// The eevent /// false bool Fractorium::eventFilter(QObject* o, QEvent* e) { if (o == ui.GLParentScrollArea && e->type() == QEvent::Resize) { m_WidthSpin->DoubleClickNonZero(ui.GLParentScrollArea->width()); m_HeightSpin->DoubleClickNonZero(ui.GLParentScrollArea->height()); } else if (QKeyEvent* ke = dynamic_cast(e)) { if (ke->key() >= Qt::Key_F1 && ke->key() <= Qt::Key_F32) { int val = ke->key() - (int)Qt::Key_F1; if (val < ui.CurrentXformCombo->count()) ui.CurrentXformCombo->setCurrentIndex(val); } else if (o == ui.LibraryTree) { if (ke->key() == Qt::Key_Delete && e->type() == QEvent::KeyRelease) { auto p = GetCurrentEmberIndex(); if (ui.LibraryTree->topLevelItem(0)->childCount() > 1 && p.second) OnDelete(p); } } } return QMainWindow::eventFilter(o, e); } /// /// 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. /// The event void Fractorium::resizeEvent(QResizeEvent* e) { m_WidthSpin->DoubleClickNonZero(ui.GLParentScrollArea->width()); m_HeightSpin->DoubleClickNonZero(ui.GLParentScrollArea->height()); QMainWindow::resizeEvent(e); } /// /// Stop rendering and block before exiting. /// Called on program exit. /// /// The event void Fractorium::closeEvent(QCloseEvent* e) { if (m_Controller.get()) { m_Controller->StopRenderTimer(true);//Will wait until fully exited and stopped. m_Controller->StopPreviewRender(); } if (e) e->accept(); } /// /// 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. /// /// The event void Fractorium::dragEnterEvent(QDragEnterEvent* e) { if (e->mimeData()->hasUrls()) { foreach (QUrl url, e->mimeData()->urls()) { QString localFile = url.toLocalFile(); QFileInfo fileInfo(localFile); QString suf = fileInfo.suffix(); if (suf == "flam3" || suf == "flame" || suf == "xml") { e->accept(); break; } } } } /// /// Always accept drag when moving, so that the drop event will correctly be called. /// /// The event void Fractorium::dragMoveEvent(QDragMoveEvent* e) { e->accept(); } /// /// Examine and open the dropped files. /// Called when the user drops a file in. /// /// The event void Fractorium::dropEvent(QDropEvent* e) { QStringList filenames; Qt::KeyboardModifiers mod = e->keyboardModifiers(); bool append = mod.testFlag(Qt::ControlModifier) ? true : false; if (e->mimeData()->hasUrls()) { foreach (QUrl url, e->mimeData()->urls()) { 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); } /// /// Setup a combo box to be placed in a table cell. /// /// The table the combo box belongs to /// The receiver object /// The row in the table where this combo box resides /// The col in the table where this combo box resides /// Double pointer to combo box which will hold the spinner upon exit /// The string values to populate the combo box with /// The signal the combo box emits /// The slot to receive the signal /// Type of the connection. Default: Qt::QueuedConnection. void Fractorium::SetupCombo(QTableWidget* table, const QObject* receiver, int& row, int col, StealthComboBox*& comboBox, const vector& vals, const char* signal, const char* slot, Qt::ConnectionType connectionType) { comboBox = new StealthComboBox(table); for (auto& s : vals) comboBox->addItem(s.c_str()); table->setCellWidget(row, col, comboBox); connect(comboBox, signal, receiver, slot, connectionType); row++; } /// /// Set the header of a table to be fixed. /// /// The header to set /// The resizing mode to use. Default: QHeaderView::Fixed. 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); } /// /// Setup and show the open XML dialog. /// This will perform lazy instantiation. /// /// The filename selected QStringList Fractorium::SetupOpenXmlDialog() { //Lazy instantiate since it takes a long time. if (!m_FileDialog) { m_FileDialog = new QFileDialog(this); m_FileDialog->setViewMode(QFileDialog::List); } if (!m_FileDialog) return QStringList(""); QStringList filenames; m_FileDialog->disconnect(SIGNAL(filterSelected(const QString&))); connect(m_FileDialog, &QFileDialog::filterSelected, [=](const QString &filter) { m_Settings->OpenXmlExt(filter); }); m_FileDialog->setFileMode(QFileDialog::ExistingFiles); m_FileDialog->setAcceptMode(QFileDialog::AcceptOpen); m_FileDialog->setNameFilter("Flam3 (*.flam3);;Flame (*.flame);;Xml (*.xml)"); m_FileDialog->setWindowTitle("Open flame"); 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()); } return filenames; } /// /// Setup and show the save XML dialog. /// This will perform lazy instantiation. /// /// The default filename to populate the text box with /// The filename selected QString Fractorium::SetupSaveXmlDialog(const QString& defaultFilename) { //Lazy instantiate since it takes a long time. if (!m_FileDialog) { m_FileDialog = new QFileDialog(this); m_FileDialog->setViewMode(QFileDialog::List); } if (!m_FileDialog) return ""; QString filename; m_FileDialog->disconnect(SIGNAL(filterSelected(const QString&))); connect(m_FileDialog, &QFileDialog::filterSelected, [=](const QString &filter) { m_Settings->SaveXmlExt(filter); }); //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->setNameFilter("Flam3 (*.flam3);;Flame (*.flame);;Xml (*.xml)"); m_FileDialog->setWindowTitle("Save flame as xml"); m_FileDialog->setDirectory(m_Settings->SaveFolder()); m_FileDialog->selectNameFilter(m_Settings->SaveXmlExt()); if (m_FileDialog->exec() == QDialog::Accepted) filename = m_FileDialog->selectedFiles().value(0); return filename; } /// /// Setup and show the save image dialog. /// This will perform lazy instantiation. /// /// The default filename to populate the text box with /// The filename selected QString Fractorium::SetupSaveImageDialog(const QString& defaultFilename) { //Lazy instantiate since it takes a long time. if (!m_FileDialog) { m_FileDialog = new QFileDialog(this); m_FileDialog->setViewMode(QFileDialog::List); } if (!m_FileDialog) return ""; QString filename; m_FileDialog->disconnect(SIGNAL(filterSelected(const QString&))); connect(m_FileDialog, &QFileDialog::filterSelected, [=](const QString &filter) { m_Settings->SaveImageExt(filter); }); //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); m_FileDialog->setOption(QFileDialog::DontUseNativeDialog, false); m_FileDialog->setNameFilter("Jpeg (*.jpg);;Png (*.png);;Bmp (*.bmp)"); m_FileDialog->setWindowTitle("Save image"); m_FileDialog->setDirectory(m_Settings->SaveFolder()); m_FileDialog->selectNameFilter(m_Settings->SaveImageExt()); if (m_FileDialog->exec() == QDialog::Accepted) filename = m_FileDialog->selectedFiles().value(0); return filename; } /// /// Setup and show the save folder dialog. /// This will perform lazy instantiation. /// /// The folder selected, with '/' appended to the end QString Fractorium::SetupSaveFolderDialog() { //Lazy instantiate since it takes a long time. if (!m_FolderDialog) { m_FolderDialog = new QFileDialog(this); m_FolderDialog->setViewMode(QFileDialog::List); } if (!m_FolderDialog) return ""; 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) { filename = MakeEnd(m_FolderDialog->selectedFiles().value(0), '/'); } return filename; } /// /// Thin wrapper around QMessageBox::critical() to allow it to be invoked from another thread. /// 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)); } } /// /// 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. /// void Fractorium::SetTabOrders() { QWidget* w = SetTabOrder(this, ui.ColorTable, m_BrightnessSpin);//Flame. 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); 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); w = SetTabOrder(this, w, m_SpatialFilterWidthSpin); w = SetTabOrder(this, w, m_SpatialFilterTypeCombo); w = SetTabOrder(this, w, m_TemporalFilterTypeCombo); w = SetTabOrder(this, w, m_DEFilterMinRadiusSpin); w = SetTabOrder(this, w, m_DEFilterMaxRadiusSpin); w = SetTabOrder(this, w, m_DECurveSpin); w = SetTabOrder(this, w, m_TemporalSamplesSpin); w = SetTabOrder(this, w, m_QualitySpin); w = SetTabOrder(this, w, m_SupersampleSpin); w = SetTabOrder(this, w, m_AffineInterpTypeCombo); w = SetTabOrder(this, w, m_InterpTypeCombo); w = SetTabOrder(this, ui.CurrentXformCombo, ui.AddXformButton);//Xforms. 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. w = SetTabOrder(this, w, m_XformColorSpeedSpin); w = SetTabOrder(this, w, m_XformOpacitySpin); w = SetTabOrder(this, w, m_XformDirectColorSpin); w = SetTabOrder(this, w, ui.SoloXformCheckBox); w = SetTabOrder(this, ui.PreAffineGroupBox, m_PreX1Spin);//Xforms affine. 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); w = SetTabOrder(this, w, ui.PreResetButton); 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.PreScaleUpButton); w = SetTabOrder(this, w, ui.PreScaleCombo); w = SetTabOrder(this, w, ui.PreScaleDownButton); w = SetTabOrder(this, w, ui.ShowPreAffineCurrentRadio); w = SetTabOrder(this, w, ui.ShowPreAffineAllRadio); 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); w = SetTabOrder(this, w, ui.PostResetButton); 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.PostScaleUpButton); w = SetTabOrder(this, w, ui.PostScaleCombo); w = SetTabOrder(this, w, ui.PostScaleDownButton); w = SetTabOrder(this, w, ui.ShowPostAffineCurrentRadio); w = SetTabOrder(this, w, ui.ShowPostAffineAllRadio); 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); //Xforms xaos is done dynamically every time. w = SetTabOrder(this, m_PaletteHueSpin, m_PaletteContrastSpin);//Palette. w = SetTabOrder(this, w, m_PaletteSaturationSpin); w = SetTabOrder(this, w, m_PaletteBlurSpin); w = SetTabOrder(this, w, m_PaletteBrightnessSpin); w = SetTabOrder(this, w, m_PaletteFrequencySpin); w = SetTabOrder(this, w, ui.PaletteListTable); w = SetTabOrder(this, ui.InfoBoundsGroupBox, ui.InfoBoundsFrame);//Info. 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); } /// /// This is no longer needed and was used to compensate for a different bug /// however the code is interesting, so keep it around for possible future use. /// This was used to correct a rotation bug where matrix rotation comes out in the wrong direction /// if x1, y1 (a & d) are on the left side of the line from 0,0 to /// x2, y2 (b, e). In that case, the angle must be flipped. In order /// to determine which side of the line it's on, create a mat2 /// and find its determinant. Values > 0 are on the left side of the line. /// /// The affine. /// int Fractorium::FlipDet(Affine2D& affine) { float x1 = affine.A(); float y1 = affine.D(); float x2 = affine.B(); float y2 = affine.E(); //Just make the other end of the line be the center of the circle. glm::mat2 mat( 0 - x1, 0 - y1,//Col 0. x2 - x1, y2 - y1);//Col 1. return (glm::determinant(mat) > 0) ? -1 : 1; } //template//See note at the end of Fractorium.h //void Fractorium::SetupSpinner(QTableWidget* table, const QObject* receiver, int& row, int col, spinType*& spinBox, int height, valType min, valType max, valType step, const char* signal, const char* slot, bool incRow, valType val, valType doubleClickZero, valType doubleClickNonZero) //{ // spinBox = new spinType(table, height, step); // spinBox->setRange(min, max); // spinBox->setValue(val); // table->setCellWidget(row, col, spinBox); // // if (string(signal) != "" && string(slot) != "") // connect(spinBox, signal, receiver, slot, connectionType); // // if (doubleClickNonZero != -999 && doubleClickZero != -999) // { // spinBox->DoubleClick(true); // spinBox->DoubleClickZero((valType)doubleClickZero); // spinBox->DoubleClickNonZero((valType)doubleClickNonZero); // } // // if (incRow) // row++; //} template class FractoriumEmberController; #ifdef DO_DOUBLE template class FractoriumEmberController; #endif