1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
use std::time::{SystemTime, UNIX_EPOCH};
#[cfg(feature = "serde")]
use serde_crate::{Deserialize, Serialize};
use crate::crypto::Crypto;
/// The internal state of the random number generator.
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate")
)]
#[derive(Copy, Clone)]
pub struct State {
pub s0: u32,
pub s1: u32,
pub s2: u32,
pub s3: u32,
}
/// An implementation of Unity's seeded PRNG, which aims to be faster than
/// .NET's `System.Random`.
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate")
)]
pub struct Random {
/// Gets or sets the full internal state of the random number generator.
pub state: State,
}
impl Random {
/// Initializes the PRNG with the current time.
pub fn new() -> Random {
let timestamp = Random::get_timestamp() as u32;
Random {
state: Crypto::init_state(timestamp),
}
}
/// (Re)-initializes the PRNG with a given seed.
pub fn init_state(&mut self, seed: i32) {
self.state = Crypto::init_state(seed as u32);
}
/// Returns a random `i32` within `[min..max]`.
///
/// Minimum is inclusive, maximum is exclusive.
pub fn range_int(&mut self, mut min: i32, mut max: i32) -> i32 {
if min > max {
(min, max) = (max, min);
}
let diff = (max - min) as u32;
if diff > 0 {
min + (self.next_u32().rem_euclid(diff)) as i32
} else {
min
}
}
/// Returns a random `f32` within `[min..max]` (range is inclusive).
pub fn range_float(&mut self, min: f32, max: f32) -> f32 {
Crypto::precision_f32(self.range_float_intern(min, max), 7)
}
/// Returns a random `f32` within `[0..1]` (range is inclusive).
pub fn value(&mut self) -> f32 {
Crypto::precision_f32(self.next_f32(), 7)
}
/// Returns a random point inside or on a circle with radius 1.0.
///
/// Note that the probability space includes the perimeter of the circle because `next_f32`,
/// which is inclusive to 1.0, is used to acquire a random radius.
pub fn inside_unit_circle(&mut self) -> (f32, f32) {
let theta = self.range_float_intern(0., std::f32::consts::TAU);
let radius = (self.range_float_intern(0., 1.)).sqrt();
let x = Crypto::precision_f32(radius * theta.cos(), 7);
let y = Crypto::precision_f32(radius * theta.sin(), 7);
(x, y)
}
/// Returns a random point on the surface of a sphere with radius 1.0.
pub fn on_unit_sphere(&mut self) -> (f32, f32, f32) {
let (mut x, mut y, mut z) = self.on_unit_sphere_intern();
x = Crypto::precision_f32(x, 7);
y = Crypto::precision_f32(y, 7);
z = Crypto::precision_f32(z, 7);
(x, y, z)
}
/// Returns a random point inside or on a sphere with radius 1.0.
///
/// Note that the probability space includes the surface of the sphere because `next_f32`,
/// which is inclusive to 1.0, is used to acquire a random radius.
pub fn inside_unit_sphere(&mut self) -> (f32, f32, f32) {
let (mut x, mut y, mut z) = self.on_unit_sphere_intern();
let dist = self.next_f32().powf(1. / 3.);
x = Crypto::precision_f32(x * dist, 7);
y = Crypto::precision_f32(y * dist, 7);
z = Crypto::precision_f32(z * dist, 7);
(x, y, z)
}
/// Returns a random rotation.
///
/// Randomize the x, y, z, and w of a Quaternion each to `[-1.0..1.0]` (inclusive)
/// via Range and normalize the result.
///
/// See also `rotation_uniform` for a slower but higher quality algorithm.
pub fn rotation(&mut self) -> (f32, f32, f32, f32) {
let mut x = self.range_float_intern(-1., 1.);
let mut y = self.range_float_intern(-1., 1.);
let mut z = self.range_float_intern(-1., 1.);
let mut w = self.range_float_intern(-1., 1.);
let mut mag = (x.powi(2) + y.powi(2) + z.powi(2) + w.powi(2)).sqrt();
if w < 0. {
mag = -mag;
}
x = Crypto::precision_f32(x / mag, 7);
y = Crypto::precision_f32(y / mag, 7);
z = Crypto::precision_f32(z / mag, 7);
w = Crypto::precision_f32(w / mag, 7);
(x, y, z, w)
}
/// Returns a random rotation with uniform distribution.
///
/// Employs Hopf fibration to return a random Quaternion
/// within a uniformly distributed selection space.
///
/// Gives higher quality results compared to the more naive approach employed by rotation,
/// though at a 40% performance cost.
pub fn rotation_uniform(&mut self) -> (f32, f32, f32, f32) {
let u1 = self.range_float_intern(0., 1.);
let u2 = self.range_float_intern(0., std::f32::consts::TAU);
let u3 = self.range_float_intern(0., std::f32::consts::TAU);
let sqrt = (u1).sqrt();
let inv = (1. - u1).sqrt();
let mut x = inv * u2.sin();
let mut y = inv * u2.cos();
let mut z = sqrt * u3.sin();
let mut w = sqrt * u3.cos();
if w < 0. {
x = -x;
y = -y;
z = -z;
w = -w;
}
x = Crypto::precision_f32(x, 7);
y = Crypto::precision_f32(y, 7);
z = Crypto::precision_f32(z, 7);
w = Crypto::precision_f32(w, 7);
(x, y, z, w)
}
/// Generates a random RGBA color.
///
/// This may produce inaccurate results due to an issue with .NET versions before 5.x.
///
/// Unity uses a custom build of Mono, which is based on an older .NET version.
pub fn color(&mut self) -> (f32, f32, f32, f32) {
self.color_hsva(0., 1., 0., 1., 0., 1., 1., 1.)
}
/// Generates a random RGBA color from hue ranges.
///
/// This may produce inaccurate results due to an issue with .NET versions before 5.x.
///
/// Unity uses a custom build of Mono, which is based on an older .NET version.
pub fn color_h(&mut self, hue_min: f32, hue_max: f32) -> (f32, f32, f32, f32) {
self.color_hsva(hue_min, hue_max, 0., 1., 0., 1., 1., 1.)
}
/// Generates a random RGBA color from hue and saturation ranges.
///
/// This may produce inaccurate results due to an issue with .NET versions before 5.x.
///
/// Unity uses a custom build of Mono, which is based on an older .NET version.
pub fn color_hs(
&mut self,
hue_min: f32,
hue_max: f32,
saturation_min: f32,
saturation_max: f32,
) -> (f32, f32, f32, f32) {
self.color_hsva(
hue_min,
hue_max,
saturation_min,
saturation_max,
0.,
1.,
1.,
1.,
)
}
/// Generates a random RGBA color from HSV ranges.
///
/// This may produce inaccurate results due to an issue with .NET versions before 5.x.
///
/// Unity uses a custom build of Mono, which is based on an older .NET version.
pub fn color_hsv(
&mut self,
hue_min: f32,
hue_max: f32,
saturation_min: f32,
saturation_max: f32,
value_min: f32,
value_max: f32,
) -> (f32, f32, f32, f32) {
self.color_hsva(
hue_min,
hue_max,
saturation_min,
saturation_max,
value_min,
value_max,
1.,
1.,
)
}
/// Generates a random RGBA color from HSV and alpha ranges.
///
/// This may produce inaccurate results due to an issue with .NET versions before 5.x.
///
/// Unity uses a custom build of Mono, which is based on an older .NET version.
pub fn color_hsva(
&mut self,
hue_min: f32,
hue_max: f32,
saturation_min: f32,
saturation_max: f32,
value_min: f32,
value_max: f32,
alpha_min: f32,
alpha_max: f32,
) -> (f32, f32, f32, f32) {
let hue = Crypto::lerp(hue_min, hue_max, self.next_f32());
let sat = Crypto::lerp(saturation_min, saturation_max, self.next_f32());
let val = Crypto::lerp(value_min, value_max, self.next_f32());
let (mut r, mut g, mut b, _) = Crypto::hsv_to_rbg(hue, sat, val, true);
let mut a = Crypto::lerp(alpha_min, alpha_max, self.next_f32());
r = Crypto::precision_f32(r, 7);
g = Crypto::precision_f32(g, 7);
b = Crypto::precision_f32(b, 7);
a = Crypto::precision_f32(a, 7);
(r, g, b, a)
}
/// Generates the next u32.
fn next_u32(&mut self) -> u32 {
Crypto::next_u32(&mut self.state)
}
/// Generates the next f32.
fn next_f32(&mut self) -> f32 {
Crypto::next_f32(&mut self.state)
}
/// Generates the next f32, converting it to the range `[min..max]` (inclusive).
fn range_float_intern(&mut self, min: f32, max: f32) -> f32 {
let next = self.next_f32();
(1. - next) * max + next * min
}
/// Returns a random point on the surface of a sphere with radius 1.0.
fn on_unit_sphere_intern(&mut self) -> (f32, f32, f32) {
let dist = self.range_float_intern(-1., 1.);
let rad = self.range_float_intern(0., std::f32::consts::TAU);
let radius_xy = (1. - dist.powi(2)).sqrt();
let x = rad.cos() * radius_xy;
let y = rad.sin() * radius_xy;
let z = dist;
(x, y, z)
}
/// Returns the current timestamp down to the second.
fn get_timestamp() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("SystemTime before UNIX Epoch!")
.as_secs()
}
}