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