| 1 | module math |
| 2 | |
| 3 | // mix performs a linear interpolation (LERP) mix between `start` and `end`, using `t` to weight between them. |
| 4 | // `t` should be in the closed interval [0, 1]. |
| 5 | // For `t` == 0, the output is `x`. |
| 6 | // Note: mix is calculated in such a way, that the output *will* be `y`, when `t` == 1.0 . |
| 7 | // See: https://registry.khronos.org/OpenGL-Refpages/gl4/html/mix.xhtml |
| 8 | // Also: https://en.wikipedia.org/wiki/Linear_interpolation . |
| 9 | @[inline] |
| 10 | pub fn mix[T](start T, end T, t T) T { |
| 11 | return start * (1 - t) + end * t |
| 12 | } |
| 13 | |
| 14 | // exp_decay returns a frame independent exponential decay value between `a` and `b` using `delta_time_seconds`. |
| 15 | // `decay` is supposed to be useful in the range 1.0 to 25.0. From slow to fast. |
| 16 | // The function is a frame rate independent (approximation) of the well-known `lerp` or `mix` (linear interpolation) function. |
| 17 | // It is ported to V from the pseudo code shown towards the end of the video https://youtu.be/LSNQuFEDOyQ?t=2977 |
| 18 | // NOTE: Thanks to Freya Holmér for the function and the work done in this field. |
| 19 | @[inline] |
| 20 | pub fn exp_decay[T](a T, b T, decay f64, delta_time_seconds f64) T { |
| 21 | return T(f64(b) + (f64(a) - f64(b)) * exp(-decay * delta_time_seconds)) |
| 22 | } |
| 23 | |
| 24 | // clip constrain the given value `x`, to lie between two further values `min_value` and `max_value`. |
| 25 | // See: https://registry.khronos.org/OpenGL-Refpages/gl4/html/clamp.xhtml |
| 26 | // Also: https://en.wikipedia.org/wiki/Clamp_(function) |
| 27 | @[inline] |
| 28 | pub fn clip[T](x T, min_value T, max_value T) T { |
| 29 | return if x > max_value { |
| 30 | max_value |
| 31 | } else if x < min_value { |
| 32 | min_value |
| 33 | } else { |
| 34 | x |
| 35 | } |
| 36 | } |
| 37 | |
| 38 | // remap the input `x`, from the range [`a`,`b`] to [`c`,`d`] . |
| 39 | // Example: assert math.remap(20, 1, 100, 50, 5000) == 1000 |
| 40 | // Note: `a` should be != `b`. |
| 41 | @[inline] |
| 42 | pub fn remap[T](x T, a T, b T, c T, d T) T { |
| 43 | return c + (d - c) * (x - a) / (b - a) |
| 44 | } |
| 45 | |
| 46 | // smoothstep smoothly maps a value between `edge0` and `edge1`. It returns: |
| 47 | // 0 if `x` is less than or equal to the left `edge0`, |
| 48 | // 1 if `x` is greater than or equal to the right `edge`, |
| 49 | // and smoothly interpolates, using a Hermite polynomial, between 0 and 1 otherwise. |
| 50 | // The gradient of the smoothstep function is zero at both edges. This is convenient |
| 51 | // for creating a sequence of transitions using smoothstep to interpolate each segment |
| 52 | // as an alternative to using more sophisticated or expensive interpolation techniques. |
| 53 | // `smoothstep` is a 1st order smoothing function, using a 3rd order polynomial. |
| 54 | // See also `smootherstep`, which is slower, but nicer looking. |
| 55 | // See also https://en.wikipedia.org/wiki/Smoothstep |
| 56 | @[inline] |
| 57 | pub fn smoothstep[T](edge0 T, edge1 T, x T) T { |
| 58 | v := clip((x - edge0) / (edge1 - edge0), 0, 1) |
| 59 | return v * v * (3 - 2 * v) |
| 60 | } |
| 61 | |
| 62 | // smootherstep smoothly maps a value between `edge0` and `edge1`. |
| 63 | // smootherstep is a 2nd order smoothing function, using a 5th order polynomial. |
| 64 | // The 1st and 2nd order derivatives of the smootherstep function are 0 at both edges. |
| 65 | // See also `smoothstep`, which is faster, but has just its gradient being 0 at both edges. |
| 66 | // See also https://en.wikipedia.org/wiki/Smoothstep |
| 67 | @[inline] |
| 68 | pub fn smootherstep[T](edge0 T, edge1 T, x T) T { |
| 69 | v := clip((x - edge0) / (edge1 - edge0), 0, 1) |
| 70 | return v * v * v * (v * (6 * v - 15) + 10) |
| 71 | } |
| 72 | |