source: trunk/src/core/random.cc @ 499

Last change on this file since 499 was 487, checked in by epyon, 9 years ago
File size: 6.9 KB
Line 
1// Copyright (C) 2012-2014 ChaosForge Ltd
2// http://chaosforge.org/
3//
4// This file is part of Nova libraries.
5// For conditions of distribution and use, see copying.txt file in root folder.
6
7#include "nv/core/random.hh"
8#include "nv/core/time.hh"
9#include "nv/stl/utility/common.hh"
10
11using namespace nv;
12
13static const uint32 mt_upper_mask = 0x80000000UL;
14static const uint32 mt_lower_mask = 0x7FFFFFFFUL;
15static const uint32 mt_full_mask  = 0xFFFFFFFFUL;
16static const uint32 mt_matrix_a   = 0x9908B0DFUL;
17
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
21void random::mt_init( uint32 seed )
22{
23        m_state[0] = static_cast<uint32>( seed & mt_full_mask );
24        for ( uint32 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        }
29
30        m_remaining = 0;
31        m_next      = nullptr;
32        m_seeded    = 1;
33}
34
35
36void random::mt_update()
37{
38        uint32 *p = m_state;
39
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;
50}
51
52
53uint32 random::mt_uint32()
54{
55        uint32 r;
56
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;
70}
71
72random::random( random::seed_type seed /*= 0 */ )
73        : m_next( nullptr ), m_remaining( 0 ), m_seeded( 0 )
74{
75        mt_init( seed == 0 ? randomized_seed() : seed );
76}
77
78random::seed_type random::set_seed( random::seed_type seed /*= 0 */ )
79{
80        if ( seed == 0 ) seed = randomized_seed();
81        mt_init( seed );
82        return seed;
83}
84
85nv::random& random::get()
86{
87        static random default_rng;
88        return default_rng;
89}
90
91random::result_type random::rand()
92{
93        return mt_uint32();
94}
95
96uint32 random::urand( uint32 val )
97{
98        uint32 x, max = mt_full_mask - ( mt_full_mask % val );
99        while ( ( x = rand() ) >= max );
100        return x / ( max / val );
101}
102
103random::seed_type random::randomized_seed()
104{
105        // TODO: this seems off, as it might often seed the same, use general time
106        // instead
107        return narrow_cast< seed_type >( get_ticks() );
108}
109
110nv::vec2 nv::random::precise_unit_vec2()
111{
112        f32 angle = frand( math::pi<f32>() * 2.f );
113        return vec2( cos( angle ), sin( angle ) );
114}
115
116nv::vec3 nv::random::precise_unit_vec3()
117{
118        f32 cos_theta = frange( -1.0f, 1.0f );
119        f32 sin_theta = sqrt( 1.0f - cos_theta * cos_theta );
120        f32 phi       = frand( 2 * math::pi<f32>() );
121        return vec3(
122                sin_theta * sin(phi),
123                sin_theta * cos(phi),
124                cos_theta
125                );
126}
127
128nv::vec2 nv::random::fast_disk_point()
129{
130        f32 r1 = frand();
131        f32 r2 = frand();
132        if ( r1 > r2 ) swap( r1, r2 );
133        f32 rf = 2* math::pi<f32>()*(r1/r2);
134        return vec2( r2*cos( rf ), r2*sin( rf ) );
135}
136
137nv::vec2 nv::random::precise_disk_point()
138{
139        f32 r = sqrt( frand() );
140        f32 rangle = frand( math::pi<f32>() );
141        return vec2( r*cos( rangle ), r*sin( rangle ) );
142}
143
144nv::vec3 nv::random::fast_sphere_point()
145{
146        f32 rad     = frand();
147        f32 pi      = math::pi<f32>();
148        f32 phi     = asin( frange( -1.0f, 1.0f ) ) + pi*.5f;
149        f32 theta   = frange( 0.0f, 2 * math::pi<f32>() );
150        f32 sin_phi = sin( phi );
151        return vec3(
152                rad * cos(theta) * sin_phi,
153                rad * sin(theta) * sin_phi,
154                rad * cos(phi)
155        );
156}
157
158nv::vec3 nv::random::precise_sphere_point()
159{
160        f32 radius = pow( frand(), 1.f/3.f );
161        f32 cos_theta = frange( -1.0f, 1.0f );
162        f32 sin_theta = sqrt( 1.0f - cos_theta * cos_theta );
163        f32 phi       = frange( 0.0f, 2 * math::pi<f32>() );
164        return vec3(
165                radius * sin_theta * sin(phi),
166                radius * sin_theta * cos(phi),
167                radius * cos_theta
168                );
169}
170
171nv::vec2 nv::random::precise_ellipse_point( const vec2& radii )
172{
173        vec2 p = range( -radii, radii );
174        vec2 inv_radii  = 1.f / radii;
175        vec2 inv_radii2 = inv_radii * inv_radii;
176        for ( uint32 i = 0; i < 12; ++i )
177        {
178                if ( p.x * p.x * inv_radii2.x + p.y * p.y * inv_radii2.y <= 1.f )
179                {
180                        return p;
181                }
182        }
183        return fast_disk_point() * radii;
184}
185
186nv::vec3 nv::random::precise_ellipsoid_point( const vec3& radii )
187{
188        vec3 p = range( -radii, radii );
189        vec3 inv_radii  = 1.f / radii;
190        vec3 inv_radii2 = inv_radii * inv_radii;
191        for ( uint32 i = 0; i < 12; ++i )
192        {
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 )
194                {
195                        return p;
196                }
197        }
198        return fast_sphere_point() * radii;
199}
200
201nv::vec2 nv::random::fast_hollow_disk_point( f32 iradius, f32 oradius )
202{
203        f32 idist2 = iradius * iradius;
204        f32 odist2 = oradius * oradius;
205        f32 rdist  = sqrt( frange( idist2, odist2 ) );
206        return rdist * precise_unit_vec2();
207}
208
209nv::vec2 nv::random::precise_hollow_disk_point( f32 iradius, f32 oradius )
210{
211        return fast_hollow_disk_point( iradius, oradius );
212}
213
214nv::vec3 nv::random::fast_hollow_sphere_point( f32 iradius, f32 oradius )
215{
216        f32 idist3 = iradius * iradius * iradius;
217        f32 odist3 = oradius * oradius * oradius;
218        f32 rdist  = pow( frange( idist3, odist3 ), 1.f/3.f );
219        return rdist * precise_unit_vec3();
220}
221
222nv::vec3 nv::random::precise_hollow_sphere_point( f32 iradius, f32 oradius )
223{
224        return fast_hollow_sphere_point( iradius, oradius );
225}
226
227
228nv::vec2 nv::random::fast_hollow_ellipse_point( const vec2& iradii, const vec2& oradii )
229{
230        vec2 iradii2 = iradii * iradii;
231        vec2 opoint     = ellipse_edge( oradii );
232        vec2 opoint2    = opoint * opoint;
233        vec2 odir       = math::normalize( opoint );
234        f32 odist2    = opoint2.x + opoint2.y;
235
236        f32 low    = iradii2.y * opoint2.x + iradii2.x * opoint2.y;
237        f32 idist2 = ((iradii2.x * iradii2.y) / low ) * odist2;
238
239        f32 rdist     = sqrt( frange( idist2, odist2 ) );
240        return odir * rdist;   
241}
242
243nv::vec2 nv::random::precise_hollow_ellipse_point( const vec2& iradii, const vec2& oradii )
244{
245        return fast_hollow_ellipse_point( iradii, oradii );
246}
247
248nv::vec3 nv::random::fast_hollow_ellipsoid_point( const vec3& iradii, const vec3& oradii )
249{
250        vec3 iradii2 = iradii * iradii;
251        vec3 opoint     = ellipsoid_edge( oradii );
252        vec3 opoint2    = opoint * opoint;
253        vec3 odir       = math::normalize( opoint );
254        f32 odist2    = opoint2.x + opoint2.y + opoint2.z;
255
256        f32 low    =
257                iradii2.y * iradii2.z * opoint2.x +
258                iradii2.x * iradii2.z * opoint2.y +
259                iradii2.x * iradii2.y * opoint2.z;
260        f32 idist2 = ((iradii2.x * iradii2.y * iradii2.z) / low ) * odist2;
261
262        f32 odist3 = odist2 * sqrt( odist2 );
263        f32 idist3 = idist2 * sqrt( idist2 );
264
265        f32 rdist     = pow( frange( idist3, odist3 ), 1.f/3.f );
266        return odir * rdist;   
267}
268
269nv::vec3 nv::random::precise_hollow_ellipsoid_point( const vec3& iradii, const vec3& oradii )
270{
271        return fast_hollow_ellipsoid_point( iradii, oradii );
272}
273
Note: See TracBrowser for help on using the repository browser.