1 /// Custom templated math
2 module sily.math;
3 
4 import std.math;
5 import std.random;
6 import std.algorithm.comparison;
7 import std.conv;
8 import std.traits;
9 import std.datetime: Clock;
10 import std.traits: isFloatingPoint;
11 
12 // https://github.com/godotengine/godot/blob/master/core/math/math_funcs.cpp
13 
14 const double degtorad = PI / 180.0; 
15 const double radtodeg = 180.0 / PI; 
16 
17 alias deg2rad = degtorad;
18 alias rad2deg = radtodeg;
19 
20 /// Linearly interpolates value
21 T lerp(T)(T from, T to, T weight) if (isFloatingPoint!T) {
22     from += (weight * (to - from));
23     return from;
24 }
25 
26 /// Snaps value to step
27 T snap(T)(T p_val, T p_step) if (isFloatingPoint!T) {
28     if (p_step != 0) {
29         p_val = floor(p_val / p_step + 0.5) * p_step;
30     }
31     return p_val;
32 }
33 
34 /// Snaps value to step, if T is not float, then explicit casts are used
35 /// and it may cause data loss
36 T snap(T)(T p_val, T p_step) if (!isFloatingPoint!T) {
37     if (p_step != 0) {
38         p_val = cast(T) (floor(cast(double) p_val / cast(double) p_step + 0.5) * cast(double) p_step);
39     }
40     return p_val;
41 }
42 
43 
44 /// std.random wrapper
45 struct RNG {
46     private uint _seed = defaultSeed;
47     private Random _rnd;
48     private ulong _calls;
49 
50     /// Creates RNG struct with set seed
51     this(uint p_seed) {
52         _seed = p_seed;
53         _rnd = Random(_seed);
54     }
55 
56     /// Randomizes seed
57     public void randomize() {
58         _seed = randomSeed;
59         setSeed(_seed);
60     }
61     /// Ditto
62     alias randomise = randomize;
63 
64     /// Sets custom seed `p_seed`
65     public void setSeed(uint p_seed) {
66         _seed = p_seed;
67         _rnd.seed(_seed);
68     }
69 
70     /// Returns current seed
71     public uint seed() { return _seed; }
72 
73     /// Alias to default seed
74     public static alias defaultSeed = Random.defaultSeed;
75     /// Alias to unpredictable seed
76     public static alias randomSeed = unpredictableSeed;
77 
78     /// Typed alias to get random value between 0 and T.max or custom min and max
79     alias randf = rand!float;
80     /// Ditto
81     alias randr = rand!real;
82     /// Ditto
83     alias randd = rand!double;
84     /// Ditto
85     alias randi = rand!int;
86     /// Ditto
87     alias randl = rand!long;
88     
89     
90     template rand(T) {
91         static if(isFloatingPoint!T) {
92             /// Returns random value between 0 and T.max or custom min and max
93             T rand() {
94                 return random / _rnd.max.to!T;
95             }
96 
97             /// Ditto
98             T rand(T min, T max) {
99                 return min + (rand!T * (max - min));
100             }
101         } else {
102             /// Ditto
103             T rand() {
104                 return (randr * T.max).to!T;
105             }
106 
107             /// Ditto
108             T rand(T min, T max) {
109                 return round(min + (randr * (max - min))).to!T;
110             } 
111         }
112     }
113 
114     /// Returns random uint
115     public uint random() {
116         uint r = _rnd.front;
117         skip();
118         return r;
119     }
120 
121     /// Skips current random value
122     public void skip() {
123         _rnd.popFront();
124         _calls ++;
125     }
126 
127     /// Skips N amount of random values
128     public void skipTo(ulong numCalls) {
129         while(_calls < numCalls) {
130             skip();
131         }
132     }
133 }
134 
135 // FIXME cubic_interpolate
136 /*
137 static _ALWAYS_INLINE_ float cubic_interpolate(float p_from, float p_to, float p_pre, float p_post, float p_weight) {
138     return 0.5f *
139             ((p_from * 2.0f) +
140                     (-p_pre + p_to) * p_weight +
141                     (2.0f * p_pre - 5.0f * p_from + 4.0f * p_to - p_post) * (p_weight * p_weight) +
142                     (-p_pre + 3.0f * p_from - 3.0f * p_to + p_post) * (p_weight * p_weight * p_weight));
143 }
144 
145 static _ALWAYS_INLINE_ float lerp_angle(float p_from, float p_to, float p_weight) {
146     float difference = fmod(p_to - p_from, (float)Math_TAU);
147     float distance = fmod(2.0f * difference, (float)Math_TAU) - difference;
148     return p_from + distance * p_weight;
149 }
150 
151 static _ALWAYS_INLINE_ float smoothstep(float p_from, float p_to, float p_s) {
152     if (is_equal_approx(p_from, p_to)) {
153         return p_from;
154     }
155     float s = CLAMP((p_s - p_from) / (p_to - p_from), 0.0f, 1.0f);
156     return s * s * (3.0f - 2.0f * s);
157 }
158 static _ALWAYS_INLINE_ double wrapf(double value, double min, double max) {
159     double range = max - min;
160     return is_zero_approx(range) ? min : value - (range * Math::floor((value - min) / range));
161 }
162 */