diff --git a/Builds/MSVC/VS2013/Fractorium.vcxproj b/Builds/MSVC/VS2013/Fractorium.vcxproj index 7ce3a14..d4c16dc 100644 --- a/Builds/MSVC/VS2013/Fractorium.vcxproj +++ b/Builds/MSVC/VS2013/Fractorium.vcxproj @@ -326,6 +326,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)" + diff --git a/Builds/MSVC/VS2013/Fractorium.vcxproj.filters b/Builds/MSVC/VS2013/Fractorium.vcxproj.filters index c96ba9b..853fb83 100644 --- a/Builds/MSVC/VS2013/Fractorium.vcxproj.filters +++ b/Builds/MSVC/VS2013/Fractorium.vcxproj.filters @@ -244,6 +244,9 @@ MainWindows + + MainWindows + diff --git a/Source/Ember/Ember.h b/Source/Ember/Ember.h index b361cbc..0a373ea 100644 --- a/Source/Ember/Ember.h +++ b/Source/Ember/Ember.h @@ -621,7 +621,7 @@ public: normalizedWeights.resize(m_Xforms.size()); ForEach(m_Xforms, [&](Xform& xform) { norm += xform.m_Weight; }); - ForEach(normalizedWeights, [&](T& weight) { weight = m_Xforms[i].m_Weight / norm; i++; }); + ForEach(normalizedWeights, [&](T& weight) { weight = (norm == T(0) ? T(0) : m_Xforms[i].m_Weight / norm); i++; }); } /// diff --git a/Source/Ember/Isaac.h b/Source/Ember/Isaac.h index ad5c4fa..887e5bd 100644 --- a/Source/Ember/Isaac.h +++ b/Source/Ember/Isaac.h @@ -199,7 +199,7 @@ public: /// A random 0 or 1 inline uint RandBit() { - return Rand() & 1; + return RandByte() & 1; } /// diff --git a/Source/Fractorium/Fractorium.cpp b/Source/Fractorium/Fractorium.cpp index 6b752d3..734728a 100644 --- a/Source/Fractorium/Fractorium.cpp +++ b/Source/Fractorium/Fractorium.cpp @@ -76,7 +76,8 @@ Fractorium::Fractorium(QWidget* p) InitXformsColorUI(); InitXformsAffineUI(); InitXformsVariationsUI(); - InitXformsXaosUI(); + InitXformsSelectUI(); + InitXaosUI(); InitPaletteUI(); InitLibraryUI(); InitMenusUI(); diff --git a/Source/Fractorium/Fractorium.h b/Source/Fractorium/Fractorium.h index 70b3ed5..b68c798 100644 --- a/Source/Fractorium/Fractorium.h +++ b/Source/Fractorium/Fractorium.h @@ -241,7 +241,11 @@ public slots: void OnVariationsFilterLineEditTextChanged(const QString& text); void OnVariationsFilterClearButtonClicked(bool checked); - //Xforms Xaos. + //Xforms Selection. + void OnXformsSelectAllButtonClicked(bool checked); + void OnXformsSelectNoneButtonClicked(bool checked); + + //Xaos. void OnXaosChanged(double d); void OnClearXaosButtonClicked(bool checked); void OnRandomXaosButtonClicked(bool checked); @@ -287,7 +291,8 @@ private: void InitXformsColorUI(); void InitXformsAffineUI(); void InitXformsVariationsUI(); - void InitXformsXaosUI(); + void InitXformsSelectUI(); + void InitXaosUI(); void InitPaletteUI(); void InitLibraryUI(); void SetTabOrders(); @@ -301,7 +306,6 @@ private: //Params. //Xforms. - void FillXforms(); //Xforms Color. @@ -309,7 +313,11 @@ private: //Xforms Variations. - //Xforms Xaos. + //Xforms Selection. + void ClearXformsSelections(); + void ForEachXformCheckbox(std::function func); + + //Xaos. void FillXaosTable(); //Palette. @@ -371,6 +379,8 @@ private: //Xforms. DoubleSpinBox* m_XformWeightSpin; SpinnerButtonWidget* m_XformWeightSpinnerButtonWidget; + QFormLayout* m_XformsSelectionLayout; + QVector m_XformSelections; //Xforms Color. QTableWidgetItem* m_XformColorValueItem; diff --git a/Source/Fractorium/Fractorium.ui b/Source/Fractorium/Fractorium.ui index 3f633e4..f279ce6 100644 --- a/Source/Fractorium/Fractorium.ui +++ b/Source/Fractorium/Fractorium.ui @@ -74,7 +74,7 @@ 0 0 - 923 + 926 942 @@ -1900,342 +1900,6 @@ 4 - - - - - 2 - 0 - - - - - 0 - 45 - - - - - 16777215 - 45 - - - - - true - - - - Qt::StrongFocus - - - false - - - QFrame::Panel - - - QFrame::Plain - - - Qt::ScrollBarAlwaysOff - - - Qt::ScrollBarAlwaysOff - - - false - - - QAbstractItemView::DoubleClicked|QAbstractItemView::SelectedClicked - - - false - - - QAbstractItemView::NoSelection - - - QAbstractItemView::ScrollPerPixel - - - QAbstractItemView::ScrollPerPixel - - - false - - - false - - - 1 - - - 2 - - - false - - - false - - - 27 - - - true - - - false - - - 21 - - - false - - - 21 - - - - - Weight - - - - - Name - - - - - AlignLeft|AlignVCenter - - - - - AlignLeft|AlignVCenter - - - - - - - - 2 - - - - - - 0 - 22 - - - - - 60 - 22 - - - - - 60 - 16777215 - - - - Current xform - - - false - - - 12 - - - - 9 - 9 - - - - true - - - - - - - - 0 - 0 - - - - - 25 - 24 - - - - - 16777215 - 24 - - - - Add xform - - - - - - - :/Fractorium/Icons/add.png:/Fractorium/Icons/add.png - - - - - - - - 0 - 0 - - - - - 25 - 24 - - - - - 16777215 - 24 - - - - Duplicate xform - - - - - - - :/Fractorium/Icons/editraise.png:/Fractorium/Icons/editraise.png - - - - - - - - 0 - 0 - - - - - 25 - 24 - - - - - 16777215 - 24 - - - - Clear xform variations - - - - - - - :/Fractorium/Icons/eraser.png:/Fractorium/Icons/eraser.png - - - - - - - - 0 - 0 - - - - - 25 - 24 - - - - - 16777215 - 24 - - - - Delete xform - - - - - - - :/Fractorium/Icons/del.png:/Fractorium/Icons/del.png - - - - - - - - 0 - 0 - - - - - 58 - 24 - - - - - 58 - 24 - - - - - 0 - 0 - - - - Add final xform - - - Qt::LeftToRight - - - Final - - - - :/Fractorium/Icons/add.png:/Fractorium/Icons/add.png - - - - - @@ -2255,6 +1919,9 @@ MS Shell Dlg 2 + + + true @@ -2294,6 +1961,9 @@ SpinBox 0 + + Color palette index for the current xform, and curve adjustment. + Color @@ -2912,6 +2582,9 @@ SpinBox 0 + + Affine transforms for the current xform. + true @@ -4270,12 +3943,12 @@ SpinBox + + Full list of available variations and their weights for the currently selected xform. + Variations - - Full list of available variations and their weights for the currently selected xform. - 6 @@ -4326,6 +3999,9 @@ SpinBox X + + false + @@ -4497,218 +4173,701 @@ SpinBox - + + + Select multiple xforms to apply operations to. + - Xaos + Select - + - 4 + 3 - 6 + 5 - 6 + 4 - 6 + 5 - 6 + 4 - + + + Select All + + + + + + + Select None + + + + + - + 0 0 - - - 0 - 67 - + + Select Xforms - - - 16777215 - 16777215 - + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - true - - - - Qt::NoFocus - - - - - - QFrame::Panel - - - QFrame::Plain - - - 1 - - - Qt::ScrollBarAsNeeded - - - Qt::ScrollBarAsNeeded - - - QAbstractScrollArea::AdjustToContents - - - false - - - QAbstractItemView::NoEditTriggers - - - false - - - false - - - false - - - false - - - QAbstractItemView::NoSelection - - - QAbstractItemView::ScrollPerPixel - - - QAbstractItemView::ScrollPerPixel - - - true - - - Qt::SolidLine - - - false - - - false - - - 3 - - - false - - - true - - - 40 - - - false - - - 15 - - - false - - - false - - - true - - - 22 - - - false - - - 22 - - - false - - - - + + + 0 - - - - F1 + + 0 - - - - F2 + + 0 - - - - F3 + + 0 - - - - + + 0 - - - - - - - - - - - + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Plain + + + QAbstractScrollArea::AdjustToContents + + + true + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + 0 + 0 + 245 + 682 + + + + + 0 + 0 + + + + + 6 + + + 6 + + + 6 + + + 6 + + + 6 + + + + + DeleteMeCheckBox + + + + + + + CheckBox + + + + + + + + - - - - - - Set all xaos values in all xforms to 1 - - - Clear Xaos - - - - - - - Randomize all xaos values in all xforms - - - Random Xaos - - - - - + + + + 2 + + + + + + 0 + 22 + + + + + 60 + 22 + + + + + 60 + 16777215 + + + + Current xform + + + false + + + 12 + + + + 9 + 9 + + + + true + + + + + + + + 0 + 0 + + + + + 25 + 24 + + + + + 16777215 + 24 + + + + Add xform + + + + + + + :/Fractorium/Icons/add.png:/Fractorium/Icons/add.png + + + + + + + + 0 + 0 + + + + + 25 + 24 + + + + + 16777215 + 24 + + + + Duplicate selected xforms + + + + + + + :/Fractorium/Icons/editraise.png:/Fractorium/Icons/editraise.png + + + + + + + + 0 + 0 + + + + + 25 + 24 + + + + + 16777215 + 24 + + + + Clear selected xforms' variations + + + + + + + :/Fractorium/Icons/eraser.png:/Fractorium/Icons/eraser.png + + + + + + + + 0 + 0 + + + + + 25 + 24 + + + + + 16777215 + 24 + + + + Delete selected xforms + + + + + + + :/Fractorium/Icons/del.png:/Fractorium/Icons/del.png + + + + + + + + 0 + 0 + + + + + 58 + 24 + + + + + 58 + 24 + + + + + 0 + 0 + + + + Add final xform + + + Qt::LeftToRight + + + Final + + + + :/Fractorium/Icons/add.png:/Fractorium/Icons/add.png + + + + + + + + + + 2 + 0 + + + + + 0 + 45 + + + + + 16777215 + 45 + + + + + true + + + + Qt::StrongFocus + + + false + + + QFrame::Panel + + + QFrame::Plain + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + false + + + QAbstractItemView::DoubleClicked|QAbstractItemView::SelectedClicked + + + false + + + QAbstractItemView::NoSelection + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + false + + + false + + + 1 + + + 2 + + + false + + + false + + + 27 + + + true + + + false + + + 21 + + + false + + + 21 + + + + + Weight + + + + + Name + + + + + AlignLeft|AlignVCenter + + + + + AlignLeft|AlignVCenter + + + + + + + + + true + + + Xaos + + + + 4 + + + 5 + + + 5 + + + 5 + + + 4 + + + + + + 0 + 0 + + + + + 0 + 67 + + + + + 16777215 + 16777215 + + + + + true + + + + Qt::NoFocus + + + + + + QFrame::Panel + + + QFrame::Plain + + + 1 + + + Qt::ScrollBarAsNeeded + + + Qt::ScrollBarAsNeeded + + + QAbstractScrollArea::AdjustToContents + + + false + + + QAbstractItemView::NoEditTriggers + + + false + + + false + + + false + + + false + + + QAbstractItemView::NoSelection + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + true + + + Qt::SolidLine + + + false + + + false + + + 3 + + + false + + + true + + + 35 + + + false + + + 35 + + + false + + + false + + + true + + + 22 + + + false + + + 22 + + + false + + + + + + + + + F1 + + + + + F2 + + + + + F3 + + + + + + + + + + + + + + + + + + + + + + + + + Set all xaos values in all xforms to 1 + + + Clear Xaos + + + + + + + Randomize all xaos values in all xforms + + + Random Xaos + + + + + @@ -5073,6 +5232,9 @@ SpinBox 24 + + Apply a random adjustment to the current palette + Random Adjustment @@ -6022,7 +6184,6 @@ SpinBox VariationsFilterLineEdit VariationsFilterClearButton VariationsTree - XaosTable PaletteAdjustTable PaletteListTable scrollArea_5 diff --git a/Source/Fractorium/FractoriumEmberController.cpp b/Source/Fractorium/FractoriumEmberController.cpp index 4fb3b8b..8185357 100644 --- a/Source/Fractorium/FractoriumEmberController.cpp +++ b/Source/Fractorium/FractoriumEmberController.cpp @@ -224,21 +224,79 @@ void FractoriumEmberController::Update(std::function func, bool } /// -/// Wrapper to call a function on the current xform, then optionally add the requested action to the rendering queue. +/// Wrapper to call a function on the specified xforms, then optionally add the requested action to the rendering queue. +/// If no xforms are selected via the checkboxes, and the update type is UPDATE_SELECTED, then the function will be called only on the currently selected xform. /// /// The function to call +/// Whether to apply this update operation on the current, all or selected xforms. Default: UPDATE_CURRENT. /// True to update renderer, else false. Default: true. /// The action to add to the rendering queue. Default: FULL_RENDER. template -void FractoriumEmberController::UpdateCurrentXform(std::function*)> func, bool updateRender, eProcessAction action) +void FractoriumEmberController::UpdateXform(std::function*)> func, eXformUpdate updateType, bool updateRender, eProcessAction action) { - if (Xform* xform = CurrentXform()) - { - func(xform); + size_t i = 0; + bool isCurrentFinal = m_Ember.IsFinalXform(CurrentXform()); + bool doFinal = updateType != eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL && updateType != eXformUpdate::UPDATE_ALL_EXCEPT_FINAL; - if (updateRender) - UpdateRender(action); + switch (updateType) + { + case eXformUpdate::UPDATE_CURRENT: + { + if (Xform* xform = CurrentXform()) + func(xform); + } + break; + + case eXformUpdate::UPDATE_SELECTED: + case eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL: + { + bool anyUpdated = false; + + while (Xform* xform = (doFinal ? m_Ember.GetTotalXform(i) : m_Ember.GetXform(i))) + { + if (QLayoutItem* child = m_Fractorium->m_XformsSelectionLayout->itemAt(i)) + { + if (auto* w = dynamic_cast(child->widget())) + { + if (w->isChecked()) + { + func(xform); + anyUpdated = true; + } + } + } + + i++; + } + + if (!anyUpdated)//None were selected, so just apply to the current. + if (doFinal || !isCurrentFinal)//If do final, call func regardless. If not, only call if current is not final. + if (Xform* xform = CurrentXform()) + func(xform); + } + + break; + + case eXformUpdate::UPDATE_ALL: + { + while (Xform* xform = m_Ember.GetTotalXform(i++)) + func(xform); + } + + break; + + case eXformUpdate::UPDATE_ALL_EXCEPT_FINAL: + default: + { + while (Xform* xform = m_Ember.GetXform(i++)) + func(xform); + } + + break; } + + if (updateRender) + UpdateRender(action); } /// @@ -276,7 +334,7 @@ void FractoriumEmberController::SetEmberPrivate(const Ember& ember, bool v #endif m_GLController->ResetMouseState(); - m_Fractorium->FillXforms();//Must do this first because the palette setup in FillParamTablesAndPalette() uses the xforms combo. + FillXforms();//Must do this first because the palette setup in FillParamTablesAndPalette() uses the xforms combo. FillParamTablesAndPalette(); //If a resize happened, this won't do anything because the new size is not reflected in the scroll area yet. diff --git a/Source/Fractorium/FractoriumEmberController.h b/Source/Fractorium/FractoriumEmberController.h index 937936a..db9dd5a 100644 --- a/Source/Fractorium/FractoriumEmberController.h +++ b/Source/Fractorium/FractoriumEmberController.h @@ -11,7 +11,12 @@ /// /// An enum representing the type of edit being done. /// -enum eEditUndoState : uint { REGULAR_EDIT = 0, UNDO_REDO = 1, EDIT_UNDO = 2 }; +enum eEditUndoState : uint { REGULAR_EDIT, UNDO_REDO, EDIT_UNDO }; + +/// +/// An enum representing which xforms an update should be applied to. +/// +enum eXformUpdate : uint { UPDATE_CURRENT, UPDATE_SELECTED, UPDATE_SELECTED_EXCEPT_FINAL, UPDATE_ALL, UPDATE_ALL_EXCEPT_FINAL }; /// /// FractoriumEmberController and Fractorium need each other, but each can't include the other. @@ -58,8 +63,8 @@ public: //virtual void Clear() { } virtual void AddXform() { } virtual void DuplicateXform() { } - virtual void ClearCurrentXform() { } - virtual void DeleteCurrentXform() { } + virtual void ClearXform() { } + virtual void DeleteXforms() { } virtual void AddFinalXform() { } virtual bool UseFinalXform() { return false; } virtual size_t XformCount() const { return 0; } @@ -145,20 +150,23 @@ public: virtual void AffineInterpTypeChanged(int i) { } virtual void InterpTypeChanged(int i) { } virtual void BackgroundChanged(const QColor& color) { } - + virtual void ClearColorCurves() { } + virtual void ColorCurveChanged(int curveIndex, int pointInxed, const QPointF& point) { } + //Xforms. virtual void CurrentXformComboChanged(int index) { } virtual void XformWeightChanged(double d) { } virtual void EqualizeWeights() { } virtual void XformNameChanged(int row, int col) { } + virtual void FillXforms() { } //Xforms Affine. virtual void AffineSetHelper(double d, int index, bool pre) { } - virtual void FlipCurrentXform(bool horizontal, bool vertical, bool pre) { } - virtual void RotateCurrentXformByAngle(double angle, bool pre) { } - virtual void MoveCurrentXform(double x, double y, bool pre) { } - virtual void ScaleCurrentXform(double scale, bool pre) { } - virtual void ResetCurrentXformAffine(bool pre) { } + virtual void FlipXforms(bool horizontal, bool vertical, bool pre) { } + virtual void RotateXformsByAngle(double angle, bool pre) { } + virtual void MoveXforms(double x, double y, bool pre) { } + virtual void ScaleXforms(double scale, bool pre) { } + virtual void ResetXformsAffine(bool pre) { } virtual void FillBothAffines() { } //Xforms Color. @@ -167,8 +175,6 @@ public: virtual void XformColorSpeedChanged(double d) { } virtual void XformOpacityChanged(double d) { } virtual void XformDirectColorChanged(double d) { } - virtual void ClearColorCurves() { } - virtual void ColorCurveChanged(int curveIndex, int pointInxed, const QPointF& point) { }//need to put this in a different section because it's not xform specific.//TODO void SetPaletteRefTable(QPixmap* pixmap); //Xforms Variations. @@ -176,7 +182,9 @@ public: virtual void ClearVariationsTree() { } virtual void VariationSpinBoxValueChanged(double d) { } - //Xforms Xaos. + //Xforms Selection. + + //Xaos. virtual void FillXaos() { } virtual QString MakeXaosNameString(uint i) { return ""; } virtual void XaosChanged(DoubleSpinBox* sender) { } @@ -281,8 +289,8 @@ public: //virtual void Clear() override { } virtual void AddXform() override; virtual void DuplicateXform() override; - virtual void ClearCurrentXform() override; - virtual void DeleteCurrentXform() override; + virtual void ClearXform() override; + virtual void DeleteXforms() override; virtual void AddFinalXform() override; virtual bool UseFinalXform() override { return m_Ember.UseFinalXform(); } //virtual bool IsFinal(uint i) { return false; } @@ -371,22 +379,25 @@ public: virtual void AffineInterpTypeChanged(int index) override; virtual void InterpTypeChanged(int index) override; virtual void BackgroundChanged(const QColor& col) override; + virtual void ClearColorCurves() override; + virtual void ColorCurveChanged(int curveIndex, int pointInxed, const QPointF& point) override; //Xforms. virtual void CurrentXformComboChanged(int index) override; virtual void XformWeightChanged(double d) override; virtual void EqualizeWeights() override; virtual void XformNameChanged(int row, int col) override; + virtual void FillXforms() override; void FillWithXform(Xform* xform); Xform* CurrentXform(); //Xforms Affine. virtual void AffineSetHelper(double d, int index, bool pre) override; - virtual void FlipCurrentXform(bool horizontal, bool vertical, bool pre) override; - virtual void RotateCurrentXformByAngle(double angle, bool pre) override; - virtual void MoveCurrentXform(double x, double y, bool pre) override; - virtual void ScaleCurrentXform(double scale, bool pre) override; - virtual void ResetCurrentXformAffine(bool pre) override; + virtual void FlipXforms(bool horizontal, bool vertical, bool pre) override; + virtual void RotateXformsByAngle(double angle, bool pre) override; + virtual void MoveXforms(double x, double y, bool pre) override; + virtual void ScaleXforms(double scale, bool pre) override; + virtual void ResetXformsAffine(bool pre) override; virtual void FillBothAffines() override; void FillAffineWithXform(Xform* xform, bool pre); @@ -396,8 +407,6 @@ public: virtual void XformColorSpeedChanged(double d) override; virtual void XformOpacityChanged(double d) override; virtual void XformDirectColorChanged(double d) override; - virtual void ClearColorCurves() override; - virtual void ColorCurveChanged(int curveIndex, int pointInxed, const QPointF& point) override; void FillColorWithXform(Xform* xform); //Xforms Variations. @@ -444,15 +453,20 @@ private: bool IsFinal(Xform* xform); //Xforms Color. - void SetCurrentXformColorIndex(double d); + void SetCurrentXformColorIndex(double d, bool updateRender); void FillCurvesControl(); + //Xforms Selection. + QString MakeXformCaption(size_t i); + bool XformCheckboxAt(int i, std::function func); + bool XformCheckboxAt(Xform* xform, std::function func); + void UpdateXform(std::function*)> func, eXformUpdate updateType = eXformUpdate::UPDATE_CURRENT, bool updateRender = true, eProcessAction action = FULL_RENDER); + //Palette. void UpdateAdjustedPaletteGUI(Palette& palette); //Rendering/progress. void Update(std::function func, bool updateRender = true, eProcessAction action = FULL_RENDER); - void UpdateCurrentXform(std::function*)> func, bool updateRender = true, eProcessAction action = FULL_RENDER); bool SyncSizes(); //Templated members. diff --git a/Source/Fractorium/FractoriumMenus.cpp b/Source/Fractorium/FractoriumMenus.cpp index d97b960..8ae234a 100644 --- a/Source/Fractorium/FractoriumMenus.cpp +++ b/Source/Fractorium/FractoriumMenus.cpp @@ -618,10 +618,12 @@ void FractoriumEmberController::AddReflectiveSymmetry() { QComboBox* combo = m_Fractorium->ui.CurrentXformCombo; - m_Ember.AddSymmetry(-1, m_Rand); - m_Fractorium->FillXforms(); - combo->setCurrentIndex(combo->count() - (m_Fractorium->HaveFinal() ? 2 : 1));//Set index to the last item before final. - UpdateRender(); + Update([&]() + { + m_Ember.AddSymmetry(-1, m_Rand); + FillXforms(); + combo->setCurrentIndex(combo->count() - (m_Fractorium->HaveFinal() ? 2 : 1));//Set index to the last item before final. + }); } void Fractorium::OnActionAddReflectiveSymmetry(bool checked) { m_Controller->AddReflectiveSymmetry(); } @@ -635,10 +637,12 @@ void FractoriumEmberController::AddRotationalSymmetry() { QComboBox* combo = m_Fractorium->ui.CurrentXformCombo; - m_Ember.AddSymmetry(2, m_Rand); - m_Fractorium->FillXforms(); - combo->setCurrentIndex(combo->count() - (m_Fractorium->HaveFinal() ? 2 : 1));//Set index to the last item before final. - UpdateRender(); + Update([&]() + { + m_Ember.AddSymmetry(2, m_Rand); + FillXforms(); + combo->setCurrentIndex(combo->count() - (m_Fractorium->HaveFinal() ? 2 : 1));//Set index to the last item before final. + }); } void Fractorium::OnActionAddRotationalSymmetry(bool checked) { m_Controller->AddRotationalSymmetry(); } @@ -652,10 +656,12 @@ void FractoriumEmberController::AddBothSymmetry() { QComboBox* combo = m_Fractorium->ui.CurrentXformCombo; - m_Ember.AddSymmetry(-2, m_Rand); - m_Fractorium->FillXforms(); - combo->setCurrentIndex(combo->count() - (m_Fractorium->HaveFinal() ? 2 : 1));//Set index to the last item before final. - UpdateRender(); + Update([&]() + { + m_Ember.AddSymmetry(-2, m_Rand); + FillXforms(); + combo->setCurrentIndex(combo->count() - (m_Fractorium->HaveFinal() ? 2 : 1));//Set index to the last item before final. + }); } void Fractorium::OnActionAddBothSymmetry(bool checked) { m_Controller->AddBothSymmetry(); } @@ -665,7 +671,7 @@ void Fractorium::OnActionAddBothSymmetry(bool checked) { m_Controller->AddBothSy /// Resets the rendering process. /// template -void FractoriumEmberController::Flatten() { UpdateCurrentXform([&] (Xform* xform) { m_Ember.Flatten(XmlToEmber::m_FlattenNames); FillVariationTreeWithXform(xform); }); } +void FractoriumEmberController::Flatten() { UpdateXform([&] (Xform* xform) { m_Ember.Flatten(XmlToEmber::m_FlattenNames); FillVariationTreeWithXform(xform); }); } void Fractorium::OnActionFlatten(bool checked) { m_Controller->Flatten(); } /// @@ -673,7 +679,7 @@ void Fractorium::OnActionFlatten(bool checked) { m_Controller->Flatten(); } /// Resets the rendering process. /// template -void FractoriumEmberController::Unflatten() { UpdateCurrentXform([&] (Xform* xform) { m_Ember.Unflatten(); FillVariationTreeWithXform(xform); }); } +void FractoriumEmberController::Unflatten() { UpdateXform([&] (Xform* xform) { m_Ember.Unflatten(); FillVariationTreeWithXform(xform); }); } void Fractorium::OnActionUnflatten(bool checked) { m_Controller->Unflatten(); } /// @@ -684,21 +690,23 @@ void Fractorium::OnActionUnflatten(bool checked) { m_Controller->Unflatten(); } template void FractoriumEmberController::ClearFlame() { - while (m_Ember.TotalXformCount() > 1) - m_Ember.DeleteTotalXform(m_Ember.TotalXformCount() - 1); - - if (m_Ember.XformCount() == 1) + Update([&]() { - if (Xform* xform = m_Ember.GetXform(0)) - { - xform->Clear(); - xform->ParentEmber(&m_Ember); - } - } + while (m_Ember.TotalXformCount() > 1) + m_Ember.DeleteTotalXform(m_Ember.TotalXformCount() - 1); - m_Fractorium->FillXforms(); - m_Fractorium->ui.CurrentXformCombo->setCurrentIndex(0); - UpdateRender(); + if (m_Ember.XformCount() == 1) + { + if (Xform* xform = m_Ember.GetXform(0)) + { + xform->Clear(); + xform->ParentEmber(&m_Ember); + } + } + + FillXforms(); + m_Fractorium->ui.CurrentXformCombo->setCurrentIndex(0); + }); } void Fractorium::OnActionClearFlame(bool checked) { m_Controller->ClearFlame(); } diff --git a/Source/Fractorium/FractoriumPalette.cpp b/Source/Fractorium/FractoriumPalette.cpp index fa1c7ac..697990f 100644 --- a/Source/Fractorium/FractoriumPalette.cpp +++ b/Source/Fractorium/FractoriumPalette.cpp @@ -202,7 +202,7 @@ void FractoriumEmberController::UpdateAdjustedPaletteGUI(Palette& palette) template void FractoriumEmberController::PaletteAdjust() { - UpdateCurrentXform([&] (Xform* xform) + Update([&]() { ApplyPaletteToEmber(); UpdateAdjustedPaletteGUI(m_Ember.m_Palette); diff --git a/Source/Fractorium/FractoriumXaos.cpp b/Source/Fractorium/FractoriumXaos.cpp index 848f6d7..d72f278 100644 --- a/Source/Fractorium/FractoriumXaos.cpp +++ b/Source/Fractorium/FractoriumXaos.cpp @@ -4,7 +4,7 @@ /// /// Initialize the xforms xaos UI. /// -void Fractorium::InitXformsXaosUI() +void Fractorium::InitXaosUI() { connect(ui.ClearXaosButton, SIGNAL(clicked(bool)), this, SLOT(OnClearXaosButtonClicked(bool)), Qt::QueuedConnection); connect(ui.RandomXaosButton, SIGNAL(clicked(bool)), this, SLOT(OnRandomXaosButtonClicked(bool)), Qt::QueuedConnection); @@ -88,6 +88,7 @@ void Fractorium::FillXaosTable() QWidget* w = nullptr; QString lbl("lbl"); + ui.XaosTable->blockSignals(true); ui.XaosTable->setRowCount(count);//This will grow or shrink the number of rows and call the destructor for previous DoubleSpinBoxes. ui.XaosTable->setColumnCount(count); @@ -137,6 +138,7 @@ void Fractorium::FillXaosTable() w = SetTabOrder(this, w, ui.ClearXaosButton); w = SetTabOrder(this, w, ui.RandomXaosButton); + ui.XaosTable->blockSignals(false); } /// diff --git a/Source/Fractorium/FractoriumXforms.cpp b/Source/Fractorium/FractoriumXforms.cpp index 3381fca..4b3e1e8 100644 --- a/Source/Fractorium/FractoriumXforms.cpp +++ b/Source/Fractorium/FractoriumXforms.cpp @@ -30,7 +30,7 @@ void Fractorium::InitXformsUI() connect(ui.XformWeightNameTable, SIGNAL(cellChanged(int, int)), this, SLOT(OnXformNameChanged(int, int)), Qt::QueuedConnection); ui.CurrentXformCombo->setProperty("soloxform", -1); - + #ifndef WIN32 //For some reason linux makes these 24x24, even though the designer explicitly says 16x16. ui.AddXformButton->setIconSize(QSize(16, 16)); @@ -101,7 +101,7 @@ void Fractorium::OnCurrentXformComboChanged(int index) { m_Controller->CurrentXf template void FractoriumEmberController::AddXform() { - UpdateCurrentXform([&] (Xform* xform) + Update([&]() { Xform newXform; QComboBox* combo = m_Fractorium->ui.CurrentXformCombo; @@ -109,7 +109,7 @@ void FractoriumEmberController::AddXform() newXform.m_Weight = 0.25; newXform.m_ColorX = m_Rand.Frand01(); m_Ember.AddXform(newXform); - m_Fractorium->FillXforms(); + FillXforms(); combo->setCurrentIndex(combo->count() - (m_Fractorium->HaveFinal() ? 2 : 1));//Set index to the last item before final. }); } @@ -117,7 +117,7 @@ void FractoriumEmberController::AddXform() void Fractorium::OnAddXformButtonClicked(bool checked) { m_Controller->AddXform(); } /// -/// Duplicate the current xform in the current ember, and set it as the current xform. +/// Duplicate the specified xforms in the current ember, and set the last one as the current xform. /// Called when the duplicate xform button is clicked. /// Resets the rendering process. /// @@ -125,71 +125,112 @@ void Fractorium::OnAddXformButtonClicked(bool checked) { m_Controller->AddXform( template void FractoriumEmberController::DuplicateXform() { - UpdateCurrentXform([&] (Xform* xform) + vector> vec; + + vec.reserve(m_Ember.XformCount()); + + UpdateXform([&] (Xform* xform) + { + vec.push_back(*xform); + }, eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL, false); + + Update([&]() { QComboBox* combo = m_Fractorium->ui.CurrentXformCombo; - if (xform && !IsFinal(xform)) - { - m_Ember.AddXform(*xform); - m_Fractorium->FillXforms(); - combo->setCurrentIndex(combo->count() - (m_Fractorium->HaveFinal() ? 2 : 1));//Set index to the last item before final. - } + for (auto& it : vec) + m_Ember.AddXform(it); + + FillXforms();//Handles xaos. + combo->setCurrentIndex(combo->count() - (m_Fractorium->HaveFinal() ? 2 : 1));//Set index to the last item before final. }); } void Fractorium::OnDuplicateXformButtonClicked(bool checked) { m_Controller->DuplicateXform(); } /// -/// Clear all variations from the current xform, affine, palette and xaos are left untouched. +/// Clear all variations from the selected xforms. Affine, palette and xaos are left untouched. /// Called when the clear xform button is clicked. /// Resets the rendering process. /// /// Ignored template -void FractoriumEmberController::ClearCurrentXform() +void FractoriumEmberController::ClearXform() { - UpdateCurrentXform([&] (Xform* xform) + UpdateXform([&] (Xform* xform) { - xform->ClearAndDeleteVariations(); - //Note xaos is left alone. - FillVariationTreeWithXform(xform); - }); + xform->ClearAndDeleteVariations();//Note xaos is left alone. + }, eXformUpdate::UPDATE_SELECTED); + + FillVariationTreeWithXform(CurrentXform()); } -void Fractorium::OnClearXformButtonClicked(bool checked) { m_Controller->ClearCurrentXform(); } +void Fractorium::OnClearXformButtonClicked(bool checked) { m_Controller->ClearXform(); } /// -/// Delete the current xform. +/// Delete the selected xforms. /// Will not delete the last remaining non-final xform. /// Called when the delete xform button is clicked. /// Resets the rendering process. /// /// Ignored template -void FractoriumEmberController::DeleteCurrentXform() +void FractoriumEmberController::DeleteXforms() { - UpdateCurrentXform([&] (Xform* xform) + int i = 0, offset = 0, current = 0, checked = 0; + bool haveFinal = false; + size_t count; + QComboBox* combo = m_Fractorium->ui.CurrentXformCombo; + + //Iterating over the checkboxes must be done instead of using UpdateXform() to iterate over xforms + //because xforms are being deleted inside the loop. + //Also manually calling UpdateRender() rather than using the usual Update() call because + //it should only be called if an xform has actually been deleted. + m_Fractorium->ForEachXformCheckbox([&](int i, QCheckBox* w) { - bool haveFinal = m_Fractorium->HaveFinal(); - QComboBox* combo = m_Fractorium->ui.CurrentXformCombo; - int count = combo->count(); - int index = combo->currentIndex(); + count = m_Ember.TotalXformCount(); + haveFinal = m_Ember.UseFinalXform();//Requery every time. + + if (w->isChecked()) + checked++; //Do not allow deleting the only remaining non-final xform. - if (haveFinal && count <= 2 && index == 0) + if (haveFinal && count <= 2 && i == 0) return; if (!haveFinal && count == 1) return; - m_Ember.DeleteTotalXform(index); - m_Fractorium->FillXforms(); - combo->setCurrentIndex(combo->count() - (haveFinal ? 2 : 1));//Set index to the last item before final. + if (w->isChecked()) + { + //qDebug() << "Deleting " << w->text(); + m_Ember.DeleteTotalXform(i - offset);//Subtract offset to account for previously deleted xforms. + offset++; + } }); + + current = combo->currentIndex(); + count = m_Ember.TotalXformCount(); + haveFinal = m_Ember.UseFinalXform();//Requery again. + + //Nothing was selected, so just delete current. + if (!checked && + !(haveFinal && count <= 2 && current == 0) &&//Again disallow deleting the only remaining non-final xform. + !(!haveFinal && count == 1)) + { + m_Ember.DeleteTotalXform(current); + offset++; + } + + if (offset) + { + FillXforms(); + combo->setCurrentIndex(combo->count() - (m_Ember.UseFinalXform() ? 2 : 1));//Set index to the last item before final. Note final is requeried one last time. + UpdateRender(); + } } -void Fractorium::OnDeleteXformButtonClicked(bool checked) { m_Controller->DeleteCurrentXform(); } +void Fractorium::OnDeleteXformButtonClicked(bool checked) { m_Controller->DeleteXforms(); } /// /// Add a final xform to the ember and set it as the current xform. @@ -201,26 +242,26 @@ void Fractorium::OnDeleteXformButtonClicked(bool checked) { m_Controller->Delete template void FractoriumEmberController::AddFinalXform() { - QComboBox* combo = m_Fractorium->ui.CurrentXformCombo; - //Check to see if a final xform is already present. if (!m_Fractorium->HaveFinal()) { - Xform xform; + Update([&]() + { + Xform final; + auto combo = m_Fractorium->ui.CurrentXformCombo; - xform.AddVariation(new LinearVariation());//Just a placeholder so other parts of the code don't see it as being empty. - m_Ember.SetFinalXform(xform); - combo->addItem("Final"); - combo->setItemIcon(combo->count() - 1, m_Fractorium->m_FinalXformComboIcon); - combo->setCurrentIndex(combo->count() - 1);//Set index to the last item. - UpdateRender(); + final.AddVariation(new LinearVariation());//Just a placeholder so other parts of the code don't see it as being empty. + m_Ember.SetFinalXform(final); + FillXforms(); + combo->setCurrentIndex(combo->count() - 1);//Set index to the last item. + }); } } void Fractorium::OnAddFinalXformButtonClicked(bool checked) { m_Controller->AddFinalXform(); } /// -/// Set the weight of the current xform. +/// Set the weight of the selected xforms. /// Called when weight spinner changes. /// Resets the rendering process. /// @@ -228,11 +269,12 @@ void Fractorium::OnAddFinalXformButtonClicked(bool checked) { m_Controller->AddF template void FractoriumEmberController::XformWeightChanged(double d) { - UpdateCurrentXform([&] (Xform* xform) + UpdateXform([&] (Xform* xform) { xform->m_Weight = d; - SetNormalizedWeightText(xform); - }); + }, eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL); + + SetNormalizedWeightText(CurrentXform()); } void Fractorium::OnXformWeightChanged(double d) { m_Controller->XformWeightChanged(d); } @@ -243,17 +285,18 @@ void Fractorium::OnXformWeightChanged(double d) { m_Controller->XformWeightChang template void FractoriumEmberController::EqualizeWeights() { - UpdateCurrentXform([&] (Xform* xform) + UpdateXform([&] (Xform* xform) { m_Ember.EqualizeWeights(); m_Fractorium->m_XformWeightSpin->setValue(xform->m_Weight);//Will trigger an update, so pass false to updateRender below. - }, false); + }, eXformUpdate::UPDATE_CURRENT, false); } void Fractorium::OnEqualWeightButtonClicked(bool checked) { m_Controller->EqualizeWeights(); } /// /// Set the name of the current xform. +/// Update the corresponding xform checkbox text with the name. /// Called when the user types in the name cell of the table. /// /// The row of the cell @@ -261,18 +304,18 @@ void Fractorium::OnEqualWeightButtonClicked(bool checked) { m_Controller->Equali template void FractoriumEmberController::XformNameChanged(int row, int col) { - UpdateCurrentXform([&] (Xform* xform) + UpdateXform([&] (Xform* xform) { - int index = m_Ember.GetXformIndex(xform); + int index = m_Ember.GetTotalXformIndex(xform); xform->m_Name = m_Fractorium->ui.XformWeightNameTable->item(row, col)->text().toStdString(); - + XformCheckboxAt(index, [&](QCheckBox* checkbox) { checkbox->setText(MakeXformCaption(index)); }); //if (index != -1) //{ // if (QTableWidgetItem* xformNameItem = m_Fractorium->ui.XaosTable->item(index, 0)) // xformNameItem->setText(MakeXaosNameString(index)); //} - }, false); + }, eXformUpdate::UPDATE_CURRENT, false); } void Fractorium::OnXformNameChanged(int row, int col) { m_Controller->XformNameChanged(row, col); } @@ -282,7 +325,7 @@ void Fractorium::OnXformNameChanged(int row, int col) { m_Controller->XformNameC /// /// The xform whose values will be used to populate the widgets template -void FractoriumEmberController::FillWithXform(Xform* xform) +void FractoriumEmberController::FillWithXform(Xform* xform)//Need to see where all this is called from and sync with FillXform(). Maybe rename the latter. { m_Fractorium->m_XformWeightSpin->SetValueStealth(xform->m_Weight); SetNormalizedWeightText(xform); @@ -332,32 +375,71 @@ bool FractoriumEmberController::IsFinal(Xform* xform) /// /// Fill the xforms combo box with the xforms in the current ember. /// Select the first one and fill all widgets with its values. +/// Also dynamically generate a checkbox for each xform which will allow the user +/// to select which xforms to apply operations to. /// -void Fractorium::FillXforms() +template +void FractoriumEmberController::FillXforms() { - int i = 0, spinHeight = 20; - QComboBox* combo = ui.CurrentXformCombo; + int i = 0, count = int(XformCount()); + auto combo = m_Fractorium->ui.CurrentXformCombo; combo->blockSignals(true); combo->clear(); - for (i = 0; i < int(m_Controller->XformCount()); i++) + //First clear all dynamically created checkboxes. + m_Fractorium->ClearXformsSelections(); + m_Fractorium->m_XformsSelectionLayout->blockSignals(true); + + //Fill combo box and create new checkboxes. + for (i = 0; i < count; i++) { combo->addItem(ToString(i + 1)); - combo->setItemIcon(i, m_XformComboIcons[i % XFORM_COLOR_COUNT]); + combo->setItemIcon(i, m_Fractorium->m_XformComboIcons[i % XFORM_COLOR_COUNT]); } - if (m_Controller->UseFinalXform()) + i = 0; + while (i < count) { - combo->addItem("Final"); - combo->setItemIcon(i, m_FinalXformComboIcon); + if (i < count - 1) + { + auto cb1 = new QCheckBox(MakeXformCaption(i), m_Fractorium); + auto cb2 = new QCheckBox(MakeXformCaption(i + 1), m_Fractorium); + + m_Fractorium->m_XformSelections.push_back(cb1); + m_Fractorium->m_XformSelections.push_back(cb2); + m_Fractorium->m_XformsSelectionLayout->addRow(cb1, cb2); + i += 2; + } + else if (i < count) + { + auto cb = new QCheckBox(MakeXformCaption(i), m_Fractorium); + + m_Fractorium->m_XformSelections.push_back(cb); + m_Fractorium->m_XformsSelectionLayout->addRow(cb, new QWidget(m_Fractorium)); + i++; + } } - + + //Special case for final xform. + if (UseFinalXform()) + { + auto cb = new QCheckBox(MakeXformCaption(i), m_Fractorium); + + m_Fractorium->m_XformSelections.push_back(cb); + m_Fractorium->m_XformsSelectionLayout->addRow(cb, new QWidget(m_Fractorium)); + + combo->addItem("Final"); + combo->setItemIcon(i, m_Fractorium->m_FinalXformComboIcon); + } + + m_Fractorium->m_XformsSelectionLayout->blockSignals(false); combo->blockSignals(false); combo->setCurrentIndex(0); - FillXaosTable(); - OnSoloXformCheckBoxStateChanged(Qt::Unchecked); - OnCurrentXformComboChanged(0);//Make sure the event gets called, because it won't if the zero index is already selected. + + m_Fractorium->FillXaosTable(); + m_Fractorium->OnSoloXformCheckBoxStateChanged(Qt::Unchecked); + m_Fractorium->OnCurrentXformComboChanged(0);//Make sure the event gets called, because it won't if the zero index is already selected. } template class FractoriumEmberController; diff --git a/Source/Fractorium/FractoriumXformsAffine.cpp b/Source/Fractorium/FractoriumXformsAffine.cpp index 1d2bfbd..783d0a8 100644 --- a/Source/Fractorium/FractoriumXformsAffine.cpp +++ b/Source/Fractorium/FractoriumXformsAffine.cpp @@ -169,7 +169,7 @@ void Fractorium::InitXformsAffineUI() template void FractoriumEmberController::AffineSetHelper(double d, int index, bool pre) { - UpdateCurrentXform([&] (Xform* xform) + UpdateXform([&] (Xform* xform) { Affine2D* affine = pre ? &xform->m_Affine : &xform->m_Post; DoubleSpinBox** spinners = pre ? m_Fractorium->m_PreSpins : m_Fractorium->m_PostSpins; @@ -220,7 +220,7 @@ void FractoriumEmberController::AffineSetHelper(double d, int index, bool pre break; } } - }); + }, eXformUpdate::UPDATE_SELECTED); } /// @@ -235,16 +235,16 @@ void Fractorium::OnO1Changed(double d) { m_Controller->AffineSetHelper(d, 2, sen void Fractorium::OnO2Changed(double d) { m_Controller->AffineSetHelper(d, 5, sender() == m_PreO2Spin); } /// -/// Flip the current pre/post affine vertically and/or horizontally. +/// Flip the selected pre/post affines vertically and/or horizontally. /// Resets the rendering process. /// /// True to flip horizontally /// True to flip vertically /// True if pre affine, else post affine. template -void FractoriumEmberController::FlipCurrentXform(bool horizontal, bool vertical, bool pre) +void FractoriumEmberController::FlipXforms(bool horizontal, bool vertical, bool pre) { - UpdateCurrentXform([&] (Xform* xform) + UpdateXform([&] (Xform* xform) { Affine2D* affine = pre ? &xform->m_Affine : &xform->m_Post; @@ -266,29 +266,31 @@ void FractoriumEmberController::FlipCurrentXform(bool horizontal, bool vertic affine->F(-affine->F()); } - FillAffineWithXform(xform, pre); - }); + }, eXformUpdate::UPDATE_SELECTED); + + FillAffineWithXform(CurrentXform(), pre); } -void Fractorium::OnFlipHorizontalButtonClicked(bool checked) { m_Controller->FlipCurrentXform(true, false, sender() == ui.PreFlipHorizontalButton); } -void Fractorium::OnFlipVerticalButtonClicked(bool checked) { m_Controller->FlipCurrentXform(false, true, sender() == ui.PreFlipVerticalButton); } +void Fractorium::OnFlipHorizontalButtonClicked(bool checked) { m_Controller->FlipXforms(true, false, sender() == ui.PreFlipHorizontalButton); } +void Fractorium::OnFlipVerticalButtonClicked(bool checked) { m_Controller->FlipXforms(false, true, sender() == ui.PreFlipVerticalButton); } /// -/// Rotate the current pre/post affine transform x degrees. +/// Rotate the selected pre/post affines transform x degrees. /// Resets the rendering process. /// /// The angle to rotate by /// True if pre affine, else post affine. template -void FractoriumEmberController::RotateCurrentXformByAngle(double angle, bool pre) +void FractoriumEmberController::RotateXformsByAngle(double angle, bool pre) { - UpdateCurrentXform([&] (Xform* xform) + UpdateXform([&] (Xform* xform) { Affine2D* affine = pre ? &xform->m_Affine : &xform->m_Post; affine->Rotate(angle); - FillAffineWithXform(xform, pre); - }); + }, eXformUpdate::UPDATE_SELECTED); + + FillAffineWithXform(CurrentXform(), pre); } /// @@ -297,8 +299,8 @@ void FractoriumEmberController::RotateCurrentXformByAngle(double angle, bool /// Resets the rendering process. /// /// Ignored -void Fractorium::OnRotate90CButtonClicked(bool checked) { m_Controller->RotateCurrentXformByAngle(90, sender() == ui.PreRotate90CButton); } -void Fractorium::OnRotate90CcButtonClicked(bool checked) { m_Controller->RotateCurrentXformByAngle(-90, sender() == ui.PreRotate90CcButton); } +void Fractorium::OnRotate90CButtonClicked(bool checked) { m_Controller->RotateXformsByAngle(90, sender() == ui.PreRotate90CButton); } +void Fractorium::OnRotate90CcButtonClicked(bool checked) { m_Controller->RotateXformsByAngle(-90, sender() == ui.PreRotate90CcButton); } /// /// Rotate the selected pre/post affine transform x degrees clockwise. @@ -314,7 +316,7 @@ void Fractorium::OnRotateCButtonClicked(bool checked) double d = ToDouble(combo->currentText(), &ok); if (ok) - m_Controller->RotateCurrentXformByAngle(d, pre); + m_Controller->RotateXformsByAngle(d, pre); } /// @@ -331,27 +333,28 @@ void Fractorium::OnRotateCcButtonClicked(bool checked) double d = ToDouble(combo->currentText(), &ok); if (ok) - m_Controller->RotateCurrentXformByAngle(-d, pre); + m_Controller->RotateXformsByAngle(-d, pre); } /// -/// Move the current pre/post affine in the x and y directions by the specified amount. +/// Move the selected pre/post affines in the x and y directions by the specified amount. /// Resets the rendering process. /// /// The x direction to move /// The y direction to move /// True if pre affine, else post affine. template -void FractoriumEmberController::MoveCurrentXform(double x, double y, bool pre) +void FractoriumEmberController::MoveXforms(double x, double y, bool pre) { - UpdateCurrentXform([&] (Xform* xform) + UpdateXform([&] (Xform* xform) { Affine2D* affine = pre ? &xform->m_Affine : &xform->m_Post; affine->C(affine->C() + x); affine->F(affine->F() + y); - FillAffineWithXform(xform, pre); - }); + }, eXformUpdate::UPDATE_SELECTED); + + FillAffineWithXform(CurrentXform(), pre); } /// @@ -368,7 +371,7 @@ void Fractorium::OnMoveUpButtonClicked(bool checked) double d = ToDouble(combo->currentText(), &ok); if (ok) - m_Controller->MoveCurrentXform(0, d, pre); + m_Controller->MoveXforms(0, d, pre); } /// @@ -385,7 +388,7 @@ void Fractorium::OnMoveDownButtonClicked(bool checked) double d = ToDouble(combo->currentText(), &ok); if (ok) - m_Controller->MoveCurrentXform(0, -d, pre); + m_Controller->MoveXforms(0, -d, pre); } /// @@ -402,7 +405,7 @@ void Fractorium::OnMoveLeftButtonClicked(bool checked) double d = ToDouble(combo->currentText(), &ok); if (ok) - m_Controller->MoveCurrentXform(-d, 0, pre); + m_Controller->MoveXforms(-d, 0, pre); } /// @@ -419,19 +422,19 @@ void Fractorium::OnMoveRightButtonClicked(bool checked) double d = ToDouble(combo->currentText(), &ok); if (ok) - m_Controller->MoveCurrentXform(d, 0, pre); + m_Controller->MoveXforms(d, 0, pre); } /// -/// Scale the current pre/post affine by the specified amount. +/// Scale the selected pre/post affines by the specified amount. /// Resets the rendering process. /// /// The scale value /// True if pre affine, else post affine. template -void FractoriumEmberController::ScaleCurrentXform(double scale, bool pre) +void FractoriumEmberController::ScaleXforms(double scale, bool pre) { - UpdateCurrentXform([&] (Xform* xform) + UpdateXform([&] (Xform* xform) { Affine2D* affine = pre ? &xform->m_Affine : &xform->m_Post; @@ -439,8 +442,9 @@ void FractoriumEmberController::ScaleCurrentXform(double scale, bool pre) affine->B(affine->B() * scale); affine->D(affine->D() * scale); affine->E(affine->E() * scale); - FillAffineWithXform(xform, pre); - }); + }, eXformUpdate::UPDATE_SELECTED); + + FillAffineWithXform(CurrentXform(), pre); } /// @@ -457,7 +461,7 @@ void Fractorium::OnScaleDownButtonClicked(bool checked) double d = ToDouble(combo->currentText(), &ok); if (ok) - m_Controller->ScaleCurrentXform(1.0 / (d / 100.0), pre); + m_Controller->ScaleXforms(1.0 / (d / 100.0), pre); } /// @@ -474,24 +478,25 @@ void Fractorium::OnScaleUpButtonClicked(bool checked) double d = ToDouble(combo->currentText(), &ok); if (ok) - m_Controller->ScaleCurrentXform(d / 100.0, pre); + m_Controller->ScaleXforms(d / 100.0, pre); } /// -/// Reset pre/post affine to the identity matrix. +/// Reset selected pre/post affines to the identity matrix. /// Called when reset pre/post affine buttons are clicked. /// Resets the rendering process. /// template -void FractoriumEmberController::ResetCurrentXformAffine(bool pre) +void FractoriumEmberController::ResetXformsAffine(bool pre) { - UpdateCurrentXform([&] (Xform* xform) + UpdateXform([&] (Xform* xform) { Affine2D* affine = pre ? &xform->m_Affine : &xform->m_Post; affine->MakeID(); - FillAffineWithXform(xform, pre); - }); + }, eXformUpdate::UPDATE_SELECTED); + + FillAffineWithXform(CurrentXform(), pre); } /// @@ -499,7 +504,7 @@ void FractoriumEmberController::ResetCurrentXformAffine(bool pre) /// Called when reset pre/post affine buttons are clicked. /// Resets the rendering process. /// -void Fractorium::OnResetAffineButtonClicked(bool checked) { m_Controller->ResetCurrentXformAffine(sender() == ui.PreResetButton); } +void Fractorium::OnResetAffineButtonClicked(bool checked) { m_Controller->ResetXformsAffine(sender() == ui.PreResetButton); } /// /// Fill the GUI with the pre and post affine xform values. diff --git a/Source/Fractorium/FractoriumXformsColor.cpp b/Source/Fractorium/FractoriumXformsColor.cpp index 594d17d..6c7474a 100644 --- a/Source/Fractorium/FractoriumXformsColor.cpp +++ b/Source/Fractorium/FractoriumXformsColor.cpp @@ -37,7 +37,7 @@ void Fractorium::InitXformsColorUI() } /// -/// Set the color index of the current xform. +/// Set the color index of the selected xforms. /// Update the color index scrollbar to match. /// Called when spinner in the color index cell in the palette ref table is changed. /// Optionally resets the rendering process. @@ -47,17 +47,14 @@ void Fractorium::InitXformsColorUI() template void FractoriumEmberController::XformColorIndexChanged(double d, bool updateRender) { - UpdateCurrentXform([&] (Xform* xform) - { - QScrollBar* scroll = m_Fractorium->ui.XformColorScroll; - int scrollVal = d * scroll->maximum(); + auto scroll = m_Fractorium->ui.XformColorScroll; + int scrollVal = d * scroll->maximum(); - scroll->blockSignals(true); - scroll->setValue(scrollVal); - scroll->blockSignals(false); + scroll->blockSignals(true); + scroll->setValue(scrollVal); + scroll->blockSignals(false); - SetCurrentXformColorIndex(d); - }, updateRender); + SetCurrentXformColorIndex(d, updateRender); } void Fractorium::OnXformColorIndexChanged(double d) { OnXformColorIndexChanged(d, true); } @@ -65,7 +62,7 @@ void Fractorium::OnXformColorIndexChanged(double d, bool updateRender) { m_Contr /// /// Set the color index of the current xform. -/// Update the color index cell in the palette ref table to match. +/// Will trigger an update which will cause the color index cell in the palette ref table to match. /// Called when color index scrollbar is changed. /// Resets the rendering process. /// @@ -73,43 +70,40 @@ void Fractorium::OnXformColorIndexChanged(double d, bool updateRender) { m_Contr template void FractoriumEmberController::XformScrollColorIndexChanged(int d) { - UpdateCurrentXform([&] (Xform* xform) - { - m_Fractorium->m_XformColorIndexSpin->setValue(d / double(m_Fractorium->ui.XformColorScroll->maximum()));//Will trigger an update. - }, false); + m_Fractorium->m_XformColorIndexSpin->setValue(d / double(m_Fractorium->ui.XformColorScroll->maximum()));//Will trigger an update. } void Fractorium::OnXformScrollColorIndexChanged(int d) { m_Controller->XformScrollColorIndexChanged(d); } /// -/// Set the color speed of the current xform. +/// Set the color speed of the selected xforms. /// Called when xform color speed spinner is changed. /// Resets the rendering process. /// /// The color speed, -1-1. template -void FractoriumEmberController::XformColorSpeedChanged(double d) { UpdateCurrentXform([&] (Xform* xform) { xform->m_ColorSpeed = d; }); } +void FractoriumEmberController::XformColorSpeedChanged(double d) { UpdateXform([&] (Xform* xform) { xform->m_ColorSpeed = d; }, eXformUpdate::UPDATE_SELECTED); } void Fractorium::OnXformColorSpeedChanged(double d) { m_Controller->XformColorSpeedChanged(d); } /// -/// Set the opacity of the current xform. +/// Set the opacity of the selected xforms. /// Called when xform opacity spinner is changed. /// Resets the rendering process. /// /// The opacity, 0-1. template -void FractoriumEmberController::XformOpacityChanged(double d) { UpdateCurrentXform([&] (Xform* xform) { xform->m_Opacity = d; }); } +void FractoriumEmberController::XformOpacityChanged(double d) { UpdateXform([&] (Xform* xform) { xform->m_Opacity = d; }, eXformUpdate::UPDATE_SELECTED); } void Fractorium::OnXformOpacityChanged(double d) { m_Controller->XformOpacityChanged(d); } /// -/// Set the direct color percentage of the current xform. +/// Set the direct color percentage of the selected xforms. /// Called when xform direct color spinner is changed. /// Note this only affects xforms that include a dc_ variation. /// Resets the rendering process. /// /// The direct color percentage, 0-1. template -void FractoriumEmberController::XformDirectColorChanged(double d) { UpdateCurrentXform([&] (Xform* xform) { xform->m_DirectColor = d; }); } +void FractoriumEmberController::XformDirectColorChanged(double d) { UpdateXform([&] (Xform* xform) { xform->m_DirectColor = d; }, eXformUpdate::UPDATE_SELECTED); } void Fractorium::OnXformDirectColorChanged(double d) { m_Controller->XformDirectColorChanged(d); } /// @@ -201,27 +195,27 @@ void Fractorium::OnCurvesGreenRadioButtonToggled(bool checked) { if (checked) ui void Fractorium::OnCurvesBlueRadioButtonToggled(bool checked) { if (checked) ui.CurvesView->SetTop(CurveIndex::BLUE); } /// -/// Set the current xform color index spinner to the current xform's color index. +/// Set the selected xforms color index to the passed in value. /// Set the color cell in the palette ref table. /// /// The index value to set, 0-1. template -void FractoriumEmberController::SetCurrentXformColorIndex(double d) +void FractoriumEmberController::SetCurrentXformColorIndex(double d, bool updateRender) { - UpdateCurrentXform([&] (Xform* xform) + UpdateXform([&] (Xform* xform) { xform->m_ColorX = Clamp(d, 0, 1); - + //Grab the current color from the index and assign it to the first cell of the first table. v4T entry = m_Ember.m_Palette[Clamp(d * COLORMAP_LENGTH_MINUS_1, 0, m_Ember.m_Palette.Size())]; entry.r *= 255; entry.g *= 255; entry.b *= 255; - + QRgb rgb = uint(entry.r) << 16 | uint(entry.g) << 8 | uint(entry.b); m_Fractorium->ui.XformColorIndexTable->item(0, 0)->setBackgroundColor(QColor::fromRgb(rgb)); - }, false); + }, eXformUpdate::UPDATE_SELECTED, updateRender); } /// @@ -247,7 +241,7 @@ void FractoriumEmberController::FillCurvesControl() } /// -/// Set the color index, speed and opacity spinners with the values of the current xform. +/// Set the color index, speed and opacity spinners with the values of the passed in xform. /// Set the cells of the palette ref table as well. /// /// The xform whose values will be copied to the GUI diff --git a/Source/Fractorium/FractoriumXformsSelect.cpp b/Source/Fractorium/FractoriumXformsSelect.cpp new file mode 100644 index 0000000..3b1e2cc --- /dev/null +++ b/Source/Fractorium/FractoriumXformsSelect.cpp @@ -0,0 +1,127 @@ +#include "FractoriumPch.h" +#include "Fractorium.h" + +/// +/// Initialize the xforms selection UI. +/// +void Fractorium::InitXformsSelectUI() +{ + m_XformsSelectionLayout = (QFormLayout*)ui.XformsSelectGroupBoxScrollAreaWidget->layout(); + connect(ui.XformsSelectAllButton, SIGNAL(clicked(bool)), this, SLOT(OnXformsSelectAllButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.XformsSelectNoneButton, SIGNAL(clicked(bool)), this, SLOT(OnXformsSelectNoneButtonClicked(bool)), Qt::QueuedConnection); + + ClearXformsSelections(); +} + +/// +/// Check all of the xform selection checkboxes. +/// +/// Ignored +void Fractorium::OnXformsSelectAllButtonClicked(bool checked) { ForEachXformCheckbox([&](int i, QCheckBox* w) { w->setChecked(true); }); } + +/// +/// Uncheck all of the xform selection checkboxes. +/// +/// Ignored +void Fractorium::OnXformsSelectNoneButtonClicked(bool checked) { ForEachXformCheckbox([&](int i, QCheckBox* w) { w->setChecked(false); }); } + +/// +/// Clear all of the dynamically created xform checkboxes. +/// +void Fractorium::ClearXformsSelections() +{ + QLayoutItem* child = nullptr; + + m_XformSelections.clear(); + m_XformsSelectionLayout->blockSignals(true); + + while (m_XformsSelectionLayout->count() && (child = m_XformsSelectionLayout->takeAt(0))) + { + auto* w = child->widget(); + delete child; + delete w; + } + + m_XformsSelectionLayout->blockSignals(false); +} + +/// +/// Make a caption from an xform. +/// The caption will be the xform count + 1, optionally followed by the xform's name. +/// For final xforms, the string "Final" will be used in place of the count. +/// +/// The index of xform to make a caption for +/// The caption string +template +QString FractoriumEmberController::MakeXformCaption(size_t i) +{ + bool isFinal = m_Ember.FinalXform() == m_Ember.GetTotalXform(i); + QString caption = isFinal ? "Final" : QString::number(i + 1); + + if (Xform* xform = m_Ember.GetTotalXform(i)) + { + if (!xform->m_Name.empty()) + caption += " (" + QString::fromStdString(xform->m_Name) + ")"; + } + + return caption; +} + +/// +/// Function to perform the specified operation on every dynamically created xform selection checkbox. +/// +/// The operation to perform +void Fractorium::ForEachXformCheckbox(std::function func) +{ + int i = 0; + + while (QLayoutItem* child = m_XformsSelectionLayout->itemAt(i)) + { + if (auto* w = dynamic_cast(child->widget())) + { + func(i, w); + } + + i++; + } +} + +/// +/// Function to perform the specified operation on one dynamically created xform selection checkbox. +/// +/// The index of the checkbox +/// The operation to perform +/// True if the checkbox was found, else false. +template +bool FractoriumEmberController::XformCheckboxAt(int i, std::function func) +{ + if (QLayoutItem* child = m_Fractorium->m_XformsSelectionLayout->itemAt(i)) + { + if (auto* w = dynamic_cast(child->widget())) + { + func(w); + return true; + } + } + + return false; +} + +/// +/// Function to perform the specified operation on one dynamically created xform selection checkbox. +/// The checkbox is specified by the xform it corresponds to, rather than its index. +/// +/// The xform that corresponds to the checkbox +/// The operation to perform +/// True if the checkbox was found, else false. +template +bool FractoriumEmberController::XformCheckboxAt(Xform* xform, std::function func) +{ + return XformCheckboxAt(m_Ember.GetTotalXformIndex(xform), func); +} + +template class FractoriumEmberController; + +#ifdef DO_DOUBLE +template class FractoriumEmberController; +#endif diff --git a/Source/Fractorium/FractoriumXformsVariations.cpp b/Source/Fractorium/FractoriumXformsVariations.cpp index 03f1505..e934aae 100644 --- a/Source/Fractorium/FractoriumXformsVariations.cpp +++ b/Source/Fractorium/FractoriumXformsVariations.cpp @@ -129,7 +129,7 @@ void FractoriumEmberController::ClearVariationsTree() /// /// The spinner value template -void FractoriumEmberController::VariationSpinBoxValueChanged(double d) +void FractoriumEmberController::VariationSpinBoxValueChanged(double d)//Would be awesome to make this work for all.//TODO { QObject* objSender = m_Fractorium->sender(); QTreeWidget* tree = m_Fractorium->ui.VariationsTree;