unity_random/
crypto.rs

1use crate::random::State;
2
3pub struct Crypto {}
4
5impl Crypto {
6    /// Initializes the PRNG with a given seed,
7    /// based on the Mersenne Twister algorithm (MT19934).
8    pub fn init_state(seed: u32) -> State {
9        let s0 = seed;
10        let s1 = s0.wrapping_mul(0x6C078965) + 1;
11        let s2 = s1.wrapping_mul(0x6C078965) + 1;
12        let s3 = s2.wrapping_mul(0x6C078965) + 1;
13
14        State { s0, s1, s2, s3 }
15    }
16
17    /// Generates the next `u32` using Marsaglia's Xorshift128 algorithm.
18    pub fn next_u32(state: &mut State) -> u32 {
19        let t = state.s0 ^ (state.s0 << 11);
20        let s = state.s3 ^ (state.s3 >> 19);
21
22        state.s0 = state.s1;
23        state.s1 = state.s2;
24        state.s2 = state.s3;
25
26        state.s3 = s ^ (t ^ (t >> 8));
27        state.s3
28    }
29
30    /// Generates the next `u32`, then converts it to a `f32`.
31    pub fn next_f32(state: &mut State) -> f32 {
32        (Crypto::next_u32(state) & 0x7FFFFF) as f32 / 0x7FFFFF as f32
33    }
34
35    /// Round to significant digits (rather than digits after the decimal).
36    ///
37    /// Calculations are done on `f64` instead of `f32`,
38    /// because such an implementation showed precision glitches
39    /// (e.g. `precision_f32(12300.0, 2) == 11999.999`).
40    ///
41    ///  From: https://stackoverflow.com/a/76572321
42    pub fn precision_f32(x: f32, decimals: u32) -> f32 {
43        if x == 0. || decimals == 0 {
44            0.
45        } else {
46            let shift = decimals as i32 - x.abs().log10().ceil() as i32;
47            let shift_factor = 10_f64.powi(shift);
48
49            ((x as f64 * shift_factor).round() / shift_factor) as f32
50        }
51    }
52
53    /// Linearly interpolates between `a` and `b` by `t`.
54    ///
55    /// `t` is clamped to the range `[0..1]`.
56    pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
57        a + (b - a) * t.clamp(0., 1.)
58    }
59
60    /// Creates an RGB color from a hue, saturation and value.
61    ///
62    /// If `hdr` is `true`, the returned color will not be clamped to `[0..1]`.
63    pub fn hsv_to_rbg(h: f32, s: f32, v: f32, hdr: bool) -> (f32, f32, f32, f32) {
64        if s == 0. {
65            return (v, v, v, 1.);
66        }
67
68        if v == 0. {
69            return (0., 0., 0., 1.);
70        }
71
72        let mut color;
73        let num = h * 6.;
74        let num2 = num.floor();
75        let num3 = num - num2;
76        let num4 = v * (1. - s);
77        let num5 = v * (1. - s * num3);
78        let num6 = v * (1. - s * (1. - num3));
79
80        match num2 + 1. {
81            0. => {
82                color = (v, num4, num5, 1.);
83            }
84            1. => {
85                color = (v, num6, num4, 1.);
86            }
87            2. => {
88                color = (num5, v, num4, 1.);
89            }
90            3. => {
91                color = (num4, v, num6, 1.);
92            }
93            4. => {
94                color = (num4, num5, v, 1.);
95            }
96            5. => {
97                color = (num6, num4, v, 1.);
98            }
99            6. => {
100                color = (v, num4, num5, 1.);
101            }
102            7. => {
103                color = (v, num6, num4, 1.);
104            }
105            _ => {
106                color = (0., 0., 0., 1.);
107            }
108        }
109
110        if !hdr {
111            color.0 = color.0.clamp(0., 1.);
112            color.1 = color.1.clamp(0., 1.);
113            color.2 = color.2.clamp(0., 1.);
114        }
115
116        color
117    }
118}