1
0

ContentHelpers.cs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. namespace Content.Shared.Rounding
  2. {
  3. public static class ContentHelpers
  4. {
  5. /// <summary>
  6. /// Assigns the value <paramref name="actual" /> going from 0 to <paramref name="max" />
  7. /// such that it is divided into a set of (amount <paramref name="levels" />) "levels".
  8. /// Rounding is performed to the "middle" so that the highest and lowest levels are only assigned
  9. /// if <paramref name="actual" /> is exactly <paramref name="max" /> or 0.
  10. /// </summary>
  11. /// <example>
  12. /// Say you have a progress bar going from 0 -> 100 inclusive and you want to map this to 6 sprite states (0, 4 intermediates and full).
  13. /// This method allows you to easily map this progress bar to the sprite states.
  14. /// </example>
  15. /// <param name="levels">The amount of levels to subdivide into.</param>
  16. /// <returns>An integer from 0 to <paramref name="levels" />-1.</returns>
  17. /// <exception cref="ArgumentException">
  18. /// Thrown if levels is less than 1.
  19. /// </exception>
  20. public static int RoundToLevels(double actual, double max, int levels)
  21. {
  22. if (levels <= 0)
  23. {
  24. throw new ArgumentException("Levels must be greater than 0.", nameof(levels));
  25. }
  26. if (actual >= max)
  27. {
  28. return levels - 1;
  29. }
  30. if (actual <= 0)
  31. {
  32. return 0;
  33. }
  34. var toOne = actual / max;
  35. return (int) Math.Ceiling(toOne * (levels - 2));
  36. }
  37. /// <summary>
  38. /// Returns the segment <paramref name="actual"/> lies on on a decimal scale from 0 to <paramref name="max"/> divided into
  39. /// <paramref name="levels"/> sections. In less mathematical terms, same as <see cref="RoundToLevels"/>
  40. /// except <paramref name="actual"/> is rounded to the nearest matching level instead of 0 and the highest level being
  41. /// precisely 0 and max and no other value.
  42. /// </summary>
  43. /// <example>
  44. /// You have a 5-segment progress bar used to display a percentile value.
  45. /// You want the display to match the percentile value as accurately as possible, so that eg.
  46. /// 95% is rounded up to 5, 89.99% is rounded down to 4, 15% is rounded up to 1 and 5% is rounded down
  47. /// to 0, in terms of number of segments lit.
  48. /// In this case you would use <code>RoundToNearestLevels(value, max, 5)</code>
  49. /// </example>
  50. /// <param name="actual">The point to be rounded to the nearest level.</param>
  51. /// <param name="max">The maximum value of the scale.</param>
  52. /// <param name="levels">Number of segments the scale is subdivided into.</param>
  53. /// <returns>The segment <paramref name="actual"/> lies on.</returns>
  54. /// <exception cref="ArgumentException">If level is 1 or less</exception>
  55. public static int RoundToNearestLevels(double actual, double max, int levels)
  56. {
  57. if (levels <= 1)
  58. {
  59. throw new ArgumentException("Levels must be greater than 1.", nameof(levels));
  60. }
  61. if (actual >= max)
  62. {
  63. return levels;
  64. }
  65. if (actual <= 0)
  66. {
  67. return 0;
  68. }
  69. return (int) Math.Round(actual / max * levels, MidpointRounding.AwayFromZero);
  70. }
  71. /// <summary>
  72. /// Basically helper for when you need to choose 0..N-1 element based on what
  73. /// percentage does actual/max takes.
  74. /// Example:
  75. /// We have a stack of 30 <paramref name="max"/> elements.
  76. /// When <paramref name="actual"/> is:
  77. /// - 0..9 we return 0.
  78. /// - 10..19 we return 1.
  79. /// - 20..30 we return 2.
  80. ///
  81. /// Useful when selecting N sprites for display in stacks, etc.
  82. /// </summary>
  83. /// <param name="actual">How many out of max elements are there</param>
  84. /// <param name="max"></param>
  85. /// <param name="levels"></param>
  86. /// <returns>The </returns>
  87. /// <exception cref="ArgumentException">if level is one or less</exception>
  88. public static int RoundToEqualLevels(double actual, double max, int levels)
  89. {
  90. if (levels <= 1)
  91. {
  92. throw new ArgumentException("Levels must be greater than 1.", nameof(levels));
  93. }
  94. if (actual >= max)
  95. {
  96. return levels - 1;
  97. }
  98. if (actual <= 0)
  99. {
  100. return 0;
  101. }
  102. return (int) Math.Round(actual / max * levels, MidpointRounding.ToZero);
  103. }
  104. }
  105. }