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 }