├── Distribute.cpp ├── Distribute.h ├── DistributeTest.cpp ├── Generate.cpp ├── Generate.h ├── LICENSE.md ├── README.md └── images ├── circle-golden-anim.gif ├── circle-golden.png ├── circle-halton-anim.gif ├── circle-pcg-anim.gif ├── circle-radially-weighted.png ├── circle-rd-anim.gif ├── cube-rd-anim.gif ├── float-triangle.png ├── float-weighted.png ├── float.png ├── gaussian.png ├── halton-100.png ├── halton-1000.png ├── halton-5000.png ├── int-gauss-like.png ├── int-triangle.png ├── int.png ├── ring.png ├── sphere-surface-golden.png ├── sphere-surface.png ├── square-gauss-like.png ├── square-golden.png ├── square-rd-anim.gif ├── square-triangle.png ├── square.png └── triangle.png /Distribute.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // File: Distribute.cpp 3 | // 4 | // Function: Distribute uniform u32 in various ways 5 | // 6 | // Copyright: Andrew Willmott, 2018 7 | // 8 | 9 | #include "Distribute.h" 10 | 11 | using namespace DistLib; 12 | 13 | namespace 14 | { 15 | constexpr float vl_pi = 3.14159265358979323846f; 16 | 17 | inline void sincosf_local(float theta, float* sOut, float* cOut) 18 | { 19 | float c = cosf(theta); 20 | 21 | *sOut = copysignf(sqrtf(1.0f - c * c), theta); 22 | *cOut = c; 23 | } 24 | 25 | inline float InvSqrtFast(float x) 26 | { 27 | float xhalf = 0.5f * x; 28 | int32_t i = (int32_t&) x; 29 | 30 | i = 0x5f375a86 - (i >> 1); 31 | x = (float&) i; 32 | x = x * (1.5f - xhalf * x * x); 33 | 34 | return x; 35 | } 36 | } 37 | 38 | // 1D 39 | 40 | uint32_t DistLib::ModGaussLike(uint32_t u) 41 | { 42 | uint32_t x0 = u; 43 | uint32_t x1 = Internal::Next(x0); 44 | uint32_t x2 = Internal::Next(x1); 45 | uint32_t x3 = Internal::Next(x2); 46 | 47 | #if 0 48 | uint32_t c0 = (x0 & x1 & 1); 49 | uint32_t c1 = (x2 & x3 & 1); 50 | 51 | uint32_t carry = c0 + c1 + (c0 & c1 & 1); 52 | #else 53 | uint32_t carry = 1; // because this situation is symmetrical we can use a constant at the expense of never getting 0 or UINT32_MAX 54 | #endif 55 | 56 | uint32_t r = (x0 >> 2) + (x1 >> 2) + (x2 >> 2) + (x3 >> 2) + carry; 57 | 58 | return r; 59 | } 60 | 61 | uint32_t DistLib::ModWeighted(uint32_t u, int numWeights, const float weights[]) 62 | { 63 | float weightSum = 0.0f; 64 | 65 | for (int i = 0; i < numWeights; i++) 66 | weightSum += weights[i]; 67 | 68 | float s = ToFloat(u, weightSum); 69 | 70 | for (int i = 0; i < numWeights; i++) 71 | { 72 | if (s < weights[i]) 73 | { 74 | float fs = float(i) / numWeights + s / (numWeights * weights[i]); 75 | return uint32_t(fs * UINT32_MAX); 76 | } 77 | 78 | s -= weights[i]; 79 | } 80 | 81 | return UINT32_MAX; 82 | } 83 | 84 | uint32_t DistLib::ModWeighted(uint32_t u, int numWeights, const int weights[]) 85 | { 86 | DL_ASSERT(numWeights > 0); 87 | 88 | uint32_t weightSum = 0; 89 | 90 | for (int i = 0; i < numWeights; i++) 91 | weightSum += weights[i]; 92 | 93 | DL_ASSERT(weightSum >= 1); 94 | 95 | uint32_t step = UINT32_MAX / weightSum; 96 | 97 | for (int i = 0; i < numWeights; i++) 98 | { 99 | if (u < weights[i] * step) 100 | { 101 | uint32_t outStep = UINT32_MAX / numWeights; 102 | 103 | return outStep * i + (weightSum * (u / weights[i])) / numWeights; // re-arranged to stick to 32-bit arithmetic at the expense of a few bits 104 | } 105 | 106 | u -= weights[i] * step; 107 | } 108 | 109 | return UINT32_MAX; 110 | } 111 | 112 | int32_t DistLib::ToInt32Weighted(uint32_t u, int32_t numWeights, const float weights[]) 113 | { 114 | float weightSum = 0.0f; 115 | 116 | for (int i = 0; i < numWeights; i++) 117 | weightSum += weights[i]; 118 | 119 | float s = ToFloat(u, weightSum); 120 | weightSum = 0.0f; 121 | 122 | for (int i = 0; i < numWeights; i++) 123 | { 124 | weightSum += weights[i]; 125 | 126 | if (s < weightSum) 127 | return i; 128 | } 129 | 130 | return numWeights - 1; 131 | } 132 | 133 | float DistLib::ToFloatGaussian(uint32_t u) 134 | { 135 | float x, y, s; 136 | 137 | while (true) 138 | { 139 | uint32_t u0 = u; 140 | uint32_t u1 = Internal::Next(u0); 141 | 142 | x = ToFloatSigned(u0); 143 | y = ToFloatSigned(u1); 144 | s = x * x + y * y; 145 | 146 | if (s <= 1.0f) 147 | break; 148 | 149 | u = Internal::Next(u1); 150 | } 151 | 152 | float f = sqrtf(-2.0f * logf(s) / s); 153 | 154 | return f * (x + y) * sqrtf(0.5f); 155 | } 156 | 157 | Vec2f DistLib::ToFloatGaussian(uint32_t u0, uint32_t u1) 158 | { 159 | // Full Box-Muller transform 160 | Vec2f v2 = ToDirection(u0); 161 | 162 | float r = ToFloat(u1); 163 | float f = sqrtf(-2.0f * logf(r)); 164 | 165 | return f * v2; 166 | } 167 | 168 | 169 | // 2D 170 | 171 | Vec2f DistLib::ToDirection2(uint32_t u) 172 | { 173 | // Reject points outside unit sphere and inside epsilon ball. May seem clunky but expected iteration count is < 2 174 | while (true) 175 | { 176 | uint32_t u0 = u; 177 | uint32_t u1 = Internal::Next(u0); 178 | 179 | Vec2f p = Vec2f(ToFloatSigned(u0), ToFloatSigned(u1)); 180 | float p2 = sqrlen(p); 181 | 182 | if (p2 <= 1.0f && p2 >= 1e-4f) 183 | return p * InvSqrtFast(p2); 184 | 185 | u = Internal::Next(u1); 186 | } 187 | } 188 | 189 | Vec2f DistLib::ToCircle(uint32_t u) 190 | { 191 | // Reject points outside unit sphere. May seem clunky but expected iteration count is < 2 192 | while (true) 193 | { 194 | uint32_t u0 = u; 195 | uint32_t u1 = Internal::Next(u0); 196 | 197 | Vec2f p = Vec2f(ToFloatSigned(u0), ToFloatSigned(u1)); 198 | 199 | if (sqrlen(p) <= 1.0f) 200 | return p; 201 | 202 | u = Internal::Next(u1); 203 | } 204 | } 205 | 206 | Vec2f DistLib::ToRing(uint32_t u, float r) 207 | { 208 | while (true) 209 | { 210 | uint32_t u0 = u; 211 | uint32_t u1 = Internal::Next(u0); 212 | 213 | Vec2f v = Vec2f(ToFloatSigned(u0), ToFloatSigned(u1)); 214 | 215 | float Vec2f = sqrlen(v); 216 | 217 | if (Vec2f >= 1.0f || Vec2f < 1e-4f) 218 | { 219 | u = Internal::Next(u1); 220 | continue; 221 | } 222 | 223 | float r1 = sqrtf(Vec2f); 224 | 225 | return v * ((r1 * r + 1.0f - r) / r1); 226 | } 227 | } 228 | 229 | Vec2f DistLib::ToDirection(uint32_t u0) 230 | { 231 | float theta = ToFloatSigned(u0, vl_pi); 232 | 233 | float ss, cs; 234 | sincosf_local(theta, &ss, &cs); 235 | 236 | return Vec2f(cs, ss); 237 | } 238 | 239 | Vec2f DistLib::ToCircle(uint32_t u0, uint32_t u1) 240 | { 241 | float theta = ToFloatSigned(u0, vl_pi); 242 | 243 | float ss, cs; 244 | sincosf_local(theta, &ss, &cs); 245 | 246 | float r2 = ToFloat(u1); 247 | 248 | return Vec2f(cs, ss) * sqrtf(r2); 249 | } 250 | 251 | Vec2f DistLib::ToRing(uint32_t u0, uint32_t u1, float r) 252 | { 253 | float theta = ToFloatSigned(u0, vl_pi); 254 | 255 | float ss, cs; 256 | sincosf_local(theta, &ss, &cs); 257 | 258 | float r0 = (1.0f - r) * (1.0f - r); 259 | float r2 = ToFloat(u1, r0, 1.0f); 260 | 261 | return Vec2f(cs, ss) * sqrtf(r2); 262 | } 263 | 264 | 265 | // 3D 266 | 267 | Vec3f DistLib::ToDirection3(uint32_t u) 268 | { 269 | while (true) 270 | { 271 | uint32_t u0 = u; 272 | uint32_t u1 = Internal::Next(u0); 273 | uint32_t u2 = Internal::Next(u1); 274 | 275 | Vec3f p = Vec3f(ToFloatSigned(u0), ToFloatSigned(u1), ToFloatSigned(u2)); 276 | float p2 = sqrlen(p); 277 | 278 | if (p2 <= 1.0f && p2 >= 1e-4f) 279 | return p * InvSqrtFast(p2); 280 | 281 | u = Internal::Next(u2); 282 | } 283 | } 284 | 285 | Vec3f DistLib::ToSphere(uint32_t u) 286 | { 287 | while (true) 288 | { 289 | uint32_t u0 = u; 290 | uint32_t u1 = Internal::Next(u0); 291 | uint32_t u2 = Internal::Next(u1); 292 | 293 | Vec3f p = Vec3f(ToFloatSigned(u0), ToFloatSigned(u1), ToFloatSigned(u2)); 294 | 295 | if (sqrlen(p) <= 1.0f) 296 | return p; 297 | 298 | u = Internal::Next(u2); 299 | } 300 | } 301 | 302 | Vec3f DistLib::ToEllipsoid(uint32_t u, Vec3f min, Vec3f max) 303 | { 304 | return (min + max + ToSphere(u) * (max - min)) * 0.5f; 305 | } 306 | 307 | Vec3f DistLib::ToTorus(uint32_t u, float r) 308 | { 309 | // Unit torus is r = 0.5, width = (0.5, 1) 310 | // The volume is 2pi * 0.5 * (pi * (0.5 * 1)) = pi^2 / 2 =~ 4.9. So the average 311 | // iteration count is a little less than for the sphere test. 312 | 313 | while (true) 314 | { 315 | uint32_t u0 = u; 316 | uint32_t u1 = Internal::Next(u0); 317 | uint32_t u2 = Internal::Next(u1); 318 | 319 | Vec3f p = Vec3f(ToFloatSigned(u0), ToFloatSigned(u1), ToFloatSigned(u2)); 320 | 321 | // is this point inside a r = 0.5 torus? 322 | float len2xy = p.x * p.x + p.y * p.y; 323 | 324 | if (len2xy < 1e-8f) 325 | { 326 | u = Internal::Next(u2); 327 | continue; 328 | } 329 | 330 | float len2z = p.z * p.z; 331 | float r1 = sqrtf(len2xy); 332 | 333 | if (len2z + len2xy > r1) 334 | { 335 | u = Internal::Next(u2); 336 | continue; 337 | } 338 | 339 | // adjust according to true radius: r1' = r1 * r + (1 - r) 340 | float r1Dash = r + (1.0f - r) / r1; 341 | 342 | p = p * Vec3f(r1Dash, r1Dash, r); 343 | 344 | return p; 345 | } 346 | } 347 | 348 | Vec3f DistLib::ToDirection(uint32_t u0, uint32_t u1) 349 | { 350 | float theta = ToFloatSigned(u0, vl_pi); 351 | 352 | float ss, cs; 353 | sincosf_local(theta, &ss, &cs); 354 | 355 | float cz = ToFloatSigned(u1); 356 | float sz = sqrtf(1.0f - cz * cz); 357 | 358 | return Vec3f(cs * sz, ss * sz, cz); 359 | } 360 | 361 | Vec3f DistLib::ToSphere(uint32_t u0, uint32_t u1, uint32_t u2) 362 | { 363 | float theta = ToFloatSigned(u0, vl_pi); 364 | 365 | float ss, cs; 366 | sincosf_local(theta, &ss, &cs); 367 | 368 | float cz = ToFloatSigned(u1); 369 | float sz = sqrtf(1.0f - cz * cz); 370 | 371 | float r3 = ToFloat(u2); 372 | 373 | return Vec3f(cs * sz, ss * sz, cz) * cbrtf(r3); 374 | } 375 | 376 | Vec3f DistLib::ToEllipsoid(uint32_t u0, uint32_t u1, uint32_t u2, Vec3f min, Vec3f max) 377 | { 378 | return (min + max + ToSphere(u0, u1, u2) * (max - min)) * 0.5f; 379 | } 380 | 381 | Vec3f DistLib::ToTorus(uint32_t u0, uint32_t u1, uint32_t u2, float r) 382 | { 383 | r *= 0.5f; 384 | 385 | float theta = ToFloatSigned(u0, vl_pi); 386 | 387 | float ss, cs; 388 | sincosf_local(theta, &ss, &cs); 389 | 390 | float cz = ToFloatSigned(u2); 391 | float sz = sqrtf(1.0f - cz * cz); 392 | 393 | float r0 = (1.0f - r - sz * r); 394 | float r1 = (1.0f - r + sz * r); 395 | float r2 = ToFloat(u1, r0 * r0, r1 * r1); 396 | 397 | float rr = sqrtf(r2); 398 | 399 | return Vec3f(cs * rr, ss * rr, cz * r); 400 | } 401 | 402 | Vec2f DistLib::ToTriangleHier(uint32_t u) 403 | { 404 | // Current tri defined by c, (cx + w, cy), (c.x, c.y + w) 405 | float cx = 0.0f, cy = 0.0f; 406 | float w = 1; 407 | 408 | for (int i = 0; i < 16; i++) 409 | { 410 | if (!u) 411 | break; // picking middle for the remaining points 412 | 413 | w *= 0.5f; 414 | 415 | switch (u & 0x3) 416 | { 417 | case 0: // middle (inverted) 418 | cx += w; 419 | cy += w; 420 | w = -w; 421 | break; 422 | case 1: // bottom-right 423 | cx += w; 424 | break; 425 | case 2: // top-left 426 | cy += w; 427 | break; 428 | case 3: // bottom-left 429 | break; 430 | } 431 | 432 | u >>= 2; 433 | } 434 | 435 | return Vec2f(cx + w / 3.0f, cy + w / 3.0f); 436 | } 437 | 438 | Vec2f DistLib::ToTriangleHierRev(uint32_t u) 439 | { 440 | // Current tri defined by c, (cx + w, cy), (c.x, c.y + w) 441 | float cx = 0.0f, cy = 0.0f; 442 | float w = 1; 443 | 444 | for (int i = 0; i < 16; i++) 445 | { 446 | if (!u) 447 | break; // picking middle for the remaining points 448 | 449 | w *= 0.5f; 450 | 451 | switch ((u >> 30) & 0x3) 452 | { 453 | case 0: // middle (inverted) 454 | cx += w; 455 | cy += w; 456 | w = -w; 457 | break; 458 | case 1: // bottom-right 459 | cx += w; 460 | break; 461 | case 2: // top-left 462 | cy += w; 463 | break; 464 | case 3: // bottom-left 465 | break; 466 | } 467 | 468 | u <<= 2; 469 | } 470 | 471 | return Vec2f(cx + w / 3.0f, cy + w / 3.0f); 472 | } 473 | -------------------------------------------------------------------------------- /Distribute.h: -------------------------------------------------------------------------------- 1 | // 2 | // File: Distribute.h 3 | // 4 | // Function: Distribute uniform u32 values in various ways 5 | // 6 | // Copyright: Andrew Willmott, 2018 7 | // 8 | 9 | #ifndef DISTRIBUTE_H 10 | #define DISTRIBUTE_H 11 | 12 | #include 13 | #include 14 | 15 | #ifndef DL_ASSERT 16 | #define DL_ASSERT(X) 17 | #endif 18 | 19 | #ifndef DL_VEC2F_CONVERT 20 | #define DL_VEC2F_CONVERT // E.g, #define DL_VEC2F_CONVERT Vec2f(MyV2 v2) : x(v2.x), y(v2.y) {}; operator MyV2() const { return { x, y }; } 21 | #define DL_VEC3F_CONVERT 22 | #endif 23 | 24 | namespace DistLib 25 | { 26 | struct Vec2f { float x; float y; Vec2f() {}; Vec2f(float xi, float yi) : x(xi), y(yi) {}; DL_VEC2F_CONVERT }; 27 | struct Vec3f { float x; float y; float z; Vec3f() {}; Vec3f(float xi, float yi, float zi) : x(xi), y(yi), z(zi) {}; DL_VEC3F_CONVERT }; 28 | 29 | 30 | // -------------------------------------------------------------------------- 31 | // Helpers for remapping an evenly distributed uint32_t value to the desired 32 | // distribution shape and range. 33 | // 34 | // These functions can be used with any RNG that returns a uint32_t or 35 | // implements operator uint32_t(), e.g., 36 | // 37 | // int i = ToInt32(my_rand(), 10); 38 | // 39 | // cRNG rng; 40 | // float r = ToFloat(rng, 1.0f, 20.0f); 41 | // 42 | // -------------------------------------------------------------------------- 43 | 44 | // 1D 45 | uint32_t ToUInt32 (uint32_t u); ///< Returns x (here for consistency) 46 | uint32_t ToUInt32 (uint32_t u, uint32_t limit); ///< Returns [0, limit - 1]. Must have limit >= 0. 47 | uint32_t ToUInt32Inclusive(uint32_t u, uint32_t limit); ///< Returns [0, limit]. Must have limit >= 0. 48 | uint32_t ToUInt32 (uint32_t u, uint32_t a, uint32_t b); ///< Returns [a, b - 1]. Must have a <= b. 49 | uint32_t ToUInt32Inclusive(uint32_t u, uint32_t a, uint32_t b); ///< Returns [a, b]. Must have a <= b. 50 | 51 | int32_t ToInt32 (uint32_t u); ///< Returns a positive int32_t 52 | int32_t ToInt32Signed (uint32_t u); ///< Returns an int32_t (can be -ve) 53 | int32_t ToInt32 (uint32_t u, int32_t limit); ///< Returns [0, limit - 1]. Must have limit >= 0 -- asserts on this. 54 | int32_t ToInt32Inclusive (uint32_t u, int32_t limit); ///< Returns [0, limit]. Must have limit >= 0. 55 | int32_t ToInt32Signed (uint32_t u, int32_t limit); ///< Returns [-limit, limit]. Must have limit >= 0. 56 | int32_t ToInt32 (uint32_t u, int32_t a, int32_t b); ///< Returns [a, b - 1]. Must have a <= b. 57 | int32_t ToInt32Inclusive (uint32_t u, int32_t a, int32_t b); ///< Returns [a, b]. Must have a <= b. 58 | 59 | float ToFloat (uint32_t u); ///< Returns a float in the range [ 0, 1] 60 | float ToFloatSigned(uint32_t u); ///< Returns a float in the range [-1, 1] 61 | float ToFloat (uint32_t u, float a); ///< Returns a float in the range [ 0, a] 62 | float ToFloatSigned(uint32_t u, float a); ///< Returns a float in the range [-a, a] 63 | float ToFloat (uint32_t u, float a, float b); ///< Returns a float in the range [ a, b] 64 | 65 | // 2D 66 | Vec2f ToSquare (uint32_t u); ///< Returns random point in the unit square 67 | Vec2f ToRectangle (uint32_t u, Vec2f min, Vec2f max); ///< Returns random point between min and max 68 | Vec2f ToTriangle (uint32_t u); ///< Returns random point in triangle with vertices (1, 0), (0, 0), (1, 0). 69 | Vec2f ToTriangle (uint32_t u, Vec2f v0, Vec2f v1, Vec2f v2); ///< Returns random point in triangle with vertices v0, v1, v2. 70 | Vec2f ToDirection2(uint32_t u); ///< Returns random direction vector 71 | Vec2f ToCircle (uint32_t u); ///< Returns random point in the unit circle 72 | Vec2f ToRing (uint32_t u, float r); ///< Returns random point in the unit ring with given width. r=1 -> full circle. 73 | 74 | // 3D 75 | Vec3f ToCube (uint32_t u); ///< Returns random point in the unit cube 76 | Vec3f ToCuboid (uint32_t u, Vec3f min, Vec3f max); ///< Returns random point between min and max 77 | Vec3f ToTriangle (uint32_t u, Vec3f v0, Vec3f v1, Vec3f v2); ///< Returns random point in triangle with vertices v0, v1, v2. 78 | Vec3f ToDirection3(uint32_t u); ///< Returns random direction vector 79 | Vec3f ToSphere (uint32_t u); ///< Returns random point in the unit sphere 80 | Vec3f ToEllipsoid (uint32_t u, Vec3f min, Vec3f max); ///< Returns random point in the ellipsoid defined by min and max. 81 | Vec3f ToTorus (uint32_t u, float r); ///< Returns random point from a torus of the given radial width. r=0 -> circle, r=1 -> no hole in the middle. 82 | 83 | // 2D Full-u versions. These are costlier and less convenient (requiring multiple rng inputs), but higher quality, and ensure the character of the generator is reflected across all dimensions. 84 | Vec2f ToSquare (uint32_t u0, uint32_t u1); ///< Returns random point in the unit square 85 | Vec2f ToRectangle (uint32_t u0, uint32_t u1, Vec2f min, Vec2f max); ///< Returns random point between min and max 86 | Vec2f ToTriangle (uint32_t u0, uint32_t u1); ///< Returns random point in triangle with vertices (1, 0), (0, 0), (1, 0). 87 | Vec2f ToTriangle (uint32_t u0, uint32_t u1, Vec2f v0, Vec2f v1, Vec2f v2); ///< Returns random point in triangle with vertices v0, v1, v2. 88 | Vec2f ToDirection (uint32_t u0); ///< Returns random direction vector. 89 | Vec2f ToCircle (uint32_t u0, uint32_t u1); ///< Returns random point in the unit circle. u0 affects angular distribute, u1 the radial. E.g., ToCircle(r1, ModTriangle(r2)) gives more samples in the centre. 90 | Vec2f ToRing (uint32_t u0, uint32_t u1, float r); ///< Returns random point in the unit ring with given width. r=1 -> full circle. 91 | 92 | // 3D Full-u versions. These are costlier and less convenient (requiring multiple rng inputs), but higher quality, and ensure the character of the generator is reflected across all dimensions. 93 | Vec3f ToCube (uint32_t u0, uint32_t u1, uint32_t u2); ///< Returns point vector in the unit cube 94 | Vec3f ToCuboid (uint32_t u0, uint32_t u1, uint32_t u2, Vec3f min, Vec3f max); ///< Returns random point between min and max 95 | Vec3f ToTriangle (uint32_t u0, uint32_t u1, Vec3f v0, Vec3f v1, Vec3f v2); ///< Returns random point in triangle with vertices v0, v1, v2. 96 | Vec3f ToDirection (uint32_t u0, uint32_t u1); ///< Returns random direction vector 97 | Vec3f ToSphere (uint32_t u0, uint32_t u1, uint32_t u2); ///< Returns random point in the unit sphere 98 | Vec3f ToEllipsoid (uint32_t u0, uint32_t u1, uint32_t u2, Vec3f min, Vec3f max); ///< Returns random point in the ellipsoid defined by min and max. 99 | Vec3f ToTorus (uint32_t u0, uint32_t u1, uint32_t u2, float r); ///< Returns random point from a torus with the given radius and bounds 100 | 101 | // Modifiers. These can be used to pre-warp the input distribution before calling the above. 102 | uint32_t ModTriangle (uint32_t u); ///< Returns a triangle-shaped distribution. Useful in combination with other distributors, e.g., ToFloat(ModTriangle(u)), or ToSquare(ModTriangle(u0), ModTriangle(u1)). 103 | uint32_t ModGaussLike(uint32_t u); ///< Returns a gaussian-like distribution with variance of 1/6. This is much cheaper than a proper gaussian. The tails are limited to the range, so ToFloat(ToGaussLike(u), a, b) will always return a number between a and b, which can be useful. 104 | 105 | uint32_t ModWeighted (uint32_t u, int numWeights, const int weights[]); ///< Returns distribution weighted to 'numWeights' bins, according to 'weights'. A weight can be zero (no samples), but at least one weight must be non-zero. 106 | uint32_t ModWeighted (uint32_t u, int numWeights, const float weights[]); ///< Version of ModWeighted that takes float weights for convenience. 107 | 108 | uint32_t ModInvert (uint32_t u); ///< Inverts distribution 109 | uint32_t ModHalfUp (uint32_t u); ///< Expands the left side of a symmetric distribution, e.g., a triangle distribution is converted to a ramp up. 110 | uint32_t ModHalfDown (uint32_t u); ///< Expands the right side of a symmetric distribution, e.g., a triangle distribution is converted to a ramp down. 111 | uint32_t ModSymmUp (uint32_t u); ///< Converts a ramp back to a symmetric distribution, inverse of ModHalfUp. 112 | uint32_t ModSymmDown (uint32_t u); ///< Converts a ramp back to a symmetric distribution, inverse of ModHalfDown. 113 | 114 | // Specialty or convenience operations. 115 | float ToFloatTriangle (uint32_t u, float a, float b); ///< Most useful variant of ModTriangle -- returns a triangle-shaped distribution in [a, b]. Shorthand for ToFloat(ModTriangle(u), a, b). 116 | float ToFloatGaussLike(uint32_t u, float a, float b); ///< Most useful variant of ModGaussLike -- returns a gaussian-like distribution in [a, b]. 117 | 118 | int32_t ToInt32Weighted(uint32_t u, int numWeights, const float weights[]); ///< Returns [0, numWeights - 1], distributed according to 'weights'. Cheaper version of ToInt32(ToWeighted(u, ...), numWeights) 119 | 120 | float ToFloatGaussian(uint32_t u); ///< Returns a proper gaussian ("normal") distribution. 121 | float ToFloatGaussian(uint32_t u, float mean, float std_dev); ///< Returns a gaussian distribution with the given mean and standard deviation 122 | Vec2f ToFloatGaussian(uint32_t u0, uint32_t u1); ///< Full version of ToFloatGaussian via the Box-Muller transform, returns two normally-distributed samples for two inputs. 123 | 124 | Vec2f ToTriangleHier (uint32_t u); ///< Distributes u hierarchically in the triangle. (See Basu & Owen '14.) Can be more evenly spaced than default version but is more expensive. 125 | Vec2f ToTriangleHierRev(uint32_t u); ///< Distributes u taking high bits first rather than low bits. 126 | } 127 | 128 | 129 | // -------------------------------------------------------------------------- 130 | // Inlines 131 | // -------------------------------------------------------------------------- 132 | 133 | namespace DistLib 134 | { 135 | // Internal helpers 136 | namespace Internal 137 | { 138 | constexpr float kFloatFromUInt32Scale = float(1.0 / 0xFFFFFFFF); 139 | constexpr float kFloatFromInt32Scale = float(2.0 / 0xFFFFFFFF); 140 | 141 | inline uint32_t Next(uint32_t u) 142 | { 143 | return u * 1664525 + 1013904223; 144 | } 145 | } 146 | 147 | // uint32_t variants 148 | 149 | inline uint32_t ToUInt32(uint32_t x) 150 | { 151 | return x; 152 | } 153 | 154 | inline uint32_t ToUInt32(uint32_t u, uint32_t limit) 155 | { 156 | return (u * (uint64_t(limit))) >> 32; 157 | } 158 | 159 | inline uint32_t ToUInt32(uint32_t u, uint32_t a, uint32_t b) 160 | { 161 | DL_ASSERT(a <= b); 162 | return a + ToUInt32(u, b - a); 163 | } 164 | 165 | inline uint32_t ToUInt32Inclusive(uint32_t u, uint32_t limit) 166 | { 167 | return (u * (uint64_t(limit) + 1)) >> 32; 168 | } 169 | 170 | inline uint32_t ToUInt32Inclusive(uint32_t u, uint32_t a, uint32_t b) 171 | { 172 | DL_ASSERT(a <= b); 173 | return a + ToUInt32Inclusive(u, b - a); 174 | } 175 | 176 | // int32_t variants 177 | 178 | inline int32_t ToInt32(uint32_t u) 179 | { 180 | return int32_t(u & 0x7FFFFFFF); 181 | } 182 | 183 | inline int32_t ToInt32Signed(uint32_t u) 184 | { 185 | return int32_t(u ^ 0x80000000); 186 | } 187 | 188 | inline int32_t ToInt32Inclusive(uint32_t u, int32_t limit) 189 | { 190 | return int32_t((u * (uint64_t(limit) + 1)) >> 32); 191 | } 192 | 193 | inline int32_t ToInt32(uint32_t u, int32_t limit) 194 | { 195 | DL_ASSERT(limit >= 0); 196 | return int32_t((u * (uint64_t(limit))) >> 32); 197 | } 198 | 199 | inline int32_t ToInt32Signed(uint32_t u, int32_t limit) 200 | { 201 | DL_ASSERT(limit >= 0); 202 | return ((u * uint64_t(2 * limit + 1)) >> 32) - limit; 203 | } 204 | 205 | inline int32_t ToInt32(uint32_t u, int32_t a, int32_t b) 206 | { 207 | DL_ASSERT(a <= b); 208 | return a + ToUInt32(u, b - a); 209 | } 210 | 211 | inline int32_t ToInt32Inclusive(uint32_t u, int32_t a, int32_t b) 212 | { 213 | DL_ASSERT(a <= b); 214 | return a + ToUInt32Inclusive(u, b - a); 215 | } 216 | 217 | // float variants 218 | inline float ToFloat(uint32_t u) 219 | { 220 | return Internal::kFloatFromUInt32Scale * u; 221 | } 222 | 223 | inline float ToFloatSigned(uint32_t u) 224 | { 225 | return Internal::kFloatFromInt32Scale * u - float(1); 226 | } 227 | 228 | inline float ToFloat(uint32_t u, float a) 229 | { 230 | return a * (Internal::kFloatFromUInt32Scale * u); 231 | } 232 | 233 | inline float ToFloatSigned(uint32_t u, float a) 234 | { 235 | return a * (Internal::kFloatFromInt32Scale * u - float(1)); 236 | } 237 | 238 | inline float ToFloat(uint32_t u, float a, float b) 239 | { 240 | return a + (b - a) * (Internal::kFloatFromUInt32Scale * u); 241 | } 242 | 243 | // Specialty 244 | 245 | inline float ToFloatTriangle(uint32_t u, float a, float b) 246 | { 247 | return ToFloat(ModTriangle(u), a, b); 248 | } 249 | 250 | inline float ToFloatGaussLike(uint32_t u, float a, float b) 251 | { 252 | return ToFloat(ModGaussLike(u), a, b); 253 | } 254 | 255 | inline float ToFloatGaussian(uint32_t u, float mean, float std_dev) 256 | { 257 | return ToFloatGaussian(u) * std_dev + mean; 258 | } 259 | 260 | // 2D 261 | 262 | inline float sqrlen(Vec2f v) { return v.x * v.x + v.y * v.y; } 263 | inline Vec2f operator+(Vec2f a, Vec2f b) { return { a.x + b.x, a.y + b.y }; } 264 | inline Vec2f operator-(Vec2f a, Vec2f b) { return { a.x - b.x, a.y - b.y }; } 265 | inline Vec2f operator*(Vec2f a, Vec2f b) { return { a.x * b.x, a.y * b.y }; } 266 | inline Vec2f operator*(float s, Vec2f a) { return { s * a.x, s * a.y }; } 267 | inline Vec2f operator*(Vec2f a, float s) { return { s * a.x, s * a.y }; } 268 | 269 | inline Vec2f ToSquare(uint32_t u) 270 | { 271 | uint32_t u0 = u; 272 | uint32_t u1 = Internal::Next(u0); 273 | 274 | return ToSquare(u0, u1); 275 | } 276 | 277 | inline Vec2f ToRectangle(uint32_t u, Vec2f min, Vec2f max) 278 | { 279 | uint32_t u0 = u; 280 | uint32_t u1 = Internal::Next(u0); 281 | 282 | return ToRectangle(u0, u1, min, max); 283 | } 284 | 285 | inline Vec2f ToTriangle(uint32_t u) 286 | { 287 | uint32_t u0 = u; 288 | uint32_t u1 = Internal::Next(u0); 289 | 290 | float x = ToFloat(u0); 291 | float y = ToFloat(u1); 292 | 293 | if (x + y > 1.0f) 294 | return { 1.0f - x, 1.0f - y }; 295 | else 296 | return { x, y }; 297 | } 298 | 299 | inline Vec2f ToTriangle(uint32_t u, Vec2f v0, Vec2f v1, Vec2f v2) 300 | { 301 | Vec2f c = ToTriangle(u); 302 | return (1.0f - c.x - c.y) * v0 + c.x * v1 + c.y * v2; 303 | } 304 | 305 | // 3D 306 | 307 | inline float sqrlen(Vec3f v) { return v.x * v.x + v.y * v.y + v.z * v.z; } 308 | inline Vec3f operator+(Vec3f a, Vec3f b) { return { a.x + b.x, a.y + b.y, a.z + b.z}; } 309 | inline Vec3f operator-(Vec3f a, Vec3f b) { return { a.x - b.x, a.y - b.y, a.z - b.z}; } 310 | inline Vec3f operator*(Vec3f a, Vec3f b) { return { a.x * b.x, a.y * b.y, a.z * b.z}; } 311 | inline Vec3f operator*(float s, Vec3f a) { return { s * a.x, s * a.y, s * a.z}; } 312 | inline Vec3f operator*(Vec3f a, float s) { return { s * a.x, s * a.y, s * a.z}; } 313 | 314 | inline Vec3f ToCube(uint32_t u) 315 | { 316 | uint32_t u0 = u; 317 | uint32_t u1 = Internal::Next(u0); 318 | uint32_t u2 = Internal::Next(u1); 319 | 320 | return ToCube(u0, u1, u2); 321 | } 322 | 323 | inline Vec3f ToCuboid(uint32_t u, Vec3f min, Vec3f max) 324 | { 325 | uint32_t u0 = u; 326 | uint32_t u1 = Internal::Next(u0); 327 | uint32_t u2 = Internal::Next(u1); 328 | 329 | return ToCuboid(u0, u1, u2, min, max); 330 | } 331 | 332 | inline Vec3f ToTriangle(uint32_t u, Vec3f v0, Vec3f v1, Vec3f v2) 333 | { 334 | Vec2f c = ToTriangle(u); 335 | return (1.0f - c.x - c.y) * v0 + c.x * v1 + c.y * v2; 336 | } 337 | 338 | // 2D Full 339 | 340 | inline Vec2f ToSquare(uint32_t u0, uint32_t u1) 341 | { 342 | return Vec2f(ToFloatSigned(u0), ToFloatSigned(u1)); 343 | } 344 | 345 | inline Vec2f ToRectangle(uint32_t u0, uint32_t u1, Vec2f min, Vec2f max) 346 | { 347 | return Vec2f(ToFloat(u0, min.x, max.x), ToFloat(u1, min.y, max.y)); 348 | } 349 | 350 | inline Vec2f ToTriangle(uint32_t u0, uint32_t u1) 351 | { 352 | float x = ToFloat(u0); 353 | float t = sqrtf(ToFloat(u1)); 354 | 355 | return { t * x, 1.0f - t}; 356 | } 357 | 358 | inline Vec2f ToTriangle(uint32_t u0, uint32_t u1, Vec2f v0, Vec2f v1, Vec2f v2) 359 | { 360 | Vec2f c = ToTriangle(u0, u1); 361 | return (1.0f - c.x - c.y) * v0 + c.x * v1 + c.y * v2; 362 | } 363 | 364 | // 3D Full 365 | 366 | inline Vec3f ToCube(uint32_t u0, uint32_t u1, uint32_t u2) 367 | { 368 | return Vec3f(ToFloatSigned(u0), ToFloatSigned(u1), ToFloatSigned(u2)); 369 | } 370 | 371 | inline Vec3f ToCuboid(uint32_t u0, uint32_t u1, uint32_t u2, Vec3f min, Vec3f max) 372 | { 373 | return Vec3f(ToFloat(u0, min.x, max.x), ToFloat(u1, min.y, max.y), ToFloat(u2, min.z, max.z)); 374 | } 375 | 376 | inline Vec3f ToTriangle(uint32_t u0, uint32_t u1, Vec3f v0, Vec3f v1, Vec3f v2) 377 | { 378 | Vec2f c = ToTriangle(u0, u1); 379 | return (1.0f - c.x - c.y) * v0 + c.x * v1 + c.y * v2; 380 | } 381 | 382 | // Modifiers 383 | inline uint32_t ModTriangle(uint32_t u) 384 | { 385 | uint32_t x0 = u; 386 | uint32_t x1 = Internal::Next(x0); 387 | 388 | // Variance is 1/6 389 | uint32_t carry = (x0 & x1 & 1); 390 | 391 | return ((x0 >> 1) + (x1 >> 1) + carry); 392 | } 393 | 394 | inline uint32_t ModInvert(uint32_t u) 395 | { 396 | return ~u; 397 | } 398 | 399 | inline uint32_t ModHalfDown(uint32_t u) 400 | { 401 | return (u & 0x80000000) ? (u << 1) : ~(u << 1); 402 | } 403 | 404 | inline uint32_t ModHalfUp(uint32_t u) 405 | { 406 | return (u & 0x80000000) ? ~(u << 1) : (u << 1); 407 | } 408 | 409 | inline uint32_t ModSymmUp(uint32_t u) 410 | { 411 | return (u & 1) ? ~(u >> 1) : (u >> 1); 412 | } 413 | 414 | inline uint32_t ModSymmDown(uint32_t u) 415 | { 416 | return (u & 1) ? (u << 1) : ~(u << 1); 417 | } 418 | } 419 | 420 | #endif 421 | 422 | -------------------------------------------------------------------------------- /DistributeTest.cpp: -------------------------------------------------------------------------------- 1 | #include "Generate.h" 2 | #include "Distribute.h" 3 | 4 | #include 5 | #include 6 | 7 | using namespace DistLib; 8 | 9 | #ifdef _MSC_VER 10 | #pragma warning (disable: 4996) // let's not make fopen an error. 11 | #endif 12 | 13 | namespace 14 | { 15 | const int kWeights[] = { 2, 1, 0, 1 }; 16 | 17 | // Mini SVG api 18 | void svg_header(FILE* out, int size) 19 | { 20 | fprintf(out, "\n", size, size); 21 | } 22 | 23 | void svg_trailer(FILE* out) 24 | { 25 | fprintf(out, "\n"); 26 | } 27 | 28 | void svg_circle(FILE* out, Vec2f p0, float r, const char* colour, int outline = 0) 29 | { 30 | fprintf(out, "\n", 31 | p0.x, p0.y, r, 32 | outline, colour 33 | ); 34 | } 35 | 36 | void svg_line(FILE* out, Vec2f p0, Vec2f p1) 37 | { 38 | fprintf(out, "\n", 39 | p0.x, p0.y, p1.x, p1.y 40 | ); 41 | } 42 | void svg_text(FILE* out, Vec2f p0, const char* text, const char* colour) 43 | { 44 | fprintf(out, "%s\n", p0.x, p0.y, colour, text); 45 | } 46 | } 47 | 48 | int main(int argc, const char* argv[]) 49 | { 50 | int numSamples = 1000; 51 | 52 | if (argc > 1) 53 | numSamples = atoi(argv[1]); 54 | 55 | uint32_t seed = 0x12345678; 56 | 57 | if (argc > 2) 58 | seed = atoi(argv[2]); 59 | 60 | // Exercise various basics over a range of numSamples samples using a LCG generator. 61 | 62 | cLCG generator(seed); 63 | 64 | for (int i = 0; i < numSamples; i++) 65 | { 66 | uint32_t u = generator; 67 | 68 | printf("-- u: 0x%08x ------------------------------------------------\n", u); 69 | 70 | printf(" ToInt32(u, 10) = %2d\n", ToInt32(u, 10)); 71 | printf(" ToFloat(u, 100) = %5.2f\n", ToFloat(u, 100.0f)); 72 | 73 | Vec2f v2 = ToCircle(u); 74 | printf(" ToCircle(u) = %5.2f, %5.2f\n", v2.x, v2.y); 75 | 76 | Vec3f v3 = ToSphere(u); 77 | printf(" ToSphere(u) = %5.2f, %5.2f, %5.2f\n", v3.x, v3.y, v3.z); 78 | 79 | uint32_t u0 = u; 80 | 81 | printf(" u = ModTriangle(u):\n"); 82 | u = ModTriangle(u0); 83 | printf(" ToInt32(u, 10) = %2d\n", ToInt32(u, 10)); 84 | printf(" ToFloat(u, 100) = %5.2f\n", ToFloat(u, 100.0f)); 85 | 86 | printf(" u = ModWeighted(u, [2, 1, 0, 1]):\n"); 87 | u = ModWeighted(u0, sizeof(kWeights) / sizeof(kWeights[0]), kWeights); 88 | printf(" ToInt32(u, 10) = %2d\n", ToInt32(u, 10)); 89 | printf(" ToFloat(u, 100) = %5.2f\n", ToFloat(u, 100.0f)); 90 | } 91 | printf("\n"); 92 | 93 | // Test examples 94 | float score = ToFloat(generator, 1.0f, 100.0f); 95 | int modifier = ToInt32Signed(generator, 5); 96 | int dayOfYear = ToInt32Inclusive(generator, 1, 365); 97 | float weightKG = ToFloat(ModGaussLike(generator), 50.0f, 130.0f); 98 | 99 | printf("score = %4.1f, modifier = %2d, dayOfYear = %3d, weightKG = %5.1f\n", 100 | score , 101 | modifier , 102 | dayOfYear , 103 | weightKG 104 | ); 105 | 106 | Vec2f circleLoc = ToCircle(generator); 107 | Vec2f pixTentSample = ToSquare(ModTriangle(generator), ModTriangle(generator)); 108 | Vec3f rayDir = ToDirection3(generator); 109 | 110 | printf("circleLoc = [%g, %g]\n", circleLoc.x, circleLoc.y); 111 | printf("pixTentSample = [%g, %g]\n", pixTentSample.x, pixTentSample.y); 112 | printf("rayDir = [%g, %g, %g]\n", rayDir.x, rayDir.y, rayDir.z); 113 | 114 | // Create an SVG showing samples distributed over a circle with triangle-ramp-down 115 | // radial distribution. 116 | FILE* svgFile = fopen("distribute.svg", "w"); 117 | 118 | svg_header(svgFile, 768); 119 | float size = 768; 120 | svg_text(svgFile, Vec2f(0.0f, 20.0f), "Circle(u, ModTriangle(u))", "black"); 121 | 122 | svg_line(svgFile, Vec2f(0.0f, 0.0f), Vec2f(size, 0.0f)); 123 | svg_line(svgFile, Vec2f(size, 0.0f), Vec2f(size, size)); 124 | svg_line(svgFile, Vec2f(size, size), Vec2f(0.0f, size)); 125 | svg_line(svgFile, Vec2f(0.0f, size), Vec2f(0.0f, 0.0f)); 126 | 127 | for (int i = 0; i < numSamples; i++) 128 | { 129 | Vec2f v = ToCircle(generator, ModHalfDown(ModTriangle(generator))); // use theta/r version, so we can modulate just r 130 | v = 0.5f * (v + Vec2f(1.0f, 1.0f)); 131 | v = v * size; 132 | svg_circle(svgFile, v, 2.0f, "green"); 133 | } 134 | 135 | svg_trailer(svgFile); 136 | fclose(svgFile); 137 | 138 | return 0; 139 | } 140 | -------------------------------------------------------------------------------- /Generate.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // File: Generate.cpp 3 | // 4 | // Function: Various sample/number generators 5 | // 6 | // Copyright: Andrew Willmott, 2018 7 | // 8 | 9 | #include "Generate.h" 10 | 11 | using namespace DistLib; 12 | 13 | namespace 14 | { 15 | constexpr float kOneOverThree = float(1.0 / 3.0); 16 | constexpr float kOneOverFive = float(1.0 / 5.0); 17 | } 18 | 19 | float DistLib::HaltonFloat(int n, int b) 20 | /// return term i of the base b Halton sequence 21 | /// In fact, this is just a generalization of Heckbert's bit reversal distribution trick. 22 | /// E.g., when b=3, write n as a base 3 number, digit 0 -> which third of interval the 23 | /// sample is in, 1 -> which third of that, 2 -> which third of that, etc. 24 | { 25 | float result = 0; 26 | float ip = 1.0f / b; 27 | float p = ip; 28 | 29 | while (n > 0) 30 | { 31 | result += (n % b) * p; 32 | n = n / b; 33 | p *= ip; 34 | } 35 | 36 | return result; 37 | } 38 | 39 | uint32_t DistLib::HaltonUInt32(int n, int b) 40 | { 41 | uint32_t result = 0; 42 | uint32_t p = UINT32_MAX / b; 43 | 44 | while (n > 0) 45 | { 46 | result += (n % b) * p; 47 | n /= b; 48 | p /= b; 49 | } 50 | 51 | return result; 52 | } 53 | 54 | 55 | // --- cHalton2 -------------------------------------------------------- 56 | 57 | int cHalton2::Next() 58 | { 59 | ///////////////////////////////////// 60 | // base 2 61 | 62 | uint32_t oldBase2 = mBase2; 63 | mBase2++; 64 | uint32_t diff = mBase2 ^ oldBase2; 65 | 66 | // bottom bit always changes, higher bits 67 | // change less frequently. 68 | float s = 0.5; 69 | 70 | // diff will be of the form 0*1+, i.e. one bits up until the last carry. 71 | // expected iterations = 1 + 0.5 + 0.25 + ... = 2 72 | do 73 | { 74 | if (oldBase2 & 1) 75 | mU[0] -= s; 76 | else 77 | mU[0] += s; 78 | 79 | s *= 0.5; 80 | 81 | diff = diff >> 1; 82 | oldBase2 = oldBase2 >> 1; 83 | } 84 | while (diff); 85 | 86 | 87 | ///////////////////////////////////// 88 | // base 3: use 2 bits for each base 3 digit. 89 | 90 | uint32_t mask = 0x3; // also the max base 3 digit 91 | uint32_t add = 0x1; // amount to add to force carry once digit==3 92 | s = kOneOverThree; 93 | 94 | mBase3++; 95 | 96 | // expected iterations: 1.5 97 | while (1) 98 | { 99 | if ((mBase3 & mask) == mask) 100 | { 101 | mBase3 += add; // force carry into next 2-bit digit 102 | mU[1] -= 2 * s; 103 | 104 | mask = mask << 2; 105 | add = add << 2; 106 | 107 | s *= kOneOverThree; 108 | } 109 | else 110 | { 111 | mU[1] += s; // we know digit n has gone from a to a + 1 112 | break; 113 | } 114 | }; 115 | 116 | return mBase2; // return the index of this sequence point 117 | } 118 | 119 | void cHalton2::Set(int n) 120 | { 121 | mU[0] = Halton2Float(n); 122 | mBase2 = n; 123 | 124 | mU[1] = 0; 125 | mBase3 = 0; 126 | 127 | float ip = kOneOverThree; 128 | float p = ip; 129 | 130 | for (int i = 0, k = n; k; i += 2, k /= 3) 131 | { 132 | int d = (k % 3); 133 | mBase3 |= d << i; 134 | mU[1] += d * p; 135 | p *= ip; 136 | } 137 | } 138 | 139 | 140 | // --- cHalton3 -------------------------------------------------------- 141 | 142 | int cHalton3::Next() 143 | { 144 | // base 2: 1 bit per digit 145 | uint32_t oldBase2 = mBase2; 146 | mBase2++; 147 | uint32_t diff = mBase2 ^ oldBase2; 148 | 149 | // bottom bit always changes, higher bits 150 | // change less frequently. 151 | float s = 0.5; 152 | 153 | // diff will be of the form 0*1+, i.e. one bits up until the last carry. 154 | // expected iterations = 1 + 0.5 + 0.25 + ... = 2 155 | do 156 | { 157 | if (oldBase2 & 1) 158 | mU[0] -= s; 159 | else 160 | mU[0] += s; 161 | 162 | s *= 0.5; 163 | 164 | diff = diff >> 1; 165 | oldBase2 = oldBase2 >> 1; 166 | } 167 | while (diff); 168 | 169 | 170 | // base 3: use 2 bits for each base 3 digit. 171 | uint32_t mask = 0x3; // also the max base 3 digit 172 | uint32_t add = 0x1; // amount to add to force carry once digit==3 173 | s = kOneOverThree; 174 | 175 | mBase3++; 176 | 177 | // expected iterations: 1.5 178 | while (1) 179 | { 180 | if ((mBase3 & mask) == mask) 181 | { 182 | mBase3 += add; // force carry into next 2-bit digit 183 | mU[1] -= 2 * s; 184 | 185 | mask = mask << 2; 186 | add = add << 2; 187 | 188 | s *= kOneOverThree; 189 | } 190 | else 191 | { 192 | mU[1] += s; // we know digit n has gone from a to a + 1 193 | break; 194 | } 195 | }; 196 | 197 | // base 5: use 3 bits for each base 5 digit. 198 | mask = 0x7; 199 | add = 0x3; // amount to add to force carry once digit==dmax 200 | uint32_t dmax = 0x5; // max digit 201 | 202 | s = kOneOverFive; 203 | 204 | mBase5++; 205 | 206 | // expected iterations: 1.25 207 | while (1) 208 | { 209 | if ((mBase5 & mask) == dmax) 210 | { 211 | mBase5 += add; // force carry into next 3-bit digit 212 | mU[2] -= 4 * s; 213 | 214 | mask = mask << 3; 215 | dmax = dmax << 3; 216 | add = add << 3; 217 | 218 | s *= kOneOverFive; 219 | } 220 | else 221 | { 222 | mU[2] += s; // we know digit n has gone from a to a + 1 223 | break; 224 | } 225 | }; 226 | 227 | return mBase2; // return the index of this sequence point 228 | } 229 | 230 | void cHalton3::Set(int n) 231 | { 232 | mU[0] = Halton2Float(n); 233 | mBase2 = n; 234 | 235 | mU[1] = 0.0f; 236 | mBase3 = 0; 237 | 238 | float p = kOneOverThree; 239 | 240 | for (int i = 0, k = n; k; i += 2, k /= 3) 241 | { 242 | int d = (k % 3); 243 | mBase3 |= d << i; 244 | mU[1] += d * p; 245 | p *= kOneOverThree; 246 | } 247 | 248 | mU[2] = 0.0f; 249 | mBase5 = 0; 250 | 251 | p = kOneOverFive; 252 | 253 | for (int i = 0, k = n; k; i += 3, k /= 5) 254 | { 255 | int d = (k % 5); 256 | mBase5 |= d << i; 257 | mU[2] += d * p; 258 | p *= kOneOverFive; 259 | } 260 | } 261 | 262 | // --- cHalton2U -------------------------------------------------------- 263 | 264 | int cHalton2U::Next() 265 | { 266 | mBase2++; 267 | 268 | uint32_t mask = 0x1; 269 | uint32_t s = UINT32_MAX / 2; 270 | 271 | while (1) // expected iterations: 2 272 | { 273 | if ((mBase2 & mask) == 0) 274 | { 275 | mU[0] -= s; 276 | 277 | mask = mask << 1; 278 | 279 | s /= 2; 280 | } 281 | else 282 | { 283 | mU[0] += s; // we know digit n has gone from 0 to 1 284 | break; 285 | } 286 | }; 287 | 288 | mBase3++; 289 | 290 | mask = 0x3; // also the max base 3 digit 291 | uint32_t add = 0x1; // amount to add to force carry once digit==3 292 | s = UINT32_MAX / 3; 293 | 294 | while (1) // expected iterations: 1.5 295 | { 296 | if ((mBase3 & mask) == mask) 297 | { 298 | mBase3 += add; // force carry into next 2-bit digit 299 | mU[1] -= 2 * s; 300 | 301 | mask = mask << 2; 302 | add = add << 2; 303 | 304 | s /= 3; 305 | } 306 | else 307 | { 308 | mU[1] += s; // we know digit n has gone from x to x + 1 309 | break; 310 | } 311 | }; 312 | 313 | return mBase2; 314 | } 315 | 316 | void cHalton2U::Set(int n) 317 | { 318 | mU[0] = Halton2UInt32(n); 319 | mBase2 = n; 320 | 321 | mU[1] = 0; 322 | mBase3 = 0; 323 | 324 | uint32_t p = UINT32_MAX / 3; 325 | 326 | for (int i = 0, k = n; k; i += 2, k /= 3) 327 | { 328 | int d = (k % 3); 329 | mBase3 |= d << i; 330 | mU[1] += d * p; 331 | p /= 3; 332 | } 333 | } 334 | 335 | 336 | // --- cHalton3U -------------------------------------------------------- 337 | 338 | int cHalton3U::Next() 339 | { 340 | mBase2++; 341 | 342 | uint32_t mask = 0x1; 343 | uint32_t s = UINT32_MAX / 2; 344 | 345 | while (1) // expected iterations: 2 346 | { 347 | if ((mBase2 & mask) == 0) 348 | { 349 | mU[0] -= s; 350 | 351 | mask = mask << 1; 352 | 353 | s /= 2; 354 | } 355 | else 356 | { 357 | mU[0] += s; // we know digit n has gone from 0 to 1 358 | break; 359 | } 360 | }; 361 | 362 | mBase3++; 363 | 364 | mask = 0x3; // also the max base 3 digit 365 | uint32_t add = 0x1; // amount to add to force carry once digit==3 366 | s = UINT32_MAX / 3; 367 | 368 | while (1) // expected iterations: 1.5 369 | { 370 | if ((mBase3 & mask) == mask) 371 | { 372 | mBase3 += add; // force carry into next 2-bit digit 373 | mU[1] -= 2 * s; 374 | 375 | mask = mask << 2; 376 | add = add << 2; 377 | 378 | s /= 3; 379 | } 380 | else 381 | { 382 | mU[1] += s; // we know digit n has gone from x to x + 1 383 | break; 384 | } 385 | }; 386 | 387 | mBase5++; 388 | 389 | mask = 0x7; 390 | add = 0x3; // amount to add to force carry once digit==5 391 | uint32_t dmax = 5; 392 | s = UINT32_MAX / 5; 393 | 394 | while (1) // expected iterations: 1.25 395 | { 396 | if ((mBase5 & mask) == dmax) 397 | { 398 | mBase5 += add; // force carry into next 3-bit digit 399 | mU[2] -= 4 * s; 400 | 401 | mask = mask << 3; 402 | dmax = dmax << 3; 403 | add = add << 3; 404 | 405 | s /= 5; 406 | } 407 | else 408 | { 409 | mU[2] += s; // we know digit n has gone from a to a + 1 410 | break; 411 | } 412 | }; 413 | 414 | return mBase2; // return the index of this sequence point 415 | } 416 | 417 | void cHalton3U::Set(int n) 418 | { 419 | mU[0] = Halton2UInt32(n); 420 | mBase2 = n; 421 | 422 | mU[1] = 0; 423 | mBase3 = 0; 424 | 425 | uint32_t p = UINT32_MAX / 3; 426 | 427 | for (int i = 0, k = n; k; i += 2, k /= 3) 428 | { 429 | int d = (k % 3); 430 | mBase3 |= d << i; 431 | mU[1] += d * p; 432 | p /= 3; 433 | } 434 | 435 | mU[2] = 0; 436 | mBase5 = 0; 437 | 438 | p = UINT32_MAX / 5; 439 | 440 | for (int i = 0, k = n; k; i += 3, k /= 5) 441 | { 442 | int d = (k % 5); 443 | mBase5 |= d << i; 444 | mU[2] += d * p; 445 | p /= 5; 446 | } 447 | } 448 | 449 | -------------------------------------------------------------------------------- /Generate.h: -------------------------------------------------------------------------------- 1 | // 2 | // File: Generate.h 3 | // 4 | // Function: Various sample/number generators 5 | // 6 | // Copyright: Andrew Willmott, 2018 7 | // 8 | 9 | #ifndef GENERATE_H 10 | #define GENERATE_H 11 | 12 | #include 13 | #include 14 | 15 | namespace DistLib 16 | { 17 | //-------------------------------------------------------------------------- 18 | // LCG 19 | //-------------------------------------------------------------------------- 20 | 21 | struct cLCG 22 | { 23 | uint32_t mState = 0x12345678; 24 | 25 | cLCG() {}; 26 | cLCG(uint32_t seed) : mState(seed) {} 27 | 28 | uint32_t Next(); ///< Explicit next number in sequence 29 | operator uint32_t(); ///< Return next number in sequence in uint32_t context 30 | }; 31 | 32 | 33 | //-------------------------------------------------------------------------- 34 | // PCG 35 | //-------------------------------------------------------------------------- 36 | 37 | struct cPCG 38 | /// See http://www.pcg-random.org 39 | { 40 | uint64_t mState = 0x853c49e6748fea9bULL; 41 | uint64_t mInc = 0xda3e39cb94b95bdbULL; 42 | 43 | cPCG() {} 44 | cPCG(uint64_t initstate, uint64_t initseq); 45 | 46 | uint32_t Next(); ///< Explicit next number in sequence 47 | operator uint32_t(); ///< Return next number in sequence in uint32_t context 48 | }; 49 | 50 | 51 | //-------------------------------------------------------------------------- 52 | // XorShift 53 | //-------------------------------------------------------------------------- 54 | 55 | struct cXORShift 56 | /// See https://en.wikipedia.org/wiki/Xorshift 57 | { 58 | uint32_t mState = 0x12345678; 59 | 60 | cXORShift() {} 61 | cXORShift(uint32_t seed); 62 | 63 | uint32_t Next(); ///< Explicit next number in sequence 64 | operator uint32_t(); ///< Return next number in sequence in uint32_t context 65 | }; 66 | 67 | 68 | //-------------------------------------------------------------------------- 69 | // cHashGen 70 | //-------------------------------------------------------------------------- 71 | 72 | struct cHashGen 73 | /// Works by hashing a counter, which means it's random access, which may 74 | /// be necessary in some applications. Applies an LCG and then xorshift to 75 | /// the counter to avoid patterns. 76 | { 77 | uint32_t mIndex = 0; 78 | 79 | uint32_t Next(); ///< Advance to next point in the sequence. Returns the index of this point. 80 | void Set (int n); ///< Jump directly to term 'n' of the sequence 81 | operator uint32_t(); ///< Return next number in sequence in uint32_t context 82 | }; 83 | uint32_t HashInt(uint32_t i); ///< Hash used by above, in case direct use is more convenient 84 | 85 | 86 | //-------------------------------------------------------------------------- 87 | // Halton 88 | //-------------------------------------------------------------------------- 89 | 90 | float HaltonFloat (int i, int b); ///< return term i of the base b Halton sequence 91 | uint32_t HaltonUInt32(int i, int b); ///< return term i of the base b Halton sequence as fraction of UINT32_MAX 92 | 93 | float Halton2Float (int i); ///< return term i of the base 2 Halton sequence 94 | uint32_t Halton2UInt32(int i); ///< return term i of the base 2 Halton sequence as fraction of UINT32_MAX 95 | 96 | struct cHalton2 97 | /// This calculates the 2D Halton sequence incrementally, faster per-call than HaltonFloat(i, 2/3). 98 | { 99 | float mU[2] = { 0.0f, 0.0f }; 100 | 101 | uint32_t mBase2 = 0; 102 | uint32_t mBase3 = 0; 103 | 104 | int Next (); ///< Advance to next point in the sequence. Returns the index of this point. 105 | void Set (int n); ///< Jump directly to term 'n' of the sequence 106 | }; 107 | 108 | struct cHalton3 109 | /// This calculates the 3D Halton sequence incrementally, faster per-call than HaltonFloat(i, 2/3/5). 110 | { 111 | float mU[3] = { 0.0f, 0.0f, 0.0f }; 112 | 113 | uint32_t mBase2 = 0; 114 | uint32_t mBase3 = 0; 115 | uint32_t mBase5 = 0; 116 | 117 | int Next (); ///< Advance to next point in the sequence. Returns the index of this point. 118 | void Set (int n); ///< Jump directly to term 'n' of the sequence 119 | }; 120 | 121 | struct cHalton2U 122 | /// This calculates the 2D Halton sequence incrementally, faster per call than HaltonUInt32. 123 | { 124 | uint32_t mU[2] = { 0 }; ///< Current sample point 125 | 126 | uint32_t mBase2 = 0; 127 | uint32_t mBase3 = 0; 128 | 129 | int Next (); ///< Advance to next point in the sequence. Returns the index of this point. 130 | void Set (int n); ///< Jump directly to term 'n' of the sequence 131 | }; 132 | 133 | struct cHalton3U 134 | /// This calculates the 3D Halton sequence incrementally, faster per-call than HaltonUInt32(i, 2/3/5). 135 | { 136 | uint32_t mU[3] = { 0 }; ///< Current sample point 137 | 138 | uint32_t mBase2 = 0; 139 | uint32_t mBase3 = 0; 140 | uint32_t mBase5 = 0; 141 | 142 | int Next (); ///< Advance to next point in the sequence. Returns the index of this point. 143 | void Set (int n); ///< Jump directly to term 'n' of the sequence 144 | }; 145 | 146 | 147 | //-------------------------------------------------------------------------- 148 | // Fibonacci/Golden Spiral 149 | //-------------------------------------------------------------------------- 150 | 151 | float GoldenFloat (int i); ///< return term i of the golden angle sequence as float 152 | uint32_t GoldenUInt32(int i); ///< return term i of the golden angle sequence as fraction of UINT32_MAX 153 | 154 | struct cGolden2U 155 | { 156 | uint32_t mU[2] = { 0 }; ///< Current sample point 157 | uint32_t mStep; 158 | 159 | cGolden2U(int numSamples); 160 | 161 | void Next(); ///< Advance to next point in the sequence. 162 | void Set(int n); ///< Jump directly to term 'n' of the sequence 163 | }; 164 | 165 | 166 | //-------------------------------------------------------------------------- 167 | // Generalised Kronecker/golden ratio combination, Rd. 168 | // See http://extremelearning.com.au/unreasonable-effectiveness-of-quasirandom-sequences/ 169 | //-------------------------------------------------------------------------- 170 | 171 | struct cR1U 172 | { 173 | uint32_t mU = UINT32_MAX / 2; ///< Current sample point 174 | 175 | void Next(); ///< Advance to next point in the sequence. 176 | void Set(int n); ///< Jump directly to term 'n' of the sequence 177 | 178 | operator uint32_t(); ///< Return next number in sequence in uint32_t context 179 | }; 180 | 181 | struct cR2U 182 | { 183 | uint32_t mU[2] = { UINT32_MAX / 2, UINT32_MAX / 2 }; ///< Current sample point 184 | 185 | void Next(); ///< Advance to next point in the sequence. 186 | void Set(int n); ///< Jump directly to term 'n' of the sequence 187 | }; 188 | 189 | struct cR3U 190 | { 191 | uint32_t mU[3] = { UINT32_MAX / 2, UINT32_MAX / 2, UINT32_MAX / 2 }; ///< Current sample point 192 | 193 | void Next(); ///< Advance to next point in the sequence. 194 | void Set(int n); ///< Jump directly to term 'n' of the sequence 195 | }; 196 | 197 | 198 | //-------------------------------------------------------------------------- 199 | // Jittered version of R2. 200 | // See http://extremelearning.com.au/a-simple-method-to-construct-isotropic-quasirandom-blue-noise-point-sequences/ 201 | //-------------------------------------------------------------------------- 202 | 203 | struct cR2JitterU 204 | { 205 | uint32_t mU[2] = { 0 }; ///< Current sample point 206 | 207 | cR2U mR; ///< R2 sequence 208 | cHashGen mJ; ///< Jitter 209 | float mS; ///< Scale term 210 | 211 | cR2JitterU(float lambda = 1.0f); ///< Lambda controls jitter amount 212 | 213 | void Next(); ///< Advance to next point in sequence 214 | void Set(int n); ///< Jump directly to term 'n' of the sequence 215 | }; 216 | 217 | 218 | 219 | // --- Inlines ------------------------------------------------------------- 220 | 221 | inline float Halton2Float(int i) 222 | { 223 | uint32_t u = Halton2UInt32(i); 224 | 225 | uint32_t resultU(0x3f800000 | (u >> 9)); 226 | float result = ((float&) resultU); 227 | return result - 1.0f; 228 | } 229 | 230 | inline uint32_t Halton2UInt32(int a) 231 | { 232 | uint32_t b; 233 | 234 | b = ((a & 0x55555555) << 1) | ((a & 0xAAAAAAAA) >> 1); 235 | a = ((b & 0x33333333) << 2) | ((b & 0xCCCCCCCC) >> 2); 236 | b = ((a & 0x0F0F0F0F) << 4) | ((a & 0xF0F0F0F0) >> 4); 237 | a = ((b & 0x00FF00FF) << 8) | ((b & 0xFF00FF00) >> 8); 238 | b = ((a & 0x0000FFFF) << 16) | ((a & 0xFFFF0000) >> 16); 239 | 240 | return b; 241 | } 242 | 243 | inline uint32_t cLCG::Next() 244 | { 245 | uint32_t oldState = mState; 246 | mState = uint32_t(mState * uint64_t(1103515245) + 12345); 247 | return oldState; 248 | } 249 | 250 | inline cLCG::operator uint32_t() 251 | { 252 | return Next(); 253 | } 254 | 255 | inline cPCG::cPCG(uint64_t initstate, uint64_t initseq) : mState(0), mInc((initseq << 1u) | 1u) 256 | { 257 | Next(); 258 | mState += initstate; 259 | Next(); 260 | } 261 | 262 | inline uint32_t cPCG::Next() 263 | { 264 | uint64_t oldState = mState; 265 | mState = oldState * 6364136223846793005ULL + mInc; 266 | 267 | uint32_t xorShifted = uint32_t(((oldState >> 18u) ^ oldState) >> 27u); 268 | uint32_t rot = oldState >> 59u; 269 | 270 | return (xorShifted >> rot) | (xorShifted << ((-int32_t(rot)) & 31)); // int32_t cast added as latest VS treats -u as error by default \o/ 271 | } 272 | 273 | inline cPCG::operator uint32_t() 274 | { 275 | return Next(); 276 | } 277 | 278 | inline cXORShift::cXORShift(uint32_t seed) : mState(seed) 279 | {} 280 | 281 | inline uint32_t cXORShift::Next() 282 | { 283 | mState ^= (mState << 13); 284 | mState ^= (mState >> 17); 285 | mState ^= (mState << 5); 286 | return mState; 287 | } 288 | 289 | inline cXORShift::operator uint32_t() 290 | { 291 | return Next(); 292 | } 293 | 294 | inline uint32_t HashInt(uint32_t i) 295 | { 296 | uint32_t hash = i * 1103515245 + 12345; 297 | hash ^= (hash << 13); 298 | hash ^= (hash >> 17); 299 | hash ^= (hash << 5); 300 | return hash; 301 | } 302 | 303 | inline void cHashGen::Set(int i) 304 | { 305 | mIndex = i; 306 | } 307 | 308 | inline uint32_t cHashGen::Next() 309 | { 310 | return HashInt(mIndex++); 311 | } 312 | 313 | inline cHashGen::operator uint32_t() 314 | { 315 | return Next(); 316 | } 317 | 318 | const float kGoldenF32 = 0.5f * (sqrtf(5.0f) - 1.0f); 319 | const uint32_t kGoldenU32 = uint32_t(UINT32_MAX * kGoldenF32); 320 | 321 | inline float GoldenFloat(int i) 322 | { 323 | float f = i * kGoldenF32; 324 | return f - floorf(f); 325 | } 326 | 327 | inline uint32_t GoldenUInt32(int i) 328 | { 329 | return uint32_t(i * kGoldenU32); 330 | } 331 | 332 | 333 | inline cGolden2U::cGolden2U(int numSamples) : 334 | mStep(UINT32_MAX / numSamples) 335 | { 336 | mU[1] = mStep / 2; 337 | } 338 | 339 | inline void cGolden2U::Next() 340 | { 341 | mU[0] += kGoldenU32; 342 | mU[1] += mStep; 343 | } 344 | 345 | inline void cGolden2U::Set(int i) 346 | { 347 | mU[0] = uint32_t(i * kGoldenU32); 348 | mU[1] = i * mStep + mStep / 2; 349 | } 350 | 351 | 352 | constexpr float kG1 = 1.6180339887498948482f; 353 | constexpr float kR1xF32 = 1.0f / kG1; 354 | constexpr uint32_t kR1xU32 = uint32_t(UINT32_MAX * kR1xF32); // Same as kGoldenU32, the Rn sequences are a generalisation of this 355 | 356 | inline void cR1U::Next() 357 | { 358 | mU += kR1xU32; 359 | } 360 | 361 | inline void cR1U::Set(int i) 362 | { 363 | mU = uint32_t(UINT32_MAX / 2 + i * kR1xU32); 364 | } 365 | 366 | inline cR1U::operator uint32_t() 367 | { 368 | uint32_t u = mU; 369 | mU += kR1xU32; 370 | return u; 371 | } 372 | 373 | constexpr float kG2 = 1.32471795724474602596f; 374 | constexpr float kR2xF32 = 1.0f / kG2; 375 | constexpr float kR2yF32 = 1.0f / (kG2 * kG2); 376 | constexpr uint32_t kR2xU32 = uint32_t(UINT32_MAX * kR2xF32); 377 | constexpr uint32_t kR2yU32 = uint32_t(UINT32_MAX * kR2yF32); 378 | 379 | inline void cR2U::Next() 380 | { 381 | mU[0] += kR2xU32; 382 | mU[1] += kR2yU32; 383 | } 384 | 385 | inline void cR2U::Set(int i) 386 | { 387 | mU[0] = uint32_t(UINT32_MAX / 2 + i * kR2xU32); 388 | mU[1] = uint32_t(UINT32_MAX / 2 + i * kR2yU32); 389 | } 390 | 391 | constexpr float kG3 = 1.22074408460575947536f; 392 | constexpr float kR3xF32 = 1.0f / kG3; 393 | constexpr float kR3yF32 = 1.0f / (kG3 * kG3); 394 | constexpr float kR3zF32 = 1.0f / (kG3 * kG3 * kG3); 395 | constexpr uint32_t kR3xU32 = uint32_t(UINT32_MAX * kR3xF32); 396 | constexpr uint32_t kR3yU32 = uint32_t(UINT32_MAX * kR3yF32); 397 | constexpr uint32_t kR3zU32 = uint32_t(UINT32_MAX * kR3zF32); 398 | 399 | inline void cR3U::Next() 400 | { 401 | mU[0] += kR3xU32; 402 | mU[1] += kR3yU32; 403 | mU[2] += kR3zU32; 404 | } 405 | 406 | inline void cR3U::Set(int i) 407 | { 408 | mU[0] = uint32_t(UINT32_MAX / 2 + i * kR3xU32); 409 | mU[1] = uint32_t(UINT32_MAX / 2 + i * kR3yU32); 410 | mU[2] = uint32_t(UINT32_MAX / 2 + i * kR3zU32); 411 | } 412 | 413 | constexpr float kR2dF32 = 0.76f * 1.772453851f / 4.0f; // 1.77245 is sqrt(pi), as C++11 didn't make sqrt constexpr \o/ 414 | constexpr uint32_t kR2dU32 = uint32_t(UINT32_MAX * kR2dF32); 415 | 416 | inline cR2JitterU::cR2JitterU(float lambda) : 417 | mS(kR2dU32 * lambda) 418 | { 419 | uint64_t si = uint64_t(mS / sqrtf(0.3f)); 420 | 421 | mU[0] = mR.mU[0] + ((si * mJ.Next()) >> 32); 422 | mU[1] = mR.mU[1] + ((si * mJ.Next()) >> 32); 423 | } 424 | 425 | inline void cR2JitterU::Next() 426 | { 427 | mR.Next(); 428 | mJ.mIndex += 2; 429 | 430 | uint64_t si = uint64_t(mS / sqrtf(mJ.mIndex / 2 + 0.3f)); 431 | 432 | mU[0] = mR.mU[0] + ((si * mJ.Next()) >> 32); 433 | mU[1] = mR.mU[1] + ((si * mJ.Next()) >> 32); 434 | } 435 | 436 | inline void cR2JitterU::Set(int n) 437 | { 438 | mR.Set(n); 439 | mJ.Set(2 * n); 440 | 441 | uint64_t si = uint64_t(mS / sqrtf(n + 0.3f)); 442 | 443 | mU[0] = mR.mU[0] + ((si * mJ.Next()) >> 32); 444 | mU[1] = mR.mU[1] + ((si * mJ.Next()) >> 32); 445 | } 446 | } 447 | 448 | #endif 449 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | DistributeLib 2 | ============= 3 | 4 | A single-source-file library for converting 32-bit integer inputs into various 5 | 1D, 2D, and 3D distributions and types. The intended use is to convert the 6 | output of an RNG into various more useful forms. 7 | 8 | Features: 9 | 10 | * Generation of int, float, 2D and 3D samples over various shapes 11 | * Cheap routines that only take one input number, but may be less accurate or 12 | controllable 13 | * More accurate routines that take a full number of input samples, e.g., three 14 | for a cube 15 | * Pre-modulation of inputs into triangular, gaussian-like, and weighted forms 16 | * Gaussian (normal) distribution as both a cheap approximation and via full 17 | Box-Muller transform 18 | * Optional generators supplying LCG/PCG/XorShift/Hash/Halton/Golden/'R' 19 | sequences. 20 | 21 | To build and run the test app: 22 | 23 | c++ --std=c++11 Distribute.cpp DistributeTest.cpp -o distribute && ./distribute 200 24 | 25 | Or add those files to your favourite IDE. 26 | 27 | 28 | Examples 29 | -------- 30 | 31 | Assuming 'rng' returns the next sample via operator uint32_t(), usage can be as 32 | simple as below, otherwise substitute rng() or whatever other syntax is 33 | appropriate for your sample generator. 34 | 35 | 1D: 36 | 37 | float score = ToFloat(rng, 1.0f, 100.0f); 38 | int modifier = ToInt32Signed(rng, 5); 39 | int dayOfYear = ToInt32Inclusive(rng, 1, 365); 40 | float weightKG = ToFloat(ModGaussLike(rng), 50.0f, 130.0f); 41 | 42 | 2D/3D: 43 | 44 | Vec2f circleLoc = ToCircle(rng); 45 | Vec2f pixTentSample = ToSquare(ModTriangle(rng), ModTriangle(rng)); 46 | Vec3f rayDir = ToDirection3(rng); 47 | 48 | Warning: do not simply use rand() to feed these functions, as, in addition to 49 | its low quality, its range is not guaranteed to be a full 32 bits. 50 | 51 | If you don't already have your own preferred RNG routines, the additional files 52 | Generate.h/cpp provide LCG, PCG, Halton, "Golden" (Spiral), and "R" generators, 53 | in a form that interoperates cleanly with Distribute.h. 54 | 55 | 56 | Output 57 | ------ 58 | 59 | * 1D: float [-1, 1], integer [-10, 10], with a simple LCG input. 60 | 61 | ![](images/float.png "Float [-1, 1]") 62 | ![](images/int.png "Integer [-10, 10]") 63 | 64 | * 2D: square, ring, triangle 65 | 66 | ![](images/square.png "square") 67 | ![](images/ring.png "ring") 68 | ![](images/triangle.png "triangle") 69 | 70 | 71 | * Triangle Modifier: integer, float, square in both dimensions 72 | 73 | ![](images/int-triangle.png "Integer triangular distribution") 74 | ![](images/float-triangle.png "Float triangular distribution") 75 | ![](images/square-triangle.png "Square triangular distribution") 76 | 77 | * Gauss-like Modifier: integer, square, and full gaussian for comparison. 78 | 79 | ![](images/int-gauss-like.png "Gauss-like integer -10 -- 10") 80 | ![](images/square-gauss-like.png "Gauss-like over the square") 81 | ![](images/gaussian.png "Full Gaussian") 82 | 83 | * Weighted Modifier: float and circle weighted by [1, 8, 0, 4, 1] 84 | 85 | ![](images/float-weighted.png "Float weighted by 1, 8, 0, 4, 1") 86 | ![](images/circle-radially-weighted.png "Circle radially weighted") 87 | 88 | * Non-random inputs: Halton 100, 1000, 5000 samples 89 | 90 | ![](images/halton-100.png "Halton 2D 100 points") 91 | ![](images/halton-1000.png "Halton 2D 1000 points") 92 | ![](images/halton-5000.png "Halton 2D 5000 points") 93 | 94 | * Non-random inputs: Golden ratio square, circle, and sphere surface 95 | 96 | ![](images/square-golden.png "Golden Square") 97 | ![](images/circle-golden.png "Golden Circle") 98 | ![](images/sphere-surface-golden.png "Golden Sphere Surface") 99 | 100 | * Progressively adding samples from the PCG, Halton, and Golden sequences 101 | 102 | PCG Circle 103 | Halton Circle 104 | Golden (Spiral) Circle 105 | 106 | * Progressively adding samples from the Rd sequence: square, circle, cube 107 | 108 | Rd Square 109 | Rd Circle 110 | Rd Cube 111 | -------------------------------------------------------------------------------- /images/circle-golden-anim.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/circle-golden-anim.gif -------------------------------------------------------------------------------- /images/circle-golden.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/circle-golden.png -------------------------------------------------------------------------------- /images/circle-halton-anim.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/circle-halton-anim.gif -------------------------------------------------------------------------------- /images/circle-pcg-anim.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/circle-pcg-anim.gif -------------------------------------------------------------------------------- /images/circle-radially-weighted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/circle-radially-weighted.png -------------------------------------------------------------------------------- /images/circle-rd-anim.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/circle-rd-anim.gif -------------------------------------------------------------------------------- /images/cube-rd-anim.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/cube-rd-anim.gif -------------------------------------------------------------------------------- /images/float-triangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/float-triangle.png -------------------------------------------------------------------------------- /images/float-weighted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/float-weighted.png -------------------------------------------------------------------------------- /images/float.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/float.png -------------------------------------------------------------------------------- /images/gaussian.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/gaussian.png -------------------------------------------------------------------------------- /images/halton-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/halton-100.png -------------------------------------------------------------------------------- /images/halton-1000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/halton-1000.png -------------------------------------------------------------------------------- /images/halton-5000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/halton-5000.png -------------------------------------------------------------------------------- /images/int-gauss-like.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/int-gauss-like.png -------------------------------------------------------------------------------- /images/int-triangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/int-triangle.png -------------------------------------------------------------------------------- /images/int.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/int.png -------------------------------------------------------------------------------- /images/ring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/ring.png -------------------------------------------------------------------------------- /images/sphere-surface-golden.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/sphere-surface-golden.png -------------------------------------------------------------------------------- /images/sphere-surface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/sphere-surface.png -------------------------------------------------------------------------------- /images/square-gauss-like.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/square-gauss-like.png -------------------------------------------------------------------------------- /images/square-golden.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/square-golden.png -------------------------------------------------------------------------------- /images/square-rd-anim.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/square-rd-anim.gif -------------------------------------------------------------------------------- /images/square-triangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/square-triangle.png -------------------------------------------------------------------------------- /images/square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/square.png -------------------------------------------------------------------------------- /images/triangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewwillmott/distribute-lib/c74c0cedee6c48e7c9694c87ce62f8c8e256ce50/images/triangle.png --------------------------------------------------------------------------------