Krita Source Code Documentation
Loading...
Searching...
No Matches
KisSprayFunctionBasedDistribution::Private Class Reference

Classes

struct  SampleInfo
 

Public Member Functions

double generate (double randomValue) const
 
template<typename Function >
void initialize (size_t numberOfSamples, double a, double b, Function f)
 

Public Attributes

std::vector< SampleInfosamples
 

Detailed Description

Definition at line 14 of file KisSprayRandomDistributions.cpp.

Member Function Documentation

◆ generate()

double KisSprayFunctionBasedDistribution::Private::generate ( double randomValue) const
inline

Definition at line 151 of file KisSprayRandomDistributions.cpp.

152 {
153 // Find the first sample that has cdf greater than the passed value
154 auto sampleIterator =
155 std::upper_bound(samples.begin(), samples.end(), SampleInfo{0.0, randomValue, 0.0},
157 const double t = (randomValue - (sampleIterator - 1)->cdfAtX) * sampleIterator->oneOverCdfDy;
158 return (sampleIterator - 1)->x + t * (sampleIterator->x - (sampleIterator - 1)->x);
159 }
auto mem_less(MemTypeNoRef Class::*ptr, MemType &&value)
mem_less is an unary functor that compares a member of the object to a given value
Definition KisMpl.h:269

References KisSprayFunctionBasedDistribution::Private::SampleInfo::cdfAtX, kismpl::mem_less(), and samples.

◆ initialize()

template<typename Function >
void KisSprayFunctionBasedDistribution::Private::initialize ( size_t numberOfSamples,
double a,
double b,
Function f )
inline

Definition at line 27 of file KisSprayRandomDistributions.cpp.

28 {
29 KIS_SAFE_ASSERT_RECOVER_RETURN(numberOfSamples > 0);
31
32 samples.clear();
33
34 if (numberOfSamples < 3) {
35 samples.push_back({a, 0.0, 0.0});
36 samples.push_back({b, 1.0, 1.0});
37 return;
38 }
39
40 // Create the CDF
41 const double domainSize = b - a;
42 const double intervalSize = domainSize / static_cast<double>(numberOfSamples - 1);
43 double sum = 0.0;
44 double lastX, lastY, lastSum;
45 size_t effectiveNumberOfSamples = numberOfSamples;
46
47 // Adjust the limits of the pdf if it has a 0 probability segment at
48 // the start or at the end
49 {
50 for (size_t i = 0; i < numberOfSamples; ++i) {
51 const double x = a + intervalSize * static_cast<double>(i);
52 const double y = f(x);
53 if (y > 0.0) {
54 if (i > 0) {
55 a += intervalSize * static_cast<double>(i - 1);
56 effectiveNumberOfSamples -= i - 1;
57 }
58 break;
59 }
60 if (i == numberOfSamples - 1) {
61 // The whole pdf must have 0 probability
62 return;
63 }
64 }
65
66 for (size_t i = 0; i < numberOfSamples; ++i) {
67 const double x = b - intervalSize * static_cast<double>(i);
68 const double y = f(x);
69 if (y > 0.0) {
70 if (i > 0) {
71 b -= intervalSize * static_cast<double>(i - 1);
72 effectiveNumberOfSamples -= i - 1;
73 }
74 break;
75 }
76 }
77 }
78 // Insert first point
79 {
80 samples.push_back({a, 0.0, 0.0});
81 lastX = a;
82 lastY = f(a);
83 lastSum = 0.0;
84 }
85 // Insert the rest of the points
86 {
87 // This angle serves as a reference to see if the cdf curve is
88 // roughly straight, and remove some points
89 constexpr double maximumAngleDeviationToSkip{M_PI / 1000.0};
90 double referenceAngle = 0.0;
91 bool mustCheckAngle = false;
92 int skippedPoints = 0;
93
94 for (size_t i = 1; i < effectiveNumberOfSamples; ++i) {
95 const double x = a + intervalSize * static_cast<double>(i);
96 const double y = f(x);
97 // Accumulate the area under the curve between the two points
98 sum += (x - lastX) * (y + lastY) / 2.0;
99 //
100 if (y == 0.0) {
101 if (lastY == 0.0) {
102 lastX = x;
103 lastY = y;
104 lastSum = sum;
105 ++skippedPoints;
106 continue;
107 } else {
108 mustCheckAngle = false;
109 }
110 } else {
111 if (lastY == 0.0) {
112 if (skippedPoints > 0) {
113 samples.push_back({lastX, lastSum, 0.0});
114 }
115 mustCheckAngle = false;
116 }
117 }
118
119 // Compute if the current point is nearly colinear to the last two points
120 if (i > 1 && mustCheckAngle) {
121 const int nPoints = samples.size();
122 const double angle = std::atan2(sum - samples[nPoints - 2].cdfAtX, x - samples[nPoints - 2].x);
123 if (std::abs(angle - referenceAngle) <= maximumAngleDeviationToSkip) {
124 samples.back().x = x;
125 samples.back().cdfAtX = sum;
126 continue;
127 }
128 }
129
130 samples.push_back({x, sum, 0.0});
131 referenceAngle = std::atan2(sum - lastSum, x - lastX);
132 mustCheckAngle = true;
133 skippedPoints = 0;
134
135 lastX = x;
136 lastY = y;
137 lastSum = sum;
138 }
139 }
140 // Scale the cdf to the [0..1] range and compute deltas
141 {
142 for (size_t i = 1; i < samples.size() - 1; ++i) {
143 samples[i].cdfAtX /= sum;
144 samples[i].oneOverCdfDy = 1.0 / (samples[i].cdfAtX - samples[i - 1].cdfAtX);
145 }
146 samples.back().cdfAtX = 1.0;
147 samples.back().oneOverCdfDy = 1.0 / (1.0 - samples[samples.size() - 2].cdfAtX);
148 }
149 }
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define M_PI
Definition kis_global.h:111

References KIS_SAFE_ASSERT_RECOVER_RETURN, M_PI, and samples.

Member Data Documentation

◆ samples

std::vector<SampleInfo> KisSprayFunctionBasedDistribution::Private::samples

Definition at line 24 of file KisSprayRandomDistributions.cpp.


The documentation for this class was generated from the following file: