From 6ff199d1efef1421dee40330f055fb609c003afc Mon Sep 17 00:00:00 2001 From: Person Date: Tue, 12 Jun 2018 21:20:15 -0700 Subject: [PATCH] --User changes -No longer constrain pitch, yaw or depth spinners to -180 - 180. --Bug fixes -Properly set color index on padded xforms. -Adding a padding final xform included a linear variation with a weight of zero to not appear empty. Made it have a weight of 1. -Always write animate tag on final xform when saving to Xml. -Motion was being applied to the wrong flame in SheepTools::Edge(), so apply it to the correct one. -Prevent divide by zero when normalizing variation weights. -Was accidentally adding the placeholder value of -9999 for motion_offset to varation weights and parameters when applying motion. Set to zero if no value present. -Clamp flame rotation values to -180 - 180 when reading a flame from Xml. -Events were not properly wired for user changes in the random rotations per blend controls in the sequencer. -Fix major UI bugs with sequencer min/max random controls which made it nearly impossible to hand type values. -Values from rotations per blend and rotations per blend max were not being saved to file between program runs. -Checking animate for an xform was not applied to all flames even if Apply All was checked. -Changing interpolation type, temporal filter width, temporal type, and affine interpolation type were not actually saving to the flame when changed. -Grid on the main window was not being drawn at the right scale initially due to some OpenGL initialization occurring in the wrong order. -Severe bugs in sequence generation code: --Improperly detected padding xforms. --When looking for specific variations during xform aligning, only presence was detected, when it should have been presence plus a weight greater than zero. --When adding specific variations during xform aligning, must first remove any variations of that type. --Two variables were unsigned when they should have been signed. This prevented large blocks of code from ever executing. --When interpolating affines, an EPS that was too small was used, causing affine values to interpolate incorrectly. Instead use 1e-10 to ensure results equal to flam3. --Code changes -Modify FractoriumEmberController::UpdateXform() to pass the selected xform index as well as the absolute index to func(). --- .../Installer/FractoriumInstaller.wixproj | 2 +- Builds/MSVC/Installer/Product.wxs | 4 +- Builds/MSVC/VS2017/Ember.rc | Bin 4502 -> 4510 bytes Builds/MSVC/VS2017/EmberAnimate.rc | 8 +- Builds/MSVC/VS2017/EmberCL.rc | Bin 4528 -> 4536 bytes Builds/MSVC/VS2017/EmberGenome.rc | 8 +- Builds/MSVC/VS2017/EmberRender.rc | 8 +- Builds/MSVC/VS2017/Fractorium.rc | Bin 4470 -> 4478 bytes Source/Ember/Ember.h | 11 +- Source/Ember/EmberDefines.h | 2 +- Source/Ember/EmberToXml.cpp | 4 +- Source/Ember/Interpolate.h | 298 +++++++++++------- Source/Ember/SheepTools.h | 5 +- Source/Ember/Xform.h | 17 +- Source/Ember/XmlToEmber.cpp | 3 +- Source/EmberCL/RendererCL.h | 1 + Source/Fractorium/AboutDialog.ui | 2 +- Source/Fractorium/Fractorium.h | 3 +- .../Fractorium/FractoriumEmberController.cpp | 48 ++- Source/Fractorium/FractoriumEmberController.h | 2 +- Source/Fractorium/FractoriumLibrary.cpp | 106 +++++-- Source/Fractorium/FractoriumMenus.cpp | 2 +- Source/Fractorium/FractoriumParams.cpp | 166 ++++++---- Source/Fractorium/FractoriumSettings.cpp | 38 ++- Source/Fractorium/FractoriumSettings.h | 8 + Source/Fractorium/FractoriumXforms.cpp | 71 +++-- Source/Fractorium/FractoriumXformsAffine.cpp | 14 +- Source/Fractorium/FractoriumXformsColor.cpp | 16 +- .../Fractorium/FractoriumXformsVariations.cpp | 2 +- Source/Fractorium/GLWidget.cpp | 37 +-- 30 files changed, 534 insertions(+), 352 deletions(-) diff --git a/Builds/MSVC/Installer/FractoriumInstaller.wixproj b/Builds/MSVC/Installer/FractoriumInstaller.wixproj index 4d2384c..8e44abd 100644 --- a/Builds/MSVC/Installer/FractoriumInstaller.wixproj +++ b/Builds/MSVC/Installer/FractoriumInstaller.wixproj @@ -6,7 +6,7 @@ 3.7 {c8096c47-e358-438c-a520-146d46b0637d} 2.0 - Fractorium_1.0.0.9 + Fractorium_1.0.0.10 Package $(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets diff --git a/Builds/MSVC/Installer/Product.wxs b/Builds/MSVC/Installer/Product.wxs index f2e9141..a4abfeb 100644 --- a/Builds/MSVC/Installer/Product.wxs +++ b/Builds/MSVC/Installer/Product.wxs @@ -1,6 +1,6 @@ - + @@ -13,7 +13,7 @@ - + yhC}zCk|#q27}3;In3FCGy^XK*W`_yqMKbfw=sbwF7k+P7UG%12;yvB&&$XH E0IXsTOaK4? delta 34 ncmdm?yg_-xCk{r-$zM6l87(*KbDn1cQj9#C8G+PxUPcxG)xQd~ diff --git a/Builds/MSVC/VS2017/EmberGenome.rc b/Builds/MSVC/VS2017/EmberGenome.rc index b6c3fac..403a961 100644 --- a/Builds/MSVC/VS2017/EmberGenome.rc +++ b/Builds/MSVC/VS2017/EmberGenome.rc @@ -49,8 +49,8 @@ // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1, 0, 0, 9 - PRODUCTVERSION 1, 0, 0, 9 + FILEVERSION 1, 0, 0, 10 + PRODUCTVERSION 1, 0, 0, 10 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -67,12 +67,12 @@ BEGIN VALUE "CompanyName", "Open Source" VALUE "FileDescription", "Manipulates fractal flames parameter files" - VALUE "FileVersion", "1.0.0.9" + VALUE "FileVersion", "1.0.0.10" VALUE "InternalName", "EmberGenome.exe" VALUE "LegalCopyright", "Copyright (C) Matt Feemster 2017, GPL v3" VALUE "OriginalFilename", "EmberGenome.exe" VALUE "ProductName", "Ember Genome" - VALUE "ProductVersion", "1.0.0.9" + VALUE "ProductVersion", "1.0.0.10" END END BLOCK "VarFileInfo" diff --git a/Builds/MSVC/VS2017/EmberRender.rc b/Builds/MSVC/VS2017/EmberRender.rc index a5be833..6e10880 100644 --- a/Builds/MSVC/VS2017/EmberRender.rc +++ b/Builds/MSVC/VS2017/EmberRender.rc @@ -49,8 +49,8 @@ // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1, 0, 0, 9 - PRODUCTVERSION 1, 0, 0, 9 + FILEVERSION 1, 0, 0, 10 + PRODUCTVERSION 1, 0, 0, 10 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -67,12 +67,12 @@ BEGIN VALUE "CompanyName", "Open Source" VALUE "FileDescription", "Renders fractal flames as single images" - VALUE "FileVersion", "1.0.0.9" + VALUE "FileVersion", "1.0.0.10" VALUE "InternalName", "EmberRender.exe" VALUE "LegalCopyright", "Copyright (C) Matt Feemster 2017, GPL v3" VALUE "OriginalFilename", "EmberRender.exe" VALUE "ProductName", "Ember Render" - VALUE "ProductVersion", "1.0.0.9" + VALUE "ProductVersion", "1.0.0.10" END END BLOCK "VarFileInfo" diff --git a/Builds/MSVC/VS2017/Fractorium.rc b/Builds/MSVC/VS2017/Fractorium.rc index cba4a0e1893cd76559f30725cd727fd8c4b89774..66d6951e91cef7ba2884194013aa3688a2325265 100644 GIT binary patch delta 52 zcmeyS^iOHS84hMc27}3GIm|)yW>(IXOke><9`Vh`xYseV0!0~=CJXY(Zl1#Xh#3H$ CXbx@w delta 46 zcmeyT^i65Q84gCv$>%xDSuGiO8Mr3*bBbThe number of xforms to add void AddXforms(size_t count) { + auto oldsize = m_Xforms.size(); + for (size_t i = 0; i < count; i++) { Xform xform; + xform.m_ColorX = T((oldsize + i) & 1); xform.AddVariation(m_VariationList->GetVariationCopy(eVariationId::VAR_LINEAR)); AddXform(xform); } @@ -266,15 +269,18 @@ public: if (UseFinalXform())//Caller wanted one and this ember has one. { ember.m_FinalXform = m_FinalXform; + ember.m_FinalXform.m_ColorX = T(XformCount() & 1); } else//Caller wanted one and this ember doesn't have one. { //Interpolated-against final xforms need animate & color speed set to 0 and motion elements cleared. + ember.m_FinalXform.m_Affine.MakeID(); + ember.m_FinalXform.m_Post.MakeID(); ember.m_FinalXform.m_Animate = 0; ember.m_FinalXform.m_ColorSpeed = 0; ember.m_FinalXform.m_Motion.clear(); ember.m_FinalXform.ClearAndDeleteVariations(); - ember.m_FinalXform.AddVariation(m_VariationList->GetVariationCopy(eVariationId::VAR_LINEAR, 0));//Do this so it doesn't appear empty. + ember.m_FinalXform.AddVariation(m_VariationList->GetVariationCopy(eVariationId::VAR_LINEAR));//Do this so it doesn't appear empty. } } @@ -798,6 +804,9 @@ public: { auto thisXform = GetTotalXform(i); + //if (i == 10) + // cout << i << endl; + if (size == 2 && stagger > 0 && thisXform != &m_FinalXform) { coefSave[0] = coefs[0]; diff --git a/Source/Ember/EmberDefines.h b/Source/Ember/EmberDefines.h index d841125..4c3f0af 100644 --- a/Source/Ember/EmberDefines.h +++ b/Source/Ember/EmberDefines.h @@ -37,7 +37,7 @@ static void sincos(float x, float* s, float* c) namespace EmberNs { -#define EMBER_VERSION "1.0.0.9" +#define EMBER_VERSION "1.0.0.10" #define EPS6 T(1e-6) #define EPS std::numeric_limits::epsilon()//Apoplugin.h uses -20, but it's more mathematically correct to do it this way. #define ISAAC_SIZE 4 diff --git a/Source/Ember/EmberToXml.cpp b/Source/Ember/EmberToXml.cpp index bb900d7..91be425 100644 --- a/Source/Ember/EmberToXml.cpp +++ b/Source/Ember/EmberToXml.cpp @@ -508,9 +508,7 @@ string EmberToXml::ToString(Xform& xform, size_t xformCount, bool isFinal, string s = xform.m_Name; std::replace(s.begin(), s.end(), ' ', '_'); os << "name=\"" << s << "\" ";//Flam3 didn't do this, but Apo does. - - if (!isFinal) - os << "animate=\"" << xform.m_Animate << "\" "; + os << "animate=\"" << xform.m_Animate << "\" ";//Flam3 only did this for non-final. Ember supports animating final. } //Variation writing order differs slightly from the original to make it a bit more readable. diff --git a/Source/Ember/Interpolate.h b/Source/Ember/Interpolate.h index 6160686..db6f1aa 100644 --- a/Source/Ember/Interpolate.h +++ b/Source/Ember/Interpolate.h @@ -24,6 +24,25 @@ template class EMBER_API Interpolater { public: + /// + /// Determine if the xform at a given index in an ember is a padding xform. + /// + /// The ember whose xforms will be examined for padding + /// The index of the ember to examine + /// Whether the xform being examined is the final one + /// True the xform at index xf is a padding one, else false. + static bool IsPadding(const Ember& ember, size_t xf, bool isFinal) + { + if (!isFinal)//Either a final wasn't present in any ember, or if there was, this xform is a normal one, so do not include final in the index check. + { + return xf >= ember.XformCount(); + } + else//There was a final present, and we are checking it, so just see if its presence differs. + { + return !ember.UseFinalXform(); + } + } + /// /// Aligns the specified array of embers and stores in the output array. /// This is used to prepare embers before interpolating them. @@ -41,7 +60,7 @@ public: static void Align(const Ember* sourceEmbers, Ember* destEmbers, size_t count) { bool aligned = true; - bool currentFinal, final = sourceEmbers[0].UseFinalXform(); + bool currentFinal, hasFinal = sourceEmbers[0].UseFinalXform(); size_t i, xf, currentCount, maxCount = sourceEmbers[0].XformCount(); Xform* destOtherXform; auto variationList = VariationList::Instance(); @@ -62,18 +81,18 @@ public: currentFinal = sourceEmbers[i].UseFinalXform(); - if (final != currentFinal)//Check if any used final. + if (hasFinal != currentFinal)//Check if any used final. { aligned = false; - final |= currentFinal; + hasFinal |= currentFinal; } } //Copy them using the max xform count, and do final if any had final. for (i = 0; i < count; i++) - destEmbers[i] = sourceEmbers[i].Copy(maxCount, final); + destEmbers[i] = sourceEmbers[i].Copy(maxCount, hasFinal); - if (final) + if (hasFinal) maxCount++; //Check to see if there's a parametric variation present in one xform @@ -82,11 +101,13 @@ public: //All embers will have the same number of xforms at this point. for (i = 0; i < count; i++) { - size_t ii; + intmax_t ii; for (xf = 0; xf < maxCount; xf++)//This will include both normal xforms and the final. { - auto destXform = destEmbers[i].GetTotalXform(xf, final); + bool isFinal = hasFinal && (xf == maxCount - 1); + auto destXform = destEmbers[i].GetTotalXform(xf, hasFinal); + Variation* dummyvar = nullptr; //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) @@ -104,9 +125,9 @@ public: //rings2, fan2, blob, perspective, julian, juliascope, ngon, curl, super_shape, split //If so, can use a better starting point for these. //If the current xform index is greater than what the original xform count was for this ember, then it's a padding xform. - if (xf >= sourceEmbers[i].TotalXformCount() && !aligned) + if (IsPadding(sourceEmbers[i], xf, isFinal) && !aligned) { - size_t found = 0; + intmax_t found = 0; //Remove linear. destXform->DeleteVariationById(eVariationId::VAR_LINEAR); @@ -121,32 +142,36 @@ public: continue; //Skip if this is also padding. - if (xf >= sourceEmbers[i + ii].TotalXformCount()) + if (IsPadding(sourceEmbers[i + ii], xf, isFinal)) continue; - destOtherXform = destEmbers[i + ii].GetTotalXform(xf); - - //Spherical / Ngon (trumps all others due to holes) - //Interpolate these against a 180 degree rotated identity - //with weight -1. - //Added JULIAN/JULIASCOPE to get rid of black wedges. - if (destOtherXform->GetVariationById(eVariationId::VAR_SPHERICAL) || - destOtherXform->GetVariationById(eVariationId::VAR_NGON) || - destOtherXform->GetVariationById(eVariationId::VAR_JULIAN) || - destOtherXform->GetVariationById(eVariationId::VAR_JULIASCOPE) || - destOtherXform->GetVariationById(eVariationId::VAR_POLAR) || - destOtherXform->GetVariationById(eVariationId::VAR_WEDGE_SPH) || - destOtherXform->GetVariationById(eVariationId::VAR_WEDGE_JULIA)) + if (destOtherXform = destEmbers[i + ii].GetTotalXform(xf)) { - destXform->AddVariation(variationList->GetVariationCopy(eVariationId::VAR_LINEAR, -1)); - //Set the coefs appropriately. - destXform->m_Affine.A(-1); - destXform->m_Affine.D(0); - destXform->m_Affine.B(0); - destXform->m_Affine.E(-1); - destXform->m_Affine.C(0); - destXform->m_Affine.F(0); - found = -1; + //Spherical / Ngon (trumps all others due to holes) + //Interpolate these against a 180 degree rotated identity + //with weight -1. + //Added JULIAN/JULIASCOPE to get rid of black wedges. + + //Testing for variation weight > 0 is to make the behavior match flam3 exactly, even though that doesn't really make sense in the modern era + //Because variations can use negative weights. + if (((dummyvar = destOtherXform->GetVariationById(eVariationId::VAR_SPHERICAL) ) && dummyvar->m_Weight > 0) || + ((dummyvar = destOtherXform->GetVariationById(eVariationId::VAR_NGON) ) && dummyvar->m_Weight > 0) || + ((dummyvar = destOtherXform->GetVariationById(eVariationId::VAR_JULIAN) ) && dummyvar->m_Weight > 0) || + ((dummyvar = destOtherXform->GetVariationById(eVariationId::VAR_JULIASCOPE) ) && dummyvar->m_Weight > 0) || + ((dummyvar = destOtherXform->GetVariationById(eVariationId::VAR_POLAR) ) && dummyvar->m_Weight > 0) || + ((dummyvar = destOtherXform->GetVariationById(eVariationId::VAR_WEDGE_SPH) ) && dummyvar->m_Weight > 0) || + ((dummyvar = destOtherXform->GetVariationById(eVariationId::VAR_WEDGE_JULIA)) && dummyvar->m_Weight > 0)) + { + destXform->AddVariation(variationList->GetVariationCopy(eVariationId::VAR_LINEAR, -1)); + //Set the coefs appropriately. + destXform->m_Affine.A(-1); + destXform->m_Affine.D(0); + destXform->m_Affine.B(0); + destXform->m_Affine.E(-1); + destXform->m_Affine.C(0); + destXform->m_Affine.F(0); + found = -1; + } } } } @@ -160,79 +185,110 @@ public: continue; //Skip if this is also padding. - if (xf >= sourceEmbers[i + ii].TotalXformCount()) + if (IsPadding(sourceEmbers[i + ii], xf, isFinal)) continue; - destOtherXform = destEmbers[i + ii].GetTotalXform(xf); - - if (destOtherXform->GetVariationById(eVariationId::VAR_RECTANGLES)) + if (destOtherXform = destEmbers[i + ii].GetTotalXform(xf)) { - if (auto var = variationList->GetParametricVariationCopy(eVariationId::VAR_RECTANGLES)) + if ((dummyvar = destOtherXform->GetVariationById(eVariationId::VAR_RECTANGLES)) && dummyvar->m_Weight > 0) { - var->SetParamVal("rectangles_x", 0); - var->SetParamVal("rectangles_y", 0); - destXform->AddVariation(var); + destXform->DeleteVariationById(eVariationId::VAR_RECTANGLES);//In case it was there, remove it first so the add below succeeds. + + if (auto var = variationList->GetParametricVariationCopy(eVariationId::VAR_RECTANGLES)) + { + var->SetParamVal("rectangles_x", 0); + var->SetParamVal("rectangles_y", 0); + + if (!destXform->AddVariation(var)) + delete var; + } + + found++; } - found++; - } - - if (destOtherXform->GetVariationById(eVariationId::VAR_RINGS2)) - { - if (auto var = variationList->GetParametricVariationCopy(eVariationId::VAR_RINGS2)) + if ((dummyvar = destOtherXform->GetVariationById(eVariationId::VAR_RINGS2)) && dummyvar->m_Weight > 0) { - var->SetParamVal("rings2_val", 0); - destXform->AddVariation(var); + destXform->DeleteVariationById(eVariationId::VAR_RINGS2); + + if (auto var = variationList->GetParametricVariationCopy(eVariationId::VAR_RINGS2)) + { + var->SetParamVal("rings2_val", 0); + + if (!destXform->AddVariation(var)) + delete var; + } + + found++; } - found++; - } - - if (destOtherXform->GetVariationById(eVariationId::VAR_FAN2)) - { - destXform->AddVariation(variationList->GetVariationCopy(eVariationId::VAR_FAN2)); - found++; - } - - if (destOtherXform->GetVariationById(eVariationId::VAR_BLOB)) - { - if (auto var = variationList->GetParametricVariationCopy(eVariationId::VAR_BLOB)) + if ((dummyvar = destOtherXform->GetVariationById(eVariationId::VAR_FAN2)) && dummyvar->m_Weight > 0) { - var->SetParamVal("blob_low", 1); - destXform->AddVariation(var); + destXform->DeleteVariationById(eVariationId::VAR_FAN2); + + if (auto var = variationList->GetVariationCopy(eVariationId::VAR_FAN2)) + if (!destXform->AddVariation(var)) + delete var; + + found++; } - found++; - } - - if (destOtherXform->GetVariationById(eVariationId::VAR_PERSPECTIVE)) - { - destXform->AddVariation(variationList->GetVariationCopy(eVariationId::VAR_PERSPECTIVE)); - found++; - } - - if (destOtherXform->GetVariationById(eVariationId::VAR_CURL)) - { - if (auto var = variationList->GetParametricVariationCopy(eVariationId::VAR_CURL)) + if ((dummyvar = destOtherXform->GetVariationById(eVariationId::VAR_BLOB)) && dummyvar->m_Weight > 0) { - var->SetParamVal("curl_c1", 0); - destXform->AddVariation(var); + destXform->DeleteVariationById(eVariationId::VAR_BLOB); + + if (auto var = variationList->GetParametricVariationCopy(eVariationId::VAR_BLOB)) + { + var->SetParamVal("blob_low", 1); + + if (!destXform->AddVariation(var)) + delete var; + } + + found++; } - found++; - } - - if (destOtherXform->GetVariationById(eVariationId::VAR_SUPER_SHAPE)) - { - if (auto var = variationList->GetParametricVariationCopy(eVariationId::VAR_SUPER_SHAPE)) + if ((dummyvar = destOtherXform->GetVariationById(eVariationId::VAR_PERSPECTIVE)) && dummyvar->m_Weight > 0) { - var->SetParamVal("super_shape_n1", 2); - var->SetParamVal("super_shape_n2", 2); - var->SetParamVal("super_shape_n3", 2); - destXform->AddVariation(var); + destXform->DeleteVariationById(eVariationId::VAR_PERSPECTIVE); + + if (auto var = variationList->GetVariationCopy(eVariationId::VAR_PERSPECTIVE)) + if (!destXform->AddVariation(var)) + delete var; + + found++; } - found++; + if ((dummyvar = destOtherXform->GetVariationById(eVariationId::VAR_CURL)) && dummyvar->m_Weight > 0) + { + destXform->DeleteVariationById(eVariationId::VAR_CURL); + + if (auto var = variationList->GetParametricVariationCopy(eVariationId::VAR_CURL)) + { + var->SetParamVal("curl_c1", 0); + + if (!destXform->AddVariation(var)) + delete var; + } + + found++; + } + + if ((dummyvar = destOtherXform->GetVariationById(eVariationId::VAR_SUPER_SHAPE)) && dummyvar->m_Weight > 0) + { + destXform->DeleteVariationById(eVariationId::VAR_SUPER_SHAPE); + + if (auto var = variationList->GetParametricVariationCopy(eVariationId::VAR_SUPER_SHAPE)) + { + var->SetParamVal("super_shape_n1", 2); + var->SetParamVal("super_shape_n2", 2); + var->SetParamVal("super_shape_n3", 2); + + if (!destXform->AddVariation(var)) + delete var; + } + + found++; + } } } } @@ -247,25 +303,36 @@ public: continue; //Skip if this is also padding. - if (xf >= sourceEmbers[i + ii].TotalXformCount()) + if (IsPadding(sourceEmbers[i + ii], xf, isFinal)) continue; - destOtherXform = destEmbers[i + ii].GetTotalXform(xf); - - if (destOtherXform->GetVariationById(eVariationId::VAR_FAN)) + if (destOtherXform = destEmbers[i + ii].GetTotalXform(xf)) { - destXform->AddVariation(variationList->GetVariationCopy(eVariationId::VAR_FAN)); - found++; - } + if ((dummyvar = destOtherXform->GetVariationById(eVariationId::VAR_FAN)) && dummyvar->m_Weight > 0) + { + destXform->DeleteVariationById(eVariationId::VAR_FAN); - if (destOtherXform->GetVariationById(eVariationId::VAR_RINGS)) - { - destXform->AddVariation(variationList->GetVariationCopy(eVariationId::VAR_RINGS)); - found++; + if (auto var = variationList->GetVariationCopy(eVariationId::VAR_FAN)) + if (!destXform->AddVariation(var)) + delete var; + + found++; + } + + if ((dummyvar = destOtherXform->GetVariationById(eVariationId::VAR_RINGS)) && dummyvar->m_Weight > 0) + { + destXform->DeleteVariationById(eVariationId::VAR_RINGS); + + if (auto var = variationList->GetVariationCopy(eVariationId::VAR_RINGS)) + if (!destXform->AddVariation(var)) + delete var; + + found++; + } } } - if (found > 0) + if (destXform && (found > 0)) { //Set the coefs appropriately. destXform->m_Affine.A(0); @@ -278,14 +345,20 @@ public: } //If there still are no matches, switch back to linear. - if (found == 0) + if (destXform) { - destXform->AddVariation(variationList->GetVariationCopy(eVariationId::VAR_LINEAR)); - } - else if (found > 0) - { - //Otherwise, normalize the weights. - destXform->NormalizeVariationWeights(); + if (found == 0) + { + destXform->DeleteVariationById(eVariationId::VAR_LINEAR); + + if (auto var = variationList->GetVariationCopy(eVariationId::VAR_LINEAR)) + if (!destXform->AddVariation(var)) + delete var; + } + else if (found > 0) + { + destXform->NormalizeVariationWeights();//Otherwise, normalize the weights. + } } } }//Xforms. @@ -448,6 +521,8 @@ public: Align(&embers[i1 - 1], &m_Embers[0], 4);//Should really be doing some sort of checking here to ensure the ember vectors have 4 elements. smoothFlag = true; } + + //smoothFlag = true; } result.m_Time = time; @@ -602,6 +677,8 @@ public: /// The vec2 vector to store the polar translation values static void ConvertLinearToPolar(const Ember* embers, size_t size, size_t xfi, size_t cflag, vector& cxAng, vector& cxMag, vector& cxTrn) { + const auto LOCALEPS = T(1e-10);//Even though EPS is defined elsewhere, need this here for full compatibility with flam3. + if (size == cxAng.size() && size == cxMag.size() && size == cxTrn.size()) @@ -687,9 +764,9 @@ public: d = cxAng[k][col] - cxAng[k - 1][col]; //Adjust to avoid the -pi/pi discontinuity. - if (d > M_PI + EPS) + if (d > M_PI + LOCALEPS) cxAng[k][col] -= M_2PI; - else if (d < -(M_PI - EPS))//Forces clockwise rotation at 180. + else if (d < -(M_PI - LOCALEPS))//Forces clockwise rotation at 180. cxAng[k][col] += M_2PI; } } @@ -709,6 +786,7 @@ public: /// The size of the embers array static void AsymmetricRefAngles(Ember* embers, size_t count) { + const auto LOCALEPS = T(1e-10);//Even though EPS is defined elsewhere, need this here for full compatibility with flam3. size_t k, xfi; T cxang[4][2], c1[2], d; @@ -734,9 +812,9 @@ public: d = cxang[k][col] - cxang[k - 1][col]; //Adjust to avoid the -pi/pi discontinuity. - if (d > T(M_PI + EPS)) + if (d > T(M_PI + LOCALEPS)) cxang[k][col] -= 2 * T(M_PI); - else if (d < -T(M_PI - EPS) ) + else if (d < -T(M_PI - LOCALEPS)) cxang[k][col] += 2 * T(M_PI); //If this is an asymmetric case, store the NON-symmetric angle diff --git a/Source/Ember/SheepTools.h b/Source/Ember/SheepTools.h index fe15a1c..6b06126 100644 --- a/Source/Ember/SheepTools.h +++ b/Source/Ember/SheepTools.h @@ -995,12 +995,13 @@ public: { m_EdgePrealign[si] = embers[si]; - for (i = 0; i < embers[si].TotalXformCount(); i++) + for (i = 0; i < m_EdgePrealign[si].TotalXformCount(); i++) { auto xform = embers[si].GetTotalXform(i); + auto prealignxform = m_EdgePrealign[si].GetTotalXform(i); if (!xform->m_Motion.empty()) - xform->ApplyMotion(*(m_EdgePrealign[si].GetTotalXform(i)), blend);//Apply motion parameters to result.xform[i] using blend parameter. + prealignxform->ApplyMotion(*xform, blend);//Apply motion parameters to result.xform[i] using blend parameter. } } diff --git a/Source/Ember/Xform.h b/Source/Ember/Xform.h index 04f88d1..4f50d1e 100644 --- a/Source/Ember/Xform.h +++ b/Source/Ember/Xform.h @@ -574,7 +574,7 @@ public: for (auto var : variations) norm += var->m_Weight; - for (auto var : variations) var->m_Weight /= norm; + for (auto var : variations) var->m_Weight /= Zeps(norm);//Ensure a divide by zero never happens. }); } @@ -708,10 +708,11 @@ public: for (size_t i = 0; i < xform.m_Motion.size(); i++) { //Original only pulls these from the first motion xform which is a bug. Want to pull it from each one. - Xform& currentMot = xform.m_Motion[i]; - T freq = currentMot.m_MotionFreq; - eMotion func = currentMot.m_MotionFunc; - T offset = currentMot.m_MotionOffset; + auto& currentMot = xform.m_Motion[i]; + auto freq = currentMot.m_MotionFreq; + auto func = currentMot.m_MotionFunc; + auto offset = currentMot.m_MotionOffset; + auto cleanOffset = offset != EMPTYFIELD ? offset : 0; //Clamp these to the appropriate range after all are applied. APPMOT(m_Weight); APPMOT(m_ColorX); @@ -730,13 +731,13 @@ public: if (!var)//It wasn't present, so add it and set the weight. { Variation* newVar = motVar->Copy(); - newVar->m_Weight = motVar->m_Weight * Interpolater::MotionFuncs(func, freq * (blend + offset)); + newVar->m_Weight = motVar->m_Weight * Interpolater::MotionFuncs(func, freq * (blend + cleanOffset)); AddVariation(newVar); var = newVar;//Use this below for params. } else//It was present, so apply the motion func to the weight. { - var->m_Weight += motVar->m_Weight * Interpolater::MotionFuncs(func, freq * (blend + offset)); + var->m_Weight += motVar->m_Weight * Interpolater::MotionFuncs(func, freq * (blend + cleanOffset)); } //At this point, we've added if needed, or just applied the motion func to the weight. @@ -750,7 +751,7 @@ public: for (size_t k = 0; k < motParVar->ParamCount(); k++) { if (!motParams[k].IsPrecalc()) - *(params[k].Param()) += motParams[k].ParamVal() * Interpolater::MotionFuncs(func, freq * (blend + offset)); + *(params[k].Param()) += motParams[k].ParamVal() * Interpolater::MotionFuncs(func, freq * (blend + cleanOffset)); } } } diff --git a/Source/Ember/XmlToEmber.cpp b/Source/Ember/XmlToEmber.cpp index dbff28b..61059b6 100644 --- a/Source/Ember/XmlToEmber.cpp +++ b/Source/Ember/XmlToEmber.cpp @@ -547,7 +547,7 @@ bool XmlToEmber::ParseEmberElement(xmlNode* emberNode, Ember& currentEmber //First parse out simple float reads. if (ParseAndAssign(curAtt->name, attStr, "time", currentEmber.m_Time, ret)) {} else if (ParseAndAssign(curAtt->name, attStr, "scale", currentEmber.m_PixelsPerUnit, ret)) { currentEmber.m_OrigPixPerUnit = currentEmber.m_PixelsPerUnit; } - else if (ParseAndAssign(curAtt->name, attStr, "rotate", currentEmber.m_Rotate, ret)) {} + else if (ParseAndAssign(curAtt->name, attStr, "rotate", currentEmber.m_Rotate, ret)) { currentEmber.m_Rotate = NormalizeDeg180(currentEmber.m_Rotate); } else if (ParseAndAssign(curAtt->name, attStr, "zoom", currentEmber.m_Zoom, ret)) { ClampGteRef(currentEmber.m_Zoom, 0); } else if (ParseAndAssign(curAtt->name, attStr, "cam_zoom", currentEmber.m_Zoom, ret)) { ClampGteRef(currentEmber.m_Zoom, 0); }//JWildfire uses cam_zoom. else if (ParseAndAssign(curAtt->name, attStr, "filter", currentEmber.m_SpatialFilterRadius, ret)) {} @@ -884,6 +884,7 @@ bool XmlToEmber::ParseEmberElement(xmlNode* emberNode, Ember& currentEmber if (!Compare(childNode->name, "finalxform")) { Xform finalXform; + finalXform.m_Animate = 0;//Do not animate final by default. if (!ParseXform(childNode, finalXform, false, fromEmber)) { diff --git a/Source/EmberCL/RendererCL.h b/Source/EmberCL/RendererCL.h index e3c9e70..8dc5202 100644 --- a/Source/EmberCL/RendererCL.h +++ b/Source/EmberCL/RendererCL.h @@ -57,6 +57,7 @@ class EMBERCL_API RendererCL : public Renderer, public RendererCLBas using EmberNs::Renderer::RendererBase::FuseCount; using EmberNs::Renderer::RendererBase::DensityFilterOffset; using EmberNs::Renderer::RendererBase::PrepFinalAccumVector; + using EmberNs::Renderer::RendererBase::Paused; using EmberNs::Renderer::RendererBase::m_ProgressParameter; using EmberNs::Renderer::RendererBase::m_YAxisUp; using EmberNs::Renderer::RendererBase::m_LockAccum; diff --git a/Source/Fractorium/AboutDialog.ui b/Source/Fractorium/AboutDialog.ui index 5c7f3a2..4543f6c 100644 --- a/Source/Fractorium/AboutDialog.ui +++ b/Source/Fractorium/AboutDialog.ui @@ -58,7 +58,7 @@ QFrame::NoFrame - <html><head/><body><p align="center">Fractorium 1.0.0.9</p><p align="center"><span style=" font-size:10pt;">A Qt-based fractal flame editor which uses a C++ re-write of the flam3 algorithm named Ember and a GPU capable version named EmberCL which implements a portion of the cuburn algorithm in OpenCL.</span></p><p align="center"><a href="http://fractorium.com"><span style=" text-decoration: underline; color:#0000ff;">fractorium.com</span></a><span style=" font-size:10pt;"><br/>Lead: Matt Feemster<br/>Contributors: Simon Detheridge, Michel Mastriani</span></p></body></html> + <html><head/><body><p align="center">Fractorium 1.0.0.10</p><p align="center"><span style=" font-size:10pt;">A Qt-based fractal flame editor which uses a C++ re-write of the flam3 algorithm named Ember and a GPU capable version named EmberCL which implements a portion of the cuburn algorithm in OpenCL.</span></p><p align="center"><a href="http://fractorium.com"><span style=" text-decoration: underline; color:#0000ff;">fractorium.com</span></a><span style=" font-size:10pt;"><br/>Lead: Matt Feemster<br/>Contributors: Simon Detheridge, Michel Mastriani</span></p></body></html> Qt::RichText diff --git a/Source/Fractorium/Fractorium.h b/Source/Fractorium/Fractorium.h index d83cca6..8874628 100644 --- a/Source/Fractorium/Fractorium.h +++ b/Source/Fractorium/Fractorium.h @@ -101,7 +101,6 @@ public: //Geometry. bool ApplyAll(); - void SetCenter(float x, float y); void SetRotation(double rot, bool stealth); void SetScale(double scale); void SetCoordinateStatus(int rasX, int rasY, float worldX, float worldY); @@ -197,6 +196,8 @@ public slots: void OnSequenceRandomRotationsMaxSpinBoxChanged(double d); void OnSequenceBlendFramesSpinBoxChanged(int d); void OnSequenceRandomBlendMaxFramesSpinBoxChanged(int d); + void OnSequenceRandomRotationsPerBlendSpinBoxChanged(int d); + void OnSequenceRandomRotationsPerBlendMaxSpinBoxChanged(int d); //Params. void OnBrightnessChanged(double d);//Color. diff --git a/Source/Fractorium/FractoriumEmberController.cpp b/Source/Fractorium/FractoriumEmberController.cpp index 1c0838f..085d5b0 100644 --- a/Source/Fractorium/FractoriumEmberController.cpp +++ b/Source/Fractorium/FractoriumEmberController.cpp @@ -217,16 +217,18 @@ void FractoriumEmberController::UpdateAll(std::function& ember, /// 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 +/// The function to call which will pass the xform under consideration, the absolute xform index, and the index within the selected xforms /// Whether to apply this update operation on the current, all or selected xforms. Default: eXformUpdate::UPDATE_CURRENT. /// True to update renderer, else false. Default: true. /// The action to add to the rendering queue. Default: eProcessAction::FULL_RENDER. /// The xform index to use when action is eXformUpdate::UPDATE_SPECIFIC. Default: 0. template -void FractoriumEmberController::UpdateXform(std::function*)> func, eXformUpdate updateType, bool updateRender, eProcessAction action, size_t index) +void FractoriumEmberController::UpdateXform(std::function*, size_t, size_t)> func, eXformUpdate updateType, bool updateRender, eProcessAction action, size_t index) { int i = 0; + size_t selIndex = 0; auto current = CurrentXform(); + auto currentIndex = m_Fractorium->ui.CurrentXformCombo->currentIndex(); bool forceFinal = m_Fractorium->HaveFinal(); bool isCurrentFinal = m_Ember.IsFinalXform(current); bool doFinal = updateType != eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL && updateType != eXformUpdate::UPDATE_ALL_EXCEPT_FINAL; @@ -236,14 +238,14 @@ void FractoriumEmberController::UpdateXform(std::function*)> fu case eXformUpdate::UPDATE_SPECIFIC: { if (auto xform = m_Ember.GetTotalXform(index, forceFinal)) - func(xform); + func(xform, index, 0); } break; case eXformUpdate::UPDATE_CURRENT: { if (current) - func(current); + func(current, currentIndex, 0); } break; @@ -253,25 +255,19 @@ void FractoriumEmberController::UpdateXform(std::function*)> fu while (auto xform = m_Ember.GetTotalXform(i, forceFinal)) { - if (i < m_Fractorium->m_XformSelections.size()) + if (m_Fractorium->IsXformSelected(i)) { - if (auto w = m_Fractorium->m_XformSelections[i]) - { - if (w->isChecked()) - { - func(xform); + func(xform, i, selIndex++); - if (xform == current) - currentDone = true; - } - } + if (xform == current) + currentDone = true; } i++; } if (!currentDone)//Current was not among those selected, so apply to it. - func(current); + func(current, currentIndex, selIndex); } break; @@ -282,16 +278,10 @@ void FractoriumEmberController::UpdateXform(std::function*)> fu while (auto xform = (doFinal ? m_Ember.GetTotalXform(i, forceFinal) : m_Ember.GetXform(i))) { - if (i < m_Fractorium->m_XformSelections.size()) + if (m_Fractorium->IsXformSelected(i)) { - if (auto w = m_Fractorium->m_XformSelections[i]) - { - if (w->isChecked()) - { - func(xform); - anyUpdated = true; - } - } + func(xform, i, selIndex++); + anyUpdated = true; } i++; @@ -300,22 +290,22 @@ 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 (current) - func(current); + func(current, currentIndex, selIndex); } break; case eXformUpdate::UPDATE_ALL: { - while (auto xform = m_Ember.GetTotalXform(i++, forceFinal)) - func(xform); + while (auto xform = m_Ember.GetTotalXform(i, forceFinal)) + func(xform, i++, selIndex++); } break; case eXformUpdate::UPDATE_ALL_EXCEPT_FINAL: default: { - while (auto xform = m_Ember.GetXform(i++)) - func(xform); + while (auto xform = m_Ember.GetXform(i)) + func(xform, i++, selIndex++); } break; } diff --git a/Source/Fractorium/FractoriumEmberController.h b/Source/Fractorium/FractoriumEmberController.h index 8db68d0..bca490a 100644 --- a/Source/Fractorium/FractoriumEmberController.h +++ b/Source/Fractorium/FractoriumEmberController.h @@ -450,7 +450,7 @@ public: 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, size_t index = 0); + void UpdateXform(std::function*, size_t, size_t)> func, eXformUpdate updateType = eXformUpdate::UPDATE_CURRENT, bool updateRender = true, eProcessAction action = eProcessAction::FULL_RENDER, size_t index = 0); //Xforms Affine. virtual void AffineSetHelper(double d, int index, bool pre) override; diff --git a/Source/Fractorium/FractoriumLibrary.cpp b/Source/Fractorium/FractoriumLibrary.cpp index 283d2b6..a1c9b73 100644 --- a/Source/Fractorium/FractoriumLibrary.cpp +++ b/Source/Fractorium/FractoriumLibrary.cpp @@ -7,14 +7,6 @@ void Fractorium::InitLibraryUI() { ui.LibraryTree->SetMainWindow(this); - ui.SequenceStaggerSpinBox->setValue(m_Settings->Stagger()); - ui.SequenceRandomStaggerMaxSpinBox->setValue(m_Settings->StaggerMax()); - ui.SequenceFramesPerRotSpinBox->setValue(m_Settings->FramesPerRot()); - ui.SequenceRandomFramesPerRotMaxSpinBox->setValue(m_Settings->FramesPerRotMax()); - ui.SequenceRotationsSpinBox->setValue(m_Settings->Rotations()); - ui.SequenceRandomRotationsMaxSpinBox->setValue(m_Settings->RotationsMax()); - ui.SequenceBlendFramesSpinBox->setValue(m_Settings->BlendFrames()); - ui.SequenceRandomBlendMaxFramesSpinBox->setValue(m_Settings->BlendFramesMax()); connect(ui.LibraryTree, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(OnEmberTreeItemChanged(QTreeWidgetItem*, int)), Qt::QueuedConnection); connect(ui.LibraryTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(OnEmberTreeItemDoubleClicked(QTreeWidgetItem*, int)), Qt::QueuedConnection); connect(ui.LibraryTree, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(OnEmberTreeItemDoubleClicked(QTreeWidgetItem*, int)), Qt::QueuedConnection); @@ -41,6 +33,33 @@ void Fractorium::InitLibraryUI() connect(ui.SequenceRandomRotationsMaxSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnSequenceRandomRotationsMaxSpinBoxChanged(double)), Qt::QueuedConnection); connect(ui.SequenceBlendFramesSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnSequenceBlendFramesSpinBoxChanged(int)), Qt::QueuedConnection); connect(ui.SequenceRandomBlendMaxFramesSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnSequenceRandomBlendMaxFramesSpinBoxChanged(int)), Qt::QueuedConnection); + connect(ui.SequenceRotationsPerBlendSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnSequenceRandomRotationsPerBlendSpinBoxChanged(int)), Qt::QueuedConnection); + connect(ui.SequenceRotationsPerBlendMaxSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnSequenceRandomRotationsPerBlendMaxSpinBoxChanged(int)), Qt::QueuedConnection); + //Stagger + ui.SequenceStaggerSpinBox->setValue(m_Settings->Stagger());//Lower. + ui.SequenceStaggerSpinBox->setMaximum(std::numeric_limits::max());//Lower max = upper. + ui.SequenceRandomStaggerMaxSpinBox->setValue(m_Settings->StaggerMax());//Upper. + ui.SequenceRandomStaggerMaxSpinBox->setMinimum(m_Settings->Stagger());//Upper min = lower max. + //Frames per rotation. + ui.SequenceFramesPerRotSpinBox->setValue(m_Settings->FramesPerRot());//Lower. + ui.SequenceFramesPerRotSpinBox->setMaximum(std::numeric_limits::max());//Lower max = upper. + ui.SequenceRandomFramesPerRotMaxSpinBox->setValue(m_Settings->FramesPerRotMax());//Upper. + ui.SequenceRandomFramesPerRotMaxSpinBox->setMinimum(m_Settings->FramesPerRot());//Upper min = lower max. + //Rotations. + ui.SequenceRotationsSpinBox->setValue(m_Settings->Rotations());//Lower. + ui.SequenceRotationsSpinBox->setMaximum(std::numeric_limits::max());//Lower max = upper. + ui.SequenceRandomRotationsMaxSpinBox->setValue(m_Settings->RotationsMax());//Upper. + ui.SequenceRandomRotationsMaxSpinBox->setMinimum(m_Settings->Rotations());//Upper min = lower max. + //Blend frames. + ui.SequenceBlendFramesSpinBox->setValue(m_Settings->BlendFrames());//Lower. + ui.SequenceBlendFramesSpinBox->setMaximum(std::numeric_limits::max());//Lower max = upper. + ui.SequenceRandomBlendMaxFramesSpinBox->setValue(m_Settings->BlendFramesMax());//Upper. + ui.SequenceRandomBlendMaxFramesSpinBox->setMinimum(m_Settings->BlendFrames());//Upper min = lower max. + //Rotations per blend. + ui.SequenceRotationsPerBlendSpinBox->setValue(m_Settings->RotationsPerBlend());//Lower. + ui.SequenceRotationsPerBlendSpinBox->setMaximum(std::numeric_limits::max());//Lower max = upper. + ui.SequenceRotationsPerBlendMaxSpinBox->setValue(m_Settings->RotationsPerBlendMax());//Upper. + ui.SequenceRotationsPerBlendMaxSpinBox->setMinimum(m_Settings->RotationsPerBlend());//Upper min = lower max. } /// @@ -627,7 +646,8 @@ void FractoriumEmberController::SequenceGenerateButtonClicked() if (framesPerRot > 1 && !rots)//Because framesPerRot control has a min value of 1, check greater than 1. Also don't need to check the inverse like in EmberGenome. { QMessageBox::critical(m_Fractorium, "Animation sequence parameters error", - "Frames per rot cannot be positive while Rotations is zero"); + "Frames per rot cannot be greater than one while Rotations is zero. Setting it to 1."); + ui.SequenceFramesPerRotSpinBox->setValue(1); return; } } @@ -713,6 +733,8 @@ void FractoriumEmberController::SequenceGenerateButtonClicked() for (frame = 0; frame < blendFrames; frame++) { + //if (frame == 43) + // cout << frame << endl; bool seqFlag = frame == 0 || (frame == blendFrames - 1); blend = frame / double(blendFrames); result.Clear(); @@ -830,25 +852,59 @@ void FractoriumEmberController::SequenceOpenButtonClicked() void Fractorium::OnSequenceOpenButtonClicked(bool checked) { m_Controller->SequenceOpenButtonClicked(); } -void Fractorium::OnSequenceRandomizeStaggerCheckBoxStateChanged(int state) { ui.SequenceRandomStaggerMaxSpinBox->setEnabled(state); } -void Fractorium::OnSequenceRandomizeFramesPerRotCheckBoxStateChanged(int state) { ui.SequenceRandomFramesPerRotMaxSpinBox->setEnabled(state); } -void Fractorium::OnSequenceRandomizeRotationsCheckBoxStateChanged(int state) { ui.SequenceRandomRotationsMaxSpinBox->setEnabled(state); } -void Fractorium::OnSequenceRandomizeBlendFramesCheckBoxStateChanged(int state) { ui.SequenceRandomBlendMaxFramesSpinBox->setEnabled(state); } -void Fractorium::OnSequenceRandomizeRotationsPerBlendCheckBoxStateChanged(int state) { ui.SequenceRotationsPerBlendMaxSpinBox->setEnabled(state); } +/// +/// Constrain all min/max spinboxes when the max spinboxes are enabled/disabled via the random checkbox. +/// +void Fractorium::OnSequenceRandomizeStaggerCheckBoxStateChanged(int state) +{ + ui.SequenceRandomStaggerMaxSpinBox->setMinimum(ui.SequenceStaggerSpinBox->value()); + ui.SequenceStaggerSpinBox->setMaximum(state ? ui.SequenceRandomStaggerMaxSpinBox->value() : std::numeric_limits::max()); + ui.SequenceRandomStaggerMaxSpinBox->setEnabled(state); +} + +void Fractorium::OnSequenceRandomizeFramesPerRotCheckBoxStateChanged(int state) +{ + ui.SequenceRandomFramesPerRotMaxSpinBox->setMinimum(ui.SequenceFramesPerRotSpinBox->value()); + ui.SequenceFramesPerRotSpinBox->setMaximum(state ? ui.SequenceRandomFramesPerRotMaxSpinBox->value() : std::numeric_limits::max()); + ui.SequenceRandomFramesPerRotMaxSpinBox->setEnabled(state); +} + +void Fractorium::OnSequenceRandomizeRotationsCheckBoxStateChanged(int state) +{ + ui.SequenceRandomRotationsMaxSpinBox->setMinimum(ui.SequenceRotationsSpinBox->value()); + ui.SequenceRotationsSpinBox->setMaximum(state ? ui.SequenceRandomRotationsMaxSpinBox->value() : std::numeric_limits::max()); + ui.SequenceRandomRotationsMaxSpinBox->setEnabled(state); +} + +void Fractorium::OnSequenceRandomizeBlendFramesCheckBoxStateChanged(int state) +{ + ui.SequenceRandomBlendMaxFramesSpinBox->setMinimum(ui.SequenceBlendFramesSpinBox->value()); + ui.SequenceBlendFramesSpinBox->setMaximum(state ? ui.SequenceRandomBlendMaxFramesSpinBox->value() : std::numeric_limits::max()); + ui.SequenceRandomBlendMaxFramesSpinBox->setEnabled(state); +} + +void Fractorium::OnSequenceRandomizeRotationsPerBlendCheckBoxStateChanged(int state) +{ + ui.SequenceRotationsPerBlendMaxSpinBox->setMinimum(ui.SequenceRotationsPerBlendSpinBox->value()); + ui.SequenceRotationsPerBlendSpinBox->setMaximum(state ? ui.SequenceRotationsPerBlendMaxSpinBox->value() : std::numeric_limits::max()); + ui.SequenceRotationsPerBlendMaxSpinBox->setEnabled(state); +} /// /// Constrain all min/max spinboxes. /// -void Fractorium::OnSequenceStaggerSpinBoxChanged(double d) { if (ui.SequenceRandomizeStaggerCheckBox->isChecked()) ConstrainLow(ui.SequenceStaggerSpinBox, ui.SequenceRandomStaggerMaxSpinBox); } -void Fractorium::OnSequenceRandomStaggerMaxSpinBoxChanged(double d) { ConstrainHigh(ui.SequenceStaggerSpinBox, ui.SequenceRandomStaggerMaxSpinBox); } -void Fractorium::OnSequenceStartFlameSpinBoxChanged(int d) { ConstrainLow(ui.SequenceStartFlameSpinBox, ui.SequenceStopFlameSpinBox); } -void Fractorium::OnSequenceStopFlameSpinBoxChanged(int d) { ConstrainHigh(ui.SequenceStartFlameSpinBox, ui.SequenceStopFlameSpinBox); } -void Fractorium::OnSequenceFramesPerRotSpinBoxChanged(int d) { if (ui.SequenceRandomizeFramesPerRotCheckBox->isChecked()) ConstrainLow(ui.SequenceFramesPerRotSpinBox, ui.SequenceRandomFramesPerRotMaxSpinBox); } -void Fractorium::OnSequenceRandomFramesPerRotMaxSpinBoxChanged(int d) { ConstrainHigh(ui.SequenceFramesPerRotSpinBox, ui.SequenceRandomFramesPerRotMaxSpinBox); } -void Fractorium::OnSequenceRotationsSpinBoxChanged(double d) { if (ui.SequenceRandomizeRotationsCheckBox->isChecked()) ConstrainLow(ui.SequenceRotationsSpinBox, ui.SequenceRandomRotationsMaxSpinBox); } -void Fractorium::OnSequenceRandomRotationsMaxSpinBoxChanged(double d) { ConstrainHigh(ui.SequenceRotationsSpinBox, ui.SequenceRandomRotationsMaxSpinBox); } -void Fractorium::OnSequenceBlendFramesSpinBoxChanged(int d) { if (ui.SequenceRandomizeBlendFramesCheckBox->isChecked()) ConstrainLow(ui.SequenceBlendFramesSpinBox, ui.SequenceRandomBlendMaxFramesSpinBox); } -void Fractorium::OnSequenceRandomBlendMaxFramesSpinBoxChanged(int d) { ConstrainHigh(ui.SequenceBlendFramesSpinBox, ui.SequenceRandomBlendMaxFramesSpinBox); } +void Fractorium::OnSequenceStaggerSpinBoxChanged(double d) { if (ui.SequenceRandomizeStaggerCheckBox->isChecked()) ui.SequenceRandomStaggerMaxSpinBox->setMinimum(d); } +void Fractorium::OnSequenceRandomStaggerMaxSpinBoxChanged(double d) { if (ui.SequenceRandomStaggerMaxSpinBox->hasFocus()) ui.SequenceStaggerSpinBox->setMaximum(d); } +void Fractorium::OnSequenceStartFlameSpinBoxChanged(int d) { ui.SequenceStopFlameSpinBox->setMinimum(d); } +void Fractorium::OnSequenceStopFlameSpinBoxChanged(int d) { if (ui.SequenceStopFlameSpinBox->hasFocus()) ui.SequenceStartFlameSpinBox->setMaximum(d); } +void Fractorium::OnSequenceFramesPerRotSpinBoxChanged(int d) { if (ui.SequenceRandomizeFramesPerRotCheckBox->isChecked()) ui.SequenceRandomFramesPerRotMaxSpinBox->setMinimum(d); } +void Fractorium::OnSequenceRandomFramesPerRotMaxSpinBoxChanged(int d) { if (ui.SequenceRandomFramesPerRotMaxSpinBox->hasFocus()) ui.SequenceFramesPerRotSpinBox->setMaximum(d); } +void Fractorium::OnSequenceRotationsSpinBoxChanged(double d) { if (ui.SequenceRandomizeRotationsCheckBox->isChecked()) ui.SequenceRandomRotationsMaxSpinBox->setMinimum(d); } +void Fractorium::OnSequenceRandomRotationsMaxSpinBoxChanged(double d) { if (ui.SequenceRandomRotationsMaxSpinBox->hasFocus()) ui.SequenceRotationsSpinBox->setMaximum(d); } +void Fractorium::OnSequenceBlendFramesSpinBoxChanged(int d) { if (ui.SequenceRandomizeBlendFramesCheckBox->isChecked()) ui.SequenceRandomBlendMaxFramesSpinBox->setMinimum(d); } +void Fractorium::OnSequenceRandomBlendMaxFramesSpinBoxChanged(int d) { if (ui.SequenceRandomBlendMaxFramesSpinBox->hasFocus()) ui.SequenceBlendFramesSpinBox->setMaximum(d); } +void Fractorium::OnSequenceRandomRotationsPerBlendSpinBoxChanged(int d) { if (ui.SequenceRandomizeRotationsPerBlendCheckBox->isChecked()) ui.SequenceRotationsPerBlendMaxSpinBox->setMinimum(d); } +void Fractorium::OnSequenceRandomRotationsPerBlendMaxSpinBoxChanged(int d) { if (ui.SequenceRotationsPerBlendMaxSpinBox->hasFocus()) ui.SequenceRotationsPerBlendSpinBox->setMaximum(d); } /// /// Save all sequence settings to match the values in the controls. @@ -863,6 +919,8 @@ void Fractorium::SyncSequenceSettings() m_Settings->RotationsMax(ui.SequenceRandomRotationsMaxSpinBox->value()); m_Settings->BlendFrames(ui.SequenceBlendFramesSpinBox->value()); m_Settings->BlendFramesMax(ui.SequenceRandomBlendMaxFramesSpinBox->value()); + m_Settings->RotationsPerBlend(ui.SequenceRotationsPerBlendSpinBox->value()); + m_Settings->RotationsPerBlendMax(ui.SequenceRotationsPerBlendMaxSpinBox->value()); } template class FractoriumEmberController; diff --git a/Source/Fractorium/FractoriumMenus.cpp b/Source/Fractorium/FractoriumMenus.cpp index f1117c6..420cdfe 100644 --- a/Source/Fractorium/FractoriumMenus.cpp +++ b/Source/Fractorium/FractoriumMenus.cpp @@ -673,7 +673,7 @@ void FractoriumEmberController::CopySelectedXforms() { m_CopiedXforms.clear(); m_CopiedFinalXform.Clear(); - UpdateXform([&](Xform* xform) + UpdateXform([&](Xform* xform, size_t xfindex, size_t selIndex) { if (m_Ember.IsFinalXform(xform)) m_CopiedFinalXform = *xform; diff --git a/Source/Fractorium/FractoriumParams.cpp b/Source/Fractorium/FractoriumParams.cpp index b60cf1b..658a586 100644 --- a/Source/Fractorium/FractoriumParams.cpp +++ b/Source/Fractorium/FractoriumParams.cpp @@ -44,18 +44,18 @@ void Fractorium::InitParamsUI() //Geometry. row = 0; table = ui.GeometryTable; - SetupSpinner (table, this, row, 1, m_WidthSpin, spinHeight, 10, 2048, 50, SIGNAL(valueChanged(int)), SLOT(OnWidthChanged(int)), true, width(), width(), width()); - SetupSpinner (table, this, row, 1, m_HeightSpin, spinHeight, 10, 2048, 50, SIGNAL(valueChanged(int)), SLOT(OnHeightChanged(int)), true, height(), height(), height()); - SetupSpinner(table, this, row, 1, m_CenterXSpin, spinHeight, -dmax, dmax, 0.05, SIGNAL(valueChanged(double)), SLOT(OnCenterXChanged(double)), true, 0, 0, 0); - SetupSpinner(table, this, row, 1, m_CenterYSpin, spinHeight, -dmax, dmax, 0.05, SIGNAL(valueChanged(double)), SLOT(OnCenterYChanged(double)), true, 0, 0, 0); - SetupSpinner(table, this, row, 1, m_ScaleSpin, spinHeight, 10, dmax, 20, SIGNAL(valueChanged(double)), SLOT(OnScaleChanged(double)), true, 240, 240, 240); - SetupSpinner(table, this, row, 1, m_ZoomSpin, spinHeight, 0, 25, 0.2, SIGNAL(valueChanged(double)), SLOT(OnZoomChanged(double)), true, 0, 0, 0); + SetupSpinner (table, this, row, 1, m_WidthSpin, spinHeight, 10, 2048, 50, SIGNAL(valueChanged(int)), SLOT(OnWidthChanged(int)), true, width(), width(), width()); + SetupSpinner (table, this, row, 1, m_HeightSpin, spinHeight, 10, 2048, 50, SIGNAL(valueChanged(int)), SLOT(OnHeightChanged(int)), true, height(), height(), height()); + SetupSpinner(table, this, row, 1, m_CenterXSpin, spinHeight, -dmax, dmax, 0.05, SIGNAL(valueChanged(double)), SLOT(OnCenterXChanged(double)), true, 0, 0, 0); + SetupSpinner(table, this, row, 1, m_CenterYSpin, spinHeight, -dmax, dmax, 0.05, SIGNAL(valueChanged(double)), SLOT(OnCenterYChanged(double)), true, 0, 0, 0); + SetupSpinner(table, this, row, 1, m_ScaleSpin, spinHeight, 10, dmax, 20, SIGNAL(valueChanged(double)), SLOT(OnScaleChanged(double)), true, 240, 240, 240); + SetupSpinner(table, this, row, 1, m_ZoomSpin, spinHeight, 0, 25, 0.2, SIGNAL(valueChanged(double)), SLOT(OnZoomChanged(double)), true, 0, 0, 0); SetupSpinner(table, this, row, 1, m_RotateSpin, spinHeight, -180, 180, 10, SIGNAL(valueChanged(double)), SLOT(OnRotateChanged(double)), true, 0, 0, 0); - SetupSpinner(table, this, row, 1, m_ZPosSpin, spinHeight, -1000, 1000, 1, SIGNAL(valueChanged(double)), SLOT(OnZPosChanged(double)), true, 0, 1, 0); - SetupSpinner(table, this, row, 1, m_PerspectiveSpin, spinHeight, -500, 500, 0.01, SIGNAL(valueChanged(double)), SLOT(OnPerspectiveChanged(double)), true, 0, 1, 0); - SetupSpinner(table, this, row, 1, m_PitchSpin, spinHeight, -180, 180, 1, SIGNAL(valueChanged(double)), SLOT(OnPitchChanged(double)), true, 0, 45, 0); - SetupSpinner(table, this, row, 1, m_YawSpin, spinHeight, -180, 180, 1, SIGNAL(valueChanged(double)), SLOT(OnYawChanged(double)), true, 0, 45, 0); - SetupSpinner(table, this, row, 1, m_DepthBlurSpin, spinHeight, -100, 100, 0.01, SIGNAL(valueChanged(double)), SLOT(OnDepthBlurChanged(double)), true, 0, 1, 0); + SetupSpinner(table, this, row, 1, m_ZPosSpin, spinHeight, -1000, 1000, 1, SIGNAL(valueChanged(double)), SLOT(OnZPosChanged(double)), true, 0, 1, 0); + SetupSpinner(table, this, row, 1, m_PerspectiveSpin, spinHeight, -500, 500, 0.01, SIGNAL(valueChanged(double)), SLOT(OnPerspectiveChanged(double)), true, 0, 1, 0); + SetupSpinner(table, this, row, 1, m_PitchSpin, spinHeight, -dmax, dmax, 1, SIGNAL(valueChanged(double)), SLOT(OnPitchChanged(double)), true, 0, 45, 0); + SetupSpinner(table, this, row, 1, m_YawSpin, spinHeight, -dmax, dmax, 1, SIGNAL(valueChanged(double)), SLOT(OnYawChanged(double)), true, 0, 45, 0); + SetupSpinner(table, this, row, 1, m_DepthBlurSpin, spinHeight, -dmax, dmax, 0.01, SIGNAL(valueChanged(double)), SLOT(OnDepthBlurChanged(double)), true, 0, 1, 0); //Set w/h max values. m_CenterXSpin->setDecimals(3); m_CenterYSpin->setDecimals(3); @@ -437,38 +437,6 @@ template void FractoriumEmberController::SpatialFilterTypeChange void Fractorium::OnSpatialFilterTypeComboCurrentIndexChanged(const QString& text) { m_Controller->SpatialFilterTypeChanged(text); } -/// -/// Set the temporal filter width to be used with animation. -/// Called when the temporal filter width spinner is changed. -/// Does not reset anything because this is only used for animation. -/// In the future, when animation is implemented, this will have an effect. -/// -/// The temporal filter width -template void FractoriumEmberController::TemporalFilterWidthChanged(double d) -{ - UpdateAll([&](Ember& ember, bool isMain) - { - ember.m_TemporalFilterWidth = d; - }, true, eProcessAction::NOTHING, m_Fractorium->ApplyAll());//Don't do anything until animation is implemented. -} -void Fractorium::OnTemporalFilterWidthChanged(double d) { m_Controller->TemporalFilterWidthChanged(d); } - -/// -/// Set the temporal filter type to be used with animation. -/// Called when the temporal filter combo box index is changed. -/// Does not reset anything because this is only used for animation. -/// In the future, when animation is implemented, this will have an effect. -/// -/// The name of the temporal filter -template void FractoriumEmberController::TemporalFilterTypeChanged(const QString& text) -{ - UpdateAll([&](Ember& ember, bool isMain) - { - ember.m_TemporalFilterType = TemporalFilterCreator::FromString(text.toStdString()); - }, true, eProcessAction::NOTHING, m_Fractorium->ApplyAll());//Don't do anything until animation is implemented. -} -void Fractorium::OnTemporalFilterTypeComboCurrentIndexChanged(const QString& text) { m_Controller->TemporalFilterTypeChanged(text); } - /// /// Set the density estimation filter min radius value. /// Resets the rendering process to density filtering. @@ -597,22 +565,6 @@ template void FractoriumEmberController::SupersampleChanged(int } void Fractorium::OnSupersampleChanged(int d) { m_Controller->SupersampleChanged(d); } -/// -/// Set the temporal samples to be used with animation. -/// Called when the temporal samples spinner is changed. -/// Does not reset anything because this is only used for animation. -/// In the future, when animation is implemented, this will have an effect. -/// -/// The temporal samples value -template void FractoriumEmberController::TemporalSamplesChanged(int i) -{ - UpdateAll([&](Ember& ember, bool isMain) - { - ember.m_TemporalSamples = i; - }, true, eProcessAction::NOTHING, m_Fractorium->ApplyAll());//Don't do anything until animation is implemented. -} -void Fractorium::OnTemporalSamplesChanged(int d) { m_Controller->TemporalSamplesChanged(d); } - /// /// Set the affine interpolation type. /// Does not reset anything because this is only used for animation. @@ -625,13 +577,21 @@ void FractoriumEmberController::AffineInterpTypeChanged(int i) { UpdateAll([&](Ember& ember, bool isMain) { + eAffineInterp interp; + if (i == 0) - ember.m_AffineInterp = eAffineInterp::AFFINE_INTERP_LINEAR; + interp = eAffineInterp::AFFINE_INTERP_LINEAR; else if (i == 1) - ember.m_AffineInterp = eAffineInterp::AFFINE_INTERP_LOG; + interp = eAffineInterp::AFFINE_INTERP_LOG; else - ember.m_AffineInterp = eAffineInterp::AFFINE_INTERP_LINEAR; - }, true, eProcessAction::NOTHING, m_Fractorium->ApplyAll()); + interp = eAffineInterp::AFFINE_INTERP_LINEAR; + + ember.m_AffineInterp = interp; + + if (!m_Fractorium->ApplyAll()) + if (m_EmberFilePointer) + m_EmberFilePointer->m_AffineInterp = interp; + }, false, eProcessAction::NOTHING, m_Fractorium->ApplyAll()); } void Fractorium::OnAffineInterpTypeComboCurrentIndexChanged(int index) { m_Controller->AffineInterpTypeChanged(index); } @@ -639,7 +599,6 @@ void Fractorium::OnAffineInterpTypeComboCurrentIndexChanged(int index) { m_Contr /// /// Set the interpolation type. /// Does not reset anything because this is only used for animation. -/// In the future, when animation is implemented, this will have an effect. /// Called when the interp type combo box index is changed. /// /// The index @@ -648,17 +607,86 @@ void FractoriumEmberController::InterpTypeChanged(int i) { UpdateAll([&](Ember& ember, bool isMain) { - if (i == 0) - ember.m_Interp = eInterp::EMBER_INTERP_LINEAR; + eInterp interp; + + if (i == 0)//Need to make this work like animate flag where it sets the value but doesn't trigger and update.//TODO + interp = eInterp::EMBER_INTERP_LINEAR; else if (i == 1) - ember.m_Interp = eInterp::EMBER_INTERP_SMOOTH; + interp = eInterp::EMBER_INTERP_SMOOTH; else - ember.m_Interp = eInterp::EMBER_INTERP_LINEAR; - }, true, eProcessAction::NOTHING, m_Fractorium->ApplyAll()); + interp = eInterp::EMBER_INTERP_LINEAR; + + ember.m_Interp = interp; + + if (!m_Fractorium->ApplyAll()) + if (m_EmberFilePointer) + m_EmberFilePointer->m_Interp = interp; + }, false, eProcessAction::NOTHING, m_Fractorium->ApplyAll()); } void Fractorium::OnInterpTypeComboCurrentIndexChanged(int index) { m_Controller->InterpTypeChanged(index); } +/// +/// Set the temporal samples to be used with animation. +/// Called when the temporal samples spinner is changed. +/// Does not reset anything because this is only used for animation. +/// +/// The temporal samples value +template +void FractoriumEmberController::TemporalSamplesChanged(int i) +{ + UpdateAll([&](Ember& ember, bool isMain) + { + ember.m_TemporalSamples = i;//This will be reset on every render to trick the renderer into not thinking it's doing an animation. So setting this has no real effect. Users should set it in the final render dialog when animating. + + if (!m_Fractorium->ApplyAll()) + if (m_EmberFilePointer) + m_EmberFilePointer->m_TemporalSamples = i; + }, false, eProcessAction::NOTHING, m_Fractorium->ApplyAll());//Don't do anything until animation is implemented. +} +void Fractorium::OnTemporalSamplesChanged(int d) { m_Controller->TemporalSamplesChanged(d); } + +/// +/// Set the temporal filter width to be used with animation. +/// Called when the temporal filter width spinner is changed. +/// Does not reset anything because this is only used for animation. +/// +/// The temporal filter width +template +void FractoriumEmberController::TemporalFilterWidthChanged(double d) +{ + UpdateAll([&](Ember& ember, bool isMain) + { + ember.m_TemporalFilterWidth = d; + + if (!m_Fractorium->ApplyAll()) + if (m_EmberFilePointer) + m_EmberFilePointer->m_TemporalFilterWidth = d; + }, false, eProcessAction::NOTHING, m_Fractorium->ApplyAll());//Don't do anything until animation is implemented. +} +void Fractorium::OnTemporalFilterWidthChanged(double d) { m_Controller->TemporalFilterWidthChanged(d); } + +/// +/// Set the temporal filter type to be used with animation. +/// Called when the temporal filter combo box index is changed. +/// Does not reset anything because this is only used for animation. +/// +/// The name of the temporal filter +template +void FractoriumEmberController::TemporalFilterTypeChanged(const QString& text) +{ + UpdateAll([&](Ember& ember, bool isMain) + { + auto filter = TemporalFilterCreator::FromString(text.toStdString()); + ember.m_TemporalFilterType = filter; + + if (!m_Fractorium->ApplyAll()) + if (m_EmberFilePointer) + m_EmberFilePointer->m_TemporalFilterType = filter; + }, false, eProcessAction::NOTHING, m_Fractorium->ApplyAll());//Don't do anything until animation is implemented. +} +void Fractorium::OnTemporalFilterTypeComboCurrentIndexChanged(const QString& text) { m_Controller->TemporalFilterTypeChanged(text); } + /// /// Set the center. /// This updates the spinners as well as the current ember center. diff --git a/Source/Fractorium/FractoriumSettings.cpp b/Source/Fractorium/FractoriumSettings.cpp index faa24d9..7262b2d 100644 --- a/Source/Fractorium/FractoriumSettings.cpp +++ b/Source/Fractorium/FractoriumSettings.cpp @@ -177,29 +177,35 @@ void FractoriumSettings::LoadLast(bool b) { setValue(LOAD /// Sequence generation settings. /// -double FractoriumSettings::Stagger() { return value(STAGGER).toDouble(); } -void FractoriumSettings::Stagger(double d) { setValue(STAGGER, d); } +double FractoriumSettings::Stagger() { return value(STAGGER).toDouble(); } +void FractoriumSettings::Stagger(double d) { setValue(STAGGER, d); } -double FractoriumSettings::StaggerMax() { return value(STAGGERMAX).toDouble(); } -void FractoriumSettings::StaggerMax(double d) { setValue(STAGGERMAX, d); } +double FractoriumSettings::StaggerMax() { return value(STAGGERMAX).toDouble(); } +void FractoriumSettings::StaggerMax(double d) { setValue(STAGGERMAX, d); } -uint FractoriumSettings::FramesPerRot() { return value(FRAMESPERROT).toUInt(); } -void FractoriumSettings::FramesPerRot(uint i) { setValue(FRAMESPERROT, i); } +uint FractoriumSettings::FramesPerRot() { return value(FRAMESPERROT).toUInt(); } +void FractoriumSettings::FramesPerRot(uint i) { setValue(FRAMESPERROT, i); } -uint FractoriumSettings::FramesPerRotMax() { return value(FRAMESPERROTMAX).toUInt(); } -void FractoriumSettings::FramesPerRotMax(uint i) { setValue(FRAMESPERROTMAX, i); } +uint FractoriumSettings::FramesPerRotMax() { return value(FRAMESPERROTMAX).toUInt(); } +void FractoriumSettings::FramesPerRotMax(uint i) { setValue(FRAMESPERROTMAX, i); } -uint FractoriumSettings::Rotations() { return value(ROTATIONS).toDouble(); } -void FractoriumSettings::Rotations(double d) { setValue(ROTATIONS, d); } +uint FractoriumSettings::Rotations() { return value(ROTATIONS).toDouble(); } +void FractoriumSettings::Rotations(double d) { setValue(ROTATIONS, d); } -uint FractoriumSettings::RotationsMax() { return value(ROTATIONSMAX).toDouble(); } -void FractoriumSettings::RotationsMax(double d) { setValue(ROTATIONSMAX, d); } +uint FractoriumSettings::RotationsMax() { return value(ROTATIONSMAX).toDouble(); } +void FractoriumSettings::RotationsMax(double d) { setValue(ROTATIONSMAX, d); } -uint FractoriumSettings::BlendFrames() { return value(BLENDFRAMES).toUInt(); } -void FractoriumSettings::BlendFrames(uint i) { setValue(BLENDFRAMES, i); } +uint FractoriumSettings::BlendFrames() { return value(BLENDFRAMES).toUInt(); } +void FractoriumSettings::BlendFrames(uint i) { setValue(BLENDFRAMES, i); } -uint FractoriumSettings::BlendFramesMax() { return value(BLENDFRAMESMAX).toUInt(); } -void FractoriumSettings::BlendFramesMax(uint i) { setValue(BLENDFRAMESMAX, i); } +uint FractoriumSettings::BlendFramesMax() { return value(BLENDFRAMESMAX).toUInt(); } +void FractoriumSettings::BlendFramesMax(uint i) { setValue(BLENDFRAMESMAX, i); } + +uint FractoriumSettings::RotationsPerBlend() { return value(ROTATIONSPERBLEND).toUInt(); } +void FractoriumSettings::RotationsPerBlend(uint i) { setValue(ROTATIONSPERBLEND, i); } + +uint FractoriumSettings::RotationsPerBlendMax() { return value(ROTATIONSPERBLENDMAX).toUInt(); } +void FractoriumSettings::RotationsPerBlendMax(uint i) { setValue(ROTATIONSPERBLENDMAX, i); } /// /// Variations filter settings. diff --git a/Source/Fractorium/FractoriumSettings.h b/Source/Fractorium/FractoriumSettings.h index f9a825f..880d5bd 100644 --- a/Source/Fractorium/FractoriumSettings.h +++ b/Source/Fractorium/FractoriumSettings.h @@ -37,6 +37,8 @@ #define ROTATIONSMAX "sequence/rotationsmax" #define BLENDFRAMES "sequence/blendframes" #define BLENDFRAMESMAX "sequence/blendframesmax" +#define ROTATIONSPERBLEND "sequence/rotationsperblend" +#define ROTATIONSPERBLENDMAX "sequence/rotationsperblendmax" #define VARFILTERSUM "varfilter/sumcheckbox" #define VARFILTERASSIGN "varfilter/assigncheckbox" @@ -193,6 +195,12 @@ public: uint BlendFramesMax(); void BlendFramesMax(uint i); + uint RotationsPerBlend(); + void RotationsPerBlend(uint i); + + uint RotationsPerBlendMax(); + void RotationsPerBlendMax(uint i); + int VarFilterSum(); void VarFilterSum(int i); diff --git a/Source/Fractorium/FractoriumXforms.cpp b/Source/Fractorium/FractoriumXforms.cpp index 0ef60f0..02b362b 100644 --- a/Source/Fractorium/FractoriumXforms.cpp +++ b/Source/Fractorium/FractoriumXforms.cpp @@ -139,7 +139,7 @@ void FractoriumEmberController::AddLinkedXform() Ember ember = m_Ember; m_Ember.Reserve(m_Ember.XformCount() + 1);//Doing this ahead of time ensures pointers remain valid. auto iterCount = 0; - UpdateXform([&](Xform* xform) + UpdateXform([&](Xform* xform, size_t xfindex, size_t selIndex) { //Covers very strange case where final is selected, but initially not considered because final is excluded, //but after adding the new xform, it thinks its selected index is non-final. @@ -210,7 +210,7 @@ void FractoriumEmberController::DuplicateXform() bool forceFinal = m_Fractorium->HaveFinal(); vector> vec; vec.reserve(m_Ember.XformCount()); - UpdateXform([&] (Xform* xform) + UpdateXform([&] (Xform* xform, size_t xfindex, size_t selIndex) { vec.push_back(*xform); }, eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL, false); @@ -235,7 +235,7 @@ void Fractorium::OnDuplicateXformButtonClicked(bool checked) { m_Controller->Dup template void FractoriumEmberController::ClearXform() { - UpdateXform([&] (Xform* xform) + UpdateXform([&] (Xform* xform, size_t xfindex, size_t selIndex) { xform->ClearAndDeleteVariations();//Note xaos is left alone. }, eXformUpdate::UPDATE_SELECTED); @@ -350,7 +350,7 @@ void Fractorium::OnAddFinalXformButtonClicked(bool checked) { m_Controller->AddF template void FractoriumEmberController::XformWeightChanged(double d) { - UpdateXform([&] (Xform* xform) + UpdateXform([&] (Xform* xform, size_t xfindex, size_t selIndex) { xform->m_Weight = d; }, eXformUpdate::UPDATE_SELECTED_EXCEPT_FINAL); @@ -363,7 +363,7 @@ void Fractorium::OnXformWeightChanged(double d) { m_Controller->XformWeightChang template void FractoriumEmberController::EqualizeWeights() { - UpdateXform([&] (Xform* xform) + UpdateXform([&] (Xform* xform, size_t xfindex, size_t selIndex) { m_Ember.EqualizeWeights(); m_Fractorium->m_XformWeightSpin->setValue(xform->m_Weight);//Will trigger an update, so pass false to updateRender below. @@ -381,65 +381,70 @@ template void FractoriumEmberController::XformNameChanged(int row, int col) { bool forceFinal = m_Fractorium->HaveFinal(); - UpdateXform([&] (Xform* xform) + UpdateXform([&] (Xform* xform, size_t xfindex, size_t selIndex) { - int index = m_Ember.GetTotalXformIndex(xform, forceFinal); xform->m_Name = m_Fractorium->ui.XformWeightNameTable->item(row, col)->text().toStdString(); - XformCheckboxAt(index, [&](QCheckBox * checkbox) { checkbox->setText(MakeXformCaption(index)); }); + XformCheckboxAt(int(xfindex), [&](QCheckBox * checkbox) { checkbox->setText(MakeXformCaption(xfindex)); }); }, eXformUpdate::UPDATE_CURRENT, false); FillSummary();//Manually update because this does not trigger a render, which is where this would normally be called. } void Fractorium::OnXformNameChanged(int row, int col) { m_Controller->XformNameChanged(row, col); } /// -/// Set the animate field of the selected xforms. +/// Set the animate field of the selected xforms, this allows excluding current if it's not checked, but applies only to it if none are checked. /// 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. +/// Applies to all embers if "Apply All" is checked. /// Called when the user toggles the animate xform checkbox. /// /// 1 for checked, else false template void FractoriumEmberController::XformAnimateChanged(int state) { - bool final = IsFinal(CurrentXform()); - auto index = m_Fractorium->ui.CurrentXformCombo->currentIndex(); T animate = state > 0 ? 1 : 0; - UpdateAll([&](Ember& ember, bool isMain) + UpdateXform([&](Xform* xform, size_t xfindex, size_t selIndex) { - if (final)//If the current xform was final, only apply to other embers which also have a final xform. + bool final = IsFinal(xform); + UpdateAll([&](Ember& ember, bool isMain) { - if (ember.UseFinalXform()) + if (final)//If the current xform was final, only apply to other embers which also have a final xform. { - auto xform = ember.NonConstFinalXform(); - xform->m_Animate = animate; - } - - if (!m_Fractorium->ApplyAll()) - if (m_EmberFilePointer && m_EmberFilePointer->UseFinalXform()) - m_EmberFilePointer->NonConstFinalXform()->m_Animate = animate; - } - else//Current was not final, so apply to other embers which have a non-final xform at this index. - { - if (auto xform = ember.GetXform(index)) - xform->m_Animate = animate; - - if (!m_Fractorium->ApplyAll() && m_EmberFilePointer) - if (auto xform = m_EmberFilePointer->GetXform(index)) + if (ember.UseFinalXform()) + { + auto xform = ember.NonConstFinalXform(); xform->m_Animate = animate; - } - }, false, eProcessAction::NOTHING, m_Fractorium->ApplyAll()); + } + + if (!m_Fractorium->ApplyAll()) + if (m_EmberFilePointer && m_EmberFilePointer->UseFinalXform()) + m_EmberFilePointer->NonConstFinalXform()->m_Animate = animate; + } + else//Current was not final, so apply to other embers which have a non-final xform at this index. + { + if (auto xform = ember.GetXform(xfindex)) + xform->m_Animate = animate; + + if (!m_Fractorium->ApplyAll() && m_EmberFilePointer) + if (auto xform = m_EmberFilePointer->GetXform(xfindex)) + xform->m_Animate = animate; + } + }, false, eProcessAction::NOTHING, m_Fractorium->ApplyAll()); + }, eXformUpdate::UPDATE_SELECTED, false); } void Fractorium::OnXformAnimateCheckBoxStateChanged(int state) { m_Controller->XformAnimateChanged(state); } + /// /// Fill all GUI widgets with values from the passed in xform. /// /// The xform whose values will be used to populate the widgets template -void FractoriumEmberController::FillWithXform(Xform* xform)//Need to see where all this is called from and sync with FillXform(). Maybe rename the latter. +void FractoriumEmberController::FillWithXform(Xform* xform) { 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. + m_Fractorium->ui.AnimateXformCheckBox->blockSignals(true); + m_Fractorium->ui.AnimateXformCheckBox->setChecked(xform->m_Animate > 0 ? true : false); + m_Fractorium->ui.AnimateXformCheckBox->blockSignals(false); if (auto item = m_Fractorium->ui.XformWeightNameTable->item(0, 1)) { diff --git a/Source/Fractorium/FractoriumXformsAffine.cpp b/Source/Fractorium/FractoriumXformsAffine.cpp index f49d7e0..23c5f65 100644 --- a/Source/Fractorium/FractoriumXformsAffine.cpp +++ b/Source/Fractorium/FractoriumXformsAffine.cpp @@ -248,7 +248,7 @@ void Fractorium::OnPostAffineColDoubleClicked(int logicalIndex) template void FractoriumEmberController::AffineSetHelper(double d, int index, bool pre) { - UpdateXform([&] (Xform* xform) + UpdateXform([&] (Xform* xform, size_t xfindex, size_t selIndex) { auto& affine = pre ? xform->m_Affine : xform->m_Post; AffineDoubleSpinBox** spinners = pre ? m_Fractorium->m_PreSpins : m_Fractorium->m_PostSpins; @@ -330,7 +330,7 @@ void Fractorium::OnO2Changed(double d) { m_Controller->AffineSetHelper(d, 5, sen template void FractoriumEmberController::FlipXforms(bool horizontal, bool vertical, bool pre) { - UpdateXform([&] (Xform* xform) + UpdateXform([&] (Xform* xform, size_t xfindex, size_t selIndex) { auto& affine = pre ? xform->m_Affine : xform->m_Post; @@ -367,7 +367,7 @@ void Fractorium::OnFlipVerticalButtonClicked(bool checked) { m_Controller->FlipX template void FractoriumEmberController::RotateXformsByAngle(double angle, bool pre) { - UpdateXform([&] (Xform* xform) + UpdateXform([&] (Xform* xform, size_t xfindex, size_t selIndex) { auto& affine = pre ? xform->m_Affine : xform->m_Post; affine.Rotate(angle * DEG_2_RAD_T); @@ -428,7 +428,7 @@ void Fractorium::OnRotateCcButtonClicked(bool checked) template void FractoriumEmberController::MoveXforms(double x, double y, bool pre) { - UpdateXform([&] (Xform* xform) + UpdateXform([&] (Xform* xform, size_t xfindex, size_t selIndex) { auto& affine = pre ? xform->m_Affine : xform->m_Post; affine.C(affine.C() + x); @@ -514,7 +514,7 @@ void Fractorium::OnMoveRightButtonClicked(bool checked) template void FractoriumEmberController::ScaleXforms(double scale, bool pre) { - UpdateXform([&] (Xform* xform) + UpdateXform([&] (Xform* xform, size_t xfindex, size_t selIndex) { auto& affine = pre ? xform->m_Affine : xform->m_Post; affine.A(affine.A() * scale); @@ -567,7 +567,7 @@ void Fractorium::OnScaleUpButtonClicked(bool checked) template void FractoriumEmberController::ResetXformsAffine(bool pre) { - UpdateXform([&] (Xform* xform) + UpdateXform([&] (Xform* xform, size_t xfindex, size_t selIndex) { auto& affine = pre ? xform->m_Affine : xform->m_Post; affine.MakeID(); @@ -585,7 +585,7 @@ void Fractorium::OnResetAffineButtonClicked(bool checked) { m_Controller->ResetX template void FractoriumEmberController::RandomXformsAffine(bool pre) { - UpdateXform([&](Xform* xform) + UpdateXform([&](Xform* xform, size_t xfindex, size_t selIndex) { auto& affine = pre ? xform->m_Affine : xform->m_Post; affine.A(m_Rand.Frand11()); diff --git a/Source/Fractorium/FractoriumXformsColor.cpp b/Source/Fractorium/FractoriumXformsColor.cpp index 30034fc..f58d04a 100644 --- a/Source/Fractorium/FractoriumXformsColor.cpp +++ b/Source/Fractorium/FractoriumXformsColor.cpp @@ -51,7 +51,7 @@ void FractoriumEmberController::XformColorIndexChanged(double d, bool updateR if (updateRender)//False when just updating GUI in response to a change elsewhere, true when in response to a GUI change so update values and reset renderer. { - UpdateXform([&](Xform* xform) + UpdateXform([&](Xform* xform, size_t xfindex, size_t selIndex) { xform->m_ColorX = Clamp(d, 0, 1); }, update, updateRender, eProcessAction::FULL_RENDER, index); @@ -99,7 +99,7 @@ void Fractorium::OnXformScrollColorIndexChanged(int d) template void FractoriumEmberController::RandomColorIndicesButtonClicked() { - UpdateXform([&](Xform* xform) { xform->m_ColorX = m_Rand.Frand01(); }, eXformUpdate::UPDATE_ALL, false);//Don't update renderer here... + UpdateXform([&](Xform* xform, size_t xfindex, size_t selIndex) { xform->m_ColorX = m_Rand.Frand01(); }, eXformUpdate::UPDATE_ALL, false);//Don't update renderer here... m_Fractorium->m_XformColorIndexSpin->setValue(CurrentXform()->m_ColorX);//...do it via GUI. This will set scrollbar value as well. } void Fractorium::OnRandomColorIndicesButtonClicked(bool b) { m_Controller->RandomColorIndicesButtonClicked(); } @@ -113,7 +113,7 @@ template void FractoriumEmberController::ToggleColorIndicesButtonClicked() { char ch = 1; - UpdateXform([&](Xform* xform) { xform->m_ColorX = T(ch ^= 1); }, eXformUpdate::UPDATE_ALL, false);//Don't update renderer here... + UpdateXform([&](Xform* xform, size_t xfindex, size_t selIndex) { xform->m_ColorX = T(ch ^= 1); }, eXformUpdate::UPDATE_ALL, false);//Don't update renderer here... m_Fractorium->m_XformColorIndexSpin->setValue(CurrentXform()->m_ColorX);//...do it via GUI. This will set scrollbar value as well. } void Fractorium::OnToggleColorIndicesButtonClicked(bool b) { m_Controller->ToggleColorIndicesButtonClicked(); } @@ -126,7 +126,7 @@ void Fractorium::OnToggleColorIndicesButtonClicked(bool b) { m_Controller->Toggl template void FractoriumEmberController::RandomColorSpeedButtonClicked() { - UpdateXform([&](Xform* xform) { xform->m_ColorSpeed = m_Rand.Frand01(); }, eXformUpdate::UPDATE_ALL); + UpdateXform([&](Xform* xform, size_t xfindex, size_t selIndex) { xform->m_ColorSpeed = m_Rand.Frand01(); }, eXformUpdate::UPDATE_ALL); m_Fractorium->m_XformColorSpeedSpin->SetValueStealth(CurrentXform()->m_ColorSpeed); } void Fractorium::OnRandomColorSpeedButtonClicked(bool b) { m_Controller->RandomColorSpeedButtonClicked(); } @@ -140,7 +140,7 @@ template void FractoriumEmberController::ToggleColorSpeedButtonClicked() { char ch = 1; - UpdateXform([&](Xform* xform) { xform->m_ColorSpeed = (T(ch ^= 1) ? 0.5 : 0.0); }, eXformUpdate::UPDATE_ALL); + UpdateXform([&](Xform* xform, size_t xfindex, size_t selIndex) { xform->m_ColorSpeed = (T(ch ^= 1) ? 0.5 : 0.0); }, eXformUpdate::UPDATE_ALL); m_Fractorium->m_XformColorSpeedSpin->SetValueStealth(CurrentXform()->m_ColorSpeed); } void Fractorium::OnToggleColorSpeedButtonClicked(bool b) { m_Controller->ToggleColorSpeedButtonClicked(); } @@ -152,7 +152,7 @@ void Fractorium::OnToggleColorSpeedButtonClicked(bool b) { m_Controller->ToggleC /// /// The color speed, -1-1. template -void FractoriumEmberController::XformColorSpeedChanged(double d) { UpdateXform([&] (Xform* xform) { xform->m_ColorSpeed = d; }, eXformUpdate::UPDATE_SELECTED); } +void FractoriumEmberController::XformColorSpeedChanged(double d) { UpdateXform([&] (Xform* xform, size_t xfindex, size_t selIndex) { xform->m_ColorSpeed = d; }, eXformUpdate::UPDATE_SELECTED); } void Fractorium::OnXformColorSpeedChanged(double d) { m_Controller->XformColorSpeedChanged(d); } /// @@ -162,7 +162,7 @@ void Fractorium::OnXformColorSpeedChanged(double d) { m_Controller->XformColorSp /// /// The opacity, 0-1. template -void FractoriumEmberController::XformOpacityChanged(double d) { UpdateXform([&] (Xform* xform) { xform->m_Opacity = d; }, eXformUpdate::UPDATE_SELECTED); } +void FractoriumEmberController::XformOpacityChanged(double d) { UpdateXform([&] (Xform* xform, size_t xfindex, size_t selIndex) { xform->m_Opacity = d; }, eXformUpdate::UPDATE_SELECTED); } void Fractorium::OnXformOpacityChanged(double d) { m_Controller->XformOpacityChanged(d); } /// @@ -173,7 +173,7 @@ void Fractorium::OnXformOpacityChanged(double d) { m_Controller->XformOpacityCha /// /// The direct color percentage, 0-1. template -void FractoriumEmberController::XformDirectColorChanged(double d) { UpdateXform([&] (Xform* xform) { xform->m_DirectColor = d; }, eXformUpdate::UPDATE_SELECTED); } +void FractoriumEmberController::XformDirectColorChanged(double d) { UpdateXform([&] (Xform* xform, size_t xfindex, size_t selIndex) { xform->m_DirectColor = d; }, eXformUpdate::UPDATE_SELECTED); } void Fractorium::OnXformDirectColorChanged(double d) { m_Controller->XformDirectColorChanged(d); } /// diff --git a/Source/Fractorium/FractoriumXformsVariations.cpp b/Source/Fractorium/FractoriumXformsVariations.cpp index cfc24eb..bcc9621 100644 --- a/Source/Fractorium/FractoriumXformsVariations.cpp +++ b/Source/Fractorium/FractoriumXformsVariations.cpp @@ -202,7 +202,7 @@ void FractoriumEmberController::VariationSpinBoxValueChanged(double d)//Would if (sender) { - UpdateXform([&](Xform* xform) + UpdateXform([&](Xform* xform, size_t xfindex, size_t selIndex) { auto var = m_VariationList->GetVariation(sender->GetVariationId());//The variation attached to the sender, for reference only. auto parVar = dynamic_cast*>(var);//The parametric cast of that variation. diff --git a/Source/Fractorium/GLWidget.cpp b/Source/Fractorium/GLWidget.cpp index 968b5e3..cdba23a 100644 --- a/Source/Fractorium/GLWidget.cpp +++ b/Source/Fractorium/GLWidget.cpp @@ -175,9 +175,9 @@ void GLWidget::InitGL() if (!b) { - m_Fractorium->OnActionNewFlock(false); m_Fractorium->m_WidthSpin->setValue(w); m_Fractorium->m_HeightSpin->setValue(h); + m_Fractorium->OnActionNewFlock(false);//This must come after the previous two lines because it uses the values of the spinners. } m_Fractorium->m_Controller->DelayedStartRenderTimer(); @@ -839,7 +839,7 @@ void GLEmberController::MousePress(QMouseEvent* e) //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);//Must do this first so UpdateXform() below properly grabs the current plus any selected. m_DragSrcTransforms.clear(); - m_FractoriumEmberController->UpdateXform([&](Xform* xform) + m_FractoriumEmberController->UpdateXform([&](Xform* xform, size_t xfindex, size_t selIndex) { 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. @@ -972,7 +972,7 @@ void GLEmberController::MouseMove(QMouseEvent* e) QRectF qrf(tl, br); T scale = m_FractoriumEmberController->AffineScaleCurrentToLocked(); int i = 0; - m_FractoriumEmberController->UpdateXform([&](Xform* xform) + m_FractoriumEmberController->UpdateXform([&](Xform* xform, size_t xfindex, size_t selIndex) { QPointF cd(xform->m_Affine.C() * scale, xform->m_Affine.F() * scale); bool b = qrf.contains(cd); @@ -1862,7 +1862,6 @@ bool GLEmberController::CheckXformHover(Xform* xform, v3T& glCoords, T& be template void GLEmberController::CalcDragXAxis() { - size_t index = 0; auto affineToWorldScale = m_FractoriumEmberController->AffineScaleLockedToCurrent(); auto worldToAffineScale = m_FractoriumEmberController->AffineScaleCurrentToLocked(); bool pre = m_AffineType == eAffineType::AffinePre; @@ -1876,10 +1875,10 @@ void GLEmberController::CalcDragXAxis() auto endDiff = (v2T(snapped) * affineToWorldScale) - m_DragSrcTransform.O(); T endAngle = std::atan2(endDiff.y, endDiff.x); T angle = startAngle - endAngle; - m_FractoriumEmberController->UpdateXform([&](Xform* xform) + m_FractoriumEmberController->UpdateXform([&](Xform* xform, size_t xfindex, size_t selIndex) { auto& affine = pre ? xform->m_Affine : xform->m_Post; - auto srcRotated = m_DragSrcTransforms[index++]; + auto srcRotated = m_DragSrcTransforms[selIndex]; if (worldPivotShiftAlt) { @@ -1923,10 +1922,10 @@ void GLEmberController::CalcDragXAxis() auto endDiff = (v2T(origXPlusOff) * affineToWorldScale); T endAngle = std::atan2(endDiff.y, endDiff.x); T angle = startAngle - endAngle; - m_FractoriumEmberController->UpdateXform([&](Xform* xform) + m_FractoriumEmberController->UpdateXform([&](Xform* xform, size_t xfindex, size_t selIndex) { auto& affine = pre ? xform->m_Affine : xform->m_Post; - auto src = m_DragSrcTransforms[index++]; + auto src = m_DragSrcTransforms[selIndex]; if (GetAlt()) { @@ -1966,7 +1965,6 @@ void GLEmberController::CalcDragXAxis() template void GLEmberController::CalcDragYAxis() { - size_t index = 0; auto affineToWorldScale = m_FractoriumEmberController->AffineScaleLockedToCurrent(); auto worldToAffineScale = m_FractoriumEmberController->AffineScaleCurrentToLocked(); bool pre = m_AffineType == eAffineType::AffinePre; @@ -1980,10 +1978,10 @@ void GLEmberController::CalcDragYAxis() auto endDiff = (v2T(snapped) * affineToWorldScale) - m_DragSrcTransform.O(); T endAngle = std::atan2(endDiff.y, endDiff.x); T angle = startAngle - endAngle; - m_FractoriumEmberController->UpdateXform([&](Xform* xform) + m_FractoriumEmberController->UpdateXform([&](Xform* xform, size_t xfindex, size_t selIndex) { auto& affine = pre ? xform->m_Affine : xform->m_Post; - auto srcRotated = m_DragSrcTransforms[index++]; + auto srcRotated = m_DragSrcTransforms[selIndex]; if (worldPivotShiftAlt) { @@ -2027,10 +2025,10 @@ void GLEmberController::CalcDragYAxis() auto endDiff = (v2T(origYPlusOff) * affineToWorldScale); T endAngle = std::atan2(endDiff.y, endDiff.x); T angle = startAngle - endAngle; - m_FractoriumEmberController->UpdateXform([&](Xform* xform) + m_FractoriumEmberController->UpdateXform([&](Xform* xform, size_t xfindex, size_t selIndex) { auto& affine = pre ? xform->m_Affine : xform->m_Post; - auto src = m_DragSrcTransforms[index++]; + auto src = m_DragSrcTransforms[selIndex]; if (GetAlt()) { @@ -2065,7 +2063,6 @@ void GLEmberController::CalcDragYAxis() template void GLEmberController::CalcDragTranslation() { - size_t index = 0; auto affineToWorldScale = m_FractoriumEmberController->AffineScaleLockedToCurrent(); auto worldToAffineScale = m_FractoriumEmberController->AffineScaleCurrentToLocked(); bool worldPivotShift = !m_Fractorium->LocalPivot() && GetShift(); @@ -2077,10 +2074,10 @@ void GLEmberController::CalcDragTranslation() T startAngle = std::atan2(m_DragSrcTransform.O().y, m_DragSrcTransform.O().x); T endAngle = std::atan2(snapped.y, snapped.x); T angle = startAngle - endAngle; - m_FractoriumEmberController->UpdateXform([&](Xform* xform) + m_FractoriumEmberController->UpdateXform([&](Xform* xform, size_t xfindex, size_t selIndex) { auto& affine = pre ? xform->m_Affine : xform->m_Post; - auto srcRotated = m_DragSrcTransforms[index++]; + auto srcRotated = m_DragSrcTransforms[selIndex]; srcRotated.RotateTrans(angle); if (worldPivotShift) @@ -2102,10 +2099,10 @@ void GLEmberController::CalcDragTranslation() if (GetControl()) { - m_FractoriumEmberController->UpdateXform([&](Xform* xform) + m_FractoriumEmberController->UpdateXform([&](Xform* xform, size_t xfindex, size_t selIndex) { auto& affine = pre ? xform->m_Affine : xform->m_Post; - auto offset = m_DragSrcTransforms[index++].O() + (affineToWorldScale * v2T(diff)); + auto offset = m_DragSrcTransforms[selIndex].O() + (affineToWorldScale * v2T(diff)); auto snapped = SnapToGrid(offset); affine.O(v2T(snapped.x, snapped.y)); @@ -2115,10 +2112,10 @@ void GLEmberController::CalcDragTranslation() } else { - m_FractoriumEmberController->UpdateXform([&](Xform* xform) + m_FractoriumEmberController->UpdateXform([&](Xform* xform, size_t xfindex, size_t selIndex) { auto& affine = pre ? xform->m_Affine : xform->m_Post; - affine.O(m_DragSrcTransforms[index++].O() + (affineToWorldScale * v2T(diff))); + affine.O(m_DragSrcTransforms[selIndex].O() + (affineToWorldScale * v2T(diff))); if (xform == m_FractoriumEmberController->CurrentXform()) m_DragHandlePos = v3T(affine.O(), 0) * worldToAffineScale;