1 | // tRandom.cpp  |
2 | //  |
3 | // Functions necessary to generate pseudo-random numbers using a number of different algorithms. Random bits, integers,  |
4 | // and floats may all be generated. The generators are objects and state isn't shared. This allows multiple generators  |
5 | // that can optionally be run on different threads. Sharing a generator between threads is not supported. One of the  |
6 | // generators implements the Mersenne Twistor algorithm by M. Matsumoto & T. Nishimura.  |
7 | //  |
8 | // Copyright (c) 2005, 2017 Tristan Grimmer.  |
9 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby  |
10 | // granted, provided that the above copyright notice and this permission notice appear in all copies.  |
11 | //  |
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL  |
13 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,  |
14 | // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN  |
15 | // AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR  |
16 | // PERFORMANCE OF THIS SOFTWARE.  |
17 |   |
18 | #include "Math/tRandom.h"  |
19 | using namespace tMath;  |
20 |   |
21 |   |
22 | tRandom::tDefaultGeneratorType tRandom::DefaultGenerator;  |
23 |   |
24 |   |
25 | void tRandom::tGeneratorMersenneTwister::SetSeed(uint32 seed)  |
26 | {  |
27 | StateVector[0] = seed;  |
28 | for (StateIndex = 1; StateIndex < Dimensions; StateIndex++)  |
29 | StateVector[StateIndex] = 0x6C078965 * (StateVector[StateIndex-1] ^ (StateVector[StateIndex-1] >> 30)) + StateIndex;  |
30 | }  |
31 |   |
32 |   |
33 | void tRandom::tGeneratorMersenneTwister::SetSeed(const uint32* seeds, int numSeeds)  |
34 | {  |
35 | tAssert(numSeeds > 0);  |
36 | SetSeed(uint32(0x54FFC97A));  |
37 |   |
38 | int i = 1; int j = 0;  |
39 | int countMax = tMax(int(Dimensions), numSeeds);  |
40 | for (int count = 0; count < countMax; count++)  |
41 | {  |
42 | StateVector[i] = (StateVector[i] ^ ((StateVector[i-1] ^ (StateVector[i-1] >> 30)) * 0x0019660D)) + seeds[j] + j;  |
43 | i++;  |
44 | j++;  |
45 | if (i >= Dimensions)  |
46 | {  |
47 | StateVector[0] = StateVector[Dimensions-1];  |
48 | i = 1;  |
49 | }  |
50 |   |
51 | if (j >= numSeeds)  |
52 | j = 0;  |
53 | }  |
54 |   |
55 | for (int count = 0; count < Dimensions-1; count++)  |
56 | {  |
57 | StateVector[i] = (StateVector[i] ^ ((StateVector[i-1] ^ (StateVector[i-1] >> 30)) * 0x5D588B65)) - i;  |
58 | if (++i >= Dimensions)  |
59 | {  |
60 | StateVector[0] = StateVector[Dimensions-1];  |
61 | i = 1;  |
62 | }  |
63 | }  |
64 |   |
65 | StateVector[0] = 0x80000000;  |
66 | }  |
67 |   |
68 |   |
69 | uint32 tRandom::tGeneratorMersenneTwister::GetBits() const  |
70 | {  |
71 | if (StateIndex >= Dimensions)  |
72 | {  |
73 | uint32 lowerBits = (1LU << LeftShift) - 1;  |
74 | uint32 upperBits = 0xFFFFFFFF << LeftShift;  |
75 |   |
76 | const uint32 toggle[2] = { 0, Toggle };  |
77 | for (int i = 0; i < Dimensions-PartialDims; i++)  |
78 | {  |
79 | uint32 y = (StateVector[i] & upperBits) | (StateVector[i+1] & lowerBits);  |
80 | StateVector[i] = StateVector[i+PartialDims] ^ (y>>1) ^ toggle[ y&1 ];  |
81 | }  |
82 |   |
83 | for (int i = Dimensions-PartialDims; i < Dimensions-1; i++)  |
84 | {  |
85 | uint32 y = (StateVector[i] & upperBits) | (StateVector[i+1] & lowerBits);  |
86 | StateVector[i] = StateVector[i+PartialDims-Dimensions] ^ (y>>1) ^ toggle[ y&1 ];  |
87 | }  |
88 |   |
89 | uint32 y = (StateVector[Dimensions-1] & upperBits) | (StateVector[0] & lowerBits);  |
90 | StateVector[Dimensions-1] = StateVector[PartialDims-1] ^ (y>>1) ^ toggle[ y&1 ];  |
91 | StateIndex = 0;  |
92 | }  |
93 |   |
94 | uint32 bits = StateVector[StateIndex++];  |
95 |   |
96 | // Tempering.  |
97 | bits ^= bits >> TemperShiftA;  |
98 | bits ^= (bits << TemperShiftB) & TemperB;  |
99 | bits ^= (bits << TemperShiftC) & TemperC;  |
100 | bits ^= bits >> TemperShiftD;  |
101 |   |
102 | return bits;  |
103 | }  |
104 |   |
105 |   |
106 | double tRandom::tGetDouble(const tGenerator& gen)  |
107 | {  |
108 | union  |
109 | {  |
110 | double real;  |
111 | uint32 pattern[2];  |
112 | } convert;  |
113 |   |
114 | uint32 bits = gen.GetBits();  |
115 |   |
116 | // This assumes knowledge of the IEEE double precision floating point representation.  |
117 | #ifdef ENDIAN_LITTLE  |
118 | convert.pattern[0] = bits << 20;  |
119 | convert.pattern[1] = (bits >> 12) | 0x3FF00000;  |
120 | #else  |
121 | convert.pattern[1] = bits << 20;  |
122 | convert.pattern[0] = (bits >> 12) | 0x3FF00000;  |
123 | #endif  |
124 |   |
125 | return convert.real - 1.0;  |
126 | }  |
127 |   |
128 |   |
129 | tVector3 tRandom::tGetDir(const tVector3& centerDir, float extentAngle, const tGenerator& gen)  |
130 | {  |
131 | // @todo This doesn't work for arbitrary central directions. For example, centerDir.z should be allowed to be zero.  |
132 | float theta = tArcTan( tSqrt(centerDir.x*centerDir.x + centerDir.y*centerDir.y), centerDir.z );  |
133 | float phi = tArcTan( centerDir.y, centerDir.x );  |
134 | float normSpread = (Pi - (tGetFloat(gen)*extentAngle)) / Pi;  |
135 |   |
136 | // Rotate around the axis.  |
137 | theta += -((extentAngle / 2.0f)) + (tGetFloat(gen) * extentAngle);  |
138 |   |
139 | // Rotate away from the axis phi radians at angle theta.  |
140 | phi += (-extentAngle/2.0f) + (tGetFloat(gen) * normSpread * extentAngle);  |
141 |   |
142 | float sinTheta = tSin(theta);  |
143 | float cosTheta = tCos(theta);  |
144 | float sinPhi = tSin(phi);  |
145 | float cosPhi = tCos(phi);  |
146 | return tVector3(sinTheta * cosPhi, sinTheta * sinPhi, cosTheta);  |
147 | }  |
148 |   |
149 |   |
150 | // @todo Make these work.  |
151 |   |
152 |   |
153 | tVector3 tRandom::tGetPointOnTriangle(const tTriangle& tri, const tGenerator& gen)  |
154 | {  |
155 | // P = a*A + b*B + c*C with a + b + c = 1. We generate a and b randomly E [0, 1/2]. Choosing 1/2 for the upper  |
156 | // bound ensures we don't go over 1. http://www.cgafaq.info/wiki/Random_Point_In_Triangle  |
157 | float a = tGetFloat(gen) / 2.0f;  |
158 | float b = tGetFloat(gen) / 2.0f;  |
159 | float c = 1.0f - a - b;  |
160 | return tri.A*a + tri.B*b + tri.C*c;  |
161 | }  |
162 |   |
163 |   |
164 | tVector3 tRandom::tGetPointOnUnitSquare(const tPlane& plane, const tGenerator& gen)  |
165 | {  |
166 | // Requires normalized plane. http://mathworld.wolfram.com/Plane.html  |
167 | tVector3 u, v;  |
168 | plane.ComputeOrthogonalBasisVectors(u, v);  |
169 |   |
170 | float randomU = tGetFloat(gen) - 0.5f;  |
171 | float randomV = tGetFloat(gen) - 0.5f;  |
172 | tVector3 point = plane.Normal*plane.Distance + u*randomU + v*randomV;  |
173 | return point;  |
174 | }  |
175 |   |
176 |   |
177 | tVector3 tRandom::tGetPointOnBox(const tBox& box, const tGenerator& gen)  |
178 | {  |
179 | tVector3 extent = box.ComputeExtents();  |
180 | tPlane plane;  |
181 | tVector3 u, v;  |
182 |   |
183 | int face = tGetBounded(1, 6, gen);  |
184 | switch (face)  |
185 | {  |
186 | case 1:  |
187 | case 2:  |
188 | plane.Distance = extent.x;  |
189 | plane.Normal = tVector3::i;  |
190 | u = extent.y * tVector3::j;  |
191 | v = extent.z * tVector3::k;  |
192 | break;  |
193 |   |
194 | case 3:  |
195 | case 4:  |
196 | plane.Distance = extent.y;  |
197 | plane.Normal = tVector3::j;  |
198 | u = extent.x * tVector3::i;  |
199 | v = extent.z * tVector3::k;  |
200 | break;  |
201 |   |
202 | case 5:  |
203 | case 6:  |
204 | plane.Distance = extent.z;  |
205 | plane.Normal = tVector3::k;  |
206 | u = extent.x * tVector3::i;  |
207 | v = extent.y * tVector3::j;  |
208 | break;  |
209 | }  |
210 |   |
211 | if (face % 2)  |
212 | plane.Normal = -plane.Normal;  |
213 |   |
214 | float randomU = tGetBounded(-1.0f, 1.0f, gen);  |
215 | float randomV = tGetBounded(-1.0f, 1.0f, gen);  |
216 |   |
217 | return plane.Normal*plane.Distance + u*randomU + v*randomV;  |
218 | }  |
219 |   |
220 |   |
221 | tVector3 tRandom::tGetPointOnSphere(const tSphere& sphere, const tGenerator& gen)  |
222 | {  |
223 | // See http://mathworld.wolfram.com/SphericalCoordinates.html  |
224 | float theta = tGetBounded(0.0f, TwoPi, gen);  |
225 | float phi = tGetBounded(0.0f, Pi, gen);  |
226 |   |
227 | // 's' for Sin, 'c' for Cos.  |
228 | float ct, st, cp, sp;  |
229 | tCosSin(ct, st, theta);  |
230 | tCosSin(cp, sp, phi);  |
231 |   |
232 | tVector3 point(ct*sp, st*sp, cp);  |
233 | point *= sphere.Radius;  |
234 | point += sphere.Center;  |
235 | return point;  |
236 | }  |
237 |   |
238 |   |
239 | tVector3 tRandom::tGetPointOnCylinder(const tCylinder& cylinder, const tGenerator& gen)  |
240 | {  |
241 | // See http://mathworld.wolfram.com/CylindricalCoordinates.html  |
242 | tVector3 axis = cylinder.B - cylinder.A;  |
243 | axis.Normalize();  |
244 |   |
245 | // This function works by first computing a plane perpendicular to the major cylinder axis  |
246 | // and placing a circle there. We simply randomly choose how far up the cylinder the plane  |
247 | // is, and then choose a random angle around the circle.  |
248 | tPlane plane(axis);  |
249 | tVector3 u, v;  |
250 | plane.ComputeOrthogonalBasisVectors(u, v);  |
251 |   |
252 | axis *= tGetFloat(gen); // Random distance along axis.  |
253 | float theta = tGetBounded(0.0f, TwoPi, gen); // Random angle around circle.  |
254 | float st = tSin(theta);  |
255 | float ct = tCos(theta);  |
256 |   |
257 | tVector3 point = cylinder.A + axis; // Point in middle of cylinder.  |
258 | point += cylinder.Radius * (u*st + v*ct); // Point on circle.  |
259 | return point;  |
260 | }  |
261 |   |
262 |   |
263 | tVector3 tRandom::tGetPointInBox(const tBox& box, const tGenerator& gen)  |
264 | {  |
265 | float xr = tGetFloat(gen);  |
266 | float yr = tGetFloat(gen);  |
267 | float zr = tGetFloat(gen);  |
268 | tVector3 offset  |
269 | (  |
270 | xr * (box.Max.x - box.Min.x),  |
271 | yr * (box.Max.y - box.Min.y),  |
272 | zr * (box.Max.z - box.Min.z)  |
273 | );  |
274 | return box.Min + offset;  |
275 | }  |
276 |   |
277 |   |
278 | tVector3 tRandom::tGetPointInSphere(const tSphere& sphere, const tGenerator& gen)  |
279 | {  |
280 | // See http://mathworld.wolfram.com/SphericalCoordinates.html  |
281 | float theta = tGetBounded(0.0f, TwoPi, gen);  |
282 | float phi = tGetBounded(0.0f, Pi, gen);  |
283 | float radius = tGetBounded(0.0f, sphere.Radius, gen);  |
284 |   |
285 | // 's' for Sin, 'c' for Cos.  |
286 | float ct, st, cp, sp;  |
287 | tCosSin(ct, st, theta);  |
288 | tCosSin(cp, sp, phi);  |
289 |   |
290 | tVector3 point(ct*sp, st*sp, cp);  |
291 | point *= radius;  |
292 | point += sphere.Center;  |
293 | return point;  |
294 | }  |
295 |   |
296 |   |
297 | tVector3 tRandom::tGetPointInCylinder(const tCylinder& cylinder, const tGenerator& gen)  |
298 | {  |
299 | // See http://mathworld.wolfram.com/CylindricalCoordinates.html  |
300 | tVector3 axis = cylinder.B - cylinder.A;  |
301 | axis.Normalize();  |
302 |   |
303 | // This function works by first computing a plane perpendicular to the major cylinder axis and placing a circle  |
304 | // there. We simply randomly choose how far up the cylinder the plane is, and then choose a random angle around  |
305 | // the circle.  |
306 | tPlane plane(axis);  |
307 | tVector3 u, v;  |
308 | plane.ComputeOrthogonalBasisVectors(u, v);  |
309 |   |
310 | axis *= tGetFloat(gen); // Random distance along axis.  |
311 | float radius = tGetBounded(0.0f, cylinder.Radius, gen); // Random radius out.  |
312 | float theta = tGetBounded(0.0f, TwoPi, gen); // Random angle around circle.  |
313 | float st = tSin(theta);  |
314 | float ct = tCos(theta);  |
315 |   |
316 | tVector3 point = cylinder.A + axis; // Point in middle of cylinder.  |
317 | point += radius * (u*st + v*ct); // Point on circle.  |
318 | return point;  |
319 | }  |
320 | |