diff --git a/Source/Ember/Affine2D.cpp b/Source/Ember/Affine2D.cpp index 1d15e3c..4e50208 100644 --- a/Source/Ember/Affine2D.cpp +++ b/Source/Ember/Affine2D.cpp @@ -112,6 +112,22 @@ typename v2T Affine2D::operator * (const v2T& v) return TransformVector(v); } +/// +/// Return a copy of the object with all values A-F scaled by the specified amount. +/// +/// The amount to scale by +/// A new Affine2D which a scaled copy of this instance +template +Affine2D Affine2D:: operator * (const T& t) +{ + return Affine2D(A() * t, + D() * t, + B() * t, + E() * t, + C() * t, + F() * t); +} + /// /// Make this affine transform the identity matrix. /// A and E = 1, all else 0. @@ -172,6 +188,21 @@ bool Affine2D::IsEmpty() const (IsClose(F(), EMPTYFIELD)); } +/// +/// Scales all values A-F by the specified amount. +/// +/// The amount to scale by +template +void Affine2D::Scale(T amount) +{ + A(A() * amount); + B(B() * amount); + C(C() * amount); + D(D() * amount); + E(E() * amount); + F(F() * amount); +} + /// /// Rotate this affine transform around its origin by the specified angle in degrees. /// @@ -181,7 +212,6 @@ void Affine2D::Rotate(T angle) { m4T origMat4 = ToMat4ColMajor(true);//Must center and use column major for glm to work. m4T newMat4 = glm::rotate(origMat4, angle * DEG_2_RAD_T, v3T(0, 0, 1));//Assuming only rotating around z. - A(newMat4[0][0]);//Use direct assignments instead of constructor to skip assigning C and F. B(newMat4[0][1]); D(newMat4[1][0]); @@ -206,7 +236,6 @@ template void Affine2D::RotateScaleXTo(const v2T& v) { Affine2D rs = CalcRotateScale(X(), v); - X(rs.TransformNormal(X())); Y(rs.TransformNormal(Y())); } @@ -219,7 +248,6 @@ template void Affine2D::RotateScaleYTo(const v2T& v) { Affine2D rs = CalcRotateScale(Y(), v); - X(rs.TransformNormal(X())); Y(rs.TransformNormal(Y())); } @@ -232,10 +260,9 @@ template Affine2D Affine2D::Inverse() const { T det = A() * E() - D() * B(); - return Affine2D(E() / det, -D() / det, - -B() / det, A() / det, - (F() * B() - C() * E()) / det, (C() * D() - F() * A()) / det); + -B() / det, A() / det, + (F() * B() - C() * E()) / det, (C() * D() - F() * A()) / det); } /// @@ -294,9 +321,8 @@ typename m4T Affine2D::ToMat4ColMajor(bool center) const { m4T mat(A(), B(), 0, center ? 0 : C(), //Col0... D(), E(), 0, center ? 0 : F(), //1 - 0, 0, 1, 0, //2 - 0, 0, 0, 1);//3 - + 0, 0, 1, 0, //2 + 0, 0, 0, 1);//3 return mat; } @@ -310,9 +336,8 @@ typename m4T Affine2D::ToMat4RowMajor(bool center) const { m4T mat(A(), D(), 0, 0, B(), E(), 0, 0, - 0, 0, 1, 0, + 0, 0, 1, 0, center ? 0 : C(), center ? 0 : F(), 0, 1); - return mat; } @@ -351,7 +376,6 @@ template Affine2D Affine2D::CalcRotateScale(const v2T& from, const v2T& to) { T a, c; - CalcRSAC(from, to, a, c); return Affine2D(a, c, -c, a, 0, 0); } @@ -368,7 +392,6 @@ template void Affine2D::CalcRSAC(const v2T& from, const v2T& to, T& a, T& c) { T lsq = from.x * from.x + from.y * from.y; - a = (from.y * to.y + from.x * to.x) / lsq; c = (from.x * to.y - from.y * to.x) / lsq; } diff --git a/Source/Ember/Affine2D.h b/Source/Ember/Affine2D.h index 4960e8c..9aea528 100644 --- a/Source/Ember/Affine2D.h +++ b/Source/Ember/Affine2D.h @@ -64,17 +64,19 @@ public: D(T(affine.D())); E(T(affine.E())); F(T(affine.F())); - return *this; } bool operator == (const Affine2D& affine); v2T operator * (const v2T& v); + Affine2D operator * (const T& t); void MakeID(); bool IsID() const; bool IsZero() const; bool IsEmpty() const; + void Scale(T amount); + Affine2D ScaleCopy(T amount); void Rotate(T angle); void Translate(const v2T& v); void RotateScaleXTo(const v2T& v); diff --git a/Source/Ember/Ember.h b/Source/Ember/Ember.h index 0608c34..6124fcc 100644 --- a/Source/Ember/Ember.h +++ b/Source/Ember/Ember.h @@ -314,8 +314,6 @@ public: /// True if success, else false. bool DeleteXform(size_t i) { - Xform* xform; - if (i < XformCount()) { m_Xforms.erase(m_Xforms.begin() + i); @@ -323,7 +321,7 @@ public: //Now shuffle xaos values from i on back by 1 for every xform. for (size_t x1 = 0; x1 < XformCount(); x1++) { - if ((xform = GetXform(x1))) + if (auto xform = GetXform(x1)) { for (size_t x2 = i + 1; x2 <= XformCount(); x2++)//Iterate from the position after the deletion index up to the old count. xform->SetXaos(x2 - 1, xform->Xaos(x2)); @@ -463,7 +461,7 @@ public: { for (size_t i = 0; i < TotalXformCount(); i++) { - Xform* xform = GetTotalXform(i); + auto xform = GetTotalXform(i); xform->CacheColorVals(); xform->SetPrecalcFlags(); } @@ -824,7 +822,7 @@ public: //This includes all xforms plus final. for (size_t i = 0; i < totalXformCount; i++) { - Xform* thisXform = GetTotalXform(i); + auto thisXform = GetTotalXform(i); if (size == 2 && stagger > 0 && thisXform != &m_FinalXform) { @@ -845,7 +843,7 @@ public: for (size_t k = 0; k < size; k++)//For each ember in the list. { - Xform* tempXform = embers[k].GetTotalXform(i);//Xform in this position in this ember, including final. + auto tempXform = embers[k].GetTotalXform(i);//Xform in this position in this ember, including final. if (tempXform) { @@ -904,7 +902,7 @@ public: { if (i < embers[k].TotalXformCount())//Xform in this position in this ember. { - Xform* tempXform = embers[k].GetTotalXform(i); + auto tempXform = embers[k].GetTotalXform(i); allID &= tempXform->m_Post.IsID(); } } @@ -931,7 +929,7 @@ public: for (size_t k = 0; k < size; k++) { - Xform* tempXform = embers[k].GetTotalXform(i);//Xform in this position in this ember. + auto tempXform = embers[k].GetTotalXform(i);//Xform in this position in this ember. if (tempXform) { @@ -971,7 +969,7 @@ public: //Now fill them with interpolated values. for (size_t j = 0; j < size; j++)//For each ember in the list. { - Xform* tempXform = embers[j].GetXform(i); + auto tempXform = embers[j].GetXform(i); for (size_t k = 0; k < XformCount(); k++)//For each xaos entry in this xform's xaos array, sum it with the same entry in all of the embers multiplied by the coef for that ember. { diff --git a/Source/Ember/Interpolate.h b/Source/Ember/Interpolate.h index f134544..cb8cfc2 100644 --- a/Source/Ember/Interpolate.h +++ b/Source/Ember/Interpolate.h @@ -41,7 +41,6 @@ public: bool aligned = true; bool currentFinal, final = sourceEmbers[0].UseFinalXform(); size_t i, xf, currentCount, maxCount = sourceEmbers[0].XformCount(); - Xform* destXform; Xform* destOtherXform; //Determine the max number of xforms present in sourceEmbers. @@ -84,7 +83,7 @@ public: for (xf = 0; xf < maxCount; xf++)//This will include both normal xforms and the final. { - destXform = destEmbers[i].GetTotalXform(xf, final); + auto destXform = destEmbers[i].GetTotalXform(xf, final); //Ensure every parametric variation contained in every xform at either position i - 1 or i + 1 is also contained in the dest xform. if (i > 0) @@ -607,7 +606,7 @@ public: //Keep translation linear. zlm[0] = zlm[1] = 0; - if (Xform* xform = embers[k].GetTotalXform(xfi)) + if (auto xform = embers[k].GetTotalXform(xfi)) { for (col = 0; col < 2; col++) { @@ -650,7 +649,7 @@ public: { for (k = 1; k < size; k++) { - if (Xform* xform = embers[k].GetTotalXform(xfi)) + if (auto xform = embers[k].GetTotalXform(xfi)) { //Adjust angles differently if an asymmetric case. if (xform->m_Wind[col] > 0 && cflag == 0) diff --git a/Source/Ember/Iterator.h b/Source/Ember/Iterator.h index 1d104ef..83d6809 100644 --- a/Source/Ember/Iterator.h +++ b/Source/Ember/Iterator.h @@ -98,7 +98,7 @@ public: { size_t i; size_t distribCount = ember.XaosPresent() ? ember.XformCount() + 1 : 1; - const Xform* xforms = ember.Xforms(); + auto xforms = ember.Xforms(); if (m_XformDistributions.size() < CHOOSE_XFORM_GRAIN * distribCount) m_XformDistributions.resize(CHOOSE_XFORM_GRAIN * distribCount); @@ -306,7 +306,7 @@ public: { size_t i, badVals = 0; Point tempPoint, p1; - Xform* xforms = ember.NonConstXforms(); + auto xforms = ember.NonConstXforms(); if (ember.ProjBits()) { @@ -473,7 +473,7 @@ public: size_t lastXformUsed = 0; size_t badVals = 0; Point tempPoint, p1; - Xform* xforms = ember.NonConstXforms(); + auto xforms = ember.NonConstXforms(); if (ember.ProjBits()) { diff --git a/Source/Ember/SheepTools.h b/Source/Ember/SheepTools.h index 7e5ad75..a2903e7 100644 --- a/Source/Ember/SheepTools.h +++ b/Source/Ember/SheepTools.h @@ -118,7 +118,7 @@ public: //First clear out any xforms that are not the final, and have a density of less than 0.001. for (i = 0; i < ember.XformCount(); i++) { - Xform* xform = ember.GetXform(i); + auto xform = ember.GetXform(i); if (xform->m_Weight < T(0.001)) { @@ -131,7 +131,7 @@ public: //Now consider all xforms, including final. for (i = 0; i < ember.TotalXformCount(); i++) { - Xform* xform = ember.GetTotalXform(i); + auto xform = ember.GetTotalXform(i); do { @@ -223,8 +223,8 @@ public: for (size_t i = 0; i < ember.TotalXformCount(); i++) { - Xform* xform1 = ember.GetTotalXform(i); - Xform* xform2 = mutation.GetTotalXform(i); + auto xform1 = ember.GetTotalXform(i); + auto xform2 = mutation.GetTotalXform(i); if (xform1 && xform2) { @@ -254,8 +254,8 @@ public: Random(mutation, useVars, sym, 2, maxVars); //Which xform to mutate? modXform = m_Rand.Rand() % ember.TotalXformCount(); - Xform* xform1 = ember.GetTotalXform(modXform); - Xform* xform2 = mutation.GetTotalXform(0); + auto xform1 = ember.GetTotalXform(modXform); + auto xform2 = mutation.GetTotalXform(0); os << "mutate xform " << modXform << " coefs"; //If less than 3 xforms, then change only the translation part. @@ -285,7 +285,7 @@ public: for (size_t i = 0; i < ember.TotalXformCount(); i++) { bool copy = (i > 0) && same; - Xform* xform = ember.GetTotalXform(i); + auto xform = ember.GetTotalXform(i); if (copy)//Copy the post from the first xform to the rest of them. { @@ -400,8 +400,8 @@ public: //Change all the coefs by a fraction of the random. for (size_t x = 0; x < ember.TotalXformCount(); x++) { - Xform* xform1 = ember.GetTotalXform(x); - Xform* xform2 = mutation.GetTotalXform(x); + auto xform1 = ember.GetTotalXform(x); + auto xform2 = mutation.GetTotalXform(x); for (glm::length_t i = 0; i < 2; i++) for (glm::length_t j = 0; j < 3; j++) @@ -498,7 +498,7 @@ public: { if (i < ember1.XformCount() && ember1.GetXform(i)->m_Weight > 0) { - Xform* xform = emberOut.GetXform(i); + auto xform = emberOut.GetXform(i); *xform = *ember1.GetXform(i); os << " 1"; got1 = 1; @@ -513,7 +513,7 @@ public: { if (i < ember0.XformCount() && ember0.GetXform(i)->m_Weight > 0) { - Xform* xform = emberOut.GetXform(i); + auto xform = emberOut.GetXform(i); *xform = *ember0.GetXform(i); os << " 0"; got0 = 1; @@ -654,7 +654,7 @@ public: //Loop over xforms. for (i = 0; i < ember.TotalXformCount(); i++) { - Xform* xform = ember.GetTotalXform(i); + auto xform = ember.GetTotalXform(i); xform->m_Weight = T(1) / ember.TotalXformCount(); xform->m_ColorX = m_Rand.Frand01();//Original pingponged between 0 and 1, which gives bad coloring. Ember does random instead. xform->m_ColorY = m_Rand.Frand01();//Will need to update this if 2D coordinates are ever supported. @@ -699,7 +699,7 @@ public: if (samed && i > 0) { //Copy the same variations from the previous xform. - Xform* prevXform = ember.GetXform(i - 1); + auto prevXform = ember.GetXform(i - 1); for (j = 0; j < prevXform->TotalVariationCount(); j++) if (xform->TotalVariationCount() < maxVars) @@ -893,10 +893,6 @@ public: /// Change palette if true, else don't void ChangeColors(Ember& ember, bool changePalette) { - size_t i; - Xform* xform0; - Xform* xform1; - if (changePalette) { if (m_PaletteList.Size()) @@ -910,14 +906,14 @@ public: } } - for (i = 0; i < ember.TotalXformCount(); i++) + for (size_t i = 0; i < ember.TotalXformCount(); i++) { ember.GetTotalXform(i)->m_ColorX = m_Rand.Frand01(); ember.GetTotalXform(i)->m_ColorY = m_Rand.Frand01(); } - xform0 = RandomXform(ember, -1); - xform1 = RandomXform(ember, ember.GetXformIndex(xform0)); + auto xform0 = RandomXform(ember, -1); + auto xform1 = RandomXform(ember, ember.GetXformIndex(xform0)); if (xform0 && (m_Rand.Rand() & 1)) { @@ -949,7 +945,7 @@ public: if (i != excluded) { - Xform* xform = ember.GetTotalXform(i); + auto xform = ember.GetTotalXform(i); if (xform->m_Weight > 0) return xform; @@ -975,8 +971,8 @@ public: //the result xforms before rotate is called. for (size_t i = 0; i < ember.TotalXformCount(); i++) { - Xform* xform1 = ember.GetTotalXform(i); - Xform* xform2 = rotated.GetTotalXform(i); + auto xform1 = ember.GetTotalXform(i); + auto xform2 = rotated.GetTotalXform(i); if (!xform1->m_Motion.empty()) xform2->ApplyMotion(*xform1, blend); @@ -1009,7 +1005,7 @@ public: for (i = 0; i < embers[si].TotalXformCount(); i++) { - Xform* xform = embers[si].GetTotalXform(i); + auto xform = embers[si].GetTotalXform(i); if (!xform->m_Motion.empty()) xform->ApplyMotion(*(prealign[si].GetTotalXform(i)), blend);//Apply motion parameters to result.xform[i] using blend parameter. diff --git a/Source/EmberCL/IterOpenCLKernelCreator.cpp b/Source/EmberCL/IterOpenCLKernelCreator.cpp index a047ca2..084896d 100644 --- a/Source/EmberCL/IterOpenCLKernelCreator.cpp +++ b/Source/EmberCL/IterOpenCLKernelCreator.cpp @@ -54,7 +54,7 @@ string IterOpenCLKernelCreator::CreateIterKernelString(const Ember& ember, for (i = 0; i < totalXformCount; i++) { - Xform* xform = ember.GetTotalXform(i); + auto xform = ember.GetTotalXform(i); bool needPrecalcSumSquares = false; bool needPrecalcSqrtSumSquares = false; bool needPrecalcAngles = false; @@ -831,9 +831,9 @@ bool IterOpenCLKernelCreator::IsBuildRequired(const Ember& ember1, const E for (i = 0; i < xformCount; i++) { - Xform* xform1 = ember1.GetTotalXform(i); - Xform* xform2 = ember2.GetTotalXform(i); - size_t varCount = xform1->TotalVariationCount(); + auto xform1 = ember1.GetTotalXform(i); + auto xform2 = ember2.GetTotalXform(i); + auto varCount = xform1->TotalVariationCount(); if (xform1->HasPost() != xform2->HasPost()) return true; diff --git a/Source/EmberCL/RendererCL.cpp b/Source/EmberCL/RendererCL.cpp index 3417d3b..03030e8 100644 --- a/Source/EmberCL/RendererCL.cpp +++ b/Source/EmberCL/RendererCL.cpp @@ -1750,7 +1750,7 @@ void RendererCL::ConvertEmber(Ember& ember, EmberCL& emberCL, for (size_t i = 0; i < ember.TotalXformCount() && i < xformsCL.size(); i++) { - Xform* xform = ember.GetTotalXform(i); + auto xform = ember.GetTotalXform(i); xformsCL[i].m_A = xform->m_Affine.A(); xformsCL[i].m_B = xform->m_Affine.B(); xformsCL[i].m_C = xform->m_Affine.C(); diff --git a/Source/EmberTester/EmberTester.cpp b/Source/EmberTester/EmberTester.cpp index 3d44d9e..4662b4c 100644 --- a/Source/EmberTester/EmberTester.cpp +++ b/Source/EmberTester/EmberTester.cpp @@ -1880,7 +1880,6 @@ void DistribTester() size_t distribCount = 1; size_t xformCount = 3; vector m_XformDistributions; - //const Xform* xforms = 3; size_t j = 0; vector weights { T(0.333333), T(1.0), T(0.25) }; double tempDensity = 0, currentDensityLimit = 0, densityPerElement; diff --git a/Source/Fractorium/Fractorium.cpp b/Source/Fractorium/Fractorium.cpp index 7e76e45..d89ae30 100644 --- a/Source/Fractorium/Fractorium.cpp +++ b/Source/Fractorium/Fractorium.cpp @@ -247,7 +247,7 @@ void FractoriumEmberController::ApplyXmlSavingTemplate(Ember& ember) /// True if the current ember contains a final xform, else false. bool Fractorium::HaveFinal() { - QComboBox* combo = ui.CurrentXformCombo; + auto combo = ui.CurrentXformCombo; return (combo->count() > 0 && combo->itemText(combo->count() - 1) == "Final"); } @@ -389,7 +389,9 @@ void Fractorium::dragEnterEvent(QDragEnterEvent* e) { if (e->mimeData()->hasUrls()) { - foreach (QUrl url, e->mimeData()->urls()) + auto urls = e->mimeData()->urls(); + + for (auto& url : urls) { QString localFile = url.toLocalFile(); QFileInfo fileInfo(localFile); @@ -426,7 +428,9 @@ void Fractorium::dropEvent(QDropEvent* e) if (e->mimeData()->hasUrls()) { - foreach (QUrl url, e->mimeData()->urls()) + auto urls = e->mimeData()->urls(); + + for (auto& url : urls) { QString localFile = url.toLocalFile(); QFileInfo fileInfo(localFile); diff --git a/Source/Fractorium/Fractorium.h b/Source/Fractorium/Fractorium.h index 3604838..364b32a 100644 --- a/Source/Fractorium/Fractorium.h +++ b/Source/Fractorium/Fractorium.h @@ -203,8 +203,10 @@ public slots: void OnXformWeightChanged(double d); void OnEqualWeightButtonClicked(bool checked); void OnXformNameChanged(int row, int col); + void OnXformAnimateCheckBoxStateChanged(int state); //Xforms Affine. + void OnLockAffineScaleCheckBoxStateChanged(int state); void OnPreAffineRowDoubleClicked(int logicalIndex); void OnPreAffineColDoubleClicked(int logicalIndex); void OnPostAffineRowDoubleClicked(int logicalIndex); diff --git a/Source/Fractorium/Fractorium.ui b/Source/Fractorium/Fractorium.ui index 46a4dfd..bffb2cd 100644 --- a/Source/Fractorium/Fractorium.ui +++ b/Source/Fractorium/Fractorium.ui @@ -2350,7 +2350,7 @@ 291 - 613 + 632 @@ -2756,7 +2756,7 @@ - + @@ -2780,7 +2780,7 @@ QTabWidget::Triangular - 2 + 1 @@ -3444,6 +3444,13 @@ 5 + + + + Lock Affine Scale + + + @@ -3464,7 +3471,7 @@ 0 0 263 - 743 + 700 @@ -5479,7 +5486,7 @@ 0 0 259 - 673 + 652 @@ -5529,6 +5536,16 @@ + + + + <html><head/><body><p>Rotate this xform when creating a sequence for animation</p></body></html> + + + Animate + + + diff --git a/Source/Fractorium/FractoriumEmberController.cpp b/Source/Fractorium/FractoriumEmberController.cpp index 5c5290a..b114130 100644 --- a/Source/Fractorium/FractoriumEmberController.cpp +++ b/Source/Fractorium/FractoriumEmberController.cpp @@ -15,6 +15,7 @@ FractoriumEmberControllerBase::FractoriumEmberControllerBase(Fractorium* fractor m_Shared = true; m_FailedRenders = 0; m_UndoIndex = 0; + m_LockedScale = 1; m_RenderType = eRendererType::CPU_RENDERER; m_OutputTexID = 0; m_SubBatchCount = 1;//Will be ovewritten by the options on first render. @@ -224,6 +225,7 @@ void FractoriumEmberController::Update(std::function func, bool /// /// 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. +/// If the update type is UPDATE_CURRENT_AND_SELECTED, and the current is not among those selected, then the function will be called on the currently selected xform as well. /// /// The function to call /// Whether to apply this update operation on the current, all or selected xforms. Default: eXformUpdate::UPDATE_CURRENT. @@ -240,19 +242,48 @@ void FractoriumEmberController::UpdateXform(std::function*)> fu { case eXformUpdate::UPDATE_CURRENT: { - if (Xform* xform = CurrentXform()) + if (auto xform = CurrentXform()) func(xform); } break; + case eXformUpdate::UPDATE_CURRENT_AND_SELECTED: + { + bool currentDone = false; + auto current = CurrentXform(); + + while (auto xform = m_Ember.GetTotalXform(i)) + { + if (auto child = m_Fractorium->m_XformsSelectionLayout->itemAt(i)) + { + if (auto* w = qobject_cast(child->widget())) + { + if (w->isChecked()) + { + func(xform); + + if (xform == current) + currentDone = true; + } + } + } + + i++; + } + + if (!currentDone)//Current was not among those selected, so apply to it. + func(current); + } + 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))) + while (auto xform = (doFinal ? m_Ember.GetTotalXform(i) : m_Ember.GetXform(i))) { - if (QLayoutItem* child = m_Fractorium->m_XformsSelectionLayout->itemAt(i)) + if (auto child = m_Fractorium->m_XformsSelectionLayout->itemAt(i)) { if (auto* w = qobject_cast(child->widget())) { @@ -269,14 +300,14 @@ void FractoriumEmberController::UpdateXform(std::function*)> fu 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()) + if (auto xform = CurrentXform()) func(xform); } break; case eXformUpdate::UPDATE_ALL: { - while (Xform* xform = m_Ember.GetTotalXform(i++)) + while (auto xform = m_Ember.GetTotalXform(i++)) func(xform); } break; @@ -284,7 +315,7 @@ void FractoriumEmberController::UpdateXform(std::function*)> fu case eXformUpdate::UPDATE_ALL_EXCEPT_FINAL: default: { - while (Xform* xform = m_Ember.GetXform(i++)) + while (auto xform = m_Ember.GetXform(i++)) func(xform); } break; @@ -325,6 +356,7 @@ void FractoriumEmberController::SetEmberPrivate(const Ember& ember, bool v string filename = "last.flame"; writer.Save(filename.c_str(), m_Ember, 0, true, false, true); m_GLController->ResetMouseState(); + m_Fractorium->ui.LockAffineCheckBox->setChecked(false); FillXforms();//Must do this first because the palette setup in FillParamTablesAndPalette() uses the xforms combo. FillParamTablesAndPalette(); FillSummary(); diff --git a/Source/Fractorium/FractoriumEmberController.h b/Source/Fractorium/FractoriumEmberController.h index eef6487..684d32f 100644 --- a/Source/Fractorium/FractoriumEmberController.h +++ b/Source/Fractorium/FractoriumEmberController.h @@ -16,7 +16,7 @@ enum class eEditUndoState : et { REGULAR_EDIT, UNDO_REDO, EDIT_UNDO }; /// /// An enum representing which xforms an update should be applied to. /// -enum class eXformUpdate : et { UPDATE_CURRENT, UPDATE_SELECTED, UPDATE_SELECTED_EXCEPT_FINAL, UPDATE_ALL, UPDATE_ALL_EXCEPT_FINAL }; +enum class eXformUpdate : et { UPDATE_CURRENT, UPDATE_SELECTED, UPDATE_CURRENT_AND_SELECTED, UPDATE_SELECTED_EXCEPT_FINAL, UPDATE_ALL, UPDATE_ALL_EXCEPT_FINAL }; /// /// FractoriumEmberController and Fractorium need each other, but each can't include the other. @@ -161,6 +161,7 @@ public: virtual void XformWeightChanged(double d) { } virtual void EqualizeWeights() { } virtual void XformNameChanged(int row, int col) { } + virtual void XformAnimateChanged(int state) { } virtual void FillXforms(int index = 0) { } //Xforms Affine. @@ -171,6 +172,8 @@ public: virtual void ScaleXforms(double scale, bool pre) { } virtual void ResetXformsAffine(bool pre) { } virtual void FillBothAffines() { } + double LockedScale() { return m_LockedScale; } + virtual void LockAffineScaleCheckBoxStateChanged(int state) { } //Xforms Color. virtual void XformColorIndexChanged(double d, bool updateRender) { } @@ -241,6 +244,7 @@ protected: uint m_SubBatchCount; uint m_FailedRenders; uint m_UndoIndex; + double m_LockedScale; eRendererType m_RenderType; eEditUndoState m_EditState; GLuint m_OutputTexID; @@ -397,9 +401,11 @@ public: virtual void XformWeightChanged(double d) override; virtual void EqualizeWeights() override; virtual void XformNameChanged(int row, int col) override; + virtual void XformAnimateChanged(int state) override; virtual void FillXforms(int index = 0) override; void FillWithXform(Xform* xform); Xform* CurrentXform(); + void UpdateXform(std::function*)> func, eXformUpdate updateType = eXformUpdate::UPDATE_CURRENT, bool updateRender = true, eProcessAction action = eProcessAction::FULL_RENDER); //Xforms Affine. virtual void AffineSetHelper(double d, int index, bool pre) override; @@ -409,7 +415,10 @@ public: virtual void ScaleXforms(double scale, bool pre) override; virtual void ResetXformsAffine(bool pre) override; virtual void FillBothAffines() override; + virtual void LockAffineScaleCheckBoxStateChanged(int state) override; void FillAffineWithXform(Xform* xform, bool pre); + T AffineScaleCurrentToLocked(); + T AffineScaleLockedToCurrent(); //Xforms Color. virtual void XformColorIndexChanged(double d, bool updateRender) override; @@ -473,7 +482,6 @@ private: 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 = eProcessAction::FULL_RENDER); //Palette. void UpdateAdjustedPaletteGUI(Palette& palette); diff --git a/Source/Fractorium/FractoriumMenus.cpp b/Source/Fractorium/FractoriumMenus.cpp index b8370ad..3c7d67e 100644 --- a/Source/Fractorium/FractoriumMenus.cpp +++ b/Source/Fractorium/FractoriumMenus.cpp @@ -176,7 +176,7 @@ void FractoriumEmberController::OpenAndPrepFiles(const QStringList& filenames StopPreviewRender(); emberFile.m_Filename = filenames[0]; - foreach (const QString& filename, filenames) + for (auto& filename : filenames) { embers.clear(); @@ -694,7 +694,7 @@ void Fractorium::OnActionResetWorkspace(bool checked) template void FractoriumEmberController::AddReflectiveSymmetry() { - QComboBox* combo = m_Fractorium->ui.CurrentXformCombo; + auto combo = m_Fractorium->ui.CurrentXformCombo; Update([&]() { m_Ember.AddSymmetry(-1, m_Rand); @@ -712,7 +712,7 @@ void Fractorium::OnActionAddReflectiveSymmetry(bool checked) { m_Controller->Add template void FractoriumEmberController::AddRotationalSymmetry() { - QComboBox* combo = m_Fractorium->ui.CurrentXformCombo; + auto combo = m_Fractorium->ui.CurrentXformCombo; Update([&]() { m_Ember.AddSymmetry(2, m_Rand); @@ -730,7 +730,7 @@ void Fractorium::OnActionAddRotationalSymmetry(bool checked) { m_Controller->Add template void FractoriumEmberController::AddBothSymmetry() { - QComboBox* combo = m_Fractorium->ui.CurrentXformCombo; + auto combo = m_Fractorium->ui.CurrentXformCombo; Update([&]() { m_Ember.AddSymmetry(-2, m_Rand); @@ -772,7 +772,7 @@ void FractoriumEmberController::ClearFlame() if (m_Ember.XformCount() == 1) { - if (Xform* xform = m_Ember.GetXform(0)) + if (auto xform = m_Ember.GetXform(0)) { xform->Clear(); xform->ParentEmber(&m_Ember); diff --git a/Source/Fractorium/FractoriumPalette.cpp b/Source/Fractorium/FractoriumPalette.cpp index de9f1f1..65ea4ce 100644 --- a/Source/Fractorium/FractoriumPalette.cpp +++ b/Source/Fractorium/FractoriumPalette.cpp @@ -10,38 +10,30 @@ void Fractorium::InitPaletteUI() { int spinHeight = 20, row = 0; - QTableWidget* paletteTable = ui.PaletteListTable; - QTableWidget* palettePreviewTable = ui.PalettePreviewTable; - + auto paletteTable = ui.PaletteListTable; + auto palettePreviewTable = ui.PalettePreviewTable; connect(ui.PaletteFilenameCombo, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(OnPaletteFilenameComboChanged(const QString&)), Qt::QueuedConnection); - connect(paletteTable, SIGNAL(cellClicked(int, int)), this, SLOT(OnPaletteCellClicked(int, int)), Qt::QueuedConnection); connect(paletteTable, SIGNAL(cellDoubleClicked(int, int)), this, SLOT(OnPaletteCellDoubleClicked(int, int)), Qt::QueuedConnection); - - //Palette adjustment table. - QTableWidget* table = ui.PaletteAdjustTable; + //Palette adjustment table. + auto table = ui.PaletteAdjustTable; table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);//Split width over all columns evenly. - SetupSpinner(table, this, row, 1, m_PaletteHueSpin, spinHeight, -180, 180, 1, SIGNAL(valueChanged(int)), SLOT(OnPaletteAdjust(int)), true, 0, 0, 0); SetupSpinner(table, this, row, 1, m_PaletteSaturationSpin, spinHeight, -100, 100, 1, SIGNAL(valueChanged(int)), SLOT(OnPaletteAdjust(int)), true, 0, 0, 0); SetupSpinner(table, this, row, 1, m_PaletteBrightnessSpin, spinHeight, -255, 255, 1, SIGNAL(valueChanged(int)), SLOT(OnPaletteAdjust(int)), true, 0, 0, 0); row = 0; - SetupSpinner(table, this, row, 3, m_PaletteContrastSpin, spinHeight, -100, 100, 1, SIGNAL(valueChanged(int)), SLOT(OnPaletteAdjust(int)), true, 0, 0, 0); SetupSpinner(table, this, row, 3, m_PaletteBlurSpin, spinHeight, 0, 127, 1, SIGNAL(valueChanged(int)), SLOT(OnPaletteAdjust(int)), true, 0, 0, 0); SetupSpinner(table, this, row, 3, m_PaletteFrequencySpin, spinHeight, 1, 10, 1, SIGNAL(valueChanged(int)), SLOT(OnPaletteAdjust(int)), true, 1, 1, 1); - connect(ui.PaletteRandomSelect, SIGNAL(clicked(bool)), this, SLOT(OnPaletteRandomSelectButtonClicked(bool)), Qt::QueuedConnection); connect(ui.PaletteRandomAdjust, SIGNAL(clicked(bool)), this, SLOT(OnPaletteRandomAdjustButtonClicked(bool)), Qt::QueuedConnection); - //Preview table. palettePreviewTable->setRowCount(1); palettePreviewTable->setColumnWidth(1, 260);//256 plus small margin on each side. - QTableWidgetItem* previewNameCol = new QTableWidgetItem(""); + auto previewNameCol = new QTableWidgetItem(""); palettePreviewTable->setItem(0, 0, previewNameCol); - QTableWidgetItem* previewPaletteItem = new QTableWidgetItem(); + auto previewPaletteItem = new QTableWidgetItem(); palettePreviewTable->setItem(0, 1, previewPaletteItem); - connect(ui.PaletteFilterLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(OnPaletteFilterLineEditTextChanged(const QString&))); connect(ui.PaletteFilterClearButton, SIGNAL(clicked(bool)), this, SLOT(OnPaletteFilterClearButtonClicked(bool))); paletteTable->setColumnWidth(1, 260);//256 plus small margin on each side. @@ -60,7 +52,6 @@ template int FractoriumEmberController::InitPaletteList(const string& s) { QDirIterator it(s.c_str(), QStringList() << "*.xml", QDir::Files, QDirIterator::FollowSymlinks); - m_PaletteList.Clear(); m_Fractorium->ui.PaletteFilenameCombo->clear(); m_Fractorium->ui.PaletteFilenameCombo->setProperty("path", QString::fromStdString(s)); @@ -69,7 +60,7 @@ int FractoriumEmberController::InitPaletteList(const string& s) { auto path = it.next().toStdString(); auto qfilename = it.fileName(); - + if (m_PaletteList.Add(path)) m_Fractorium->ui.PaletteFilenameCombo->addItem(qfilename); } @@ -89,9 +80,8 @@ bool FractoriumEmberController::FillPaletteTable(const string& s) { if (!s.empty())//This occasionally seems to get called with an empty string for reasons unknown. { - QTableWidget* paletteTable = m_Fractorium->ui.PaletteListTable; - QTableWidget* palettePreviewTable = m_Fractorium->ui.PalettePreviewTable; - + auto paletteTable = m_Fractorium->ui.PaletteListTable; + auto palettePreviewTable = m_Fractorium->ui.PalettePreviewTable; m_CurrentPaletteFilePath = m_Fractorium->ui.PaletteFilenameCombo->property("path").toString().toStdString() + "/" + s; if (size_t paletteSize = m_PaletteList.Size(m_CurrentPaletteFilePath)) @@ -99,14 +89,11 @@ bool FractoriumEmberController::FillPaletteTable(const string& s) paletteTable->clear(); paletteTable->blockSignals(true); paletteTable->setRowCount(paletteSize); - //Headers get removed when clearing, so must re-create here. - QTableWidgetItem* nameHeader = new QTableWidgetItem("Name"); - QTableWidgetItem* paletteHeader = new QTableWidgetItem("Palette"); - + auto nameHeader = new QTableWidgetItem("Name"); + auto paletteHeader = new QTableWidgetItem("Palette"); nameHeader->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); paletteHeader->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); - paletteTable->setHorizontalHeaderItem(0, nameHeader); paletteTable->setHorizontalHeaderItem(1, paletteHeader); @@ -117,13 +104,10 @@ bool FractoriumEmberController::FillPaletteTable(const string& s) { auto v = p->MakeRgbPaletteBlock(PALETTE_CELL_HEIGHT); auto nameCol = new QTableWidgetItem(p->m_Name.c_str()); - nameCol->setToolTip(p->m_Name.c_str()); paletteTable->setItem(i, 0, nameCol); - QImage image(v.data(), p->Size(), PALETTE_CELL_HEIGHT, QImage::Format_RGB888); auto paletteItem = new PaletteTableWidgetItem(p); - paletteItem->setData(Qt::DecorationRole, QPixmap::fromImage(image)); paletteTable->setItem(i, 1, paletteItem); } @@ -135,7 +119,6 @@ bool FractoriumEmberController::FillPaletteTable(const string& s) else { vector errors = m_PaletteList.ErrorReport(); - m_Fractorium->ErrorReportToQTextEdit(errors, m_Fractorium->ui.InfoFileOpeningTextEdit); m_Fractorium->ShowCritical("Palette Read Error", "Could not load palette file, all images will be black. See info tab for details."); } @@ -163,7 +146,6 @@ void FractoriumEmberController::ApplyPaletteToEmber() double brightness = double(m_Fractorium->m_PaletteBrightnessSpin->value() / 255.0); double contrast = double(m_Fractorium->m_PaletteContrastSpin->value() > 0 ? (m_Fractorium->m_PaletteContrastSpin->value() * 2) : m_Fractorium->m_PaletteContrastSpin->value()) / 100.0; double hue = double(m_Fractorium->m_PaletteHueSpin->value()) / 360.0; - //Use the temp palette as the base and apply the adjustments gotten from the GUI and save the result in the ember palette. m_TempPalette.MakeAdjustedPalette(m_Ember.m_Palette, 0, hue, sat, brightness, contrast, blur, freq); } @@ -177,23 +159,21 @@ void FractoriumEmberController::ApplyPaletteToEmber() template void FractoriumEmberController::UpdateAdjustedPaletteGUI(Palette& palette) { - Xform* xform = CurrentXform(); - QTableWidget* palettePreviewTable = m_Fractorium->ui.PalettePreviewTable; - QTableWidgetItem* previewPaletteItem = palettePreviewTable->item(0, 1); - QString paletteName = QString::fromStdString(m_Ember.m_Palette.m_Name); + auto xform = CurrentXform(); + auto palettePreviewTable = m_Fractorium->ui.PalettePreviewTable; + auto previewPaletteItem = palettePreviewTable->item(0, 1); + auto paletteName = QString::fromStdString(m_Ember.m_Palette.m_Name); if (previewPaletteItem)//This can be null if the palette file was moved or corrupted. { //Use the adjusted palette to fill the preview palette control so the user can see the effects of applying the adjustements. vector v = palette.MakeRgbPaletteBlock(PALETTE_CELL_HEIGHT);//Make the palette repeat for PALETTE_CELL_HEIGHT rows. - m_FinalPaletteImage = QImage(palette.Size(), PALETTE_CELL_HEIGHT, QImage::Format_RGB888);//Create a QImage out of it. memcpy(m_FinalPaletteImage.scanLine(0), v.data(), v.size() * sizeof(v[0]));//Memcpy the data in. - QPixmap pixmap = QPixmap::fromImage(m_FinalPaletteImage);//Create a QPixmap out of the QImage. + auto pixmap = QPixmap::fromImage(m_FinalPaletteImage);//Create a QPixmap out of the QImage. previewPaletteItem->setData(Qt::DecorationRole, pixmap.scaled(QSize(pixmap.width(), palettePreviewTable->rowHeight(0) + 2), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));//Set the pixmap on the palette tab. m_Fractorium->SetPaletteTableItem(&pixmap, m_Fractorium->ui.XformPaletteRefTable, m_Fractorium->m_PaletteRefItem, 0, 0);//Set the palette ref table on the xforms | color tab. - - QTableWidgetItem* previewNameItem = palettePreviewTable->item(0, 0); + auto previewNameItem = palettePreviewTable->item(0, 0); previewNameItem->setText(paletteName);//Finally, set the name of the palette to be both the text and the tooltip. previewNameItem->setToolTip(paletteName); } @@ -308,7 +288,6 @@ void Fractorium::OnPaletteRandomSelectButtonClicked(bool checked) void Fractorium::OnPaletteRandomAdjustButtonClicked(bool checked) { QTIsaac* gRand = QTIsaac::GlobalRand.get(); - m_PaletteHueSpin->setValue(-180 + gRand->Rand(361)); m_PaletteSaturationSpin->setValue(-50 + gRand->Rand(101));//Full range of these leads to bad palettes, so clamp range. m_PaletteBrightnessSpin->setValue(-50 + gRand->Rand(101)); @@ -338,7 +317,6 @@ void Fractorium::OnPaletteRandomAdjustButtonClicked(bool checked) void Fractorium::OnPaletteFilterLineEditTextChanged(const QString& text) { auto table = ui.PaletteListTable; - table->setUpdatesEnabled(false); for (int i = 0; i < table->rowCount(); i++) @@ -407,5 +385,5 @@ void Fractorium::SetPaletteFileComboIndex(const string& filename) template class FractoriumEmberController; #ifdef DO_DOUBLE - template class FractoriumEmberController; +template class FractoriumEmberController; #endif diff --git a/Source/Fractorium/FractoriumParams.cpp b/Source/Fractorium/FractoriumParams.cpp index 25bedef..891e96e 100644 --- a/Source/Fractorium/FractoriumParams.cpp +++ b/Source/Fractorium/FractoriumParams.cpp @@ -167,12 +167,12 @@ void FractoriumEmberController::BackgroundChanged(const QColor& color) Update([&] { int itemRow = 5; - QTableWidget* colorTable = m_Fractorium->ui.ColorTable; + auto colorTable = m_Fractorium->ui.ColorTable; colorTable->item(itemRow, 1)->setBackgroundColor(color); - QString r = ToString(color.red()); - QString g = ToString(color.green()); - QString b = ToString(color.blue()); + auto r = ToString(color.red()); + auto g = ToString(color.green()); + auto b = ToString(color.blue()); colorTable->item(itemRow, 1)->setTextColor(VisibleColor(color)); colorTable->item(itemRow, 1)->setText("rgb(" + r + ", " + g + ", " + b + ")"); diff --git a/Source/Fractorium/FractoriumXaos.cpp b/Source/Fractorium/FractoriumXaos.cpp index abd8326..859f32b 100644 --- a/Source/Fractorium/FractoriumXaos.cpp +++ b/Source/Fractorium/FractoriumXaos.cpp @@ -56,7 +56,7 @@ void FractoriumEmberController::FillXaos() template QString FractoriumEmberController::MakeXaosNameString(uint i) { - Xform* xform = m_Ember.GetXform(i); + auto xform = m_Ember.GetXform(i); QString name; //if (xform) //{ diff --git a/Source/Fractorium/FractoriumXforms.cpp b/Source/Fractorium/FractoriumXforms.cpp index 3aec572..91dc804 100644 --- a/Source/Fractorium/FractoriumXforms.cpp +++ b/Source/Fractorium/FractoriumXforms.cpp @@ -7,7 +7,6 @@ void Fractorium::InitXformsUI() { int spinHeight = 20, row = 0; - connect(ui.AddXformButton, SIGNAL(clicked(bool)), this, SLOT(OnAddXformButtonClicked(bool)), Qt::QueuedConnection); connect(ui.AddLinkedXformButton, SIGNAL(clicked(bool)), this, SLOT(OnAddLinkedXformButtonClicked(bool)), Qt::QueuedConnection); connect(ui.DuplicateXformButton, SIGNAL(clicked(bool)), this, SLOT(OnDuplicateXformButtonClicked(bool)), Qt::QueuedConnection); @@ -15,7 +14,7 @@ void Fractorium::InitXformsUI() connect(ui.DeleteXformButton, SIGNAL(clicked(bool)), this, SLOT(OnDeleteXformButtonClicked(bool)), Qt::QueuedConnection); connect(ui.AddFinalXformButton, SIGNAL(clicked(bool)), this, SLOT(OnAddFinalXformButtonClicked(bool)), Qt::QueuedConnection); connect(ui.CurrentXformCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(OnCurrentXformComboChanged(int)), Qt::QueuedConnection); - + connect(ui.AnimateXformCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnXformAnimateCheckBoxStateChanged(int)), Qt::QueuedConnection); SetFixedTableHeader(ui.XformWeightNameTable->horizontalHeader()); //Use SetupSpinner() just to create the spinner, but use col of -1 to prevent it from being added to the table. SetupSpinner(ui.XformWeightNameTable, this, row, -1, m_XformWeightSpin, spinHeight, 0, 1000, 0.05, SIGNAL(valueChanged(double)), SLOT(OnXformWeightChanged(double)), false, 0, 1, 0); @@ -25,13 +24,11 @@ void Fractorium::InitXformsUI() m_XformWeightSpinnerButtonWidget->m_Button->setToolTip("Equalize weights"); m_XformWeightSpinnerButtonWidget->m_Button->setStyleSheet("text-align: center center"); connect(m_XformWeightSpinnerButtonWidget->m_Button, SIGNAL(clicked(bool)), this, SLOT(OnEqualWeightButtonClicked(bool)), Qt::QueuedConnection); - ui.XformWeightNameTable->setCellWidget(0, 0, m_XformWeightSpinnerButtonWidget); ui.XformWeightNameTable->setItem(0, 1, new QTableWidgetItem()); connect(ui.XformWeightNameTable, SIGNAL(cellChanged(int, int)), this, SLOT(OnXformNameChanged(int, int)), Qt::QueuedConnection); - ui.CurrentXformCombo->setProperty("soloxform", -1); -#ifndef WIN32 +#ifndef WIN32 //For some reason linux makes these 24x24, even though the designer explicitly says 16x16. ui.AddXformButton->setIconSize(QSize(16, 16)); ui.DuplicateXformButton->setIconSize(QSize(16, 16)); @@ -70,19 +67,15 @@ void Fractorium::CurrentXform(uint i) template void FractoriumEmberController::CurrentXformComboChanged(int index) { - if (Xform* xform = m_Ember.GetTotalXform(index)) + if (auto xform = m_Ember.GetTotalXform(index)) { FillWithXform(xform); m_GLController->SetSelectedXform(xform); - int solo = m_Fractorium->ui.CurrentXformCombo->property("soloxform").toInt(); - m_Fractorium->ui.SoloXformCheckBox->blockSignals(true); m_Fractorium->ui.SoloXformCheckBox->setChecked(solo == index); m_Fractorium->ui.SoloXformCheckBox->blockSignals(false); - bool enable = !IsFinal(CurrentXform()); - m_Fractorium->ui.DuplicateXformButton->setEnabled(enable); m_Fractorium->m_XformWeightSpin->setEnabled(enable); m_Fractorium->ui.SoloXformCheckBox->setEnabled(enable); @@ -105,7 +98,6 @@ void FractoriumEmberController::AddXform() Update([&]() { Xform newXform; - newXform.m_Weight = 0.25; newXform.m_ColorX = m_Rand.Frand01(); m_Ember.AddXform(newXform); @@ -131,7 +123,6 @@ void FractoriumEmberController::AddLinkedXform() { size_t i, count = m_Ember.XformCount(); Xform newXform; - newXform.m_Weight = 0.5; newXform.m_Opacity = 1; newXform.m_ColorSpeed = 1; @@ -175,21 +166,18 @@ template void FractoriumEmberController::DuplicateXform() { 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; + auto combo = m_Fractorium->ui.CurrentXformCombo; for (auto& it : vec) m_Ember.AddXform(it); - + int index = m_Ember.TotalXformCount() - (m_Ember.UseFinalXform() ? 2 : 1);//Set index to the last item before final. FillXforms(index);//Handles xaos. }); @@ -210,7 +198,6 @@ void FractoriumEmberController::ClearXform() { xform->ClearAndDeleteVariations();//Note xaos is left alone. }, eXformUpdate::UPDATE_SELECTED); - FillVariationTreeWithXform(CurrentXform()); } @@ -229,17 +216,16 @@ void FractoriumEmberController::DeleteXforms() int i = 0, offset = 0, current = 0, checked = 0; bool haveFinal = false; size_t count; - QComboBox* combo = m_Fractorium->ui.CurrentXformCombo; - + auto 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) + m_Fractorium->ForEachXformCheckbox([&](int i, QCheckBox * w) { count = m_Ember.TotalXformCount(); haveFinal = m_Ember.UseFinalXform();//Requery every time. - + if (w->isChecked()) checked++; @@ -252,20 +238,18 @@ void FractoriumEmberController::DeleteXforms() 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)) + !(haveFinal && count <= 2 && current == 0) &&//Again disallow deleting the only remaining non-final xform. + !(!haveFinal && count == 1)) { m_Ember.DeleteTotalXform(current); offset++; @@ -298,7 +282,6 @@ void FractoriumEmberController::AddFinalXform() { Xform final; auto combo = m_Fractorium->ui.CurrentXformCombo; - final.AddVariation(new LinearVariation());//Just a placeholder so other parts of the code don't see it as being empty. m_Ember.SetFinalXform(final); int index = m_Ember.TotalXformCount() - 1;//Set index to the last item. @@ -322,7 +305,6 @@ void FractoriumEmberController::XformWeightChanged(double d) { xform->m_Weight = d; }, eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL); - SetNormalizedWeightText(CurrentXform()); } @@ -356,19 +338,32 @@ void FractoriumEmberController::XformNameChanged(int row, int col) UpdateXform([&] (Xform* 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)); - //} + XformCheckboxAt(index, [&](QCheckBox * checkbox) { checkbox->setText(MakeXformCaption(index)); }); }, eXformUpdate::UPDATE_CURRENT, false); } void Fractorium::OnXformNameChanged(int row, int col) { m_Controller->XformNameChanged(row, col); } +/// +/// Set the animate field of the selected xforms. +/// This has no effect on interactive rendering, it only sets a value +/// that will later be saved to Xml when the user saves. +/// This value is observed when creating sequences for animation. +/// Called when the user toggles the animate xform checkbox. +/// +/// 1 for checked, else false +template +void FractoriumEmberController::XformAnimateChanged(int state) +{ + UpdateXform([&](Xform* xform) + { + xform->m_Animate = state > 0 ? 1 : 0; + }, eXformUpdate::UPDATE_SELECTED, false); +} + +void Fractorium::OnXformAnimateCheckBoxStateChanged(int state) { m_Controller->XformAnimateChanged(state); } + /// /// Fill all GUI widgets with values from the passed in xform. /// @@ -378,8 +373,9 @@ void FractoriumEmberController::FillWithXform(Xform* xform)//Need to see w { m_Fractorium->m_XformWeightSpin->SetValueStealth(xform->m_Weight); SetNormalizedWeightText(xform); + m_Fractorium->ui.AnimateXformCheckBox->setChecked(xform->m_Animate > 0 ? true : false);//Make a signal/slot to handle checking this. - if (QTableWidgetItem* item = m_Fractorium->ui.XformWeightNameTable->item(0, 1)) + if (auto item = m_Fractorium->ui.XformWeightNameTable->item(0, 1)) { m_Fractorium->ui.XformWeightNameTable->blockSignals(true); item->setText(QString::fromStdString(xform->m_Name)); @@ -402,7 +398,6 @@ void FractoriumEmberController::SetNormalizedWeightText(Xform* xform) if (xform) { int index = m_Ember.GetXformIndex(xform); - m_Ember.CalcNormalizedWeights(m_NormalizedWeights); if (index != -1 && index < m_NormalizedWeights.size()) @@ -433,29 +428,27 @@ void FractoriumEmberController::FillXforms(int index) { int i = 0, count = int(XformCount()); auto combo = m_Fractorium->ui.CurrentXformCombo; - combo->blockSignals(true); combo->clear(); - //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_Fractorium->m_XformComboIcons[i % XFORM_COLOR_COUNT]); } - + i = 0; + while (i < count) { 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); @@ -464,31 +457,28 @@ void FractoriumEmberController::FillXforms(int index) 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); if (index < combo->count()) combo->setCurrentIndex(index); - + m_Fractorium->FillXaosTable(); m_Fractorium->OnSoloXformCheckBoxStateChanged(Qt::Unchecked); m_Fractorium->OnCurrentXformComboChanged(index);//Make sure the event gets called, because it won't if the zero index is already selected. @@ -497,5 +487,5 @@ void FractoriumEmberController::FillXforms(int index) template class FractoriumEmberController; #ifdef DO_DOUBLE - template class FractoriumEmberController; +template class FractoriumEmberController; #endif diff --git a/Source/Fractorium/FractoriumXformsAffine.cpp b/Source/Fractorium/FractoriumXformsAffine.cpp index 42c042b..ab131cb 100644 --- a/Source/Fractorium/FractoriumXformsAffine.cpp +++ b/Source/Fractorium/FractoriumXformsAffine.cpp @@ -8,8 +8,8 @@ void Fractorium::InitXformsAffineUI() { int row = 0, affinePrec = 6, spinHeight = 20; double affineStep = 0.01, affineMin = std::numeric_limits::lowest(), affineMax = std::numeric_limits::max(); - QTableWidget* table = ui.PreAffineTable; - + auto table = ui.PreAffineTable; + connect(ui.LockAffineCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnLockAffineScaleCheckBoxStateChanged(int)), Qt::QueuedConnection); table->verticalHeader()->setVisible(true);//The designer continually clobbers these values, so must manually set them here. table->horizontalHeader()->setVisible(true); table->verticalHeader()->setSectionsClickable(true); @@ -18,7 +18,6 @@ void Fractorium::InitXformsAffineUI() table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); connect(table->verticalHeader(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(OnPreAffineRowDoubleClicked(int)), Qt::QueuedConnection); connect(table->horizontalHeader(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(OnPreAffineColDoubleClicked(int)), Qt::QueuedConnection); - //Pre affine spinners. SetupAffineSpinner(table, this, 0, 0, m_PreX1Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnX1Changed(double))); SetupAffineSpinner(table, this, 0, 1, m_PreX2Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnX2Changed(double))); @@ -26,7 +25,6 @@ void Fractorium::InitXformsAffineUI() SetupAffineSpinner(table, this, 1, 1, m_PreY2Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnY2Changed(double))); SetupAffineSpinner(table, this, 2, 0, m_PreO1Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnO1Changed(double))); SetupAffineSpinner(table, this, 2, 1, m_PreO2Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnO2Changed(double))); - table = ui.PostAffineTable; table->verticalHeader()->setVisible(true);//The designer continually clobbers these values, so must manually set them here. table->horizontalHeader()->setVisible(true); @@ -34,10 +32,8 @@ void Fractorium::InitXformsAffineUI() table->horizontalHeader()->setSectionsClickable(true); table->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch); table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); - connect(table->verticalHeader(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(OnPostAffineRowDoubleClicked(int)), Qt::QueuedConnection); connect(table->horizontalHeader(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(OnPostAffineColDoubleClicked(int)), Qt::QueuedConnection); - //Post affine spinners. SetupAffineSpinner(table, this, 0, 0, m_PostX1Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnX1Changed(double))); SetupAffineSpinner(table, this, 0, 1, m_PostX2Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnX2Changed(double))); @@ -45,73 +41,60 @@ void Fractorium::InitXformsAffineUI() SetupAffineSpinner(table, this, 1, 1, m_PostY2Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnY2Changed(double))); SetupAffineSpinner(table, this, 2, 0, m_PostO1Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnO1Changed(double))); SetupAffineSpinner(table, this, 2, 1, m_PostO2Spin, spinHeight, affineMin, affineMax, affineStep, affinePrec, SIGNAL(valueChanged(double)), SLOT(OnO2Changed(double))); - - QDoubleValidator* preRotateVal = new QDoubleValidator(ui.PreRotateCombo); preRotateVal->setLocale(QLocale::system()); - QDoubleValidator* preMoveVal = new QDoubleValidator(ui.PreMoveCombo); preMoveVal->setLocale(QLocale::system()); - QDoubleValidator* preScaleVal = new QDoubleValidator(ui.PreScaleCombo); preScaleVal->setLocale(QLocale::system()); - - QDoubleValidator* postRotateVal = new QDoubleValidator(ui.PostRotateCombo); postRotateVal->setLocale(QLocale::system()); - QDoubleValidator* postMoveVal = new QDoubleValidator(ui.PostMoveCombo); postMoveVal->setLocale(QLocale::system()); - QDoubleValidator* postScaleVal = new QDoubleValidator(ui.PostScaleCombo); postScaleVal->setLocale(QLocale::system()); - + auto preRotateVal = new QDoubleValidator(ui.PreRotateCombo); preRotateVal->setLocale(QLocale::system()); + auto preMoveVal = new QDoubleValidator(ui.PreMoveCombo); preMoveVal->setLocale(QLocale::system()); + auto preScaleVal = new QDoubleValidator(ui.PreScaleCombo); preScaleVal->setLocale(QLocale::system()); + auto postRotateVal = new QDoubleValidator(ui.PostRotateCombo); postRotateVal->setLocale(QLocale::system()); + auto postMoveVal = new QDoubleValidator(ui.PostMoveCombo); postMoveVal->setLocale(QLocale::system()); + auto postScaleVal = new QDoubleValidator(ui.PostScaleCombo); postScaleVal->setLocale(QLocale::system()); ui.PreRotateCombo->setValidator(preRotateVal); ui.PreMoveCombo->setValidator(preMoveVal); ui.PreScaleCombo->setValidator(preScaleVal); - ui.PostRotateCombo->setValidator(postRotateVal); ui.PostMoveCombo->setValidator(postMoveVal); ui.PostScaleCombo->setValidator(postScaleVal); - QStringList moveList; - moveList.append(ToString(0.5)); moveList.append(ToString(0.25)); moveList.append(ToString(0.1)); moveList.append(ToString(0.05)); moveList.append(ToString(0.025)); moveList.append(ToString(0.01)); - ui.PreMoveCombo->addItems(moveList); ui.PostMoveCombo->addItems(moveList); - - connect(ui.PreFlipHorizontalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipHorizontalButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.PreFlipVerticalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipVerticalButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.PreRotate90CButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.PreRotate90CcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CcButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.PreRotateCButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.PreRotateCcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCcButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.PreMoveUpButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveUpButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.PreMoveDownButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveDownButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.PreMoveLeftButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveLeftButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.PreMoveRightButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveRightButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.PreScaleDownButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleDownButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.PreScaleUpButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleUpButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.PreResetButton, SIGNAL(clicked(bool)), this, SLOT(OnResetAffineButtonClicked(bool)), Qt::QueuedConnection); - - connect(ui.PostFlipHorizontalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipHorizontalButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.PostFlipVerticalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipVerticalButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.PostRotate90CcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CcButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.PostRotateCcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCcButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.PostRotateCButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.PostRotate90CButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.PostMoveUpButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveUpButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.PostMoveDownButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveDownButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.PostMoveLeftButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveLeftButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.PostMoveRightButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveRightButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.PostScaleDownButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleDownButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.PostScaleUpButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleUpButtonClicked(bool)), Qt::QueuedConnection); - connect(ui.PostResetButton, SIGNAL(clicked(bool)), this, SLOT(OnResetAffineButtonClicked(bool)), Qt::QueuedConnection); - - connect(ui.PreAffineGroupBox, SIGNAL(toggled(bool)), this, SLOT(OnAffineGroupBoxToggled(bool)), Qt::QueuedConnection); - connect(ui.PostAffineGroupBox, SIGNAL(toggled(bool)), this, SLOT(OnAffineGroupBoxToggled(bool)), Qt::QueuedConnection); - - connect(ui.ShowPreAffineAllRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection); - connect(ui.ShowPreAffineCurrentRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection); - connect(ui.ShowPostAffineAllRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection); - connect(ui.ShowPostAffineCurrentRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection); - - connect(ui.PolarAffineCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnPolarAffineCheckBoxStateChanged(int)), Qt::QueuedConnection); - + connect(ui.PreFlipHorizontalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipHorizontalButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PreFlipVerticalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipVerticalButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PreRotate90CButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PreRotate90CcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CcButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PreRotateCButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PreRotateCcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCcButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PreMoveUpButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveUpButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PreMoveDownButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveDownButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PreMoveLeftButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveLeftButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PreMoveRightButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveRightButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PreScaleDownButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleDownButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PreScaleUpButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleUpButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PreResetButton, SIGNAL(clicked(bool)), this, SLOT(OnResetAffineButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PostFlipHorizontalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipHorizontalButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PostFlipVerticalButton, SIGNAL(clicked(bool)), this, SLOT(OnFlipVerticalButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PostRotate90CcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CcButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PostRotateCcButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCcButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PostRotateCButton, SIGNAL(clicked(bool)), this, SLOT(OnRotateCButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PostRotate90CButton, SIGNAL(clicked(bool)), this, SLOT(OnRotate90CButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PostMoveUpButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveUpButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PostMoveDownButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveDownButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PostMoveLeftButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveLeftButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PostMoveRightButton, SIGNAL(clicked(bool)), this, SLOT(OnMoveRightButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PostScaleDownButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleDownButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PostScaleUpButton, SIGNAL(clicked(bool)), this, SLOT(OnScaleUpButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PostResetButton, SIGNAL(clicked(bool)), this, SLOT(OnResetAffineButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.PreAffineGroupBox, SIGNAL(toggled(bool)), this, SLOT(OnAffineGroupBoxToggled(bool)), Qt::QueuedConnection); + connect(ui.PostAffineGroupBox, SIGNAL(toggled(bool)), this, SLOT(OnAffineGroupBoxToggled(bool)), Qt::QueuedConnection); + connect(ui.ShowPreAffineAllRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection); + connect(ui.ShowPreAffineCurrentRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection); + connect(ui.ShowPostAffineAllRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection); + connect(ui.ShowPostAffineCurrentRadio, SIGNAL(toggled(bool)), this, SLOT(OnAffineDrawAllCurrentRadioButtonToggled(bool)), Qt::QueuedConnection); + connect(ui.PolarAffineCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnPolarAffineCheckBoxStateChanged(int)), Qt::QueuedConnection); #ifndef WIN32 //For some reason linux makes these 24x24, even though the designer explicitly says 16x16. //Also, in order to get 4 pixels of spacing between elements in the grid layout, 0 must be specified. @@ -130,7 +113,6 @@ void Fractorium::InitXformsAffineUI() ui.PreResetButton->setIconSize(QSize(16, 16)); ui.PreAffineGridLayout->setHorizontalSpacing(0); ui.PreAffineGridLayout->setVerticalSpacing(0); - ui.PostFlipHorizontalButton->setIconSize(QSize(16, 16)); ui.PostFlipVerticalButton->setIconSize(QSize(16, 16)); ui.PostRotate90CButton->setIconSize(QSize(16, 16)); @@ -146,13 +128,11 @@ void Fractorium::InitXformsAffineUI() ui.PostResetButton->setIconSize(QSize(16, 16)); ui.PostAffineGridLayout->setHorizontalSpacing(0); ui.PostAffineGridLayout->setVerticalSpacing(0); - //Further, the size of the dock widget won't be properly adjusted until the xforms tab is shown. //So show it here and it will be switched back in Fractorium's constructor. //ui.ParamsTabWidget->setCurrentIndex(2); //ui.DockWidget->update(); #endif - //Placing pointers to the spin boxes in arrays makes them easier to access in various places. m_PreSpins[0] = m_PreX1Spin;//A m_PreSpins[1] = m_PreY1Spin;//B @@ -160,20 +140,58 @@ void Fractorium::InitXformsAffineUI() m_PreSpins[3] = m_PreX2Spin;//D m_PreSpins[4] = m_PreY2Spin;//E m_PreSpins[5] = m_PreO2Spin;//F - m_PostSpins[0] = m_PostX1Spin; m_PostSpins[1] = m_PostY1Spin; m_PostSpins[2] = m_PostO1Spin; m_PostSpins[3] = m_PostX2Spin; m_PostSpins[4] = m_PostY2Spin; m_PostSpins[5] = m_PostO2Spin; - ui.PreAffineGroupBox->setChecked(false);//Flip both once to force enabling/disabling the disabling of the group boxes and the corner buttons. ui.PreAffineGroupBox->setChecked(true);//Pre affine enabled. ui.PostAffineGroupBox->setChecked(true); ui.PostAffineGroupBox->setChecked(false);//Post affine disabled. } +/// +/// Toggle whether to lock the visual scale of the affine spinners. +/// Called when the user checks LockAffineCheckBox. +/// +/// True if checked, else false. +template +void FractoriumEmberController::LockAffineScaleCheckBoxStateChanged(int state) +{ + m_LockedScale = m_Ember.m_PixelsPerUnit; + m_Fractorium->ui.GLDisplay->update(); +} + +void Fractorium::OnLockAffineScaleCheckBoxStateChanged(int state) { m_Controller->LockAffineScaleCheckBoxStateChanged(state); } + +/// +/// Return the value needed to multiply the current scale by to get back to the locked scale. +/// +/// The scale value +template +T FractoriumEmberController::AffineScaleCurrentToLocked() +{ + if (m_Fractorium->ui.LockAffineCheckBox->isChecked()) + return LockedScale() / m_Ember.m_PixelsPerUnit; + else + return 1; +} + +/// +/// Return the value needed to multiply the locked scale by to get to the current scale. +/// +/// The scale value +template +T FractoriumEmberController::AffineScaleLockedToCurrent() +{ + if (m_Fractorium->ui.LockAffineCheckBox->isChecked()) + return m_Ember.m_PixelsPerUnit / LockedScale(); + else + return 1; +} + /// /// Toggle all pre affine values in one row for the selected xforms. /// Resets the rendering process. @@ -226,7 +244,7 @@ void FractoriumEmberController::AffineSetHelper(double d, int index, bool pre { UpdateXform([&] (Xform* xform) { - Affine2D* affine = pre ? &xform->m_Affine : &xform->m_Post; + auto affine = pre ? &xform->m_Affine : &xform->m_Post; DoubleSpinBox** spinners = pre ? m_Fractorium->m_PreSpins : m_Fractorium->m_PostSpins; if (m_Fractorium->ui.PolarAffineCheckBox->isChecked()) @@ -238,11 +256,13 @@ void FractoriumEmberController::AffineSetHelper(double d, int index, bool pre affine->A(cos(spinners[0]->value() * DEG_2_RAD) * spinners[3]->value()); affine->D(sin(spinners[0]->value() * DEG_2_RAD) * spinners[3]->value()); break; + case 1: case 4: affine->B(cos(spinners[1]->value() * DEG_2_RAD) * spinners[4]->value()); affine->E(sin(spinners[1]->value() * DEG_2_RAD) * spinners[4]->value()); break; + case 2: case 5: default: @@ -258,18 +278,23 @@ void FractoriumEmberController::AffineSetHelper(double d, int index, bool pre case 0: affine->A(d); break; + case 1: affine->B(d); break; + case 2: affine->C(d); break; + case 3: affine->D(d); break; + case 4: affine->E(d); break; + case 5: affine->F(d); break; @@ -301,7 +326,7 @@ void FractoriumEmberController::FlipXforms(bool horizontal, bool vertical, bo { UpdateXform([&] (Xform* xform) { - Affine2D* affine = pre ? &xform->m_Affine : &xform->m_Post; + auto affine = pre ? &xform->m_Affine : &xform->m_Post; if (horizontal) { @@ -320,9 +345,7 @@ void FractoriumEmberController::FlipXforms(bool horizontal, bool vertical, bo if (!m_Fractorium->LocalPivot()) affine->F(-affine->F()); } - }, eXformUpdate::UPDATE_SELECTED); - FillAffineWithXform(CurrentXform(), pre); } @@ -340,11 +363,9 @@ void FractoriumEmberController::RotateXformsByAngle(double angle, bool pre) { UpdateXform([&] (Xform* xform) { - Affine2D* affine = pre ? &xform->m_Affine : &xform->m_Post; - + auto affine = pre ? &xform->m_Affine : &xform->m_Post; affine->Rotate(angle); }, eXformUpdate::UPDATE_SELECTED); - FillAffineWithXform(CurrentXform(), pre); } @@ -367,7 +388,7 @@ void Fractorium::OnRotateCButtonClicked(bool checked) { bool ok; bool pre = sender() == ui.PreRotateCButton; - QComboBox* combo = pre ? ui.PreRotateCombo : ui.PostRotateCombo; + auto combo = pre ? ui.PreRotateCombo : ui.PostRotateCombo; double d = ToDouble(combo->currentText(), &ok); if (ok) @@ -384,7 +405,7 @@ void Fractorium::OnRotateCcButtonClicked(bool checked) { bool ok; bool pre = sender() == ui.PreRotateCcButton; - QComboBox* combo = pre ? ui.PreRotateCombo : ui.PostRotateCombo; + auto combo = pre ? ui.PreRotateCombo : ui.PostRotateCombo; double d = ToDouble(combo->currentText(), &ok); if (ok) @@ -403,12 +424,10 @@ void FractoriumEmberController::MoveXforms(double x, double y, bool pre) { UpdateXform([&] (Xform* xform) { - Affine2D* affine = pre ? &xform->m_Affine : &xform->m_Post; - + auto* affine = pre ? &xform->m_Affine : &xform->m_Post; affine->C(affine->C() + x); affine->F(affine->F() + y); }, eXformUpdate::UPDATE_SELECTED); - FillAffineWithXform(CurrentXform(), pre); } @@ -422,9 +441,9 @@ void Fractorium::OnMoveUpButtonClicked(bool checked) { bool ok; bool pre = sender() == ui.PreMoveUpButton; - QComboBox* combo = pre ? ui.PreMoveCombo : ui.PostMoveCombo; + auto combo = pre ? ui.PreMoveCombo : ui.PostMoveCombo; double d = ToDouble(combo->currentText(), &ok); - + if (ok) m_Controller->MoveXforms(0, d, pre); } @@ -439,9 +458,9 @@ void Fractorium::OnMoveDownButtonClicked(bool checked) { bool ok; bool pre = sender() == ui.PreMoveDownButton; - QComboBox* combo = pre ? ui.PreMoveCombo : ui.PostMoveCombo; + auto combo = pre ? ui.PreMoveCombo : ui.PostMoveCombo; double d = ToDouble(combo->currentText(), &ok); - + if (ok) m_Controller->MoveXforms(0, -d, pre); } @@ -456,9 +475,9 @@ void Fractorium::OnMoveLeftButtonClicked(bool checked) { bool ok; bool pre = sender() == ui.PreMoveLeftButton; - QComboBox* combo = pre ? ui.PreMoveCombo : ui.PostMoveCombo; + auto combo = pre ? ui.PreMoveCombo : ui.PostMoveCombo; double d = ToDouble(combo->currentText(), &ok); - + if (ok) m_Controller->MoveXforms(-d, 0, pre); } @@ -473,9 +492,9 @@ void Fractorium::OnMoveRightButtonClicked(bool checked) { bool ok; bool pre = sender() == ui.PreMoveRightButton; - QComboBox* combo = pre ? ui.PreMoveCombo : ui.PostMoveCombo; + auto combo = pre ? ui.PreMoveCombo : ui.PostMoveCombo; double d = ToDouble(combo->currentText(), &ok); - + if (ok) m_Controller->MoveXforms(d, 0, pre); } @@ -491,14 +510,12 @@ void FractoriumEmberController::ScaleXforms(double scale, bool pre) { UpdateXform([&] (Xform* xform) { - Affine2D* affine = pre ? &xform->m_Affine : &xform->m_Post; - + auto affine = pre ? &xform->m_Affine : &xform->m_Post; affine->A(affine->A() * scale); affine->B(affine->B() * scale); affine->D(affine->D() * scale); affine->E(affine->E() * scale); }, eXformUpdate::UPDATE_SELECTED); - FillAffineWithXform(CurrentXform(), pre); } @@ -512,9 +529,9 @@ void Fractorium::OnScaleDownButtonClicked(bool checked) { bool ok; bool pre = sender() == ui.PreScaleDownButton; - QComboBox* combo = pre ? ui.PreScaleCombo : ui.PostScaleCombo; + auto combo = pre ? ui.PreScaleCombo : ui.PostScaleCombo; double d = ToDouble(combo->currentText(), &ok); - + if (ok) m_Controller->ScaleXforms(1.0 / (d / 100.0), pre); } @@ -529,9 +546,9 @@ void Fractorium::OnScaleUpButtonClicked(bool checked) { bool ok; bool pre = sender() == ui.PreScaleUpButton; - QComboBox* combo = pre ? ui.PreScaleCombo : ui.PostScaleCombo; + auto combo = pre ? ui.PreScaleCombo : ui.PostScaleCombo; double d = ToDouble(combo->currentText(), &ok); - + if (ok) m_Controller->ScaleXforms(d / 100.0, pre); } @@ -546,11 +563,9 @@ void FractoriumEmberController::ResetXformsAffine(bool pre) { UpdateXform([&] (Xform* xform) { - Affine2D* affine = pre ? &xform->m_Affine : &xform->m_Post; - + auto affine = pre ? &xform->m_Affine : &xform->m_Post; affine->MakeID(); }, eXformUpdate::UPDATE_SELECTED); - FillAffineWithXform(CurrentXform(), pre); } @@ -699,5 +714,5 @@ bool Fractorium::LocalPivot() { return ui.LocalPivotRadio->isChecked(); template class FractoriumEmberController; #ifdef DO_DOUBLE - template class FractoriumEmberController; +template class FractoriumEmberController; #endif diff --git a/Source/Fractorium/GLEmberController.h b/Source/Fractorium/GLEmberController.h index 350a48b..ef38353 100644 --- a/Source/Fractorium/GLEmberController.h +++ b/Source/Fractorium/GLEmberController.h @@ -142,6 +142,7 @@ private: m4T m_Projection; Affine2D m_DragSrcTransform; + vector> m_DragSrcTransforms; Xform* m_HoverXform; Xform* m_SelectedXform; diff --git a/Source/Fractorium/GLWidget.cpp b/Source/Fractorium/GLWidget.cpp index 14c655f..f220d51 100644 --- a/Source/Fractorium/GLWidget.cpp +++ b/Source/Fractorium/GLWidget.cpp @@ -2,8 +2,6 @@ #include "GLWidget.h" #include "Fractorium.h" -//#define OLDDRAG 1 - /// /// Constructor which passes parent widget to the base and initializes OpenGL profile. /// This will need to change in the future to implement all drawing as shader programs. @@ -142,7 +140,7 @@ void GLEmberControllerBase::ClearControl() { m_DragModifier &= ~et(eDragModifier template void GLEmberController::ClearWindow() { - Ember* ember = m_FractoriumEmberController->CurrentEmber(); + auto ember = m_FractoriumEmberController->CurrentEmber(); m_GL->makeCurrent(); m_GL->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); m_GL->glClearColor(ember->m_Background.r, ember->m_Background.g, ember->m_Background.b, 1.0); @@ -211,10 +209,10 @@ void GLWidget::initializeGL() /// void GLWidget::paintGL() { - FractoriumEmberControllerBase* controller = m_Fractorium->m_Controller.get(); + auto controller = m_Fractorium->m_Controller.get(); //Ensure there is a renderer and that it's supposed to be drawing, signified by the running timer. - if (controller && controller->Renderer() && controller->RenderTimerRunning()) + if (controller && controller->Renderer()) { RendererBase* renderer = controller->Renderer(); m_Drawing = true; @@ -256,8 +254,8 @@ void GLWidget::paintGL() template void GLEmberController::DrawImage() { - RendererBase* renderer = m_FractoriumEmberController->Renderer(); - Ember* ember = m_FractoriumEmberController->CurrentEmber(); + auto renderer = m_FractoriumEmberController->Renderer(); + auto ember = m_FractoriumEmberController->CurrentEmber(); m_GL->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); m_GL->glClearColor(ember->m_Background.r, ember->m_Background.g, ember->m_Background.b, 1.0); m_GL->glDisable(GL_DEPTH_TEST); @@ -286,14 +284,14 @@ template void GLEmberController::DrawAffines(bool pre, bool post) { QueryVMP();//Resolves to float or double specialization function depending on T. - Ember* ember = m_FractoriumEmberController->CurrentEmber(); + auto ember = m_FractoriumEmberController->CurrentEmber(); bool dragging = m_DragState == eDragState::DragDragging; //Draw grid if control key is pressed. if (m_GL->hasFocus() && GetControl()) { m_GL->glLineWidth(1.0f); - m_GL->DrawGrid(); + m_GL->DrawGrid(m_FractoriumEmberController->AffineScaleLockedToCurrent()); } //When dragging, only draw the selected xform's affine and hide all others. @@ -308,7 +306,7 @@ void GLEmberController::DrawAffines(bool pre, bool post) { for (size_t i = 0; i < ember->TotalXformCount(); i++) { - Xform* xform = ember->GetTotalXform(i); + auto xform = ember->GetTotalXform(i); bool selected = dragging ? (m_SelectedXform == xform) : (m_HoverXform == xform); DrawAffine(xform, true, selected); } @@ -322,7 +320,7 @@ void GLEmberController::DrawAffines(bool pre, bool post) { for (size_t i = 0; i < ember->TotalXformCount(); i++) { - Xform* xform = ember->GetTotalXform(i); + auto xform = ember->GetTotalXform(i); bool selected = dragging ? (m_SelectedXform == xform) : (m_HoverXform == xform); DrawAffine(xform, false, selected); } @@ -359,27 +357,12 @@ void GLEmberController::DrawAffines(bool pre, bool post) /// The event bool GLEmberControllerBase::KeyPress_(QKeyEvent* e) { -#ifdef OLDDRAG - - if (e->key() == Qt::Key_Shift) - SetShift(); - else if (e->key() == Qt::Key_Control || e->key() == Qt::Key_C) - SetControl(); - else if (e->key() == Qt::Key_Alt || e->key() == Qt::Key_A) - SetAlt(); - else - return false; - - return true; -#else - if (e->key() == Qt::Key_Control) { SetControl(); return true; } -#endif return false; } @@ -401,27 +384,12 @@ void GLWidget::keyPressEvent(QKeyEvent* e) /// The event bool GLEmberControllerBase::KeyRelease_(QKeyEvent* e) { -#ifdef OLDDRAG - - if (e->key() == Qt::Key_Shift) - ClearShift(); - else if (e->key() == Qt::Key_Control || e->key() == Qt::Key_C) - ClearControl(); - else if (e->key() == Qt::Key_Alt || e->key() == Qt::Key_A) - ClearAlt(); - else - return false; - - return true; -#else - if (e->key() == Qt::Key_Control) { ClearControl(); return true; } -#endif return false; } @@ -448,8 +416,10 @@ template void GLEmberController::MousePress(QMouseEvent* e) { v3T mouseFlipped(e->x() * m_GL->devicePixelRatio(), m_Viewport[3] - e->y() * m_GL->devicePixelRatio(), 0);//Must flip y because in OpenGL, 0,0 is bottom left, but in windows, it's top left. - Ember* ember = m_FractoriumEmberController->CurrentEmber(); - RendererBase* renderer = m_FractoriumEmberController->Renderer(); + auto ember = m_FractoriumEmberController->CurrentEmber(); + auto xforms = ember->TotalXformCount(); + auto renderer = m_FractoriumEmberController->Renderer(); + size_t i = 0; //Ensure everything has been initialized. if (!renderer) @@ -461,7 +431,6 @@ void GLEmberController::MousePress(QMouseEvent* e) m_BoundsDown.x = renderer->LowerLeftY(false); m_BoundsDown.y = renderer->UpperRightX(false); m_BoundsDown.z = renderer->UpperRightY(false); -#ifndef OLDDRAG Qt::KeyboardModifiers mod = e->modifiers(); if (mod.testFlag(Qt::ShiftModifier)) @@ -472,8 +441,6 @@ void GLEmberController::MousePress(QMouseEvent* e) if (mod.testFlag(Qt::AltModifier))// || mod.testFlag(Qt::Key_A)) SetAlt(); -#endif - if (m_DragState == eDragState::DragNone)//Only take action if the user wasn't already dragging. { m_MouseDownWorldPos = m_MouseWorldPos;//Set the mouse down position to the current position. @@ -486,11 +453,16 @@ void GLEmberController::MousePress(QMouseEvent* e) { m_SelectedXform = m_HoverXform; m_DragSrcTransform = Affine2D(m_AffineType == eAffineType::AffinePre ? m_SelectedXform->m_Affine : m_SelectedXform->m_Post);//Copy the affine of the xform that was selected. - m_DragHandlePos = m_HoverHandlePos; - m_DragHandleOffset = m_DragHandlePos - m_MouseWorldPos; - m_DragState = eDragState::DragDragging; //The user has selected an xform by clicking on it, so update the main GUI by selecting this xform in the combo box. - m_Fractorium->CurrentXform(xformIndex); + m_Fractorium->CurrentXform(xformIndex);//Must do this first so UpdateXform() below properly grabs the current plus any selected. + //m_DragSrcTransforms.clear(); + //m_FractoriumEmberController->UpdateXform([&](Xform* xform) + //{ + // m_DragSrcTransforms.push_back(m_AffineType == eAffineType::AffinePre ? xform->m_Affine : xform->m_Post); + //}, eXformUpdate::UPDATE_CURRENT_AND_SELECTED, false);//Don't update renderer here. + m_DragHandlePos = m_HoverHandlePos;//The location in local coordinates of the point selected on the spinner, x, y or center. + m_DragHandleOffset = m_DragHandlePos - m_MouseWorldPos;//The distance in world coordinates from the point selected to the center of the spinner. + m_DragState = eDragState::DragDragging; //Draw large yellow dot on select or drag. m_GL->glPointSize(6.0f); m_GL->glBegin(GL_POINTS); @@ -554,9 +526,7 @@ void GLEmberController::MouseRelease(QMouseEvent* e) UpdateHover(mouseFlipped); m_DragState = eDragState::DragNone; -#ifndef OLDDRAG m_DragModifier = 0; -#endif m_GL->repaint();//Force immediate redraw. } @@ -585,7 +555,7 @@ void GLEmberController::MouseMove(QMouseEvent* e) bool draw = true; glm::ivec2 mouse(e->x() * m_GL->devicePixelRatio(), e->y() * m_GL->devicePixelRatio()); v3T mouseFlipped(e->x() * m_GL->devicePixelRatio(), m_Viewport[3] - e->y() * m_GL->devicePixelRatio(), 0);//Must flip y because in OpenGL, 0,0 is bottom left, but in windows, it's top left. - Ember* ember = m_FractoriumEmberController->CurrentEmber(); + auto ember = m_FractoriumEmberController->CurrentEmber(); //First check to see if the mouse actually moved. if (mouse == m_MousePos) @@ -593,7 +563,6 @@ void GLEmberController::MouseMove(QMouseEvent* e) m_MousePos = mouse; m_MouseWorldPos = WindowToWorld(mouseFlipped, false); - //v3T mouseDelta = m_MouseWorldPos - m_MouseDownWorldPos;//Determine how far the mouse has moved in world cartesian coordinates. //Update status bar on main window, regardless of whether anything is being dragged. if (m_Fractorium->m_Controller->RenderTimerRunning()) @@ -605,7 +574,15 @@ void GLEmberController::MouseMove(QMouseEvent* e) Affine2D* affine = pre ? &m_SelectedXform->m_Affine : &m_SelectedXform->m_Post;//Determine pre or post affine. if (m_HoverType == eHoverType::HoverTranslation) + { + //m_FractoriumEmberController->UpdateXform([&](Xform* xform) + //{ + // affine = pre ? &xform->m_Affine : &xform->m_Post;//Determine pre or post affine. + // *affine = CalcDragTranslation(); + //}, eXformUpdate::UPDATE_ALL, false);//Don't need to update render for every xform, just do it once below. *affine = CalcDragTranslation(); + //CalcDragTranslation(); + } else if (m_HoverType == eHoverType::HoverXAxis) *affine = CalcDragXAxis(); else if (m_HoverType == eHoverType::HoverYAxis) @@ -649,7 +626,7 @@ void GLEmberController::MouseMove(QMouseEvent* e) if (UpdateHover(mouseFlipped) == -1) draw = false; - //Xform* previousHover = m_HoverXform; + //auto previousHover = m_HoverXform; // //if (UpdateHover(mouseFlipped) == -1) // m_HoverXform = m_SelectedXform; @@ -690,7 +667,7 @@ void GLWidget::mouseMoveEvent(QMouseEvent* e) template void GLEmberController::Wheel(QWheelEvent* e) { - Ember* ember = m_FractoriumEmberController->CurrentEmber(); + auto ember = m_FractoriumEmberController->CurrentEmber(); if (m_Fractorium && !(e->buttons() & Qt::MiddleButton))//Middle button does whole image translation, so ignore the mouse wheel while panning to avoid inadvertent zooming. m_Fractorium->SetScale(ember->m_PixelsPerUnit + (e->angleDelta().y() >= 0 ? 50 : -50)); @@ -822,7 +799,7 @@ void GLWidget::SetViewport() template bool GLEmberController::SizesMatch() { - Ember* ember = m_FractoriumEmberController->CurrentEmber(); + auto ember = m_FractoriumEmberController->CurrentEmber(); return (ember && ember->m_FinalRasW == m_GL->width() && ember->m_FinalRasH == m_GL->height() && @@ -837,12 +814,13 @@ bool GLEmberController::SizesMatch() /// The frequency of the grid lines will change depending on the zoom. /// Calculated with the frame always centered, the renderer just moves the camera. /// -void GLWidget::DrawGrid() +/// A value to scale by, used when locking the affine scale +void GLWidget::DrawGrid(double scale) { RendererBase* renderer = m_Fractorium->m_Controller->Renderer(); float unitX = std::abs(renderer->UpperRightX(false) - renderer->LowerLeftX(false)) / 2.0f; float unitY = std::abs(renderer->UpperRightY(false) - renderer->LowerLeftY(false)) / 2.0f; - float rad = std::max(unitX, unitY); + float rad = std::max(unitX * scale, unitY * scale); float xLow = floor(-unitX); float xHigh = ceil(unitX); float yLow = floor(-unitY); @@ -866,6 +844,9 @@ void GLWidget::DrawGrid() } } + unitX *= scale; + unitY *= scale; + if (unitX <= 64.0f) { glColor4f(0.5f, 0.5f, 0.5f, 1.0f); @@ -934,16 +915,16 @@ void GLWidget::DrawUnitSquare() template void GLEmberController::DrawAffine(Xform* xform, bool pre, bool selected) { - Ember* ember = m_FractoriumEmberController->CurrentEmber(); + auto ember = m_FractoriumEmberController->CurrentEmber(); bool final = ember->IsFinalXform(xform); int index = ember->GetXformIndex(xform); size_t size = ember->m_Palette.m_Entries.size(); v4T color = ember->m_Palette.m_Entries[Clamp(xform->m_ColorX * size, 0, size - 1)]; - Affine2D* affine = pre ? &xform->m_Affine : &xform->m_Post; + auto affine = pre ? &xform->m_Affine : &xform->m_Post; //For some incredibly strange reason, even though glm and OpenGL use matrices with a column-major //data layout, nothing will work here unless they are flipped to row major order. This is how it was //done in Fractron. - m4T mat = affine->ToMat4RowMajor(); + m4T mat = (*affine * m_FractoriumEmberController->AffineScaleCurrentToLocked()).ToMat4RowMajor(); m_GL->glPushMatrix(); m_GL->glLoadIdentity(); MultMatrix(mat); @@ -1054,7 +1035,7 @@ int GLEmberController::UpdateHover(v3T& glCoords) bool postAll = post && m_Fractorium->DrawAllPost(); uint bestIndex = -1; T bestDist = 10; - Ember* ember = m_FractoriumEmberController->CurrentEmber(); + auto ember = m_FractoriumEmberController->CurrentEmber(); m_HoverType = eHoverType::HoverNone; //If there's a selected/current xform, check it first so it gets precedence over the others. @@ -1076,7 +1057,7 @@ int GLEmberController::UpdateHover(v3T& glCoords) //Check all xforms. for (size_t i = 0; i < ember->TotalXformCount(); i++) { - Xform* xform = ember->GetTotalXform(i); + auto xform = ember->GetTotalXform(i); if (preAll || (pre && m_HoverXform == xform))//Only check pre affine if they are shown. { @@ -1116,17 +1097,17 @@ template bool GLEmberController::CheckXformHover(Xform* xform, v3T& glCoords, T& bestDist, bool pre, bool post) { bool preFound = false, postFound = false; - float dist = 0.0f; + T dist = 0, scale = m_FractoriumEmberController->AffineScaleCurrentToLocked(); v3T pos; - Ember* ember = m_FractoriumEmberController->CurrentEmber(); if (pre) { - v3T translation(xform->m_Affine.C()/* - ember->m_CenterX*/, /*ember->m_CenterY + */xform->m_Affine.F(), 0); + auto affineScaled = xform->m_Affine * scale; + v3T translation(affineScaled.C(), affineScaled.F(), 0); v3T transScreen = glm::project(translation, m_Modelview, m_Projection, m_Viewport); - v3T xAxis(xform->m_Affine.A(), xform->m_Affine.D(), 0); + v3T xAxis(affineScaled.A(), affineScaled.D(), 0); v3T xAxisScreen = glm::project(translation + xAxis, m_Modelview, m_Projection, m_Viewport); - v3T yAxis(xform->m_Affine.B(), xform->m_Affine.E(), 0); + v3T yAxis(affineScaled.B(), affineScaled.E(), 0); v3T yAxisScreen = glm::project(translation + yAxis, m_Modelview, m_Projection, m_Viewport); pos = translation; dist = glm::distance(glCoords, transScreen); @@ -1167,11 +1148,12 @@ bool GLEmberController::CheckXformHover(Xform* xform, v3T& glCoords, T& be if (post) { - v3T translation(xform->m_Post.C()/* - ember->m_CenterX*/, /*ember->m_CenterY + */xform->m_Post.F(), 0); + auto affineScaled = xform->m_Post * scale; + v3T translation(affineScaled.C(), affineScaled.F(), 0); v3T transScreen = glm::project(translation, m_Modelview, m_Projection, m_Viewport); - v3T xAxis(xform->m_Post.A(), xform->m_Post.D(), 0); + v3T xAxis(affineScaled.A(), affineScaled.D(), 0); v3T xAxisScreen = glm::project(translation + xAxis, m_Modelview, m_Projection, m_Viewport); - v3T yAxis(xform->m_Post.B(), xform->m_Post.E(), 0); + v3T yAxis(affineScaled.B(), affineScaled.E(), 0); v3T yAxisScreen = glm::project(translation + yAxis, m_Modelview, m_Projection, m_Viewport); pos = translation; dist = glm::distance(glCoords, transScreen); @@ -1235,7 +1217,8 @@ template Affine2D GLEmberController::CalcDragXAxis() { v3T t3, newAxis, newPos; - Affine2D result = m_DragSrcTransform; + auto scale = m_FractoriumEmberController->AffineScaleLockedToCurrent(); + auto result = m_DragSrcTransform; bool worldPivotShiftAlt = !m_Fractorium->LocalPivot() && GetShift() && GetAlt(); if (worldPivotShiftAlt) @@ -1245,7 +1228,7 @@ Affine2D GLEmberController::CalcDragXAxis() if (GetShift()) { - v3T targetAxis = m_MouseWorldPos - t3; + v3T targetAxis = (m_MouseWorldPos * scale) - t3; v3T norm = glm::normalize(targetAxis); if (GetControl()) @@ -1263,7 +1246,7 @@ Affine2D GLEmberController::CalcDragXAxis() else newPos = m_MouseWorldPos + m_DragHandleOffset; - newAxis = newPos - t3; + newAxis = (newPos * scale) - t3; } if (GetAlt()) @@ -1278,7 +1261,8 @@ Affine2D GLEmberController::CalcDragXAxis() result.RotateScaleXTo(v2T(newAxis)); } - m_DragHandlePos = v3T(result.O() + result.X(), 0); + T scaleBack = m_FractoriumEmberController->AffineScaleCurrentToLocked(); + m_DragHandlePos = v3T((result.O() + result.X()) * scaleBack, 0); return result; } @@ -1304,7 +1288,8 @@ template Affine2D GLEmberController::CalcDragYAxis() { v3T t3, newAxis, newPos; - Affine2D result = m_DragSrcTransform; + auto scale = m_FractoriumEmberController->AffineScaleLockedToCurrent(); + auto result = m_DragSrcTransform; bool worldPivotShiftAlt = !m_Fractorium->LocalPivot() && GetShift() && GetAlt(); if (worldPivotShiftAlt) @@ -1314,7 +1299,7 @@ Affine2D GLEmberController::CalcDragYAxis() if (GetShift()) { - v3T targetAxis = m_MouseWorldPos - t3; + v3T targetAxis = (m_MouseWorldPos * scale) - t3; v3T norm = glm::normalize(targetAxis); if (GetControl()) @@ -1332,7 +1317,7 @@ Affine2D GLEmberController::CalcDragYAxis() else newPos = m_MouseWorldPos + m_DragHandleOffset; - newAxis = newPos - t3; + newAxis = (newPos * scale) - t3; } if (GetAlt()) @@ -1347,7 +1332,8 @@ Affine2D GLEmberController::CalcDragYAxis() result.RotateScaleYTo(v2T(newAxis)); } - m_DragHandlePos = v3T(result.O() + result.Y(), 0); + T scaleBack = m_FractoriumEmberController->AffineScaleCurrentToLocked(); + m_DragHandlePos = v3T((result.O() + result.Y()) * scaleBack, 0); return result; } @@ -1368,8 +1354,9 @@ Affine2D GLEmberController::CalcDragYAxis() template Affine2D GLEmberController::CalcDragTranslation() { - v3T newPos, newX, newY; - Affine2D result = m_DragSrcTransform; + v3T newPos; + auto scale = m_FractoriumEmberController->AffineScaleLockedToCurrent(); + auto result = m_DragSrcTransform; bool worldPivotShift = !m_Fractorium->LocalPivot() && GetShift(); if (GetShift()) @@ -1387,17 +1374,32 @@ Affine2D GLEmberController::CalcDragTranslation() T endAngle = atan2(newPos.y, newPos.x); T angle = startAngle - endAngle; result.Rotate(angle * RAD_2_DEG); + //RotateXformsByAngle } } else { if (GetControl()) + { newPos = SnapToGrid(m_MouseWorldPos); + } else + { newPos = m_MouseWorldPos + m_DragHandleOffset; + //bool pre = m_AffineType == eAffineType::AffinePre; + //size_t index = 0; + //newPos = m_MouseWorldPos; + //auto diff = m_MouseWorldPos - m_MouseDownWorldPos; + //m_FractoriumEmberController->UpdateXform([&](Xform* xform) + //{ + // auto affine = pre ? &xform->m_Affine : &xform->m_Post;//Determine pre or post affine. + // affine->O(m_DragSrcTransforms[index++].O() + v2T(diff)); + //}, eXformUpdate::UPDATE_CURRENT_AND_SELECTED, false);//Don't need to update render for every xform, just do it once below. + } } - result.O(v2T(newPos)); + T scaleBack = m_FractoriumEmberController->AffineScaleCurrentToLocked(); + result.O(v2T(newPos * scale)); m_DragHandlePos = newPos; return result; } diff --git a/Source/Fractorium/GLWidget.h b/Source/Fractorium/GLWidget.h index a437163..ef59552 100644 --- a/Source/Fractorium/GLWidget.h +++ b/Source/Fractorium/GLWidget.h @@ -24,7 +24,7 @@ static const float GridStep = 1.0f / 8.0f; /// The current xform is set by either clicking on it, or by changing the index of the xforms combo box on the main window. /// A problem here is that all drawing is done using the legacy OpenGL fixed function pipeline which is deprecated /// and even completely disabled on Mac OS. This will need to be replaced with shader programs for every draw operation. -/// Since this window has to know about various states of the renderer and the main window, it retains pointers to +/// Since this window has to know about various states of the renderer and the main window, it retains pointers to /// the main window and several of its members. /// This class uses a controller-based design similar to the main window. /// @@ -36,12 +36,12 @@ class GLWidget : public QOpenGLWidget, protected QOpenGLFunctions_2_0//QOpenGLFu friend FractoriumEmberController; friend GLEmberControllerBase; friend GLEmberController; - + #ifdef DO_DOUBLE friend GLEmberController; friend FractoriumEmberController; #endif - + public: GLWidget(QWidget* p = nullptr); ~GLWidget(); @@ -63,13 +63,13 @@ protected: virtual void mouseMoveEvent(QMouseEvent* e) override; virtual void wheelEvent(QWheelEvent* e) override; //virtual void resizeEvent(QResizeEvent* e) override; - + private: void SetDimensions(int w, int h); bool Allocate(bool force = false); bool Deallocate(); void SetViewport(); - void DrawGrid(); + void DrawGrid(double scale); void DrawUnitSquare(); void DrawAffineHelper(int index, bool selected, bool pre, bool final, bool background); GLEmberControllerBase* GLController(); diff --git a/Source/Fractorium/VariationsDialog.cpp b/Source/Fractorium/VariationsDialog.cpp index 13816c1..837a1ef 100644 --- a/Source/Fractorium/VariationsDialog.cpp +++ b/Source/Fractorium/VariationsDialog.cpp @@ -56,7 +56,7 @@ void FractoriumVariationsDialog::ForEachSelectedCell(std::function selectedItems = table->selectedItems(); table->model()->blockSignals(true); - foreach (QTableWidgetItem* item, selectedItems) + for (auto item : selectedItems) if (item) func(item);