math.ts 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. /**
  2. * @module lib/math
  3. * This module contains various math functions - [see the documentation for more info](https://github.com/Sv443-Network/UserUtils/blob/main/docs.md#math)
  4. */
  5. import type { Stringifiable } from "./types.js";
  6. /** Ensures the passed {@linkcode value} always stays between {@linkcode min} and {@linkcode max} */
  7. export function clamp(value: number, min: number, max: number): number
  8. /** Ensures the passed {@linkcode value} always stays between 0 and {@linkcode max} */
  9. export function clamp(value: number, max: number): number
  10. /** Ensures the passed {@linkcode value} always stays between {@linkcode min} and {@linkcode max} - if `max` isn't given, it defaults to `min` and `min` defaults to 0 */
  11. export function clamp(value: number, min: number, max?: number): number {
  12. if(typeof max !== "number") {
  13. max = min;
  14. min = 0;
  15. }
  16. return Math.max(Math.min(value, max), min);
  17. }
  18. /**
  19. * Transforms the value parameter from the numerical range `range1min` to `range1max` to the numerical range `range2min` to `range2max`
  20. * For example, you can map the value 2 in the range of 0-5 to the range of 0-10 and you'd get a 4 as a result.
  21. */
  22. export function mapRange(value: number, range1min: number, range1max: number, range2min: number, range2max: number): number;
  23. /**
  24. * Transforms the value parameter from the numerical range `0` to `range1max` to the numerical range `0` to `range2max`
  25. * For example, you can map the value 2 in the range of 0-5 to the range of 0-10 and you'd get a 4 as a result.
  26. */
  27. export function mapRange(value: number, range1max: number, range2max: number): number;
  28. export function mapRange(value: number, range1min: number, range1max: number, range2min?: number, range2max?: number): number {
  29. if(typeof range2min === "undefined" || typeof range2max === "undefined") {
  30. range2max = range1max;
  31. range1max = range1min;
  32. range2min = range1min = 0;
  33. }
  34. if(Number(range1min) === 0.0 && Number(range2min) === 0.0)
  35. return value * (range2max / range1max);
  36. return (value - range1min) * ((range2max - range2min) / (range1max - range1min)) + range2min;
  37. }
  38. /**
  39. * Returns a random number between {@linkcode min} and {@linkcode max} (inclusive)
  40. * Set {@linkcode enhancedEntropy} to true to use `crypto.getRandomValues()` for better cryptographic randomness (this also makes it take longer to generate)
  41. */
  42. export function randRange(min: number, max: number, enhancedEntropy?: boolean): number
  43. /**
  44. * Returns a random number between 0 and {@linkcode max} (inclusive)
  45. * Set {@linkcode enhancedEntropy} to true to use `crypto.getRandomValues()` for better cryptographic randomness (this also makes it take longer to generate)
  46. */
  47. export function randRange(max: number, enhancedEntropy?: boolean): number
  48. /**
  49. * Returns a random number between {@linkcode min} and {@linkcode max} (inclusive)
  50. * Set {@linkcode enhancedEntropy} to true to use `crypto.getRandomValues()` for better cryptographic randomness (this also makes it take longer to generate)
  51. */
  52. export function randRange(...args: (number | boolean | undefined)[]): number {
  53. let min: number, max: number, enhancedEntropy = false;
  54. // using randRange(min, max)
  55. if(typeof args[0] === "number" && typeof args[1] === "number")
  56. [ min, max ] = args;
  57. // using randRange(max)
  58. else if(typeof args[0] === "number" && typeof args[1] !== "number") {
  59. min = 0;
  60. [ max ] = args;
  61. }
  62. else
  63. throw new TypeError(`Wrong parameter(s) provided - expected (number, boolean|undefined) or (number, number, boolean|undefined) but got (${args.map(a => typeof a).join(", ")}) instead`);
  64. if(typeof args[2] === "boolean")
  65. enhancedEntropy = args[2];
  66. else if(typeof args[1] === "boolean")
  67. enhancedEntropy = args[1];
  68. min = Number(min);
  69. max = Number(max);
  70. if(isNaN(min) || isNaN(max))
  71. return NaN;
  72. if(min > max)
  73. throw new TypeError("Parameter \"min\" can't be bigger than \"max\"");
  74. if(enhancedEntropy) {
  75. const uintArr = new Uint8Array(1);
  76. crypto.getRandomValues(uintArr);
  77. return Number(Array.from(
  78. uintArr,
  79. (v) => Math.round(mapRange(v, 0, 255, min, max)).toString(10),
  80. ).join(""));
  81. }
  82. else
  83. return Math.floor(Math.random() * (max - min + 1)) + min;
  84. }
  85. /** Calculates the amount of digits in the given number - the given number or string will be passed to the `Number()` constructor. Returns NaN if the number is invalid. */
  86. export function digitCount(num: number | Stringifiable): number {
  87. num = Number((!["string", "number"].includes(typeof num)) ? String(num) : num);
  88. if(typeof num === "number" && isNaN(num))
  89. return NaN;
  90. return num === 0
  91. ? 1
  92. : Math.floor(
  93. Math.log10(Math.abs(Number(num))) + 1
  94. );
  95. }