File indexing completed on 2024-12-22 04:16:23

0001 /*
0002  *  SPDX-FileCopyrightText: 2022 Deif Lou <ginoba@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include <cmath>
0008 
0009 #include <kis_assert.h>
0010 #include <KisMpl.h>
0011 
0012 #include "KisSprayRandomDistributions.h"
0013 
0014 class KisSprayFunctionBasedDistribution::Private
0015 {
0016 public:
0017     struct SampleInfo
0018     {
0019         double x;
0020         double cdfAtX;
0021         double oneOverCdfDy;
0022     };
0023 
0024     std::vector<SampleInfo> samples;
0025 
0026     template <typename Function>
0027     void initialize(size_t numberOfSamples, double a, double b, Function f)
0028     {
0029         KIS_SAFE_ASSERT_RECOVER_RETURN(numberOfSamples > 0);
0030         KIS_SAFE_ASSERT_RECOVER_RETURN(b > a);
0031 
0032         samples.clear();
0033 
0034         if (numberOfSamples < 3) {
0035             samples.push_back({a, 0.0, 0.0});
0036             samples.push_back({b, 1.0, 1.0});
0037             return;
0038         }
0039 
0040         // Create the CDF
0041         const double domainSize = b - a;
0042         const double intervalSize = domainSize / static_cast<double>(numberOfSamples - 1);
0043         double sum = 0.0;
0044         double lastX, lastY, lastSum;
0045         size_t effectiveNumberOfSamples = numberOfSamples;
0046 
0047         // Adjust the limits of the pdf if it has a 0 probability segment at
0048         // the start or at the end
0049         {
0050             for (size_t i = 0; i < numberOfSamples; ++i) {
0051                 const double x = a + intervalSize * static_cast<double>(i);
0052                 const double y = f(x);
0053                 if (y > 0.0) {
0054                     if (i > 0) {
0055                         a += intervalSize * static_cast<double>(i - 1);
0056                         effectiveNumberOfSamples -= i - 1;
0057                     }
0058                     break;
0059                 }
0060                 if (i == numberOfSamples - 1) {
0061                     // The whole pdf must have 0 probability
0062                     return;
0063                 }
0064             }
0065 
0066             for (size_t i = 0; i < numberOfSamples; ++i) {
0067                 const double x = b - intervalSize * static_cast<double>(i);
0068                 const double y = f(x);
0069                 if (y > 0.0) {
0070                     if (i > 0) {
0071                         b -= intervalSize * static_cast<double>(i - 1);
0072                         effectiveNumberOfSamples -= i - 1;
0073                     }
0074                     break;
0075                 }
0076             }
0077         }
0078         // Insert first point
0079         {
0080             samples.push_back({a, 0.0, 0.0});
0081             lastX = a;
0082             lastY = f(a);
0083             lastSum = 0.0;
0084         }
0085         // Insert the rest of the points
0086         {
0087             // This angle serves as a reference to see if the cdf curve is
0088             // roughly straight, and remove some points
0089             constexpr double maximumAngleDeviationToSkip{M_PI / 1000.0};
0090             double referenceAngle = 0.0;
0091             bool mustCheckAngle = false;
0092             int skippedPoints = 0;
0093 
0094             for (size_t i = 1; i < effectiveNumberOfSamples; ++i) {
0095                 const double x = a + intervalSize * static_cast<double>(i);
0096                 const double y = f(x);
0097                 // Accumulate the area under the curve between the two points
0098                 sum += (x - lastX) * (y + lastY) / 2.0;
0099                 //
0100                 if (y == 0.0) {
0101                     if (lastY == 0.0) {
0102                         lastX = x;
0103                         lastY = y;
0104                         lastSum = sum;
0105                         ++skippedPoints;
0106                         continue;
0107                     } else {
0108                         mustCheckAngle = false;
0109                     }
0110                 } else {
0111                     if (lastY == 0.0) {
0112                         if (skippedPoints > 0) {
0113                             samples.push_back({lastX, lastSum, 0.0});
0114                         }
0115                         mustCheckAngle = false;
0116                     }
0117                 }
0118 
0119                 // Compute if the current point is nearly colinear to the last two points
0120                 if (i > 1 && mustCheckAngle) {
0121                     const int nPoints = samples.size();
0122                     const double angle = std::atan2(sum - samples[nPoints - 2].cdfAtX, x - samples[nPoints - 2].x);
0123                     if (std::abs(angle - referenceAngle) <= maximumAngleDeviationToSkip) {
0124                         samples.back().x = x;
0125                         samples.back().cdfAtX = sum;
0126                         continue;
0127                     }
0128                 }
0129 
0130                 samples.push_back({x, sum, 0.0});
0131                 referenceAngle = std::atan2(sum - lastSum, x - lastX);
0132                 mustCheckAngle = true;
0133                 skippedPoints = 0;
0134 
0135                 lastX = x;
0136                 lastY = y;
0137                 lastSum = sum;
0138             }
0139         }
0140         // Scale the cdf to the [0..1] range and compute deltas
0141         {
0142             for (size_t i = 1; i < samples.size() - 1; ++i) {
0143                 samples[i].cdfAtX /= sum;
0144                 samples[i].oneOverCdfDy = 1.0 / (samples[i].cdfAtX - samples[i - 1].cdfAtX);
0145             }
0146             samples.back().cdfAtX = 1.0;
0147             samples.back().oneOverCdfDy = 1.0 / (1.0 - samples[samples.size() - 2].cdfAtX);
0148         }
0149     }
0150 
0151     double generate(double randomValue) const
0152     {
0153         // Find the first sample that has cdf greater than the passed value
0154         auto sampleIterator =
0155             std::upper_bound(samples.begin(), samples.end(), SampleInfo{0.0, randomValue, 0.0},
0156                              kismpl::mem_less(&SampleInfo::cdfAtX));
0157         const double t = (randomValue - (sampleIterator - 1)->cdfAtX) * sampleIterator->oneOverCdfDy;
0158         return (sampleIterator - 1)->x + t * (sampleIterator->x - (sampleIterator - 1)->x);
0159     }
0160 };
0161 
0162 KisSprayFunctionBasedDistribution::KisSprayFunctionBasedDistribution()
0163     : m_d(new Private)
0164 {}
0165 
0166 template <typename Function>
0167 KisSprayFunctionBasedDistribution::KisSprayFunctionBasedDistribution(int numberOfSamples, double a, double b, Function f)
0168     : m_d(new Private)
0169 {
0170     m_d->initialize(numberOfSamples, a, b, f);
0171 }
0172 
0173 KisSprayFunctionBasedDistribution::~KisSprayFunctionBasedDistribution()
0174 {}
0175 
0176 KisSprayFunctionBasedDistribution::KisSprayFunctionBasedDistribution(const KisSprayFunctionBasedDistribution &other)
0177     : m_d(new Private)
0178 {
0179     m_d->samples = other.m_d->samples;
0180 }
0181 
0182 KisSprayFunctionBasedDistribution& KisSprayFunctionBasedDistribution::KisSprayFunctionBasedDistribution::operator=(const KisSprayFunctionBasedDistribution &rhs)
0183 {
0184     if (this != &rhs) {
0185         m_d->samples = rhs.m_d->samples;
0186     }
0187     return *this;
0188 }
0189 
0190 double KisSprayFunctionBasedDistribution::operator()(KisRandomSourceSP rs) const
0191 {
0192     return m_d->generate(rs->generateNormalized());
0193 }
0194 
0195 double KisSprayFunctionBasedDistribution::min() const
0196 {
0197     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(isValid(), NAN);
0198 
0199     return m_d->samples.front().x;
0200 }
0201 
0202 double KisSprayFunctionBasedDistribution::max() const
0203 {
0204     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(isValid(), NAN);
0205 
0206     return m_d->samples.back().x;
0207 }
0208 
0209 bool KisSprayFunctionBasedDistribution::isValid() const
0210 {
0211     return m_d->samples.size() > 1;
0212 }
0213 
0214 template <typename Function>
0215 void KisSprayFunctionBasedDistribution::initialize(size_t numberOfSamples, double a, double b, Function f)
0216 {
0217     m_d->initialize(numberOfSamples, a, b, f);
0218 }
0219 
0220 double KisSprayUniformDistribution::operator()(KisRandomSourceSP rs) const
0221 {
0222     return rs->generateNormalized();
0223 }
0224 
0225 double KisSprayUniformDistributionPolarDistance::operator()(KisRandomSourceSP rs) const
0226 {
0227     return std::sqrt(rs->generateNormalized());
0228 }
0229 
0230 KisSprayNormalDistribution::KisSprayNormalDistribution()
0231 {}
0232 
0233 KisSprayNormalDistribution::KisSprayNormalDistribution(double mean, double standardDeviation)
0234 {
0235     KIS_SAFE_ASSERT_RECOVER_RETURN(standardDeviation > 0.0);
0236 
0237     const double m_c1 = 1.0 / (standardDeviation * std::sqrt(2.0 * M_PI));
0238     const double m_c2 = 2.0 * standardDeviation * standardDeviation;
0239     KisSprayFunctionBasedDistribution::initialize(1000, 0.0, standardDeviation * 5.0,
0240         [mean, m_c1, m_c2](double x)
0241         {
0242             const double shiftedX = x - mean;
0243             return m_c1 * std::exp(-(shiftedX * shiftedX / m_c2));
0244         }
0245     );
0246 }
0247 
0248 KisSprayNormalDistributionPolarDistance::KisSprayNormalDistributionPolarDistance()
0249 {}
0250 
0251 KisSprayNormalDistributionPolarDistance::KisSprayNormalDistributionPolarDistance(double mean, double standardDeviation)
0252 {
0253     KIS_SAFE_ASSERT_RECOVER_RETURN(standardDeviation > 0.0);
0254 
0255     const double m_c1 = 1.0 / (standardDeviation * std::sqrt(2.0 * M_PI));
0256     const double m_c2 = 2.0 * standardDeviation * standardDeviation;
0257     KisSprayFunctionBasedDistribution::initialize(1000, 0.0, standardDeviation * 5.0,
0258         [mean, m_c1, m_c2](double x)
0259         {
0260             const double shiftedX = x - mean;
0261             return 2.0 * x * m_c1 * std::exp(-(shiftedX * shiftedX / m_c2));
0262         }
0263     );
0264 }
0265 
0266 KisSprayClusterBasedDistribution::KisSprayClusterBasedDistribution()
0267 {}
0268 
0269 KisSprayClusterBasedDistribution::KisSprayClusterBasedDistribution(double clusteringAmount)
0270 {
0271     KIS_SAFE_ASSERT_RECOVER_RETURN(clusteringAmount >= -100.0 && clusteringAmount <= 100.0);
0272 
0273     if (clusteringAmount == 0.0) {
0274         // Uniform distribution basically
0275         KisSprayFunctionBasedDistribution::initialize(2, 0.0, 1.0,
0276             [](double)
0277             {
0278                 return 1.0;
0279             }
0280         );
0281         return;
0282     }
0283     const double sigma = 1.0 / std::abs(clusteringAmount);
0284     const double m_c1 = 2.0 * sigma * sigma;
0285     if (clusteringAmount < 0.0) {
0286         KisSprayFunctionBasedDistribution::initialize(1000, 1.0 - std::min(1.0, sigma * 4.0), 1.0,
0287             [m_c1](double x)
0288             {
0289                 const double xx = 1.0 - x;
0290                 return std::exp(-(xx * xx / m_c1));
0291             }
0292         );
0293     } else {
0294         KisSprayFunctionBasedDistribution::initialize(1000, 0.0, std::min(1.0, sigma * 4.0),
0295             [m_c1](double x)
0296             {
0297                 return std::exp(-(x * x / m_c1));
0298             }
0299         );
0300     }
0301 }
0302 
0303 KisSprayClusterBasedDistributionPolarDistance::KisSprayClusterBasedDistributionPolarDistance()
0304 {}
0305 
0306 KisSprayClusterBasedDistributionPolarDistance::KisSprayClusterBasedDistributionPolarDistance(double clusteringAmount)
0307 {
0308     KIS_SAFE_ASSERT_RECOVER_RETURN(clusteringAmount >= -100.0 && clusteringAmount <= 100.0);
0309 
0310     if (clusteringAmount == 0.0) {
0311         // Uniform distribution basically
0312         KisSprayFunctionBasedDistribution::initialize(1000, 0.0, 1.0,
0313             [](double x)
0314             {
0315                 return 2.0 * x;
0316             }
0317         );
0318         return;
0319     }
0320     const double sigma = 1.0 / std::abs(clusteringAmount);
0321     const double m_c1 = 2.0 * sigma * sigma;
0322     if (clusteringAmount < 0.0) {
0323         KisSprayFunctionBasedDistribution::initialize(1000, 1.0 - std::min(1.0, sigma * 4.0), 1.0,
0324             [m_c1](double x)
0325             {
0326                 const double xx = 1.0 - x;
0327                 return 2.0 * x * std::exp(-(xx * xx / m_c1));
0328             }
0329         );
0330     } else {
0331         KisSprayFunctionBasedDistribution::initialize(1000, 0.0, std::min(1.0, sigma * 4.0),
0332             [m_c1](double x)
0333             {
0334                 return 2.0 * x * std::exp(-(x * x / m_c1));
0335             }
0336         );
0337     }
0338 }
0339 
0340 KisSprayCurveBasedDistribution::KisSprayCurveBasedDistribution()
0341 {}
0342 
0343 KisSprayCurveBasedDistribution::KisSprayCurveBasedDistribution(const KisCubicCurve &curve, size_t repeat)
0344 {
0345     KIS_SAFE_ASSERT_RECOVER_RETURN(repeat > 0);
0346 
0347     KisSprayFunctionBasedDistribution::initialize(((curve.points().size() % 4) + 1) * 1000 * repeat, 0.0, 1.0,
0348         [curve, repeat](double x)
0349         { 
0350             const double sx = x * static_cast<double>(repeat);
0351             return curve.value(sx - std::floor(sx));
0352         }
0353     );
0354 }
0355 
0356 KisSprayCurveBasedDistributionPolarDistance::KisSprayCurveBasedDistributionPolarDistance()
0357 {}
0358 
0359 KisSprayCurveBasedDistributionPolarDistance::KisSprayCurveBasedDistributionPolarDistance(const KisCubicCurve &curve, size_t repeat)
0360 {
0361     KIS_SAFE_ASSERT_RECOVER_RETURN(repeat > 0);
0362 
0363     KisSprayFunctionBasedDistribution::initialize(((curve.points().size() % 4) + 1) * 1000 * repeat, 0.0, 1.0,
0364         [curve, repeat](double x)
0365         { 
0366             const double sx = x * static_cast<double>(repeat);
0367             return 2.0 * x * curve.value(sx - std::floor(sx));
0368         }
0369     );
0370 }