From dea12ee96a5f1b4b843edf56e59f3531e76a6d67 Mon Sep 17 00:00:00 2001 From: Person Date: Mon, 27 Mar 2017 18:05:06 -0700 Subject: [PATCH] --Code changes -Bump to version 1.0.0.3 -Remove all code for opacity adjustment, it's no longer needed. -Small optimization on accumulating to the histogram on the CPU. --- .../Installer/FractoriumInstaller.wixproj | 2 +- Builds/MSVC/Installer/Product.wxs | 4 +- Builds/MSVC/VS2015/Ember.rc | Bin 4502 -> 4502 bytes Builds/MSVC/VS2015/EmberAnimate.rc | 10 ++-- Builds/MSVC/VS2015/EmberCL.rc | Bin 4528 -> 4528 bytes Builds/MSVC/VS2015/EmberGenome.rc | 10 ++-- Builds/MSVC/VS2015/EmberRender.rc | 10 ++-- Builds/MSVC/VS2015/Fractorium.rc | Bin 4470 -> 4470 bytes Source/Ember/EmberDefines.h | 2 +- Source/Ember/Iterator.h | 8 +-- Source/Ember/Point.h | 4 +- Source/Ember/Renderer.cpp | 52 +++++++++--------- Source/Ember/Xform.h | 24 +------- Source/EmberCL/EmberCLStructs.h | 2 - Source/EmberCL/IterOpenCLKernelCreator.cpp | 10 ++-- Source/EmberCL/RendererCL.cpp | 1 - Source/EmberTester/EmberTester.cpp | 18 +++--- Source/Fractorium/AboutDialog.ui | 2 +- Source/Fractorium/FractoriumParams.cpp | 18 ++++-- 19 files changed, 81 insertions(+), 96 deletions(-) diff --git a/Builds/MSVC/Installer/FractoriumInstaller.wixproj b/Builds/MSVC/Installer/FractoriumInstaller.wixproj index c581c50..1179170 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.2 + Fractorium_1.0.0.3 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 66f518e..5bd8471 100644 --- a/Builds/MSVC/Installer/Product.wxs +++ b/Builds/MSVC/Installer/Product.wxs @@ -1,6 +1,6 @@ - + @@ -13,7 +13,7 @@ - + yg_-xCk{rV$)7pQS&bNY8Mr2I::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/Iterator.h b/Source/Ember/Iterator.h index 4a065e3..4d1dda8 100644 --- a/Source/Ember/Iterator.h +++ b/Source/Ember/Iterator.h @@ -213,7 +213,7 @@ protected: firstBadPoint.m_Y = rand.Frand11(); firstBadPoint.m_Z = 0; firstBadPoint.m_ColorX = point->m_ColorX; - firstBadPoint.m_VizAdjusted = point->m_VizAdjusted; + firstBadPoint.m_Opacity = point->m_Opacity; xformIndex = NextXformFromIndex(rand.Rand()); if (!xforms[xformIndex].Apply(&firstBadPoint, point, rand)) @@ -245,9 +245,9 @@ protected: { if (IsClose(ember.FinalXform()->m_Opacity, 1) || rand.Frand01() < ember.FinalXform()->m_Opacity) { - T tempVizAdjusted = tempPoint.m_VizAdjusted; + T tempOpacity = tempPoint.m_Opacity; ember.NonConstFinalXform()->Apply(&tempPoint, sample, rand); - sample->m_VizAdjusted = tempVizAdjusted; + sample->m_Opacity = tempOpacity; } else { @@ -433,7 +433,7 @@ public: firstBadPoint.m_Y = rand.Frand11(); firstBadPoint.m_Z = 0; firstBadPoint.m_ColorX = point->m_ColorX; - firstBadPoint.m_VizAdjusted = point->m_VizAdjusted; + firstBadPoint.m_Opacity = point->m_Opacity; xformIndex = NextXformFromIndex(rand.Rand(), lastXformUsed); if (!xforms[xformIndex].Apply(&firstBadPoint, point, rand)) diff --git a/Source/Ember/Point.h b/Source/Ember/Point.h index a71bee2..15f11cb 100644 --- a/Source/Ember/Point.h +++ b/Source/Ember/Point.h @@ -72,7 +72,7 @@ public: m_Z = point.m_Z; m_ColorX = point.m_ColorX; //m_ColorY = point.m_ColorY; - m_VizAdjusted = point.m_VizAdjusted; + m_Opacity = point.m_Opacity; return *this; } @@ -82,7 +82,7 @@ public: T m_Z = 0; T m_ColorX = 0; //T m_ColorY; - T m_VizAdjusted = 1; + T m_Opacity = 1; }; /// diff --git a/Source/Ember/Renderer.cpp b/Source/Ember/Renderer.cpp index 9982d74..ebbaf5f 100644 --- a/Source/Ember/Renderer.cpp +++ b/Source/Ember/Renderer.cpp @@ -1516,21 +1516,21 @@ void Renderer::Accumulate(QTIsaac& rand, Poin { Point p(samples[i]);//Slightly faster to cache this. - if (Rotate() != 0) + if (p.m_Opacity != 0) { - T p00 = p.m_X - CenterX(); - T p11 = p.m_Y - m_Ember.m_RotCenterY; - p.m_X = (p00 * m_RotMat.A()) + (p11 * m_RotMat.B()) + CenterX(); - p.m_Y = (p00 * m_RotMat.D()) + (p11 * m_RotMat.E()) + m_Ember.m_RotCenterY; - } + if (Rotate() != 0) + { + T p00 = p.m_X - CenterX(); + T p11 = p.m_Y - m_Ember.m_RotCenterY; + p.m_X = (p00 * m_RotMat.A()) + (p11 * m_RotMat.B()) + CenterX(); + p.m_Y = (p00 * m_RotMat.D()) + (p11 * m_RotMat.E()) + m_Ember.m_RotCenterY; + } - //Checking this first before converting gives better performance than converting and checking a single value, which the original did. - //Second, an interesting optimization observation is that when keeping the bounds vars within m_CarToRas and calling its InBounds() member function, - //rather than here as members, about a 7% speedup is achieved. This is possibly due to the fact that data from m_CarToRas is accessed - //right after the call to Convert(), so some caching efficiencies get realized. - if (m_CarToRas.InBounds(p)) - { - if (p.m_VizAdjusted != 0) + //Checking this first before converting gives better performance than converting and checking a single value, which the original did. + //Second, an interesting optimization observation is that when keeping the bounds vars within m_CarToRas and calling its InBounds() member function, + //rather than here as members, about a 7% speedup is achieved. This is possibly due to the fact that data from m_CarToRas is accessed + //right after the call to Convert(), so some caching efficiencies get realized. + if (m_CarToRas.InBounds(p)) { m_CarToRas.Convert(p, histIndex); @@ -1563,7 +1563,7 @@ void Renderer::Accumulate(QTIsaac& rand, Poin auto cifm1 = bucketT(1) - colorIndexFrac; //Loops are unrolled to allow auto vectorization. - if (p.m_VizAdjusted == 1) + if (p.m_Opacity == 1) { hist[0] += (pal[0] * cifm1) + (pal2[0] * colorIndexFrac); hist[1] += (pal[1] * cifm1) + (pal2[1] * colorIndexFrac); @@ -1572,7 +1572,7 @@ void Renderer::Accumulate(QTIsaac& rand, Poin } else { - auto va = bucketT(p.m_VizAdjusted); + auto va = bucketT(p.m_Opacity); hist[0] += ((pal[0] * cifm1) + (pal2[0] * colorIndexFrac)) * va; hist[1] += ((pal[1] * cifm1) + (pal2[1] * colorIndexFrac)) * va; hist[2] += ((pal[2] * cifm1) + (pal2[2] * colorIndexFrac)) * va; @@ -1589,17 +1589,17 @@ void Renderer::Accumulate(QTIsaac& rand, Poin { Point p(samples[i]);//Slightly faster to cache this. - if (Rotate() != 0) + if (p.m_Opacity != 0) { - T p00 = p.m_X - CenterX(); - T p11 = p.m_Y - m_Ember.m_RotCenterY; - p.m_X = (p00 * m_RotMat.A()) + (p11 * m_RotMat.B()) + CenterX(); - p.m_Y = (p00 * m_RotMat.D()) + (p11 * m_RotMat.E()) + m_Ember.m_RotCenterY; - } + if (Rotate() != 0) + { + T p00 = p.m_X - CenterX(); + T p11 = p.m_Y - m_Ember.m_RotCenterY; + p.m_X = (p00 * m_RotMat.A()) + (p11 * m_RotMat.B()) + CenterX(); + p.m_Y = (p00 * m_RotMat.D()) + (p11 * m_RotMat.E()) + m_Ember.m_RotCenterY; + } - if (m_CarToRas.InBounds(p)) - { - if (p.m_VizAdjusted != 0) + if (m_CarToRas.InBounds(p)) { m_CarToRas.Convert(p, histIndex); @@ -1609,7 +1609,7 @@ void Renderer::Accumulate(QTIsaac& rand, Poin bucketT* __restrict hist = glm::value_ptr(m_HistBuckets[histIndex]);//Vectorizer can't tell these point to different locations. const bucketT* __restrict pal = glm::value_ptr(palette->m_Entries[intColorIndex]); - if (p.m_VizAdjusted == 1) + if (p.m_Opacity == 1) { hist[0] += pal[0]; hist[1] += pal[1]; @@ -1618,7 +1618,7 @@ void Renderer::Accumulate(QTIsaac& rand, Poin } else { - auto va = bucketT(p.m_VizAdjusted); + auto va = bucketT(p.m_Opacity); hist[0] += pal[0] * va; hist[1] += pal[1] * va; hist[2] += pal[2] * va; diff --git a/Source/Ember/Xform.h b/Source/Ember/Xform.h index fecbbca..de800e6 100644 --- a/Source/Ember/Xform.h +++ b/Source/Ember/Xform.h @@ -474,7 +474,7 @@ public: m_ParentEmber = nullptr; m_ColorSpeedCache = 0; m_OneMinusColorCache = 0; - m_VizAdjusted = 0; + m_Opacity = 0; m_Animate = 0; m_Wind[0] = 0; m_Wind[1] = 0; @@ -482,7 +482,7 @@ public: } /// - /// Compute color cache values: color speed, one minus color speed and adjusted visibility. + /// Compute color cache values: color speed and one minus color speed. /// void CacheColorVals() { @@ -491,7 +491,6 @@ public: //m_OneMinusColorCache = (1 + m_ColorSpeed) / 2; m_ColorSpeedCache = m_ColorSpeed * m_ColorX;//Flam3 style. m_OneMinusColorCache = T(1.0) - m_ColorSpeed; - m_VizAdjusted = AdjustOpacityPercentage(m_Opacity); } /// @@ -598,7 +597,7 @@ public: //to the histogram. Calculate this value by interpolating between the index value of the //last iteration with the one specified in this xform. Note that some cached values are used //to reduce the amount of processing. - outPoint->m_VizAdjusted = m_VizAdjusted; + outPoint->m_Opacity = m_Opacity; iterHelper.m_Color.x = outPoint->m_ColorX = m_ColorSpeedCache + (m_OneMinusColorCache * inPoint->m_ColorX); if (m_HasPreOrRegularVars) @@ -788,7 +787,6 @@ public: size_t PostVariationCount() const { return m_PostVariations.size(); } size_t TotalVariationCount() const { return PreVariationCount() + VariationCount() + PostVariationCount(); } bool Empty() const { return TotalVariationCount() == 0 && m_Affine.IsID(); }//Use this instead of padding like the original did. - T VizAdjusted() const { return m_VizAdjusted; } T ColorSpeedCache() const { return m_ColorSpeedCache; } T OneMinusColorCache() const { return m_OneMinusColorCache; } const vector& XaosVec() const { return m_Xaos; } @@ -1133,7 +1131,6 @@ public: ss << "\nColor Speed: " << m_ColorSpeed; ss << "\nAnimate: " << m_Animate; ss << "\nOpacity: " << m_Opacity; - ss << "\nViz Adjusted: " << m_VizAdjusted; ss << "\nWind: " << m_Wind[0] << ", " << m_Wind[1]; ss << "\nMotion Frequency: " << m_MotionFreq; ss << "\nMotion Func: " << m_MotionFunc; @@ -1165,7 +1162,6 @@ public: private: bool m_HasPreOrRegularVars;//Whethere there are any pre or regular variations present. - T m_VizAdjusted;//Adjusted visibility for better transitions. public: //Color coordinates for this function. This is the index into the palette used to look up a color and add to the histogram for each iter. @@ -1237,20 +1233,6 @@ private: func(m_PostVariations, keepGoing); } - /// - /// Adjust opacity. - /// - /// The opacity to adjust, range 0-1. - /// The adjusted opacity - static T AdjustOpacityPercentage(T in) - { - return in; - /* if (in == 0) - return 0; - else - return std::pow(T(10.0), -std::log(T(1.0) / T(in)) / std::log(T(2)));*/ - } - vector m_Xaos;//Xaos vector which affects the probability that this xform is chosen. Usually empty. Ember* m_ParentEmber;//The parent ember that contains this xform. bool m_NeedPrecalcSumSquares;//Whether any variation uses the precalc sum squares value in its calculations. diff --git a/Source/EmberCL/EmberCLStructs.h b/Source/EmberCL/EmberCLStructs.h index 127d4eb..badf518 100644 --- a/Source/EmberCL/EmberCLStructs.h +++ b/Source/EmberCL/EmberCLStructs.h @@ -168,7 +168,6 @@ struct ALIGN XformCL T m_ColorSpeedCache;//88 (176) T m_OneMinusColorCache;//92 (184) T m_Opacity;//96 (192) - T m_VizAdjusted;//100 (200) }; /// @@ -184,7 +183,6 @@ static const char* XformCLStructString = " real_t m_ColorSpeedCache;\n" " real_t m_OneMinusColorCache;\n" " real_t m_Opacity;\n" - " real_t m_VizAdjusted;\n" "} XformCL;\n" "\n"; diff --git a/Source/EmberCL/IterOpenCLKernelCreator.cpp b/Source/EmberCL/IterOpenCLKernelCreator.cpp index 461c31c..c8bed7b 100644 --- a/Source/EmberCL/IterOpenCLKernelCreator.cpp +++ b/Source/EmberCL/IterOpenCLKernelCreator.cpp @@ -490,15 +490,15 @@ string IterOpenCLKernelCreator::CreateIterKernelString(const Ember& ember, if (lockAccum) { os << - " AtomicAdd(&(histogram[histIndex].m_Reals[0]), palColor1.x * (real_bucket_t)xforms[secondPoint.m_LastXfUsed].m_VizAdjusted);\n"//Always apply opacity, even though it's usually 1. - " AtomicAdd(&(histogram[histIndex].m_Reals[1]), palColor1.y * (real_bucket_t)xforms[secondPoint.m_LastXfUsed].m_VizAdjusted);\n" - " AtomicAdd(&(histogram[histIndex].m_Reals[2]), palColor1.z * (real_bucket_t)xforms[secondPoint.m_LastXfUsed].m_VizAdjusted);\n" - " AtomicAdd(&(histogram[histIndex].m_Reals[3]), palColor1.w * (real_bucket_t)xforms[secondPoint.m_LastXfUsed].m_VizAdjusted);\n"; + " AtomicAdd(&(histogram[histIndex].m_Reals[0]), palColor1.x * (real_bucket_t)xforms[secondPoint.m_LastXfUsed].m_Opacity);\n"//Always apply opacity, even though it's usually 1. + " AtomicAdd(&(histogram[histIndex].m_Reals[1]), palColor1.y * (real_bucket_t)xforms[secondPoint.m_LastXfUsed].m_Opacity);\n" + " AtomicAdd(&(histogram[histIndex].m_Reals[2]), palColor1.z * (real_bucket_t)xforms[secondPoint.m_LastXfUsed].m_Opacity);\n" + " AtomicAdd(&(histogram[histIndex].m_Reals[3]), palColor1.w * (real_bucket_t)xforms[secondPoint.m_LastXfUsed].m_Opacity);\n"; } else { os << - " histogram[histIndex].m_Real4 += (palColor1 * (real_bucket_t)xforms[secondPoint.m_LastXfUsed].m_VizAdjusted);\n";//real_bucket_t should always be float. + " histogram[histIndex].m_Real4 += (palColor1 * (real_bucket_t)xforms[secondPoint.m_LastXfUsed].m_Opacity);\n";//real_bucket_t should always be float. } os << diff --git a/Source/EmberCL/RendererCL.cpp b/Source/EmberCL/RendererCL.cpp index 3f6e87c..d6e31af 100644 --- a/Source/EmberCL/RendererCL.cpp +++ b/Source/EmberCL/RendererCL.cpp @@ -1781,7 +1781,6 @@ void RendererCL::ConvertEmber(Ember& ember, EmberCL& emberCL, xformsCL[i].m_ColorSpeedCache = xform->ColorSpeedCache(); xformsCL[i].m_OneMinusColorCache = xform->OneMinusColorCache(); xformsCL[i].m_Opacity = xform->m_Opacity; - xformsCL[i].m_VizAdjusted = xform->VizAdjusted(); for (size_t varIndex = 0; varIndex < xform->TotalVariationCount() && varIndex < MAX_CL_VARS; varIndex++)//Assign all variation weights for this xform, with a max of MAX_CL_VARS. xformsCL[i].m_VariationWeights[varIndex] = xform->GetVariation(varIndex)->m_Weight; diff --git a/Source/EmberTester/EmberTester.cpp b/Source/EmberTester/EmberTester.cpp index 763031a..136152f 100644 --- a/Source/EmberTester/EmberTester.cpp +++ b/Source/EmberTester/EmberTester.cpp @@ -1140,7 +1140,7 @@ void TestXformsInOutPoints() orig.m_Y = rand.Frand11(); orig.m_Z = rand.Frand11(); orig.m_ColorX = rand.Frand01(); - orig.m_VizAdjusted = rand.Frand01(); + orig.m_Opacity = rand.Frand01(); Point p1 = orig, p2 = orig, p3; xforms[i].Apply(&p1, &p1, rand); xforms[i].Apply(&p2, &p3, rand); @@ -1148,12 +1148,12 @@ void TestXformsInOutPoints() badVals |= (p1.m_Y != p1.m_Y); badVals |= (p1.m_Z != p1.m_Z); badVals |= (p1.m_ColorX != p1.m_ColorX); - badVals |= (p1.m_VizAdjusted != p1.m_VizAdjusted); + badVals |= (p1.m_Opacity != p1.m_Opacity); badVals |= (p3.m_X != p3.m_X); badVals |= (p3.m_Y != p3.m_Y); badVals |= (p3.m_Z != p3.m_Z); badVals |= (p3.m_ColorX != p3.m_ColorX); - badVals |= (p3.m_VizAdjusted != p3.m_VizAdjusted); + badVals |= (p3.m_Opacity != p3.m_Opacity); if (badVals) cout << "Variation " << regVar->Name() << ": Bad value detected" << endl; @@ -1172,8 +1172,8 @@ void TestXformsInOutPoints() if (p1.m_ColorX != p3.m_ColorX) cout << "Variation " << regVar->Name() << ": p1.m_ColorX " << p1.m_ColorX << " != p3.m_ColorX " << p3.m_ColorX << endl; - if (p1.m_VizAdjusted != p3.m_VizAdjusted) - cout << "Variation " << regVar->Name() << ": p1.m_VizAdjusted " << p1.m_VizAdjusted << " != p3.m_VizAdjusted " << p3.m_VizAdjusted << endl; + if (p1.m_Opacity != p3.m_Opacity) + cout << "Variation " << regVar->Name() << ": p1.m_Opacity " << p1.m_Opacity << " != p3.m_Opacity " << p3.m_Opacity << endl; } } } @@ -1221,7 +1221,7 @@ void TestVarTime() helper.In.y = helper.m_TransY = (xform.m_Affine.D() * p.m_X) + (xform.m_Affine.E() * p.m_Y) + xform.m_Affine.F(); helper.In.z = helper.m_TransZ = p.m_Z; helper.m_Color.x = p.m_ColorX = rand.Frand01(); - p.m_VizAdjusted = rand.Frand01(); + p.m_Opacity = rand.Frand01(); helper.m_PrecalcSumSquares = SQR(helper.m_TransX) + SQR(helper.m_TransY); helper.m_PrecalcSqrtSumSquares = sqrt(helper.m_PrecalcSumSquares); helper.m_PrecalcSina = helper.m_TransX / helper.m_PrecalcSqrtSumSquares; @@ -1346,7 +1346,7 @@ void TestVarsSimilar() helper.In.y = helper.m_TransY = (xform.m_Affine.D() * p.m_X) + (xform.m_Affine.E() * p.m_Y) + xform.m_Affine.F(); helper.In.z = helper.m_TransZ = p.m_Z; helper.m_Color.x = p.m_ColorX = rand.Frand01(); - p.m_VizAdjusted = rand.Frand01(); + p.m_Opacity = rand.Frand01(); pComp = p; helper.m_PrecalcSumSquares = SQR(helper.m_TransX) + SQR(helper.m_TransY); helper.m_PrecalcSqrtSumSquares = sqrt(helper.m_PrecalcSumSquares); @@ -1501,7 +1501,7 @@ void TestCpuGpuResults(size_t platform, size_t device) p.m_Y = rand.Frand(-5, 5); p.m_Z = rand.Frand(-5, 5); p.m_ColorX = rand.Frand01(); - p.m_VizAdjusted = rand.Frand01(); + p.m_Opacity = rand.Frand01(); varCopy->Random(rand); xform.AddVariation(varCopy); ember.AddXform(xform); @@ -1579,7 +1579,7 @@ void TestGpuVectorRead(size_t platform, size_t device) p.m_Y = rand.Frand(-5, 5); p.m_Z = rand.Frand(-5, 5); p.m_ColorX = rand.Frand01(); - p.m_VizAdjusted = rand.Frand01(); + p.m_Opacity = rand.Frand01(); varCopy->Random(rand); xform.AddVariation(varCopy); ember.AddXform(xform); diff --git a/Source/Fractorium/AboutDialog.ui b/Source/Fractorium/AboutDialog.ui index ed92b23..4f1e78e 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.2</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.3</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/FractoriumParams.cpp b/Source/Fractorium/FractoriumParams.cpp index 792e670..66757b9 100644 --- a/Source/Fractorium/FractoriumParams.cpp +++ b/Source/Fractorium/FractoriumParams.cpp @@ -471,10 +471,13 @@ void Fractorium::OnTemporalFilterTypeComboCurrentIndexChanged(const QString& tex template void FractoriumEmberController::DEFilterMinRadiusWidthChanged(double d) { - UpdateAll([&](Ember& ember) + if (m_Ember.m_MinRadDE != d) { - ember.m_MinRadDE = d; - }, true, eProcessAction::FILTER_AND_ACCUM, m_Fractorium->ApplyAll()); + UpdateAll([&](Ember& ember) + { + ember.m_MinRadDE = d; + }, true, eProcessAction::FILTER_AND_ACCUM, m_Fractorium->ApplyAll()); + } } void Fractorium::OnDEFilterMinRadiusWidthChanged(double d) @@ -491,10 +494,13 @@ void Fractorium::OnDEFilterMinRadiusWidthChanged(double d) template void FractoriumEmberController::DEFilterMaxRadiusWidthChanged(double d) { - UpdateAll([&](Ember& ember) + if (m_Ember.m_MaxRadDE != d) { - ember.m_MaxRadDE = d; - }, true, eProcessAction::FILTER_AND_ACCUM, m_Fractorium->ApplyAll()); + UpdateAll([&](Ember& ember) + { + ember.m_MaxRadDE = d; + }, true, eProcessAction::FILTER_AND_ACCUM, m_Fractorium->ApplyAll()); + } } void Fractorium::OnDEFilterMaxRadiusWidthChanged(double d)