[534] | 1 | // Copyright (C) 2012-2017 ChaosForge Ltd
|
---|
[175] | 2 | // http://chaosforge.org/
|
---|
| 3 | //
|
---|
[395] | 4 | // This file is part of Nova libraries.
|
---|
| 5 | // For conditions of distribution and use, see copying.txt file in root folder.
|
---|
[175] | 6 |
|
---|
[319] | 7 | #include "nv/core/random.hh"
|
---|
| 8 | #include "nv/core/time.hh"
|
---|
[443] | 9 | #include "nv/stl/utility/common.hh"
|
---|
[175] | 10 |
|
---|
| 11 | using namespace nv;
|
---|
| 12 |
|
---|
[443] | 13 | static const uint32 mt_upper_mask = 0x80000000UL;
|
---|
| 14 | static const uint32 mt_lower_mask = 0x7FFFFFFFUL;
|
---|
| 15 | static const uint32 mt_full_mask = 0xFFFFFFFFUL;
|
---|
| 16 | static const uint32 mt_matrix_a = 0x9908B0DFUL;
|
---|
[175] | 17 |
|
---|
[533] | 18 | #define NV_MT_MIXBITS(u, v) ( uint32( (u) & mt_upper_mask) | uint32( (v) & mt_lower_mask) )
|
---|
| 19 | #define NV_MT_TWIST(u, v) ( uint32(NV_MT_MIXBITS(u, v) >> uint32(1)) ^ uint32( (v) & uint32(1) ? mt_matrix_a : uint32(0)) )
|
---|
[443] | 20 |
|
---|
[503] | 21 | nv::random& random::get()
|
---|
[175] | 22 | {
|
---|
[503] | 23 | static random default_rng;
|
---|
| 24 | return default_rng;
|
---|
| 25 | }
|
---|
| 26 |
|
---|
| 27 | void random_mersenne::mt_init( uint32 seed )
|
---|
| 28 | {
|
---|
[471] | 29 | m_state[0] = static_cast<uint32>( seed & mt_full_mask );
|
---|
[487] | 30 | for ( uint32 i = 1; i < mersenne_n; i++ )
|
---|
[443] | 31 | {
|
---|
| 32 | m_state[i] = ( 1812433253UL * ( m_state[i - 1] ^ ( m_state[i - 1] >> 30 ) ) + i );
|
---|
| 33 | m_state[i] &= mt_full_mask;
|
---|
| 34 | }
|
---|
[175] | 35 |
|
---|
[443] | 36 | m_remaining = 0;
|
---|
| 37 | m_next = nullptr;
|
---|
| 38 | m_seeded = 1;
|
---|
[175] | 39 | }
|
---|
| 40 |
|
---|
[443] | 41 |
|
---|
[503] | 42 | void random_mersenne::mt_update()
|
---|
[175] | 43 | {
|
---|
[443] | 44 | uint32 *p = m_state;
|
---|
[533] | 45 | constexpr int m = mersenne_m;
|
---|
| 46 | constexpr int n = mersenne_n;
|
---|
[175] | 47 |
|
---|
[533] | 48 | for ( int count = ( n - m + 1 ); --count; p++ )
|
---|
[443] | 49 | *p = p[mersenne_m] ^ NV_MT_TWIST( p[0], p[1] );
|
---|
| 50 |
|
---|
| 51 | for ( int count = mersenne_m; --count; p++ )
|
---|
[533] | 52 | {
|
---|
| 53 | *p = p[m - n] ^ NV_MT_TWIST( p[0], p[1] );
|
---|
| 54 | }
|
---|
[443] | 55 |
|
---|
[533] | 56 | *p = p[m - n] ^ NV_MT_TWIST( p[0], m_state[0] );
|
---|
[443] | 57 |
|
---|
| 58 | m_remaining = mersenne_n;
|
---|
| 59 | m_next = m_state;
|
---|
[175] | 60 | }
|
---|
| 61 |
|
---|
[443] | 62 |
|
---|
[503] | 63 | uint32 random_mersenne::mt_uint32()
|
---|
[175] | 64 | {
|
---|
[443] | 65 | uint32 r;
|
---|
[175] | 66 |
|
---|
[443] | 67 | if ( !m_remaining ) mt_update();
|
---|
| 68 |
|
---|
| 69 | r = *m_next++;
|
---|
| 70 | m_remaining--;
|
---|
| 71 |
|
---|
| 72 | r ^= ( r >> 11 );
|
---|
| 73 | r ^= ( r << 7 ) & 0x9D2C5680UL;
|
---|
| 74 | r ^= ( r << 15 ) & 0xEFC60000UL;
|
---|
| 75 | r ^= ( r >> 18 );
|
---|
| 76 |
|
---|
| 77 | r &= mt_full_mask;
|
---|
| 78 |
|
---|
| 79 | return r;
|
---|
[175] | 80 | }
|
---|
| 81 |
|
---|
[503] | 82 | random_mersenne::random_mersenne( random_mersenne::seed_type seed /*= 0 */ )
|
---|
[487] | 83 | : m_next( nullptr ), m_remaining( 0 ), m_seeded( 0 )
|
---|
[175] | 84 | {
|
---|
[503] | 85 | mt_init( seed );
|
---|
[175] | 86 | }
|
---|
| 87 |
|
---|
[503] | 88 | random_mersenne::seed_type random_mersenne::set_seed( random_mersenne::seed_type seed /*= 0 */ )
|
---|
[175] | 89 | {
|
---|
[443] | 90 | mt_init( seed );
|
---|
| 91 | return seed;
|
---|
[175] | 92 | }
|
---|
| 93 |
|
---|
[503] | 94 | random_mersenne::result_type random_mersenne::rand()
|
---|
[175] | 95 | {
|
---|
[443] | 96 | return mt_uint32();
|
---|
[175] | 97 | }
|
---|
| 98 |
|
---|
[503] | 99 | random_base::seed_type random_base::randomized_seed()
|
---|
[175] | 100 | {
|
---|
[372] | 101 | // TODO: this seems off, as it might often seed the same, use general time
|
---|
| 102 | // instead
|
---|
[175] | 103 | return narrow_cast< seed_type >( get_ticks() );
|
---|
| 104 | }
|
---|
[308] | 105 |
|
---|
[503] | 106 | nv::vec2 nv::random_base::precise_unit_vec2()
|
---|
[308] | 107 | {
|
---|
[451] | 108 | f32 angle = frand( math::pi<f32>() * 2.f );
|
---|
[471] | 109 | return vec2( cos( angle ), sin( angle ) );
|
---|
[308] | 110 | }
|
---|
| 111 |
|
---|
[503] | 112 | nv::vec3 nv::random_base::precise_unit_vec3()
|
---|
[308] | 113 | {
|
---|
[451] | 114 | f32 cos_theta = frange( -1.0f, 1.0f );
|
---|
[471] | 115 | f32 sin_theta = sqrt( 1.0f - cos_theta * cos_theta );
|
---|
[451] | 116 | f32 phi = frand( 2 * math::pi<f32>() );
|
---|
[308] | 117 | return vec3(
|
---|
[471] | 118 | sin_theta * sin(phi),
|
---|
| 119 | sin_theta * cos(phi),
|
---|
[308] | 120 | cos_theta
|
---|
| 121 | );
|
---|
| 122 | }
|
---|
| 123 |
|
---|
[503] | 124 | nv::vec2 nv::random_base::fast_disk_point()
|
---|
[308] | 125 | {
|
---|
[451] | 126 | f32 r1 = frand();
|
---|
| 127 | f32 r2 = frand();
|
---|
[443] | 128 | if ( r1 > r2 ) swap( r1, r2 );
|
---|
[451] | 129 | f32 rf = 2* math::pi<f32>()*(r1/r2);
|
---|
[471] | 130 | return vec2( r2*cos( rf ), r2*sin( rf ) );
|
---|
[308] | 131 | }
|
---|
| 132 |
|
---|
[503] | 133 | nv::vec2 nv::random_base::precise_disk_point()
|
---|
[308] | 134 | {
|
---|
[471] | 135 | f32 r = sqrt( frand() );
|
---|
[451] | 136 | f32 rangle = frand( math::pi<f32>() );
|
---|
[471] | 137 | return vec2( r*cos( rangle ), r*sin( rangle ) );
|
---|
[308] | 138 | }
|
---|
| 139 |
|
---|
[503] | 140 | nv::vec3 nv::random_base::fast_sphere_point()
|
---|
[308] | 141 | {
|
---|
[451] | 142 | f32 rad = frand();
|
---|
| 143 | f32 pi = math::pi<f32>();
|
---|
[471] | 144 | f32 phi = asin( frange( -1.0f, 1.0f ) ) + pi*.5f;
|
---|
[451] | 145 | f32 theta = frange( 0.0f, 2 * math::pi<f32>() );
|
---|
[471] | 146 | f32 sin_phi = sin( phi );
|
---|
[308] | 147 | return vec3(
|
---|
[471] | 148 | rad * cos(theta) * sin_phi,
|
---|
| 149 | rad * sin(theta) * sin_phi,
|
---|
| 150 | rad * cos(phi)
|
---|
[308] | 151 | );
|
---|
| 152 | }
|
---|
| 153 |
|
---|
[503] | 154 | nv::vec3 nv::random_base::precise_sphere_point()
|
---|
[308] | 155 | {
|
---|
[471] | 156 | f32 radius = pow( frand(), 1.f/3.f );
|
---|
[451] | 157 | f32 cos_theta = frange( -1.0f, 1.0f );
|
---|
[471] | 158 | f32 sin_theta = sqrt( 1.0f - cos_theta * cos_theta );
|
---|
[451] | 159 | f32 phi = frange( 0.0f, 2 * math::pi<f32>() );
|
---|
[308] | 160 | return vec3(
|
---|
[471] | 161 | radius * sin_theta * sin(phi),
|
---|
| 162 | radius * sin_theta * cos(phi),
|
---|
[308] | 163 | radius * cos_theta
|
---|
| 164 | );
|
---|
| 165 | }
|
---|
| 166 |
|
---|
[503] | 167 | nv::vec2 nv::random_base::precise_ellipse_point( const vec2& radii )
|
---|
[308] | 168 | {
|
---|
[443] | 169 | vec2 p = range( -radii, radii );
|
---|
[308] | 170 | vec2 inv_radii = 1.f / radii;
|
---|
| 171 | vec2 inv_radii2 = inv_radii * inv_radii;
|
---|
| 172 | for ( uint32 i = 0; i < 12; ++i )
|
---|
| 173 | {
|
---|
[443] | 174 | if ( p.x * p.x * inv_radii2.x + p.y * p.y * inv_radii2.y <= 1.f )
|
---|
[308] | 175 | {
|
---|
[443] | 176 | return p;
|
---|
[308] | 177 | }
|
---|
| 178 | }
|
---|
| 179 | return fast_disk_point() * radii;
|
---|
| 180 | }
|
---|
| 181 |
|
---|
[503] | 182 | nv::vec3 nv::random_base::precise_ellipsoid_point( const vec3& radii )
|
---|
[308] | 183 | {
|
---|
[443] | 184 | vec3 p = range( -radii, radii );
|
---|
[308] | 185 | vec3 inv_radii = 1.f / radii;
|
---|
| 186 | vec3 inv_radii2 = inv_radii * inv_radii;
|
---|
| 187 | for ( uint32 i = 0; i < 12; ++i )
|
---|
| 188 | {
|
---|
[443] | 189 | if ( p.x * p.x * inv_radii2.x + p.y * p.y * inv_radii2.y + p.z * p.z * inv_radii2.z <= 1.f )
|
---|
[308] | 190 | {
|
---|
[443] | 191 | return p;
|
---|
[308] | 192 | }
|
---|
| 193 | }
|
---|
| 194 | return fast_sphere_point() * radii;
|
---|
| 195 | }
|
---|
| 196 |
|
---|
[503] | 197 | nv::vec2 nv::random_base::fast_hollow_disk_point( f32 iradius, f32 oradius )
|
---|
[308] | 198 | {
|
---|
[451] | 199 | f32 idist2 = iradius * iradius;
|
---|
| 200 | f32 odist2 = oradius * oradius;
|
---|
[471] | 201 | f32 rdist = sqrt( frange( idist2, odist2 ) );
|
---|
[308] | 202 | return rdist * precise_unit_vec2();
|
---|
| 203 | }
|
---|
| 204 |
|
---|
[503] | 205 | nv::vec2 nv::random_base::precise_hollow_disk_point( f32 iradius, f32 oradius )
|
---|
[308] | 206 | {
|
---|
| 207 | return fast_hollow_disk_point( iradius, oradius );
|
---|
| 208 | }
|
---|
| 209 |
|
---|
[503] | 210 | nv::vec3 nv::random_base::fast_hollow_sphere_point( f32 iradius, f32 oradius )
|
---|
[308] | 211 | {
|
---|
[451] | 212 | f32 idist3 = iradius * iradius * iradius;
|
---|
| 213 | f32 odist3 = oradius * oradius * oradius;
|
---|
[471] | 214 | f32 rdist = pow( frange( idist3, odist3 ), 1.f/3.f );
|
---|
[308] | 215 | return rdist * precise_unit_vec3();
|
---|
| 216 | }
|
---|
| 217 |
|
---|
[503] | 218 | nv::vec3 nv::random_base::precise_hollow_sphere_point( f32 iradius, f32 oradius )
|
---|
[308] | 219 | {
|
---|
| 220 | return fast_hollow_sphere_point( iradius, oradius );
|
---|
| 221 | }
|
---|
| 222 |
|
---|
| 223 |
|
---|
[503] | 224 | nv::vec2 nv::random_base::fast_hollow_ellipse_point( const vec2& iradii, const vec2& oradii )
|
---|
[308] | 225 | {
|
---|
[372] | 226 | vec2 iradii2 = iradii * iradii;
|
---|
[308] | 227 | vec2 opoint = ellipse_edge( oradii );
|
---|
| 228 | vec2 opoint2 = opoint * opoint;
|
---|
[454] | 229 | vec2 odir = math::normalize( opoint );
|
---|
[451] | 230 | f32 odist2 = opoint2.x + opoint2.y;
|
---|
[308] | 231 |
|
---|
[451] | 232 | f32 low = iradii2.y * opoint2.x + iradii2.x * opoint2.y;
|
---|
| 233 | f32 idist2 = ((iradii2.x * iradii2.y) / low ) * odist2;
|
---|
[308] | 234 |
|
---|
[471] | 235 | f32 rdist = sqrt( frange( idist2, odist2 ) );
|
---|
[308] | 236 | return odir * rdist;
|
---|
| 237 | }
|
---|
| 238 |
|
---|
[503] | 239 | nv::vec2 nv::random_base::precise_hollow_ellipse_point( const vec2& iradii, const vec2& oradii )
|
---|
[308] | 240 | {
|
---|
| 241 | return fast_hollow_ellipse_point( iradii, oradii );
|
---|
| 242 | }
|
---|
| 243 |
|
---|
[503] | 244 | nv::vec3 nv::random_base::fast_hollow_ellipsoid_point( const vec3& iradii, const vec3& oradii )
|
---|
[308] | 245 | {
|
---|
[372] | 246 | vec3 iradii2 = iradii * iradii;
|
---|
[308] | 247 | vec3 opoint = ellipsoid_edge( oradii );
|
---|
| 248 | vec3 opoint2 = opoint * opoint;
|
---|
[454] | 249 | vec3 odir = math::normalize( opoint );
|
---|
[451] | 250 | f32 odist2 = opoint2.x + opoint2.y + opoint2.z;
|
---|
[308] | 251 |
|
---|
[451] | 252 | f32 low =
|
---|
[308] | 253 | iradii2.y * iradii2.z * opoint2.x +
|
---|
| 254 | iradii2.x * iradii2.z * opoint2.y +
|
---|
| 255 | iradii2.x * iradii2.y * opoint2.z;
|
---|
[451] | 256 | f32 idist2 = ((iradii2.x * iradii2.y * iradii2.z) / low ) * odist2;
|
---|
[308] | 257 |
|
---|
[471] | 258 | f32 odist3 = odist2 * sqrt( odist2 );
|
---|
| 259 | f32 idist3 = idist2 * sqrt( idist2 );
|
---|
[308] | 260 |
|
---|
[471] | 261 | f32 rdist = pow( frange( idist3, odist3 ), 1.f/3.f );
|
---|
[308] | 262 | return odir * rdist;
|
---|
| 263 | }
|
---|
| 264 |
|
---|
[503] | 265 | nv::vec3 nv::random_base::precise_hollow_ellipsoid_point( const vec3& iradii, const vec3& oradii )
|
---|
[308] | 266 | {
|
---|
| 267 | return fast_hollow_ellipsoid_point( iradii, oradii );
|
---|
| 268 | }
|
---|
| 269 |
|
---|
[503] | 270 | nv::random_xor128::random_xor128( seed_type seed /*= randomized_seed() */ )
|
---|
| 271 | {
|
---|
| 272 | set_seed( seed );
|
---|
| 273 | }
|
---|
| 274 |
|
---|
| 275 | nv::random_base::seed_type nv::random_xor128::set_seed( seed_type seed /*= 0 */ )
|
---|
| 276 | {
|
---|
[509] | 277 | if ( seed == 0 ) seed = randomized_seed();
|
---|
[504] | 278 | uint32 s = uint32( 4294967296 - seed );
|
---|
[503] | 279 | m_state[0] = 123456789 * s;
|
---|
| 280 | m_state[1] = 362436069 * s;
|
---|
| 281 | m_state[2] = 521288629 * s;
|
---|
| 282 | m_state[3] = 88675123 * s;
|
---|
| 283 | return seed;
|
---|
| 284 | }
|
---|
| 285 |
|
---|
| 286 | nv::random_base::result_type nv::random_xor128::rand()
|
---|
| 287 | {
|
---|
| 288 | uint32 t = m_state[0];
|
---|
| 289 | t ^= t << 11;
|
---|
| 290 | t ^= t >> 8;
|
---|
| 291 | m_state[0] = m_state[1]; m_state[1] = m_state[2]; m_state[2] = m_state[3];
|
---|
| 292 | m_state[3] ^= m_state[3] >> 19;
|
---|
| 293 | m_state[3] ^= t;
|
---|
| 294 | return m_state[3];
|
---|
| 295 | }
|
---|